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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
本文将介绍Framebuffer子系统
8 Y. [: f* S3 l5 C/ `
9 y7 Q+ L4 R! Q$ r" _3 P4 B目标平台:TQ2440 CPU:s3c24401 J: E4 v8 s1 H$ o

" o. Z( `3 H0 U' c; n% S( t$ OLCD设备:3.5英寸,分辨率320X240- M! ^0 {2 I; J) K- A; k

  |4 E# H: z* _' t) X: j$ M5 i- B* n. p$ {

5 Y' T! x3 B& u1. 概述+ y5 i- k* ^5 k% `. U' d
  A2 d6 V/ F) _: n
Framebuffer,中文名字是帧缓冲,这个帧也就是一副图像所需要的数据。因此,帧缓冲其实就是LCD设备的驱动程序。Linux中,framebuffer子系统框架如下:
  L2 q0 P9 F% X: j
3 n* `1 s* M9 C1 T8 j% V0 P% U 5 c+ @: g9 U2 M: W

0 J8 _/ N1 T/ k5 f核心层的代码以fbmem.c为主,核心层包括许多与具体硬件无关的代码,并且提供了API给用户空间。用户空间使用系统调用,系统调用会使用相应的API函数,最后会调用驱动层实现功能。对于不同的设备,驱动层的代码将有所不同。2 l! Q2 C. S$ W/ P

: D2 s6 e. f& \+ ^- i接下来的内容中,首先给出framerbuffer使用的数据结构;随后简单描述framerbuffer核心层;最后,针对S3C2440,对驱动代码进行分析。
; w$ H5 s! ^0 [8 T$ U0 ]0 [
* v# c" {0 l; ]' }, E  H( Y2. 数据结构
1 I" _# K* W: M. j+ J0 _! I3 c" r2.1 fb_info 结构* t0 a1 j9 i/ [9 V
  该结构是内核用于描述一个特定framebuffer设备。其中包含了几个重要的结构,将在下面介绍。. n( z' t9 b5 {. I0 `( B9 O! G
* V" P5 C- m, {; ^# O1 }! D" q
  下列代码位于include/linux/fb.h
3 _5 D) P- o$ ~# ?- ~; f; t& z: ]
struct fb_info {
5 c5 m" X# x2 Y4 g9 R% l        int node;- I( j4 R  J3 V, c5 C
        int flags;
; ^" a) Q" D& i. y4 [( G        struct mutex lock;                /* Lock for open/release/ioctl funcs */0 c% f1 f2 ]7 f) V- L% p; z7 A
        struct fb_var_screeninfo var;        /* Current var */
! g, D9 j1 x1 [( ~7 A% Q2 L7 [7 G& L        struct fb_fix_screeninfo fix;        /* Current fix */8 S. T* s" Q/ z; l. O$ f
        struct fb_monspecs monspecs;        /* Current Monitor specs */, O% B2 D. r3 d: h' }" d
        struct work_struct queue;        /* Framebuffer event queue */
& A2 F% j: y- v6 S! ^2 ]        struct fb_pixmap pixmap;        /* Image hardware mapper */
, E: E; X2 n1 I8 A2 m) c        struct fb_pixmap sprite;        /* Cursor hardware mapper */% n& i' r* d8 N* Z+ D
        struct fb_cmap cmap;                /* Current cmap */2 m, ?2 A  \0 p; x. M
        struct list_head modelist;      /* mode list */
2 ^$ n* B- [% v/ s  P6 C* s        struct fb_videomode *mode;        /* current mode */
8 g) [/ C+ s' c' f2 @2 E  P0 A
* x: Q+ Q! y* n3 d7 \$ a: m#ifdef CONFIG_FB_BACKLIGHT* k& j0 y- b, w$ M& j
        /* assigned backlight device */
& X: i- T0 G. s# U3 U        /* set before framebuffer registration,
0 g2 h# ?7 V2 F2 ^' f' R           remove after unregister */+ x' ]5 P  v; D0 a0 ^0 D
        struct backlight_device *bl_dev;
1 i3 F8 d; M# b: [* v
6 [% Y3 a2 Y& \; v* |: W        /* Backlight level curve */
6 C* y' k! u& Q" l, t: V$ N        struct mutex bl_curve_mutex;        # v/ m1 Y9 o3 V! y, _. Q
        u8 bl_curve[FB_BACKLIGHT_LEVELS];
, }% Q/ g# l( w' s#endif
  P# F; \" G; B9 W; ?8 D: y" W( u7 `#ifdef CONFIG_FB_DEFERRED_IO! n6 h9 D8 I+ P# R0 |
        struct delayed_work deferred_work;
# j. s$ {! R: ]: O# Z        struct fb_deferred_io *fbdefio;
8 @+ F, Z5 J6 c* T5 }! Z#endif
2 ]' G4 Y. c4 j( E4 w
. l! B2 y4 {% f7 }6 p! Y        struct fb_ops *fbops;" x* V7 q3 ?' @; T+ x
        struct device *device;                /* This is the parent */2 b7 v9 m+ T9 P/ y3 w( [( [
        struct device *dev;                /* This is this fb device */
) H2 f/ S, t- C: R3 ]6 ~8 ^& o9 l        int class_flag;                    /* private sysfs flags */
: a1 S+ ]: P$ t#ifdef CONFIG_FB_TILEBLITTING
+ B7 U* c. P* ^9 Y6 K2 G* \. B/ w        struct fb_tile_ops *tileops;    /* Tile Blitting */; `* a) j/ m7 H& S* [  k8 A
#endif# w* S7 j' y7 C  ]
        char __iomem *screen_base;        /* Virtual address */
) p3 ]% J! Y% t, Z        unsigned long screen_size;        /* Amount of ioremapped VRAM or 0 */
& r" `9 c8 x/ ]5 c' `6 j3 r        void *pseudo_palette;                /* Fake palette of 16 colors */ ) C1 F1 y  f1 g
#define FBINFO_STATE_RUNNING        0
+ t+ l. i1 j' Y) V. h5 ]9 V#define FBINFO_STATE_SUSPENDED        1: [- T$ y$ ~/ S5 h
        u32 state;                        /* Hardware state i.e suspend */4 o2 V6 ]2 l' g* {! j) S
        void *fbcon_par;                /* fbcon use-only private area */
3 e$ S: E# s' |# y9 ~5 ]        /* From here on everything is device dependent */# I8 V( V) N7 v1 E5 }
        void *par;        & D- x# [4 Q' n0 Z! V" G
};
4 g1 H+ O# k: {4 o& x2.2 fb_fix_screeninfo结构% C% t5 H" }2 M& \0 M' V) C' W
  该数据结构是不可改变的,也就是说用户空间不能改变该结构中的任何成员,在核心层我们将会看到这点。
3 {) w" d3 U  n6 h0 \& U% p1 e$ v4 ~% \4 j
  下列代码位于include/linux/fb.h
0 j; w* y, B: i, @- K
) k" x& p$ Y+ B3 |  G* Wstruct fb_fix_screeninfo {0 P: V9 f# {8 g2 E$ h8 Y* ?4 u/ x5 \
        char id[16];                        /* identification string eg "TT Builtin" */" l9 `! F& c' y  p& ^
        unsigned long smem_start;        /* Start of frame buffer mem */% z  e% E! V2 T8 w, M3 p, o
                                        /* (physical address) */
) l" d" O) F& z' S3 w) t        __u32 smem_len;                        /* Length of frame buffer mem */: Z' k8 D5 t% a; x
        __u32 type;                        /* see FB_TYPE_*                */$ P$ _- R  s' K) k
        __u32 type_aux;                        /* Interleave for interleaved Planes */
  a) o: F. S- P* ^8 I. M, L2 _0 _4 x        __u32 visual;                        /* see FB_VISUAL_*                */
" c# S4 N+ J7 \" l8 D: w2 V+ B        __u16 xpanstep;                        /* zero if no hardware panning  */6 ~3 ?+ g6 ~8 S
        __u16 ypanstep;                        /* zero if no hardware panning  */
4 N4 n9 J8 V3 u2 k        __u16 ywrapstep;                /* zero if no hardware ywrap    */
4 I; M9 n9 v7 S; J$ N' q        __u32 line_length;                /* length of a line in bytes    */$ ?' _2 T  x% A. r6 v
        unsigned long mmio_start;        /* Start of Memory Mapped I/O   */
/ w' s- N- _; ^  ?; x. ]3 x% p7 Q                                        /* (physical address) */" N6 G. T8 E( p  U
        __u32 mmio_len;                        /* Length of Memory Mapped I/O  */
) a$ X' f8 {5 e; H" C0 K" k        __u32 accel;                        /* Indicate to driver which        */1 |% J1 W& O6 |0 K2 ]5 _! R
                                        /*  specific chip/card we have        */) v) E& a: B, d2 o( q" J" Q
        __u16 reserved[3];                /* Reserved for future compatibility */
+ M, q  j- z* s( M/ M: V, }};- L# t4 ^! A2 Q4 |0 F7 F

2 _, P; f6 Y- a2.3 fb_var_screeninfo结构
! e$ i7 t5 F' M5 Y+ `# Z  该数据结构是可以改变的,也就是说用户空间可以改变该结构中的成员。该数据结构中的很多成员就由板级信息复制而来,在驱动代码中我们将会看到这点。
! Z$ {' D8 X7 `4 c
5 W+ J" x- `5 q6 I/ w1 b5 H/ ]  下列代码位于include/linux/fb.h7 F4 c; [+ z$ ?. q1 f
; @1 U5 g1 {6 P! @+ B2 \: c4 ~
struct fb_var_screeninfo {3 a" w* p$ x3 t- m3 H
        __u32 xres;                        /* visible resolution                */# ?3 K! Y! h9 A
        __u32 yres;
, h3 H  w! J. j8 e' @2 N3 `        __u32 xres_virtual;                /* virtual resolution                */6 ^! b$ u/ R' P& o
        __u32 yres_virtual;4 t" T- d6 n3 `$ @4 k/ D4 Y) j/ a& g
        __u32 xoffset;                        /* offset from virtual to visible */" T" X" V( I% ]5 w. ^! X
        __u32 yoffset;                        /* resolution                        */
% T- Z" ^, C* k' ^; g) f8 {7 q: ]6 w) S
1 n1 m7 Q, E- Q! Q        __u32 bits_per_pixel;                /* guess what                        */
/ I/ r+ c2 w  j) X. O        __u32 grayscale;                /* != 0 Graylevels instead of colors */+ L+ x) m- r3 g# Y, m9 x9 h" M
7 D+ t- Y* s. N! L# L. s! p
        struct fb_bitfield red;                /* bitfield in fb mem if true color, */
+ r6 P$ ~7 k! q: U" R4 P        struct fb_bitfield green;        /* else only length is significant */" v5 m( f! R) |+ i( K
        struct fb_bitfield blue;
5 \" ^/ O. @$ d1 N" T        struct fb_bitfield transp;        /* transparency                        */       
% U2 z5 i# s. K2 q6 l) M$ }# s" M, ~- Q! u! e
        __u32 nonstd;                        /* != 0 Non standard pixel format */. V  A& U3 L: F  d3 q- v
& C% W; k7 h3 p9 V" d
        __u32 activate;                        /* see FB_ACTIVATE_*                */" s3 I9 m; J1 k* A& @$ r+ F$ h- Q

; B0 l. F: r: |6 |. J        __u32 height;                        /* height of picture in mm    */( e1 q& Z" ?4 ?7 b+ \2 n3 h
        __u32 width;                        /* width of picture in mm     */
) Q+ q) X+ ^2 {( y( `6 I! @9 x$ Y$ G7 m  E4 y! F. s( ?
        __u32 accel_flags;                /* (OBSOLETE) see fb_info.flags */
; N8 p4 Y1 K' E2 h9 N) G0 m" C+ ]3 y! A
        /* Timing: All values in pixclocks, except pixclock (of course) */1 |! [* P' q7 Q+ O4 v8 C
        __u32 pixclock;                        /* pixel clock in ps (pico seconds) */0 V- X! b- f+ U7 i$ d5 e7 I9 a  ]; h
        __u32 left_margin;                /* time from sync to picture        */
6 L, n$ _3 ^1 A+ m0 y        __u32 right_margin;                /* time from picture to sync        */
8 T7 L8 z) b1 h3 R& f- P5 L        __u32 upper_margin;                /* time from sync to picture        */5 q; r) F* {; F1 Q; H
        __u32 lower_margin;; G1 ]! M8 T$ e& {
        __u32 hsync_len;                /* length of horizontal sync        */7 C% G+ p8 }& P9 Y+ ]
        __u32 vsync_len;                /* length of vertical sync        */7 c: W$ A) t2 r& x. J
        __u32 sync;                        /* see FB_SYNC_*                */
3 W8 z5 s3 p  B, A" d& p1 w/ D        __u32 vmode;                        /* see FB_VMODE_*                */) H3 x. f9 v( A2 }# h8 p0 s% O( H& b
        __u32 rotate;                        /* angle we rotate counter clockwise */) ^+ _/ H% z+ a) S$ u$ w9 i
        __u32 reserved[5];                /* Reserved for future compatibility */$ _, J9 @- T; D) I  Y4 T
};
0 L8 p+ L4 }8 S, T8 n; h9 _
; c! Z3 W$ u  C5 y2.4 fb_ops结构: M: \% \5 A+ t0 R: S
  该结构描述了用于fb_info的方法,这些方法中有些是要驱动程序提供的,而有些可以使用内核提供的方法。$ ~8 d  v5 V2 r6 l

! e' Z- m% Z6 K$ h* B- H  k 下列代码位于include/linux/fb.h3 e/ q; s8 q$ o4 q
7 @1 E. h! p: {& s2 B8 I  S+ z
/*4 p9 V+ w0 s- \) m8 a
* Frame buffer operations" l( y9 h1 m7 P1 ]' v$ q
*9 X0 a+ F# m" Q4 Q! F3 W
* LOCKING NOTE: those functions must _ALL_ be called with the console
7 }  G4 c( |" n3 s: o * semaphore held, this is the only suitable locking mechanism we have
% {: Z& ]5 N1 {" E: ] * in 2.6. Some may be called at interrupt time at this point though.
- g# ^* e7 t% t6 q */- f# ^% w7 I; Y5 g$ L  e

* Y5 H5 T5 k/ `7 Z; a; t7 h2 d1 Lstruct fb_ops {
% G5 `* J3 K5 i5 k9 |5 p        /* open/release and usage marking */
$ P  K6 M9 K* |5 s$ I$ G* [        struct module *owner;3 o3 S/ c+ h; J! E* i! N
        int (*fb_open)(struct fb_info *info, int user);
3 J* a9 x+ b9 t- Y        int (*fb_release)(struct fb_info *info, int user);$ V. D# A6 h$ f# F! {

+ e9 `: r. W: @, y: ]9 i: r: b        /* For framebuffers with strange non linear layouts or that do not! v/ Y8 e, ~8 }- {% q
         * work with normal memory mapped access
) R% d6 l: l  Q1 p3 D         */
5 \. T$ O$ @) r3 `# D        ssize_t (*fb_read)(struct fb_info *info, char __user *buf,( \* @" h/ |2 M9 n9 X; e
                           size_t count, loff_t *ppos);
$ h- y; I* P4 o* K* q        ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
# t5 x# i6 ~4 L8 b* s1 K( w                            size_t count, loff_t *ppos);
' j1 H, I$ N$ B4 M& ~' I
( M2 c7 s( a# B3 b        /* checks var and eventually tweaks it to something supported,
6 _  x. J+ V- P2 a3 |* {. |         * DO NOT MODIFY PAR */
9 d& ?! `2 f- F6 j4 h  h. C# d        int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
' T) `! ~+ j4 ~8 C" B
# u  o$ p: {( [  t* U        /* set the video mode according to info->var */9 i2 L9 i: [# m+ y1 ^/ t
        int (*fb_set_par)(struct fb_info *info);
& m1 c8 b4 b/ S- t* H1 p% j6 i
; w7 N& ^* H9 p+ h        /* set color register */
. ?! i: S7 ^+ {% W        int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
4 u( e' V  ~2 {' l                            unsigned blue, unsigned transp, struct fb_info *info);
9 w. l& A7 @) _+ W9 A6 C1 F" L. b- k6 }- t1 a, o/ g4 j
        /* set color registers in batch */5 N9 D! d. I: p- |4 s$ j
        int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);% y' q5 l+ G* f0 h5 _1 i# R  R5 q3 {. S( }

: V  h6 I! n  l$ Y% {+ {        /* blank display */
. J) d2 \+ Q; g1 K% M) p        int (*fb_blank)(int blank, struct fb_info *info);7 U) e0 H9 y, P% M7 ?7 l
, J/ t+ b% u( J/ r2 G$ r
        /* pan display */3 R5 h+ L2 _2 P+ H
        int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
% L5 \) o" {3 J  d2 k$ T# |
1 U$ F' G7 v2 J. i) q0 s        /* Draws a rectangle */
2 q; b. c% }' B* Z+ p& M        void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
1 r6 O. x( ]0 W' u9 n: X, w        /* Copy data from area to another */2 z4 }& u( K  b0 F/ B2 W; C
        void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
: S9 X; h6 H$ W7 |        /* Draws a image to the display */
  q3 P: Q! k) V        void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);4 X3 k6 E) P+ I7 T8 S' F8 J5 ^
& }  `7 h, O( E- k& A
        /* Draws cursor */
7 f& Z( w6 R4 [- h( C' j! E        int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
. |0 h  I6 g% ~- v8 e- _( }/ F! M9 h# }* l" B& h% ~+ r
        /* Rotates the display *// B+ F8 S: E( {* z1 T3 B
        void (*fb_rotate)(struct fb_info *info, int angle);. j! m4 N5 b$ O6 @
. K9 n1 T% }/ X0 F. X: }8 c
        /* wait for blit idle, optional */
% L; ^9 b( E0 F5 G$ e4 ~3 Q5 a        int (*fb_sync)(struct fb_info *info);+ p3 N: U  M/ @" J

4 @- J. N4 ~% o0 S" a        /* peRForm fb specific ioctl (optional) */; |( e1 q! ^, e1 i) i8 i
        int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,7 i4 F2 k2 r) [7 ?8 Y9 B. q( M7 a
                        unsigned long arg);
/ ?. ]1 U; ?# G7 ]
8 j* Q1 i" o/ T        /* Handle 32bit compat ioctl (optional) */
+ z( \" n  r/ R3 }7 R9 M        int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,5 k. [0 w, f2 @7 ]% X; t9 V: x* m
                        unsigned long arg);. }' @6 S. c8 j: Y( C  ^, d4 d
: a# K; a. x" V1 a( k% A
        /* perform fb specific mmap */
0 ~5 u) f) X7 j9 p3 P        int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);. V9 b$ l* l% i# H0 S
- `* T! d1 ?# C. R- p
        /* save current hardware state */& o  }) k$ [. I* m8 _/ [
        void (*fb_save_state)(struct fb_info *info);
& K" u2 U% V: Y+ A& A( d
7 U* x+ V4 p; }& J        /* restore saved state */: \' y: f8 G- M+ {
        void (*fb_restore_state)(struct fb_info *info);
/ Y9 f8 o% n  l+ @2 z' \
- K- j* c. u% U) q% Z        /* get capability given var */
# l9 K, x- F1 C! w# ~% \4 Y6 }0 ?        void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
/ I% P" W# ]$ d, ~& q4 x                            struct fb_var_screeninfo *var);
) \3 e& ]( C( O" Y};
$ F/ c  Z0 y" [: O7 [
& T8 Q+ s8 _: @$ Y. s
4 l  r) }3 r, p3. frambuffer核心层
- \/ p1 j7 U/ ?. W  i9 i9 u首先来看下frmaebuffer子系统的初始化函数。
9 _; A- |& q- k1 w1 E
0 P% _6 L: P& `6 D' e3.1 fbmem_init和fbmem_exit
1 l# a' ]- R) m! @3 a下列代码位于drivers/video/fbmem.c# v- ]0 ~# h% J( E
; w# B( d4 m6 z- U
/*** D0 ?, o% F+ R: U9 d" d
*        fbmem_init - init frame buffer subsystem4 \* }6 W* t' h* v% \
*
% N/ B5 v1 G7 o- W8 j, S *        Initialize the frame buffer subsystem.  q) B2 ^5 s" Q+ t7 V& }3 P: w; f
*
9 ~, B) \% g+ C! M" ~2 O; S9 \/ f *        NOTE: This function is _only_ to be called by drivers/char/mem.c.
) c+ T  A" j. C9 Z( Y7 w *& D" U% N" J# V/ _  a# ?
*/
9 O. e* q& C) O5 n. F+ ~. t# J) C, I  G2 o3 L
static int __init6 o$ o" o$ x" t
fbmem_init(void)
8 d4 X0 @+ k/ }$ I6 e{
5 A8 B8 S; d+ W        proc_create("fb", 0, NULL, &fb_proc_fops);/ V+ R0 c3 R4 p& M7 f7 |* p" u- @: q

5 H* g+ E- i1 j# j        if (register_chrdev(FB_MAJOR,"fb",&fb_fops))                /*注册字符设备,major=29*/
- m% }& J$ V, Y0 ^) R" ^( G7 H; M                printk("unable to get major %d for fb devs\n", FB_MAJOR);; d" q9 `) `: g; `) {6 m

- U' W9 z# h% C- S, ?        fb_class = class_create(THIS_MODULE, "graphics");        /*创建类*/5 }" \8 m* ~: P% g: Z( H9 }9 e: T% p
        if (IS_ERR(fb_class)) {
, y: |6 ~; A2 h8 ~/ V                printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));; H) ]8 Y, Q6 Q9 I
                fb_class = NULL;
