找回密码
 注册
关于网站域名变更的通知
查看: 248|回复: 1
打印 上一主题 下一主题

基于S3C2440的嵌入式Linux驱动——Framebuffer子系统解读

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-6-24 15:55 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
本文将介绍Framebuffer子系统6 q. y  R# D! g" k) m7 O
2 o6 ~; q4 n# V; ^" M  Z9 W
目标平台:TQ2440 CPU:s3c24402 m9 t8 M4 a+ [9 U) u2 s7 I3 _- G( t
! v- D4 u- a$ t; s+ Q8 ?8 O/ ~
LCD设备:3.5英寸,分辨率320X240# ?6 X- L- n, [* n$ q) R% Y
) d( o5 U3 }5 p
5 A) B" n( y+ K5 O, u
$ A6 t7 w0 T# y! F) g+ P
1. 概述# o, k" ?( R: N. c) x4 d

: D7 i- _3 n3 p8 A# BFramebuffer,中文名字是帧缓冲,这个帧也就是一副图像所需要的数据。因此,帧缓冲其实就是LCD设备的驱动程序。Linux中,framebuffer子系统框架如下:
% D: ~' r3 t9 [; i- g8 S. u! e1 Z* {+ l5 b# N$ e9 Y# R' Z- y; H
$ [; y" s0 Z" v9 G
3 M( a  J& y& C& a
核心层的代码以fbmem.c为主,核心层包括许多与具体硬件无关的代码,并且提供了API给用户空间。用户空间使用系统调用,系统调用会使用相应的API函数,最后会调用驱动层实现功能。对于不同的设备,驱动层的代码将有所不同。1 Y2 c9 \4 @; u# U! N
' Q, ~$ R- O* I6 H
接下来的内容中,首先给出framerbuffer使用的数据结构;随后简单描述framerbuffer核心层;最后,针对S3C2440,对驱动代码进行分析。; T7 y9 j4 H, L( }& r" [

2 E' L8 v5 v; I- J" I6 h$ \9 O1 f2. 数据结构8 H* r# ~# _2 g3 T/ @
2.1 fb_info 结构+ P7 N, o9 X# H' g8 c
  该结构是内核用于描述一个特定framebuffer设备。其中包含了几个重要的结构,将在下面介绍。
+ F" Y$ U6 z5 ^) ?" g( M
9 x" W; P+ y0 t! w  下列代码位于include/linux/fb.h' y/ `# L0 @. t- Q
6 ?" ?% r7 z( ?. s9 ~( n$ U' M
struct fb_info {
. r% E" P- R9 l9 Y) X$ Q! @: B        int node;. q! u% a5 f9 H. j2 [# J: U# u1 c( s
        int flags;4 `1 G4 e# y: P; A
        struct mutex lock;                /* Lock for open/release/ioctl funcs */! r& c2 |, O6 _4 e7 B" w
        struct fb_var_screeninfo var;        /* Current var */1 I% l3 q. O; P$ C2 U: i
        struct fb_fix_screeninfo fix;        /* Current fix */
+ T8 D1 Z3 i  g$ o' X! E0 y        struct fb_monspecs monspecs;        /* Current Monitor specs */
% l7 O. I5 i% y, c9 N        struct work_struct queue;        /* Framebuffer event queue */
& W1 l& d) H  [        struct fb_pixmap pixmap;        /* Image hardware mapper */% x- ?, y5 t& B/ P# e! l$ P
        struct fb_pixmap sprite;        /* Cursor hardware mapper */1 q; `2 m% D5 Q' b6 L8 T3 B7 q1 ^) \
        struct fb_cmap cmap;                /* Current cmap */
& k3 f- y) I; q% C* I        struct list_head modelist;      /* mode list */) h4 i3 j0 y% q0 R
        struct fb_videomode *mode;        /* current mode */: {; X+ G3 x' g, T
  w/ d2 t$ M' j7 {1 N: A( L! p6 {
#ifdef CONFIG_FB_BACKLIGHT
: H3 h) ~' s" Y" I+ T        /* assigned backlight device */- p1 L* W# ]/ R! n( S
        /* set before framebuffer registration, 2 C0 q+ z8 ]2 h
           remove after unregister */0 @7 P2 f. s/ l; F" }- l
        struct backlight_device *bl_dev;  n5 ]; b- J  \* x' H

1 K! `4 v5 K: F* h* s        /* Backlight level curve */
2 A0 ?6 G) h, V) l, M        struct mutex bl_curve_mutex;       
% U  B9 H/ o6 h0 f        u8 bl_curve[FB_BACKLIGHT_LEVELS];7 V" r& @$ c; ~" e" L3 h  u
#endif. b  U; p* {8 X/ J/ q. r) J' x( i
#ifdef CONFIG_FB_DEFERRED_IO
6 a6 W' E6 o! q5 X+ z        struct delayed_work deferred_work;2 m6 l( G- f7 N. d
        struct fb_deferred_io *fbdefio;; a% R6 U  }$ u: N$ p* m
#endif
7 Q' C- ?6 D- o
& q6 h- e9 M$ W6 }, p: o4 M) J% q        struct fb_ops *fbops;
) d3 z+ \6 C2 J7 P        struct device *device;                /* This is the parent */
& o5 W" ~/ R% \9 y        struct device *dev;                /* This is this fb device */! v9 ^; _) V5 `& p7 F2 q6 L6 h
        int class_flag;                    /* private sysfs flags */  v* M8 ^/ \0 ~5 l
#ifdef CONFIG_FB_TILEBLITTING
, R4 U/ {  A8 i/ I% X        struct fb_tile_ops *tileops;    /* Tile Blitting */  V0 I8 V9 Y1 q- A; F& v# M
#endif! d( @/ y- J( o! J5 x
        char __iomem *screen_base;        /* Virtual address */: t# Q) ~( u$ k
        unsigned long screen_size;        /* Amount of ioremapped VRAM or 0 */ * J' O& i3 b3 E# p
        void *pseudo_palette;                /* Fake palette of 16 colors */ 4 l. h5 p% n$ W' \# A* h! `8 z9 `
#define FBINFO_STATE_RUNNING        0
. Y3 O' h5 ]# Y! h- H' J#define FBINFO_STATE_SUSPENDED        17 j/ f6 z8 b" m+ F4 P
        u32 state;                        /* Hardware state i.e suspend */
$ ?3 f1 b- F0 U; S2 V        void *fbcon_par;                /* fbcon use-only private area */0 _5 V5 J" @6 R* L- d; {
        /* From here on everything is device dependent */
9 X' t+ X) y) P* W  N, z        void *par;        ' ?1 A. N' N& ?/ q
};, Y5 A2 A% F% v' B9 R) z" u
2.2 fb_fix_screeninfo结构; q& j4 C- d& J" ~! U
  该数据结构是不可改变的,也就是说用户空间不能改变该结构中的任何成员,在核心层我们将会看到这点。
; O. S1 D+ D1 j: P# v+ n
: a) o. \- n3 Z  下列代码位于include/linux/fb.h
2 f  M& }+ z1 i7 u5 H' r' V6 C) B8 B6 _' v
struct fb_fix_screeninfo {2 f+ n; \# e+ r
        char id[16];                        /* identification string eg "TT Builtin" */
0 e4 @. E" V3 }  ?. e( \        unsigned long smem_start;        /* Start of frame buffer mem */3 p+ M( |. c5 n' A
                                        /* (physical address) */
& s  c( B3 V0 P; H8 G# y& p* O        __u32 smem_len;                        /* Length of frame buffer mem */) E- q% M: \9 S
        __u32 type;                        /* see FB_TYPE_*                */
5 I# ^; {7 B3 v& k0 ?        __u32 type_aux;                        /* Interleave for interleaved Planes */
2 H; C* u  I1 N9 G7 x9 T        __u32 visual;                        /* see FB_VISUAL_*                */ 1 k: f1 f2 T. I# s/ A. s2 d/ R3 h
        __u16 xpanstep;                        /* zero if no hardware panning  */
) a  V1 o5 Z. W! l        __u16 ypanstep;                        /* zero if no hardware panning  */
' [& Q9 h2 q6 `/ `& n+ l, k5 h0 d0 I        __u16 ywrapstep;                /* zero if no hardware ywrap    */
0 w/ `9 O# r( N2 z# V; `        __u32 line_length;                /* length of a line in bytes    */
( y4 {  R3 R0 F9 [        unsigned long mmio_start;        /* Start of Memory Mapped I/O   */
) ?4 W* w0 z: A                                        /* (physical address) */. |3 l' i9 v+ t# O2 c/ Z- H+ A3 N
        __u32 mmio_len;                        /* Length of Memory Mapped I/O  */6 m$ K$ X1 n, U2 o9 q/ [
        __u32 accel;                        /* Indicate to driver which        */  y" w  e$ K) V3 F2 d
                                        /*  specific chip/card we have        */) i+ K- y& b: z: U% i9 }
        __u16 reserved[3];                /* Reserved for future compatibility */
1 J: D  E" f2 F- K& N5 ^7 _( _};
& y) T$ I( P$ r4 z/ K/ X6 e
8 z! n  g1 E* b6 D' }* z* }2.3 fb_var_screeninfo结构
; w* }5 }3 q. S( ~' y  该数据结构是可以改变的,也就是说用户空间可以改变该结构中的成员。该数据结构中的很多成员就由板级信息复制而来,在驱动代码中我们将会看到这点。# V- n8 e8 l, S

# |; z; E2 E  M) H  下列代码位于include/linux/fb.h
, O, d) ?9 i& K9 _$ R% _6 ]8 W+ Q# }/ z/ Q) k  c/ [
struct fb_var_screeninfo {/ _8 H- m' @0 _# Y
        __u32 xres;                        /* visible resolution                */. o! `0 D5 R  v1 n
        __u32 yres;
0 q9 ?. D8 M7 \9 V' [        __u32 xres_virtual;                /* virtual resolution                */9 |7 z& R: S$ a: m2 s$ a" a
        __u32 yres_virtual;
8 @% R9 K7 N. b! \( n) T% {        __u32 xoffset;                        /* offset from virtual to visible */% r) q, y" V: K' A
        __u32 yoffset;                        /* resolution                        */4 a) l/ k* ?; b5 P* [: }
. @8 v  T; v7 S# E% G
        __u32 bits_per_pixel;                /* guess what                        */
, n" I1 T. h  a; l6 g        __u32 grayscale;                /* != 0 Graylevels instead of colors */+ |4 u% W* H' c2 }/ o

5 S  H$ S1 T( |, Y- P3 B        struct fb_bitfield red;                /* bitfield in fb mem if true color, */
3 n, \' C2 f( o  P/ h- ~1 [7 k' A' f" ?        struct fb_bitfield green;        /* else only length is significant */
& H) p( N# H" u) W8 {, j  q        struct fb_bitfield blue;7 S7 e$ A; C8 O$ D3 |3 i; X5 y2 [
        struct fb_bitfield transp;        /* transparency                        */        7 v- Z; P- R+ T/ _* X" F0 h
  r4 C  T$ t8 H. b! V1 w
        __u32 nonstd;                        /* != 0 Non standard pixel format */
. @, A( F2 [6 \5 ]3 q) s8 c/ V* c' Q1 n- L
        __u32 activate;                        /* see FB_ACTIVATE_*                */7 ~$ C: ]& d  i: s
8 q* A! H2 a" r6 W! q9 C
        __u32 height;                        /* height of picture in mm    */
! w$ d8 ]: f! ]3 k3 v& |5 m% H: G        __u32 width;                        /* width of picture in mm     */1 Q' }5 I- z' X7 y% K& e, q( t  _
: @' \3 }* g0 S3 o5 E* v$ ]9 v
        __u32 accel_flags;                /* (OBSOLETE) see fb_info.flags */1 b% s" B6 X7 s

! x- U: V& }- L# R% n" [3 l        /* Timing: All values in pixclocks, except pixclock (of course) */
3 w- ~2 E. x) g" }* K6 @7 x- }        __u32 pixclock;                        /* pixel clock in ps (pico seconds) */
4 K4 ~3 E6 Z  j! A' K        __u32 left_margin;                /* time from sync to picture        */
2 @( S) W8 @3 M0 I5 @+ _        __u32 right_margin;                /* time from picture to sync        */- d3 e8 f( w) s2 G& u
        __u32 upper_margin;                /* time from sync to picture        */! z( a# l& z- C% x& f# ^
        __u32 lower_margin;0 D$ z, v$ ^2 o) L
        __u32 hsync_len;                /* length of horizontal sync        */
: H0 |& L8 t$ U0 M* c8 k& I5 L        __u32 vsync_len;                /* length of vertical sync        */$ p+ ]4 L% R! q; q$ g5 p
        __u32 sync;                        /* see FB_SYNC_*                */( V- ?! J/ u+ s- j0 U5 i# v; @
        __u32 vmode;                        /* see FB_VMODE_*                */
4 {% n* {7 h& C7 v* f        __u32 rotate;                        /* angle we rotate counter clockwise */% R( F- e" d7 H3 @8 y
        __u32 reserved[5];                /* Reserved for future compatibility */. h% o" f1 a/ |
};
) O4 ^' S. b. u6 e8 c/ \/ j2 N( i8 a" U6 r0 ?+ L- _! j* f: _
2.4 fb_ops结构
$ f; F' U1 S& a) O  该结构描述了用于fb_info的方法,这些方法中有些是要驱动程序提供的,而有些可以使用内核提供的方法。
0 y3 \$ Q: F! S5 Y
3 K7 G5 a; w) t 下列代码位于include/linux/fb.h
8 M& T9 Q0 G2 }2 V/ ?4 p- |
6 @! Q$ l. [/ [% L4 {; G! C6 E/*% @/ Q0 @! c9 e9 e8 r, I
* Frame buffer operations: k  x2 ?; ]2 t
*
; U2 i) ?+ j  M9 F  I' U0 z * LOCKING NOTE: those functions must _ALL_ be called with the console" V7 `' Q* r# R' ?
* semaphore held, this is the only suitable locking mechanism we have+ R3 m# N2 a: w3 r( C( t) A' ?
* in 2.6. Some may be called at interrupt time at this point though.
! [) F6 V% @# v" f+ N */
7 q4 F" u7 p; z9 m( Q, d' b* _6 P% Y/ g1 I$ A
struct fb_ops {
+ H; w( L8 C3 W6 Y! W, a3 c9 y# w# t        /* open/release and usage marking */
/ {( u9 T4 X: x: [# C# p( O$ V; p        struct module *owner;
0 F# B, }% k; W5 P7 g2 s; i        int (*fb_open)(struct fb_info *info, int user);( C! h2 I  r! h% C4 H. `2 s
        int (*fb_release)(struct fb_info *info, int user);
- P1 X" n# a2 N- D! c1 o1 N
2 _* O; u  M; {7 T        /* For framebuffers with strange non linear layouts or that do not
' C9 ]1 F3 g$ Q         * work with normal memory mapped access
# p5 S: A9 u2 L; i) D2 j         */% O9 W2 }& B# p- |6 g
        ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
/ J0 T; D  m. j" |- m7 E                           size_t count, loff_t *ppos);5 H- [) ~$ f3 p  U" P  s9 B9 e! X
        ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
6 @! j: W9 }6 U' f! P* F4 U. x                            size_t count, loff_t *ppos);. ]0 s* T" F5 u' z5 x

5 M6 E9 h' ?  P0 h! X& X        /* checks var and eventually tweaks it to something supported,
7 n' W( i% ?6 c( z3 Y         * DO NOT MODIFY PAR */, V2 F0 u, `8 i& R# w5 N* _
        int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);; P- x+ ~" U; Q  J' V

# t, o+ w8 p" k$ k- r8 j* L6 T        /* set the video mode according to info->var */, \5 K+ v. ^$ H  ?
        int (*fb_set_par)(struct fb_info *info);
$ s2 [& s0 g! |( X) S! ]5 ~
8 S0 E; n" J" C$ U2 v& s        /* set color register */( a, d  R) g# x# A( S) ?+ Q* \
        int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
- @/ W' D4 l+ k                            unsigned blue, unsigned transp, struct fb_info *info);/ C5 Z' e5 L; o' W/ Z
, S( ^+ ^" P5 @0 n8 l' W4 M( v
        /* set color registers in batch */8 o; w3 e- i; n4 `- N
        int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);: E& Y- {' W" G( R/ Y

2 n' t* d( M; {        /* blank display */+ p& v" {, }- y) L
        int (*fb_blank)(int blank, struct fb_info *info);/ j' h) Y" n0 N! ]0 M; @0 n, A
( G1 G8 X9 s& b( H
        /* pan display */+ k" S4 J- }" f% w
        int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);: F( `& `! f1 t  P

4 A$ S; N' t3 M' b$ n        /* Draws a rectangle */) r. s0 f* m, A, b# w+ s1 F
        void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);+ x" f0 R- z% q2 B. I  l5 V
        /* Copy data from area to another */
; d  ^% [# [1 L        void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);, N/ O/ Q" S4 {4 r" Z' B
        /* Draws a image to the display */4 V0 z3 \% F" e4 q2 `2 n; _
        void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);  H; q2 H9 V6 m* v. G9 ~

' ?& X9 j0 i3 z; }        /* Draws cursor */# C# Q) ^; a1 C5 B) E, ^: o
        int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
" w; }  u1 b. ]' T3 X
) |% i5 _1 S/ r4 R2 m+ G        /* Rotates the display */
) p$ Z7 r1 r7 r6 @( h        void (*fb_rotate)(struct fb_info *info, int angle);
) \% s" W" F  B0 z6 x+ v; g; f5 v- ?( N/ @# H
        /* wait for blit idle, optional */8 d) O9 _0 h2 _( J& ^& J
        int (*fb_sync)(struct fb_info *info);# z0 H& P# c: k
7 w5 e$ C; D6 c+ i
        /* peRForm fb specific ioctl (optional) */8 h6 X6 R7 g! ^4 j- w6 m
        int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,6 d1 l9 _3 Z0 h- n9 f, H% k
                        unsigned long arg);
6 n4 ]5 v! h+ b" H/ x
. j* Y# c0 c7 D) t" ?4 ~        /* Handle 32bit compat ioctl (optional) */
/ v' E; e7 B9 m' K        int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
! I( D9 G7 m. e3 y0 g                        unsigned long arg);
" z. J3 O# R9 g2 F8 H  e; j0 T! y. r
        /* perform fb specific mmap */
- K; B- W3 h0 A. A1 Q( B- [6 B        int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
8 N" J/ O6 Y- V9 K# n; X
$ G9 i3 O9 a8 ~8 }2 G, P# V        /* save current hardware state */0 S! r+ y1 _( Q; N
        void (*fb_save_state)(struct fb_info *info);) a1 t% S  R! U! M6 U2 j

# t# P, l' e; r7 z        /* restore saved state */' H' e+ h5 q2 M5 G, D! U
        void (*fb_restore_state)(struct fb_info *info);! S" ~: y' {7 G6 {( K% b  z
0 {# m- H* C  z* O7 t
        /* get capability given var */1 k/ P+ h+ v8 B" J! m/ A& g
        void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
8 Z3 p5 W/ ]3 `$ [# O2 _$ F                            struct fb_var_screeninfo *var);+ I& H$ w  \1 Q  b7 h1 L
};/ H6 H  N, g. J& C( s

  R0 I2 i( N( P8 f$ R$ s) \6 w9 v: b- U, H
3. frambuffer核心层3 S  ^* a, n1 @7 T6 l& |; }
首先来看下frmaebuffer子系统的初始化函数。
2 q' L. l3 M4 m3 y6 j+ @$ G) a7 {; }
3.1 fbmem_init和fbmem_exit
. K9 @& E3 q& p$ m. j* ?" ]7 z下列代码位于drivers/video/fbmem.c
7 c8 {4 O3 q; h/ U: y( r; w
8 ]3 q4 O; O1 B. V/*** p- o  X8 W2 @- `0 ]
*        fbmem_init - init frame buffer subsystem7 q4 z9 t, a1 s* C) t
*% j4 }- W4 W/ t* [
*        Initialize the frame buffer subsystem.4 N2 @0 ~) s! Y) T
*5 X: t9 C1 L& x8 e; E5 x
*        NOTE: This function is _only_ to be called by drivers/char/mem.c.
! e6 {: i) y1 m *
' D+ q9 Q4 B% j% e6 R */
2 r2 p1 R% u6 e! f- E
  F' E% f$ v- ~1 R9 ~static int __init+ c( v. |3 `2 n; J9 a8 o
fbmem_init(void)
0 O# J$ {  ^3 X3 d7 n+ J, m{% m' E4 F: ]) f$ S7 H$ B
        proc_create("fb", 0, NULL, &fb_proc_fops);9 ?  c; o3 J# i$ r