1 z$ m8 b2 ?  {* [( R* Z8 ]        }
& T  A$ J3 h4 v+ `' v# \        return 0;+ |  }1 @1 b, E
}' O3 S) I3 m* V- B& ]# I: s9 s/ S# w* x
' o" c3 l1 z) r
#ifdef MODULE2 c' t. c% R/ b* s3 `$ f2 z
module_init(fbmem_init);
1 J, [+ X! ^. astatic void __exit
8 Q! j' ^  w6 K" B* a8 K3 [8 Ufbmem_exit(void)
/ N  ^1 g3 L" h: B# ]! K( g6 y{/ ~5 S( Y  q, b7 N6 `
        remove_proc_entry("fb", NULL);
' y) O7 N. s# ]5 w+ f        class_destroy(fb_class);
$ r" P2 L& h0 ^  w1 {' t2 C% t7 D        unregister_chrdev(FB_MAJOR, "fb");
* v: k) F* x  J; w4 X}
6 \* j, [/ W; w$ O4 A1 c/ u
  D  N5 h0 w! T6 ~3 L% O$ k5 Gmodule_exit(fbmem_exit);
4 F. f- b* b4 i+ V' d/ fMODULE_LICENSE("GPL");! L  M8 A, x" T& g  N( D! T, {
MODULE_DESCRIPTION("Framebuffer base");/ ?- E4 l) Q# N
#else( S( g% Y- Y# }" {$ E0 r
subsys_initcall(fbmem_init);  ]) v( ~3 D9 J4 D, B
#endif
* [3 R/ d# d8 `( M  D( y2 B$ h, _" S4 W$ y, L# m! L
static const struct file_operations fb_fops = {
8 T- G9 T- c3 h+ ^% t' S3 J4 ]    .owner =    THIS_MODULE,
2 x% s% f% R3 c# `: q4 l% q    .read =        fb_read,2 f- E8 C, z% z7 d
    .write =    fb_write,
( u6 |' E" n" j1 Z& y  `    .unlocked_ioctl = fb_ioctl,
- u, P. ^) q; m$ h8 v5 S2 M- v) M#ifdef CONFIG_COMPAT
) N* {& F5 S6 y+ S; x$ ~6 ]% B    .compat_ioctl = fb_compat_ioctl,5 `; |3 |& n$ |& n
#endif
: P! p3 C2 @6 I% z1 F    .mmap =        fb_mmap,
/ h. O/ C2 r0 }4 n) w    .open =        fb_open,
. S6 g( s' p7 M, m" v) i    .release =    fb_release,% i" X* v! q  I. w7 A7 J/ u
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA9 h6 I1 k% T; {) J+ s
    .get_unmapped_area = get_fb_unmapped_area,
/ X  _% d* J, A. m# t4 ^# S#endif
" A5 e% [3 b4 E* F) g#ifdef CONFIG_FB_DEFERRED_IO/ d, Q9 c# N9 `% m. [
    .fsync =    fb_deferred_io_fsync," B" _7 U! a- Z. z" h0 a9 t
#endif- Z8 R* v/ Z; O
};4 a1 Z- P: C, P+ T
8 z; b& y, {) Y5 T' r1 X7 `% q
我们看到,如果不是作为模块,那么该初始化程序将在subsys_initcall阶段被调用。初始化时,仅仅注册了一个字符设备,并创建了一个类。通过字符设备,提供了API给用户空间,包open,release,write,read等。: _/ i- f. V! @
随后我们看看如何分配一个fb_info结构。
2 @* g: S3 e1 U) e  E$ \" f) N- C. a- e
3.2 framebuffer_alloc+ F7 E: R# L1 z3 f2 B6 N+ F7 G
下列代码位于drivers/video/fbmem.c
) t5 s  W1 J  d, G3 D
; r+ F2 g6 D+ s1 A; F/**" |" `+ l0 u  }& T8 J2 s8 T1 P
* framebuffer_alloc - creates a new frame buffer info structure; M6 B3 \6 Y: N" a; J
*/ O" b2 G) E% O' G/ _
* @size: size of driver private data, can be zero/ b$ K: i; x8 q; a$ `& W
* @dev: pointer to the device for this fb, this can be NULL
" W" l: G% a3 u6 O *
$ S6 A' n7 Z. ~; p6 T1 ?- R5 [ * Creates a new frame buffer info structure. Also reserves @size bytes
, _- P6 S7 v) f* _ * for driver private data (info->par). info->par (if any) will be+ b  ^; q6 M9 G' o& O9 l( |& d
* aligned to sizeof(long).# n2 g6 h. K7 }$ t! b& ?; P
*
( ?# G4 Z. I: Y# s * Returns the new structure, or NULL if an error occured.
6 F$ v8 h0 s0 Y, v$ W( ?- S *
* W* h8 {' x, U3 ?+ i: ` */
- Z' ^# {( P6 n9 F' g, L: jstruct fb_info *framebuffer_alloc(size_t size, struct device *dev)( \: x* i1 E; T0 I9 ^
{
' D9 T) s/ C0 T: W4 i! P#define BYTES_PER_LONG (BITS_PER_LONG/8). ?2 K1 f! y: Z( B  h
#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))% j5 S0 g- y- e  i- ]0 Q! k1 a
        int fb_info_size = sizeof(struct fb_info);
1 g* C+ ?9 r% H        struct fb_info *info;, }* C$ N; w7 |+ O8 N
        char *p;4 D8 y+ ]- M% M) w8 j
% m7 M* [( p* I* b" U! I
        if (size)
" n$ {8 y2 X% N2 v; E                fb_info_size += PADDING;
; X" ^* K# R, j, m, A" Y; i1 |$ |+ \3 L/ b3 ~
        p = kzalloc(fb_info_size + size, GFP_KERNEL);
) p; Z0 Z- C% N, {/ }5 _9 ^" e3 Q5 A
' }- e3 m2 Y; N) R5 g        if (!p)" v) W# ~! R! A" _& x. R4 ~, O5 _
                return NULL;
  _( e' T& E* z
. P7 i0 }( |) u3 A5 q% S! t        info = (struct fb_info *) p;
% _% c; ~3 c) F. p. s9 m' {8 z+ J8 K
        if (size)+ G: |+ ~% v( e
                info->par = p + fb_info_size;
* Z3 t$ H; j/ Q4 y* ^* b9 m" ~
! e: z" Z1 W$ J" l        info->device = dev;
  C2 a4 k% Y6 U
; \; ?/ {' [2 _. U( a4 l! O#ifdef CONFIG_FB_BACKLIGHT. X- l$ U; ~7 d9 E6 {" B
        mutex_init(&info->bl_curve_mutex);9 y+ u! R* J$ h
#endif
: o' \7 T& f4 X* Y% p4 U! n9 h! D# R, }" T) }0 m* s' e
        return info;
, v4 d* G/ D) B" @1 d#undef PADDING  y4 |  v7 p4 x8 B' X5 J, v6 f: k
#undef BYTES_PER_LONG
$ l/ W2 M3 O0 k6 l: D* D}
; o1 `, q$ W7 rEXPORT_SYMBOL(framebuffer_alloc);
) {- G9 c8 w  \+ ?在进行分配时,根据参数size的大小,分配了b_info_size + size的空间,然后让fb_info->par指向size的空间。因此par所指向的空间可视为设备特有的数据。
6 n8 y5 N; V1 g. H$ c在分配了fb_info结构之后,需要将它注册到内核中。注册由register_framebuffer完成。我们来看下。. ]+ U8 u% C2 g! k- h. l

7 F. O7 B+ g- u5 `3.3 register_framebuffer
+ H/ i: `  M" ?0 D0 }下列代码位于drivers/video/fbmem.c) v- R' s7 `* q4 G0 S' m7 B
9 G+ K; }/ J1 o" r, z0 B
/**
( B6 V( @/ e2 O- ?' B *        register_framebuffer - registers a frame buffer device
4 z. Y, q& v7 I0 D* R" c( o *        @fb_info: frame buffer info structure* Y% T  h4 Q$ `3 d3 y8 {% v
*- O- x: F8 s7 h$ K5 P# \
*        Registers a frame buffer device @fb_info.
; b- n# g- X' b. ?+ k  x& @4 u *
5 ~, c$ g, ?0 U* f *        Returns negative errno on error, or zero for success.
) y% d# B" k3 t0 p! {4 y  ^ *  `# N% F: S7 _9 P' F* k
*/
+ p  U2 K6 y1 A. v- S
$ G1 M) o+ ^- O* i+ Jint
' V; _. j2 u% K' h0 Jregister_framebuffer(struct fb_info *fb_info)3 K$ S: \3 q: V8 u: M* k# f
{, v3 q. E, ~6 l% o% R  l2 q7 ~" Y
        int i;; S0 g& s; V+ B2 C$ J  Y) q
        struct fb_event event;
2 q1 V5 P. C# s        struct fb_videomode mode;# e, L6 q9 i5 `1 X3 ]5 l

" M% I* x0 \; T: {0 ]3 r4 n) @. |        if (num_registered_fb == FB_MAX) /*最多32个FB*/
% g+ y6 L. a, o4 u; G5 f4 @                return -ENXIO;
( w- o+ h9 A% o8 V
' l2 H9 y1 J1 Z& O        if (fb_check_foreignness(fb_info))
3 Z* @" z+ J$ Z( V9 [0 h                return -ENOSYS;
1 [. T0 ~9 e8 G- p
- E& D0 }  x- X  f2 g( l        num_registered_fb++;                /*对注册的FB计数*/
4 _  H. e! c9 H! n+ n        /*寻找第一个空位*/
$ A, j! D6 _+ N; T& h        for (i = 0 ; i < FB_MAX; i++)/*FB_MAX=32,也就是最多32个framebuffer*/
0 y* c& F' w$ d                if (!registered_fb)        /*struct fb_info *registered_fb[FB_MAX]*/
& I6 h0 e$ ~% K  r9 l& q                        break;
1 T/ E8 `$ b& @$ Q# v& L; W        fb_info->node = i;* J# ^2 \0 `: M$ Z( [
        mutex_init(&fb_info->lock);        /*初始化互斥体*/
) t% C& l! b3 H2 J" O3 y4 z4 C% l
+ [( p6 {! O6 |) z5 P        fb_info->dev = device_create(fb_class, fb_info->device,/*创建设备节点,节点名为fbx*/
7 b+ w( e* K( k8 e, O# `  V                                     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);, r; H- L! F, b+ E/ W% m  C
        if (IS_ERR(fb_info->dev)) {
, r7 B1 [# Q) \0 K# K+ r6 _                /* Not fatal */
8 L1 B2 ]6 |. S/ q4 D                printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
' {" Q& E/ q( d" q) v                fb_info->dev = NULL;4 S8 ]4 [" |( x. v! `( r$ O8 P
        } else$ Z- H/ e1 g- J& B5 Y
                fb_init_device(fb_info);        /*初始化,在class/graphics/fbx/下创建设备属性*/5 Y( c1 i+ @6 J' q6 W

0 |" e" D0 Y2 q. |- W9 d        if (fb_info->pixmap.addr == NULL) {
3 P3 x; K; D' S  E1 ]4 W0 l                fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);/*分配内存,1024 * 8字节*/
1 y" v& r+ |5 u. a9 o3 z                if (fb_info->pixmap.addr) {
. @; |2 G9 J' t/ ^2 q& S7 x6 R) r                        fb_info->pixmap.size = FBPIXMAPSIZE;! P+ t! w* k; e% G% @, T
                        fb_info->pixmap.buf_align = 1;" A' z8 {( D2 A6 m
                        fb_info->pixmap.scan_align = 1;
9 Z. B0 P( X0 q                        fb_info->pixmap.access_align = 32;
7 l, H2 H. V) f9 Z: P6 _* b                        fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;, h( d7 |& n0 Z/ W5 S
                }
: L1 x; F* v* R/ h        }        " U! s% }# B9 U. p$ W+ J# `
        fb_info->pixmap.offset = 0;1 f& s1 N6 Y5 s
& [' V! w# m0 y
        if (!fb_info->pixmap.blit_x)
' W6 V' A8 G( X9 T. _3 P                fb_info->pixmap.blit_x = ~(u32)0;
. k3 K- A9 S2 y2 Y  v2 }/ w- Q% Q2 E# b4 {# O
        if (!fb_info->pixmap.blit_y)- G1 z7 A! m2 `) G; R
                fb_info->pixmap.blit_y = ~(u32)0;
, A. ?% }9 z! `0 v5 j; N8 U* `$ H4 a
% [, r  p. I* d9 q" S9 B        if (!fb_info->modelist.prev || !fb_info->modelist.next)        /*该链表没有指向其他节点*/
0 o/ ^( p* _; m                INIT_LIST_HEAD(&fb_info->modelist);        /*初始化链表头*/
: h3 w) k; O* m1 a: H: d' r; w: x7 b  p5 V7 d
        fb_var_to_videomode(&mode, &fb_info->var);/*转换fb_var_screeninfo成fb_videomode*/
7 A% t9 p5 ^  n: _9 A. e9 D        fb_add_videomode(&mode, &fb_info->modelist);/*添加mode至链表中*/
' Q3 |$ o9 Y8 t        registered_fb = fb_info;( W+ I* P8 V2 M
3 f9 `6 ^9 K( W' o! j
        event.info = fb_info;! U& g1 h# U" W
        if (!lock_fb_info(fb_info))
2 _4 x4 U) U4 t8 j; C# {                return -ENODEV;
: _! o' ~* m& t6 _        fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);/*???*/
" }% J/ x( d. s; F        unlock_fb_info(fb_info);
/ M6 X# F) x- |! f) i        return 0;  v7 Z% K/ F. V' u' [/ {
}" s& f0 A! S2 N* D$ [4 R
+ o2 _$ X) L* U7 z& K0 n. J6 ~
从这个函数我们可以看出,framebuffer子系统只支持32个设备。在创建了设备节点以后,建立设备属性节点,随后将fb_var_screeninfo转换成fb_videomode,最后添加fb_videomode至链表中。$ w* J' m% n6 I' R! \
我们看下其中调用的函数,首先是fb_init_device。- h5 D/ Q* q0 q1 b
1 b$ v8 K6 l, `1 g1 `
下列代码位于drivers/video/fbsysfs.c7 j$ i$ y" x5 f, n# G9 e" g# N; l
) g, a) S8 u5 L5 R, Q3 _
int fb_init_device(struct fb_info *fb_info)
8 E: [2 W! ~' o* f8 n5 q1 ]3 U; S$ s{
" }2 L7 W% [- S7 |/ k1 `3 W; W        int i, error = 0;4 n% f! ], _- s  F* Q9 K, g- F9 u

! P& x( J) A- F; ^* `5 A# R9 b1 ]- N; t        dev_set_drvdata(fb_info->dev, fb_info);. K8 c6 u! |4 c; t) E! m
( G6 v) `1 s! M1 S( @
        fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
# k2 h6 J$ a* J; n) N
$ Y7 u+ w& N. b$ V% W) ~( e        /*建立设备属性*/
% Z; W/ i7 I$ E8 V        for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {8 n% i3 K8 ?5 B% [4 T7 e% }9 C" i
                error = device_create_file(fb_info->dev, &device_attrs);- {2 F( O0 P* u4 d- ^

3 _# H- Q7 i4 L3 b3 c3 H# g                if (error)0 o1 Y! ~- i: x; y) R8 o3 W
                        break;1 |: B9 K5 n& a, L: S
        }
+ [& Y8 t' `1 i; P$ A4 w2 I6 m3 I% m7 P! f- ~- e. i6 {; W9 F
        if (error) {
: b$ a5 h. u- N5 k: B0 |                while (--i >= 0)
+ {4 i$ G& n' F" U6 p0 N                        device_remove_file(fb_info->dev, &device_attrs);
7 N4 S% ^2 |3 Q( R7 n2 \1 _                fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
: E+ [/ p2 d1 N. e4 x- x        }
, o' C7 X, {5 {# m
) D9 w; g3 i% K; g        return 0;
1 e- I- F; @' C$ X) Y( ?: A}3 }! Q% M  ]; Z. {

* S5 O( ^* s7 \, S9 U% h/* When cmap is added back in it should be a binary attribute
+ A( `6 a5 D. S* M/ L6 B2 t% @# A * not a text one. Consideration should also be given to converting+ ^4 l4 l! T  |1 d+ k
* fbdev to use configfs instead of sysfs */
+ }( r6 P' D2 P2 _2 T, {1 ?static struct device_attribute device_attrs[] = {/ {0 J; G* c. y1 j# k
    __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),' j$ s( s0 t4 x; c% G- {
    __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),( Y# w) y3 ?1 t: d) O
    __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),3 C* |6 F$ I+ I0 y  f% F$ g9 H* h
    __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),3 g" ~" D: a' D$ T/ S7 Z
    __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
8 a+ q: s/ C. v: K9 u% _- s    __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
- |, V. y5 ~2 [9 t! {    __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),0 K1 C  W5 o% s' [7 l  [, w5 c
    __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
  r& R' `' `: O+ v( Y    __ATTR(name, S_IRUGO, show_name, NULL),& B& ~, T  j5 v: |( {
    __ATTR(stride, S_IRUGO, show_stride, NULL),( k$ \- E1 K) T* P, l; i" R
    __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),$ [" Y5 u/ s, t( [/ m
    __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
6 k/ d- A" j3 x$ m  r# [#ifdef CONFIG_FB_BACKLIGHT
0 _) P- Z+ O3 c& E4 a. @    __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
) `/ b$ W3 e8 E" ?( @#endif
% S$ y. K( n8 [+ U};
( l2 w3 M# U+ K+ X% K
- L' i5 b' P; i. }' v/ Q4 n. M我们可以在/sys/class/graphics/fb0下发现这些属性文件。" S% ~# c+ ^) |6 G0 ?8 g
[root@yj423 fb0]#pwd
$ q2 A+ V6 K9 c8 ?/sys/class/graphics/fb0& W" S2 b5 w9 K+ |+ y$ Y# }
[root@yj423 fb0]#ls- _1 R& Q! l6 z! q7 o7 R
bits_per_pixel  cursor          mode            pan             state           uevent
0 @0 A3 w. A/ ~' H$ i! n1 f% G( Jblank           dev             modes           power           stride          virtual_size
# z/ \2 O  e) R* k3 h8 ?% Fconsole         device          name            rotate          subsystem# P! b" d6 B/ o' ~$ i* ^% t4 P
5 I: U3 v8 T9 G3 D# o) R3 X
4 n. {* v- r6 ~" }& G" R$ E! q0 b
接着看下fb_var_to_videomode和fb_add_videomode函数。
4 ?3 ~9 P1 v( V2 R$ B6 g+ ^
+ j; d/ D! t& I0 O下列代码位于drivers/video/modedb.c和drivers/video/fb.h
1 V! u5 ]) t$ T" V
2 X  R5 K& }7 {& rstruct fb_videomode {
/ B/ u" m0 b2 a9 |        const char *name;        /* optional */
  W+ w9 u. R: X' z" \        u32 refresh;                /* optional */
2 J+ N1 r) ~8 ~0 r6 r        u32 xres;
- g; M7 j! ^$ o1 [. C        u32 yres;
- N5 {# c$ j( `7 Z4 z; A( B        u32 pixclock;
8 N  r2 m" ?* Z! J5 A        u32 left_margin;
& S) t) P5 `+ o: F6 w; O, }  [        u32 right_margin;
6 z* ~9 J/ l$ M        u32 upper_margin;: g* E2 U1 h7 A
        u32 lower_margin;
  Q; z& Y, `* G# E0 K8 h; p& d3 |        u32 hsync_len;
+ }7 \) K+ J/ z4 U2 l        u32 vsync_len;$ `# e; P$ }, ^- f
        u32 sync;
+ d( F& S2 q. U5 r. d* K% `        u32 vmode;
' v. P% ]3 D( S$ a9 u$ H8 |1 c. |: M        u32 flag;7 U' ^' r, H/ `# P4 W) U0 `% C
};8 U" M1 O+ j( U# N: i1 j+ o
( y* N, I. @3 a2 h
/**6 z# l0 ~% M2 }2 B9 Z% v9 V
* fb_var_to_videomode - convert fb_var_screeninfo to fb_videomode. ?% m) J" b+ s! _
* @mode: pointer to struct fb_videomode4 U/ d4 s: ~2 v/ t9 w2 L7 X
* @var: pointer to struct fb_var_screeninfo! N- \% k8 R! Y( N1 @
*/; j2 s/ r1 O7 c" G
void fb_var_to_videomode(struct fb_videomode *mode,
) ]1 t5 l9 ~% G. M0 v             const struct fb_var_screeninfo *var). J- H+ Y3 w$ n& [3 u. C6 w
{+ i, t, N; m" n8 j
    u32 pixclock, hfreq, htotal, vtotal;& l( _+ a! G2 ?5 c" `; Z$ y) d

4 C9 h8 _# {! [: L/ M- t) ^    mode->name = NULL;
1 k6 t; }+ r5 ?2 S( m    mode->xres = var->xres;# n) ]: V' f1 n# y4 w
    mode->yres = var->yres;
$ W# O) h- ?* c: G9 ~    mode->pixclock = var->pixclock;
* @& C4 B& o$ [5 z) b( \% Z    mode->hsync_len = var->hsync_len;+ \# B7 S  V6 H2 _9 i( Z
    mode->vsync_len = var->vsync_len;
" G8 `5 d4 w5 d    mode->left_margin = var->left_margin;3 T) w  m. y. A6 S: R" V
    mode->right_margin = var->right_margin;
/ G; s6 w$ K/ H; g' ^    mode->upper_margin = var->upper_margin;
, ~: p# |2 c; R( {' f1 o    mode->lower_margin = var->lower_margin;
" X) Q( v7 _+ j    mode->sync = var->sync;
! X$ x9 [- p( l$ P    mode->vmode = var->vmode & FB_VMODE_MASK;
. c/ P2 Y7 a: y% o+ ?: @    mode->flag = FB_MODE_IS_FROM_VAR;' D; s  V: o& v+ A( P+ G  A' K4 G( M
    mode->refresh = 0;
0 I" }; V+ N5 P' `/ _* X2 d. U! U- O/ i7 x) T, b& g" s2 X8 z
    if (!var->pixclock)3 ~1 J- z1 }7 d$ [
        return;
, T1 U# L) Y3 Q8 b' z9 ]
6 z" {: A$ ^3 [6 X$ Q    pixclock = PICOS2KHZ(var->pixclock) * 1000;
( k7 l" h' D  J; I* ~  v& a/ X) u8 Q: E& E( i8 U- K3 c) t
    htotal = var->xres + var->right_margin + var->hsync_len +: ~: ?( N2 {1 N
        var->left_margin;
( Q3 g5 q: a, |" M7 d8 a" ?( P/ p" k    vtotal = var->yres + var->lower_margin + var->vsync_len +  c  B4 v6 L1 B
        var->upper_margin;+ S' |4 K, }. |  T2 o2 `
% Q2 ?3 F: ?( s0 q% A4 C' y
    if (var->vmode & FB_VMODE_INTERLACED)
9 x5 ^: Y6 Y2 G' V3 `        vtotal /= 2;
7 ~: i# A6 w5 p6 N    if (var->vmode & FB_VMODE_DOUBLE)7 K& A/ p) n! N) o8 K1 e. s
        vtotal *= 2;
' q: t! j7 Q: O, x3 |" [$ S. Z; n1 G/ Z+ C% Q
    hfreq = pixclock/htotal;
; {, o) f, w( b# R/ G& K5 ~" u4 m( j2 E    mode->refresh = hfreq/vtotal;
8 y2 l6 P: F- ~, {" F}5 B, T1 t1 j7 Y- e8 ]! }$ k
4 W- e$ ^6 m6 d  V6 T" Q* Y, a
/**3 t& p6 Q, S' N) a7 t, W7 @# ~
* fb_add_videomode: adds videomode entry to modelist  ]* j8 S4 w' f( N+ R
* @mode: videomode to add$ g2 J' B" ^; k
* @head: struct list_head of modelist' ~& }% Y) X; C. t0 D1 [
*, |, B! _8 d( \5 ^2 a/ a4 g5 E
* NOTES:
5 x9 L% [" c  H  t9 P& @0 @& u * Will only add unmatched mode entries& g1 v2 B  y1 Y
*/$ Y& x' @. x; d; B0 U$ T( }
int fb_add_videomode(const struct fb_videomode *mode, struct list_head *head)8 b& T& P; M3 e$ Y
{7 h0 @" c/ F5 ]/ h3 ^
    struct list_head *pos;. t5 b4 e6 i1 T- X9 V( `
    struct fb_modelist *modelist;d
! O9 }1 E* Q3 a8 T8 `2 ~    struct fb_videomode *m;* `& e( `  Y+ S3 ^# F) C( M
    int found = 0;+ s  o7 Q* z* O* ^
    /*遍历所有的fb_modelist,查找mode是否存在*/7 A) J" n. T$ W
    list_for_each(pos, head) {
0 }' e- K' l  G6 r7 v* ^, ]! h        modelist = list_entry(pos, struct fb_modelist, list);6 g+ ~( k0 r! m  O
        m = &modelist->mode;
6 B( ~& z1 n+ T        if (fb_mode_is_equal(m, mode)) {    /*比较两个fb_videomode*/
& ]% x6 ?4 n, v: @- Y4 }            found = 1;                        /*该fb_videomode已存在*/4 ]+ B9 Y0 p/ x9 |- F
            break;
3 h& _) `) D% d& q        }
7 g  k$ ?7 h+ L" U    }
/ g7 {9 |9 J7 i# Y6 u- v  z! {- `    if (!found) {    /*不存在*/
! v. r) j) i& z7 A3 G        modelist = kmalloc(sizeof(struct fb_modelist),    /*分配fb_modelist*// }( ]; |. l( H/ p$ \6 b; x
                          GFP_KERNEL);) E  r' q/ \. @8 [% B/ [7 d

' j# S7 \( {* H        if (!modelist)6 G8 d+ `! {# A8 Y
            return -ENOMEM;* O0 B& L" F9 z. s5 H5 \
        modelist->mode = *mode;            /*保存mode*/6 m2 K3 u; K' r+ R; I
        list_add(&modelist->list, head);/*添加mode至链表中*/
" `. ^9 v1 c' o* N4 b    }
+ m) T4 q7 o0 I8 K" T( _+ N    return 0;
9 T( m* S: S7 K% s}
7 Q' g" Q+ @# V4 u& q3 N; Q# N: E6 e5 V0 ?( r
/**  h6 h/ Z) k0 T, c& [6 o& _" K( m
* fb_mode_is_equal - compare 2 videomodes5 P+ E! E- _6 b
* @mode1: first videomode
6 l  v$ Y% s  u. M, |4 `0 b * @mode2: second videomode
, C  V4 _/ t0 i+ p$ v; T' c *8 ~; y( V1 H1 r0 O
* RETURNS:
  S9 q& r1 [/ [! D * 1 if equal, 0 if not/ y( z) R5 A0 r
*/
$ J' z' V; P7 a: Bint fb_mode_is_equal(const struct fb_videomode *mode1,, _) G% Y6 [3 o$ w
             const struct fb_videomode *mode2)
" V' q' D2 o0 Z1 I4 b& b{
3 }4 V( ]0 i& |0 w    return (mode1->xres         == mode2->xres &&9 L4 X) m2 V9 z& {; I6 t
        mode1->yres         == mode2->yres &&' c5 ?0 [1 E- I* c' d# j  y
        mode1->pixclock     == mode2->pixclock &&
/ J( I  i) c8 Y3 Z  S; O* X3 H        mode1->hsync_len    == mode2->hsync_len &&4 u. g9 r# ~2 u) I3 e4 s5 d
        mode1->vsync_len    == mode2->vsync_len &&. b: }5 e: l- H/ X* c: q
        mode1->left_margin  == mode2->left_margin &&  p. `# \7 E, ]
        mode1->right_margin == mode2->right_margin &&
. _$ w  J4 w4 Y: _0 m8 f* P, ?" y: ^        mode1->upper_margin == mode2->upper_margin &&
- O1 _5 [2 T' c( S        mode1->lower_margin == mode2->lower_margin &&
( u1 y8 V& W+ b: g' {& E        mode1->sync         == mode2->sync &&9 t' q7 e" ~' p5 y$ m, W6 T) s# p1 ?
        mode1->vmode        == mode2->vmode);
1 y9 X$ c6 Z9 D. @}
2 `8 X6 R& w$ ?' D2 l6 C  ?' Q( X5 Z; \9 S3 Q" F4 V
& S2 K' `+ |/ r7 y
fb_var_to_videomode函数只是将fb_var_screeninfo结构转换成fb_videomode结构。而fb_add_videomode函数查找是否该fb_videomode已经存在,如果不存在则添加到列表中。4 q/ ]! L, S- F( Y7 s& w
3.4 字符设备方法
1 [" Q: k! F' T, Z8 L  在看过framebuffer子系统建立和注册过程后,我们看下framebuffer留给用户空间的API是怎样实现的。
( s4 M+ S2 n* p: O, ]- w
0 s  n, e: y2 d% F8 i' y( [) b本小结只分析5个常用的方法,即open,release,read,write和ioctl。! ~; F! c" F0 n& _! T1 T& h, x4 W

  U" a: T1 v6 h2 f9 E  E3 I5 U  因为所有的方法和struct fb_ops定义的方法有紧密的联系,而该结构的定义由驱动程序给出,在这里我们提前看下在驱动中是如何定义的。3 `. N- l+ Y- t, s7 T
: I; h9 S0 e6 [7 c
下列代码位于drivers/video/s3c2410fb..c( b: L' y9 T+ ^$ H* t& G
! R4 r! P$ z; x; b' _" E
static struct fb_ops s3c2410fb_ops = {) O% B- S4 J; E$ D
        .owner                = THIS_MODULE,6 u4 c& r4 r6 @" O# t5 H: |+ Y/ ~
        .fb_check_var        = s3c2410fb_check_var,                        /*检查变量的合法性*/
9 c5 p9 k: J% T' P6 Q3 R        .fb_set_par        = s3c2410fb_set_par,                        /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/: p$ W; |6 q1 p. s1 O
        .fb_blank        = s3c2410fb_blank,                        /*该方法支持显示消隐和去消隐*/
) K, f( U7 u0 \9 V        .fb_setcolreg        = s3c2410fb_setcolreg,                  /*设置颜色寄存器*/
+ I8 @$ m& C7 g        .fb_fillrect        = cfb_fillrect,                                /*用像素行填充矩形框,通用库函数*/
/ H* F5 }( g+ C5 g* b2 F0 I        .fb_copyarea        = cfb_copyarea,                                /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/* i3 t. C- C; d7 N8 E
        .fb_imageblit        = cfb_imageblit,                        /*显示一副图像,通用库函数*/" f. j1 y' K) C
};
* d' {7 R, d* k最下面的三个方法使用的是内核提供的库函数,而上面4个则是由驱动提供。
, ~2 A7 v  i8 c* i- i& O3.4.1 open方法
. n7 Z, Z4 y9 d, h7 {2 u下列代码位于drivers/video/fbmem.c
0 \+ x; s* ]9 L0 o$ J% Z: @& g7 o" r7 K7 W5 Y2 f
static int+ I( K6 {7 A  z$ d6 n1 D+ a& L
fb_open(struct inode *inode, struct file *file)
0 P4 v0 S2 T; @. a__acquires(&info->lock)% w; a( l% q: r5 n
__releases(&info->lock)+ h+ Y7 {& `0 h3 q
{: i& S" i# o& ~6 b9 ^
        int fbidx = iminor(inode);
2 w8 a9 N9 M1 v& f& r& O9 Q: |: v        struct fb_info *info;
! e9 q( I1 d) q( V% R8 A! |        int res = 0;
1 z+ M, U0 X6 P- T& K* h8 a/ a! d7 ^' Y/ i- z
        if (fbidx >= FB_MAX)
9 ?! X% x0 S; h9 u                return -ENODEV;# e! A" [) _4 [9 p: t" E
        info = registered_fb[fbidx];                /*在register_framebuffer函数中已经设置了元素*/
5 M* g! G' f, ]0 W2 x( V8 P. O1 L+ f        if (!info)  U  B% l) h* R" G: U2 M
                request_module("fb%d", fbidx);        /*加载模块,这里不加载*/" ?$ ~, G( L, O* w3 j, C4 {7 ?( L; C" Z
        info = registered_fb[fbidx];* F! R- z- r, m& @9 G4 E
        if (!info)3 A  @! x, x9 O/ V+ R' P4 r3 e* ]
                return -ENODEV;* ]/ F; }; w8 k, |1 U" K* K
        mutex_lock(&info->lock);                        /*加锁互斥体*/
" E2 d* N; x: q6 i        if (!try_module_get(info->fbops->owner)) {        /*增加模块引用计数*/+ o" x5 s( A1 M; n8 n
                res = -ENODEV;
$ H" K/ d- m* F' }6 j& z' I% ^) L                goto out;! x6 p; p; ^2 ?( V. M3 O
        }
1 e7 _  h1 \' f' V        file->private_data = info;                /*保存info*/8 D) {# g: b# z8 \6 A: S, @+ {
        if (info->fbops->fb_open) {                /*这里fb_open方法为空*/
! h+ w9 a4 T+ Y                res = info->fbops->fb_open(info,1);
1 |* N6 w5 [9 `: w: ^5 J                if (res)
! |3 H' B7 x( ~' f5 |( E                        module_put(info->fbops->owner);
2 @+ K2 a; e; P4 a. J        }4 X3 @# Z" T3 x$ ^$ |+ _1 `1 `
#ifdef CONFIG_FB_DEFERRED_IO
; Y1 e# }8 m8 j  t. y8 w        if (info->fbdefio), v+ Q3 D7 S' Z4 G& y% \
                fb_deferred_io_open(info, inode, file);
. [- K% L/ Z$ F3 Q#endif6 ]) @  n2 g. a1 C' ?+ ~% w
out:5 ^9 M: m  b$ I' v8 i# u6 r
        mutex_unlock(&info->lock);                /*解锁互斥体*/' c1 g" N3 h" {8 V2 W* }1 {
        return res;: o. k3 g% a9 Q
}( w0 [  k2 B+ ^3 w2 O- u
主要的一个工作就是增加模块引用计数。还有,程序会判断是否fb_open在驱动中给出,如果有则调用该方法。我们已经知道fb_open没有给出。8 |* k9 P# f8 p. O1 f
3.4.2 release方法
5 y3 K! P% M5 m6 i! B下列代码位于drivers/video/fbmem.c/ J/ [- L+ ?# w  t. i) N2 H7 [
% a( @; e. b, b: I
static int
% M2 u9 ^7 N2 }4 {0 G: Zfb_release(struct inode *inode, struct file *file)
  `2 T" |, U6 J, }+ I__acquires(&info->lock)
: N5 c; p" ~' s- X3 a. n5 p% `__releases(&info->lock)3 Y7 Y; ?1 L) j! d( }0 O
{& ~* M# v+ l  [4 F$ O
        struct fb_info * const info = file->private_data;/ X! k  C7 T: K- y1 C' q
( ]& ~0 V3 ]4 h/ e7 e
        mutex_lock(&info->lock);4 S$ z' d( u4 u- [$ `
        if (info->fbops->fb_release)        /*这里fb_release为空*/
' T$ B  K: I; q# I) m( d1 F! i                info->fbops->fb_release(info,1);
6 ?, K  N  O4 S# ~        module_put(info->fbops->owner);        /*减少模块引用计数*/- C" S. T5 J! Y+ N/ y9 S+ p
        mutex_unlock(&info->lock);
3 P( n* t5 _# }2 H$ y0 B        return 0;
) V/ W' }3 D: {2 m! Q  u}6 V) y# N* I, H  f. U3 ], q
和open相反,减少模块引用计数。$ J3 b6 T2 ~2 p; R+ `& J. c3 J- k
( u. @! Z# M- F1 x' X/ h9 J
3.4.3 write方法
( K) G' p, Y& H0 [/ g3 l+ Y& y  通过调用该方法,LCD将显示画面。% n* X' C* V2 I

+ f) J- d+ b. p4 m. z6 Y) U( Z  下列代码位于drivers/video/fbmem.c& r8 H$ b* y4 {( g1 a7 J1 W$ O
! r, ?% K. k) x6 T  Y! r; n
static ssize_t
/ ~- F5 M$ |7 F4 V' ?2 h  [fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
8 f2 U! r2 t" W: _# ?{7 m, M0 W" D3 G8 s3 U4 p
    unsigned long p = *ppos;( [9 G+ E8 C0 P: I0 A- m
    struct inode *inode = file->f_path.dentry->d_inode;# \! L1 C& ^6 k
    int fbidx = iminor(inode);- \. z9 ]$ t+ U5 ]
    struct fb_info *info = registered_fb[fbidx];3 I* r9 L/ i: ?  ^5 V  M& V" J
    u32 *buffer, *src;
8 M7 l- E8 r% i! Y) g% H; F- T    u32 __iomem *dst;
# n5 T2 f& z6 {/ A% ~5 e    int c, i, cnt = 0, err = 0;+ v/ ^, v% c$ h$ p$ |, @5 ?- w
    unsigned long total_size;
( b' q5 B! G; F1 }  b3 X4 H5 a9 m0 _) N% s8 i* R& L* S
    if (!info || !info->screen_base)    /*screen_base在驱动中给出*/+ d9 F9 S- K1 u* N  R8 @# `( a
        return -ENODEV;2 \* r. K6 U9 D8 Z' k5 O
( N& z9 g4 J, Q0 B
    if (info->state != FBINFO_STATE_RUNNING)
1 e! Y. U  |  Z( }        return -EPERM;! U1 R! o  [$ @( }

/ U# w, p: ?4 V1 u3 K7 \: _+ i% |& K! X    if (info->fbops->fb_write)    /*没有fb_write方法*/, V) u' U. b8 H2 q& s- r
        return info->fbops->fb_write(info, buf, count, ppos);/ W) ?3 ]( {2 `4 X- r# Q- {: F
7 e1 \7 V9 }; Y/ f9 Y
    total_size = info->screen_size;    /*screen_size没有给出*/
! W! u" s$ `! _  j
9 [+ O: Q4 n+ v  h3 b8 |    if (total_size == 0)9 f+ U! A. Q: T5 a9 |( a8 R2 b
        total_size = info->fix.smem_len;/*153600字节,驱动probe方法中计算*/$ a: R9 b. ], r# X' v% b( g
3 F/ w$ y5 M$ T: E
    if (p > total_size)& q. D& O* V! n8 J/ o
        return -EFBIG;
# e, Q  V9 Q0 n3 h5 `  {9 `+ l1 ~# e7 f0 D- s7 J# C/ z
    if (count > total_size) {    /*要写入的字节数大于153600*/
; m: z, X! C6 q& X        err = -EFBIG;        /*file too big*/
8 p3 h0 E5 j$ l        count = total_size;  _( I8 Q! n' h* |4 N
    }+ C: w: y) U; {: c
, S4 b: z0 w/ h
    if (count + p > total_size) {/*偏移量加上字节数超出了缓冲区*/7 q/ \$ h2 H, b' p7 g
        if (!err)- n6 D3 E, j+ t8 l8 H
            err = -ENOSPC;4 F( x  I; X( W. S2 t
/ k& s4 V* F$ W0 d
        count = total_size - p;9 X8 p! B: \2 `" q  i. s! A
    }) w* d( q4 z# x2 `' L6 V
+ `* v1 L. {, v! S, H7 M
    /*分配buffer,GFP_KERNEL*/
5 H  z, T1 u. s1 S9 w# D    buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
6 s$ C2 i/ c! q: W; X             GFP_KERNEL);                /! S1 O& }! c" U* D( j- F4 Q9 M5 O
    if (!buffer)
; o" t4 s$ f$ C: y- B        return -ENOMEM;+ v& S' V7 h6 i: W9 {9 T
% K, R5 e% `0 }) A0 z7 m
    dst = (u32 __iomem *) (info->screen_base + p);/*修改目的指针*/
; R. M( }+ Z( j( Z5 J7 z8 X$ v
& W" `1 m. ]. }* c* h! q    if (info->fbops->fb_sync)    /*没有定义fb_sync*/
& [, b3 F5 ]* O$ y7 B. j        info->fbops->fb_sync(info);- c# j$ h& x% [7 l* Q( w$ y
9 S% G) }- g3 r
    while (count) {) y0 o4 R& X: c8 z7 y0 i
        c = (count > PAGE_SIZE) ? PAGE_SIZE : count;/ \; {. Y( V2 T8 {6 y2 p
        src = buffer;2 r7 G3 b* X7 m2 P1 }9 x( \
" i0 W, W8 g/ N5 W' j* Y9 \
        /*从buf(用户空间)拷贝数据到src中,一开始为1页,最后为count字节*/
( w& j8 L+ M! W5 W( s9 u        if (copy_from_user(src, buf, c)) {    : Q7 D* Q$ f' _  V5 v
            err = -EFAULT;- i2 {: A# I7 p3 D/ \) `" E
            break;
9 K8 }! D$ d2 |; s: k/ {        }7 L1 B  }9 I! t' n; T" a6 {
        /*一次for循环,写入4个字节数据到dst处*/
1 @- ~6 a, D" J, J        for (i = c >> 2; i--; )9 O3 w. e" [7 |" ]8 ]
            fb_writel(*src++, dst++);
) I# v3 w: p: I6 f        /*最后还有3个,2个或者1个字节*/5 C3 X& v" c4 Q' `, C
        if (c & 3) {/ i. ~% K3 k$ {9 M
            u8 *src8 = (u8 *) src;
0 k/ [- s$ Q$ h& R            u8 __iomem *dst8 = (u8 __iomem *) dst;
* L( h$ g8 G* L1 X( L            /*一次写入一个字节*/, M4 S* u$ O0 d/ L/ ]
            for (i = c & 3; i--; )! ~* _4 X& ]3 k3 S( u2 B0 U& x2 f" x
                fb_writeb(*src8++, dst8++);
8 N7 l2 k; L+ O/ n+ s
  s$ K. `. C6 \            dst = (u32 __iomem *) dst8;
8 H% `* ?# j. {) k  _7 u/ @        }
8 p% W4 F; E$ }- t3 @" N) ?: C) x5 a& G. Y1 @8 e3 `8 a
        *ppos += c;    /*用户空间偏移量增加*/
' ?/ Z, j9 k; ~2 c2 p4 m. F# w        buf += c;    /*用户空间指针增加*/
" k+ i' K. }: s% h' k" z/ r7 M+ H        cnt += c;    /*修改已发送字节数*/3 N$ c2 |  k/ K$ p, J! ]. r
        count -= c;    /*减去1页*/$ O4 h( O3 ?& N* R3 L
    }
6 {; q2 i( Z" G4 R, R' R
% j! V( K' G! C/ Z& q& T    kfree(buffer);    /*释放buffer*/( n) u7 p! P2 ~# U7 b: d
1 a* M3 r% I$ D- b  ]9 g  q# K9 M# y
    return (cnt) ? cnt : err;& T& c1 A: M2 ?! x% S
}) B! n. ~( k1 A/ P
2 ^5 H: T2 S) A) x. c' `5 R
这里,做了一系列的检查之后,开始拷贝数据。这里一个有三个buffer,一个是用户空间提供的buf,一个是在这里新开辟的buffer,还有就是驱动层提供的screen_base。
( q) g) K) ?: D" x+ f3 G9 v数据流如下:
0 E$ S& n8 a0 W+ V( k1 O/ u9 a3 {5 X+ x# N: b) w

, e" {1 x" z/ J4 A8 p' X2 @# ?
% Y, c$ ~& T! b' m. p. q4 ^用户空间的数据首先被复制到buffer中,然后从buffer中复制到screen_base中,最后被映射到LCD上,LCD就显示响应的画面了。
* h" Y! C6 p" L0 O5 C% H' Z8 G
0 i5 ?. j$ a  G, V$ i$ L) l" \' z7 P3.4.4 read方法  u. k$ I- }. C$ g4 K+ t( f
该方法用于读取屏幕画面的数据。
$ Y9 P# f0 t! n$ a! G
" C  b" {3 X+ ]* m7 ]read和write类似,只是数据流是反响的,就不多做介绍了。
$ q' f7 `3 U2 w# s7 V
5 G4 D3 i3 R& I% h/ i* x' i下列代码位于drivers/video/fbmem.c
" h! l, c' u& D9 u$ F4 p
' `) k& {7 a& B0 d+ I0 Fstatic ssize_t0 M4 S2 B( r1 [8 d5 w. v
fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)' B. b1 W/ _7 y( }/ P/ K
{/ x) y: [; w+ z, s# E) d# g
        unsigned long p = *ppos;
7 I# q# |/ K4 P- r$ L        struct inode *inode = file->f_path.dentry->d_inode;$ W3 o  g* G) V4 `
        int fbidx = iminor(inode);
( V) D) t  u/ l1 ~4 [6 g        struct fb_info *info = registered_fb[fbidx];
, m) V( C, R8 f' [, P" i$ r& s        u32 *buffer, *dst;
  F: P) g& o! B5 C  E7 C        u32 __iomem *src;
7 ]3 U5 I, y0 ^# X+ s! E        int c, i, cnt = 0, err = 0;) u1 j7 ~5 p: r+ c4 ]9 J7 u! M
        unsigned long total_size;( y- `, t3 i# c+ W2 n$ V  L
) J, n$ h4 v( p# x
        if (!info || ! info->screen_base)$ }" n  W3 Z0 H7 O2 u
                return -ENODEV;
; ?) K, s3 R2 F! m
/ f4 }0 J0 W4 t        if (info->state != FBINFO_STATE_RUNNING)7 F& Z9 a/ t1 ~( l0 b
                return -EPERM;
; E! i, _5 x* k7 J5 ]  v) {- h1 t2 k* ?2 A( p4 y/ X3 X- r. t
        if (info->fbops->fb_read)        /*没有定义fb_read*/5 y8 k, e- z# T# _2 r
                return info->fbops->fb_read(info, buf, count, ppos);
0 Z/ d# F; R0 h        8 Q: G  \9 t* O- x  E' W  }
        total_size = info->screen_size;2 g# Q9 W6 h3 d2 d# W/ T

8 R9 M, N5 g' Q! l% z        if (total_size == 0)# D4 h# }; @0 {0 d7 {
                total_size = info->fix.smem_len;
& M3 [  X9 a7 n7 Y/ v# D' a" Z6 K; ^
        if (p >= total_size)) L1 {5 L' q  p
                return 0;
" h1 ]4 w9 N# t" ]5 A
6 x: k4 r, F' e( a9 X3 [        if (count >= total_size)! G5 o3 ^( z1 h
                count = total_size;8 H$ ^4 t; x# s4 n

3 R# _: A( G1 G+ j3 e4 c% I  O, l        if (count + p > total_size)
% m( ~7 b8 a" \                count = total_size - p;
6 s9 ]; k% h+ ]% c: @: x( P2 e: R. g2 x1 w1 S) ~: @3 ~0 z
        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
( }! O: U6 b6 i2 R9 D, y- m                         GFP_KERNEL);* Q, W0 y$ H; `# n4 j! h( K
        if (!buffer)  C5 r3 k! f7 N8 c' J% j- \
                return -ENOMEM;1 o" a: \4 \* c8 o
) z1 |+ l; n; q0 V. M, g
        src = (u32 __iomem *) (info->screen_base + p);
2 L, B3 @# g( C$ M! V+ d/ y: s8 b* I( n8 U* W6 {3 b
        if (info->fbops->fb_sync)
7 L, a) d# [/ \) Q                info->fbops->fb_sync(info);/*没有定义fb_sync*/
* X! n+ k" S4 I) B6 J% g% ?7 I6 b5 {. B5 m* D* k
        while (count) {
6 `4 s6 |3 C: j& k                c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;0 ]3 }8 ~$ f8 m0 [) q: X: K' C# K
                dst = buffer;2 B1 r# O6 q% J- z
                for (i = c >> 2; i--; )
1 @5 v- [7 C: \7 |. j                        *dst++ = fb_readl(src++);
8 L3 r+ }7 W2 w3 M0 H) a                if (c & 3) {; P  @% e; w3 s3 W& i1 |
                        u8 *dst8 = (u8 *) dst;
% I" ^- {1 x# j* V" E                        u8 __iomem *src8 = (u8 __iomem *) src;
) D  C  r5 j. w- |
9 z1 f5 H% T& {  B& e                        for (i = c & 3; i--;)3 a# N/ x# c6 r: t* ]
                                *dst8++ = fb_readb(src8++);
$ l! o& k* B$ J
4 y2 V: T4 B% f% ]4 q3 ?; R                        src = (u32 __iomem *) src8;
4 a0 l  n9 C( k. ?, n                }, T7 w6 d3 X$ {( L/ C' P
. r* S0 ~0 r3 _$ u
                if (copy_to_user(buf, buffer, c)) {. O$ ^4 r+ x. v  Y. V: {
                        err = -EFAULT;
) e- [5 D; m5 O1 F                        break;
- c/ b* Q3 h4 }' W2 v" t                }
& E' W+ E1 j0 {2 R+ D                *ppos += c;
, k' e7 V) j( a* m$ D% N                buf += c;
+ i) M' [8 ^# w: w                cnt += c;
2 ]) w  _* P( o/ r; B  V: H                count -= c;
  s+ _( Y2 |# n8 {" h        }
4 f+ {! o; J! U+ ]  ~
) k" y3 Y( Q/ Y- _/ q* s6 k: p! t        kfree(buffer);! K0 ~# |4 l( N' @
6 w3 U6 f% W4 O# f& \
        return (err) ? err : cnt;# T: K; I1 M( Q7 z' K
}
  ?( U* K) z: j3.4.5 ioctl方法$ ^( s0 @6 N- b  c* u/ g9 ?* `9 ]
这里只是简单的看下ioctl方法,这个函数调用很多其他的函数,详细的请自己看吧。
, z9 g+ [% I, I3 L/ P+ B- s6 f/ w, j& ~- q
下列代码位于drivers/video/fbmem.c. c, `$ X( e3 {  B3 g
5 x# F* W2 l1 F4 U  M1 u
static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
' x$ W; p" T  k" u2 r{/ z/ l) F# i8 d" ~( K
        /*获取inode,再获取对应的fb_info*/
9 H% p5 p3 z3 j: f3 `        struct inode *inode = file->f_path.dentry->d_inode;
+ L  F2 N! R! K' j8 ^! h. l        int fbidx = iminor(inode);        8 z& _0 z  u1 T( ], m' G' z$ O
        struct fb_info *info = registered_fb[fbidx];
8 N% D  M# e+ Z- }. Y
! y5 Q: x6 @; O, x& t8 i, {1 ^        return do_fb_ioctl(info, cmd, arg);$ h9 Z) c# }! E6 [7 ^% v- m4 l
}% D& ^4 E8 r& m8 k" S
6 T) E5 b1 m7 n' F- m
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
' B) e4 e1 q. o            unsigned long arg)3 j, x0 T7 v" v1 h
{
( \5 L. Q8 ]; S) U" l9 ]$ b    struct fb_ops *fb;7 q) ~* d! X+ a2 g* u
    struct fb_var_screeninfo var;
" O5 E1 Y- B5 `% y. A0 j    struct fb_fix_screeninfo fix;% r" X! x9 X- {9 y$ a4 V
    struct fb_con2fbmap con2fb;7 w4 ~: r8 k/ t8 c- d; H
    struct fb_cmap cmap_from;
5 z: k2 Q$ D: ]/ W5 I    struct fb_cmap_user cmap;
/ |) d, [- N% ?) O    struct fb_event event;, w) v1 ?1 I$ [! x2 E* e; I( _
    void __user *argp = (void __user *)arg;
$ x5 W) ~& K1 \    long ret = 0;0 ^  ]  u' Y" V; i  p

& P& r+ Q) |0 p2 P    switch (cmd) {# M7 C0 X: N" ~" R1 }0 l
    /*获取fb_var_screeninfo*/
% K+ q& e% U  W0 y. A0 R' b  y: l    case FBIOGET_VSCREENINFO:   
; U  d. f- t+ t* k% u: {, L( e        if (!lock_fb_info(info))    /*加锁互斥体info->lock*/
2 p5 P7 ]1 b# A. Z2 v; k% y            return -ENODEV;. |0 e* _  [% ?' O  C
        var = info->var;            /*复制var*/
6 I; q1 L$ k, Q9 {  M5 F        unlock_fb_info(info);2 N$ a5 T3 i6 W) L' H( ~

- F  `- p+ v1 s, G        ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;    /*复制var到用户空间*/
6 D" N8 F0 f/ U4 s- A& |$ I        break;
1 ]; }7 Q. l- q, N' \7 R    /*设置fb_var_screeninfo*/
% O6 Q7 d5 q9 t: M0 }: S: E' y    case FBIOPUT_VSCREENINFO:. a5 ~( i* E7 x2 n
        if (copy_from_user(&var, argp, sizeof(var)))    /*从用户空间获取var*/8 d% ]0 I" W2 ]9 s& H
            return -EFAULT;
& Q$ a! ]$ K. S  C% P8 X        if (!lock_fb_info(info))
% K: L& ]$ R8 h4 h            return -ENODEV;5 m+ p( Y5 q$ @1 W7 \
        acquire_console_sem();
. W! F4 j' ~) [' S/ h        info->flags |= FBINFO_MISC_USEREVENT;
, T) {$ P1 A% G" \: \* E        ret = fb_set_var(info, &var);            /*设置var*/
* {4 D' R' D9 _2 W        info->flags &= ~FBINFO_MISC_USEREVENT;  _* {; k& r" ~: S& O( Q! a# q
        release_console_sem();. f$ c  O7 @% C5 |  [
        unlock_fb_info(info);( s% H$ U: {" |" `/ n# F7 J
        if (!ret && copy_to_user(argp, &var, sizeof(var)))
& X- M0 ]& h6 v8 q% L4 |            ret = -EFAULT;* J+ u! `! U0 V$ r; u) x' ?
        break;  }! M% z. C. r  a5 E
    /*获取fb_fix_screeninfo*/    /*fix为不可改变信息,只能获取,不能设置*/8 u  y. D0 _$ E+ \3 ?3 a
    case FBIOGET_FSCREENINFO:
% L- V: h# G- B2 M+ {        if (!lock_fb_info(info))" w  ]  ^# G- X' j/ z' D! ?. ]
            return -ENODEV;9 y3 O' a$ L; @; ]. M) T- R
        fix = info->fix;
- D4 K$ D* w8 z: ^  R! m0 }        unlock_fb_info(info);
- I$ i/ Y; y  c! L: s# T. O4 I0 ]
5 I4 x& A5 ^0 k8 O9 K        ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
0 P  T7 W/ b; X6 X3 Z' H" s' [! j0 f        break;: T$ n' H$ l) [9 x1 P, H3 A
    /*设置fb_cmap*/   
* W* v1 `& h* |2 [5 \7 T    case FBIOPUTCMAP:# a" a- l0 E/ l- \: @: q2 a
        if (copy_from_user(&cmap, argp, sizeof(cmap))); O3 `8 F' e; R- F
            return -EFAULT;
* R4 @5 w5 m1 q! J) p# A. d9 ~        ret = fb_set_user_cmap(&cmap, info);    /*设置fb_cmap*/, v) N) o* S7 x' |3 Q( p3 Z( D) C
        break;
% R, W" U8 g8 n2 N3 e( ?9 {    /*获取fb_cmap*/   
4 u# k* H5 O" Z: F5 k2 i    case FBIOGETCMAP:
1 [/ G7 Q7 Z( F        if (copy_from_user(&cmap, argp, sizeof(cmap)))$ h8 B! n7 c" i5 }
            return -EFAULT;( ~: C5 q  X& Q3 P" l
        if (!lock_fb_info(info))7 @: P& u9 ~; X" V& \1 D( L2 b. d
            return -ENODEV;
0 G- p% G3 m2 r. n% I        cmap_from = info->cmap;
  z6 S% l' b4 d- j$ g        unlock_fb_info(info);
3 M  J5 N* S. R! M        ret = fb_cmap_to_user(&cmap_from, &cmap);/*获取fb_cmp*/
: ?: p% a3 B4 O6 W        break;* h  a( R6 K$ t1 O" L* q' _
    case FBIOPAN_DISPLAY:
7 m9 R$ p9 B( \& x( I        if (copy_from_user(&var, argp, sizeof(var)))
' D) k, Q& K: N7 Q$ x2 Z/ q            return -EFAULT;
5 U2 D$ P# _7 U  A9 I, f        if (!lock_fb_info(info))
4 R" y: ]0 ^0 N& C: n# `, T            return -ENODEV;
5 y& N% N. Z2 [9 ^        acquire_console_sem();1 Q9 m- a8 f9 v5 _$ S# f( u
        ret = fb_pan_display(info, &var);
* f* e3 P# p, L  _        release_console_sem();7 K; E# K: Z* T/ K! u7 R9 Y
        unlock_fb_info(info);
. ~" d4 n9 i/ |7 B. v9 f        if (ret == 0 && copy_to_user(argp, &var, sizeof(var))), M+ V6 j5 w6 A" k
            return -EFAULT;
3 o2 j9 X# [, z# P. W        break;
# g4 U) p$ r- L- E    case FBIO_CURSOR:" ~+ t  {& \! T+ L! X' t
        ret = -EINVAL;3 o$ L; [( W+ K7 X5 r
        break;, F* g3 B* }0 D0 P/ u2 V! q$ `
    case FBIOGET_CON2FBMAP:
5 |& b; g/ h1 W        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))0 D# i; W$ u+ f/ N! [( ?1 V9 e
            return -EFAULT;
3 A5 }* y  O& j% H: g6 k        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES): r6 Z1 K0 e4 o4 v! g
            return -EINVAL;
- Y. S/ T- T, ^9 ^        con2fb.framebuffer = -1;& O- E" H5 P+ {( n  K" c1 h" d
        event.data = &con2fb;: z- X6 a& b8 [& v( k8 b
        if (!lock_fb_info(info))
& K1 t2 M* ?" M" l; R: S            return -ENODEV;
+ b! k" V3 S5 O5 T# ]3 h) C2 C; a        event.info = info;2 T" L2 I7 }) w$ q; p
        fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);( D  {$ b0 W/ j& @
        unlock_fb_info(info);! C2 ^; R2 c  Y. c8 ^6 {- Y
        ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;; E4 A  J, z2 X# [% m% \* `
        break;# |% ]  X8 V5 |# I/ L
    case FBIOPUT_CON2FBMAP:
/ {) b6 K5 Z3 b& d- ?        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
1 i& U  E0 ]5 r4 h+ V& N3 r            return -EFAULT;3 k; p% l! U. ?4 Q3 I% ~5 I
        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES); s! Q+ G* n' x, G
            return -EINVAL;
( V5 v2 r/ m' P* b9 |        if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)+ Q2 X0 K" s, {% `( L+ y9 t* R
            return -EINVAL;0 R0 S( U! p' N" R6 c* w
        if (!registered_fb[con2fb.framebuffer])
# S( n. N( B. g6 n% w! G! w            request_module("fb%d", con2fb.framebuffer);
: m2 Z% P0 K! a. H9 ], d        if (!registered_fb[con2fb.framebuffer]) {
) t/ ~" U+ N! F  A( _" Y* j            ret = -EINVAL;" |* ?" I: W- Q- E
            break;4 P4 `7 T2 x9 G9 b
        }
/ s6 z8 U) r% r$ S6 ~1 K        event.data = &con2fb;
0 Z9 X8 t1 P8 j: M/ L* N! q        if (!lock_fb_info(info))1 X& t( @0 y: }
            return -ENODEV;- s3 U( ]5 f; F$ E; q0 i
        event.info = info;
6 G+ H; p* Y, T' j$ E        ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);4 `) I& ]: |* W) L( K: `
        unlock_fb_info(info);
/ f7 z4 \  }  h2 y        break;
7 S: s( y- @2 a    case FBIOBLANK:
, G, Q! n: @! f  q) h2 D        if (!lock_fb_info(info))6 u' M0 @. @! Q* O1 A
            return -ENODEV;  D# ?% b# Y4 K- z/ H9 Y
        acquire_console_sem();
; K' Z- m+ E0 l7 S6 \4 i        info->flags |= FBINFO_MISC_USEREVENT;4 l! N# ]1 S/ ?' n& ~  ]
        ret = fb_blank(info, arg);        ?*最后调用驱动提供的s3c2410fb_blank*/! ^; q2 I& m' n' [3 o
        info->flags &= ~FBINFO_MISC_USEREVENT;
/ F" w) M( V8 o  t* N        release_console_sem();: O& ]9 Z/ O! T
        unlock_fb_info(info);; V2 x1 P+ p+ s/ A/ J) p7 L
        break;- n% Y" G8 J$ d! {' B
    default:- ?( T  s* \1 @; k
        if (!lock_fb_info(info))
6 t" X& w* i7 p4 ]- j+ V            return -ENODEV;
4 l: I8 T4 r8 R% Z0 ]( ?/ o        fb = info->fbops;7 @+ a  K9 P' y. E& s9 I7 j4 f
        if (fb->fb_ioctl)        /*fb_ioctl为空*/7 C& V, F2 d$ k+ r0 `
            ret = fb->fb_ioctl(info, cmd, arg);( T; O  c9 ?. h/ z
        else
8 ~5 q' t' t; m/ ~6 G  }! N            ret = -ENOTTY;2 a: Y2 I+ W; f" }/ l9 @
        unlock_fb_info(info);
0 M2 ^* n% B. e4 R    }
; A7 k: m9 n" t: _5 A5 Z    return ret;' X) ?& q8 a9 z7 E: W! Z
}
# A" ^9 h5 q8 b2 A' F+ |' U, C, e正如2.2小结所说的,fb_fix_screeninfo只能获取不能设置,因此,ioctl只提供了获取fb_fix_screeninfo的方法,而没有提供设置fb_fix_screeninfo的方法。) b" z$ c, n# c5 c% i% [1 z& O& d& q& s
6 n1 Q( q5 U3 ^( }( W) p( Q/ E' P
3.5 小结
/ M3 J( P8 A; V' n0 |" `# I  本节对frambuffer的核心层进行了介绍。包括frambuffer子系统的创建,frambuffer的注册和提供给用户空间的5个API函数。下面开始介绍驱动层。
3 z& e  A; P6 H! N1 H, W3 G9 g- G
0 ?: v9 Z9 b+ \4. 驱动层6 H! W9 c+ `% i7 A9 R# i- E
本节将开始介绍S3C2440的frambuffer驱动,该驱动源码位于drivers/video/s3c2410fb.c
/ J  c- |, h( j) x5 G
: O! D% h' H0 T/ M$ K; r首先来看下驱动模块的初始化和清除函数。
  R4 W1 ]' a3 F$ t% R& V/ W9 |- ]$ {# |4 G
4.1 s3c2410fb_init和s3c2410fb_cleanup; U: i7 l" t$ D8 L' @7 m
static struct platform_driver s3c2410fb_driver = {: Z6 M0 }4 B1 @: g9 _/ {
    .probe        = s3c2410fb_probe,& G8 `1 C2 t* U9 U/ j
    .remove        = s3c2410fb_remove,% S  M) E3 Q" X- ?" s. f0 V! R
    .suspend    = s3c2410fb_suspend,
+ y& Q0 \: j: X6 b1 O2 w( k1 T    .resume        = s3c2410fb_resume,6 G; D& y6 R8 z
    .driver        = {1 n5 b% x& h* c: v' I4 Y
        .name    = "s3c2410-lcd",) _7 Z- p6 c$ z, ~2 h8 |
        .owner    = THIS_MODULE,/ [* `0 N4 u; q# e  ?* u
    },
) c$ p( x6 g/ V+ y# h" G};9 W3 ^/ ~# ^7 k7 X! j' E) l

( A4 u# S0 p9 ~4 @* @8 {. tint __init s3c2410fb_init(void)% e& h( u5 N: ]
{, U% ~: N: g/ n; s- Q% G' U
        int ret = platform_driver_register(&s3c2410fb_driver);
' C" g' f- a3 T4 y1 _& x; |
3 }4 r9 Y% D5 H        if (ret == 0)
) J  Y: x" }! v. E' C                ret = platform_driver_register(&s3c2412fb_driver);;% d; T% w: Z% H# }2 k% h$ [
: c1 X+ g8 f) s2 ]  \3 t7 F
        return ret;
6 H1 W; T" z5 Q}) r4 {3 ^) e# ^" [- {2 @
" \6 x( `3 U4 O$ Y, c! H
static void __exit s3c2410fb_cleanup(void)# v8 s; N# p- x. Z6 [& m% \' R
{+ Y* _! ?8 O9 j# Y/ A# _/ g
        platform_driver_unregister(&s3c2410fb_driver);
8 b/ k  d; [5 m) t        platform_driver_unregister(&s3c2412fb_driver);1 S, u  M6 x# h1 j. r
}
% o! x4 p1 O6 _$ |& [# w. u/ ^. z# {0 c
module_init(s3c2410fb_init);  J" ]! w( z: G% z! T. ^
module_exit(s3c2410fb_cleanup);
7 S6 d; O* I2 p5 Z9 R: T
7 z/ I# S, Z/ Q9 I3 G当platform_driver_register调用的最后会调用probe方法。接下来就来看看probe方法。
" R9 l0 l9 I4 O4 Z! c; P: D4.2 probe方法4 U; D: G6 J9 ]- S# z, t' p
struct s3c2410fb_info {  X6 K& p5 D" b
    struct device        *dev;- r9 B* B  m# c' @+ Z3 ^
    struct clk        *clk;
' G  A! N4 H# B& a. ^3 M1 \; ~, t% w/ S6 V- s# t4 [1 i
    struct resource        *mem;
% v3 P  a! d+ V1 D( v    void __iomem        *io;        /*虚拟地址*/
# D, I$ P6 P( H  j    void __iomem        *irq_base;
1 y$ d! A" M$ ]  ^( q+ e" g1 I
    enum s3c_drv_type    drv_type;! X9 e2 K. u8 I
    struct s3c2410fb_hw    regs;8 z' z# M9 Q5 W

% ?2 w6 F1 o! V" F' _4 w1 |3 D    unsigned int        palette_ready;" c* {' \* m+ r1 o. v2 q/ k1 z8 V

+ g7 ]* U) i: R) D" f    /* keep these registers in case we need to re-write palette */
1 J& l; u( ^, h4 E+ f    u32            palette_buffer[256];! V" J' P2 D6 H; y4 k
    u32            pseudo_pal[16];
5 q  f0 }2 q9 g# s};! V! C% ?7 k2 i; x7 s  C7 Y
' Y' I0 t+ B! h+ t: ^$ D, N
struct s3c2410fb_mach_info {
1 y" V" ]( x' b# w: V
4 C6 `- Q8 i  N8 H9 j& T  y' B        struct s3c2410fb_display *displays;        /* attached diplays info */( |' t! t, b. K1 T
        unsigned num_displays;                        /* number of defined displays */
$ Y3 \! w5 \" q! o        unsigned default_display;
! m5 Q  N; y  ~5 |
: z8 [* |. p# l/ u. h8 V        /* GPIOs */# W/ ~9 S, Q# w0 b' t) N
! H: i$ r: j+ _+ Y7 G
        unsigned long        gpcup;
" [+ q/ n% d: X0 E) ^# D( N4 V& d; m/ b        unsigned long        gpcup_mask;
. z: V6 X$ i: F4 f5 ~' R        unsigned long        gpccon;) K/ e3 X8 t( f4 g
        unsigned long        gpccon_mask;
* w+ p) {6 S  w9 u/ n/ N' J        unsigned long        gpdup;8 ?* k" x( R3 y. H9 o1 P
        unsigned long        gpdup_mask;& f" C, e, t' d" r
        unsigned long        gpdcon;
! b, O- R, k6 O9 |$ w* i; M* \        unsigned long        gpdcon_mask;2 J# A# F1 J1 a  J3 y% L9 l
. [, Y" B6 L5 ~' U5 I1 C
        /* lpc3600 control register */
3 b5 y1 |# f6 ?; g. y        unsigned long        lpcsel;
  ~+ h* D8 r' ?};& A) Q" J+ O' g9 Z, R2 `5 {/ G

" a1 s# {5 E- g7 D# W$ O/* LCD description */
- y" K9 i: R6 n" }struct s3c2410fb_display {
0 K! I5 o; m; F% ?5 o" t    /* LCD type */$ _; I+ d, c7 P- A3 q& }
    unsigned type;% i! A+ n* ]# ]' i

! C) n- N+ f8 K: D6 ?* S0 X/ N0 ?    /* Screen size */
, i* j! ~" X7 q- i9 d    unsigned short width;
8 c. d3 l( z. |. q0 }+ m; k    unsigned short height;
4 a5 v1 ?; \6 F! m/ O! p6 G$ M2 {; v" H6 u! W
    /* Screen info */
9 j+ Y! y4 `, P7 f+ w$ a; s    unsigned short xres;
- u; H/ R, d  u5 i& C    unsigned short yres;
9 @' G: t2 J8 l: {7 N    unsigned short bpp;5 L2 n4 J* t/ s2 l! |+ L8 Z

# L5 j' S1 b9 Y  {: \1 h' t: X1 g1 s3 U    unsigned pixclock;        /* pixclock in picoseconds */
& E$ o/ Z/ F- h$ b8 g7 j6 ^9 w" A    unsigned short left_margin;  /* value in pixels (TFT) or HCLKs (STN) */2 n3 q  L7 f/ ]- N0 D5 G
    unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */
1 I# ~0 g$ z& E0 M    unsigned short hsync_len;    /* value in pixels (TFT) or HCLKs (STN) */
) F& d7 u* F2 e- J6 \* R$ F- u    unsigned short upper_margin;    /* value in lines (TFT) or 0 (STN) */# Z( Q5 s$ C* \8 g) i5 c
    unsigned short lower_margin;    /* value in lines (TFT) or 0 (STN) */
3 z, s2 z) s1 l! R  ~% G    unsigned short vsync_len;    /* value in lines (TFT) or 0 (STN) */1 w$ Y5 n' o; \0 I1 G- y/ ~  {

$ U: P6 a1 w% {* P5 [7 D    /* lcd configuration registers */; _1 n3 h5 _: D0 b* O
    unsigned long    lcdcon5;& ?% k$ r7 s' w4 w5 k* ]) u% W  R
};0 z4 k" G1 D2 K- T+ n
5 p* x* q6 G+ k$ |1 N3 u
static int __init s3c24xxfb_probe(struct platform_device *pdev,
% N, i  v( z+ D5 Z  y9 t+ K- ~                  enum s3c_drv_type drv_type)* [9 k, f. B% C8 C- @+ b  u
{
! {. m# ^% ^& A: D- M    struct s3c2410fb_info *info;* i0 D, ~7 V) T  x) E* d
    struct s3c2410fb_display *display;, E1 i- N; m  p3 A5 d
    struct fb_info *fbinfo;, A; y; O. j2 ~, j  ]
    struct s3c2410fb_mach_info *mach_info;6 q: w- Z" _6 v* @( |& f. {+ x
    struct resource *res;
; P" Y9 n' s& B# [    int ret;( S: @# q5 e( p9 i0 |; [( l
    int irq;7 E# I8 ]: f( C: b. \
    int i;4 `$ h- u: l/ ]* r
    int size;. ~9 H4 Y3 X" }# P8 E' e9 n/ h
    u32 lcdcon1;
* F) J7 W/ _8 F, O3 g    /*dev.platform_data由函数s3c24xx_fb_set_platdata(mach-smdk2410.c)设置,指向s3c2410fb_mach_info*/
/ [8 e$ g# x( s+ ^6 c, K% O' a1 M$ R3 S    mach_info = pdev->dev.platform_data;    ! ]# B5 @/ ]+ f) ]# j3 T
    if (mach_info == NULL) {' T) z9 D5 R9 d& Q' i& ]
        dev_err(&pdev->dev,
) v5 D, _# m0 b8 C/ e            "no platform data for lcd, cannot attach\n");) ?! c8 n6 \6 L  H9 V
        return -EINVAL;6 D- `0 ]& z! c4 F# l$ }
    }
& S4 D: D, o, J  c: G                                    /*在mach-smdk2440.c中,default_display=0, num_displays=1*/4 Z1 }( {  {& x
    if (mach_info->default_display >= mach_info->num_displays) {     $ \+ m- T2 ?* g2 y8 |* |
        dev_err(&pdev->dev, "default is %d but only %d displays\n",9 r; C9 C/ P; T
            mach_info->default_display, mach_info->num_displays);1 A1 d- L+ n- r+ k; ]
        return -EINVAL;
; I, \/ q5 N& ~' {0 p    }$ V9 W; F) Q- s6 }1 E% l0 U

+ T9 @. s$ f! @; v. V    display = mach_info->displays + mach_info->default_display;
- r$ W+ Y( g* g7 V6 w
3 g6 P) t: M9 ?6 q1 g2 ^5 O0 P5 Z    irq = platform_get_irq(pdev, 0);    /*获取IRQ号,16号中断*/; |) U& e' u- U8 B7 f
    if (irq < 0) {: o4 v  [$ _3 y7 A3 Q. v! K0 K9 L
        dev_err(&pdev->dev, "no irq for device\n");4 R& x* R, G6 @/ F5 {3 D
        return -ENOENT;
: ?! i# x9 ^' \/ ?, y    }
2 j! K9 Y) G$ x                                        /*分配struct fb_info 其中包括sizeof字节的私有数据区*/3 X6 U3 M- J0 r9 O3 m; q" S' y
    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
2 _9 }- t( |6 g( x    if (!fbinfo)
. K" \% Q) ^) W+ }        return -ENOMEM;7 f  ^# D# j2 a: G+ T! v

* p4 p/ K, O- D9 r7 o0 ~    platform_set_drvdata(pdev, fbinfo);    /*让platform_device->dev.driver_data指向struct fb_info*/* Y5 K- G0 X0 @, [/ T5 f$ \' \0 q
3 C+ P8 e% S0 @) k; F
    info = fbinfo->par;                    /*par指向s3c2410fb_info*/6 w; h; a1 @5 b: Y2 v
    info->dev = &pdev->dev;6 ~5 ?$ |8 X" c  u9 y0 R
    info->drv_type = drv_type;
( \5 z7 M# D3 P4 B( Q3 C
4 h' g( \# y+ Q# L8 Y0 w  b! f& D& [* t    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*获取平台资源*/5 a+ \% d% @+ n1 d& G' B
    if (res == NULL) {
: m) ?0 c* Z" d1 K        dev_err(&pdev->dev, "failed to get memory registers\n");9 O1 d) N2 R2 H/ j6 ^7 c% i
        ret = -ENXIO;
% X& Y' }  ~$ F3 i6 M' z        goto dealloc_fb;
: ?9 v: c+ E) v$ ~3 N! J. g    }
- c' M1 b2 n+ I, n$ o1 }0 B( |2 i  _
9 V& C5 r. E5 E2 B( ?    size = (res->end - res->start) + 1;                /*IO内存申请*/
/ k5 b4 W9 O7 A% D$ b0 m5 w    info->mem = request_mem_region(res->start, size, pdev->name);    " c# O  ?2 i. _' E9 Q5 w
    if (info->mem == NULL) {
6 @( o1 I' w# ~        dev_err(&pdev->dev, "failed to get memory region\n");0 G/ D+ I6 p* {  _
        ret = -ENOENT;
" h; A9 a3 `. g/ ^6 r* L        goto dealloc_fb;  T) ^2 x) ^3 w4 C4 Z  g
    }
8 t! M/ m! \4 P
5 [+ U. ^3 Y: _9 |# d    info->io = ioremap(res->start, size);            /*IO内存映射,获取lcd第一个寄存器的映射地址*/                & K  d6 D% u' {" H# x
    if (info->io == NULL) {5 L6 \* t% p5 f1 D
        dev_err(&pdev->dev, "ioremap() of registers failed\n");        , f- E, `/ M: p
        ret = -ENXIO;   
) M0 I1 ^, i1 A1 ~# K2 i        goto release_mem;# S9 V, U8 L( b, h
    }
' ]5 Q/ D% ~$ L$ ~' K                                            /*irq_base对应的物理地址是0X4D00 0054(寄存器LCDINTPND)*/2 c  A6 |; [  U8 I% N/ X
    info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
# O* O+ l5 a( j9 s: r( k9 M+ H6 O% M9 g3 E  c( }* R
    dprintk("devinit\n");4 u- q9 i9 L3 U

' ^3 Y* P) C+ \4 R# M$ A    strcpy(fbinfo->fix.id, driver_name);    /*复制名字*/: Z" F) q; f4 t* S, v

6 ?9 N# l4 j2 X" q/ E1 y    /* Stop the video */
! w5 R6 r7 N4 \% Y* V4 T9 J( d    lcdcon1 = readl(info->io + S3C2410_LCDCON1);4 U7 X9 i+ W: J
    writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1); /*禁止LCD*/) J1 M) Q  V* Z
* ~/ H  T# L8 ]4 v. @& `
    fbinfo->fix.type        = FB_TYPE_PACKED_PIXELS;
7 ]4 n  G( N1 _; c! y    fbinfo->fix.type_aux        = 0;2 P* B2 |; m4 n
    fbinfo->fix.xpanstep        = 0;
( M' G4 r# z: r+ f1 ^. g& m5 X    fbinfo->fix.ypanstep        = 0;
' _2 _8 e$ ^2 Z5 ^0 W  l9 m    fbinfo->fix.ywrapstep        = 0;
) Y4 o( R( w9 T: Y/ x2 _; F' I1 l- |    fbinfo->fix.accel        = FB_ACCEL_NONE;    /* no hardware accelerator    */
( @+ C2 n( r- J3 z/ n7 H+ r
% ]( s, V# Z7 E    fbinfo->var.nonstd        = 0;* j& f% y, z+ p
    fbinfo->var.activate        = FB_ACTIVATE_NOW;; y- o# H3 @8 ^6 e. s6 d, |* K
    fbinfo->var.accel_flags     = 0;& P6 t+ S% i; B
    fbinfo->var.vmode        = FB_VMODE_NONINTERLACED;
! I: A* R& M' k6 O1 F6 v- V! B" ?" U
    fbinfo->fbops            = &s3c2410fb_ops;" n" B1 p6 h. P4 t/ D: i
    fbinfo->flags            = FBINFO_FLAG_DEFAULT;
- Z) U' |, A8 a3 ]% `! A! _    fbinfo->pseudo_palette      = &info->pseudo_pal;
+ H) E# v" L8 |" E5 e/ M, ]1 v/ a2 V) n6 N9 m0 F
    for (i = 0; i < 256; i++)% z: n+ w/ e  S6 [
        info->palette_buffer = PALETTE_BUFF_CLEAR;
- |, O3 h* g3 J& x  ^
& s2 W& H; o% k3 I9 U    ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);    /*申请IRQ,快速中断*/
6 ]4 J4 e, V0 y3 N4 F2 Q9 U    if (ret) {
+ }+ G$ N5 z1 j9 X        dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);" T( B% z8 A9 k9 I6 h
        ret = -EBUSY;
* R. X9 a( d3 X) A! o! k1 s, T        goto release_regs;) B3 y( k/ n) {, ^8 S
    }
; a% m% P" t6 U- K2 w2 \0 b
8 L& G: u2 w! i2 k* J0 z    info->clk = clk_get(NULL, "lcd");        /*获取时钟信息*/! l3 t' S9 b& s) Z1 ?4 `
    if (!info->clk || IS_ERR(info->clk)) {
& h8 O5 W* [+ Y        printk(KERN_ERR "failed to get lcd clock source\n");
% n; @& }, s8 l4 w+ _        ret = -ENOENT;
& D* z8 R$ K8 N2 b5 j2 \        goto release_irq;
1 C& `% V$ l6 j3 n& @) E, Y; q9 c    }
. O" G+ ]. E# ^3 Q% z" R# j5 M$ X- l
    clk_enable(info->clk);                    /*使能时钟*/
; L) h6 j' l# V: n+ P1 D    dprintk("got and enabled clock\n");" f1 f7 j7 X  S* c# [5 |

7 Y& o, ?, b% m0 ]. k+ N9 ]6 d7 _' _    msleep(1);+ n- ]" `3 k7 _: D9 f8 R

8 F% m" R& z5 i( @  A( X, p6 l6 P. V    /* find maximum required memory size for display */
  `* _4 |$ W% O: h    /*在多个屏幕中,找出需要的最大memory*/
6 D* n$ W/ n/ W  f9 C    for (i = 0; i < mach_info->num_displays; i++) {8 f( N4 q3 g! @: G0 I
        unsigned long smem_len = mach_info->displays.xres;
5 p6 i* j; k$ G' Q        /*所需的memory空间 = xres * yres * bpp / 8*/
& c: G5 b8 a9 y; I        smem_len *= mach_info->displays.yres;& M9 C+ w, f' @- [+ j! ?4 E8 X
        smem_len *= mach_info->displays.bpp;
  l6 E! E# B! s$ g        smem_len >>= 3;
* O# k6 \. |* ?. `: e) x        if (fbinfo->fix.smem_len < smem_len)) [: p2 u: F/ T
            fbinfo->fix.smem_len = smem_len;
/ \  k. S0 Y2 {    }# b, [/ \; g% I

  o' U/ ^; m4 J: u% V; w6 O, T    /* Initialize video memory */    /*根据上面fix.smem_len的大小,获取DMA映射内存,一致性映射方式*/: l+ u2 U3 R1 \$ k  `
    ret = s3c2410fb_map_video_memory(fbinfo);# a0 E; J2 K7 w0 e1 r& H# I- r6 J" h
    if (ret) {. g- P2 P' e' @9 A8 h# z( e/ u5 z$ w
        printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);& w% t/ [  N+ W+ M" F
        ret = -ENOMEM;  K, g" d; Y5 q$ B6 n( S
        goto release_clock;% S- e8 b; O' g: p, ~" w
    }4 n% K( y* \  [5 p7 w. B2 w

9 F) o3 I; i- a# A, c7 w    dprintk("got video memory\n");' @. }9 g& [0 @! s" j3 ^7 C- O

9 M! P7 E0 K# u) c$ g. W" p    fbinfo->var.xres = display->xres;            /*320*/
: X, [6 X9 v7 B  C    fbinfo->var.yres = display->yres;            /*240*/   
0 C* \. L8 l; }4 i3 U+ C    fbinfo->var.bits_per_pixel = display->bpp;    /*16*/2 b0 r8 h/ L/ l# o  l
: t/ L3 |" m, T1 ~
    s3c2410fb_init_registers(fbinfo);            /*LCD寄存器初始化*/        ' t3 b+ Q' J5 Q7 _+ m) i+ X

, B6 f' r; b7 O/ ]; o0 b* L    s3c2410fb_check_var(&fbinfo->var, fbinfo);
: `' S; k- K4 I' v! S4 s
+ b; l% B, D/ d# A& g! n    ret = register_framebuffer(fbinfo);            /*注册framebuffer*/
6 [5 v1 u2 \, ~1 Z8 q. z    if (ret < 0) {
9 H, {: b, X9 ]* j        printk(KERN_ERR "Failed to register framebuffer device: %d\n",ret);" {' G: r8 C- S
        goto free_video_memory;; ~* C: }& p% b% ]
    }! h( V1 F6 ~( ]/ p7 @$ Q

& X+ D% U  Y5 b7 d    /* create device files */" y; ^$ h$ t8 C: M. o0 ^/ W. F4 {
    ret = device_create_file(&pdev->dev, &dev_attr_debug); /*添加设备属性*/
" ~8 g: f/ Z0 \  b$ D. \    if (ret) {
( E4 H) w$ v( F- P- R        printk(KERN_ERR "failed to add debug attribute\n");& J0 `+ N# M" u3 J9 [
    }( _) c+ n2 \( w6 m2 P( m

9 V0 ~4 F; s; f. M  i+ N    printk(KERN_INFO "fb%d: %s frame buffer device\n",) ]  c6 [+ P/ H& M; b3 T" V! z' Q
        fbinfo->node, fbinfo->fix.id);' l! w0 j, K' k. y. ]& Y
& \) ?5 R0 M5 l+ E7 b
    return 0;& _- }; ^: X0 s- |: F& P
/*一旦某个步骤发生错误,以注册的相反顺序开始注销*/
1 \9 E6 w. _' a! Z4 @free_video_memory:
/ U7 o% E' Z) i, Q0 n    s3c2410fb_unmap_video_memory(fbinfo);
0 P0 `4 M; Y, _release_clock:; T4 c3 i( m. t9 b9 R' K* |6 `
    clk_disable(info->clk);
6 a# f- Q) F+ g& `' J9 V( S  Y2 P    clk_put(info->clk);
6 U7 m* Z0 U3 Arelease_irq:% K* B% E6 B  v. ?* c
    free_irq(irq, info);5 C# x# U% t% l4 S& _
release_regs:  I' F& J3 l; D7 {' `
    iounmap(info->io);
3 w: t; ^& N4 O+ t3 i3 p& @2 vrelease_mem:7 C/ u) X  V* E4 ]7 x
    release_resource(info->mem);& a; W1 p/ o7 D3 }
    kfree(info->mem);2 [  C3 C( R% K) j% w9 \
dealloc_fb:
. i# H/ \. x3 E6 A9 K: d    platform_set_drvdata(pdev, NULL);
( k% s/ d, u, M. L  `3 m    framebuffer_release(fbinfo);   
$ D' @( j6 l6 Q3 \. v0 {" A    return ret;
. y/ t% z8 o9 i5 p% a}
1 ]; x  z" f# ?- L2 ]; F: i2 l/ [6 s* i: s
这里使用了三个新的数据结构。s3c2410fb_info是驱动程序使用的,里面将保存所有驱动程序所要使用的资源等。而s3c2410fb_display和s3c2410fb_mach_info,是由板级信息,通过platform总线添加到内核中。
4 z3 p# Z' O( A4 t9 ]$ Y; `+ k% |0 \5 W  X4 ]6 D
s3c2410fb_display中的成员将被复制到fb_var_screeninfo结构中。6 D4 Q" D; m1 E

' _& {" I- z3 W5 S: @该板级信息的定义在arch/ARM/mach-s3c2440/mach-smdk2440.c中,来看下
3 A0 `  O% J4 ?0 `# {8 k" Z; }3 Q/ H$ [  ^9 j
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {# e& a& f8 L" p+ Z9 Z
: {+ N3 a4 W7 f, I
        .lcdcon5        = S3C2410_LCDCON5_FRM565 |; u2 s, ]5 s/ |" I
                          S3C2410_LCDCON5_INVVLINE |; G- v9 X! H( U0 B4 p
                          S3C2410_LCDCON5_INVVFRAME |
' F+ A  Q. D' f3 `& |                          S3C2410_LCDCON5_PWREN |: `% N0 b2 |. L+ ]8 `+ ~1 ^) d% S
                          S3C2410_LCDCON5_HWSWP,
  R9 Y7 s0 c4 H4 }* F+ y
5 M+ i( {: ^. {        .type                = S3C2410_LCDCON1_TFT,
2 p/ k& ]7 D- r* u9 `
& `9 Z/ P3 K% X/ W9 P0 M9 T$ w        .width                = 320,//240,
0 ?0 n& z0 @, Y8 n* X* F! G        .height                = 240,//320,4 O& _) C: ~7 P1 Q# N$ f9 W( ~3 x
  B* s- g, O  H% x! t
        .pixclock        = 149000,//166667, /* HCLK 60 MHz, divisor 10 */
2 e2 p: S" l& H5 R/ {! W8 O        .xres                = 320,//240,
& L' g+ l" j4 U: p' ]        .yres                = 240,//320,8 x& D2 v% u( E
        .bpp                = 16,
0 [0 d9 k, G3 a6 {$ o        .left_margin        = 20,- b+ G8 y7 p7 v4 R' {
        .right_margin        = 38,//8,
. \3 }& y" ]2 t        .hsync_len        = 30,//4,+ A3 J- J- ^0 q
        .upper_margin        = 15,//8,2 K2 K  O4 b. v" ?6 Z& p9 L, b/ b% z
        .lower_margin        = 12,//7,+ _; [7 b7 ~( _
        .vsync_len        = 3,//4,8 q) G* J- n5 X
};+ B1 \  |+ _. _

; J2 E2 {5 x8 k  q7 Zstatic struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
) X2 ?( N5 B3 S        .displays        = &smdk2440_lcd_cfg,$ \2 s- I. \, z  X( v
        .num_displays        = 1,
+ f+ R7 i+ @2 E1 Z5 C0 _4 k! f        .default_display = 0,
9 Q- t+ O4 o5 f6 R
# |( b5 b+ r& q. A( W- n6 }#if 0
8 q! d$ j9 |* A. d        /* currently setup by downloader */9 B: U- w2 s* S. E) k6 u
        .gpccon                = 0xaa940659,
5 n& Z2 Y* w: s0 q0 g# @' d        .gpccon_mask        = 0xffffffff,
2 z4 X9 v* f8 \' ?- D% H6 d4 ]        .gpcup                = 0x0000ffff,7 n2 z) ?* n* m& d' \% H" B# k
        .gpcup_mask        = 0xffffffff,
2 p8 n1 D2 h4 R        .gpdcon                = 0xaa84aaa0,
" `" }  y' N3 J" H; E        .gpdcon_mask        = 0xffffffff,$ Y( \& T3 J7 w* B* r8 S5 a
        .gpdup                = 0x0000faff,1 ~! A- R6 H* l) e: M
        .gpdup_mask        = 0xffffffff,
5 u" Z5 W7 A. K  `: J& N' P#endif" z% s5 l' J; `& C) X9 X
//no
8 d; o& D# O* E/ q//        .lpcsel                = ((0xCE6) & ~7) | 1<<4,2 S1 x# w6 T5 U0 q& @  c
};
6 \; Y9 x0 W8 k, q! Y) ?
, I) {& Z% H# |1 m这里NOTE:每个LCD屏幕的参数不一样,因此上面的参数会有所不同,这是需要移植的地方。
- X' q6 Q4 c2 Y/ d& `! U( @
. @# w6 U% @% Q6 ?+ E随后,我们看下在probe方法中调用的几个函数。首先是s3c2410fb_map_video_memory。3 l( L* `8 M3 M: [4 e3 V

+ O2 v4 {- R: v) B- {1 z/*
1 a0 D5 I7 H5 E7 S( A4 J3 V * s3c2410fb_map_video_memory():
5 k# D3 A; o- B *        Allocates the DRAM memory for the frame buffer.  This buffer is
' f( }; F4 ^/ o *        remapped into a non-cached, non-buffered, memory region to
! W2 H# ^" z% ^1 N8 C8 ` *        allow palette and pixel writes to occur without flushing the9 C5 ^  t- b. S  h' K9 `( L
*        cache.  Once this area is remapped, all virtual memory0 `' @! |* y0 S3 J+ R; E
*        access to the video memory should occur at the new region.
8 A  T* ^- b9 Y: D8 X */, O  N/ ^# r% \6 m  `, v
static int __init s3c2410fb_map_video_memory(struct fb_info *info)
1 ^- O  {2 G7 ^( F7 k. C) j1 @{9 l3 f- C) O) M% q
        struct s3c2410fb_info *fbi = info->par;( J9 ?/ B8 m: }! u
        dma_addr_t map_dma;, n- J+ F* G! F% s$ Y
        unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
; U; l: q) X4 T" _$ R* }1 c) z( m; K
        dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
6 M) P5 J( c/ }' I2 o                /*分配DMA缓冲区,并保存DMA缓冲区虚拟地址*/
6 D! i, M- ]9 q. \! e7 u6 N" C, H        info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,/ R( [! _) w' }* d1 |
                                                   &map_dma, GFP_KERNEL);
; N) o  H' p& |6 q+ r2 v2 n$ }5 B7 `% v: e
        if (info->screen_base) {
+ a$ R9 p. g% H5 h7 y                /* prevent initial garbage on screen */5 T7 I4 O& w/ J2 s! y5 p2 Z) V) U
                dprintk("map_video_memory: clear %p:%08x\n",
( ?3 |: G. ^6 ~7 g7 V7 j                        info->screen_base, map_size);
7 N4 a" S& S( ]! J5 |2 s                memset(info->screen_base, 0x00, map_size);        /*DMA缓冲区清0*/
; z. Q5 y3 V) Q% a1 I( l# f
+ O, v; _2 a. Y/ j% o                info->fix.smem_start = map_dma;        /*保存DMA缓冲区物理地址*/
' w: Y8 }+ j' N
$ F& P* v2 ?- Y( y5 W$ }4 ~$ p+ D5 v, L                dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",' _9 C1 L9 p/ u* J
                        info->fix.smem_start, info->screen_base, map_size);
5 w3 V: B* a8 x9 p; X' c& g' W        }+ K3 z8 }; \' Y) ?
5 T) \# y/ ?+ y5 P
        return info->screen_base ? 0 : -ENOMEM;
$ C5 G* {7 O% s. G7 {}
' A; |! I, A) m- N& k, m2 q! _8 ^; I, U. r7 v9 s+ x

0 f9 `$ r, p9 c该函数根据fix.smem_len的大小,分配了一个DMA缓冲区,保存了该缓冲区的物理地址和虚拟地址。0 s1 o) L! t! o: F+ u0 s& {
  ^1 C8 e$ M, @8 l3 B
接着是s3c2410fb_init_registers:: e) P% b; W8 p5 Z' }

! }6 j% f) l7 L4 \7 A. z# M9 y" G& Q7 l, K
/*# @/ v% l6 ?6 @  g& T2 |
* s3c2410fb_init_registers - Initialise all LCD-related registers
- c+ F, p2 Q+ A9 w4 @ */
9 b+ d  u0 g2 Tstatic int s3c2410fb_init_registers(struct fb_info *info)
( A' _  u( I- D" }{
: Q8 l2 y2 N% Q1 P: L    struct s3c2410fb_info *fbi = info->par;        /*par指向s3c2410fb_info*/) P4 p2 I5 a) R; B9 N+ I7 Q
    struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
$ W7 y1 O3 c! p    unsigned long flags;  `, Y* H, \% Y0 ~
    void __iomem *regs = fbi->io;. f0 ^7 X, y$ ~3 H! W  z1 }
    void __iomem *tpal;
+ _9 l4 {, \0 i' [& a/ x    void __iomem *lpcsel;3 I% d+ ^" _  D) \; U2 B7 b' G
* C4 Y' R9 J# i, ?& {1 @4 O
    if (is_s3c2412(fbi)) {
# V1 n  v# P0 C        tpal = regs + S3C2412_TPAL;
4 C# ~( Z/ M8 C% X" y        lpcsel = regs + S3C2412_TCONSEL;6 c, s# S* d: l4 ?; k7 Y7 j( w
    } else {
$ M9 B$ ~6 R3 l7 H1 Y        tpal = regs + S3C2410_TPAL;- r8 Y' ^$ }; b& ]* c2 s
        lpcsel = regs + S3C2410_LPCSEL;6 L- G3 H# c% V% T
    }
, o7 [4 \2 M; ~( C, r! n
+ v3 l' k1 |8 I5 X4 h    /* Initialise LCD with values from haret */# |2 d' H5 ~7 ^

" _: i! h* ^+ e* M* }    local_irq_save(flags);                        /*禁止所有中断*/( w) ]3 v/ Z0 A' Y+ B; o" E( p

2 ?4 z4 ?- @% Z. @4 |, _7 d    /* modify the gpio(s) with interrupts set (bjd) */    /*初始化io管脚*/; x- }. t- o/ R
- t4 I8 `8 y3 ?$ \
    modify_gpio(S3C2410_GPCUP,  mach_info->gpcup,  mach_info->gpcup_mask);9 J8 Z0 Q6 @) ?8 j
    modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
# x7 F+ R4 a) k( f! M    modify_gpio(S3C2410_GPDUP,  mach_info->gpdup,  mach_info->gpdup_mask);
8 d3 j; \, p3 ~( u: Z1 P    modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);" ?" @' [6 Q! S# d  |
4 H8 F! n: B7 d  j& g% L& L
    local_irq_restore(flags);                    /*恢复中断*/+ e' r! |* `3 v' `; Y5 Q

. I( `/ @/ o1 ^$ [* C- o) X" B& p8 h  w    dprintk("LPCSEL    = 0x%08lx\n", mach_info->lpcsel);    /*设置TCONSEL,禁止LPC3600*/" g* \) M# ?& n& l
    writel(mach_info->lpcsel, lpcsel);
# f% S# f( N) P/ B3 }+ G% E/ h$ k1 n6 \4 p3 j$ [0 N
    dprintk("replacing TPAL %08x\n", readl(tpal));% g- V* `1 ]6 K5 c/ W$ R; A+ |) O* `

, ~7 N. Q6 N* P5 d4 S0 n    /* ensure temporary palette disabled */  n9 m; H! }" a  V: {1 t5 ~+ s
    writel(0x00, tpal);    /*禁止调色板*/
4 ]  _4 ]  u( S3 E# a. H/ B( D; _
4 ^7 e$ a% c  S    return 0;
$ T( N! y& C* @& [4 _' K2 t/ c}
  V( X! R5 b2 L$ q" I- `. G5 M4 d; D& J  Z. N" R+ u
static inline void modify_gpio(void __iomem *reg,; x7 m8 v& A6 Z; P  a1 ^8 W" v  e
                   unsigned long set, unsigned long mask), x* _+ t' Z# S3 T; m$ \; U
{; q6 _. l* }7 A" {
    unsigned long tmp;6 a) {* H8 w  j; z- c$ b
: R# Q, o' ^  L* T
    tmp = readl(reg) & ~mask;" C& W+ z# E' F3 J) h! y6 s
    writel(tmp | set, reg);
1 g. |& M! |9 w3 u2 g}
& {' k" j; S' I: T最后是s3c2410fb_check_var:# H. S, s6 Y- g  |7 U
/ H" E+ M/ Z3 J% ^) g* o7 `
/*' x# f; J& W& N( W; B
*        s3c2410fb_check_var():
; B, M, q! f1 w: A0 y *        Get the video params out of 'var'. If a value doesn't fit, round it up,
+ [$ r& S5 q1 p1 X; s; J *        if it's too big, return -EINVAL.3 k% e. T, f- A& Z
*        检查变量的合法性
4 ?; ]; Q  b% D. E$ J */
8 ~- j4 H$ g% o( V1 h( U. Nstatic int s3c2410fb_check_var(struct fb_var_screeninfo *var,
" r; W6 a& F, K5 f( d$ c# R" ^                               struct fb_info *info)+ e) }& O6 m) V
{
; K0 m. v+ v8 ?/ H. \4 B! h        struct s3c2410fb_info *fbi = info->par;                        /*par指向s3c2410fb_info*/
8 R! {$ K( k+ v9 a0 g        struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;/*指向s3c2410fb_mach_info*/
0 ^$ C  Z2 M) A        struct s3c2410fb_display *display = NULL;7 t+ t7 b$ Z3 @: K' N; W
        struct s3c2410fb_display *default_display = mach_info->displays +1 Q, R  v- E6 _1 \& }2 h. D1 M. M
                                                    mach_info->default_display;/ K1 T, g! a6 Z) o! T3 q- v
        int type = default_display->type;         /*S3C2410_LCDCON1_TFT*/
! N  }7 L% l9 Z        unsigned i;- |+ O- _8 \* H6 }# Y/ x0 D

4 u0 G( `! ~9 h  h0 h        dprintk("check_var(var=%p, info=%p)\n", var, info);. O$ t* w9 }2 M; j0 @: c% M  w

# T' u& X8 d5 M3 z: c. y5 P        /* validate x/y resolution */# z. {" u; ]- h0 d4 }- G
        /* choose default mode if possible */                        /*var中的成员在probe中设置*/
2 U8 a6 Q; q" x! z4 r        if (var->yres == default_display->yres &&
  _5 k/ P7 d1 o; g2 d) g            var->xres == default_display->xres &&2 s( u& |1 v  U- l9 q
            var->bits_per_pixel == default_display->bpp)7 s: _, u( P7 g% ?2 x
                display = default_display;
) y. k1 d: v$ g: q8 `        else
1 Y% K+ v- o5 f, l5 c4 v                for (i = 0; i < mach_info->num_displays; i++)5 d8 o2 ^# D. q! R* O# ~4 f3 S" i
                        if (type == mach_info->displays.type &&9 s; ~- g0 V# Q1 q0 E
                            var->yres == mach_info->displays.yres &&# G7 @% Z! w7 t- P
                            var->xres == mach_info->displays.xres &&
$ X6 S5 f) c  L7 ?0 S2 ?. x                            var->bits_per_pixel == mach_info->displays.bpp) {
( z4 ~& A* K7 d- e                                display = mach_info->displays + i;; m2 M* P" M) _. Q* f
                                break;9 Z. B' @! S8 l
                        }; D4 ^4 P3 F- G  r! k* O
7 J, S7 J! ^) {1 K/ s/ e/ I/ ^+ m
        if (!display) {
; `# q' H) h6 Y7 Q. N0 Y" {                dprintk("wrong resolution or depth %dx%d at %d bpp\n",5 U, J7 P3 k/ ^/ `# A
                        var->xres, var->yres, var->bits_per_pixel);
( z) w: j+ H" i; y6 f# ^                return -EINVAL;' ]( w% t2 n% w: k
        }/ o* n$ F" i+ y) |7 l
2 t% U% \' I3 X$ p
        /* it is always the size as the display */9 V# m% H8 i* K3 u9 q
        var->xres_virtual = display->xres;
+ }! s: @% f3 L+ k7 z1 ~        var->yres_virtual = display->yres;! l  ]2 t( m( M$ S1 a
        var->height = display->height;
  c) o3 O1 U, T: _/ C% q) H        var->width = display->width;: B, G  v5 p% b7 E$ L8 o0 M1 A0 `

, ?) {2 b* |' t- {8 Y' ]. p        /* copy lcd settings */) J0 P5 D% O& Z+ Q- P% k: s
        var->pixclock = display->pixclock;
0 u3 {% J4 L- L, ?; G% ^        var->left_margin = display->left_margin;7 G2 f; E- l3 C8 c- ^9 l. N& q! s: `
        var->right_margin = display->right_margin;
  P/ e# o; k1 `2 M" R        var->upper_margin = display->upper_margin;
$ g3 }5 c+ G4 E1 |        var->lower_margin = display->lower_margin;
: z4 S4 a* M8 \, x+ t        var->vsync_len = display->vsync_len;
* ]( ], W' R) D8 b! f# m        var->hsync_len = display->hsync_len;0 I1 L2 E9 z0 W: I6 g9 {+ S
- H, l1 X- s: H1 A3 F4 \
        fbi->regs.lcdcon5 = display->lcdcon5;6 t4 ~8 C% _4 [( f2 P! u: w
        /* set display type */
: \4 z5 ~/ w. u! O  q        fbi->regs.lcdcon1 = display->type;5 t* a! b" H7 p: A9 o# Y8 \- f' [. ~
  m; S, D+ C) o( ^. R2 }2 Y1 [
        var->transp.offset = 0;
7 y3 e/ A; E; |2 c        var->transp.length = 0;
- p( {" P; i, B) Z4 `        /* set r/g/b positions */3 f* Y; O4 H: d0 ^% I
        switch (var->bits_per_pixel) {
7 ?% i- ~5 t+ q6 ^2 @& V; @  Y        case 1:
5 _1 b" `; Z: C$ F0 \        case 2:
; G, b3 r, Q5 z# e' l        case 4:
/ U5 O' y- v( U! t5 s1 K                var->red.offset        = 0;2 |) V3 d2 ^+ g. r; t; [; f. ~* X0 O( R
                var->red.length        = var->bits_per_pixel;" |0 P& f$ N- G" [  p
                var->green        = var->red;3 I8 o: R6 E# q0 \6 V
                var->blue        = var->red;4 x3 U7 }/ q, d: g  v( I+ C
                break;
. {( ?) `9 G3 {        case 8:* r2 r0 D; B0 R" h' z; e
                if (display->type != S3C2410_LCDCON1_TFT) {
  Q, P, T" ~2 a                        /* 8 bpp 332 */) f4 _; Z( l8 r4 O) `. u
                        var->red.length                = 3;
" Z* [* j4 |) p2 ~) O; b7 K                        var->red.offset                = 5;" _7 E/ Z5 C' j! y, @! z
                        var->green.length        = 3;% E1 H4 j& {2 \9 X
                        var->green.offset        = 2;& e" b7 |6 L. N: l  Y" b0 k( y
                        var->blue.length        = 2;7 i1 ?) x) W2 k: U
                        var->blue.offset        = 0;) K. n: C9 O  ]* I$ _) v1 C2 \2 g( |! i0 U
                } else {# b4 A) W5 z' @* K9 M) e
                        var->red.offset                = 0;
  s+ L5 K5 Y: y3 [: e- V! n                        var->red.length                = 8;
" H7 i  A% S5 K. U1 q4 J9 Y" r                        var->green                = var->red;" A% J0 W2 A' a* N
                        var->blue                = var->red;" V( _& y( }# y) {& J8 s/ r  t$ M
                }7 d; R" A" }! V6 E# h# c
                break;0 r; T2 h5 }1 L; q0 C6 S
        case 12:
" i6 H+ u' e: b* q4 d                /* 12 bpp 444 */
; `, }4 j. {, D; d% O0 Z1 h, B                var->red.length                = 4;2 M% e! [' f  W- u9 y$ V
                var->red.offset                = 8;
2 H, R& E8 d$ |- C                var->green.length        = 4;
$ s9 q, L: s& l9 E4 f                var->green.offset        = 4;
! F- U8 {6 B7 s8 |' \% T                var->blue.length        = 4;
% b  R3 V0 p3 b: ?0 G" n                var->blue.offset        = 0;
# ?0 x/ a7 I- |3 g" F6 B# ?                break;
+ E% \" q7 o( g; ~; B) P( k
# Z8 D2 k  l  Y! x        default:& B$ n$ T; N  U' M6 {" t- Y3 s$ T
        case 16:
6 w' N$ y2 D7 T, x' J                if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) { /*使用565格式*/' m* d. Z# f9 C) o8 Y
                        /* 16 bpp, 565 format */
7 t7 E$ C) {: G9 l9 a( x0 o+ K                        var->red.offset                = 11;
  `7 `+ Y. k, b( R                        var->green.offset        = 5;
- A. s9 d* J7 S9 y: h1 z' U                        var->blue.offset        = 0;% C# g  s" V9 F
                        var->red.length                = 5;
4 f) F4 j+ \$ V                        var->green.length        = 6;" q7 \, O; v" g4 u/ i
                        var->blue.length        = 5;0 o2 @( X; E1 i+ b
                } else {
6 [) u0 I4 b% ?1 B( W. Q$ u                        /* 16 bpp, 5551 format */0 Y9 S: w0 n1 k
                        var->red.offset                = 11;
1 }5 y. d1 l( o+ X& I2 m                        var->green.offset        = 6;
' [+ o$ X" P7 A% P                        var->blue.offset        = 1;, o: d* E' N2 D" F
                        var->red.length                = 5;
4 R/ e% _8 E' p) p; C- @9 V                        var->green.length        = 5;, u' k2 U+ x7 m5 u9 S. W$ T
                        var->blue.length        = 5;
9 |6 y. d8 I( E$ i- [; i                }( ^7 u2 R! k: c" ~# ^
                break;- R+ s+ t. c3 S6 p# p- m
        case 32:2 i7 C9 t  T: _1 K0 `* {
                /* 24 bpp 888 and 8 dummy */- \5 T8 O! p  W# b! U9 h+ A
                var->red.length                = 8;/ l; n) ]' l0 Y- p) C4 v9 o
                var->red.offset                = 16;
' k. r5 G5 }* y! j! _" F                var->green.length        = 8;
4 Q$ k& @3 ]* L                var->green.offset        = 8;2 `1 B" X0 E7 a: {* {4 F9 x
                var->blue.length        = 8;6 @7 w- a+ I' x, [! J/ l1 \
                var->blue.offset        = 0;
0 N  L- m2 H0 A" T3 I) B- w/ v, N                break;7 z! \" e1 t. v/ E
        }
6 `4 l% D; H, \7 c- F  z. P+ J        return 0;' p8 i8 @/ W: p6 u/ t' V
}' A+ b) {' X' n2 [" Q- X

, W1 U$ f- `, c2 U' a/* Interpretation of offset for color fields: All offsets are from the right,9 e" J8 o( l3 D
* inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you; B0 {& G  m- b
* can use the offset as right argument to <<). A pixel afterwards is a bit; }& m5 j( [$ b+ W! O
* stream and is written to video memory as that unmodified.9 F0 k, Y; o" ^" s  s
*
2 |6 S6 u8 {8 B% c) w/ [3 e8 g * For pseudocolor: offset and length should be the same for all color# d+ u, n  f: `- B" J! [4 J
* components. Offset specifies the position of the least significant bit
1 b3 k- |) r4 ^3 t% t. _% Q * of the pallette index in a pixel value. Length indicates the number
2 K( V. b& N( c6 k5 k * of available palette entries (i.e. # of entries = 1 << length).
, V2 j1 {+ S- e */! `$ d6 i1 X" S5 v0 E/ Y" @, @
struct fb_bitfield {/ f0 _) I5 M5 x5 X% x5 A2 a
    __u32 offset;            /* beginning of bitfield    */
4 J$ v: S6 `5 `" _5 ~4 y    __u32 length;            /* length of bitfield        */
7 C' I# z' \& j4 L$ l. g    __u32 msb_right;        /* != 0 : Most significant bit is */ % B5 f9 c9 B: e4 ^2 e  l% h
                    /* right */ % T$ V# J( O+ `7 O, {+ u1 l2 b1 @
};6 w+ v* T! Q# o" P
该函数主要将板级信息s3c2410fb_display复制到对应的地方,然后根据RGB的模式设置位域。
% R0 t9 B0 n4 }5 S+ l! ?5 k  T0 [4.3 fb_ops方法# a8 l# X, S, k; ?
在驱动程序中,定义了fb_ops,如下:4 ?8 t* W; R' \- n0 z/ r% q. H, v
) t2 p- z. @$ S. k. G% K1 H1 x
static struct fb_ops s3c2410fb_ops = {, q/ \3 D" {1 o, @- r
        .owner                = THIS_MODULE,$ j& W1 I) K, q6 v+ q  k
        .fb_check_var        = s3c2410fb_check_var,                /*检查变量的合法性*/8 r# c+ O) o) l' _  j4 j* _
        .fb_set_par        = s3c2410fb_set_par,                        /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/+ ^/ I1 k3 [% I9 }5 @) I
        .fb_blank        = s3c2410fb_blank,                                /*该方法支持显示消隐和去消隐*/
, l& c9 @" n6 h3 E; y; A        .fb_setcolreg        = s3c2410fb_setcolreg,                /*设置颜色寄存器*/
5 |& |9 _1 X* J! e2 G/ z2 M        .fb_fillrect        = cfb_fillrect,                                /*用像素行填充矩形框,通用库函数*/$ K9 c7 J+ f6 x+ C
        .fb_copyarea        = cfb_copyarea,                                /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/
' a8 [; `4 z. {  D/ _        .fb_imageblit        = cfb_imageblit,                        /*显示一副图像,通用库函数*/, c8 n  y9 u2 N( n
};
+ {5 x, e; _# b0 @5 N: x
2 k6 j& I5 _7 J其中s3c2410fb_check_var在4.2节中已经分析过了,最后三个方法是通用库函数,在这里不作分析。
/ }" [! a. c* V4 y剩余三个也是驱动程序提供的,现在对这三个程序进行分析。2 F5 C& Z! Y: R+ Y  A
4.3. 1 s3c2410fb_set_par
/ u) b& H& `2 T6 h+ P, u/*& c, j5 t: w9 Q7 |  T2 L
*      s3c2410fb_set_par - Alters the hardware state.
# N- h# ?8 k6 H( S* G3 h *      @info: frame buffer structure that represents a single frame buffer- J3 B! ]2 K8 _: o: S
*                根据var中的值设置LCD控制器的寄存器* y; s$ H* X+ J: D9 |5 Q4 ^# h
*/
- f/ P" [6 l, Y) T4 p# k  ustatic int s3c2410fb_set_par(struct fb_info *info)+ V/ d1 B% w0 q5 I
{
3 |+ x% t( @# S- U* z0 G        struct fb_var_screeninfo *var = &info->var;
8 L) F) f, C$ P3 [, n
' R) m- Z6 A' S6 X- [        switch (var->bits_per_pixel) {) }" f- e8 e" t1 P! P4 p
        case 32:
2 P. }1 @0 T3 |/ s        case 16:9 p9 ~; I& k7 w& [( H
        case 12:
6 |, _- p8 P- i                info->fix.visual = FB_VISUAL_TRUECOLOR;) G3 g7 q, `# C4 H5 Z
                break;4 m0 Q; B/ R; _' m( Y: j, O
        case 1:1 y, Q4 t2 i  C( ^+ V" \
                info->fix.visual = FB_VISUAL_MONO01;
, I$ `& L; o1 p$ q                break;) m: U/ o# h4 W
        default:
" b3 o3 y4 `% ]4 }" Z. k: h: G3 n                info->fix.visual = FB_VISUAL_PSEUDOCOLOR;% u- ~( r( s' I( \8 F4 }
                break;
) b8 T3 c; Z. W; K1 V  x1 X        }: J  E' w( l: _& e' l5 h
. Y- d% a3 A/ k2 p! V
        info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; /* 320*16/8 = 640Bytes */! f& e, _6 `- L5 y# x8 N- F7 O

2 U, j+ @+ d. I2 V8 _  U8 n        /* activate this new configuration */- Z4 R  y- ]0 s/ \  S3 _9 f; K

; k- o3 ~. O( b& L( N        s3c2410fb_activate_var(info);5 Z) h% {. e' ?" @' F% u; ?& V
        return 0;
6 j% a1 C: `0 x  e" G: `}3 T) ?9 I2 I8 y6 \6 M; I
该函数根据像素的位数设置了视觉模式,本例为16为,使用真彩色。然后计算了每行的数据元素大小。共240行。
5 u) C0 b  U: ]/ |" J2 E+ S" L$ N: a9 r" v% D
然后调用了s3c2410fb_activate_var来设置控制器并激活LCD。
' Z* j2 L6 F- Q( T& ~# M; X& K- ^
) F; G; u6 W1 w5 F& Q# ^s3c2410fb_activate_var函数如下:& D+ `) M2 [* m+ B
+ L! A% l( D0 W: g
/* s3c2410fb_activate_var
6 X) G/ h8 Z, `% B. v- H *, [7 m1 _1 L, R5 w$ i5 Q; m0 Y6 l
* activate (set) the controller from the given framebuffer
2 T" q6 T, U3 |' x& W& j, h * information6 C# O) L2 _, q1 X
*/
- d" f7 R4 _0 H3 ^static void s3c2410fb_activate_var(struct fb_info *info)
9 H" J2 ?( q  L) ^- F{
  Y+ n. ?, p% W        struct s3c2410fb_info *fbi = info->par;. A8 q. L5 ?) [/ ?0 U' D" @
        void __iomem *regs = fbi->io;& u) v  C# v& G
        int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/% v. G, N9 I3 _( A
        struct fb_var_screeninfo *var = &info->var;1 x' l% w6 @7 x+ X' w/ u! l: F
        int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
& F+ E8 |1 C8 S: g$ A+ b7 n! S2 F4 F/ Z& ]/ A  d
        dprintk("%s: var->xres  = %d\n", __func__, var->xres);
1 \0 U0 e* T& u- Z' k9 T3 C        dprintk("%s: var->yres  = %d\n", __func__, var->yres);3 a8 L& B( V1 G0 {( M' N
        dprintk("%s: var->bpp   = %d\n", __func__, var->bits_per_pixel);
0 G5 V: Z% W* L& y1 g: V( v
1 P0 {) ?# T) X- w& u1 ]/ X        if (type == S3C2410_LCDCON1_TFT) {4 N# J( N/ W# S) G5 A# @
                s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/
3 {  |' R3 m6 a                --clkdiv;. p4 {6 }1 W# |- o
                if (clkdiv < 0)
- q% ^! g# s/ R                        clkdiv = 0;
' G# K4 R4 y$ a+ p+ m. O        } else {& P, W" H0 X/ u* f2 R$ f
                s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
/ c. s7 p4 \3 D  u                if (clkdiv < 2)! P( O! D# [. b5 e
                        clkdiv = 2;
2 n% X" y) w& v3 T        }
" h8 f1 v/ V8 w9 J, d+ q' S. g, h/ C& ~; a" Q. e7 Z4 h9 L2 u
        fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/
. w: u6 G8 `) G& K
0 O6 \* J: _" [. P6 T        /* write new registers */: [; K) h, X6 j* U* K

' e# |: q6 V0 H; J8 y9 ?) D        dprintk("new register set:\n");7 O# D# c  P  c8 `1 x' f; g) y5 u
        dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);& n( ^% I% J9 K8 X
        dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
! f+ }1 G( @/ g: Y1 i0 e! c        dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);8 Z/ z1 {4 w; o/ y  C
        dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
' l$ E, B4 p6 H, C. \6 m) _        dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
, A: ]. e3 z+ U- b, O5 r6 k        /*把计算好的值填入LCD控制器中*/6 K0 v( K; L. e4 @& n8 D
        writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,        / w2 K2 R8 Y6 r  f; f# Z
                regs + S3C2410_LCDCON1);                                                /*仍然禁止LCD*/
& g8 W" @# A9 f- j* Y        writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);" M( k: d' t) i+ @& j% w* _$ U' `
        writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
0 b" b- X8 N9 k7 g: u+ @& z        writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);: W$ Y! U+ {  P: N
        writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);# C" V2 y- X, e- m  l$ b& f

9 C0 Y/ Z" u+ H- ~# l. o        /* set lcd address pointers */
; ^7 t' w6 G- n6 Z. C( r9 P1 P        s3c2410fb_set_lcdaddr(info);                                                /*设置LCD帧缓冲起始地址*/% ~: ~" l: x) M5 d
' y) f& y" x: {8 w; H
        fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,4 D! W& ?4 n9 n
        writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);        /*使能LCD*/+ Y: W- j( l: p0 O1 a9 v
}
. _$ n, F  r+ p1 x其中调用的三个函数如下:; s5 v7 Y7 [6 u
static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,' H3 t7 M! C/ y
                      unsigned long pixclk)$ b+ D" c9 q. T% O0 P
{1 x' T% o0 f( V" f- D) O6 V
    unsigned long clk = clk_get_rate(fbi->clk);         /*获取当前时钟频率(Hz)*/! ?9 Q6 }% D  e: l* R
    unsigned long long div;! X8 q( I( O8 q7 P# H& k
" @" M7 q  [, |: h0 R
    /* pixclk is in picoseconds, our clock is in Hz' o' ?' T5 H1 X2 i* y/ v* f
     *7 H! F" S: u3 m2 h# ?
     * Hz -> picoseconds is / 10^-12+ V" N4 e& y- f4 H& s
     */
2 w; m, `1 g  w+ ?( q
( U# o$ M, x4 T% P% _( [' i' B    div = (unsigned long long)clk * pixclk;7 O0 W9 }2 f* J9 E
    div >>= 12;            /* div / 2^12 */6 L- y* c3 }+ U6 b; j7 [- o
    do_div(div, 625 * 625UL * 625); /* div / 5^12 */9 x* O# P; l$ d
$ u! z' h1 Q3 U" b4 k, ?
    dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
$ Y7 h  B5 @; E2 m- ~    return div;
% F  k6 g$ `1 E7 K" h9 y}
0 c: P  R! }8 ^& i
, g0 U2 y8 O& ^6 v2 K3 O. b/* s3c2410fb_calculate_tft_lcd_regs
6 |' s6 a/ P  B3 T( ~ *+ q4 s! S1 X, x
* calculate register values from var settings
4 g) F0 B3 w3 b2 z1 w- c( n8 V/ n */
  T' J% W# I- S$ [& ]static void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info,
! H, k9 @' s, R, M# N& x; B# P                         struct s3c2410fb_hw *regs)
( J9 \! l. `+ a1 u& l' t+ h{
+ w8 v; V3 l0 ~. Z- Y1 c    const struct s3c2410fb_info *fbi = info->par;
9 }+ r- f. L2 @- J    const struct fb_var_screeninfo *var = &info->var;
! \0 }: Q1 [) }' G/ S
: g, x9 F. l( n% n5 ]5 A% I    switch (var->bits_per_pixel) {
4 B6 e9 F  Q# a* P  E, m# a# V5 K0 c+ c    case 1:- O$ y; o; Y: O; ?
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;
! Y7 K/ [8 a9 a3 i* x! p        break;
1 e* _# D# T% Y% C! l7 I    case 2:
( w7 e/ E) J+ Z7 A1 j; Q  U8 D: o- B3 h        regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;
1 W4 z5 p6 `: A        break;3 M* H+ ^; w$ {+ X  @
    case 4:; ~1 G, k' b0 l6 d! {, @) G/ c: j
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;% }" [' ~* k) F4 z
        break;$ _2 W& [: S) a( c+ R( h
    case 8:
3 C( e" V9 v- ~; i4 V8 m+ f& |! n        regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
- n4 w) C8 z6 b7 K        regs->lcdcon5 |= S3C2410_LCDCON5_BSWP |8 k) _6 d0 s1 Q6 f# M7 K! r
                 S3C2410_LCDCON5_FRM565;
. R8 ~; N2 m' y# a3 a: i+ Z        regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;; Q# W/ H3 ]& a1 a. c
        break;
; a6 l# e& A/ s7 ~6 N7 Z    case 16:
5 q9 m6 w/ o. b) M" u# y* b        regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;! Q- M. f  S5 w: ]. C7 \) _
        regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;0 O3 E' E2 u. o, g9 A' A" g
        regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;" E2 _) P. O/ g$ j
        break;
' Q/ D9 T  |( H! w, n/ K    case 32:# l/ B+ f" f: A7 i# {
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;4 J1 }% i3 \0 B0 D% D
        regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP |7 C7 U) c4 [; d1 T% i
                   S3C2410_LCDCON5_HWSWP |
+ M+ h5 b/ L9 Y# P4 S. H                   S3C2410_LCDCON5_BPP24BL);
- [' d, j; ?/ z8 s1 C% ?        break;7 L1 C. L4 o  z& `4 j
    default:
( H# [6 d9 O+ o9 c% Q% X        /* invalid pixel depth */6 t$ E& n# y* ]6 p0 t, \; g, R  f
        dev_err(fbi->dev, "invalid bpp %d\n",5 t; v. S8 v6 u. j9 k7 Q/ `- b
            var->bits_per_pixel);4 N" E  I0 w* `5 k# e, [
    }! H0 W4 l, W$ b2 p: G: C$ ~0 i
    /* update X/Y info */
+ l/ ~1 {: M9 `1 J/ J    dprintk("setting vert: up=%d, low=%d, sync=%d\n",5 Z- U0 m& e! p6 Q
        var->upper_margin, var->lower_margin, var->vsync_len);
, S3 u3 O  z; F$ m: B6 Y& _( m# O: R
    dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",. \/ P$ P9 O" a4 i: b9 o# U& v
        var->left_margin, var->right_margin, var->hsync_len);' Y; v9 f" o: y& C
    /*7 h7 ]0 u! t4 U( I! A
        所有时序参数必须减1,因为在公式中:. s. C  u/ c) b. ]8 _
        Frame Rate = 1/ [ { (VSPW+1) + (VBPD+1) + (LIINEVAL + 1) + (VFPD+1) } x {(HSPW+1) + (HBPD +1)
) Q2 U3 f( Z+ }2 [        + (HFPD+1) + (HOZVAL + 1) } x { 2 x ( CLKVAL+1 ) / ( HCLK ) } ]7 a( p) K: `8 U+ I. z+ Z
    */
! S' z5 R8 f  J5 B  `    regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |
  P1 d' m( w" v" Q* A/ T- g            S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |5 ~" Q/ L% N. s7 B. d1 K
            S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |' v6 w2 f; Q0 b* ~
            S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
9 A6 r& p9 g: N$ y) a7 B3 `* h
3 G4 G$ L' d$ l, Y$ Y0 i    regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
2 J( ]; z) D6 ?! L5 A            S3C2410_LCDCON3_HFPD(var->left_margin - 1) |
; a) B  z. u, i7 S- V- Z            S3C2410_LCDCON3_HOZVAL(var->xres - 1);" W9 U/ W9 x- B. Q* [( R( g  m7 b

- H. h# N# ?' l: L: Q" P    regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);& ~, ~+ M+ H. C* A
}0 b6 r: O$ {7 g& }$ y3 f6 d! f; a

" ?, [4 @* N! [/* s3c2410fb_set_lcdaddr
1 {7 c" s2 U- A6 e3 h *
/ R9 Q' v2 F1 x1 d  z/ b * initialise lcd controller address pointers
' \0 Q9 j4 e; K1 x0 O; L */3 A6 [& A7 W; F* ~, [; X
static void s3c2410fb_set_lcdaddr(struct fb_info *info)) v( ]3 T% _, c( z9 z# E
{. K8 K! ?  C- E' j% K% n
        unsigned long saddr1, saddr2, saddr3;1 I% a8 j9 j4 d) C
        struct s3c2410fb_info *fbi = info->par;/ z; [9 u. f( J: i; Z
        void __iomem *regs = fbi->io;2 \3 k, t6 W& {% `/ ~, R. |
0 m0 l1 z/ G" O8 }% H" p
        saddr1  = info->fix.smem_start >> 1;          /*帧缓冲区起始地址*/
3 I- i1 A5 ], k- v2 o( h# X8 k        saddr2  = info->fix.smem_start;                        ' g  F! J: m4 \: A# ?+ N! y+ @
        saddr2 += info->fix.line_length * info->var.yres;3 @0 u' c9 i2 W6 @; q' v; o* `
        saddr2 >>= 1;                                                        /*帧缓冲区结束地址*// ~& \# F6 r9 k. f4 ]" I. X
5 Y/ w6 _: O) C6 I
        saddr3 = S3C2410_OFFSIZE(0) |; ^  W6 _0 l! E9 `* A( ~+ b
                 S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff); /*offset = 0, pagewidth = 320*/" P2 M  z( e6 L0 M, d! R' w1 c
. F: @& ^( A: i3 [7 O
        dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);
6 ~0 r8 D& I) e( e2 z% }' i        dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);0 y! X0 a! A& n2 m
        dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);
1 k; }7 q# g5 T2 D
: D/ h" D; z7 f! p- ~5 V        writel(saddr1, regs + S3C2410_LCDSADDR1);. z0 ^/ B+ ^: m4 t1 H+ o% A+ f
        writel(saddr2, regs + S3C2410_LCDSADDR2);
" V( X: I9 ]! d( Q        writel(saddr3, regs + S3C2410_LCDSADDR3);/ n' }; H( P1 k/ Z
}
  k# X! l0 t+ m; s4 q) ?6 G) _, f9 Zs3c2410fb_calc_pixclk用于计算时钟频率。) s8 `( T2 k7 G7 f5 U- V
* Q. r5 a2 z# A( N2 c# O: l' A8 T
s3c2410fb_calculate_tft_lcd_regs设置了LCD的控制寄存器。1 v$ s1 C- p% I: O) i! L
& K) c7 I& |" y- v  h
s3c2410fb_set_lcdaddr设置了LCD的地址寄存器,该寄存器的设置请参考datasheet。9 }% D* C" S( Q* G/ o. p

( R* z' f' b5 f" M" {4.3. 2 s3c2410fb_blank. s3 D' e6 {4 V. f9 N* Z" W; `2 A
该方法完成消隐功能。
& q3 d1 U' Q4 [- ]. n
8 i- f7 n* F( m% P3 H/*  P. }, i5 P$ V. u8 g
*      s3c2410fb_blank
1 J2 Y" U) L, @ *        @blank_mode: the blank mode we want.& M# D5 C! R8 U8 K! a
*        @info: frame buffer structure that represents a single frame buffer
8 a- O3 P& q, g: M* E- {9 v0 ]0 b *6 q1 {& Z& G& Y2 g- K3 O$ ^) g
*        Blank the screen if blank_mode != 0, else unblank. Return 0 if
0 [) h9 r* H7 ]7 R *        blanking succeeded, != 0 if un-/blanking failed due to e.g. a) w* L1 [2 @. O. a2 f, X7 h" T2 _
*        video mode which doesn't support it. Implements VESA suspend5 X" W% l1 u6 |, p. Q; I3 p
*        and powerdown modes on hardware that supports disabling hsync/vsync:
, d& i$ E8 }2 R) ?" `  p *+ H: v4 j+ |  u( ?5 C! s
*        Returns negative errno on error, or zero on success.
! t+ a) j; V: X5 Y  k0 ]- l *9 B% T* ~# L# v4 ~9 m
*/
& u$ m/ }' q9 f5 i8 x; v5 X# u3 Dstatic int s3c2410fb_blank(int blank_mode, struct fb_info *info)
1 ^. h* o: v  h+ z- i% Q  u: Q" {{
/ U; s9 y  ?; y: s# r. ]% E    struct s3c2410fb_info *fbi = info->par;* `: F; W& Y2 J" x
    void __iomem *tpal_reg = fbi->io;
  e/ \; u( U! T5 H& G7 T- {3 P4 Z  E9 [
    dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);
" |3 G! U' M3 v3 ?# m/ x3 ~3 t5 O) Q. p9 B6 T7 m; D
    tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;8 W- M9 \  G0 V, {3 L1 D

: P( a% e' d: `! D) h    if (blank_mode == FB_BLANK_POWERDOWN) {    /*消隐*/- L+ \' R7 c" S3 r3 Z4 m
        s3c2410fb_lcd_enable(fbi, 0);        /*禁止LCD*/
$ L3 T$ U4 f- z4 E$ _8 z& q    } else {8 f4 c5 y9 T0 w
        s3c2410fb_lcd_enable(fbi, 1);        /*启动LCD*/6 u6 J# u) A- W+ \
    }
5 L9 R# i; r! v' Y. y" l- c0 h1 w  F# }
    if (blank_mode == FB_BLANK_UNBLANK)        /*去消隐*/
; C# j1 ?( B/ x9 M0 |( t# v5 g* x, g6 l        writel(0x0, tpal_reg);                /*禁止temporary palette*/, o; N& I8 E- K7 _/ K- y9 ?3 J
    else {                                    /*消隐*/
. b! n) J2 r$ ^, D3 {9 \3 H' X% W        dprintk("setting TPAL to output 0x000000\n");0 @; \" T% @2 L
        writel(S3C2410_TPAL_EN, tpal_reg);    /*使能temporary palette,颜色为黑色*/
) M1 J9 L( n5 t7 A% e+ g    }4 R  H9 w8 c' h; l7 d, o7 C4 s

7 X. n+ i; V4 x/ h    return 0;3 `  O6 m* s; H. {7 S7 w  p
}
) z4 ?: z5 l& i, a5 {" c2 e3 R在消隐时,屏幕将全黑。3 E) x8 |9 ~, w: l* w( r
6 Q+ r) E6 X, r  \% S
4.3.3 s3c2410fb_setcolreg5 x( q8 o9 Z9 a: m
该函数的功能用于设置LCD的调色板。调色板的概念请看我的转帖:LCD调色板。1 t& U9 B4 s5 z( F

. E  A) v) [- k9 D# ?static int s3c2410fb_setcolreg(unsigned regno,. E2 ^& r: M" o3 k8 n
                   unsigned red, unsigned green, unsigned blue,
! X' a" |7 S; h                   unsigned transp, struct fb_info *info)* X. G. Q5 C  S$ @" p
{% K/ T% \4 o* w! G' Z! j3 ]
    struct s3c2410fb_info *fbi = info->par;    /*par指向s3c2410fb_info*/
* m& F5 w9 B! |0 p. T' U2 d1 i9 S    void __iomem *regs = fbi->io;$ {9 B5 \& J7 Q9 l7 W( O2 y
    unsigned int val;
" m5 |2 H% t2 Z! ~) Z
% y7 h; }, }% h. n0 h0 ^: M: @    /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
. D) ^0 ?- o% b2 m8 G9 u+ Z           regno, red, green, blue); */' C9 E0 G# k  E* m& v/ @

; z$ P" ^7 V4 M    switch (info->fix.visual) {    - Z8 Y) U, K8 _" M
    case FB_VISUAL_TRUECOLOR:    /*使用真彩色*/; X( r  t- F, ?8 K
        /* true-colour, use pseudo-palette */
  J3 B$ f5 ^3 L* }" D7 g% H% W5 ]( Y3 F& e7 u4 C
        if (regno < 16) {    /*16种颜色,为什么只用16种颜色???????????*/" Z4 z" z$ S0 p+ M& C$ T
            u32 *pal = info->pseudo_palette;
, E1 z* V& C: f, ^0 _/ A, E" p( M
, B7 P7 J5 D0 K6 ^( w7 |1 y            val  = chan_to_field(red,   &info->var.red);4 o3 [" |4 j7 ]; Y. }. r7 B% a; B
            val |= chan_to_field(green, &info->var.green);* ]# O+ M" e. |  h; o- `
            val |= chan_to_field(blue,  &info->var.blue);: X" d: I( e+ N( H- G% a9 @
( @7 R) T9 P' _) V
            pal[regno] = val;    /*保存颜色值*/* A: x6 q1 d1 o- ?/ a1 ^
        }
: X7 N! M9 Q" [& A        break;* `! S$ k7 t' l0 E  L
6 Y  ?) V; ?* I) w
    case FB_VISUAL_PSEUDOCOLOR:* d! k: S& q* [" \
        if (regno < 256) {$ r2 i5 O% \5 ?$ n
            /* currently assume RGB 5-6-5 mode */+ D3 q+ a5 Z- C9 m) e8 e1 |4 X4 ^

4 ]! [' r4 Q9 E' Q8 j  |            val  = (red   >>  0) & 0xf800;
4 d6 w' ^; T( m% r# {: x            val |= (green >>  5) & 0x07e0;, Q$ ^" m3 _; q$ q* |. @# N
            val |= (blue  >> 11) & 0x001f;" x! p7 Y8 u  R. S

9 F! |4 v5 x: n0 D            writel(val, regs + S3C2410_TFTPAL(regno));
, A% I! Z6 S  U: a! }# G0 }0 H; \            schedule_palette_update(fbi, regno, val);
/ O, t; B9 n/ v% ]        }. w  {( I  Y' J1 Q+ G* J2 u7 }% I& n+ S

6 `4 l  @5 S3 |9 {, l        break;
. L$ T9 q! w. N5 R' r% a: `
  M$ g: @3 j& ]9 {/ I/ q    default:
' ~, f$ _2 e6 Y7 ]# |4 _$ D! g        return 1;    /* unknown type */5 U& F7 i& |! o+ J
    }
. r, b$ U8 o5 D# f
: B- u5 y# ?& M$ j' u    return 0;
3 C) }, L# u$ x}
- p: Z1 g4 h: ~2 x6 }/* from pxafb.c */- S  n2 D0 k6 K& E2 P4 t0 a
static inline unsigned int chan_to_field(unsigned int chan,
" j: @- N- q" [) Y) e# E                     struct fb_bitfield *bf)- }4 g9 |: d1 @6 J. V
{   
3 Q4 h& z! {4 z. n# z    /*下面用到的length和offset在s3c2410fb_check_var函数中设置*/
$ `4 V5 ^' Y, d+ B: p    chan &= 0xffff;                /*取低16位*/& F; {# E1 G+ i. N' p9 c$ T9 j
    chan >>= 16 - bf->length;   /*取第length为到16位为有效位*/+ g' F! `3 S- Y) }/ _
    return chan << bf->offset;    /*移动到相应的位置。*/
& j. R2 W% h8 @2 @: u}  M3 X6 e- v9 Z7 U! T
我们使用的是真彩色,可以设置16种颜色,颜色的位域值通过调用chan_to_field获得,然后保存了颜色的值。但是比较奇怪的是,这里并没有将值保存到0x4d000400为起始的内存中,不知为何。 反而倒是在伪彩色模式下, 使用了writel(val, regs + S3C2410_TFTPAL(regno))写到内存中,然后调用schedule_palette_update函数来更新palette表。我们来看下。
8 \# k  \2 z1 X4 c, I: A6 B7 M; h8 J7 P. D7 r% p
static void schedule_palette_update(struct s3c2410fb_info *fbi,
4 \1 j- p3 u: P. n. G( b                                    unsigned int regno, unsigned int val)2 P3 K7 }1 P1 x0 b* ~9 `: ~
{! L# l& R5 F4 V& c* P
        unsigned long flags;" E# G8 z' H' k. _4 T8 O+ q, y
        unsigned long irqen;/ a7 o) d' B9 X$ D
        void __iomem *irq_base = fbi->irq_base;7 A# l. i! A( i$ ]$ w
7 o; Z. R( q4 c5 w) }& I/ v4 `
        local_irq_save(flags);
2 n0 L& Q5 i$ c# d" ?( T) |4 p
! P4 t( Z8 B6 k. u3 Q7 l, W* a        fbi->palette_buffer[regno] = val;1 L: O7 {+ L4 i7 w

) N5 z1 N" i8 T4 c        if (!fbi->palette_ready) {9 x0 n; V% Z5 v' V
                fbi->palette_ready = 1;1 b8 r6 s2 D$ P2 V+ R0 e/ x

7 l' Y7 H- m7 Z; v) X) H8 H8 B# N                /* enable IRQ */
+ V! j! z$ B. v) I7 J( f                irqen = readl(irq_base + S3C24XX_LCDINTMSK);, k2 s$ O+ w0 Q/ ]$ M1 V. e, ]
                irqen &= ~S3C2410_LCDINT_FRSYNC;
! x; [3 ]; e/ x3 z7 e1 a* @                writel(irqen, irq_base + S3C24XX_LCDINTMSK);
2 S* ~3 k. t/ K        }
7 H9 W* z1 |$ {2 L" k1 j' g& {8 h# X3 w( N6 s+ ]
        local_irq_restore(flags);; n. _8 `" c: h
}- [. c0 N4 q) r/ C; i$ {
这个函数的作用就是开启了LCD的帧同步中断,这个中断在VSYNC信号从无效变成有效时产生。当中断产生时,会调用在probe方法中注册的ISR。ISR如下:
6 U. ~/ f; `5 \6 j* o$ k* Dstatic irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
/ _" ?8 g/ @& A" H{
4 x/ W7 f- x% {  L        struct s3c2410fb_info *fbi = dev_id;
: D/ j7 Q" Z6 W; h  E- h' f        void __iomem *irq_base = fbi->irq_base;
& e/ n1 ^! [# I# j        unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);
/ P- T+ L7 t4 p* Tb6 `# e4 \" ?  u' w
        if (lcdirq & S3C2410_LCDINT_FRSYNC) {
6 ^/ r4 V" A) s                if (fbi->palette_ready)
; x) J! a/ m1 {, z6 M' I+ I                        s3c2410fb_write_palette(fbi);- t0 y* z% W- ~* K9 G* l7 Q
6 A3 e. q' f: k+ k  k0 [' [
                writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);, u0 Z; }' k/ N; e
                writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);/ N4 \* V9 ]5 {4 V9 c  e
        }
' {9 ^; w0 A2 Q) G/ U( @7 h
. C; C2 W; `4 K2 E2 f* ?! u( ?        return IRQ_HANDLED;. w  e; M* g7 h& z, o( B8 B
}: K; A7 p, W! P' ~

5 P' Z% H0 v7 Z' m8 _+ Estatic void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)1 e* q! N" G% H6 L
{) v4 a: l, t" o% a9 [& Y
    unsigned int i;! n0 S' @& T. [
    void __iomem *regs = fbi->io;9 F- j' V% @7 d' _( ~' f% U

: `: |1 R8 A1 H- P3 T    fbi->palette_ready = 0;  /*清除ready标志*/8 V3 W# l0 A+ R5 `, u

7 e) `, f* ^& f9 }; U    for (i = 0; i < 256; i++) {- b7 ]& |4 N6 p; f
        unsigned long ent = fbi->palette_buffer;
7 P) _7 p$ [+ E3 ~9 [8 ]. O        if (ent == PALETTE_BUFF_CLEAR)8 Q: T/ Z+ [# M# ?6 j9 v* z# p
            continue;
- T% X2 ^9 Q8 n. D" {4 O4 I
. }6 A7 z0 k2 C9 n        writel(ent, regs + S3C2410_TFTPAL(i));
. x9 |4 J. m+ |3 L6 j, s4 j; x! V" p+ V8 T* T) g! X; j* B
        /* it seems the only way to know exactly
" |! k, s$ t& `# d8 c: m: }' `: [3 s         * if the palette wrote ok, is to check
7 u$ U5 j% E7 S         * to see if the value verifies ok
8 V! O" g+ s7 }5 h9 h         */+ N: D0 {' b& l. ?  l9 G/ {

8 V# d% H2 q% k* a: e* e' a* A3 J$ I        if (readw(regs + S3C2410_TFTPAL(i)) == ent)8 T0 ]' c5 T$ `" C9 V; V
            fbi->palette_buffer = PALETTE_BUFF_CLEAR;
) n% c# s% l% O5 d        else
& D) s% q5 J9 o            fbi->palette_ready = 1;   /* retry */3 p& M+ w9 x2 _0 f/ n5 ]
    }* G5 }: S% V8 n
}
% k0 t/ G; u# P' v1 y) C在中断函数中调用了s3c2410fb_write_palette,该函数对写入内存的颜色值进行检查。如果确认其已经写入,则将palette_buffer中的清除,否则retry。4 h2 {5 H. |8 F* f, r8 @
' v/ ~0 U+ t. C
5. 总结" Q* R* S; m$ I' z5 a8 O% H- U
本文对frambuffer子系统做了简单的介绍。frambuffer子系统的函数相当之多,在这里是不可能一一介绍的。本文首先介绍了主要的数据结构,
( L6 X' d) G, |' C' t( Y! O% {- r& N/ L1 y- G
随后分析了frambuffer核心层,在核心层简单的分析了5个API函数,接着,对驱动层做了介绍,驱动层的大多数函数都给出了分析。1 t( i3 h) F3 M  N( J& D6 ?7 f1 Q

0 X1 `0 r% a; g9 \; W8 x+ p  d4 V6 L
) m1 T8 C4 b: N! z0 ]$ `8 }
$ i9 e, A# }: W  ]( U
! l. s3 S1 O% b4 }: Y5 H" f% h

+ \- m) ~0 E! b: [( M2 x
) M) i+ ?( h6 j" G
( `5 i- _: E" d3 X- w7 l3 m

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-11-25 17:36 , Processed in 0.312500 second(s), 27 queries , Gzip On.

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

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

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