7 Z+ d. j0 F/ D9 k+ n4 n# G
        if (register_chrdev(FB_MAJOR,"fb",&fb_fops))                /*注册字符设备,major=29*/
7 Y+ x# l# l- b8 y4 W                printk("unable to get major %d for fb devs\n", FB_MAJOR);5 h9 L" A0 ~2 D# c

+ D& R1 s3 Q6 a, M        fb_class = class_create(THIS_MODULE, "graphics");        /*创建类*/6 l5 N9 ]1 }# s! @1 q; y$ g
        if (IS_ERR(fb_class)) {
2 ?2 |- S- d4 ], L9 y                printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));* `' s8 f8 X3 K
                fb_class = NULL;
0 |! ?+ K$ H6 |+ @        }1 L6 M' }. @# U' w
        return 0;2 Y( m+ D( p! f
}
! M9 t4 k( f; E. T7 s
. V& D2 U( K4 S6 Y6 I" q6 Y: E#ifdef MODULE9 Q2 @, ^! U$ C/ |* h, _) V
module_init(fbmem_init);
" z- c* v$ o1 s2 k8 v, d( ^6 Fstatic void __exit
5 d- h. ^6 e0 t: `  F3 ]4 |7 }! D; v3 Dfbmem_exit(void); l. x& O2 s9 A( a
{/ `: [. F0 u6 W$ Z- K
        remove_proc_entry("fb", NULL);
* c8 ^2 ?+ u* B* l        class_destroy(fb_class);
7 Q% R, U4 m0 `, `        unregister_chrdev(FB_MAJOR, "fb");6 P$ C: C, G- k; H- c
}& m3 T) n1 O6 E- |5 Q* q0 ]9 q3 O% n
0 r/ b% n7 q# M2 n/ Q4 S0 b
module_exit(fbmem_exit);& _* i. U" X, C1 B3 _7 w1 M
MODULE_LICENSE("GPL");
2 i5 E& y+ b1 H6 UMODULE_DESCRIPTION("Framebuffer base");7 m6 c" R- L3 s* d; t8 b
#else
7 [, p# p, y1 x( Rsubsys_initcall(fbmem_init);
" h9 H  I; B7 W0 S+ y#endif
  q1 }8 b5 N" l4 O/ u8 c  x8 ?) ^) L
static const struct file_operations fb_fops = {! m9 b& {5 a% B" N" S
    .owner =    THIS_MODULE,
% y) V% ^" z- p" Z: v2 Q8 W& p    .read =        fb_read,; e/ ]* M$ I- w9 M& w2 M% T. x; k1 r
    .write =    fb_write,% q1 n1 w) [: E3 S' `( J4 j' j
    .unlocked_ioctl = fb_ioctl,
' \4 c2 ^: @7 g#ifdef CONFIG_COMPAT" K3 E3 c# _; _$ U+ r" @
    .compat_ioctl = fb_compat_ioctl,
. ^9 {# @# @: y, Q/ @#endif% ]6 S! m* |1 \- F
    .mmap =        fb_mmap,
2 }( ^. T4 ]& R+ ?2 q    .open =        fb_open,! a" o8 `6 ^7 a; S! m6 a
    .release =    fb_release,
. T" R4 ~6 G5 E: I: b/ M" ^#ifdef HAVE_ARCH_FB_UNMAPPED_AREA( G, k& e8 L' v. [/ ^
    .get_unmapped_area = get_fb_unmapped_area,
8 ^" O% \1 z. N& i8 x7 v#endif' G* B( z" o9 W! m4 c) d/ J
#ifdef CONFIG_FB_DEFERRED_IO) V, @( l1 ]5 B7 V$ a( `
    .fsync =    fb_deferred_io_fsync,8 R$ K/ p" G3 n# A; h, v
#endif
+ F3 K7 s2 V. ~6 a: ~' f# ~5 l};: ^+ ~) G# P7 t/ ~! z. H8 I" s

% K* [0 T: P  Z  _! u- M我们看到,如果不是作为模块,那么该初始化程序将在subsys_initcall阶段被调用。初始化时,仅仅注册了一个字符设备,并创建了一个类。通过字符设备,提供了API给用户空间,包open,release,write,read等。
; H8 o% e# n. q0 i2 |! ]随后我们看看如何分配一个fb_info结构。
' W" p9 E/ r9 P6 i9 b% N$ v8 q, E
3.2 framebuffer_alloc
* X9 p0 c- R; {- \2 H+ }" K2 R下列代码位于drivers/video/fbmem.c
9 T5 Z: Z' @$ ^, z2 Y. |' X
/ Q3 C! m$ `4 r: r/**
$ e) @) A* I2 G! N! Q * framebuffer_alloc - creates a new frame buffer info structure
( F" K# G$ U+ B *: y2 [0 g5 p9 I
* @size: size of driver private data, can be zero7 {: U/ S+ {1 e
* @dev: pointer to the device for this fb, this can be NULL
, Q. Z6 Q, n' ?" w) H0 C2 E *
! t' k+ d6 Z/ N# d1 ~  F4 _ * Creates a new frame buffer info structure. Also reserves @size bytes2 y! B$ R. k! P7 Y# U+ x2 i5 c
* for driver private data (info->par). info->par (if any) will be
0 K& w6 e6 H* S- [9 G1 r  K; [ * aligned to sizeof(long).
! k/ q6 `9 [6 b- Q/ k' V) _- C *
- n7 E. |4 V. }9 K  b * Returns the new structure, or NULL if an error occured.
5 a) G0 s! d2 D: n/ R *6 t% n0 J3 S) A8 i4 J! [
*/
( \! y  R' N1 B1 K" \  [/ wstruct fb_info *framebuffer_alloc(size_t size, struct device *dev); R/ m% C* ~5 \  x  c2 H$ g1 a
{
  l% S$ a$ n/ B5 t$ E3 G% Y2 W& y7 [#define BYTES_PER_LONG (BITS_PER_LONG/8)
0 L& H; D; Q; o7 W; C8 O#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))7 ^* _7 O) _. n8 R2 z5 V% O7 A, F
        int fb_info_size = sizeof(struct fb_info);
; l! Q- |1 a0 ]( e8 E        struct fb_info *info;
% z$ d  `+ H' i" J9 X3 r        char *p;
% M0 g/ Y5 Q' Z; _! M# p7 @! \: P4 y1 S
        if (size)! V2 A3 w1 M0 a1 q) w5 ^
                fb_info_size += PADDING;
+ |& n- z3 Y9 o7 V3 @3 O3 d0 Y4 R; Q
/ ~; \  i% S# f3 X- o        p = kzalloc(fb_info_size + size, GFP_KERNEL);  _: g5 S  s: n9 L- j2 l

% q! o4 u9 L3 v5 x        if (!p)
% Y3 S) Q7 h! [3 z                return NULL;
5 ^  _& R0 m5 u( V( N
0 u+ q0 \2 ]# K) H* Y) J% e        info = (struct fb_info *) p;! y5 {5 Y3 G  h* ]9 n5 A
7 Q; |7 r, f" A) l
        if (size)0 `' R- A# e, A; y  g# K  {+ i
                info->par = p + fb_info_size;( w5 C" c& B. j% @, v7 _$ _

, Y% y& W( Z5 F        info->device = dev;$ Z6 }5 j+ _9 R+ x

+ e, B% o  Y8 ?9 E# @#ifdef CONFIG_FB_BACKLIGHT  A  {% s# h2 t* u
        mutex_init(&info->bl_curve_mutex);
9 z8 L' S5 r1 v/ |0 N( }5 u0 }#endif
/ S& `5 Y5 ?0 S: Q# d  W
2 J3 d; U0 @% g; }% J$ E        return info;$ c8 k* l( q$ F2 D
#undef PADDING
% n( U/ K* W/ m& u#undef BYTES_PER_LONG
/ w$ x: ^7 }; f# Q}" p4 J, \/ J1 q7 L# i, s) p
EXPORT_SYMBOL(framebuffer_alloc);/ N9 z' w8 Q, ?. N- X1 M8 f5 E2 O
在进行分配时,根据参数size的大小,分配了b_info_size + size的空间,然后让fb_info->par指向size的空间。因此par所指向的空间可视为设备特有的数据。; [% L+ h8 M( u+ B4 l
在分配了fb_info结构之后,需要将它注册到内核中。注册由register_framebuffer完成。我们来看下。) M2 `, c! h6 B1 V3 H+ v# e3 a9 T$ g
" m$ g3 \9 S) C9 E( Z
3.3 register_framebuffer
% z+ ?0 x/ }( v8 m6 }下列代码位于drivers/video/fbmem.c
! ^% M1 ^2 j2 E  M1 W
7 m. k1 \0 q- \  V# u/**
5 M& n" P/ x/ s6 A *        register_framebuffer - registers a frame buffer device
+ T$ E4 j. M) k1 T1 P: W* w *        @fb_info: frame buffer info structure$ E' s0 i% [% T  ?) S' C5 L
*9 F( x* n/ W  G( z' U5 \
*        Registers a frame buffer device @fb_info.
" L4 R. v& Z7 L- W9 y, b; Q- w0 u *. n8 V2 h, S1 r7 P
*        Returns negative errno on error, or zero for success.1 a6 y" H" m' B0 S: |! c
*. i' Q9 M2 S4 m2 Z8 d( x) F
*/
% _0 l/ W+ P$ Y6 f/ W- r; Z( Z- }  w7 m& Z3 ^/ s$ y9 X
int7 N. X; d1 a3 E
register_framebuffer(struct fb_info *fb_info)- B  d( w0 `3 J2 z4 p
{- M/ a+ E* l  p) ]6 c! Y. G
        int i;
+ `  A% ~" t0 Z* Z2 I% I5 q        struct fb_event event;
7 z. U. l3 D( A9 d; |4 `+ ?7 B$ G        struct fb_videomode mode;
+ Z& a1 `. m/ R1 q' i2 H- @' _& V- J: F* s
        if (num_registered_fb == FB_MAX) /*最多32个FB*/7 b/ F5 e/ A8 P0 F
                return -ENXIO;4 s& y( u8 m) u' f* W! k  V

+ ~. }- |7 b* W7 {, F- d        if (fb_check_foreignness(fb_info))
% O( P+ i1 g' u0 h1 k, ^                return -ENOSYS;
, A" Y! D  v( j7 n' C! l
9 A/ m( b+ G4 c2 a        num_registered_fb++;                /*对注册的FB计数*/, |( k7 v. }3 h. X
        /*寻找第一个空位*/. @) w0 E. c/ k* U: G1 F6 I
        for (i = 0 ; i < FB_MAX; i++)/*FB_MAX=32,也就是最多32个framebuffer*/& A1 w4 m' M4 z! y
                if (!registered_fb)        /*struct fb_info *registered_fb[FB_MAX]*/# k1 D9 u' {; r
                        break;8 i$ z( f  J, P0 |# O
        fb_info->node = i;! R) {* `5 `5 Q! V
        mutex_init(&fb_info->lock);        /*初始化互斥体*/
' f( ^& B: ~# ]# R4 s0 L1 `+ y# l5 y; [. P! u' J5 v
        fb_info->dev = device_create(fb_class, fb_info->device,/*创建设备节点,节点名为fbx*/
/ I( \1 A$ [# T1 l$ j                                     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
* z8 V; c9 @- [; i3 d% s        if (IS_ERR(fb_info->dev)) {' e& O0 v- S, `0 z& ^& O0 `
                /* Not fatal */! ?8 X0 [) _' Z6 A% e3 c- e) v$ R2 g
                printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
1 i# p0 J4 o/ U  ~& a                fb_info->dev = NULL;* K1 V! u3 @, E/ v9 r
        } else
. O8 ?) r2 e# i- o3 z  q                fb_init_device(fb_info);        /*初始化,在class/graphics/fbx/下创建设备属性*/
, ^3 F& b) t+ V4 U+ r8 x6 p8 m( Q) w- V7 D
        if (fb_info->pixmap.addr == NULL) {
/ \( N- |( ~' p' h3 ?                fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);/*分配内存,1024 * 8字节*/
1 F2 F7 k8 j9 F5 y+ ~' n- F- B                if (fb_info->pixmap.addr) {
9 c* |+ d. Q$ \' i) e# f                        fb_info->pixmap.size = FBPIXMAPSIZE;
& q+ f5 `* _9 t. x% t# p, o# {                        fb_info->pixmap.buf_align = 1;# }3 C- J) l( b9 }) \
                        fb_info->pixmap.scan_align = 1;3 Z. @+ D" w; Y; v% ~
                        fb_info->pixmap.access_align = 32;
: \3 t5 m, y5 C- ~                        fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;$ r  m- `4 c3 s5 ~3 C- b
                }. Q+ g/ @1 {0 H' w! R
        }       
; F: w6 r( \5 ?4 T' v+ O- ^        fb_info->pixmap.offset = 0;
! H( n$ N% c. i2 V; u0 s9 k# e1 e( K. q! ^
        if (!fb_info->pixmap.blit_x)0 l/ I2 Y- `- _8 w) j" U7 _
                fb_info->pixmap.blit_x = ~(u32)0;; ^6 W# P* y) E7 i$ O7 m" V& T

: r: @! T+ `' w# f  @5 F  y6 ^+ O) c        if (!fb_info->pixmap.blit_y)
2 k* e' ?# p+ H! Y* e" @: u! |: \                fb_info->pixmap.blit_y = ~(u32)0;
2 D; I+ V" S6 _- G% m- |2 I8 i% y& ]% ^. u) l' S
        if (!fb_info->modelist.prev || !fb_info->modelist.next)        /*该链表没有指向其他节点*/
2 j, b0 M8 G8 J                INIT_LIST_HEAD(&fb_info->modelist);        /*初始化链表头*/
" p+ j& a9 a" O
. w' Q" S7 A3 C        fb_var_to_videomode(&mode, &fb_info->var);/*转换fb_var_screeninfo成fb_videomode*/
0 e  \* a% l5 d5 e        fb_add_videomode(&mode, &fb_info->modelist);/*添加mode至链表中*/
! q/ ~4 C& u' ?) W2 f) t) \8 x        registered_fb = fb_info;
0 l: B# s# P, j9 D/ `! m" S$ S$ m  R& w3 k
        event.info = fb_info;. P3 d7 C: I/ A! ?5 i; M9 U) O
        if (!lock_fb_info(fb_info))
  k1 ~4 c% R" |' M% {/ N                return -ENODEV;4 g/ M2 H$ y) ]! V) p
        fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);/*???*/
6 w$ j1 B7 ]& N        unlock_fb_info(fb_info);
5 G- y- W$ M8 [) z( p4 U        return 0;
2 |# B) f' ~0 F* ]4 I( U}, g9 x. H  d! v* J, I3 o( L

/ Y2 W9 w9 R/ Z9 _  u9 Q  K# |. U从这个函数我们可以看出,framebuffer子系统只支持32个设备。在创建了设备节点以后,建立设备属性节点,随后将fb_var_screeninfo转换成fb_videomode,最后添加fb_videomode至链表中。& U8 X: D6 b$ C
我们看下其中调用的函数,首先是fb_init_device。
& O5 J: X$ c6 ^
0 T/ U* c; }: {- ?9 {下列代码位于drivers/video/fbsysfs.c
- i! A3 I" e7 i) M) J" O% l: y8 B- P9 X/ |4 M
int fb_init_device(struct fb_info *fb_info)
: U# u9 ]/ I$ A; o# d" F{
8 c! c, L6 ?4 I, P( J3 b1 }: P& V/ f        int i, error = 0;3 T  v! i* b1 Q/ r4 V* J3 w8 m3 _5 s

& f: T4 E6 E0 C# S# O/ q$ J        dev_set_drvdata(fb_info->dev, fb_info);
8 A1 q. |9 e0 }( i* c2 T
6 k$ ?0 s; [' q. ]        fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;+ P4 n2 F3 p2 Z) y; w: N! U
% q# |$ Y6 P% {$ Y1 ~
        /*建立设备属性*/# }/ d9 K* t. F8 m( C$ ?9 w
        for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
  }  o( Z* r: ~. I                error = device_create_file(fb_info->dev, &device_attrs);
4 n* k7 [- e+ L# m8 |2 \8 `8 T5 N
4 p; F. P: b7 S% v3 {6 P                if (error)2 r" n4 I$ Z  R) T% k/ u
                        break;
6 w( |) W4 `& T$ J; \" H: i9 ?( g1 S        }1 `7 Q1 P+ [/ O: y! b

: ~* |. c" @! A  e  _2 A; l. z        if (error) {. z0 Z4 b, q  N3 ]8 R8 E0 y' P
                while (--i >= 0)' y2 M: @: ^5 c* X/ l
                        device_remove_file(fb_info->dev, &device_attrs);9 e, S2 Y+ p1 v$ R  D  d
                fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;" ]5 T" p  G- {
        }
* x' z! d! V; E1 f" H) F" t% W! @1 n9 x/ S- ~* \6 l/ R  W
        return 0;
- \; U/ H2 Z, G}
/ v. p: b* R4 l$ A- S" [! ?
: D1 w4 u5 U, g; t5 x0 g* H, ]8 R3 Z/* When cmap is added back in it should be a binary attribute3 Z/ s) h2 C/ M* S  O
* not a text one. Consideration should also be given to converting% \+ m  w  P, U9 ~& d
* fbdev to use configfs instead of sysfs */9 ~& ~; ?- w% Q# ~1 _
static struct device_attribute device_attrs[] = {: K. |5 B- q+ {" a( ^3 P, |
    __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),3 B/ d/ N6 O/ m
    __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),/ ^: `& U' @6 N( N
    __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
3 w/ |6 h6 W' Q6 y    __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),5 L( R4 v8 F  A5 I  X' m+ ~/ S) c
    __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),  J( I0 K# m0 M5 X( j/ {4 Q
    __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),( \( M2 O2 A9 B8 m6 s
    __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
" i, u( Z3 _! @8 A    __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),, l; h- [+ J; R/ d
    __ATTR(name, S_IRUGO, show_name, NULL),9 G% `, L) N# M. K" s
    __ATTR(stride, S_IRUGO, show_stride, NULL),. p! `. U4 X4 T1 y5 j, o, V* C
    __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),8 C( ~/ D- r; m1 x7 o, j  u
    __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),3 y, d: U8 {3 E. S: Y" C
#ifdef CONFIG_FB_BACKLIGHT. C8 G" |7 _0 N, ^# J7 d
    __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
: H* W( t* S6 `. w+ _! h+ I/ G#endif
' P- H3 B# @  l3 H};
0 Z; L9 n; Q8 B9 N4 B  N$ u8 ]; U4 g5 C$ j0 o8 q- K
我们可以在/sys/class/graphics/fb0下发现这些属性文件。4 _1 Z0 I4 z$ I8 v6 W2 u
[root@yj423 fb0]#pwd( q) U- N' Q& h7 C
/sys/class/graphics/fb0
& I5 Z- w! M2 b" q[root@yj423 fb0]#ls+ k( |. j" U% T0 M- c
bits_per_pixel  cursor          mode            pan             state           uevent
: \; a, W! j$ Hblank           dev             modes           power           stride          virtual_size: b; p; b- b# O+ e' ]7 @
console         device          name            rotate          subsystem
0 f$ I! z$ A/ f5 }% Q! F2 r2 w: J/ i7 h" Y) O; u+ `9 j
' C- p* i: P. D6 U/ o
接着看下fb_var_to_videomode和fb_add_videomode函数。
5 D3 z' P7 ^$ q+ f
$ }' V" Q) O) Y1 a下列代码位于drivers/video/modedb.c和drivers/video/fb.h
" s. H- K  c: t9 L2 f, M& i5 u7 U2 T: L
struct fb_videomode {
7 P0 }" G& H. Y) _. w        const char *name;        /* optional */' j4 {) Q! J6 a5 J& R  F! \
        u32 refresh;                /* optional */
9 a- A9 \/ p, M        u32 xres;
" K4 w; ?" L1 i+ J2 g2 o4 Y& I8 {        u32 yres;' N7 c4 n1 G5 p/ W/ x5 r
        u32 pixclock;
: m6 x+ n1 i' G7 i) Z- h0 D+ ]+ E        u32 left_margin;3 F( i  u# G* D) }
        u32 right_margin;
6 E: n7 y, s: y1 K) V6 H' a8 `# M) u        u32 upper_margin;3 N8 c' R% J/ B  `& x3 P% ^' N
        u32 lower_margin;
/ {2 z7 g4 r9 u4 H5 G- O        u32 hsync_len;8 t+ j, J! m9 v! A
        u32 vsync_len;$ _6 e. O+ Q( B
        u32 sync;6 W0 Q: m( ?; I; K; _* ~* o- o
        u32 vmode;
) p* V+ a1 g) @1 @6 ~        u32 flag;/ S4 t5 z$ s/ ]
};
% y2 z- n3 R; d  S- ~) Y% {) h5 S1 {9 f2 Q
/**
+ J2 \! E# X" ~8 |, _; j0 O$ @3 r* {' e7 L * fb_var_to_videomode - convert fb_var_screeninfo to fb_videomode( X( T* r- a7 m9 n. a, ?
* @mode: pointer to struct fb_videomode5 z, o- O/ P. K; P
* @var: pointer to struct fb_var_screeninfo
6 L/ H! z1 y/ n. O8 Q1 w$ R! J) h( v, d */
6 J+ B: y7 R$ k7 G* Uvoid fb_var_to_videomode(struct fb_videomode *mode,
/ r8 c0 M, U( [             const struct fb_var_screeninfo *var): W+ A- i4 I; |8 c
{
1 w$ U8 z9 j& W, O6 c    u32 pixclock, hfreq, htotal, vtotal;
; M( N0 v, F' l4 l) ?3 v$ f
; |  }5 V6 B) F5 N& x5 e    mode->name = NULL;5 d# h7 g; A6 |+ L+ I! n7 _- x
    mode->xres = var->xres;/ a5 E. j  X% R$ m! z8 c/ b
    mode->yres = var->yres;% \) {0 Y- c9 K6 ^# T
    mode->pixclock = var->pixclock;
* G1 P* b7 i- i, D1 U! w    mode->hsync_len = var->hsync_len;4 x- }8 m7 U" o: `0 K7 G0 e
    mode->vsync_len = var->vsync_len;3 K, L% Z+ G/ W" Y& S8 u
    mode->left_margin = var->left_margin;$ o  }9 S& q* }& S) l
    mode->right_margin = var->right_margin;
  b( A: S+ o- \9 {! x    mode->upper_margin = var->upper_margin;  g) {4 F8 a7 }  f4 z+ }
    mode->lower_margin = var->lower_margin;
) C+ x! m  L* |' X5 x    mode->sync = var->sync;
2 H% M2 Q( i/ O9 Q) n" a    mode->vmode = var->vmode & FB_VMODE_MASK;" o% d  i* k' }% ?1 g
    mode->flag = FB_MODE_IS_FROM_VAR;$ j" u4 o2 P5 c6 y2 S3 v% j$ }
    mode->refresh = 0;7 X3 Q- U# D- I6 N9 F5 Z
1 D0 W" b8 P( e: x! e  s& ]$ y
    if (!var->pixclock)
9 _, ]' @. Z; Y/ u        return;
' I+ b6 ~7 E6 F( u. A2 [0 S8 X; q0 d  U1 T) k4 b* Z; \
    pixclock = PICOS2KHZ(var->pixclock) * 1000;# T4 q8 ^. E3 f3 s' t; m

1 T) Y% H+ K* J/ m1 ?" }4 }; @    htotal = var->xres + var->right_margin + var->hsync_len +
' }' C4 Z( b% ]" q. `4 S& G        var->left_margin;
" K% I6 o; e' [0 q: o5 P. ]    vtotal = var->yres + var->lower_margin + var->vsync_len +5 p2 g6 @' X; u
        var->upper_margin;2 w1 d* s% }- [: e, D

* ^/ [% x+ K$ K  \6 w, i" P    if (var->vmode & FB_VMODE_INTERLACED)+ T$ H; w3 J& t' y
        vtotal /= 2;
* n. u& C9 S9 V- H8 g6 A3 S    if (var->vmode & FB_VMODE_DOUBLE)
$ c, ~4 i, ~0 m* }% m        vtotal *= 2;; H" b) w8 a$ D* K% r7 E
1 h1 H- r% D' Q4 _) Y$ h
    hfreq = pixclock/htotal;0 p. w- E* N* ]. N, v
    mode->refresh = hfreq/vtotal;, p& |" P5 N- Z" }# [* j
}6 l; b5 H( U5 T; b  C" x

( ~  b/ l1 Z' w. Q+ J/**/ @5 Y* ?$ E/ T* p
* fb_add_videomode: adds videomode entry to modelist8 D* E' m2 l' p. U/ E: S6 p7 q
* @mode: videomode to add
- o7 B- u7 N6 t5 z- I * @head: struct list_head of modelist
2 w- y0 f: f2 z *
, f1 ?! T6 P) P * NOTES:% s' Y1 c" }( \2 i7 _1 }
* Will only add unmatched mode entries
0 A/ e' M1 Q; q( G% M' t */. Z0 X$ }3 D4 M4 h$ v$ b4 K
int fb_add_videomode(const struct fb_videomode *mode, struct list_head *head)# ?4 v4 }* W! X: F
{8 G( b, z! O. H2 e( U
    struct list_head *pos;
+ J* H/ Y" L2 s* W3 D; A1 Z    struct fb_modelist *modelist;d; x2 F+ |3 v0 ]8 p( \/ j
    struct fb_videomode *m;
8 ]0 x  g9 a" E7 K) O" }; M4 }& l0 B    int found = 0;
4 b- {( k9 y3 c' _" P    /*遍历所有的fb_modelist,查找mode是否存在*/8 a8 M/ ]( Y& `# }0 C) r
    list_for_each(pos, head) {
3 |5 R# O% X$ r0 l# I        modelist = list_entry(pos, struct fb_modelist, list);3 p3 X% d+ }6 [2 ^$ e( Z# h9 l
        m = &modelist->mode;
# n. u# p3 h( ]; @' A9 a" f        if (fb_mode_is_equal(m, mode)) {    /*比较两个fb_videomode*/+ \- \7 p% s, g# {3 [
            found = 1;                        /*该fb_videomode已存在*/
5 Z2 p4 H2 i( L/ o8 C+ O# Y  m# l            break;$ d* u  F0 f9 \* O) W7 v3 R" c
        }
( u7 H) ?6 k) ^: `& g' l+ m- i    }. e& J! m9 W) K! Y6 c; l
    if (!found) {    /*不存在*/5 e8 E, r1 Q9 |  V
        modelist = kmalloc(sizeof(struct fb_modelist),    /*分配fb_modelist*/2 B2 s( V6 q2 B1 G0 j
                          GFP_KERNEL);
: D$ A6 O" ~, c. ?2 v2 X6 S
  O, C8 c. x! w" V% C/ ^6 Q0 F        if (!modelist)% c8 I( y! v9 U% W0 H$ @
            return -ENOMEM;1 u" [7 [5 k7 R8 w) g( ]
        modelist->mode = *mode;            /*保存mode*/
5 Z. q9 v, h+ T1 e6 o        list_add(&modelist->list, head);/*添加mode至链表中*/
, {" G4 Z: E$ J! H    }3 {1 l, r* o2 z! t( f3 M3 u
    return 0;3 p' w4 d3 B& W8 y2 b# T3 ~
}* f2 s! H, T) X7 ~( i9 X) Q
8 z4 i4 E/ E& S
/**
* [- Z* ]+ S) [# q * fb_mode_is_equal - compare 2 videomodes6 N2 M, `' W# f$ i6 E
* @mode1: first videomode
) N" w2 @! o4 a * @mode2: second videomode" b* Z/ y4 j5 I( t+ o9 s" F# I
*. f0 R& i2 X5 Q
* RETURNS:
3 p" w9 _( R6 }1 G, I  I; |6 e7 F * 1 if equal, 0 if not
$ M+ ~* e% I1 M3 n */& X3 u' H( J6 u$ L
int fb_mode_is_equal(const struct fb_videomode *mode1,( r1 v5 Y7 U3 ]: c
             const struct fb_videomode *mode2)
. D! q7 R1 U9 I# w/ }8 e. |{, u7 C% U* I" u+ @
    return (mode1->xres         == mode2->xres &&5 f0 o: b- M6 P3 z
        mode1->yres         == mode2->yres &&( n0 U/ o4 Y1 v% Y; b
        mode1->pixclock     == mode2->pixclock &&
6 @! s, C! b9 t2 S        mode1->hsync_len    == mode2->hsync_len &&! X9 `3 Y+ K5 D% c! n! A
        mode1->vsync_len    == mode2->vsync_len &&  ]8 Z6 j! C) N' N. @1 A% Q
        mode1->left_margin  == mode2->left_margin &&; _% V) x: o' d1 k8 f
        mode1->right_margin == mode2->right_margin &&: G' O1 e+ a8 n( o7 q: Y2 e
        mode1->upper_margin == mode2->upper_margin &&9 d5 A: f& B, V) B; G7 N
        mode1->lower_margin == mode2->lower_margin &&
- |2 V8 e& m; h, y, F- H        mode1->sync         == mode2->sync &&4 h, @8 {1 r' W/ `/ K! w. d
        mode1->vmode        == mode2->vmode);4 t* O! w- G8 ~6 i* S( C
}4 I  T1 Z6 K" {/ F

5 D7 a4 j& m: r( n6 V3 p) x6 J% U+ N2 }# ^( h
fb_var_to_videomode函数只是将fb_var_screeninfo结构转换成fb_videomode结构。而fb_add_videomode函数查找是否该fb_videomode已经存在,如果不存在则添加到列表中。4 T8 E# J; G0 G$ ]2 _8 p
3.4 字符设备方法) `5 C& o7 K! R
  在看过framebuffer子系统建立和注册过程后,我们看下framebuffer留给用户空间的API是怎样实现的。: r% N' o2 f9 h3 ]. A

4 X2 W% A  _! M: q  b! G/ l  Y本小结只分析5个常用的方法,即open,release,read,write和ioctl。
/ D5 o) U& @' a$ ]3 ?, b# T  W- m# [
  因为所有的方法和struct fb_ops定义的方法有紧密的联系,而该结构的定义由驱动程序给出,在这里我们提前看下在驱动中是如何定义的。
; Z/ ?  }- m4 R' I% _" d# R; }
* d  M# H% G" ?, L 下列代码位于drivers/video/s3c2410fb..c
# c7 V2 \: u& m4 A/ G4 x+ k$ b% F
* W5 N6 u" E! N5 \( n: ustatic struct fb_ops s3c2410fb_ops = {
+ o9 q* p$ W4 c3 d+ k5 \        .owner                = THIS_MODULE,
( R, X2 q4 T! L) \5 n        .fb_check_var        = s3c2410fb_check_var,                        /*检查变量的合法性*/
8 ~" d* ]' E3 t% P5 O9 V1 O        .fb_set_par        = s3c2410fb_set_par,                        /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/
' J+ g8 T! v0 v9 J- w. l/ M( H        .fb_blank        = s3c2410fb_blank,                        /*该方法支持显示消隐和去消隐*/1 k  Z$ h( Z$ j$ i
        .fb_setcolreg        = s3c2410fb_setcolreg,                  /*设置颜色寄存器*/
+ ^( O; @# r8 l4 k4 m( S        .fb_fillrect        = cfb_fillrect,                                /*用像素行填充矩形框,通用库函数*/
0 _" X5 N. i6 k        .fb_copyarea        = cfb_copyarea,                                /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/
3 p. n/ z2 |$ S: V( t3 E/ q        .fb_imageblit        = cfb_imageblit,                        /*显示一副图像,通用库函数*/1 L" \$ M( z6 b* Z
};
( s9 n6 r( P5 e. t; F+ W: l- k最下面的三个方法使用的是内核提供的库函数,而上面4个则是由驱动提供。( M4 T- C7 n! d
3.4.1 open方法. ^1 a. q# H6 k: [6 X3 j- a! |1 W- p# o
下列代码位于drivers/video/fbmem.c" g, M/ e% g1 }: `& J
% J( w4 {* E- I+ f
static int
% y: ]. k9 u# K! Kfb_open(struct inode *inode, struct file *file)0 W( N9 c! `" z  C7 U$ D
__acquires(&info->lock)- e9 ?  q- Z) ]  ~, j# W/ V+ F) v5 ?
__releases(&info->lock)- m1 c5 L, q  `; L
{" o0 q  ~, v& j) K' R! `0 m
        int fbidx = iminor(inode);3 k* e( O" a5 K8 J# w# O8 h, f3 y' ]
        struct fb_info *info;
: ^  @) h. M/ ?* a1 L        int res = 0;" f  s+ d; N0 W5 ~# n
- M3 T* i. E6 ^/ T% ?
        if (fbidx >= FB_MAX)/ V* ^; C- L. I# t
                return -ENODEV;2 d8 T! [0 _! j
        info = registered_fb[fbidx];                /*在register_framebuffer函数中已经设置了元素*/
2 N" U1 ?* Y! |        if (!info)
" F$ m% K& a- N! P" I2 G; T                request_module("fb%d", fbidx);        /*加载模块,这里不加载*/( I6 S$ A4 Y6 f' A; @2 o
        info = registered_fb[fbidx];, z# \) l  Y' p! V  g% O, w
        if (!info)
# d- C+ q8 F! I8 J" x                return -ENODEV;
' h7 k2 k( S' g3 X; c: A/ {, @3 Y        mutex_lock(&info->lock);                        /*加锁互斥体*// \9 N/ l" v; E
        if (!try_module_get(info->fbops->owner)) {        /*增加模块引用计数*/
# o! }( [  x% \2 Z$ z( i7 R9 S/ M. J                res = -ENODEV;
5 b4 K7 X' G' W/ g  G- h8 \  R                goto out;
% f1 x* X; Y" ~  j  B        }
) J: z) y# k, m1 t        file->private_data = info;                /*保存info*/
/ ?0 f! L$ Z6 _        if (info->fbops->fb_open) {                /*这里fb_open方法为空*/
, G3 s, @" Y: p& i! q- X- a                res = info->fbops->fb_open(info,1);
# M2 Z6 Z4 P2 D# J; S+ n                if (res)' y; _7 h: R* }8 o+ N1 y
                        module_put(info->fbops->owner);2 i8 v9 D, \  m
        }
. W2 W3 ~" l5 }7 k7 e3 \! ]0 o#ifdef CONFIG_FB_DEFERRED_IO
5 C5 `5 q4 g& g+ n& D0 o        if (info->fbdefio)
' }. |* k9 {! J& e* ~, L* g1 F# n# j                fb_deferred_io_open(info, inode, file);
) k, U* r8 Y8 ~2 {9 |! z#endif  V; b, x( G) I+ k5 L5 g
out:3 x+ `6 ?5 x0 J5 V- C% J8 x
        mutex_unlock(&info->lock);                /*解锁互斥体*/
* P$ f2 {. `8 w0 R2 B4 b        return res;
" a/ b, j9 _7 `}- [4 K. W& s- M/ X% P7 t
主要的一个工作就是增加模块引用计数。还有,程序会判断是否fb_open在驱动中给出,如果有则调用该方法。我们已经知道fb_open没有给出。: y7 {& I% G; h; L& D
3.4.2 release方法
8 K( K3 O, F! ]5 T+ C+ ^下列代码位于drivers/video/fbmem.c; P6 Q2 m0 q5 H/ c9 ^' X; W

6 t( V# ]5 V% Gstatic int ; s) H5 u% ]- v' w% O% W# G& _7 B8 y
fb_release(struct inode *inode, struct file *file)
9 Q+ ^  M& C1 [2 T__acquires(&info->lock)' f# R1 C1 N- Z  M- x! J
__releases(&info->lock)
* k$ h: i# ]- i' Y1 |# O/ K{
. N5 Y5 Y$ w2 k7 Z8 a: Q* ?9 g        struct fb_info * const info = file->private_data;+ k/ `( {0 Y, S2 r% a8 c' g' ^

% D, b3 ~' \. P        mutex_lock(&info->lock);
9 E: ?/ c: L9 z( c, }        if (info->fbops->fb_release)        /*这里fb_release为空*/# i  O, l1 D" u6 b) P
                info->fbops->fb_release(info,1);. v" Q) ?! E: K& T* w3 I- G" O
        module_put(info->fbops->owner);        /*减少模块引用计数*/
2 @. z( {) {" m$ ]3 s        mutex_unlock(&info->lock);
& p0 H. Y1 n! T' x+ {5 N        return 0;
" t; Y# h% m* [" l' U# V/ t}
3 \/ p7 Q3 K6 \$ Q/ E6 A, N/ S和open相反,减少模块引用计数。
1 X6 s9 P7 w# }, K5 C$ k  t: h- @( u7 P% c4 m
3.4.3 write方法5 ~1 K3 M. R3 @/ L. C& k- b" z0 Z
  通过调用该方法,LCD将显示画面。
. ?6 c% ]: `5 y5 r5 O- t
7 t+ N7 T" _7 _  下列代码位于drivers/video/fbmem.c
9 R) ^0 a; K5 m* E9 }* W& y8 @. ]+ y8 }7 C5 L. x
static ssize_t; @, H; G- V- H- k" a: B, ?
fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
6 @7 i" B- o* n; U* q{2 ]8 \  ^+ K% a0 M
    unsigned long p = *ppos;
1 W  ]  C$ L% Y/ p/ o/ g8 e    struct inode *inode = file->f_path.dentry->d_inode;( N5 v3 ^" _$ h9 n& s
    int fbidx = iminor(inode);* {2 V4 S8 o( V. ]" N2 S
    struct fb_info *info = registered_fb[fbidx];+ m+ y0 e6 E% W' h" B
    u32 *buffer, *src;: I  J: y1 X2 n' W- W. _3 ?
    u32 __iomem *dst;
9 h' @0 L& U$ ]6 Q    int c, i, cnt = 0, err = 0;
8 H: M; |/ O; H/ }    unsigned long total_size;6 a- T# h' g  T+ @6 V7 m
( C6 O! W& ~; M" j7 C
    if (!info || !info->screen_base)    /*screen_base在驱动中给出*/
* ^3 x' ?; s. u! D4 X        return -ENODEV;
8 u8 _5 q: z0 U6 f! h4 ?( S$ q  n) F" b* Y
    if (info->state != FBINFO_STATE_RUNNING)! I7 p: Z$ L* x( p: p+ E
        return -EPERM;
: F0 l# F& j  j! E6 r9 U. R2 T, t( {. `" s  n- D
    if (info->fbops->fb_write)    /*没有fb_write方法*/
# @5 q! R5 _# W        return info->fbops->fb_write(info, buf, count, ppos);% d: D! ]& S$ x: ~" p& T
, u8 X" _* M5 t5 X/ e- ^
    total_size = info->screen_size;    /*screen_size没有给出*/
# D) W: a3 |( q4 ]) @' ]- y6 e; |( _2 z- N# S$ F3 s
    if (total_size == 0)
  E! }" B& `2 q; I        total_size = info->fix.smem_len;/*153600字节,驱动probe方法中计算*/' e6 ^% k# k9 X( w

) J5 \# R0 N# {) r7 y: M7 B    if (p > total_size)
9 L: Y) S  w4 u4 g  S  G        return -EFBIG;8 {* \* U6 B& {& c/ ?' K: A  ?

5 D! n( Z6 m/ P9 u; i+ D    if (count > total_size) {    /*要写入的字节数大于153600*/
; Y7 P) y5 N* E1 W6 V* G+ |% M, R        err = -EFBIG;        /*file too big*/* Y8 E3 E$ M& N4 j+ Q* r3 _
        count = total_size;
8 o, k6 w& b2 X3 a    }
: C3 g5 R$ h) F6 L3 {' J  P9 J! t# Q3 G$ d
    if (count + p > total_size) {/*偏移量加上字节数超出了缓冲区*/( v8 V9 z0 \( Q
        if (!err), a5 y  q* N6 i) N* G8 H$ G
            err = -ENOSPC;
( X; C( [( f6 s+ i6 x2 p
5 A; m; W, q# ?7 z+ ^; v        count = total_size - p;% G( c2 y( d1 z: m1 Z' Y
    }7 C( o1 L( t; j5 W# ~: X

5 J9 F# b0 P8 L3 s. o    /*分配buffer,GFP_KERNEL*/! L7 D3 H' x4 @! M+ d
    buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,, g: g3 i8 o# M1 d: m( |4 C# f
             GFP_KERNEL);                /: C  T$ n9 V7 C
    if (!buffer), ?1 O$ O! L3 y
        return -ENOMEM;4 {; W' r/ Q/ v

$ C8 P6 a; y% B- H( K7 j7 ?6 J+ V    dst = (u32 __iomem *) (info->screen_base + p);/*修改目的指针*/
; P) p3 k2 ?" I9 r
; w: j% r8 s- R6 o6 d" L6 m    if (info->fbops->fb_sync)    /*没有定义fb_sync*/
- U5 }0 A, G, J) X; t! F: ^        info->fbops->fb_sync(info);
, _' T* z0 P% |& B# G! V
. g& V7 b9 D9 R8 H    while (count) {
" s" t0 L/ X" P3 {        c = (count > PAGE_SIZE) ? PAGE_SIZE : count;# Y  h) f8 H% u7 Q" ]# Z- s+ c
        src = buffer;
1 z& x; c, T4 k& w
% s- c( f, j! h4 }* a. n        /*从buf(用户空间)拷贝数据到src中,一开始为1页,最后为count字节*/! X0 \8 ^! J8 r% {
        if (copy_from_user(src, buf, c)) {   
8 T  ?, b! D4 ^& c# g, X! j            err = -EFAULT;
# m4 U3 V  Q4 ]3 v3 h8 D, i( I$ c            break;
+ a3 A  \* b6 K' J3 N" ?$ r4 ?        }
8 `$ H2 x4 }4 m* U- P$ f        /*一次for循环,写入4个字节数据到dst处*/
/ f8 p1 \0 ^1 W  V: H) u& W) U6 _        for (i = c >> 2; i--; )2 \; Y) `6 G9 ?3 l- [$ `9 k, y
            fb_writel(*src++, dst++);
* P& |" }8 h: Z& A7 T: l0 w. l        /*最后还有3个,2个或者1个字节*/
* r5 e1 o; W) j        if (c & 3) {* W5 s! e; K$ T% ?5 H$ Y
            u8 *src8 = (u8 *) src;0 W* f7 t, h6 f$ U
            u8 __iomem *dst8 = (u8 __iomem *) dst;
, D) a% ?8 b4 l0 ]            /*一次写入一个字节*/4 S4 _+ R4 A5 b8 G  C, B9 P; i
            for (i = c & 3; i--; )
* g! ?6 ^  k5 T! L7 W  g$ z( f" @                fb_writeb(*src8++, dst8++);, _" v, t. ?: X1 L; d

/ v+ L4 K7 ^! n1 a6 [            dst = (u32 __iomem *) dst8;
" F9 l/ V  F9 U- z" A        }! ]) T# d. d# _: [2 w* T4 h

3 m+ V3 P$ b" h: [        *ppos += c;    /*用户空间偏移量增加*/
4 B3 I2 }" `0 w+ A' {9 M0 z        buf += c;    /*用户空间指针增加*/2 V+ U5 Z9 ?/ P( K  [  o9 G* F5 b
        cnt += c;    /*修改已发送字节数*/
1 A, P- `5 b# f9 M0 ]        count -= c;    /*减去1页*/
# l2 |" p: E$ j; J5 W- {" @3 D# `    }* c. ^; Q- l  s9 t: k# @
) {: d6 J1 S- N9 L1 a" j7 n
    kfree(buffer);    /*释放buffer*/" C) k3 r' q# J( U' {: x

+ S8 T7 P/ G3 i+ l" I    return (cnt) ? cnt : err;
. M0 V& ^- F% Q+ E2 E7 p( ]}
# d8 y4 _/ `& l1 k$ P% [1 }% }4 `  O+ M$ I7 d" }  N! ^' `( D
这里,做了一系列的检查之后,开始拷贝数据。这里一个有三个buffer,一个是用户空间提供的buf,一个是在这里新开辟的buffer,还有就是驱动层提供的screen_base。, C. G  T3 A+ P0 B
数据流如下:
- L7 c" U5 \- u2 |, G; P. b5 W) _. k1 p. m2 p" V0 I0 X+ y

8 O, }# G" }( y' `1 H& }' a& i7 }- M$ K' R* A7 S9 h
用户空间的数据首先被复制到buffer中,然后从buffer中复制到screen_base中,最后被映射到LCD上,LCD就显示响应的画面了。
+ P( q+ ~* [" _; h5 |; d8 `& G) h# s( ~/ f  N- a1 R
3.4.4 read方法/ h% z2 d" N4 a% {$ k" @
该方法用于读取屏幕画面的数据。0 v" J) I+ L) v& c0 z: d2 w7 S
4 y6 I0 G7 a* @  v" i+ o) a' y8 H
read和write类似,只是数据流是反响的,就不多做介绍了。
' q- i" T) R2 F& m/ S; I' n# [8 t0 ?: K  W0 i! T
下列代码位于drivers/video/fbmem.c
1 {, f) a. M4 p" M6 j4 h3 D
4 P" S3 c# k8 O( \7 n: o0 f& D- _static ssize_t/ ?8 ^. A$ |& m- W+ t
fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)3 h! h- F0 c% ]- Z
{
2 w4 L2 u! ]7 ]2 d( S        unsigned long p = *ppos;9 z% p' D/ O* n, F
        struct inode *inode = file->f_path.dentry->d_inode;( k" l& @# z$ E7 l* u0 z
        int fbidx = iminor(inode);
" O  R% X  Z1 O6 G3 g        struct fb_info *info = registered_fb[fbidx];. p/ j. @; ~# J3 ]  d; I
        u32 *buffer, *dst;9 ~( x' u1 Z! }# q6 a
        u32 __iomem *src;( U' p' @2 @7 S1 B6 h
        int c, i, cnt = 0, err = 0;
, |4 w6 K& f& a( v; @, O        unsigned long total_size;
8 f$ ~" @6 Q5 S& g! z0 n
- g. z, T# l; A# ?% _        if (!info || ! info->screen_base)
# m5 F  ^8 l5 }) Q5 n  ^/ E                return -ENODEV;1 h# U4 Z5 W+ N9 |: h1 }

. J2 M, {0 j+ q' y, I        if (info->state != FBINFO_STATE_RUNNING)
' s) ^  P/ k9 z$ \; I9 u                return -EPERM;
0 [' g% M6 Z4 _; t( R# Y* O8 V8 K) }
; m& s  [8 s0 T; y- H        if (info->fbops->fb_read)        /*没有定义fb_read*/
) a+ J' e$ Y0 F                return info->fbops->fb_read(info, buf, count, ppos);
' ^+ [$ s- o; S+ k7 O: N/ m        5 G+ p3 G& ~' P% `& I
        total_size = info->screen_size;
4 Y* ^; a( q* S2 x2 `+ L& Y. R0 S' O0 Z' q: V3 g. }
        if (total_size == 0)
, y. N* |% q5 u* G) f% k                total_size = info->fix.smem_len;
" ]) m6 K# X, w$ f
2 M. q- Q* s0 ^* ?        if (p >= total_size)
& e/ [$ k9 {4 ^  Z  f9 _                return 0;  Y+ B7 `. ^0 `& T  [, }
2 d" M: {& d2 N/ g$ I# X
        if (count >= total_size)" e) B- o, k6 H- T. T' o
                count = total_size;
6 A6 T  H# ]$ k- B6 X
2 b3 @5 A2 H1 j6 z6 Z        if (count + p > total_size)
/ H9 Q$ y( {' o0 A5 J- Z                count = total_size - p;
- R4 S. w" U9 I2 p: f+ W' M" P
4 I; X5 M# w( P& w9 ?& n6 m7 d1 i* s        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,. ?' ]" b  s. s& K, X! |! }/ E
                         GFP_KERNEL);. v% L; H+ Y- M% s3 Y2 `8 |5 K( n
        if (!buffer)4 c: n1 `) y1 I
                return -ENOMEM;
/ r' ^1 c% N6 B9 v; i# q+ v8 N# k. \  `
        src = (u32 __iomem *) (info->screen_base + p);
2 t# K# G1 F0 z, _8 i( @8 U
" f9 a* p* u# k- r* K& ^        if (info->fbops->fb_sync)% N1 P( K1 I# K. V
                info->fbops->fb_sync(info);/*没有定义fb_sync*/
0 E; _+ O/ a) M5 m& E: h1 D  Y2 q9 t* z- b1 f
        while (count) {
- x  a, [. p6 i" L0 O; S( }/ f8 n) K                c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;
" g1 q' l' u: a! R                dst = buffer;' [2 G# w+ ]' g9 j! v6 O' m! p
                for (i = c >> 2; i--; )2 D5 C9 a) Y1 i' [  \
                        *dst++ = fb_readl(src++);8 [  X0 {! ]% D6 b$ c  k  T2 b
                if (c & 3) {/ H; Y" W) g0 B0 {. v2 `1 f0 v6 P) X, v
                        u8 *dst8 = (u8 *) dst;4 r/ ~+ h1 v' d0 F8 d$ x! H
                        u8 __iomem *src8 = (u8 __iomem *) src;
1 B4 x4 k8 R. C" {  i0 M# e! [/ B
/ L7 @* u; ?! F                        for (i = c & 3; i--;), ~# s7 \! G! X" F& D1 ?
                                *dst8++ = fb_readb(src8++);
. r( T& g6 q  |+ G1 }' i
' C! h' \. F# ~8 C- {1 r9 }. I1 F# G9 m                        src = (u32 __iomem *) src8;8 b( m% H* C5 y1 L
                }. o& V/ c- u1 J! S  N; k) p

2 A7 D' h: F7 B) L                if (copy_to_user(buf, buffer, c)) {6 Z: s' d$ Q" ]/ o: S! Q+ v9 H  J
                        err = -EFAULT;
. ^8 P) ~1 ]) p  d, [4 P                        break;
6 O+ t# F5 E+ n1 b9 V                }; ?/ ]$ [& z3 r
                *ppos += c;
$ a7 t1 k6 T  M/ U                buf += c;
4 C$ u; d3 p9 y' @8 p+ M                cnt += c;2 G) L4 G! v: I7 `. j7 ]
                count -= c;/ \) E6 C  u0 F" Z6 D9 ~
        }$ \# _; Z% S4 T; m) |7 P

8 W6 l, P0 N) `) q. Y        kfree(buffer);% }) J6 m4 {: c  M: \; y
" l) p1 x0 ~1 R, ~% k/ i
        return (err) ? err : cnt;  T  \! x2 l; R; U
}
7 j* T% J7 _) }5 E+ r3.4.5 ioctl方法) ~+ ^& U- q$ {( ], Z5 _" c
这里只是简单的看下ioctl方法,这个函数调用很多其他的函数,详细的请自己看吧。
: e! Y7 W: h. I; N. r  f2 c3 G  I; z8 I3 V2 ^- O2 i1 F$ h
下列代码位于drivers/video/fbmem.c( {5 C6 |6 V3 @. V6 {! |
- n/ K4 G# ^) @7 r/ v
static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
4 t" f( a6 n3 O; o+ Y' T; z- L; [{) |9 G7 w3 B' G8 c6 K7 p
        /*获取inode,再获取对应的fb_info*/; O4 v7 f2 ]( N; r- b+ b; Z% I
        struct inode *inode = file->f_path.dentry->d_inode;
* E, A0 }8 i+ h8 C        int fbidx = iminor(inode);       
" b6 m' b6 J- A1 Y' r/ P        struct fb_info *info = registered_fb[fbidx];! s$ A( T+ A2 s1 f. z) R
5 y4 A) w* Y" g4 i  q2 B
        return do_fb_ioctl(info, cmd, arg);+ {4 }$ C$ B6 ~$ c, Q* {
}- [! _2 v  P2 X6 g
, T6 `9 g4 ]3 [* z+ I0 T0 I
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,5 p  R  E% n" `. f4 ^- r
            unsigned long arg)
3 F+ |  A. ]" E/ a* M1 D' R{8 K: r( V: K: V! j5 Q& O* p
    struct fb_ops *fb;
' n0 h/ r, _7 g% d# w0 C; W% `    struct fb_var_screeninfo var;
' k- K! u- y3 T( U. F* C( k! f    struct fb_fix_screeninfo fix;
5 X* ^1 I/ y7 w8 N- o    struct fb_con2fbmap con2fb;
% m: m- ?6 W& ]' M3 `/ R3 F    struct fb_cmap cmap_from;- W+ p  d4 y4 U" u
    struct fb_cmap_user cmap;- Z% J! H7 P1 k* Y+ e( H& w
    struct fb_event event;4 l* W( A/ u! _" B
    void __user *argp = (void __user *)arg;
4 f6 [9 T* r: p( P( t$ v    long ret = 0;
2 i; }# m0 V# J# o# b5 x$ i
8 p0 l( y: D7 F5 I* t0 a    switch (cmd) {3 H8 k- S' t& [8 w
    /*获取fb_var_screeninfo*/: e! B; W# t" o9 p) I+ k9 e
    case FBIOGET_VSCREENINFO:   
  I5 w1 t6 _. z- }2 A3 }& \        if (!lock_fb_info(info))    /*加锁互斥体info->lock*/8 ~" P1 R* S# v. U* k
            return -ENODEV;
) v, @& A9 y2 G5 K! i2 Y  r        var = info->var;            /*复制var*/
; z# t: \, Z) j$ U+ T        unlock_fb_info(info);8 o3 p4 Q3 `! W& R1 D: f2 A
4 ~6 H, q. F1 {, J% U' F
        ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;    /*复制var到用户空间*/4 z8 h  f7 F: v$ J2 W4 ]+ |( A1 o
        break;: |5 s! ?6 \+ k/ H7 ^% \
    /*设置fb_var_screeninfo*/9 k7 C! d' q. R, c
    case FBIOPUT_VSCREENINFO:0 B! m, J! h' r9 t1 D* o1 I
        if (copy_from_user(&var, argp, sizeof(var)))    /*从用户空间获取var*/
  k0 |8 G& E. D            return -EFAULT;
" L& C# u) `# h        if (!lock_fb_info(info))& {( j3 O  I" ?$ }$ D8 X
            return -ENODEV;
1 P' a; h$ M6 ?) n- ?" v; f, P        acquire_console_sem();
/ z1 @" _. v$ X4 K( B        info->flags |= FBINFO_MISC_USEREVENT;
# x$ m( k# l0 H- F  g4 ^$ Z        ret = fb_set_var(info, &var);            /*设置var*/: w4 [- r' B; Y  y) a2 s
        info->flags &= ~FBINFO_MISC_USEREVENT;: _7 D: c* ^# r- u5 o* G
        release_console_sem();3 g5 P; h2 w" g$ v9 J5 E
        unlock_fb_info(info);
1 |* {- S5 e* ?4 t        if (!ret && copy_to_user(argp, &var, sizeof(var)))- e0 _$ t/ d4 O' @6 |  ~) r# b' _
            ret = -EFAULT;1 O) s- T1 H. H6 ~  k- V; w6 H
        break;
$ M% S/ h& B6 m9 D    /*获取fb_fix_screeninfo*/    /*fix为不可改变信息,只能获取,不能设置*/
2 E) m' V! m) j    case FBIOGET_FSCREENINFO:
& ]$ C5 z* G$ c& i% z* o2 B( z6 Y; U        if (!lock_fb_info(info))
2 p# s9 N( X6 x  q& a            return -ENODEV;. [! R! E. M  m% l
        fix = info->fix;3 s! t+ d( g: p9 S5 x
        unlock_fb_info(info);
$ ?6 ?2 d6 F+ |! p6 t9 e8 U6 N
1 n* ~1 c- ]: P  x2 [% ]        ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
; M9 W7 b& w( ?1 v( S        break;
; Y) v- s1 }1 _- \    /*设置fb_cmap*/    ! s1 }/ T: x$ S0 Z4 t7 D
    case FBIOPUTCMAP:
) G1 e* E5 `  X6 g        if (copy_from_user(&cmap, argp, sizeof(cmap)))
2 c2 ?6 b6 H5 Q5 R( D+ H" H            return -EFAULT;
. [8 Q6 f1 O2 Y5 U& R# S        ret = fb_set_user_cmap(&cmap, info);    /*设置fb_cmap*/
: L/ u5 G/ G; q        break;, }2 ?2 ]9 V  G0 V6 R4 x
    /*获取fb_cmap*/   
# D0 v2 V) d$ \7 ]) d0 u    case FBIOGETCMAP:( R( H' q( v0 U
        if (copy_from_user(&cmap, argp, sizeof(cmap)))
( d* X' ^3 P3 e6 O# i            return -EFAULT;
# S7 ?8 [2 U4 j4 Y& }* O& O8 H! C        if (!lock_fb_info(info))0 b8 F/ d. \7 b6 M# P7 @: J; y
            return -ENODEV;
! a4 C( X7 e( F$ ^$ \        cmap_from = info->cmap;
: T  X4 ~7 {2 [7 v: U& [. q        unlock_fb_info(info);
/ K' w7 G8 r! E, ^, F; h% G        ret = fb_cmap_to_user(&cmap_from, &cmap);/*获取fb_cmp*/; b% z/ `( A7 ?# d6 s7 I
        break;
; Z8 m5 E7 s7 F- Y    case FBIOPAN_DISPLAY:6 m* |# k% x5 S- Y, Y
        if (copy_from_user(&var, argp, sizeof(var)))
8 p3 ]5 A# e- h            return -EFAULT;: b( \/ u3 H* C% ?, ?$ P, Q( N
        if (!lock_fb_info(info)); k* n) s' I# Z. D, _
            return -ENODEV;
& @+ P! z8 B# ?) R        acquire_console_sem();
7 |1 h  {$ n2 j9 [' z# g        ret = fb_pan_display(info, &var);; S. q, F/ q5 d* T8 ?5 N9 p8 s
        release_console_sem();' \3 e; }( o$ m+ L$ w
        unlock_fb_info(info);
* A2 f1 u$ r- J! e% @9 n        if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
: p. x7 b8 l$ V- i2 B7 y            return -EFAULT;1 Y% p! P% c5 a2 g# Y
        break;$ j+ G% l3 |& h0 H
    case FBIO_CURSOR:( J) L0 \0 K8 c: G
        ret = -EINVAL;
, }* m6 W/ R% D! e* u7 G: ?0 a# V        break;! |$ o, D& L" i
    case FBIOGET_CON2FBMAP:
  C. v  c6 M$ W. ?& }4 l# a        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))2 K/ C# z7 c! a
            return -EFAULT;
: ]4 [' ~1 M9 Q3 l7 o( W        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
: Z; J7 O$ z/ R& Q1 @9 G8 A            return -EINVAL;
$ v( E& ~. E, A6 F! c! y8 s        con2fb.framebuffer = -1;
6 Z& R" a1 v6 I0 N. _- T        event.data = &con2fb;
  s9 G/ u  t- |! v; p  e* L        if (!lock_fb_info(info))6 ~5 ?; o( H5 h" i
            return -ENODEV;
0 s3 O7 @; t+ x$ D1 q        event.info = info;
; p+ V+ ]+ n, g, x& b        fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
) `5 [9 v3 @. w) x9 M3 n        unlock_fb_info(info);
1 q4 y0 C7 c: _$ o. u$ [        ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;$ b  K' G2 m4 g0 O4 _
        break;
# ~5 n2 h% Y8 l* ]7 M    case FBIOPUT_CON2FBMAP:; ?& Z' N4 E* i8 y% Z# o9 g
        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
  i; [: J' Z3 u0 ]6 G9 m! w. R            return -EFAULT;
5 T8 z/ z0 b9 g* ~; u8 C+ M        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)" y0 d& o' D9 c6 n% E3 j7 M
            return -EINVAL;
2 l  S6 K. U4 e" K4 o        if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
+ g4 X0 V+ v# @% ~9 F. B            return -EINVAL;  M& ^' G% y; T; [$ P
        if (!registered_fb[con2fb.framebuffer])
7 w8 V0 c2 Y% m& |' f# Q  e            request_module("fb%d", con2fb.framebuffer);
8 n0 f% h" E) y4 C        if (!registered_fb[con2fb.framebuffer]) {. k* M* H6 j- {- e2 W! m% a' b
            ret = -EINVAL;9 P, T3 ?! F" g3 V' _
            break;6 D4 h% S7 G9 j) X9 C0 c
        }
2 a2 }8 D& L$ _0 I' ~0 L# f        event.data = &con2fb;+ d6 s2 H, w. O4 ?- Q0 }
        if (!lock_fb_info(info))
! B8 Q. Y5 b8 I- N- ^            return -ENODEV;) \7 U3 N0 G7 Q/ K0 }+ _
        event.info = info;
1 g" F/ T# n' W2 Q        ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
' y8 t/ a8 k+ M4 D! M& M7 z8 @! X        unlock_fb_info(info);6 }( i" n/ m2 c* [- O+ j$ x
        break;
* @( P1 W$ k% H% K* k: g5 ?& v    case FBIOBLANK:
  ?7 k3 Z0 E  ~1 L# [" J        if (!lock_fb_info(info))
/ p0 S) G) j7 T- q            return -ENODEV;9 d1 v% x3 L4 x
        acquire_console_sem();
5 e5 Y5 B0 N/ i. t! a3 @        info->flags |= FBINFO_MISC_USEREVENT;
& X# M& P/ U; n5 ?: D        ret = fb_blank(info, arg);        ?*最后调用驱动提供的s3c2410fb_blank*/+ F+ H, w: [  k" i
        info->flags &= ~FBINFO_MISC_USEREVENT;3 x/ s: b3 W+ i9 B' s
        release_console_sem();
6 H3 v% S0 N7 t  _4 T        unlock_fb_info(info);/ o+ j1 ]0 M( M. L! ]- F7 O
        break;
7 K9 D; W9 V6 {4 V    default:: f6 c: j3 q, ~" {9 q( Y
        if (!lock_fb_info(info))0 h; Q5 q) O. J/ e
            return -ENODEV;
. W; W  X7 B, i/ ^$ f3 |! W        fb = info->fbops;3 D8 a0 G6 ~' e+ C  M6 J" g
        if (fb->fb_ioctl)        /*fb_ioctl为空*/
! d; {9 _2 z  ~7 ?6 l9 j            ret = fb->fb_ioctl(info, cmd, arg);: ~  a5 [" Y$ \4 F
        else- R# a* S6 T* ]$ G
            ret = -ENOTTY;
- E: J2 P' S' I        unlock_fb_info(info);
+ v6 _0 P1 c4 ]3 c1 H" t    }
2 f. H) O) H6 `8 j% q: l: d    return ret;
! C! W4 ]( W! ~- w" G6 \}0 O6 |4 v7 o+ S  n, l
正如2.2小结所说的,fb_fix_screeninfo只能获取不能设置,因此,ioctl只提供了获取fb_fix_screeninfo的方法,而没有提供设置fb_fix_screeninfo的方法。0 P" A2 b9 b8 O$ `8 h  M# R
. S0 Y' m! l$ z7 a- t# x
3.5 小结/ }8 K$ W8 r, W9 q" `" m. J" R
  本节对frambuffer的核心层进行了介绍。包括frambuffer子系统的创建,frambuffer的注册和提供给用户空间的5个API函数。下面开始介绍驱动层。6 _0 F: k- y! @9 M4 i" ?( g* R

# X6 Q% }6 \0 x& ]) y6 C4. 驱动层( e. `  f- q7 q: }. L
本节将开始介绍S3C2440的frambuffer驱动,该驱动源码位于drivers/video/s3c2410fb.c* b8 u3 r0 y7 @+ v4 {
( l$ x$ L: ~& K# U
首先来看下驱动模块的初始化和清除函数。
. ^3 V  X* `5 f) Y+ q  K+ P) G; m: S2 [( u
4.1 s3c2410fb_init和s3c2410fb_cleanup# u9 v* A  U# z# B3 f
static struct platform_driver s3c2410fb_driver = {/ A6 }) G- A2 S9 \; f2 ^! @
    .probe        = s3c2410fb_probe,
5 b9 m- C/ H7 p5 F1 F    .remove        = s3c2410fb_remove,
- ?9 y0 m8 P% N- _    .suspend    = s3c2410fb_suspend,
1 X# `8 D6 t. x# H: O1 L0 F    .resume        = s3c2410fb_resume,
# m' A! c: \2 S  `0 N) e    .driver        = {" l. K. P' p6 i; D3 q+ J2 p2 c
        .name    = "s3c2410-lcd",
. J1 F1 J) C+ W! }& u        .owner    = THIS_MODULE,6 t) p3 |) R9 s' y
    },* F8 z. Q0 |( Y. ^$ F& f
};
+ M4 v  Y8 W9 S, c( D9 S% v. I
1 c3 k% y4 J+ I8 m  D! [int __init s3c2410fb_init(void)9 @8 U( g6 K( Y, `9 R+ a
{9 N; U6 g: ?. G
        int ret = platform_driver_register(&s3c2410fb_driver);' @* j" F- b3 ^8 G9 n: K$ m

2 c% G! G' Q3 d        if (ret == 0)) J- g+ M: d. ^7 L& Q) L; a4 O! l/ `
                ret = platform_driver_register(&s3c2412fb_driver);;, V3 T% ]1 B( Q7 A+ }
$ Q* }& \$ `9 V  j4 Q
        return ret;7 Q" S5 E1 b4 C8 {/ m7 p0 H7 ~
}
+ k; z5 I% W* Y, A# w3 Q1 W) [+ u9 b5 {/ g& i! ^# Q0 T, w
static void __exit s3c2410fb_cleanup(void)6 |' S/ C4 Q( ?- K* _, k5 |
{
! S+ y! I; D0 \( w        platform_driver_unregister(&s3c2410fb_driver);
* [7 e. w8 q7 h1 p        platform_driver_unregister(&s3c2412fb_driver);
& X0 F$ j, U4 T, i( k3 Q}
' _5 h& Q' v; q1 z+ Y. ?/ p
0 Z/ d/ P% i( W) xmodule_init(s3c2410fb_init);
& B* O: Y* e" p2 Dmodule_exit(s3c2410fb_cleanup);
( I: A' J% I( J3 K8 H
, p: s0 v* A! c/ _3 A当platform_driver_register调用的最后会调用probe方法。接下来就来看看probe方法。
) ^9 }- s, U& H. V4.2 probe方法
( q; l6 i! x1 {1 m( ostruct s3c2410fb_info {
9 H5 A- h+ M$ U. a: w7 l    struct device        *dev;* S& X" d0 G* i' X. _$ ?. q' l
    struct clk        *clk;
  u7 D1 f* m& K7 r+ \! p3 g
* M9 x# Q2 I. |/ L; S    struct resource        *mem;' H% \& X( F1 r
    void __iomem        *io;        /*虚拟地址*/
$ l* u$ q* E1 N2 i    void __iomem        *irq_base;. J. [6 t2 Y: n* y
" Z4 N. Z3 i2 |) ]0 o
    enum s3c_drv_type    drv_type;1 p- P6 r) \4 q
    struct s3c2410fb_hw    regs;; E0 B8 f3 ?# R, c
* z1 s$ h1 w& W/ r0 s% {  I( {
    unsigned int        palette_ready;0 t) e: V4 @' u$ e4 J

6 U& N& \% U" \8 x; x    /* keep these registers in case we need to re-write palette */& l, _, j8 E, a4 L- L2 g
    u32            palette_buffer[256];
% W+ q( Y  q9 ]5 I    u32            pseudo_pal[16];
$ j# N; I7 O- j6 B! ~2 i};) s+ V# Z+ q7 q; u8 V9 m

2 q5 J1 a" y: H- n1 C: dstruct s3c2410fb_mach_info {/ t  h! h+ x) S4 _
* X! {8 o1 _0 _4 j1 y$ Y
        struct s3c2410fb_display *displays;        /* attached diplays info */
$ W$ Q+ G/ \# ~* e( o4 z" u# w        unsigned num_displays;                        /* number of defined displays */
/ }0 Q+ f8 T* y; S$ \9 W0 R5 q, H        unsigned default_display;
2 {4 Q; G! [$ P4 r
! A, _3 C4 P( U# j7 _0 P        /* GPIOs */
3 F% A  ]5 }8 l+ p
2 E  H( h2 c7 \2 }+ ?! {9 B        unsigned long        gpcup;0 ]2 F5 S2 U. g2 l& i' o
        unsigned long        gpcup_mask;  a) [8 m8 f% m5 `
        unsigned long        gpccon;
' y" h% I7 o" e6 `7 h- `        unsigned long        gpccon_mask;
$ Y* u' ~# j+ Q2 p, O        unsigned long        gpdup;
# t5 r/ H) h4 Z" D        unsigned long        gpdup_mask;9 P- F9 w7 {5 I5 O
        unsigned long        gpdcon;
! }" n* O8 h/ o( L# Y2 w" G        unsigned long        gpdcon_mask;
& u" m, v3 s' O) u! ~. J  F4 g  [3 Z$ H
        /* lpc3600 control register */2 g- _1 s- f1 @& G
        unsigned long        lpcsel;; J- Q$ }* M& G% [, }
};4 _; ?2 n+ D7 P* p
/ |( V$ w% B, x4 D8 `, ?
/* LCD description */
7 E7 ]& |( D" S. f' R* w2 C' sstruct s3c2410fb_display {
# i0 x4 E0 ~9 u% g5 p/ ^7 \    /* LCD type */
7 J, W8 H2 G, C6 G/ D    unsigned type;
& {( l# }8 y3 q2 _1 o! i; I! V
3 t3 K  l* k9 D* L, P- W    /* Screen size */, i* @4 @! K. r4 I
    unsigned short width;
0 S$ n% L) F4 ?    unsigned short height;
# i( t& R: i0 P8 h, c9 J5 S5 V1 u5 o7 W$ D0 T4 I2 X( M
    /* Screen info */
: x/ F, ]4 G4 L    unsigned short xres;
( ~: i& w+ s. v4 }8 Y" W! q8 G    unsigned short yres;8 a  Q+ F1 Y, Q: @2 R$ z- o- `
    unsigned short bpp;7 l. n' ?, p5 y" b. K$ p: o7 e
/ {5 C1 d$ l; @3 Y* c
    unsigned pixclock;        /* pixclock in picoseconds */8 K0 u, i; N! K) @' c! D6 e0 }
    unsigned short left_margin;  /* value in pixels (TFT) or HCLKs (STN) */  i# g& O5 m! A+ [
    unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */
8 E* l) e7 h. J, W4 x( I8 \    unsigned short hsync_len;    /* value in pixels (TFT) or HCLKs (STN) */) L9 r2 W# B# a  e1 L
    unsigned short upper_margin;    /* value in lines (TFT) or 0 (STN) */3 V. z5 ]1 X1 J4 O
    unsigned short lower_margin;    /* value in lines (TFT) or 0 (STN) */, F. T3 v9 \3 Q  m# H0 h
    unsigned short vsync_len;    /* value in lines (TFT) or 0 (STN) */- `3 a" p% M0 Q, y
, E7 @6 X8 |2 k5 o
    /* lcd configuration registers */
; X; ^* H7 V. R' Q. p    unsigned long    lcdcon5;( N* u) G" y8 X$ @6 {+ i
};$ ]$ q8 i/ U: r% j7 V$ Z2 A
: v, b. ^+ I! ?0 v
static int __init s3c24xxfb_probe(struct platform_device *pdev,
* D; Y. d2 `4 s& T                  enum s3c_drv_type drv_type)( y" \8 }, @" i! w6 O. [2 G
{
3 J  V7 k7 ^* _    struct s3c2410fb_info *info;! Y1 u7 m4 |: f/ Z
    struct s3c2410fb_display *display;
: k$ r& |& O$ S" ~5 l- q0 t4 U$ R    struct fb_info *fbinfo;0 C7 S2 G& `+ p! `$ Y+ U" H
    struct s3c2410fb_mach_info *mach_info;
0 z* r0 U) J1 z( y& ?4 S) W; Y    struct resource *res;2 K. X) C+ X. u( G* Y2 p  o7 P
    int ret;
& i8 H7 U) `3 M. s# y. U  L    int irq;& t  u) `4 Q6 S; ]) P2 m" I
    int i;. T: K! u; A( X( H! B
    int size;* Y2 x2 w6 K7 p, e* d
    u32 lcdcon1;. c0 w2 ~0 Q: C
    /*dev.platform_data由函数s3c24xx_fb_set_platdata(mach-smdk2410.c)设置,指向s3c2410fb_mach_info*/! ]+ c+ D  n/ c8 U
    mach_info = pdev->dev.platform_data;   
% p7 M4 ]! [5 i6 ]/ O    if (mach_info == NULL) {9 Z6 K. d: I/ ]* M
        dev_err(&pdev->dev,! x9 z0 J' x1 R' x* p9 U! U
            "no platform data for lcd, cannot attach\n");9 b* U" a2 S( f
        return -EINVAL;* ~) C& Q7 }6 _+ ?# e0 q+ k9 T
    }  F' D1 X) w5 u
                                    /*在mach-smdk2440.c中,default_display=0, num_displays=1*/
# @" |- M  @( B1 w+ G& U    if (mach_info->default_display >= mach_info->num_displays) {     
7 U$ f6 n# f# y9 Y        dev_err(&pdev->dev, "default is %d but only %d displays\n",
, @! z% Y; |' R& y2 m            mach_info->default_display, mach_info->num_displays);; V& |9 p5 j9 W% G) Q6 n" N) l
        return -EINVAL;/ j: w: A; l8 I. Y
    }
6 [) ^4 @% Y& O! L% n0 L& Z/ k3 c5 V% \3 ~0 s
    display = mach_info->displays + mach_info->default_display;
' Z. C( D; a' f, M8 |4 \# b# Q+ ^& R1 U! T1 c# G" ]
    irq = platform_get_irq(pdev, 0);    /*获取IRQ号,16号中断*/
; k5 T# }  [2 U    if (irq < 0) {$ H6 P$ j& `4 x
        dev_err(&pdev->dev, "no irq for device\n");, S3 _: n) x& a5 D( D" K
        return -ENOENT;
' A4 T# B" W( V3 w" X( W    }) i% ~% ]4 O, s* v. x
                                        /*分配struct fb_info 其中包括sizeof字节的私有数据区*/
: i6 R0 }2 F  ?7 f    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);: p+ g' @/ R# W: j$ d. T7 |
    if (!fbinfo)
$ v) r/ h* {4 q- q2 Z' q        return -ENOMEM;' K+ @) h* r( s9 L" X( d! z( u- J
4 C+ l- R, `, h5 q7 d" E
    platform_set_drvdata(pdev, fbinfo);    /*让platform_device->dev.driver_data指向struct fb_info*/3 Z$ S4 ~% p2 s" A. D1 z7 p

. [- q; T1 b$ \/ G' f0 P    info = fbinfo->par;                    /*par指向s3c2410fb_info*/
% V0 K% S* h  B$ `4 k2 i  Y    info->dev = &pdev->dev;
5 e2 A; M: t  n# ^! Q    info->drv_type = drv_type;6 W' @- V$ }5 _- h7 t
! e/ o. d, k: J) {- b; [
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*获取平台资源*/6 ~. g) h8 L* ]# t& c
    if (res == NULL) {
  ~4 B, x1 [( U# H7 F        dev_err(&pdev->dev, "failed to get memory registers\n");
- f1 j% e5 Y2 e' l7 u6 j" J( [        ret = -ENXIO;7 g1 B! W; R* c( `$ k
        goto dealloc_fb;
0 v7 m% \$ ^  [& ?' T% P. R( D    }' l8 x! C3 ]4 A2 }# r7 G! M- m
; a# D# f+ [6 i" V
    size = (res->end - res->start) + 1;                /*IO内存申请*/
" H1 a. m  o% ]3 U$ r8 D4 P% b    info->mem = request_mem_region(res->start, size, pdev->name);    0 g* M& A$ K+ ~% e8 T) x1 v6 W
    if (info->mem == NULL) {
/ S% A/ r- e) C9 F( }- e& [        dev_err(&pdev->dev, "failed to get memory region\n");6 V0 F7 y8 f. [* y& L/ y
        ret = -ENOENT;
9 X8 S9 ~+ S' c        goto dealloc_fb;
" Q8 g1 g2 q  v) M2 p    }, i9 l5 Z3 w8 I  j" i( W0 R; b
; C$ o3 ~% a  x' Z  j* v
    info->io = ioremap(res->start, size);            /*IO内存映射,获取lcd第一个寄存器的映射地址*/               
% {1 T; j7 x3 q7 i9 `) Q& t5 D    if (info->io == NULL) {, U  J# T, o, M' T% @
        dev_err(&pdev->dev, "ioremap() of registers failed\n");        
- I' U( {% x" r        ret = -ENXIO;   
1 K  c# A- b, H! U+ L( P6 u        goto release_mem;
% a/ k+ Y! T9 d, Q7 K    }
; T5 z8 R3 P, C3 x# `5 L                                            /*irq_base对应的物理地址是0X4D00 0054(寄存器LCDINTPND)*/* X% z" L- v* k/ N% d) w
    info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
4 O, p. x# H% a* a  W9 G" K
) N( d; B. t$ ~- N" d8 F    dprintk("devinit\n");
4 C% b  R0 Z: h" \
* h3 E# n3 l$ d: i2 E    strcpy(fbinfo->fix.id, driver_name);    /*复制名字*/- n( r9 L$ ?; Q; e/ G
0 E. x0 X1 R% \% o3 ?
    /* Stop the video */% _, k. q6 a* R& Q+ r; N' C
    lcdcon1 = readl(info->io + S3C2410_LCDCON1);; N: t  T; p, z0 w7 Y6 N4 w
    writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1); /*禁止LCD*/+ x7 w( s$ ^/ @8 W

" N, P2 E# ?" y% D    fbinfo->fix.type        = FB_TYPE_PACKED_PIXELS;
( L& y* [# F" B* c4 m    fbinfo->fix.type_aux        = 0;
2 F0 x5 R6 G0 j. K" M8 }    fbinfo->fix.xpanstep        = 0;
: @# F! i; C6 ?1 K% }3 n# D( s    fbinfo->fix.ypanstep        = 0;
! R3 R- H( M. Z5 [8 |. Q" j( Y    fbinfo->fix.ywrapstep        = 0;5 D! C9 E+ d( f( j( {* B* ~% D
    fbinfo->fix.accel        = FB_ACCEL_NONE;    /* no hardware accelerator    */9 v$ b1 h+ ~% t4 y# Y' ^
+ W! ]6 D& k2 n' C% L
    fbinfo->var.nonstd        = 0;7 T5 ^3 B6 J4 _2 K" ?
    fbinfo->var.activate        = FB_ACTIVATE_NOW;  j- b8 M* u2 @7 J0 D
    fbinfo->var.accel_flags     = 0;2 b, O5 V% `6 }1 v
    fbinfo->var.vmode        = FB_VMODE_NONINTERLACED;$ j/ k8 O( l; S+ X

* i( O, T/ @& R4 e! t& J" N    fbinfo->fbops            = &s3c2410fb_ops;
: [: s7 V# U; v) H8 ^. Q+ z- E    fbinfo->flags            = FBINFO_FLAG_DEFAULT;
0 K; V* h$ R9 V1 q8 H! v. A. @    fbinfo->pseudo_palette      = &info->pseudo_pal;
! L: F3 V' I0 E" q8 `5 J! e0 W+ r
' J/ y+ V5 r+ W3 c3 s' Q    for (i = 0; i < 256; i++)' V: |4 W& e. M( }$ _; q
        info->palette_buffer = PALETTE_BUFF_CLEAR;
: P+ N. D+ Y) G9 _- Q3 |# j# j9 H# x9 N5 h! v0 N
    ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);    /*申请IRQ,快速中断*/
5 t3 p# u; f+ [0 ?    if (ret) {* K1 u" N' m; N0 z- |1 Z1 u! g
        dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
2 [$ _. d6 _/ R        ret = -EBUSY;: ?% B+ L; G* Z' [
        goto release_regs;, U) c, u- k9 e  s4 {* b+ l
    }, s1 J  ~! V$ b. ?7 e- \
5 Y0 X# C# k6 I* R9 F% E3 R& ]
    info->clk = clk_get(NULL, "lcd");        /*获取时钟信息*/( c% G6 L# |6 _2 Q2 l8 d3 x$ O0 G
    if (!info->clk || IS_ERR(info->clk)) {
+ q: t4 }) n5 x) ?! m; `) g        printk(KERN_ERR "failed to get lcd clock source\n");" w4 Q  t8 J7 a4 ?  C2 {
        ret = -ENOENT;0 m% Q; Q1 j/ ~- E
        goto release_irq;
% ]! e0 P/ T2 p5 B2 m    }
, s3 V4 x' b' @; Z/ P; r8 b* C4 w8 J" }! V  k6 R+ G
    clk_enable(info->clk);                    /*使能时钟*/
% N" k8 e' L5 Y& R6 ]    dprintk("got and enabled clock\n");3 H3 j  }0 _' k5 J) f6 X1 L" r
, s( k, q5 m% l' S: R' `3 m9 A, m9 F' I
    msleep(1);
# T7 R9 O5 T; f& o" [* e- d% `( W, e( A0 [* M$ {( z
    /* find maximum required memory size for display */
+ h5 S$ }7 [' ~% W( j% G. @    /*在多个屏幕中,找出需要的最大memory*/& t; W9 r' j# Z& Z4 [' F; q
    for (i = 0; i < mach_info->num_displays; i++) {
; n* O' U# T  @        unsigned long smem_len = mach_info->displays.xres;" @; \3 A2 _5 c0 F2 {
        /*所需的memory空间 = xres * yres * bpp / 8*/  }" Q3 a; N1 G! U' j$ K1 \
        smem_len *= mach_info->displays.yres;9 I, x3 k% Y/ s" L- O
        smem_len *= mach_info->displays.bpp;8 j* b  ~3 B7 W  R) N) y8 U3 J# K
        smem_len >>= 3;, `3 l* K" l3 R
        if (fbinfo->fix.smem_len < smem_len)
2 u9 J( H) G9 h+ a4 p; \0 O# f! G            fbinfo->fix.smem_len = smem_len;
$ x( z3 H0 \: c5 U5 X& v( m5 y    }3 k" y% C0 x' R: z  O! S# M1 q! K
. l/ ?& c5 }& b/ y6 I% k* |
    /* Initialize video memory */    /*根据上面fix.smem_len的大小,获取DMA映射内存,一致性映射方式*/
# D' Z4 a: u- q5 {4 R    ret = s3c2410fb_map_video_memory(fbinfo);1 p7 ?8 a( X* R# w6 e3 z
    if (ret) {
  I* k3 o' H& J2 n7 P# R4 O- ^        printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
( J# J1 Z! V5 n- G. Y        ret = -ENOMEM;
7 [3 I8 y; h0 m5 h        goto release_clock;+ b: A7 d* @8 n/ z) i
    }9 i4 Y: Q9 h, P* L0 q
2 |4 U* J4 O5 i6 `& u2 v
    dprintk("got video memory\n");
  d1 g9 m9 r, f9 k
+ w9 N# `% z3 b- h! ?    fbinfo->var.xres = display->xres;            /*320*/! v' t6 B+ M  N- o8 n
    fbinfo->var.yres = display->yres;            /*240*/   
3 O2 S2 N9 \/ u$ I6 i: u0 A    fbinfo->var.bits_per_pixel = display->bpp;    /*16*/
1 |% S2 O/ l  Z5 r* _9 d9 c& d, q$ J0 \7 o+ X' ?9 `5 e4 n
    s3c2410fb_init_registers(fbinfo);            /*LCD寄存器初始化*/        
3 Y) [/ @' z# U8 S9 G" X0 i: Q7 O) ]0 p: Y" w! {
    s3c2410fb_check_var(&fbinfo->var, fbinfo);
4 t. T. M4 ]  C) |: r1 V! D# |7 c. E( a- c2 }) V1 R8 ~
    ret = register_framebuffer(fbinfo);            /*注册framebuffer*/  K3 c# G2 a% T
    if (ret < 0) {: M1 P8 x! y% Z. K2 K* a5 ]- B
        printk(KERN_ERR "Failed to register framebuffer device: %d\n",ret);; m: R: A4 D, j1 }9 j* V. o
        goto free_video_memory;) M% q4 {. h( _( W: X9 w, |/ G* x
    }+ G" y( o; w+ B1 X. v7 {0 V  X% ?+ P

$ ]3 B# d/ ]0 j6 A0 }% Y    /* create device files */; B, _& [7 {5 \, N- [8 S+ h
    ret = device_create_file(&pdev->dev, &dev_attr_debug); /*添加设备属性*/+ l, B2 F* s) X) `
    if (ret) {% z" R4 Y+ K1 g2 T( h  B
        printk(KERN_ERR "failed to add debug attribute\n");
  F8 p& {3 _8 \- P+ y1 }    }/ B9 T4 a; s* D, N7 s( u% Q
) C" o6 a) o, [/ T% X1 g
    printk(KERN_INFO "fb%d: %s frame buffer device\n"," [2 Q+ O# Q  O" \; Y1 Q% Y% Z+ V& }
        fbinfo->node, fbinfo->fix.id);' @5 f% q# ~3 u, @5 y
. u5 }# K+ Z8 d. _% R1 i
    return 0;
5 ]7 U& m$ R7 C' t& y/*一旦某个步骤发生错误,以注册的相反顺序开始注销*/& z- }& K/ j0 @# ^" I$ E2 N6 C$ c/ _5 [
free_video_memory:/ R8 f+ N' r; N" P" M
    s3c2410fb_unmap_video_memory(fbinfo);
" ^8 o" P6 y, {. Jrelease_clock:
! @' p1 q# y8 k& W    clk_disable(info->clk);. G$ Y/ R6 u! p% g5 d: z
    clk_put(info->clk);
& I' J' p+ e1 \4 Y1 P2 ^release_irq:
! G5 A7 [) {9 O+ K    free_irq(irq, info);
- T9 v% Q. |3 s+ N+ Brelease_regs:6 H# S: A0 U6 X6 v! p
    iounmap(info->io);
& f3 H" J* }; Zrelease_mem:
0 V5 {# {7 j$ y' |! i    release_resource(info->mem);, D; H( T; x) @, f, I. O7 O, U# `# o- {
    kfree(info->mem);' f  [5 H/ C+ H7 [4 I, T4 d
dealloc_fb:0 u- x1 g8 s* E. y) W: P
    platform_set_drvdata(pdev, NULL);
/ [; p) B. ^6 b: Y# C$ T$ x    framebuffer_release(fbinfo);    ' S5 @' f9 N3 B7 v  q
    return ret;! Q" a# Z% V# }8 R" u3 m! L
}( |1 q7 U/ z. z" O+ x" [4 w1 S3 O, u6 d3 P
( q; V* J3 e# h
这里使用了三个新的数据结构。s3c2410fb_info是驱动程序使用的,里面将保存所有驱动程序所要使用的资源等。而s3c2410fb_display和s3c2410fb_mach_info,是由板级信息,通过platform总线添加到内核中。
$ F- y7 [1 o4 l/ u; i2 D  ^" b7 m; S( D! F! A- _3 G
s3c2410fb_display中的成员将被复制到fb_var_screeninfo结构中。( y. C1 n( q8 P" Q$ O
. z6 o- n6 i/ d" C
该板级信息的定义在arch/ARM/mach-s3c2440/mach-smdk2440.c中,来看下
1 A& u  R5 Y, S8 C) J$ F5 j1 b# I+ F, Q8 h
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {1 i! e. C! g. k) L* T
# \/ ^( s/ f6 P# m2 W# u
        .lcdcon5        = S3C2410_LCDCON5_FRM565 |
8 D" u6 H  A; m                          S3C2410_LCDCON5_INVVLINE |' M' J2 H' P; i: \7 d" ~% G' R
                          S3C2410_LCDCON5_INVVFRAME |0 |0 X, G; g  i1 `( i; x9 M/ `
                          S3C2410_LCDCON5_PWREN |
% L! H: S: R) C8 j" m+ _" b                          S3C2410_LCDCON5_HWSWP,
' D, d* h# S& V3 |6 _: T3 r0 l5 j# D
        .type                = S3C2410_LCDCON1_TFT,
, h9 ?: L3 _+ \8 u) n# k; }( _( Y& \  F+ [
        .width                = 320,//240,4 k% W& G  }% C7 W+ j3 N$ G& A
        .height                = 240,//320,
' z0 O' t1 d* j9 Y; u& d& J' g- I# \  j% O- N) s
        .pixclock        = 149000,//166667, /* HCLK 60 MHz, divisor 10 */. x$ C, C9 D, V' @% S
        .xres                = 320,//240,
) G) d/ Z+ s2 U: J8 j4 x        .yres                = 240,//320,0 O0 k+ i4 p; P
        .bpp                = 16,& p+ O3 h# h9 Z# m& n* [" j
        .left_margin        = 20,
% _5 E: ^$ a# i: c- W        .right_margin        = 38,//8,
' z2 h7 e: b/ p6 D! T        .hsync_len        = 30,//4,) X# j! ~/ K( w" h1 O
        .upper_margin        = 15,//8,
/ R; a( r# `8 ]6 W% E% Z+ u        .lower_margin        = 12,//7,
# _. u* n  ]4 h9 e& ]        .vsync_len        = 3,//4,$ K: |7 ]  C6 G2 |
};
5 n- G; [$ w& Z. P6 [/ w
2 m* }8 j( K9 v4 S9 @/ p/ Ystatic struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {* l+ s) U7 z8 e' w" Z
        .displays        = &smdk2440_lcd_cfg,. m8 Q0 ]* c$ Y) P; _$ y$ n- h7 F
        .num_displays        = 1,* Y" {7 q) r. r7 H
        .default_display = 0," Q9 d3 n3 M; P2 ~- Q0 M$ l

# X* W4 Z0 m) ?% I#if 0
4 q9 r  ?) s4 W) u% s        /* currently setup by downloader */6 `( F3 q0 z( ~* s3 C$ \
        .gpccon                = 0xaa940659,
" y) e: O# k5 N9 z+ ]; B* s/ }$ M        .gpccon_mask        = 0xffffffff,
0 x! p! z2 ^* ]  d5 O5 }        .gpcup                = 0x0000ffff,
! J  X( B" U) b/ j        .gpcup_mask        = 0xffffffff,7 L/ a# Q  F. P( M. H
        .gpdcon                = 0xaa84aaa0,
7 S3 {( h3 J$ _8 ?9 n. G, k  p; v        .gpdcon_mask        = 0xffffffff,# z( _3 ^+ P5 ]7 }. s" D
        .gpdup                = 0x0000faff,) \" i- Q6 f. C5 `6 Y7 O* p3 ~& d
        .gpdup_mask        = 0xffffffff,* O* I: i  t1 E/ f. M: r8 Z& k! J
#endif
0 R8 E; h# b; i8 n//no
" o, g5 E3 \8 u- j4 d  H3 i//        .lpcsel                = ((0xCE6) & ~7) | 1<<4,
8 W6 A* b- k# R6 C& I; T! ~};
$ \/ ?; G; q) i+ i( F5 V$ }2 l: M+ j1 f. n
这里NOTE:每个LCD屏幕的参数不一样,因此上面的参数会有所不同,这是需要移植的地方。
* W- n3 _" p; V' v
; {# F' s  a* A随后,我们看下在probe方法中调用的几个函数。首先是s3c2410fb_map_video_memory。
% @- k8 Q6 \$ _! n) u
5 ?- b8 I# ^$ H0 E4 ?/*
% L) L! z5 M  M+ ~ * s3c2410fb_map_video_memory():* W7 X1 [; [3 x4 {+ d  p1 B
*        Allocates the DRAM memory for the frame buffer.  This buffer is9 c! G2 z, R* W' u' m0 h: L
*        remapped into a non-cached, non-buffered, memory region to
  d" z# F3 {" c *        allow palette and pixel writes to occur without flushing the% J* B! c. I: A4 l7 v: I6 ~" W# `
*        cache.  Once this area is remapped, all virtual memory
5 R' w" v5 X  E+ y* ?. ~ *        access to the video memory should occur at the new region.
8 S" d' I3 m% z: v/ A9 x */
; h% ~* K/ g/ {. E% Z* Nstatic int __init s3c2410fb_map_video_memory(struct fb_info *info): t% y6 y) b/ @0 d# m
{) D  \; d7 O( {: L7 V8 Q8 D  d
        struct s3c2410fb_info *fbi = info->par;, j7 V& o0 S# J  ]; A
        dma_addr_t map_dma;
8 f3 r! i0 M7 b4 s        unsigned map_size = PAGE_ALIGN(info->fix.smem_len);, Q* ~8 M$ W$ M% M: u% F; a
0 M# ~5 Z: Z' C" h( V
        dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
5 w4 B. m. P7 \* t8 ~5 y; {                /*分配DMA缓冲区,并保存DMA缓冲区虚拟地址*/  f! y- O% W5 B% s6 A9 e  ~
        info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
2 ?- O1 P! E* l4 I3 k                                                   &map_dma, GFP_KERNEL);
" ^5 C, T, W# z' s( B8 I& S, g8 R7 z5 z. n
        if (info->screen_base) {
, U: \- T2 `; m. j8 ?& O                /* prevent initial garbage on screen */: g, n9 `) f/ v$ m5 N
                dprintk("map_video_memory: clear %p:%08x\n",# D5 O! C! }/ Z
                        info->screen_base, map_size);
+ I1 J' b6 `7 O7 ^- U* y9 R                memset(info->screen_base, 0x00, map_size);        /*DMA缓冲区清0*/" O+ h* N* Z- o
0 l$ A. T5 h. T* I2 X
                info->fix.smem_start = map_dma;        /*保存DMA缓冲区物理地址*/
" P; e  ?6 n' w  F7 J2 @) |0 F' u5 Q$ }- y0 T3 ~
                dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
4 c2 p5 G% Z+ m, }; C; c1 j1 Q                        info->fix.smem_start, info->screen_base, map_size);7 D1 ]" Z6 h& ]4 `% [
        }
& B( H. X$ Z( B3 e% y) Q4 C: A, ?" V  D& T0 h
        return info->screen_base ? 0 : -ENOMEM;2 X2 T6 F  ^% d0 u
}7 q. I! w( ]( ?( C6 \. O) x0 j

; H( Z/ @  }. p  K1 V$ R9 [9 f8 u: p8 z0 M
该函数根据fix.smem_len的大小,分配了一个DMA缓冲区,保存了该缓冲区的物理地址和虚拟地址。. f: B+ S0 `; q0 ^, P

7 z" f* `, t0 C; h" f接着是s3c2410fb_init_registers:
9 {; I* N3 a& A, z4 i
# {+ R0 _" E6 N- I# ?4 O
3 @! o* G2 g* J9 h6 d/*
" i: K" X) a  i+ t * s3c2410fb_init_registers - Initialise all LCD-related registers
) a1 |& e: t# x6 i0 @: [0 W/ h */
+ c. w' w. l% s6 L3 G; K% \) Fstatic int s3c2410fb_init_registers(struct fb_info *info)8 V% p6 s: G. d* Q# B6 Z  l. g# |
{: i! j0 t. h8 @7 h0 q
    struct s3c2410fb_info *fbi = info->par;        /*par指向s3c2410fb_info*/1 o. s: T3 e) F3 R  G/ y7 V9 \
    struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;% e$ S$ A: i" t  M$ V" ]
    unsigned long flags;( g' i0 V7 c5 E, ?. p2 s8 ?! y7 j
    void __iomem *regs = fbi->io;
8 w5 K4 R( t- ?3 h- L6 N    void __iomem *tpal;
1 |. a2 Y+ @+ Y  m7 O; E    void __iomem *lpcsel;) ^0 p- t! n6 l2 ]8 m4 }
$ A& g  u. e7 p1 r2 G9 R* s
    if (is_s3c2412(fbi)) {# ~5 Z3 [& O: Y* K4 K
        tpal = regs + S3C2412_TPAL;2 Q- m+ c" @/ y4 Z. y* W6 D
        lpcsel = regs + S3C2412_TCONSEL;5 e- ]- ~1 }/ _1 ^5 u* q
    } else {  |" g  v8 s! |; ?  a( p0 p, u4 g6 k0 C
        tpal = regs + S3C2410_TPAL;8 }3 Y: p" }" J6 R' G2 b
        lpcsel = regs + S3C2410_LPCSEL;
7 N) Y' Q3 U; n" }& x$ o    }
( C4 P! F& ?4 m% Q( c3 ?! A- E. T0 V! B. q: k. L. ?
    /* Initialise LCD with values from haret */! e( W  G7 C. \) `6 w! D+ r: [
- e1 J/ t2 |7 O6 P. R( }  N7 t" a
    local_irq_save(flags);                        /*禁止所有中断*// w7 X% m5 [: y9 O  V

& O0 C0 \7 r; A/ j# u6 `    /* modify the gpio(s) with interrupts set (bjd) */    /*初始化io管脚*/
# B+ T. J- c9 n4 O, u/ T! \* z, g1 R
    modify_gpio(S3C2410_GPCUP,  mach_info->gpcup,  mach_info->gpcup_mask);8 i! B8 g4 n+ W* l0 }' m% e, w
    modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);4 f1 j) H1 Z  {: f
    modify_gpio(S3C2410_GPDUP,  mach_info->gpdup,  mach_info->gpdup_mask);" p; g$ R" P( t  j8 G$ E( K
    modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
' M( L3 @  N: @4 |/ _; l  r2 P5 }; ~) ^- c, G) w
    local_irq_restore(flags);                    /*恢复中断*/
8 J& j8 N8 Q( C" V. A
9 S1 m' n% Q; k* y: i% A- c4 i    dprintk("LPCSEL    = 0x%08lx\n", mach_info->lpcsel);    /*设置TCONSEL,禁止LPC3600*/
& \4 u' m# i  [. p+ |8 Y    writel(mach_info->lpcsel, lpcsel);8 d% s. W8 n1 h" \; |
) Q, J8 |2 C0 B5 }2 q9 s
    dprintk("replacing TPAL %08x\n", readl(tpal));! ~! m% R1 n; v; U
# F% |' Q! n% [
    /* ensure temporary palette disabled */% B" S% K+ ^6 F2 ~7 A8 v6 v
    writel(0x00, tpal);    /*禁止调色板*/
* F) p1 Z3 s$ C. g* q
( i( V5 G3 s4 O% q+ @    return 0;$ s( Y8 R3 Y4 V# n: N
}6 [  \9 @8 C( p2 E: C0 Z

3 a5 V. c8 v$ A: X- ustatic inline void modify_gpio(void __iomem *reg,/ K. u0 J( L/ r4 m
                   unsigned long set, unsigned long mask)  z$ W" d& M# W
{
: ~/ p5 P1 s6 Y1 [0 u9 P; r    unsigned long tmp;
0 |. V: F* e( D3 g9 A* s3 k- |  E
* c0 z0 G! |7 s    tmp = readl(reg) & ~mask;
$ ^1 t+ N1 a1 o9 G* }- Z/ q# H    writel(tmp | set, reg);
  @! x" z' ~# i- [; {) h, ]% f}
/ B- N( G; f5 C2 A( O最后是s3c2410fb_check_var:  v& B; L" D3 p+ V

% @/ n* `$ `: N5 g1 m) X! m" Q/*) ~. j; t' w, G6 `
*        s3c2410fb_check_var():
! w- N+ M1 P# t* {- g5 b) h *        Get the video params out of 'var'. If a value doesn't fit, round it up,
4 ?9 D  }+ j7 d$ r4 q7 V *        if it's too big, return -EINVAL.
8 h% u4 ?# g$ h$ | *        检查变量的合法性% f3 j5 v& A6 _( E
*/
3 a! m6 W% @6 _9 Cstatic int s3c2410fb_check_var(struct fb_var_screeninfo *var,
4 R3 G, e) Z) ]; c# X( }                               struct fb_info *info). p3 |: i2 O- H! x- ^
{. {4 P0 l$ ]0 q# w' A- {6 E2 B& v
        struct s3c2410fb_info *fbi = info->par;                        /*par指向s3c2410fb_info*/
9 q& {& R  ?5 P) [' x4 B        struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;/*指向s3c2410fb_mach_info*/: G  v6 F6 L& @6 `
        struct s3c2410fb_display *display = NULL;
9 l: z' b% D# G2 F( o7 w. _2 p0 q        struct s3c2410fb_display *default_display = mach_info->displays +
7 f3 g. {# |9 l! j                                                    mach_info->default_display;: h, k- T1 L3 f. {0 p6 o
        int type = default_display->type;         /*S3C2410_LCDCON1_TFT*/! q3 t0 n, K$ Z# Q
        unsigned i;
: L3 j- Y8 H  S2 u+ X! L
/ g% ^9 n! E, [9 H3 E        dprintk("check_var(var=%p, info=%p)\n", var, info);+ J' m: C" K' M" Q
7 q- ?9 D0 T. n$ @5 ?1 F
        /* validate x/y resolution */; C  R7 w" T8 W
        /* choose default mode if possible */                        /*var中的成员在probe中设置*/
6 k& o9 ~* L7 W, G8 D8 ^: g! `/ t        if (var->yres == default_display->yres &&, u. A5 y; F& v4 p! E4 Y: v
            var->xres == default_display->xres &&1 S7 ?  P% M  G
            var->bits_per_pixel == default_display->bpp)  @6 t0 \7 U* d0 ?
                display = default_display;
  ]6 }; [6 i. b$ v- z        else& b+ F; d) E' T% w. l% l" p- K% G
                for (i = 0; i < mach_info->num_displays; i++)
$ Y) g/ {3 X# n; m9 j& ?                        if (type == mach_info->displays.type &&9 |1 |+ B( Z9 e- U, B8 R
                            var->yres == mach_info->displays.yres &&# ^3 D5 b7 K: N3 D% q- R
                            var->xres == mach_info->displays.xres &&
7 h! f( G, H& n/ g/ W. v: F3 v3 O                            var->bits_per_pixel == mach_info->displays.bpp) {3 ~, H" f. m  {' u4 F
                                display = mach_info->displays + i;* M; {/ s1 P# a1 K* v  @
                                break;
% s! v' t$ Z" P) m                        }) o# w/ N$ k( M, J6 B: e
1 s! Z6 C* k6 D( C$ j* o" q" V
        if (!display) {% G! z$ ~' t; K
                dprintk("wrong resolution or depth %dx%d at %d bpp\n",! n' V- p# Y% P
                        var->xres, var->yres, var->bits_per_pixel);$ O& t; ^& x6 k8 j& R, C* L
                return -EINVAL;
: J/ B- W* f7 |9 z        }% \$ K7 `, ^: y- h+ Z# m' N

, u1 O" \1 t% H+ H' C        /* it is always the size as the display */3 a4 J/ _9 P# B) G, ~* ~/ N4 ?# P
        var->xres_virtual = display->xres;
+ A6 u! A5 z; m, d# x        var->yres_virtual = display->yres;
% Z, u5 `- P4 `& \+ ]1 ~) f        var->height = display->height;! U7 C+ I# F; w) u& u* V5 O
        var->width = display->width;$ g) a3 @4 \# @/ _+ L
! o& h9 H+ {' y2 X8 |( y
        /* copy lcd settings */
9 @6 c: D1 P* K" j        var->pixclock = display->pixclock;% b% N" O0 u1 C
        var->left_margin = display->left_margin;# b- a- d3 _9 e
        var->right_margin = display->right_margin;9 f" O; }, ~1 |& V( Y2 o/ B" L  L5 O
        var->upper_margin = display->upper_margin;; p- s# C8 I) p& `, b6 q1 C
        var->lower_margin = display->lower_margin;: z6 K, s! _( E4 w$ h2 a
        var->vsync_len = display->vsync_len;# k% a# m4 B9 R
        var->hsync_len = display->hsync_len;  E$ y  r5 C' ]  U* c7 @! W

4 v; C5 Q4 e6 ]& @        fbi->regs.lcdcon5 = display->lcdcon5;
2 X! C/ x! h8 ?3 ^1 u4 P8 B        /* set display type */
: M- J; v5 a: }; ?4 R, C        fbi->regs.lcdcon1 = display->type;
4 S2 C  B, T( B. S; [& g* A4 G* U0 d2 [
        var->transp.offset = 0;8 a3 U4 q, I. \1 V
        var->transp.length = 0;
2 l9 y  |% y: T) b9 F  {4 Q        /* set r/g/b positions */
& g  f: z$ _$ f& s0 V* m- A1 P        switch (var->bits_per_pixel) {
$ g% b$ y4 w9 G        case 1:
3 z$ b6 c; q/ ]  c% H" N        case 2:2 [" o* C5 E8 [" b2 @7 C  R# |3 `2 R
        case 4:
, L+ d4 d5 _2 `/ ^2 e) x- f9 s                var->red.offset        = 0;
; E& s  [, ^3 o. |& E                var->red.length        = var->bits_per_pixel;1 b3 [0 A+ ~0 t$ @
                var->green        = var->red;/ x. ~2 ^$ P- l2 t2 X* E
                var->blue        = var->red;& M* X, r" l9 A, \
                break;
0 ~+ J, q+ [. Z6 W, S0 n8 n        case 8:
+ A, O% I/ D; ^$ ?2 `                if (display->type != S3C2410_LCDCON1_TFT) {" R& E' K1 H; w1 C0 y2 n, k
                        /* 8 bpp 332 */, l' A+ @: p0 L- v$ Z. T" v( Y0 y
                        var->red.length                = 3;% ]# ~) Q' y7 k2 d9 c
                        var->red.offset                = 5;
4 v8 K% n* _! ~+ o" T- @+ v! @                        var->green.length        = 3;
: C+ ]% i4 u. a: n                        var->green.offset        = 2;
) i, j: a: O$ u                        var->blue.length        = 2;, ]/ V6 I: g, B+ a
                        var->blue.offset        = 0;
; |! D7 O  O2 ?" b                } else {
8 }% O" K" o4 r4 H( s" C                        var->red.offset                = 0;
# X0 L% Y( M: V" F                        var->red.length                = 8;9 k( }: I2 M2 Y. D. q& T( r
                        var->green                = var->red;! B# j! b  x' ?+ `+ D" X& c! C
                        var->blue                = var->red;
2 O. C% y( d% J! z6 N                }$ P- ~. _7 o* R
                break;8 c! z8 q0 L$ r+ S7 \1 U8 Z
        case 12:
7 y) N" @# \: q                /* 12 bpp 444 */
; Q4 x# a8 S' r$ e6 M                var->red.length                = 4;+ M7 p  K/ V2 e4 P3 K0 ?
                var->red.offset                = 8;
# ]2 `3 E9 f8 _4 x$ ^                var->green.length        = 4;$ f: N& ?0 n% m, ^& U
                var->green.offset        = 4;
+ y$ b0 D# B% |3 Q/ c                var->blue.length        = 4;
  n5 b3 v" x9 i9 R8 G                var->blue.offset        = 0;4 r+ b" F" k' j) v) p  [
                break;
9 u$ N* {3 Z; G) R4 `
! Z) ^! A* q1 q' k        default:& S0 m  k, A# U
        case 16:5 C/ _! G0 X) x% w6 T; g
                if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) { /*使用565格式*/
, ^* g5 m/ o5 x. A$ _                        /* 16 bpp, 565 format */8 }; o2 k! `+ ^# A/ |
                        var->red.offset                = 11;
- b2 C6 G6 A$ l; w# N2 t                        var->green.offset        = 5;# h9 I4 @0 _% ~, Y7 i; H
                        var->blue.offset        = 0;6 K( p# y& s' G5 @6 I8 T
                        var->red.length                = 5;
# k, o. D4 i( P: }# E  I- K/ \                        var->green.length        = 6;
  N) z' v$ ]$ ~6 R! W: ]                        var->blue.length        = 5;9 o. S- }% @( N. ?9 K
                } else {
2 g  I9 Z# G* Z4 L/ o                        /* 16 bpp, 5551 format */
3 k# o8 d- i2 X) {                        var->red.offset                = 11;
. L* e3 J5 I0 e) u                        var->green.offset        = 6;
2 Y" o2 [0 u" Q6 ?0 _8 [3 u                        var->blue.offset        = 1;( R) a4 E2 b" {) n$ k9 C
                        var->red.length                = 5;
8 _$ r- y  D3 T+ c! T! {                        var->green.length        = 5;" D$ R9 ~; `6 b& R& q4 l
                        var->blue.length        = 5;
3 J: d  z3 ?4 R3 M                }9 z1 n5 V$ S" J/ k/ b2 u1 Z
                break;! o% _9 K/ V/ R" K0 j
        case 32:
) d; b" P; l! k9 }. A/ G5 x/ w+ _                /* 24 bpp 888 and 8 dummy */
4 I8 P' `( `7 k4 k  ?1 V. ~5 O                var->red.length                = 8;
" C8 a3 ^: h/ N% a                var->red.offset                = 16;1 H6 N6 ?% N# s4 j" a( s, K
                var->green.length        = 8;' w" n4 B. w" R, |: h4 c* T4 Q
                var->green.offset        = 8;
* ]( D9 X3 ?7 \2 g                var->blue.length        = 8;
9 b9 k9 m1 b  v                var->blue.offset        = 0;) Q4 r" K! P/ h! f) H3 c
                break;. w4 c, ^: }  y3 p# |
        }6 P# m' v* r+ v+ N! N' }! e
        return 0;5 A* g8 j2 m9 ?; D8 w
}) m/ m  O) j) ~+ Z7 e8 _9 Z+ M
; t- b+ d% m% n3 V3 W& Q% R8 E
/* Interpretation of offset for color fields: All offsets are from the right,
; G" F; t7 {( T' ~. ?: J * inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
8 B" P: r; \# \2 t% D/ ]4 e * can use the offset as right argument to <<). A pixel afterwards is a bit
8 I: b: ~; ?1 z4 D' S( W' I * stream and is written to video memory as that unmodified.8 ]; `9 P3 j2 X/ q; g2 }5 q
*) P0 }( ^% k: o& ?/ A! y2 \
* For pseudocolor: offset and length should be the same for all color# A7 o% w' j2 e8 S( H% |. g/ W
* components. Offset specifies the position of the least significant bit; V! q& N8 D% w% Y' p
* of the pallette index in a pixel value. Length indicates the number( O, X, `  {  H% c. D* ~
* of available palette entries (i.e. # of entries = 1 << length).5 l  w, N. P5 Y, G$ [$ K: o* c
*/5 z7 o) l2 N2 Q2 H$ ]( b
struct fb_bitfield {
) f) @! h6 E! W  C    __u32 offset;            /* beginning of bitfield    */8 c. V5 B( E9 r4 Z" P3 M- D
    __u32 length;            /* length of bitfield        */
5 C& E3 q3 p8 E0 o3 F0 s    __u32 msb_right;        /* != 0 : Most significant bit is */ 2 _" y) F; _4 W0 u* o3 Z( x
                    /* right */ . U) [6 p5 C1 L7 V
};
# s  `+ F8 ]4 ?% j6 v2 E9 H9 o该函数主要将板级信息s3c2410fb_display复制到对应的地方,然后根据RGB的模式设置位域。
0 a6 d: ]9 Z* s. \& q4.3 fb_ops方法* d; p3 F& J4 u; V  W! h5 g! U6 J, W
在驱动程序中,定义了fb_ops,如下:
& w& @+ t: ]/ _" {5 S0 w' h" n9 D6 F8 M/ y$ J9 b
static struct fb_ops s3c2410fb_ops = {
9 s8 u. _+ n1 b' a' \' g        .owner                = THIS_MODULE,
# s% @% G1 m& I! V+ J* j0 g        .fb_check_var        = s3c2410fb_check_var,                /*检查变量的合法性*/% o& m2 e) P8 I
        .fb_set_par        = s3c2410fb_set_par,                        /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/
% B" x$ C: ^0 F$ ]        .fb_blank        = s3c2410fb_blank,                                /*该方法支持显示消隐和去消隐*/
" h2 L4 ?5 W9 `* x" W        .fb_setcolreg        = s3c2410fb_setcolreg,                /*设置颜色寄存器*/* W1 X2 E8 ~; I; e/ Y
        .fb_fillrect        = cfb_fillrect,                                /*用像素行填充矩形框,通用库函数*/* {) n) X' y! Q# C2 n$ ?
        .fb_copyarea        = cfb_copyarea,                                /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/) }4 v2 }" ~( K
        .fb_imageblit        = cfb_imageblit,                        /*显示一副图像,通用库函数*/1 h( w$ |; s& [6 j- q2 p( A
};
/ w( ^+ W2 ^) V
: D  g. Z) _# Z) O& p. U% a* n; `其中s3c2410fb_check_var在4.2节中已经分析过了,最后三个方法是通用库函数,在这里不作分析。
. I+ K/ d; J) c& d7 P0 L剩余三个也是驱动程序提供的,现在对这三个程序进行分析。  y7 }. o) E* H% M8 M3 X( l
4.3. 1 s3c2410fb_set_par
3 e& q) K5 w, K! _; Z+ K9 e; l/*
- D) s/ k9 T( _% g: x *      s3c2410fb_set_par - Alters the hardware state.
% F7 V# E! y! t *      @info: frame buffer structure that represents a single frame buffer0 g+ [5 A) R9 [# t  o$ }( K" d- o
*                根据var中的值设置LCD控制器的寄存器
1 ]; ?& e4 k8 H! J! k */
# T# ^$ M0 B/ R+ S; p$ wstatic int s3c2410fb_set_par(struct fb_info *info)
) q3 P3 E$ M# i7 b! |{5 T  g! g" m5 n- g3 E6 F; x7 D
        struct fb_var_screeninfo *var = &info->var;4 C/ p2 ?9 b* c. s% ?

+ O% U8 Q$ K4 ?  g        switch (var->bits_per_pixel) {+ c& Z+ `4 N4 q& `9 \9 P
        case 32:/ n/ U! f# Y0 E2 B( W" j
        case 16:
8 c9 d- U' c8 _8 I) ]        case 12:# e3 [3 w! j9 I9 ?
                info->fix.visual = FB_VISUAL_TRUECOLOR;# r5 k" f/ p4 B. M; {
                break;# i8 ^) E, n* K6 g; d
        case 1:
, \! H3 n. F0 v  @5 l9 b8 K! [                info->fix.visual = FB_VISUAL_MONO01;
% `. T7 [- m  l6 N, ]                break;
5 X& A  v! _$ R$ u        default:
& Y$ w2 ?1 S& \  B! N                info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
( Y$ ~  `% ?; v2 x' I- R2 E                break;
$ x( Y0 g5 C8 l8 z        }$ W3 e1 O1 ~/ B( p% ^
7 E3 ]! l, n8 X. Q1 y# {+ C
        info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; /* 320*16/8 = 640Bytes */
8 [  W2 l+ W2 @6 ?- }6 j0 J: y
( _! E" B  E% U; g  z/ g4 k- y6 c        /* activate this new configuration */' [1 Y7 Y) p3 N
& w2 E' Z! q+ M1 y% O* `' m
        s3c2410fb_activate_var(info);- Q1 R! [$ J* J% R: ~
        return 0;7 d0 X2 ]& [: l/ G$ C7 x1 n
}
! H- b8 d" r  ~6 z. T, e+ g该函数根据像素的位数设置了视觉模式,本例为16为,使用真彩色。然后计算了每行的数据元素大小。共240行。7 u6 z7 J! ]7 G% D

9 n) L) @( v- v( D! B然后调用了s3c2410fb_activate_var来设置控制器并激活LCD。
; _" q( n( \7 D! `- B; w4 ^, @7 v7 F3 c8 b9 T
s3c2410fb_activate_var函数如下:8 G- r: R0 R; r% c

2 x; q, s* {- t* A$ D% o/* s3c2410fb_activate_var/ g6 e4 d! ^2 k! t- Y8 F% y
*
) P/ \1 `; q& K( ?3 U7 b3 R8 @ * activate (set) the controller from the given framebuffer
' j( M6 k) R% }, I6 x& S# ?$ X * information; Z3 y7 S3 |( F: I2 d" d
*/9 A  K  M: e! }4 _0 L- t
static void s3c2410fb_activate_var(struct fb_info *info)4 R5 p& _5 l, u% J" h1 Z' \
{
# V; e+ f0 @" d) Z2 X( v4 P* f        struct s3c2410fb_info *fbi = info->par;
  W" ~% z1 v. t        void __iomem *regs = fbi->io;
: E- Z) G& |+ I; i1 T        int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/
1 z# {; }3 P; j        struct fb_var_screeninfo *var = &info->var;
. }/ z/ ^( ?# K! e1 Z$ r        int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;: {! q* R+ A0 ?5 _
) z% K/ G" c7 F) Z
        dprintk("%s: var->xres  = %d\n", __func__, var->xres);* f4 A5 _! F& _$ o
        dprintk("%s: var->yres  = %d\n", __func__, var->yres);
4 _* @) `) z9 X2 |  ]        dprintk("%s: var->bpp   = %d\n", __func__, var->bits_per_pixel);
9 g; R  @1 _8 i' Z$ \* z* l( _2 k
, M0 H' D3 U+ ]0 B  Q        if (type == S3C2410_LCDCON1_TFT) {8 q0 i2 n, O, u1 L6 h
                s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/
& u8 b& f; ]3 Q6 B                --clkdiv;: I5 H/ L3 ?- Y* ^
                if (clkdiv < 0)
% I2 z  W. |+ z0 n                        clkdiv = 0;
5 A' V' u  ^* m! p        } else {7 V+ J  b" P1 z
                s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
3 [1 W- ]- W$ M+ m1 P# F3 Z" M                if (clkdiv < 2)  s+ O' L% i% B4 X2 O
                        clkdiv = 2;
. s  j5 f% Z0 ~/ W$ a        }
9 O8 R% ]5 V4 S) R, ^0 K9 N& h, D
        fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/
. y' S; {/ L* z8 q3 \: Q/ {2 d( i% w+ s5 v7 N- |
        /* write new registers */
' l0 f7 E' R, O) E6 j' c( p) W% X* q
        dprintk("new register set:\n");
( o! t, H3 C" r        dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);- h" c* E, Y  g, z$ p- W" c, M
        dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
- g! u( D% T: s        dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);; ]) V* }. d5 k( P# c: a' k
        dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);( F$ U, U) s8 n* S
        dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
% T& f2 ?; N! k. \& a        /*把计算好的值填入LCD控制器中*/. ^$ \3 o1 S: M- V3 r
        writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,        ! X" k" d0 S9 r, I+ X+ L
                regs + S3C2410_LCDCON1);                                                /*仍然禁止LCD*// w8 H6 i+ b! F. D
        writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
& Y3 o* y  l, B$ z/ J        writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
7 D2 ]% S$ ^7 m/ S0 w% i$ V        writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
/ Z+ b3 O/ u  ~& M        writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
! ~5 z- ]# H  o8 N5 D$ I: N6 R/ q: q$ h
        /* set lcd address pointers */# x1 S4 T8 }* L! f& K4 g
        s3c2410fb_set_lcdaddr(info);                                                /*设置LCD帧缓冲起始地址*/$ r, v" r3 e4 K$ K& t, h& P

0 ?2 t' v9 G& s( u8 x        fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
6 z( U5 A$ n3 N- Y) E- \0 @6 h        writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);        /*使能LCD*/7 a2 v% ?- E! U
}# e8 T6 g3 {4 [8 o  e- G
其中调用的三个函数如下:+ y" I8 K: s8 Y' z, Y, u
static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
  o! s) b0 k6 c- G) X                      unsigned long pixclk)/ L8 j. U* T; X) D( L" Z
{
# w; o9 x" W: W" a    unsigned long clk = clk_get_rate(fbi->clk);         /*获取当前时钟频率(Hz)*/
4 I# v' N$ q5 q* t1 @8 Y    unsigned long long div;
* ?' j2 a4 W# g8 `  I- [. w' ?1 u# c6 b9 Z4 ~& ]
    /* pixclk is in picoseconds, our clock is in Hz
  e- |, t1 W" N     *
, k% u2 x: g9 l, W     * Hz -> picoseconds is / 10^-12; z6 A; I0 T" w. N4 L  [
     */
: b) r# C" D1 i7 f% p2 }* A! t9 ?* z
    div = (unsigned long long)clk * pixclk;$ F, J. }8 K1 y" F0 G
    div >>= 12;            /* div / 2^12 */
1 O9 B2 b4 a: e* o- e" ]5 U    do_div(div, 625 * 625UL * 625); /* div / 5^12 */
& t  x" h( u( J
* U( a# |6 ]! Z9 a4 X    dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
% G  F+ ~$ T$ D3 \0 n5 ?    return div;2 n0 N# E  F" t7 F+ e
}+ |. Z1 `. Y3 T3 g* Z" ~

0 V7 }- W7 O" o* v: u/* s3c2410fb_calculate_tft_lcd_regs
5 R( P) C3 I* m2 I *( ~) I4 j0 |) y9 M( j+ w& f, z6 H
* calculate register values from var settings
/ U% ?8 }, {) K3 l; l */
- E% T0 N! S1 k9 Q, r" }static void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info,
8 k) o" t* z, N  a5 B. {                         struct s3c2410fb_hw *regs)
9 S  `( G! u0 V- G{
# p0 Z& q+ M2 @    const struct s3c2410fb_info *fbi = info->par;
2 y3 h) Q! O) m    const struct fb_var_screeninfo *var = &info->var;- f5 i) y+ N) x' K, O. G" `5 f

9 h0 c1 O( d0 ~# O! J8 Z% }    switch (var->bits_per_pixel) {- H, K+ x$ j, [8 |1 r9 S& C# v
    case 1:
( ~, F* m/ }2 e+ H  z& Z2 X        regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;; \6 K  s, I3 O' L  \* @
        break;
6 f! G3 P* U. a7 a& x    case 2:2 x% N4 M8 [, d# ~
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;0 |% H; @! r( B; O0 m* P4 y
        break;
1 }! N+ S& ?  G6 E    case 4:
- N- K7 w4 x6 O8 ]7 O        regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;
% [) _. ~/ G1 E: ]$ q+ l        break;
" `- I- z$ v- B4 ?4 q8 y7 z    case 8:
9 Z. x: ]- B! j' i& C' n        regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;# E7 \& h& A" A- g
        regs->lcdcon5 |= S3C2410_LCDCON5_BSWP |
) h  O  F% I% `                 S3C2410_LCDCON5_FRM565;
  n0 ~' }' A* B7 ]) v2 F- m        regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;, o. n  h, V: j
        break;
: d0 _. o: x2 S    case 16:
; Y# S7 M0 i* z! A- \; w4 V        regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;
4 v! S2 P2 [- Z* ^8 E7 N5 B+ |  O. T; h        regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;3 K( r2 I0 f8 r3 j2 q' |
        regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;) E% ?9 D1 ~; v" K4 o0 _! l+ B
        break;2 P8 x/ w  j& D- r, P5 {
    case 32:' |/ u* j4 t/ i, L" o  x
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;
5 F! \: s( B8 ?- \% T! e* W        regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP |
! M" v2 [4 W2 n0 r. B                   S3C2410_LCDCON5_HWSWP |
7 J6 R- q# p4 s  o0 s$ ?                   S3C2410_LCDCON5_BPP24BL);: q3 r9 ]6 s" E( v
        break;
! {; @5 |% c8 X( |8 v    default:
) ^* b; q8 ?0 d  u% Q* F        /* invalid pixel depth */, i. l# T9 h. m( I
        dev_err(fbi->dev, "invalid bpp %d\n",/ V3 m/ e9 @. ?+ p" x* a) N$ [
            var->bits_per_pixel);- I2 B- r2 }. ^8 s* a! |
    }& Q& ^8 ?# b5 }2 C" g' g- ^3 J2 F
    /* update X/Y info *// ]7 O! |" w/ Q2 g
    dprintk("setting vert: up=%d, low=%d, sync=%d\n",! N7 r' Z4 G) {- _* |6 F
        var->upper_margin, var->lower_margin, var->vsync_len);
  A; C% `& _0 R9 W" n
3 l2 r4 K) v# C! A    dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
. r0 d. T" W7 s$ P6 {" w8 G6 {+ ^        var->left_margin, var->right_margin, var->hsync_len);0 Z* A" W2 m) e4 f( h0 I
    /*
4 E# W7 W# E5 o        所有时序参数必须减1,因为在公式中:
" h/ h2 P# J& Z3 n2 ^$ S; s) r        Frame Rate = 1/ [ { (VSPW+1) + (VBPD+1) + (LIINEVAL + 1) + (VFPD+1) } x {(HSPW+1) + (HBPD +1)/ ]3 s3 s, p3 m
        + (HFPD+1) + (HOZVAL + 1) } x { 2 x ( CLKVAL+1 ) / ( HCLK ) } ]
5 X! P2 b4 L9 v$ i5 H    */
8 K0 q/ S; J3 k4 ~: c4 v( x    regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |4 u! X! c+ ~2 C/ Y7 {, o$ c2 ?+ R' ~
            S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |
- p' Q) ?# Z1 A. s; q# Q' U            S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
" `" w; C) m- `- I0 f            S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
  f# g8 s( Z; }$ F0 \( n2 z7 k" t7 d  N+ H1 J$ T( ?# P
    regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |) D# T0 G2 |) ~/ Z9 r
            S3C2410_LCDCON3_HFPD(var->left_margin - 1) |3 _* V' _1 i1 F. ~* G( P+ f
            S3C2410_LCDCON3_HOZVAL(var->xres - 1);
  v# v& J; g5 d. D3 t# v
9 r: Q# t0 b8 [1 ?    regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);) J, K0 B# r# ?$ s( _
}
. Y7 M. d+ n5 P9 Q! z
; s) T( `! B, [9 _* L# L( `/* s3c2410fb_set_lcdaddr
0 H$ `9 D) u6 z/ j" w *
$ q5 X. e* H  V4 B * initialise lcd controller address pointers
# a3 w! Y3 g/ ]$ e7 j1 v */3 @$ w: |+ b9 W' X
static void s3c2410fb_set_lcdaddr(struct fb_info *info)' i& M2 Y# i9 @: D2 P
{  {1 g. K1 W/ B/ g# z+ T
        unsigned long saddr1, saddr2, saddr3;/ Q% f3 a! r4 t1 D/ v
        struct s3c2410fb_info *fbi = info->par;1 o- x8 @1 E$ ^2 d; e
        void __iomem *regs = fbi->io;
( |6 l4 ]# w7 L( H: P/ l( K, s' g3 r6 [0 _# \8 B
        saddr1  = info->fix.smem_start >> 1;          /*帧缓冲区起始地址*/
- j1 z3 u: C0 b        saddr2  = info->fix.smem_start;                        4 P) A( |/ M3 a9 u
        saddr2 += info->fix.line_length * info->var.yres;
. W  r8 ~7 O& I3 x$ j" H        saddr2 >>= 1;                                                        /*帧缓冲区结束地址*/, [% d8 @) T8 J* t  E

4 @$ ^9 t" Z( Q3 ]2 r+ O3 Q        saddr3 = S3C2410_OFFSIZE(0) |
% d0 n, k- Z8 A2 o+ u/ e                 S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff); /*offset = 0, pagewidth = 320*/; T: u7 t3 B9 q. p! ]& G  Z
$ q7 P! M: A" ^3 z: h6 T: G5 A4 `9 d
        dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);
' B4 L8 s( i  l6 y5 e( G. t        dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);( F4 ]1 N$ \4 T7 Y5 q
        dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);+ ~9 ^( j# q: U+ D% \" t

1 q& _) ?/ Q" k        writel(saddr1, regs + S3C2410_LCDSADDR1);% c# }: b- T5 _, I. p) `3 K) }
        writel(saddr2, regs + S3C2410_LCDSADDR2);
: j% J' N8 m/ }  x        writel(saddr3, regs + S3C2410_LCDSADDR3);  l- T) C3 ]9 G; c7 A  k& n
}" ]; D1 O& F% X1 G
s3c2410fb_calc_pixclk用于计算时钟频率。
' W4 d9 ]4 y, B) V) [% o) {% A6 J# K
s3c2410fb_calculate_tft_lcd_regs设置了LCD的控制寄存器。( L* x: s7 ?+ l& W4 P: S7 b* |( o
- I5 b- x8 t1 l# P, L2 ]5 L. J
s3c2410fb_set_lcdaddr设置了LCD的地址寄存器,该寄存器的设置请参考datasheet。% \" _. C( D7 n/ @, _- P
& I0 ]2 G( ~% X+ P$ Z3 O
4.3. 2 s3c2410fb_blank3 z5 t* c1 J2 l/ r' |
该方法完成消隐功能。$ x+ w: c1 Y8 p8 A% I

4 w, E8 X0 H9 V* T7 a+ I/*# A8 v/ c* {" Q+ }
*      s3c2410fb_blank# ?3 z9 G$ Z: @7 N! v! P
*        @blank_mode: the blank mode we want.  w* S8 w, \" E0 U/ Y
*        @info: frame buffer structure that represents a single frame buffer
1 W$ w8 j5 S' h+ M3 T+ @ *
. c, k( L4 K9 o) v+ S8 n. E *        Blank the screen if blank_mode != 0, else unblank. Return 0 if
; L2 Q, [7 ]: ?8 T5 _4 o( O; x *        blanking succeeded, != 0 if un-/blanking failed due to e.g. a
# }7 S4 G9 i2 V6 m *        video mode which doesn't support it. Implements VESA suspend
+ r" ~' D7 F6 q" k  D *        and powerdown modes on hardware that supports disabling hsync/vsync:
' y# w0 a; B7 I *+ j* b# ^' I1 W8 U, u2 [0 J
*        Returns negative errno on error, or zero on success.
1 N- H7 \1 J* f# ^$ y* R0 r5 X6 O *7 Z" J2 {0 v& h, Q9 d
*/
$ w, X7 _/ O+ u, Pstatic int s3c2410fb_blank(int blank_mode, struct fb_info *info)
. m# g% j* U$ y* p8 z+ Z2 r, x8 D{
: H% J9 _. F6 c. m/ B: P    struct s3c2410fb_info *fbi = info->par;
% y, o$ \) N9 p  x& `4 c    void __iomem *tpal_reg = fbi->io;( C! w+ w' ^' e8 B$ V2 y- }4 T

" f5 F! e! v5 P1 W    dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);
& X: Z( u+ U0 {. c& S
* u& C. G' N3 r  A6 w/ h$ x    tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;
" o- ~; h) U; \% z: y2 v  R2 f
6 X+ z  `- K5 H- P4 m    if (blank_mode == FB_BLANK_POWERDOWN) {    /*消隐*/2 A- m- h1 W; R9 w1 y
        s3c2410fb_lcd_enable(fbi, 0);        /*禁止LCD*/5 P8 T: L6 F, d5 C, |
    } else {. O, S9 `+ X1 g- @
        s3c2410fb_lcd_enable(fbi, 1);        /*启动LCD*/
( M' r6 K$ ~6 _# |+ u- `$ m  r- o! ?    }
/ ]1 ^$ q' s0 v# q4 r* l. R; o- {5 f. f6 l9 {" ~
    if (blank_mode == FB_BLANK_UNBLANK)        /*去消隐*/
1 P  _8 a' Q! }9 ?2 a        writel(0x0, tpal_reg);                /*禁止temporary palette*/% p" _: G5 m4 c. Z2 }" _
    else {                                    /*消隐*/
, R" k# x$ `& |4 j        dprintk("setting TPAL to output 0x000000\n");( ]4 v$ l% s  H! @& e5 S: Y7 B- _
        writel(S3C2410_TPAL_EN, tpal_reg);    /*使能temporary palette,颜色为黑色*/: ?8 j8 e" Q4 G6 i
    }
" D8 U+ A+ Z* h1 w' |" m, f1 g) o  K* x7 X% E  b; L( D0 m) m, O
    return 0;
4 U! r0 A) g* j% r/ D6 l}, @4 i4 h/ Z; J4 x* M
在消隐时,屏幕将全黑。
; o; t9 K9 e) C" _' R+ @9 T/ z: d$ z
4.3.3 s3c2410fb_setcolreg
% @* x4 d/ z+ W9 j; V- p( F0 d该函数的功能用于设置LCD的调色板。调色板的概念请看我的转帖:LCD调色板。1 _3 M' A# R7 _

- R6 G9 v$ L  m1 wstatic int s3c2410fb_setcolreg(unsigned regno,
' J- s7 H( l8 B  e1 P                   unsigned red, unsigned green, unsigned blue,0 U" S9 y) j" O( O
                   unsigned transp, struct fb_info *info); A; }3 k% k" T. }
{
, a! ?6 {' G) C  L" ~- S    struct s3c2410fb_info *fbi = info->par;    /*par指向s3c2410fb_info*/8 q3 _% o3 Z$ n& v0 _8 y
    void __iomem *regs = fbi->io;
+ Y0 l& C: Z  V- ~9 r    unsigned int val;; v) Y1 V  z" D% x! S
0 S9 I5 L  s7 |6 ?  g% D: a1 n
    /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",5 m2 l8 F' E: }  B2 O$ [
           regno, red, green, blue); */
' ?' P9 l. D' i4 M& w
# n2 K9 Y4 x* D$ _1 }    switch (info->fix.visual) {    , K* k# w+ \5 r5 h& g
    case FB_VISUAL_TRUECOLOR:    /*使用真彩色*// W2 z* k- v+ x8 q6 \$ t
        /* true-colour, use pseudo-palette */
. j# p- D4 M" D* n' n
/ ~- q  B7 F$ q$ l        if (regno < 16) {    /*16种颜色,为什么只用16种颜色???????????*/
* H7 _/ Y+ E( E  C( ~1 d            u32 *pal = info->pseudo_palette;' a  l; M; U% c3 O
7 n' @" \! z. l) e
            val  = chan_to_field(red,   &info->var.red);
0 ]" e/ g; d- ~: V% O2 U            val |= chan_to_field(green, &info->var.green);
7 [& {1 f2 v# J% r# g3 M% n, u3 P1 N3 D            val |= chan_to_field(blue,  &info->var.blue);
& M7 M4 O' }: q# j& a' F$ L7 ]/ P, s, Q+ U- Y
            pal[regno] = val;    /*保存颜色值*/8 T( t+ M3 g  c3 b& U. Z# c. B
        }3 U* H7 Q3 Z+ c0 u9 j
        break;5 y8 \( r9 q1 }* M+ g

, x( C5 L8 e8 Z8 `3 |    case FB_VISUAL_PSEUDOCOLOR:
. J) A! c; W. I; {        if (regno < 256) {& {! M. r4 E- f2 Z" w4 o. Q" ^
            /* currently assume RGB 5-6-5 mode */
7 y2 \! h; }8 y$ _7 n2 ]% E8 R( n" k! |0 O$ C$ ]8 v
            val  = (red   >>  0) & 0xf800;
# g, y! o0 p5 e/ G            val |= (green >>  5) & 0x07e0;5 g7 i1 S$ q/ N4 y
            val |= (blue  >> 11) & 0x001f;8 P2 \! Z# S9 a9 D* z3 n
' \( p( h9 D+ V; l7 J2 u5 A$ M
            writel(val, regs + S3C2410_TFTPAL(regno));
. b" H$ S. @% H& ]0 l) N3 z" V            schedule_palette_update(fbi, regno, val);
7 x0 s2 _! z! H6 ?9 P0 L+ u: n% [        }5 B# k* [& w0 g: W

! ]; d# a; L3 X        break;
4 f  |1 U& a+ r/ x* o4 F1 b# m. i5 ~( ~( e
    default:
2 @% D4 y( U) V* A  @/ Z        return 1;    /* unknown type */
9 `8 c- z" N% W4 F) C    }9 x4 a3 f8 H! W

' T2 M. g/ u$ v3 Z    return 0;' I' R5 O4 ~5 O3 l. N& Y
}
7 r1 m# W2 i* H, }/* from pxafb.c */
' q$ }! H7 U. g2 v* ~: y6 kstatic inline unsigned int chan_to_field(unsigned int chan,
. d7 F5 a5 y* @7 d$ ], ~                     struct fb_bitfield *bf)
  B' j$ i; \$ R5 E& X+ N{    ' @0 b) b5 q) X5 T5 R9 W" u- X: _
    /*下面用到的length和offset在s3c2410fb_check_var函数中设置*/
6 ^. I4 F* ^; q* z, T    chan &= 0xffff;                /*取低16位*/
( e6 I" V% E" k* V" P$ Y! L    chan >>= 16 - bf->length;   /*取第length为到16位为有效位*/% {7 c0 {) Y+ n6 j+ d3 @7 c
    return chan << bf->offset;    /*移动到相应的位置。*/
: S" O& ^3 X% f! W}) @) }4 z% U9 K. c( Z' b4 [
我们使用的是真彩色,可以设置16种颜色,颜色的位域值通过调用chan_to_field获得,然后保存了颜色的值。但是比较奇怪的是,这里并没有将值保存到0x4d000400为起始的内存中,不知为何。 反而倒是在伪彩色模式下, 使用了writel(val, regs + S3C2410_TFTPAL(regno))写到内存中,然后调用schedule_palette_update函数来更新palette表。我们来看下。
" W3 Z$ @8 r7 {* U& }- V# W% j- g7 }: ]" E
static void schedule_palette_update(struct s3c2410fb_info *fbi,- n0 [0 `  L% g1 V+ W/ L. s
                                    unsigned int regno, unsigned int val)1 k$ ~1 j! g" ?& r
{
# s% @" e8 ?( D# c        unsigned long flags;" s1 r. J' V% g& i( l1 R
        unsigned long irqen;- g+ T5 `5 A# \$ {+ N/ |
        void __iomem *irq_base = fbi->irq_base;' i8 N0 _; e1 y) ^9 }% g

/ `" k& _# r; I7 D        local_irq_save(flags);3 b% p2 I8 w- Z2 Q  X
. s) X& @0 Q; U6 s2 [: x* U& a  P. @, s
        fbi->palette_buffer[regno] = val;3 c( T" I! C- L5 \5 X8 `" `
  X' h4 g" B3 {+ u
        if (!fbi->palette_ready) {& O8 K6 K$ `# @5 L8 m  G
                fbi->palette_ready = 1;
# M( N' _  T0 w! h- V
5 q2 `% k* f. b% `+ h, ^5 ^* C                /* enable IRQ */( ]6 A1 V; w1 ]. `* h  W
                irqen = readl(irq_base + S3C24XX_LCDINTMSK);3 a' K" E0 j8 d
                irqen &= ~S3C2410_LCDINT_FRSYNC;
  E  Z" b% X9 q) Q) y                writel(irqen, irq_base + S3C24XX_LCDINTMSK);
7 n% s+ B. o4 V1 c8 K9 e8 Z        }1 x' l; ^2 {% S
& U, f, U- \( C. x0 j3 u
        local_irq_restore(flags);
( X0 O9 F0 P, ^/ a  N  v9 O( |3 D}1 b* T: v4 K, H+ A
这个函数的作用就是开启了LCD的帧同步中断,这个中断在VSYNC信号从无效变成有效时产生。当中断产生时,会调用在probe方法中注册的ISR。ISR如下:, W9 p+ j& `6 d
static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)- |" D5 v+ Z% D0 ?
{
/ o0 e5 T$ x2 h& O4 J0 M        struct s3c2410fb_info *fbi = dev_id;3 B4 C0 Q( A- J
        void __iomem *irq_base = fbi->irq_base;3 F) Q" f1 u4 a7 ]2 p
        unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);
5 H: O, \' B% X- ~" y& zb& ]) w7 d- q2 b6 ?2 y/ s+ q/ M
        if (lcdirq & S3C2410_LCDINT_FRSYNC) {. S" h7 M# e# ~+ X5 }, }
                if (fbi->palette_ready)
; ]: f6 [5 O( h+ ~6 y5 Z                        s3c2410fb_write_palette(fbi);" W, H! y% N% s1 e* L5 p" [
) j" W7 B; c1 y
                writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);3 g6 a# i2 C" B; W
                writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);% j& Y0 o6 {# T3 Q
        }" U6 U# X0 q# O' n# i+ Z
0 I7 V% @0 s3 X( [7 s
        return IRQ_HANDLED;% L2 a0 c6 L- R# s& w8 x9 {
}
1 L( H8 V1 H4 Y$ n
1 ]3 {: E8 ^# x; O5 h. D7 O: astatic void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)
1 U* |; x8 z# P, ]5 G. l{1 h- U3 @6 b; F/ E8 H3 ?
    unsigned int i;
+ I& Y) E4 s3 Z2 d: C8 _    void __iomem *regs = fbi->io;
7 }8 x- O  R; B% B
; u7 n; v* }6 B# u8 ]$ N. E2 u    fbi->palette_ready = 0;  /*清除ready标志*/5 G; M6 t/ L4 B9 ^, y: Q, M
5 g( |0 G2 _7 S. ?, p4 m! y8 ~
    for (i = 0; i < 256; i++) {9 n" ?4 \, c" p% ^3 |
        unsigned long ent = fbi->palette_buffer;$ ~' h: }% ^" r! N9 G
        if (ent == PALETTE_BUFF_CLEAR)" M8 `5 a" ], p! `" `! G8 \4 x
            continue;
: c& V; x! r7 f/ E9 z5 r
3 d# J& \2 x$ g, q6 v) B# Z7 `1 ?  d1 k        writel(ent, regs + S3C2410_TFTPAL(i));7 q7 T% U+ ?; N7 D- s; v: r

5 C/ N" K9 ?* S6 W        /* it seems the only way to know exactly, k. g$ }& L  C1 D
         * if the palette wrote ok, is to check
' Y1 E7 Q7 Q/ L) L         * to see if the value verifies ok
- ~! D7 g% o1 r, _$ Z8 Q2 \1 r& b         */
8 u" M: R3 d4 O- c1 D$ l2 @, y* C; N0 N  C
        if (readw(regs + S3C2410_TFTPAL(i)) == ent), i2 k+ N' l8 u
            fbi->palette_buffer = PALETTE_BUFF_CLEAR;8 L' @3 Y4 Y2 r5 l/ @  I, z
        else* A; s$ `/ k( g5 @' \& y) v' R: f8 h
            fbi->palette_ready = 1;   /* retry */
" D0 ]" g7 p/ b6 t5 m    }
* @/ |* g' g" K0 v}' @: i8 @) C8 {0 O
在中断函数中调用了s3c2410fb_write_palette,该函数对写入内存的颜色值进行检查。如果确认其已经写入,则将palette_buffer中的清除,否则retry。0 C0 d! y! k* l

( P( ^5 V# h% M# i  }  H- j5. 总结; D8 \* s9 V9 Y9 z
本文对frambuffer子系统做了简单的介绍。frambuffer子系统的函数相当之多,在这里是不可能一一介绍的。本文首先介绍了主要的数据结构,
0 B( a2 ~: B) s1 B
: r6 K# q+ V9 `+ Z- c2 [' v随后分析了frambuffer核心层,在核心层简单的分析了5个API函数,接着,对驱动层做了介绍,驱动层的大多数函数都给出了分析。& f$ u6 k8 ~, \, k

" ^+ I7 O6 a  r8 r1 y/ Y. E. `) `  _6 T; e( K( C" b( B
* Y, h7 i9 _. R4 {7 `/ k2 i
! }: {% {1 ^+ Z! c# H

  C* M! u  L8 K+ |' H2 g1 h
( N8 W1 }% k& Y2 ]( ^
6 E/ @) P" K- i4 a+ \7 S
8 K* h5 h- E+ C" ~. i& X

该用户从未签到

2#
发表于 2020-6-24 17:11 | 只看该作者
基于S3C2440的嵌入式Linux驱动——Framebuffer子系统解读
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-11-25 12:48 , Processed in 0.281250 second(s), 26 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表