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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
本文将介绍Framebuffer子系统
$ n8 b( e( I8 y, T* S, _8 o# W3 N! P1 F8 s
目标平台:TQ2440 CPU:s3c24406 S( K- ?5 R* w5 M

* Z- s) ?$ Q  d3 u1 w5 w) _( xLCD设备:3.5英寸,分辨率320X240; t7 p2 }0 e( Q* d: W! h

% p, U/ S: t% O1 G/ W2 d
( x! H; E7 z9 s9 t/ `2 U
0 e* l# l( m4 r1. 概述0 G" J" o; n5 {; Z. E
/ J! z( J4 G: Y1 |" U1 P) C
Framebuffer,中文名字是帧缓冲,这个帧也就是一副图像所需要的数据。因此,帧缓冲其实就是LCD设备的驱动程序。Linux中,framebuffer子系统框架如下:
  z  l+ D2 n8 V, E" }; i) U5 a* q% B/ ?9 e5 o' W5 d
; B2 h7 f- q( }4 X" F1 W# g6 B1 ?

+ X; R  p! L6 i* G3 J: i$ n/ [8 H核心层的代码以fbmem.c为主,核心层包括许多与具体硬件无关的代码,并且提供了API给用户空间。用户空间使用系统调用,系统调用会使用相应的API函数,最后会调用驱动层实现功能。对于不同的设备,驱动层的代码将有所不同。
, t' S7 R1 W4 {2 e0 x0 z7 x' I) L0 C# X: f
接下来的内容中,首先给出framerbuffer使用的数据结构;随后简单描述framerbuffer核心层;最后,针对S3C2440,对驱动代码进行分析。
5 V1 D) X9 f; d2 b! s' D, H- ^9 ~" V) |7 F  f. ]4 O
2. 数据结构
5 \' z, |7 d7 L! P9 m/ ^2.1 fb_info 结构" a+ Z9 R8 T: f: W
  该结构是内核用于描述一个特定framebuffer设备。其中包含了几个重要的结构,将在下面介绍。
8 M8 U$ L4 y6 l: \! G8 ]- }- ?1 V) H1 p. x1 o8 s* Z
  下列代码位于include/linux/fb.h
( n+ k3 E9 e8 L, D9 H* V9 o% E: a; O. y- w' c9 I- _* G2 ?  k
struct fb_info {$ N: {( Y" C+ |; T8 H' I
        int node;3 i' n* C3 w' V+ W
        int flags;
, r0 \7 F# X$ ~# G$ W' l+ B' d$ s        struct mutex lock;                /* Lock for open/release/ioctl funcs */. p2 y: ~% P% p. g: m
        struct fb_var_screeninfo var;        /* Current var */) `; [0 P! v7 d; w3 H8 \
        struct fb_fix_screeninfo fix;        /* Current fix */
, G" ?/ u8 K, |3 [: `$ f        struct fb_monspecs monspecs;        /* Current Monitor specs */
9 D. V, Q$ {$ P) \        struct work_struct queue;        /* Framebuffer event queue */- O% Q0 |# t+ i- {: n+ D4 }
        struct fb_pixmap pixmap;        /* Image hardware mapper */
! @0 H* j; S6 d" g, g1 f        struct fb_pixmap sprite;        /* Cursor hardware mapper */
0 x0 S7 [+ C# k2 H3 ^0 V        struct fb_cmap cmap;                /* Current cmap */0 a% i( Y+ }# ?7 Z, w2 H
        struct list_head modelist;      /* mode list */4 Q4 }8 [+ c/ q1 }# y$ J% ~
        struct fb_videomode *mode;        /* current mode */% n* G/ x) H5 B& ^$ H

' n0 q7 ?, N: ^. b; t0 I) l% G#ifdef CONFIG_FB_BACKLIGHT
8 m- i; g6 _" ]: d7 U; L        /* assigned backlight device */' s6 t% e# o0 @# u% P
        /* set before framebuffer registration,
7 z6 A, U  [( d           remove after unregister */
( o3 ]' O- x5 b6 R5 c6 G9 D* I) _        struct backlight_device *bl_dev;
) {1 l4 F, h: y& z  z9 s0 V
6 X/ ?- i  `; B' }        /* Backlight level curve */# T7 j) V' W% }& J
        struct mutex bl_curve_mutex;       
( R8 s9 V1 d7 A! r% o8 {* B" `$ O' q        u8 bl_curve[FB_BACKLIGHT_LEVELS];1 f, b9 X- L  m8 ]2 e% ]
#endif
7 K3 D( K7 l$ p; y#ifdef CONFIG_FB_DEFERRED_IO. _3 {, T( v; W+ k5 ]2 B
        struct delayed_work deferred_work;
3 q* S& m# U5 D: z/ F        struct fb_deferred_io *fbdefio;+ @/ E, W" A) R: G3 g0 \: m' S: O
#endif
: ~0 S9 W9 J' q- j2 c: f7 A8 {$ r2 g1 e' Q/ @2 I
        struct fb_ops *fbops;
; a2 ]4 V% b6 `0 h; {        struct device *device;                /* This is the parent */
  M' M5 P; ]% h        struct device *dev;                /* This is this fb device */, j+ Z& Q4 v& V; w) d3 b( S4 v, \0 d
        int class_flag;                    /* private sysfs flags */
& e' J2 U: Q% w' w- Q6 @#ifdef CONFIG_FB_TILEBLITTING
  D$ l* A7 m. T! j$ v' F        struct fb_tile_ops *tileops;    /* Tile Blitting */; G# B& W" H' E" S* z' F
#endif
7 D# c. q7 Y  {$ J, @# q* h& P, Z        char __iomem *screen_base;        /* Virtual address */* G/ e6 W7 C/ `9 [  B/ E
        unsigned long screen_size;        /* Amount of ioremapped VRAM or 0 */
: Q( Q& E; ]& k  A6 j' g        void *pseudo_palette;                /* Fake palette of 16 colors */ 7 r) A9 X2 E0 y, S
#define FBINFO_STATE_RUNNING        07 P6 M/ I# y& o1 T
#define FBINFO_STATE_SUSPENDED        1+ z- Q2 q1 [8 T8 Q
        u32 state;                        /* Hardware state i.e suspend */+ Q+ S! H. [6 T. u) ~4 O: Q4 g
        void *fbcon_par;                /* fbcon use-only private area *// c: M! [" M6 o
        /* From here on everything is device dependent */, u! N# W9 a/ Y4 |; F* f* g% X- a
        void *par;       
  Q- n/ O! W' o) f};! c) l) a- g" ^: C3 K
2.2 fb_fix_screeninfo结构; b" o0 E3 E+ c! E- r2 C  [1 i, C
  该数据结构是不可改变的,也就是说用户空间不能改变该结构中的任何成员,在核心层我们将会看到这点。* d  r% m; L4 ?! I2 x3 L
2 i- H) `& |9 k+ B# Y8 Q
  下列代码位于include/linux/fb.h8 m+ p* U6 r8 K6 H7 F' U( y. X
$ e% @2 B' w4 L9 w: d
struct fb_fix_screeninfo {' i( X2 I" u6 }& B4 R
        char id[16];                        /* identification string eg "TT Builtin" */
- h5 p' b# H1 _8 i4 Q* F7 @        unsigned long smem_start;        /* Start of frame buffer mem */' y: {/ ]: C% v: s, s
                                        /* (physical address) */
7 L0 \! r9 ?6 U' m! d* ~5 U; l, z5 K        __u32 smem_len;                        /* Length of frame buffer mem */! _  f* z, E- l' _4 P" W6 [7 ]
        __u32 type;                        /* see FB_TYPE_*                */; m9 g3 C. \* L  R
        __u32 type_aux;                        /* Interleave for interleaved Planes */
& O$ K) b0 Z. [' `) |4 s        __u32 visual;                        /* see FB_VISUAL_*                */
9 l6 B' g) S" e! _        __u16 xpanstep;                        /* zero if no hardware panning  */4 S; ~  W& [8 B( X5 s/ {0 v. }
        __u16 ypanstep;                        /* zero if no hardware panning  */
9 E- B) j! ^; {; m        __u16 ywrapstep;                /* zero if no hardware ywrap    */
! F6 U: ~8 }; Y6 u, ~/ a4 @1 o" w        __u32 line_length;                /* length of a line in bytes    *// G3 [8 B" G2 B) B
        unsigned long mmio_start;        /* Start of Memory Mapped I/O   */3 F/ p( {& A; v9 q8 D$ _# |
                                        /* (physical address) */
" f1 n5 d! G" ?" D8 G        __u32 mmio_len;                        /* Length of Memory Mapped I/O  */+ [. x6 d/ h2 k1 x9 [
        __u32 accel;                        /* Indicate to driver which        */
# t  J& R4 N7 P( X                                        /*  specific chip/card we have        */
# q+ T: w% D1 X        __u16 reserved[3];                /* Reserved for future compatibility */$ [6 @% \# q* d
};
; ]7 |* l  w* w* B3 @& _" B  O2 H- K- Z' A
2.3 fb_var_screeninfo结构
- S9 D6 N, i1 A5 H  该数据结构是可以改变的,也就是说用户空间可以改变该结构中的成员。该数据结构中的很多成员就由板级信息复制而来,在驱动代码中我们将会看到这点。4 A- j8 i! `9 w3 z5 n) J; A% ]

" X& d) p9 |3 \# ?# ~0 P, n  下列代码位于include/linux/fb.h; ^* a% O, g; C. j

. H+ O" y+ l8 a6 E( {struct fb_var_screeninfo {
+ l- c) l/ z7 q, ?+ S$ B7 [        __u32 xres;                        /* visible resolution                */
1 q) \8 U- c+ b3 O3 \9 [- |; O        __u32 yres;
+ ~3 X# @' p; {        __u32 xres_virtual;                /* virtual resolution                */
6 F; w( \- z3 D1 S" o8 B/ W        __u32 yres_virtual;1 C! O& a( G" k) W0 T' v
        __u32 xoffset;                        /* offset from virtual to visible */
5 C* @+ `+ @2 N: P4 Y# N" B4 z        __u32 yoffset;                        /* resolution                        */. @8 @. R7 W: {1 a
2 D- [* B; E' m" Y! X! p
        __u32 bits_per_pixel;                /* guess what                        */
  V& S3 Y, r- G) g4 W. R        __u32 grayscale;                /* != 0 Graylevels instead of colors */: @: R' [* z. s8 ]

* ^9 s) B3 ^; V9 |5 p        struct fb_bitfield red;                /* bitfield in fb mem if true color, */9 ^0 |: P5 b3 e( a, C. k) ?0 t6 t4 g
        struct fb_bitfield green;        /* else only length is significant */' @- f) P2 Q8 w0 ^+ M6 y
        struct fb_bitfield blue;5 \+ `  m6 h, ?9 m& R2 b; j! f- \! C
        struct fb_bitfield transp;        /* transparency                        */        & a# I9 y9 G" n/ C/ ~

$ ?; q7 D- L5 e( e) T& O0 w- J        __u32 nonstd;                        /* != 0 Non standard pixel format */
' u5 o* a1 i0 J% ~1 e* o7 B6 p( _4 s% V. J! ^- ^' F6 O6 f$ D& l0 b, G. M
        __u32 activate;                        /* see FB_ACTIVATE_*                */
' s0 H. V% z1 f
8 I2 Y/ \' L' m        __u32 height;                        /* height of picture in mm    */4 i1 S" ~" t  C9 t$ o; y
        __u32 width;                        /* width of picture in mm     */' ?# u8 \& V1 c4 O$ Q; v
+ v  h0 C) A* F* f; M/ C
        __u32 accel_flags;                /* (OBSOLETE) see fb_info.flags */( I2 y/ y3 R6 X1 l( Q
  m) ]. l. _! i# H' P  d
        /* Timing: All values in pixclocks, except pixclock (of course) *// _( O3 U8 H# _7 t* X
        __u32 pixclock;                        /* pixel clock in ps (pico seconds) *// f9 Z7 S% Q8 Y& w7 O+ O
        __u32 left_margin;                /* time from sync to picture        */: `5 J- A( w+ }, s
        __u32 right_margin;                /* time from picture to sync        */
7 p5 ~4 R& l# Y0 V        __u32 upper_margin;                /* time from sync to picture        */
! W0 O7 t) M! @+ b- C% p! D! s5 r        __u32 lower_margin;
  k& n3 g3 e8 N. v" I        __u32 hsync_len;                /* length of horizontal sync        */  X3 {2 \0 i7 x+ q* H: p3 V$ o
        __u32 vsync_len;                /* length of vertical sync        */
4 \% n6 ?8 D1 P/ U( i        __u32 sync;                        /* see FB_SYNC_*                */$ @# H* Q! T6 t& i
        __u32 vmode;                        /* see FB_VMODE_*                */% n6 A' p0 v6 H0 m
        __u32 rotate;                        /* angle we rotate counter clockwise */
% r9 ^" H4 g- Z% V        __u32 reserved[5];                /* Reserved for future compatibility */% \* w" Z$ D6 ^3 V
};4 B( ?8 x; U! [# J1 w" a- C

8 n8 @; F! J' Z' k7 G2.4 fb_ops结构
: |0 c! Q/ J$ w6 g/ y- }  该结构描述了用于fb_info的方法,这些方法中有些是要驱动程序提供的,而有些可以使用内核提供的方法。
3 N+ r1 j# R' O  v) B
3 s$ K6 t  g& ?' S% u 下列代码位于include/linux/fb.h0 H. ?( L# P, T

% R% u# t* o4 r) E) k/*% S2 D5 A# x7 e8 [
* Frame buffer operations5 |& y4 k8 d6 q$ C2 n. u7 D9 F
*) A" b4 J7 w# G  X9 E/ Y0 J5 u
* LOCKING NOTE: those functions must _ALL_ be called with the console& B! c& T0 k2 U! Z6 v5 |* {
* semaphore held, this is the only suitable locking mechanism we have; B3 i* ]6 T7 m( X+ n# e. A% h
* in 2.6. Some may be called at interrupt time at this point though.& X" G/ o$ Q$ ^* F$ `
*/+ w; M; S3 \+ e- ^# b1 _# I; \

" P8 j  K% B' S0 L/ @struct fb_ops {
) }& E5 D- l$ w3 X0 E# A        /* open/release and usage marking */
6 P+ m) @+ m8 U6 n1 S" S        struct module *owner;
* |" c# N' D' e2 Y7 ^4 N# R        int (*fb_open)(struct fb_info *info, int user);
7 H; ~) C, W2 C  U0 o, _$ N        int (*fb_release)(struct fb_info *info, int user);' Q* o7 E, _9 O* x. m+ U

8 Y/ ?# A2 O1 w1 Z8 ?: \        /* For framebuffers with strange non linear layouts or that do not" I  `1 y1 m, p: X" d0 j
         * work with normal memory mapped access# C' S' T; _  f
         */
1 d0 z) a, I1 L, O7 O  R        ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
4 D* p6 Y; K2 [( J( z                           size_t count, loff_t *ppos);. |, Z) m) I& Y. \8 A$ a
        ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,' \' [4 m4 o1 I3 p" Y4 f
                            size_t count, loff_t *ppos);# B6 l; v) v+ p1 E; H
: x8 R( w' m. b7 @  V( m6 W. J
        /* checks var and eventually tweaks it to something supported,
1 k+ M2 {- _- \& Q$ A         * DO NOT MODIFY PAR */
2 W( B2 \. a8 N2 f" ~2 u        int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);& x% V# _( p+ a% D# Y
5 d( A9 U  s3 m( b! w
        /* set the video mode according to info->var */
9 y$ Z! b" s3 i- k' T: }5 n        int (*fb_set_par)(struct fb_info *info);
5 k! U) o/ [6 F& h3 X6 s( {- T
3 `8 f1 ~4 g6 m. Y        /* set color register */
& O+ M! V: m! \6 d7 Z  n        int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,/ s- N5 Q' H- ]% r0 v
                            unsigned blue, unsigned transp, struct fb_info *info);7 Z4 R( F2 i/ A! Q: G) v% M2 O5 M$ ~: j

; Y- P! F# Y: O: ]% t$ |        /* set color registers in batch */  X* F. a8 p  U  x1 A* G
        int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);- ?8 N, G% H, W9 V8 l& H

- O9 }9 N; @* [0 B4 H+ W8 `        /* blank display */3 e( ]1 `# o) m; h
        int (*fb_blank)(int blank, struct fb_info *info);% h1 W+ d: Y$ u7 e3 ~, i* Y
! n* b5 u7 d; {9 d7 @
        /* pan display */
5 s2 v0 L* j+ b/ [, s& N" p        int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);- b$ B) N% u0 M6 Q
2 b: x$ g( Z* a4 i' B9 L4 @
        /* Draws a rectangle */* j. n9 F8 {  n
        void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
* ]( F5 P6 a0 c3 i        /* Copy data from area to another */
; \2 e- C  j* a% ^        void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);2 {! T3 q5 k9 C# ], @8 q
        /* Draws a image to the display */
7 [- \4 R6 |6 D9 g        void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);: N" _7 l$ Y4 O; Q4 B

+ }) c9 c0 q+ [2 v$ _        /* Draws cursor */
* F7 p* i" ~( w% S7 p4 A. `        int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);1 R8 q- F: R/ G+ N
. f$ R8 K0 o# }4 g; D: b5 n
        /* Rotates the display */- \+ D$ X; p: I: L, s9 |
        void (*fb_rotate)(struct fb_info *info, int angle);9 P0 d7 ?  S6 a" y$ h* d
7 J/ m. f6 l3 H: ^! J0 s
        /* wait for blit idle, optional */, W2 H+ |* m* G! n
        int (*fb_sync)(struct fb_info *info);  K& ]4 a% [6 P) h, N& R

) F9 r4 w- ~, \. O  G: A' n% E- W        /* peRForm fb specific ioctl (optional) */+ k& d* Y" v/ O- }  N4 k+ q
        int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,% T4 _. y# g$ |, i3 n" J! ~9 N& H
                        unsigned long arg);/ L8 e( [8 l2 |0 D$ D0 b7 L

  v  e1 u- }& ~' D% Q6 ~' J        /* Handle 32bit compat ioctl (optional) */
& b4 Q6 t4 I7 {! T7 J        int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
( @! q8 i8 p: X4 b; B: m/ m                        unsigned long arg);
6 M, p, |; Y% h4 M' R" q6 l2 s% A1 R2 L& {' V. U
        /* perform fb specific mmap */6 n* ?& h* D( X5 S  j& W+ U" [. t
        int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);. t* L0 y( v4 T. K: B- D. ^
0 j1 V& x1 p& \+ \- P/ J+ o8 y" J
        /* save current hardware state */9 [- k2 a" _  Q: \1 h$ x
        void (*fb_save_state)(struct fb_info *info);/ e% x8 R+ v+ @$ x% L# L
, L- L/ h0 g" M3 f+ A
        /* restore saved state */
0 x# d' J; H) E, n7 X0 j4 ?/ O        void (*fb_restore_state)(struct fb_info *info);8 y+ R1 I: H/ ^: U
  ~! S5 p6 P" [# R; G% \  l
        /* get capability given var */
0 F' c! G# @2 A& U        void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,) N& E/ e2 g; d# ?
                            struct fb_var_screeninfo *var);6 H1 t9 n% n7 B7 g5 H2 R
};
9 |( n% ?  O  V, y: t# I
# M( o  @9 I: s% X% G& @; l% c$ @; `0 g" K* n6 }
3. frambuffer核心层
0 X9 J- N9 x4 p( ^  d首先来看下frmaebuffer子系统的初始化函数。
4 @& |* @& H  Y, d( }& m) [
5 f/ L2 P, r" i0 v  r- ]5 P, q( g3.1 fbmem_init和fbmem_exit
' G3 D5 g- ^& r! l* p, f  y下列代码位于drivers/video/fbmem.c0 I- G( V; r; m/ d6 i: F

, e5 T/ g6 V" @4 o2 E/**# k) z1 z& S# P+ g
*        fbmem_init - init frame buffer subsystem
3 J8 y" n$ @5 t9 v. `1 `7 o' r *
% Y: k$ k! b: X. X *        Initialize the frame buffer subsystem., A" ~) L" f) Y0 Y
*
$ _. ~+ I" p: t* F, i *        NOTE: This function is _only_ to be called by drivers/char/mem.c.' q/ G  p4 H& b9 O2 m
*
' G) v' F7 [! { */
# o9 G4 Y7 g7 V/ ?  b2 _: Z5 U- v$ d3 `0 t( C& o
static int __init
  @& t9 E) x. g- Bfbmem_init(void)
! d" W( P# J8 r- z{9 f4 A7 \4 L. s2 \; ~. O7 j
        proc_create("fb", 0, NULL, &fb_proc_fops);$ m: [( k! }1 P; A: h* W

; N' |" J" g2 V; m' G: y, u        if (register_chrdev(FB_MAJOR,"fb",&fb_fops))                /*注册字符设备,major=29*/
, {% h; b  O4 j. P- M# z" d6 J                printk("unable to get major %d for fb devs\n", FB_MAJOR);7 [1 ]8 u3 f3 Z( H# S0 ]

0 p5 N* v3 k& N/ S1 V$ f        fb_class = class_create(THIS_MODULE, "graphics");        /*创建类*/
, t) e5 K- u# z7 N% T! c        if (IS_ERR(fb_class)) {; T4 K8 L+ z" _0 [  \
                printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));$ f2 m* p8 }" |. \8 F. t2 J1 ]
                fb_class = NULL;( m" i1 d- z$ ?* Y5 m
        }
# e" \9 z6 \$ Q: }; T2 l$ U% m        return 0;% x0 m. L5 x+ z1 U
}
! `. U% L9 S6 C0 Q6 Q0 r
& ^% R8 [$ a- ~9 h$ n5 s% _: W#ifdef MODULE6 v' g& Z3 _; V7 J4 A
module_init(fbmem_init);
1 C; f2 ~9 g  ?4 zstatic void __exit
! D' J3 w) z7 R7 @. u8 Mfbmem_exit(void)+ G( Z2 O5 ?$ G
{) W/ m# D+ I; T+ `  I! S
        remove_proc_entry("fb", NULL);
: m+ L) J: e; f4 d        class_destroy(fb_class);
3 s) s& A  ]( S6 v+ L5 w" ~        unregister_chrdev(FB_MAJOR, "fb");4 u* p* l" c" Z3 L
}  b) b$ c: y) U8 O4 D1 y; {- U6 k
  A6 G" @! ^* m" s
module_exit(fbmem_exit);; t2 c& |) s4 A6 J0 {$ ^3 N+ g
MODULE_LICENSE("GPL");
; C8 Y: f9 _. W( XMODULE_DESCRIPTION("Framebuffer base");
% W* T4 M7 M; M3 j1 E* W/ E- v#else
8 Q( \0 g& c' c) n9 G0 osubsys_initcall(fbmem_init);
: I6 @: F5 x6 J3 X#endif
3 c! ^( @$ x* x  r# }/ E7 ]9 p: _6 R5 g
static const struct file_operations fb_fops = {0 v! k+ t) n# ^( h, s
    .owner =    THIS_MODULE,
. L$ G- E1 E. k2 y& W7 f& _# z9 \" D    .read =        fb_read,
" }4 L: `" u4 B, p    .write =    fb_write,* S. h5 V* K% g" y8 f
    .unlocked_ioctl = fb_ioctl,5 a+ L; k  J( B
#ifdef CONFIG_COMPAT
4 @7 `3 }, r, I* w. n; V    .compat_ioctl = fb_compat_ioctl,
6 T8 m# H7 E9 c2 f1 |' ^#endif- a5 b* c$ t6 ^* m# W& {0 p8 n) ~, i
    .mmap =        fb_mmap,
0 f- S: ?! c, Q+ W8 |4 O0 V: L    .open =        fb_open,
8 D; S! e; S' D) e4 \/ ]    .release =    fb_release,
4 |2 N+ |  ]7 O9 Y#ifdef HAVE_ARCH_FB_UNMAPPED_AREA6 I5 r: _/ \8 U
    .get_unmapped_area = get_fb_unmapped_area,
7 @0 O* E) E+ {6 e* a#endif
: h4 ~( Q) z! M$ o* z. L8 Q#ifdef CONFIG_FB_DEFERRED_IO9 e3 g; p) R1 S0 T
    .fsync =    fb_deferred_io_fsync,
* ^* {9 o! j  g#endif6 E0 Y2 U7 D" y8 U8 [! N
};* R& L7 z: y' z4 d3 g6 ~7 \$ N1 w

1 g4 w) h" B$ O, a3 R, R4 o) C我们看到,如果不是作为模块,那么该初始化程序将在subsys_initcall阶段被调用。初始化时,仅仅注册了一个字符设备,并创建了一个类。通过字符设备,提供了API给用户空间,包open,release,write,read等。
3 R6 @' X9 D, F) Y& G9 F/ Q1 `4 L随后我们看看如何分配一个fb_info结构。7 p# |, g3 g# `) s8 z, o9 |6 Z# I

7 l! e3 m( F8 A' x' c3.2 framebuffer_alloc
; i6 L1 @) \/ d9 X* f下列代码位于drivers/video/fbmem.c
1 ^- N' G( @+ h" N' B+ _
3 }  r2 g7 L' p0 H6 A! k/**$ _; y, \9 `9 I
* framebuffer_alloc - creates a new frame buffer info structure
, k3 ?5 D2 `8 w7 U0 ?/ A, J4 E8 r3 ^( w *+ v) d) S# R/ v9 s
* @size: size of driver private data, can be zero7 s/ b8 ?% K9 g0 g% D
* @dev: pointer to the device for this fb, this can be NULL7 o9 i. T- J; }: e$ s; ]- g
*# S$ V  E( }' M7 X3 {/ X3 w
* Creates a new frame buffer info structure. Also reserves @size bytes* L/ D/ ^$ P& I" R
* for driver private data (info->par). info->par (if any) will be
2 G, J7 @; t. G; ?, q0 x: a * aligned to sizeof(long).0 p7 s/ f5 `# i' s; c) N* Y
*
: U  G( j  a' \  O9 X9 _. f * Returns the new structure, or NULL if an error occured.7 l2 o1 @/ \; z5 d: a$ |; N
*
& t# b! B* b1 T+ |; {+ Z1 S  b */" R1 k& r  g  r" h/ `
struct fb_info *framebuffer_alloc(size_t size, struct device *dev)+ M4 P2 c  m* k: c! V
{
% F6 ^' x/ H) Y, ^/ u0 `* ^#define BYTES_PER_LONG (BITS_PER_LONG/8)
' B9 p# {+ Q" U) n#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))3 B% p; n8 _4 `# @$ O
        int fb_info_size = sizeof(struct fb_info);0 k  Z1 b9 R7 u
        struct fb_info *info;
) i" L: }' ~3 y# x" R, ^! [$ O        char *p;) v/ e" `7 V! i7 I  }3 X

9 l' F$ o4 M- \        if (size)$ e  c# I! G: K$ H* s* ~
                fb_info_size += PADDING;' B# ?# q& {" l: O( ?9 A% q; a
. z# E1 D1 t; ]& R. t
        p = kzalloc(fb_info_size + size, GFP_KERNEL);& k0 P% c7 d8 P. g5 V" |" z2 `

: D; m" o9 }0 S$ r+ Y- I        if (!p)
4 ~# V5 V' ]* b$ v1 X8 Q; C' N                return NULL;* _1 p/ }9 C; w# `" \0 Z1 `# b) s
& s7 {# S# e3 V# U' N' u9 L
        info = (struct fb_info *) p;* u) c" q" S# }+ A

. |3 ~2 I9 ?2 k9 X: C        if (size)
6 _! i# X4 L! L- a% T/ ^                info->par = p + fb_info_size;( D+ s( h4 A. ^, n4 O+ f
; t% ~, k# j5 I1 w" @6 L
        info->device = dev;
: r# w" ?  k* X) i& D& [8 V4 r7 }, a* V1 n8 e2 O1 Q; |
#ifdef CONFIG_FB_BACKLIGHT
) U/ z6 K2 c+ w& y, S- }        mutex_init(&info->bl_curve_mutex);
( E  i: m3 b7 i- X) `* O#endif
; S: W8 x$ ^$ H2 k5 @( V5 w7 i
8 E) \3 p6 I: r: J0 G9 x  y: L  }        return info;; M- d  u9 R' y
#undef PADDING2 s! k* }/ z0 X/ t2 i' Z
#undef BYTES_PER_LONG7 w# k% H) W3 x3 i& R
}
, Q; H6 q6 N# I# V* {2 sEXPORT_SYMBOL(framebuffer_alloc);
* h* k7 A; |8 ^: @4 h+ ]在进行分配时,根据参数size的大小,分配了b_info_size + size的空间,然后让fb_info->par指向size的空间。因此par所指向的空间可视为设备特有的数据。8 d/ r' H! p; N4 d/ V4 M
在分配了fb_info结构之后,需要将它注册到内核中。注册由register_framebuffer完成。我们来看下。; ^7 e8 s5 Q, J% v9 Z3 D; c
2 W% i" m& q8 X' }; M
3.3 register_framebuffer% ?# q2 J6 ^, W$ E: D3 k
下列代码位于drivers/video/fbmem.c/ A) E# f+ P1 S7 s8 V. P
, Q6 E' V4 y9 }  ]$ ^
/**
% `9 E  m5 g. c0 Q7 Z *        register_framebuffer - registers a frame buffer device, k/ A2 }7 F2 b! g& |$ v
*        @fb_info: frame buffer info structure
- O) U3 s, N0 T- _ *
( r$ C7 X$ T8 a, P$ e3 E: ^4 _ *        Registers a frame buffer device @fb_info.0 n" P; _, ~# j# L  h1 P
*0 ?; s7 e; r7 i4 z) m' L) e/ M# O
*        Returns negative errno on error, or zero for success.
4 ]  ^% D1 c/ P$ j6 w *
" i1 H) m! l8 m' g8 s) M& W */
9 E! h/ A6 t& A7 t' r: O6 n1 b0 a9 S* ]7 \2 _* v# y* p& H
int
$ i+ {3 f3 @7 p- R5 x/ Aregister_framebuffer(struct fb_info *fb_info)3 L2 s' x; |% @
{0 e7 X( Y; k4 j1 |  `+ _
        int i;$ q* n; `6 H3 v9 l% a: E* {
        struct fb_event event;3 h2 d  z4 }! I+ H6 y
        struct fb_videomode mode;
! [: W1 c. }% k' y/ A" V3 S
$ p6 s- p" g/ s6 Q1 b        if (num_registered_fb == FB_MAX) /*最多32个FB*/
* ^% c+ P4 s5 @/ o) K0 i                return -ENXIO;- p  I2 |1 S' ^" @8 v4 Q# f

) @7 x' X: ~+ d+ [; d* W        if (fb_check_foreignness(fb_info))' X4 p0 ^& S3 H6 K
                return -ENOSYS;
5 f! e$ ^1 U4 T) m, b# K1 o
9 f; h- P* Q$ \1 n( {        num_registered_fb++;                /*对注册的FB计数*/" ]! j7 K' `1 I
        /*寻找第一个空位*/
& N* h, U* T' |        for (i = 0 ; i < FB_MAX; i++)/*FB_MAX=32,也就是最多32个framebuffer*/
2 v2 [8 q9 `9 f/ c8 p% V( K8 j  _                if (!registered_fb)        /*struct fb_info *registered_fb[FB_MAX]*/0 F; c+ H' G9 V$ N6 n$ J' u1 Z
                        break;  w0 i* e$ h: F( ]- t" w5 M7 }
        fb_info->node = i;+ u# S6 S& e1 |5 R: F
        mutex_init(&fb_info->lock);        /*初始化互斥体*/2 V0 X0 E/ y' i& p
6 U! }" Q# `% a( Q% t' I
        fb_info->dev = device_create(fb_class, fb_info->device,/*创建设备节点,节点名为fbx*/7 }2 V  |. g( \& ]+ b/ \1 b
                                     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);3 O5 `8 F% p' Z- q2 T
        if (IS_ERR(fb_info->dev)) {
# O- s8 J1 V& [7 i                /* Not fatal */
6 ]* o+ Z% s% N* B9 J2 ?                printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
( d4 a: \3 _/ M! _8 E7 d5 z                fb_info->dev = NULL;
. O0 s* T# L$ q! o# q        } else
: g% k  _! ]  x$ X1 A  _                fb_init_device(fb_info);        /*初始化,在class/graphics/fbx/下创建设备属性*/
! T3 X1 p( R) x3 H  U
8 m6 A! t0 U! p) _) E9 s: J! P% u        if (fb_info->pixmap.addr == NULL) {+ d/ Y$ k  @+ p% _, [9 o
                fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);/*分配内存,1024 * 8字节*/
1 I! s$ M: Q6 t+ V/ \) X                if (fb_info->pixmap.addr) {
+ M( E! Z0 r: p  @# D                        fb_info->pixmap.size = FBPIXMAPSIZE;4 m6 y/ a- o5 K) g) b- e4 @* A) ~
                        fb_info->pixmap.buf_align = 1;% u0 k% f$ `& g! J. w
                        fb_info->pixmap.scan_align = 1;0 W; `' J% W5 }% q: T% y( _
                        fb_info->pixmap.access_align = 32;- }# ^9 b- D$ |
                        fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
- h( T' }! n/ S6 A4 o# }                }4 n! a; b. c/ ^$ e
        }       
! |9 X% F, j3 Q        fb_info->pixmap.offset = 0;
! y8 w( n( C& w; d. O* f9 \6 s- b2 t- a( d4 M' H8 ~+ V# W
        if (!fb_info->pixmap.blit_x)
5 a+ c& C+ b+ c1 n  S7 N8 ]                fb_info->pixmap.blit_x = ~(u32)0;
" a7 C: a7 j  v' d7 n& t3 j$ K5 W% X) Q6 k3 l7 F1 J/ x5 E2 B
        if (!fb_info->pixmap.blit_y)
( d0 N- m* M9 j( \" n+ ]                fb_info->pixmap.blit_y = ~(u32)0;# y: t9 w0 C# _  ~6 ]
- E4 D& w) P9 x+ T
        if (!fb_info->modelist.prev || !fb_info->modelist.next)        /*该链表没有指向其他节点*/
& p8 D  a5 a6 j& H# A( A9 f                INIT_LIST_HEAD(&fb_info->modelist);        /*初始化链表头*/- f/ D$ K9 m+ ~, ]- C; b

2 V# c1 o5 D; X* `# i        fb_var_to_videomode(&mode, &fb_info->var);/*转换fb_var_screeninfo成fb_videomode*/
+ v* q& d2 ^5 c5 l: B+ |7 w8 l        fb_add_videomode(&mode, &fb_info->modelist);/*添加mode至链表中*/
5 O- @- f  l6 j% r* e1 P6 r        registered_fb = fb_info;9 w" I3 E3 [& \/ n
/ b, j% a7 D/ N
        event.info = fb_info;
  t: R2 u6 e" f        if (!lock_fb_info(fb_info))' A6 s0 X7 Z4 Y; D+ J
                return -ENODEV;
9 J( a* `& u' k" R        fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);/*???*/
6 P0 v: k6 C* K8 E0 ]        unlock_fb_info(fb_info);! a6 K+ D+ Z0 K
        return 0;
( m( z- |1 @3 j) r& f}
1 e. \, l2 B3 E" {* B0 e& z6 @) |, I3 @) |/ A6 C) L  q
从这个函数我们可以看出,framebuffer子系统只支持32个设备。在创建了设备节点以后,建立设备属性节点,随后将fb_var_screeninfo转换成fb_videomode,最后添加fb_videomode至链表中。
0 j2 x1 i2 G$ f6 N+ r% A- F6 B  [我们看下其中调用的函数,首先是fb_init_device。, E0 l6 f, B3 u/ V# T& e
: r, _: ?, S1 ]( D* S" i
下列代码位于drivers/video/fbsysfs.c
1 {% \8 R" d( O. ^& @9 V& {& n# J( N
7 z3 ~% V2 e- m: U+ Q2 l2 zint fb_init_device(struct fb_info *fb_info)
5 _; S, p0 j$ ^, N9 F( T0 T{
* }- I- w  o1 ?$ f5 L% V% J        int i, error = 0;
8 L+ _) a0 w& i6 x) m  l& E9 J  |; [. X9 F5 o$ g
        dev_set_drvdata(fb_info->dev, fb_info);! d9 k$ U: }$ x  k2 t2 C' g3 @

; K, c* L" ?5 R& w5 Z* J        fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;4 Q+ N- s4 _1 U% u! z' g5 y( l

/ E+ a1 c) Z: L3 Q$ ]        /*建立设备属性*/7 C/ ~* N9 Q+ v% g6 [7 W
        for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {8 l% {2 J" o5 z" {# [. M# O" Y
                error = device_create_file(fb_info->dev, &device_attrs);! p7 K$ T) h3 Z- i7 J! o( @

& H: Q- i7 {# \- _- b  C                if (error), u; J* m+ _) ]/ y) J. L' o
                        break;
5 e9 V. {( f$ @4 L( ?        }
: D' d; Z1 v  m. I' P/ {
4 E1 S' O' a2 S, S' t        if (error) {" I0 h) J0 F% U$ S
                while (--i >= 0)
+ C! l  v- G( t  d3 c* H  R                        device_remove_file(fb_info->dev, &device_attrs);
' a3 w) R- p0 }                fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
" z! J/ n8 z8 X: r6 \3 n        }* x  g' k2 V: w  s3 i) J8 ?
; L$ t* s% t, j8 h3 M: o9 k3 n2 I
        return 0;
( i7 j. P( c9 s7 T  C  f$ E4 x- ?" y}
  {7 I* }2 Q# O. g' u" l' O. L) k
/* When cmap is added back in it should be a binary attribute
9 t" o+ d' d; i * not a text one. Consideration should also be given to converting' u' \. X8 Q& h7 S
* fbdev to use configfs instead of sysfs */) b* d& U" W: ^
static struct device_attribute device_attrs[] = {
9 n  r+ ^5 C8 I) \    __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
: U4 I! Q+ w$ J    __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
* Y. {' `7 [" z* ]( I; |) H    __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),* n6 Q- N! Y  @+ y) F; t$ ]  l
    __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),' Y1 o8 X) d1 M! x1 D
    __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
" N' i* ]% I! q8 k    __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),3 H( J: O: Q( B' g
    __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),/ a$ d8 f; ~8 o, k! `/ {
    __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),* @0 q3 S$ j/ c: m  u- W% I
    __ATTR(name, S_IRUGO, show_name, NULL),
: d- q9 }' v8 G    __ATTR(stride, S_IRUGO, show_stride, NULL),) c- q$ n! _4 ^9 X" d
    __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
7 t) t* |% x7 k  I1 I  {$ i% A    __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
* E  r5 b  K4 Z; \+ a  D, N#ifdef CONFIG_FB_BACKLIGHT5 v9 F6 @9 e- i( W0 t+ B0 V% V0 k
    __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),. r" i5 i5 Q3 ?$ e, m; f
#endif
0 |2 ~2 x4 M0 m. g};$ U) [, R( F0 N- Z" O

, c8 A4 I" K# j. c* n( R我们可以在/sys/class/graphics/fb0下发现这些属性文件。; A# d2 _! i( r4 q' N' q5 [$ k7 P
[root@yj423 fb0]#pwd  |- h" W8 r: k4 g/ m6 t
/sys/class/graphics/fb0
2 h# ?% ~& b% |5 f[root@yj423 fb0]#ls3 K) |3 q" f* p& ~
bits_per_pixel  cursor          mode            pan             state           uevent9 y' h) {' W9 w* G
blank           dev             modes           power           stride          virtual_size1 f/ C% F( M( n$ q5 O5 y
console         device          name            rotate          subsystem
' B/ O5 u  D  V% O: R# w  R$ _! e1 ~. W2 I$ n
; L& o. M2 C) K( L6 l! }6 [
接着看下fb_var_to_videomode和fb_add_videomode函数。1 B8 n8 P: x. x

2 G" r2 A6 E$ B下列代码位于drivers/video/modedb.c和drivers/video/fb.h
# [8 a- ~/ W; G/ i: l8 Y# m
! b8 k1 k: o5 I, ostruct fb_videomode {. y. O# ]& R/ E% c
        const char *name;        /* optional */
5 ?3 d# N% L' v1 G+ p# X' D        u32 refresh;                /* optional */
9 p; t* F) S# T; H2 F7 X# Q        u32 xres;/ l7 N9 ^9 H* J( A4 d! K- s
        u32 yres;
8 J5 c) D9 E% f8 c% w        u32 pixclock;
/ E3 O: S8 t# q( O: G9 y- K        u32 left_margin;+ T& j6 u& j. v4 ]- ?
        u32 right_margin;% U: r& P* v) q5 k/ |2 ~4 _
        u32 upper_margin;+ ^$ @: A3 t, F
        u32 lower_margin;
' o1 G" J, d6 }# }& l3 \4 `        u32 hsync_len;
; y9 w4 @: H7 Z5 H        u32 vsync_len;
1 g) ]2 g6 N; a" i        u32 sync;; y  t3 Z* t- i: Q- }& _
        u32 vmode;
9 N# ~6 |) I4 j: }        u32 flag;/ `- ~8 Z# N$ Z9 n5 _' W
};, A( H/ b  s2 z& [( D: @# J5 b

0 f/ _" W( g+ b5 W* Y4 |5 L! k/**' H' Q4 \1 y% G: W2 P' m( l
* fb_var_to_videomode - convert fb_var_screeninfo to fb_videomode
. m: D, t; E/ K; I8 e. L- g1 p * @mode: pointer to struct fb_videomode
. n+ [+ s: p5 P! N% T * @var: pointer to struct fb_var_screeninfo
! |* f- y9 \8 }  }  ~! w4 l */) M! W) E0 L5 p" A+ u8 e" p# V
void fb_var_to_videomode(struct fb_videomode *mode,
* J6 |  A8 ?1 g0 d. u- l6 h             const struct fb_var_screeninfo *var); W' B0 K, U$ G, K2 C2 n/ B& S6 e
{
. [, t7 o$ y5 b1 c; H- \: P4 T+ V    u32 pixclock, hfreq, htotal, vtotal;) Z% T8 a7 M# o/ _, ?$ \- W# ?

( Q$ h' b( }2 y& R' d! z- B    mode->name = NULL;$ h  y' ~6 ]( C5 q1 p
    mode->xres = var->xres;' J5 r( c8 u7 n
    mode->yres = var->yres;
9 V- a( ?- v% P; J$ N& X    mode->pixclock = var->pixclock;
* A3 q7 [% ^7 H3 u4 j    mode->hsync_len = var->hsync_len;
& ~% o% z% a) j, [2 B    mode->vsync_len = var->vsync_len;
# v4 N- ^8 w( I/ T: U    mode->left_margin = var->left_margin;% E: `0 @4 r; \* Y$ t0 R
    mode->right_margin = var->right_margin;
0 I0 j7 u. Y0 N0 h$ i    mode->upper_margin = var->upper_margin;
1 A8 Y  T: S4 c' P1 I/ N, s    mode->lower_margin = var->lower_margin;3 g, D$ t3 H0 o1 {9 t& P$ F
    mode->sync = var->sync;
% l/ a( Y8 [5 j8 i    mode->vmode = var->vmode & FB_VMODE_MASK;
3 H4 u* s  `- c* V- m+ }4 A    mode->flag = FB_MODE_IS_FROM_VAR;
1 O8 H$ w! H: X9 m    mode->refresh = 0;
) ]2 M  b8 i' G1 R8 a* y" A( r' A* S+ S: u& S) r
    if (!var->pixclock)' E. K8 V3 d# d0 J: s# \* a
        return;
& w2 F8 g: d! Z2 {# y# }3 m' H6 O( S
    pixclock = PICOS2KHZ(var->pixclock) * 1000;
0 d* ^; V/ V" R8 \* M* z! f0 i* ?4 V# `% O1 p4 A0 P& S9 ?2 y
    htotal = var->xres + var->right_margin + var->hsync_len +
* Q: l6 W- w+ _$ q5 J9 U        var->left_margin;
( m# A1 h/ Z: V& \: n( X- k    vtotal = var->yres + var->lower_margin + var->vsync_len +! w: F% X. C4 J, ~
        var->upper_margin;
; Z- R$ {* Q/ I8 |) c  R# L7 f+ F: K
    if (var->vmode & FB_VMODE_INTERLACED)
  L/ w( O5 n* j; z; i  `. i3 C        vtotal /= 2;
0 k& x; i  e: W: [( g' |) z    if (var->vmode & FB_VMODE_DOUBLE)( y" a" b- C1 a
        vtotal *= 2;, [, L' z! L: X& X9 n& a" T
" D8 g  m9 r" a7 q1 M
    hfreq = pixclock/htotal;
% w5 V, g0 t2 C" U- A    mode->refresh = hfreq/vtotal;
! f& s$ b  J" j+ r}
5 m! y7 t6 s" v" O1 j, t$ x, V* A6 r5 c: y0 m$ @2 O
/**
9 O9 L6 O. N; i* m * fb_add_videomode: adds videomode entry to modelist
) t/ I6 _. T. |  I * @mode: videomode to add/ U$ L0 @# g' S6 w
* @head: struct list_head of modelist
; `/ s* ?: \; ^6 [ *
' I3 U: W8 B2 B( N * NOTES:1 B: N. w4 D$ ]. B6 a8 H
* Will only add unmatched mode entries; n$ Y; j$ d! z! d( Z% j. B" Z
*/
; ?6 f; W0 q4 }7 H& z- [( W) o  Eint fb_add_videomode(const struct fb_videomode *mode, struct list_head *head)
$ E, l4 L5 u5 c) z* p1 G{5 T9 z+ u$ h+ y4 ~  I
    struct list_head *pos;, }: V0 H" `7 Z+ f$ V5 v* G/ N
    struct fb_modelist *modelist;d
, A* Z6 F  `4 }    struct fb_videomode *m;3 a. Y: G) T& p
    int found = 0;
6 }, n& S9 u: A# l6 u- T    /*遍历所有的fb_modelist,查找mode是否存在*/6 _. A* J6 d, U, F
    list_for_each(pos, head) {
9 P1 f  V" T' L        modelist = list_entry(pos, struct fb_modelist, list);
1 Q0 N( C! U" I9 n1 V, w  c* z" Q1 n9 _        m = &modelist->mode;
1 X& E& u; e/ S$ M! \        if (fb_mode_is_equal(m, mode)) {    /*比较两个fb_videomode*/
: w0 H# @4 J- M+ }1 l7 ?" ]% o) S# [            found = 1;                        /*该fb_videomode已存在*/
( [5 _6 d7 ^5 I% U& D5 z6 R* \& }            break;
$ w% z& Y9 ]7 s8 w        }
6 j; P' z% V, e" F2 @7 |% C  d8 D2 A    }  z$ a! Z7 X7 R$ Q
    if (!found) {    /*不存在*/
1 y5 L5 ?9 m* @, O# H: w$ V        modelist = kmalloc(sizeof(struct fb_modelist),    /*分配fb_modelist*/: W, N) O& t3 x8 W# ~5 t, d
                          GFP_KERNEL);0 A. [! K0 k0 K6 g# a

6 p3 x2 b4 ^! [5 Z# {; A        if (!modelist)
1 E& D, S5 L) C/ ~: m; h            return -ENOMEM;
0 ^' l/ M, ]8 h# M8 D        modelist->mode = *mode;            /*保存mode*/
7 J$ U/ K, T% H. @5 k6 s/ H        list_add(&modelist->list, head);/*添加mode至链表中*/3 A% E: \- e6 @3 _' ^
    }
/ H$ ?6 k: B; y1 ~( k    return 0;
- D% \/ I% Z' E% ~) @3 X4 q}
, M* k  b3 Y- k, f% S4 [( ~% W5 t$ I4 v1 f
/**
: c* `* K0 ?& z# s* m * fb_mode_is_equal - compare 2 videomodes
4 P1 v" y3 D& O) e) U * @mode1: first videomode
5 G  Q* C/ m* R * @mode2: second videomode
( i+ ?4 R( K& @( k6 f+ H4 {: p7 j *1 O: h# I% O) J8 w
* RETURNS:
: Q3 v7 `+ n% z6 @ * 1 if equal, 0 if not; ^$ |& J2 |- n& \, e" j
*/
  _# `1 x. L/ d- l* Q, k; v) w% Gint fb_mode_is_equal(const struct fb_videomode *mode1,/ V8 A5 K* D+ K+ @* U
             const struct fb_videomode *mode2)0 y" p1 i* n7 U4 s' Q
{
9 ]5 ]# [  f5 w' `+ M) l    return (mode1->xres         == mode2->xres &&
( w1 ?: p& ~7 Y; H0 _        mode1->yres         == mode2->yres &&0 ]* Z: g: ]7 Y4 W
        mode1->pixclock     == mode2->pixclock &&  @2 d8 d9 g" L2 l
        mode1->hsync_len    == mode2->hsync_len &&
& ^0 i/ u8 [0 v$ G  \        mode1->vsync_len    == mode2->vsync_len &&
$ ?( a7 [/ V/ e, G, m        mode1->left_margin  == mode2->left_margin &&3 V+ s6 ?# m- Z. }3 y! g
        mode1->right_margin == mode2->right_margin &&% f9 `& \6 n1 e0 ~9 a
        mode1->upper_margin == mode2->upper_margin &&
7 `! e; k1 J/ J        mode1->lower_margin == mode2->lower_margin &&
+ \  }/ t( N3 u8 [$ p9 k% C: Q        mode1->sync         == mode2->sync &&
6 d2 C3 n8 W2 ^6 O: r. o7 x        mode1->vmode        == mode2->vmode);
* k" Q2 G# J2 l& K, C) [}
: ~' H' q- ~6 ~7 E$ @$ f8 z* {( J& S! [
6 C6 p' O% ]: V7 b6 u" z  r& V0 F
fb_var_to_videomode函数只是将fb_var_screeninfo结构转换成fb_videomode结构。而fb_add_videomode函数查找是否该fb_videomode已经存在,如果不存在则添加到列表中。& w5 @4 I8 I2 R' d4 f2 A( }
3.4 字符设备方法
7 h- S6 H/ V* g. @# r6 ]  在看过framebuffer子系统建立和注册过程后,我们看下framebuffer留给用户空间的API是怎样实现的。
0 t* ^& k% U) G( U9 |8 I$ O. ]+ G  J. b
本小结只分析5个常用的方法,即open,release,read,write和ioctl。. T; E0 L  y$ X, B& s+ A" s

! |+ |: J4 @2 T. W  因为所有的方法和struct fb_ops定义的方法有紧密的联系,而该结构的定义由驱动程序给出,在这里我们提前看下在驱动中是如何定义的。' M6 P; t6 Y, n) \9 N5 m

8 f0 a5 h" t* l3 q7 ?  h9 Z 下列代码位于drivers/video/s3c2410fb..c
+ F6 [/ Y, o2 @8 G" a3 X2 Y7 m
. |+ d9 L& T. _9 B9 @static struct fb_ops s3c2410fb_ops = {
; N: e, @; [! \        .owner                = THIS_MODULE,
, A$ p# `5 J* Q        .fb_check_var        = s3c2410fb_check_var,                        /*检查变量的合法性*/3 l8 ?6 N4 x8 g3 {  S1 n4 d
        .fb_set_par        = s3c2410fb_set_par,                        /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/
# H2 ]3 J7 t# y8 E, {) _, Z* X        .fb_blank        = s3c2410fb_blank,                        /*该方法支持显示消隐和去消隐*/, F, O- [8 n1 [/ \1 y
        .fb_setcolreg        = s3c2410fb_setcolreg,                  /*设置颜色寄存器*/
. D' w& p' C% C4 D. r# ?        .fb_fillrect        = cfb_fillrect,                                /*用像素行填充矩形框,通用库函数*/
" I( p8 ]2 m$ V! o7 I# p        .fb_copyarea        = cfb_copyarea,                                /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/: G) J/ e5 m0 @9 U9 b" F% W
        .fb_imageblit        = cfb_imageblit,                        /*显示一副图像,通用库函数*/. L; W: A* p4 y3 y
};
/ X& Q5 [/ Y3 m' q( j. @6 k# r最下面的三个方法使用的是内核提供的库函数,而上面4个则是由驱动提供。, _5 T3 _+ ~" }8 [, k: u
3.4.1 open方法
/ ^1 o# R0 c! c5 M+ v9 Y& m9 k下列代码位于drivers/video/fbmem.c! a$ G9 V& h) B: }8 c
5 U8 z; O3 b3 e
static int) {8 W" ?# b7 n
fb_open(struct inode *inode, struct file *file)
8 z/ x# E/ g4 S/ |5 L__acquires(&info->lock)
; n. ~: w6 J6 O( u, o( [__releases(&info->lock)
* ?6 \7 s( Z7 b1 i7 @{2 H$ y- V: ]" x- `1 E) t7 Z
        int fbidx = iminor(inode);2 a. |' f! W& u' v' B6 u0 Y% L
        struct fb_info *info;* K* J0 Z- r, H# E
        int res = 0;
- ]# w; ]! J, C3 k
7 K4 @7 M+ I2 E; W  r- r4 f8 K        if (fbidx >= FB_MAX)" c- M: I7 n/ D. @9 j) o
                return -ENODEV;  }7 Z$ c0 n$ H7 D. ]/ s" [
        info = registered_fb[fbidx];                /*在register_framebuffer函数中已经设置了元素*/4 s: D) }) l& U
        if (!info)
! ]7 i( G1 V, h; U& Z& }! ]+ G. f                request_module("fb%d", fbidx);        /*加载模块,这里不加载*/# r, n* T, ~3 C, n1 Y: y) ?! @
        info = registered_fb[fbidx];0 N2 i. w$ p; h4 C$ `
        if (!info)9 w4 D+ K5 Z7 y; @
                return -ENODEV;
- e; w2 k+ u: e4 `3 A" u0 O        mutex_lock(&info->lock);                        /*加锁互斥体*/
- m' j  N3 Y1 ]8 L        if (!try_module_get(info->fbops->owner)) {        /*增加模块引用计数*/
+ ~- }) Q  H( T7 f; h                res = -ENODEV;7 x2 e0 a- Z7 ?, \& S' t  t
                goto out;$ ?0 t. f# x; f. Y! ]
        }
  z% O# `. g# P, C, P1 p        file->private_data = info;                /*保存info*/! |7 L' U, \; |- T, i+ Y- B
        if (info->fbops->fb_open) {                /*这里fb_open方法为空*/
9 g7 u1 {6 O7 q5 _* q                res = info->fbops->fb_open(info,1);( v' @: |& c8 R, P9 l* ^0 d2 ^+ ]
                if (res)
- T$ y# @$ L8 [$ b4 A% c                        module_put(info->fbops->owner);2 H/ E& Z7 }& T
        }# o- B8 a1 A6 i$ y
#ifdef CONFIG_FB_DEFERRED_IO
: N# c5 H6 p; H2 }5 S" c        if (info->fbdefio): F: B. F' I- l. u
                fb_deferred_io_open(info, inode, file);
7 ?- V, t) j  @  |4 y#endif
3 Z/ T6 k% ~, ?) p" k8 m- T: J. ~out:
8 Y" j* y3 z4 i  }  [9 o" o% A/ y- |        mutex_unlock(&info->lock);                /*解锁互斥体*/
, u) _( ~0 C7 ~, S4 t; V) }        return res;3 E- r( Q& k# n/ N1 s
}
) Q  l( w) S; [  X* {主要的一个工作就是增加模块引用计数。还有,程序会判断是否fb_open在驱动中给出,如果有则调用该方法。我们已经知道fb_open没有给出。4 t; z5 |/ b9 N3 G6 p8 y
3.4.2 release方法
  U! s0 M9 v# I( j# u0 {' J0 G) J( ~; u下列代码位于drivers/video/fbmem.c7 A& D5 t' t" f. p
( y: Y! d& _4 }
static int , s* i* x4 K! b% U
fb_release(struct inode *inode, struct file *file)
* G" o$ k4 `9 U$ z__acquires(&info->lock)
5 b- g+ C* y! j, Z* d. [7 R__releases(&info->lock)
- j: S# E& i# d0 K3 E5 z$ l  M$ a{
7 _) ]0 W+ }" s6 P% S# w6 u$ n        struct fb_info * const info = file->private_data;* W3 L7 L$ i' a$ k

9 L8 z" P/ Q. R        mutex_lock(&info->lock);, I, O9 y! G1 h$ ~
        if (info->fbops->fb_release)        /*这里fb_release为空*/* A( Q; V6 Q: |9 v3 A2 b8 F
                info->fbops->fb_release(info,1);( B: l$ C3 n5 \* y' Q) w! b
        module_put(info->fbops->owner);        /*减少模块引用计数*/
1 m, \: W6 `( \) N3 J        mutex_unlock(&info->lock);
) L9 t8 ?1 \$ C+ P3 r5 I        return 0;, Y" Q" V. b3 m; @
}
2 c3 {3 J/ o' r! e3 P: ^和open相反,减少模块引用计数。) L$ |% {* I2 x$ v
) S4 Y' z1 J/ B) o, b
3.4.3 write方法
6 t( ^* L. |7 \* V' k4 h  通过调用该方法,LCD将显示画面。
+ @" U. H) e9 I1 J  N; D7 k& d6 K& B/ d) u: Y& A
  下列代码位于drivers/video/fbmem.c
; q1 s/ e* R3 @/ X+ H
. j; |; ^8 P, H) r9 J9 `8 o4 E6 sstatic ssize_t
; L6 `9 N$ F1 y$ C9 afb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)5 x4 g$ Z4 X3 `1 \
{; p9 Y8 Q; P% e7 Z6 x! ~- H
    unsigned long p = *ppos;
4 W! a; R- L  x) d9 a* o    struct inode *inode = file->f_path.dentry->d_inode;
# c% u4 p% }; s    int fbidx = iminor(inode);
' p" F( q! Q3 e8 o+ O    struct fb_info *info = registered_fb[fbidx];
6 U5 }5 e: X1 n+ U. X    u32 *buffer, *src;
8 a2 u# @; O9 u; l0 Z3 S    u32 __iomem *dst;
' `5 L, T. G4 y    int c, i, cnt = 0, err = 0;; R# K! I) k; ~4 v/ A0 P( [
    unsigned long total_size;9 L$ o7 V) {: T. c

6 W) [0 X$ q( g* ?9 }    if (!info || !info->screen_base)    /*screen_base在驱动中给出*/
* _+ E9 t, b6 y4 m! |1 V        return -ENODEV;5 a; b, Q$ s' j8 j& b- @7 M

7 G5 {3 p+ z: w* l- r1 e1 e5 |! `    if (info->state != FBINFO_STATE_RUNNING)
2 l+ n( g. L" R9 e% y. v3 `3 k& n        return -EPERM;
% z1 |- Q" }7 U! F1 Q  g2 o; @3 t# F1 Q; Y5 u3 g
    if (info->fbops->fb_write)    /*没有fb_write方法*/
! D3 V5 b# h% W        return info->fbops->fb_write(info, buf, count, ppos);
- n2 o: w0 f$ |$ `& w: e- C; O4 q3 r( P; @
    total_size = info->screen_size;    /*screen_size没有给出*/
8 _2 i) U  c: z# q! X: `  Z6 F% L% M8 a4 Q" y% U
    if (total_size == 0)
: Z9 \. d6 E* R        total_size = info->fix.smem_len;/*153600字节,驱动probe方法中计算*/# s8 v! n& m8 G$ F7 @9 Z
" O+ `3 w" J" u# d% t: `& R
    if (p > total_size): x" C0 ?- Q/ N. e3 Q
        return -EFBIG;
) Z9 l8 P' D  R; `6 s- F
3 a1 h$ J1 R$ p* k    if (count > total_size) {    /*要写入的字节数大于153600*/# k( _+ x* e! @8 ?7 V& y+ J6 O
        err = -EFBIG;        /*file too big*/# d" `* ~: [. t$ k$ f9 @
        count = total_size;8 p& F% C6 G: |& @4 z/ I* j) V# k
    }
5 S) V, a0 A0 z! I' O- `$ s. Z
) s( q" q1 K7 D1 q7 \: x    if (count + p > total_size) {/*偏移量加上字节数超出了缓冲区*/  v4 R4 u  f) k$ G2 V$ z
        if (!err)5 Y: d6 R/ w( |* r- W7 x4 c2 E- d
            err = -ENOSPC;
& C) K/ k9 h9 g3 a/ h4 F7 `$ |
$ J) X6 g' w5 m8 M1 P" m        count = total_size - p;
5 k5 C4 z0 f. F3 C    }4 b, m6 G  F! n/ U0 x
: l. W* @% u6 s1 f9 V3 M3 r" J
    /*分配buffer,GFP_KERNEL*/
- x7 g# x; I: b" H) X2 `    buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
+ h) \( W8 S0 E% t             GFP_KERNEL);                /
3 i1 u9 p/ q" Q( F* W    if (!buffer). w( @4 ~8 r1 R. Z
        return -ENOMEM;  |1 z9 S4 F3 _! j

# h) c( Z6 i# w/ U1 k    dst = (u32 __iomem *) (info->screen_base + p);/*修改目的指针*/
" v4 H& U: o1 G( i) ~& O
! B! k( x$ ?- `/ L    if (info->fbops->fb_sync)    /*没有定义fb_sync*/+ O* _$ P# o. G$ E
        info->fbops->fb_sync(info);
, h0 o6 I$ R/ V$ `! Y1 q5 W3 Z1 X9 L; Q- b
    while (count) {
$ _2 \" h$ [( _" O) ~- g8 J        c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
! M9 V8 L; b; n        src = buffer;
% a  i7 q5 @1 v; x# U5 M
1 Q1 ]8 E5 ?  y5 {1 n- q- @        /*从buf(用户空间)拷贝数据到src中,一开始为1页,最后为count字节*/
% J) J- }6 m9 d( ]2 J        if (copy_from_user(src, buf, c)) {    . ]' B) B! y# c  {
            err = -EFAULT;* Y' Z1 O% [+ }* S8 S" |
            break;0 |3 F4 P7 S1 f% [; M9 G1 h
        }
8 e4 b8 k4 z9 G: c; l( S; a        /*一次for循环,写入4个字节数据到dst处*/' A5 L8 H  i6 d
        for (i = c >> 2; i--; )9 z4 B( {. L( q) T( B/ E! G% _4 Y
            fb_writel(*src++, dst++);& [4 y3 y# t; j& D( o9 O
        /*最后还有3个,2个或者1个字节*/
! |- B1 `& g# \% n/ d. a        if (c & 3) {
1 {2 i8 y8 b( F6 d, ]            u8 *src8 = (u8 *) src;
8 m! e* j0 Z$ @  ~) J7 A            u8 __iomem *dst8 = (u8 __iomem *) dst;# ]! |0 E( m8 I4 V) G
            /*一次写入一个字节*/
; `1 A! F/ ]$ Y% ^7 @            for (i = c & 3; i--; )
- f" N- M/ q. Z; M; ]                fb_writeb(*src8++, dst8++);. S1 Q. u, V, @: Y% x0 k  {3 C& O
- H4 ?! ~9 ]# l7 W3 K) [5 S
            dst = (u32 __iomem *) dst8;% Q& U3 F  n0 Y
        }
% I! J- l3 p, q6 b, U" J1 y. b( a* K! m" B0 T) b* o. w( v
        *ppos += c;    /*用户空间偏移量增加*/
( X: U4 k7 l8 T        buf += c;    /*用户空间指针增加*/' Q% ]" Z8 u8 I( R* \$ b
        cnt += c;    /*修改已发送字节数*/
3 G  b  j: ~# y: [5 ~; z5 [        count -= c;    /*减去1页*/
+ }6 |5 {2 m, \    }
" S2 \: p) A7 _% l0 x& P6 B- G- D; n/ [- T9 h/ _* C. e2 `
    kfree(buffer);    /*释放buffer*/
2 k6 g' Q# k. J! A* O$ |% H! M' F+ Y% ?: z& x
    return (cnt) ? cnt : err;
" m7 c1 `9 ]& P( v6 ~( z6 _* _}
2 r) D* W% }0 H) l$ i! V+ P0 u1 {4 Z
这里,做了一系列的检查之后,开始拷贝数据。这里一个有三个buffer,一个是用户空间提供的buf,一个是在这里新开辟的buffer,还有就是驱动层提供的screen_base。
, t7 c! q  L0 T1 W# d数据流如下:
: v  T. d3 {8 h2 T6 \# T( q* a: Z
' `. P  D' h# |9 s4 ^$ M7 f  D( e" W
  i9 @. [4 L9 L+ u6 ]3 m$ ^( E" \  U2 E9 i
用户空间的数据首先被复制到buffer中,然后从buffer中复制到screen_base中,最后被映射到LCD上,LCD就显示响应的画面了。2 L' E$ D& z0 w8 }

2 C( W" \+ e, }- g" j+ e3.4.4 read方法- ?. J* E& |8 W5 m
该方法用于读取屏幕画面的数据。9 z  {- C$ b& v) v! L3 s/ C7 k$ P

3 h' Q1 o) t& t+ S: A. w8 Q) Fread和write类似,只是数据流是反响的,就不多做介绍了。- {: z* V2 E5 A! l, i- h4 D/ |4 F

5 O. }) b; }% }# T' r下列代码位于drivers/video/fbmem.c
9 y: H4 f8 V1 V! |1 ]) ~' W0 K# v8 h7 I6 v
static ssize_t
3 @6 J# G. Z- e& [6 ifb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)4 J" o& I' t; d: D1 x  j3 w
{
. L' T! e  F( p0 I4 J% _- f        unsigned long p = *ppos;
2 a8 A9 Y. S2 |$ v3 I" f        struct inode *inode = file->f_path.dentry->d_inode;0 }& O  o$ o5 E6 \/ T! E8 s; Z
        int fbidx = iminor(inode);
7 g9 P5 d' E0 u. `+ W$ Z4 z, J        struct fb_info *info = registered_fb[fbidx];
- m, h$ M+ U% R4 x9 Z        u32 *buffer, *dst;
9 ]7 |9 b) U. A8 m, }# E- r. ~0 X9 j        u32 __iomem *src;. @' I7 ^, X0 f8 o4 m
        int c, i, cnt = 0, err = 0;
- b' |& U4 s' ]* h1 \, ?5 e5 l        unsigned long total_size;
2 E8 u' S: \6 t# j5 z) L  ]5 G' U6 }3 [% ^
        if (!info || ! info->screen_base)( ?" p; L$ a/ M- ?# }
                return -ENODEV;  S* [; f* e- E) _

8 {7 g5 M, k7 i) _        if (info->state != FBINFO_STATE_RUNNING)" @# ]. s% w7 G2 b$ j8 P  c6 G
                return -EPERM;8 f! d2 P5 `' U" x* i( c
  Q/ r- t1 n1 x  l# k* t
        if (info->fbops->fb_read)        /*没有定义fb_read*/
, X$ r3 n* P2 e$ h: h                return info->fbops->fb_read(info, buf, count, ppos);
! H5 _- b( W( M7 X+ _* D        - x$ f# y4 b, w4 O3 \, k/ i8 Y
        total_size = info->screen_size;
& J4 N8 H: s' T: m* O
. d+ G& p; H" A( C& K        if (total_size == 0)9 Z: J9 h. j+ F- q
                total_size = info->fix.smem_len;% e" _! q0 Z/ X9 h# w1 \+ \; n
0 X- O" P6 v4 I
        if (p >= total_size)4 ~. M' `, E& A
                return 0;3 ]; X- h2 |( D( f4 H
5 a3 U. V; A7 W' j- {; v) s
        if (count >= total_size): g0 F" m5 X+ F) h' ~
                count = total_size;& D+ j0 {# f' G* y
2 U9 q! \, |( }( B6 a& d7 y
        if (count + p > total_size)* ^0 T& J3 Q4 G' }
                count = total_size - p;+ N: g, y* N/ ^* @4 y
. l" m. P) ^. ?  V! S
        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
, e, b7 @& U1 E1 K2 N! t                         GFP_KERNEL);
: q( ~/ I; R; u1 [) A        if (!buffer)& l( Z2 q8 `5 ]- Q% w; [
                return -ENOMEM;
- T7 e9 E7 N6 o
1 F8 Y/ I: e9 L        src = (u32 __iomem *) (info->screen_base + p);
1 V% x' y8 A! @8 t4 ~
* w! y, p" r3 Q% ]; p6 b        if (info->fbops->fb_sync)! t# m, b' Z0 f/ z2 F$ l
                info->fbops->fb_sync(info);/*没有定义fb_sync*/
  P( w7 s; p1 O  ]9 |' T' S4 z
+ T5 D- @( y; j! s! |  g        while (count) {
( ^8 ?7 M8 O0 f; E1 @, y                c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;
( a5 R- }, a$ c( a- Z, }                dst = buffer;4 Y4 {. q  x& |9 O
                for (i = c >> 2; i--; )
( |4 `9 k! J' |$ g. |' U; e5 v+ r, n8 F                        *dst++ = fb_readl(src++);
; ^5 E5 a& |# ^2 F                if (c & 3) {7 q# Y. q9 K& `9 X
                        u8 *dst8 = (u8 *) dst;
; e' e8 O, s  s$ u/ Q                        u8 __iomem *src8 = (u8 __iomem *) src;
& e! o3 Y- d: X/ K# N% x- ^% ~) F( s8 n4 _0 b
                        for (i = c & 3; i--;)
. y. k1 _9 A" s  M6 S. U- D- n                                *dst8++ = fb_readb(src8++);: {# P2 }- [, h+ k: O6 N
& [0 `; D' f7 }! m6 p8 S
                        src = (u32 __iomem *) src8;9 a, s* ]2 [/ R, u2 ]
                }3 e3 j3 h6 D2 Z
  a& K: i$ K+ C/ b
                if (copy_to_user(buf, buffer, c)) {& r" m" [5 h" l
                        err = -EFAULT;
0 J/ g/ |0 R- O, z# c; p3 l                        break;7 p- I7 i+ L/ L5 [- R+ u3 ?& D
                }$ c7 U7 j4 m. j# j# t
                *ppos += c;0 b; [1 F5 S6 s( K
                buf += c;
% ^: X7 V' c' K. u                cnt += c;
3 A; H' j% V: ]6 j5 G3 R& \                count -= c;- e( l8 O- D$ v# t4 ?
        }% k8 z& m" V* m4 r" U: l, t4 g! _

( d: \! r4 [5 V4 K        kfree(buffer);( @" f. z7 `% Z/ |9 g& ]7 @
+ Q  c6 l/ Q1 j1 R* e% k( L9 \/ y
        return (err) ? err : cnt;
1 B% j3 p, _  k  N- P" G& |}
$ N0 k/ C6 z  @$ f+ [, M1 e+ h; L3.4.5 ioctl方法6 [6 S* E# d; n( n
这里只是简单的看下ioctl方法,这个函数调用很多其他的函数,详细的请自己看吧。
& X+ a' P: n9 G) q( L! O6 r) T
& V( U5 M: L0 X1 {6 w3 C5 a" C: M下列代码位于drivers/video/fbmem.c
, Q" U, H5 K3 Q1 @3 E$ E, l( ^5 u& c4 X' w# r8 M3 y3 [
static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)" W0 E8 q5 ?: y: f' I% q1 h
{; h0 G- Y7 I% F" p( v3 o3 v
        /*获取inode,再获取对应的fb_info*/9 `7 m: f" R$ C& ]  d6 V
        struct inode *inode = file->f_path.dentry->d_inode;
& P  N6 a5 q/ X: ]        int fbidx = iminor(inode);       
! R& v9 e5 V" w8 B8 }        struct fb_info *info = registered_fb[fbidx];" V- D/ f5 i! Z. e* q
6 B$ l$ F  i6 q! C. {  B8 @
        return do_fb_ioctl(info, cmd, arg);
* V% U0 Y+ q1 `; y}
3 L6 ]. R& i& E$ }4 W: ]* ~: L# ]; m
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,# w5 F. l& h5 t1 X3 @$ l
            unsigned long arg). Y8 J$ m( N1 }- L( D0 P' O
{
6 N9 w. ]) Y1 D: W4 D8 p    struct fb_ops *fb;
  }! Q& c! Y- _/ f9 ^. v    struct fb_var_screeninfo var;8 h8 _( N# Y; h+ ?% H
    struct fb_fix_screeninfo fix;
: B, Z% T# s, }* J* R' Z    struct fb_con2fbmap con2fb;6 n# ~8 `! X6 a+ ^/ b9 o
    struct fb_cmap cmap_from;
( }1 y  f" w- |+ B8 c# d    struct fb_cmap_user cmap;1 n; J; V" h5 d0 w$ _+ f
    struct fb_event event;
! I9 x6 J$ A, N; E    void __user *argp = (void __user *)arg;
# R! p* |6 `6 ]    long ret = 0;
3 J5 O' g- `4 E; J& o7 t, a) `# D3 A2 N9 y7 t
    switch (cmd) {* i4 C8 Z9 ^1 E+ [
    /*获取fb_var_screeninfo*/
+ T- N  L4 O3 `7 k7 d- e    case FBIOGET_VSCREENINFO:   
6 x! y! i3 L. M* M6 T        if (!lock_fb_info(info))    /*加锁互斥体info->lock*/% i$ r/ b: \; l  p% J1 ?8 w! u
            return -ENODEV;" z$ \0 u: O: C
        var = info->var;            /*复制var*/2 K% ^& ]7 o9 |* P
        unlock_fb_info(info);
9 m/ K9 E/ u% i' _4 U: W  U2 Q" L& b/ J, R: T4 v! G) t
        ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;    /*复制var到用户空间*/9 l  y1 O- p, Z+ t
        break;4 k" _) Z% W! C8 ^
    /*设置fb_var_screeninfo*/. ]9 {# w% v) H& j8 n0 R
    case FBIOPUT_VSCREENINFO:
( P% c/ o( ]' W/ X3 J" [3 T        if (copy_from_user(&var, argp, sizeof(var)))    /*从用户空间获取var*/
; k" j: i0 U# l8 }            return -EFAULT;
1 n& G1 o) B4 g5 x8 `" n        if (!lock_fb_info(info))
6 T0 g) E. L: i2 C            return -ENODEV;" t- Q. R8 v, n+ G( D" D. o$ Q
        acquire_console_sem();
. _( S" D( x. I: \' E$ i- g5 s) W( S        info->flags |= FBINFO_MISC_USEREVENT;
3 w* H( d2 R0 O8 W        ret = fb_set_var(info, &var);            /*设置var*/
: @& o' v  A6 j. F2 R        info->flags &= ~FBINFO_MISC_USEREVENT;, C) S* w# r! @  |- b
        release_console_sem();* E! T! h1 s; n9 C; X
        unlock_fb_info(info);
1 q# H6 o9 G0 a* {* q$ Q        if (!ret && copy_to_user(argp, &var, sizeof(var)))
( y3 D9 S6 x) G; y# J, H+ O            ret = -EFAULT;3 b0 ~: p" ]# c8 @! d
        break;
2 K( n6 v! y: U8 i) A* L    /*获取fb_fix_screeninfo*/    /*fix为不可改变信息,只能获取,不能设置*/3 ]# U+ [/ k# N+ _
    case FBIOGET_FSCREENINFO:
2 ~8 k/ e4 X" F5 }+ r4 _        if (!lock_fb_info(info))
3 {: e+ a, `6 d3 V            return -ENODEV;$ Q; L% T. a  [7 o
        fix = info->fix;
8 f9 K# ~: s! E1 F        unlock_fb_info(info);
% z) h9 h$ ?# m; `9 T( v7 f; Q8 m6 H  T& L, }
        ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
, u* Z& m2 _! b4 J+ v; |        break;. E0 B9 r. M  X! S
    /*设置fb_cmap*/    + U, |: \9 R5 s% Q
    case FBIOPUTCMAP:
2 P1 G- v1 U4 r% a* J  G' @$ b        if (copy_from_user(&cmap, argp, sizeof(cmap)))
0 p8 o  G$ G5 X/ O            return -EFAULT;
3 P/ D, h. {7 r2 v/ h        ret = fb_set_user_cmap(&cmap, info);    /*设置fb_cmap*/. i6 [( ~: W4 m3 ?
        break;
' [9 j( }% e, U- E, E4 r9 T, X2 ^* @    /*获取fb_cmap*/   
& i9 w/ J" \+ O$ h    case FBIOGETCMAP:1 Q, Z$ ^& e. o" `5 I
        if (copy_from_user(&cmap, argp, sizeof(cmap)))
6 I/ R1 S- ~% W6 {  e            return -EFAULT;
/ @* I5 I' }$ |) o& U. ~! q        if (!lock_fb_info(info))
: p9 a8 V- b! O2 z* j5 d( j2 {            return -ENODEV;
( B* r# D/ ]" Y6 `5 u) \( W        cmap_from = info->cmap;$ O4 ~9 j; {8 g" Y! O) r
        unlock_fb_info(info);9 J5 L% P4 p5 R8 r" s5 U
        ret = fb_cmap_to_user(&cmap_from, &cmap);/*获取fb_cmp*/0 ^6 N. v3 c- i. J# S$ y5 u
        break;
/ ]5 H% x6 u) m/ Z5 Q    case FBIOPAN_DISPLAY:
  `/ T; }* k# r. v        if (copy_from_user(&var, argp, sizeof(var))); n$ G5 |7 A8 [8 b
            return -EFAULT;  X0 s/ m  q# z' }3 ~# A9 _4 r
        if (!lock_fb_info(info))' A# D+ S7 d! {6 J: I2 w
            return -ENODEV;- r5 z$ A0 G; ?& G( o& E
        acquire_console_sem();& ]+ ^; U; z) E  T- k
        ret = fb_pan_display(info, &var);
! c2 N  f& o7 L# m1 e        release_console_sem();
* f5 _, R3 K% v4 m, \        unlock_fb_info(info);4 p/ k% h- k" k# ?" n- l& W. M
        if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
; \* u2 c: g/ I1 r            return -EFAULT;
3 q' k$ W% }+ M- g  _2 z        break;
  T" V( t5 M+ h4 l$ H    case FBIO_CURSOR:& r; F+ L% o5 z& ]0 {0 w; n3 ~
        ret = -EINVAL;1 X* A" O/ B1 W' Q" ^
        break;9 p0 \' m1 ?# e$ P0 Y* Q* t
    case FBIOGET_CON2FBMAP:. z  x1 O' G4 Y7 P" j8 P
        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
; ~2 ?1 U# m" X$ m. M            return -EFAULT;
- B1 r+ L! n6 _( H) d" L        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)0 A7 ]% {$ U+ X6 M; q
            return -EINVAL;
3 [% Q/ C( _* ]7 D6 \, x1 l( ]        con2fb.framebuffer = -1;
: D" R" p) K+ x; F. }' }. ]8 {        event.data = &con2fb;
' A2 N7 t) l( m1 \4 P        if (!lock_fb_info(info))
0 C* p& B- |. n& ]6 t            return -ENODEV;8 z4 H( R% P, S$ _- _6 C
        event.info = info;) g& l. I+ s% c6 v( R' m6 t; h
        fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);6 P4 h8 R, `+ l; y) e4 W' ~
        unlock_fb_info(info);
" n0 Z$ l  O1 z" d6 ]& K9 R; k& E2 T        ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
+ j# a0 N2 a/ Y+ m5 m4 ~        break;3 j% y/ S- m" x6 O
    case FBIOPUT_CON2FBMAP:! e8 H8 \1 W  R, @7 p& O
        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
6 V2 r2 m6 X5 c# o, O2 X# v            return -EFAULT;: n: n6 ^" j4 U
        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)5 [! y& \+ `5 \. V. r
            return -EINVAL;! V- u& m/ e, s. W' _% |5 @/ L
        if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)5 W4 `/ c8 z; \
            return -EINVAL;
4 B( n/ p) j/ T+ |& ~- s: H1 F        if (!registered_fb[con2fb.framebuffer])! i( N% [( m: t# ^
            request_module("fb%d", con2fb.framebuffer);
5 ?! w' }! j# o; v& p        if (!registered_fb[con2fb.framebuffer]) {
/ r* [/ v# ^! f8 @            ret = -EINVAL;6 a$ W- |* _% J" E$ ^
            break;
2 Z) b" g7 t& u( h+ L8 M9 f- H        }* }) k! l% E: N1 R
        event.data = &con2fb;
) p6 q4 T- y8 v& X) p0 P! x  k  y8 S        if (!lock_fb_info(info))
1 Y3 [) z5 x2 A( I6 l            return -ENODEV;
( S% z3 V7 ]! q  p6 q        event.info = info;* q* B9 C( x9 A$ o) J; V. k
        ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
8 ]  \4 C: A1 i9 I8 \        unlock_fb_info(info);" {1 v4 j; ^6 C+ ?7 b
        break;% b! e2 d3 H5 A/ ]
    case FBIOBLANK:
+ O. f' I5 y0 \& Q) Q1 O        if (!lock_fb_info(info))6 k; U" t* a2 r9 y
            return -ENODEV;# |5 k& G; ]% T( C- ^( |
        acquire_console_sem();' K) _6 K8 i5 L8 r( ~
        info->flags |= FBINFO_MISC_USEREVENT;
) e( z  Q7 p4 w  W3 k  X6 ~        ret = fb_blank(info, arg);        ?*最后调用驱动提供的s3c2410fb_blank*/2 ^4 v! Q. d& h3 E! \
        info->flags &= ~FBINFO_MISC_USEREVENT;
* A. P% Y/ i, t3 ]9 U) C        release_console_sem();; m3 D+ t) g: _4 [% a, p
        unlock_fb_info(info);
0 R& Z. T, H7 t" O  M8 Q. k        break;; ?$ a( b! E4 P" z) K
    default:
2 }4 H5 `  \7 T1 m        if (!lock_fb_info(info)): E0 q; e" [- g2 k) }) U
            return -ENODEV;- G! \# S! m! `+ J
        fb = info->fbops;
4 t: y: @" S* W. A9 z7 _        if (fb->fb_ioctl)        /*fb_ioctl为空*/
4 Y& T0 z9 U/ l            ret = fb->fb_ioctl(info, cmd, arg);" @( M5 E; Y! }3 e) d- a, r' L
        else
/ T1 C) I6 L: `5 Z! V" Q! b            ret = -ENOTTY;
; k" j8 J: I# O' Z6 c( w        unlock_fb_info(info);. T6 C0 R# R! {, a, G
    }
: t/ g% {0 ?4 _/ E7 ?    return ret;
+ ^: R; h* _1 C+ Y0 }2 ?9 o+ ~}, `7 H3 q+ R1 _& C
正如2.2小结所说的,fb_fix_screeninfo只能获取不能设置,因此,ioctl只提供了获取fb_fix_screeninfo的方法,而没有提供设置fb_fix_screeninfo的方法。8 P. |7 r& k$ k' k; Q+ ?7 L3 T

' p: w, r" y; f- @$ ?! M8 j6 M3.5 小结
+ t0 W# Q- e: I) {3 S8 A! e( ^, S  本节对frambuffer的核心层进行了介绍。包括frambuffer子系统的创建,frambuffer的注册和提供给用户空间的5个API函数。下面开始介绍驱动层。( F* j/ E: E, p# R9 V& y

  X' g+ l" \3 F+ q0 g4. 驱动层
! h% Y/ [( q+ Z5 m7 a本节将开始介绍S3C2440的frambuffer驱动,该驱动源码位于drivers/video/s3c2410fb.c" Z" e/ K! L' p+ p' A; F& g1 N! W8 R. h
& x& W: b  f/ e( C( Y
首先来看下驱动模块的初始化和清除函数。
; D1 W7 h+ g) z9 P' {$ T* q4 f1 V. p- k7 V$ \9 Q
4.1 s3c2410fb_init和s3c2410fb_cleanup
  P2 Y1 E2 R; n5 e1 Z: E% rstatic struct platform_driver s3c2410fb_driver = {
! a' @% P9 O$ B# ~0 G/ A    .probe        = s3c2410fb_probe,
$ \4 P* ?3 g: O% V' k$ p: w- n    .remove        = s3c2410fb_remove,: U+ t. q% z) o, G
    .suspend    = s3c2410fb_suspend,; i/ `* Q5 {5 Q- v+ u
    .resume        = s3c2410fb_resume,6 P5 j8 Z. V5 Q' M2 ]' I
    .driver        = {" _! o7 W4 Y+ s/ `5 H
        .name    = "s3c2410-lcd",
& Y  t& y; [% D. `( n        .owner    = THIS_MODULE,- F1 j  ~" j+ U
    },
, o2 Z8 I" l  S! h% Q};9 U, p2 b" j+ m, Z

/ K* ^. y8 H9 h# x4 Jint __init s3c2410fb_init(void)
) Y% H: h* o( N5 S& b- I0 E{
9 F& h6 O  f5 ^4 C: x        int ret = platform_driver_register(&s3c2410fb_driver);
' S; p' G, Z' T0 w
% f, v+ Q3 P& `2 O) u* U4 |1 `        if (ret == 0)3 j# E( t* l0 Z( \! s2 l
                ret = platform_driver_register(&s3c2412fb_driver);;
! N& _5 A& u% ~  u
" P& j9 ~5 t1 ^/ V' b# W" X& B1 r- z' v        return ret;
7 O( F% f7 [& p" B1 R! c}
3 r& ^% f8 b5 I# x6 R- b0 r& J3 S
static void __exit s3c2410fb_cleanup(void)
& i8 P* ~. `9 ?& G{
. C3 Q% I* X5 T% ?- i, s9 p. J        platform_driver_unregister(&s3c2410fb_driver);
; ~- q  i+ @# U5 M! V# k4 P8 F1 H! w        platform_driver_unregister(&s3c2412fb_driver);5 V; }* B; \0 ?5 O9 ?0 _0 r% [
}+ t  a& Q* u. K, o7 ^3 y# P3 `

+ G0 Q4 e7 v0 `. ?module_init(s3c2410fb_init);- p2 D+ ?2 m/ O5 }
module_exit(s3c2410fb_cleanup);7 K5 ^+ ^" T0 Q; [1 W

, w4 ^! ]1 c2 e+ o4 ?当platform_driver_register调用的最后会调用probe方法。接下来就来看看probe方法。5 F5 i' H) j! }- h! @
4.2 probe方法
9 s& e4 R0 M0 estruct s3c2410fb_info {
. U7 W8 \( I$ g$ g    struct device        *dev;" T3 g2 @8 o- y: e2 p2 I- V" S
    struct clk        *clk;1 E8 v  V) A8 J( }

0 x% a; A  l" S: i6 m4 d5 d7 u' x0 |    struct resource        *mem;
4 |, f9 k1 p$ A    void __iomem        *io;        /*虚拟地址*/
' m7 c: x6 J! B' n6 G    void __iomem        *irq_base;
) z0 e* x( {6 I' X/ d
5 X2 p* F! |! k$ u, n, Q( O9 E- \    enum s3c_drv_type    drv_type;
- c+ R0 t2 g# R$ f9 Z/ @    struct s3c2410fb_hw    regs;  u7 A' M; d) r0 x7 C
8 g6 X) L* G1 c
    unsigned int        palette_ready;
. s: I+ R8 F/ [8 U/ ?. R
4 r: c. |, ~  |4 u# r% S6 i6 A+ X& K    /* keep these registers in case we need to re-write palette */
% O2 d7 Z1 X4 t0 G+ x4 c    u32            palette_buffer[256];
' b. T7 V' Z' A8 N! }' s    u32            pseudo_pal[16];8 |% U& M0 k0 I$ E! v
};
  K* ~* e# a3 r7 s3 C* b8 X
3 l$ L2 H* F, x3 X( H- Q9 h! X5 A4 ]struct s3c2410fb_mach_info {$ s, _9 {& m' p/ ]7 m

  o+ e4 G# Z$ S) Y% v/ w        struct s3c2410fb_display *displays;        /* attached diplays info */
4 L% Z  }; N! O- E        unsigned num_displays;                        /* number of defined displays */
% D4 D& ]0 S9 W  L1 c        unsigned default_display;
6 i  [7 ^  s- t( G% C  x, p% q9 E/ L# N+ y! J: d' J" h
        /* GPIOs */
& c' z6 s& |4 D3 @. L, Y1 I. G! |/ B
! D5 ?. o9 T" `! C% e: |- ^* `8 y        unsigned long        gpcup;
' [2 Q0 Q" L0 k! J/ ?" Z% @2 ~        unsigned long        gpcup_mask;/ r- w6 y  B  e
        unsigned long        gpccon;: d- |# q) c) _: B! a
        unsigned long        gpccon_mask;
0 B  Y8 k" z! o& w+ X, D        unsigned long        gpdup;4 N# w2 b6 z1 A
        unsigned long        gpdup_mask;2 V9 u% }% I  j- D8 O4 X- P  H
        unsigned long        gpdcon;
% Y4 X' d/ O" X4 U. j        unsigned long        gpdcon_mask;
- `; X) r* b3 m6 I$ p8 j# V: n, F- r2 I' W3 c, j! ^
        /* lpc3600 control register */
( k$ Z# [- d7 |3 }        unsigned long        lpcsel;
2 ^4 q" z3 B6 q. B0 f6 s};7 r) N1 G" b# V- g: @+ @
: x; i3 e; A, Z
/* LCD description */) G4 D, s, n# |' S7 Y# k8 u
struct s3c2410fb_display {
5 |8 {) q; I% N0 \1 Y    /* LCD type */  ?& j( j( `) S$ l; q
    unsigned type;, X6 }! C, H* J6 P( r6 ^
7 W) s+ Z" l, B- y$ t+ A
    /* Screen size */
9 J7 o3 \% L9 u3 D. w    unsigned short width;0 N" D) _& M3 \, [2 @& m+ i5 T
    unsigned short height;: s; N) m2 X( y; U

( [, C% q0 }# G6 }    /* Screen info */9 b. e9 I( i$ f0 S
    unsigned short xres;
) G8 X' S# R- @    unsigned short yres;
+ p0 l/ ]+ F( V5 e    unsigned short bpp;+ C# `: Y6 t$ i9 h4 m4 x1 W

  V  C( V. J# N$ X+ q. d2 F    unsigned pixclock;        /* pixclock in picoseconds */
- q) h" \: M* `* i    unsigned short left_margin;  /* value in pixels (TFT) or HCLKs (STN) */
$ N2 K$ z$ U' y  y( e2 U    unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */  Y0 Z0 [- X7 j* K3 w. n6 D
    unsigned short hsync_len;    /* value in pixels (TFT) or HCLKs (STN) */
# Q. v( l3 J' S8 d) ]0 q* ^6 j    unsigned short upper_margin;    /* value in lines (TFT) or 0 (STN) */5 `7 I5 B4 I% f
    unsigned short lower_margin;    /* value in lines (TFT) or 0 (STN) */
: h, G; Y: B% v    unsigned short vsync_len;    /* value in lines (TFT) or 0 (STN) */
8 w, x) q0 u) T5 N0 h6 |5 i7 J6 a. O
    /* lcd configuration registers */
, \& c+ W8 X" g) V# @2 k/ }    unsigned long    lcdcon5;
4 Z6 j! V0 F; B! V* L) l0 |9 r};
) e& P+ K/ Q. V% ~$ ~5 D) K" [' G% s5 i
static int __init s3c24xxfb_probe(struct platform_device *pdev,
% L0 X2 K. W; g0 T- R                  enum s3c_drv_type drv_type)
' R/ D9 v8 o9 s* Z" g3 X{% `, i/ Y  i1 Q0 d0 e
    struct s3c2410fb_info *info;( M: U. w, G' I. r
    struct s3c2410fb_display *display;
3 k0 e( ?, n* z    struct fb_info *fbinfo;6 e* g& K4 f. d0 R  b4 Q. K$ ]" e8 d
    struct s3c2410fb_mach_info *mach_info;
; R( U+ \) I; w% O7 Y: \    struct resource *res;
+ {2 I% X( ~; F; |0 U- @( ?; H    int ret;
* ]+ a9 s4 O/ r1 @* g- L, C    int irq;3 d5 Q6 d+ [' r3 g9 v% Y: r7 c
    int i;+ V: E% Z% A. y+ R4 t. O
    int size;
9 ^$ p6 h# D2 E. B( Y    u32 lcdcon1;
! E- W, `1 i. _( O2 h+ ~    /*dev.platform_data由函数s3c24xx_fb_set_platdata(mach-smdk2410.c)设置,指向s3c2410fb_mach_info*/
2 l. a" D. U1 k    mach_info = pdev->dev.platform_data;   
$ H- t" T/ W- z    if (mach_info == NULL) {
& e: N8 U7 X4 o        dev_err(&pdev->dev,) z' ^# p* F  v/ T+ w) V
            "no platform data for lcd, cannot attach\n");
2 _/ H9 |, W+ W6 Z        return -EINVAL;
+ V! s+ K+ e) T    }
" h) k% Y* i  \# l' H" o' ^                                    /*在mach-smdk2440.c中,default_display=0, num_displays=1*/* [! W' L6 T- `& K0 m* f9 {
    if (mach_info->default_display >= mach_info->num_displays) {     
- L: Z/ Z6 u. I- m        dev_err(&pdev->dev, "default is %d but only %d displays\n",% H0 E' }8 {" S0 ^; j
            mach_info->default_display, mach_info->num_displays);/ q) q- Y) S# B8 \
        return -EINVAL;
. y# F: B) f3 _3 e$ J7 ^% d6 j    }- j5 D& }4 Z: Q& F- `, X3 a6 R2 t
; \0 h7 z% v% r; S% r
    display = mach_info->displays + mach_info->default_display;! s6 V. H8 w' D) C# Z
/ J" l5 w- j& p# A' A
    irq = platform_get_irq(pdev, 0);    /*获取IRQ号,16号中断*/! Z, e$ A- R: i
    if (irq < 0) {
+ I2 |# a5 L/ L2 u- g9 O/ J" Q        dev_err(&pdev->dev, "no irq for device\n");
8 B0 f$ q( y- _. ^        return -ENOENT;. o/ w; D8 P! f$ P- t! l
    }
5 y9 u# C. H3 u                                        /*分配struct fb_info 其中包括sizeof字节的私有数据区*/0 I0 Y3 {5 o7 @& ?* |- E! {
    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);7 a) x6 Z, H1 o* _
    if (!fbinfo)
, O; z$ f2 ?0 q: D" b, }! a        return -ENOMEM;
7 p" e" D- u+ F! L/ v
9 ^- N+ r/ A, g* ]) U* F, D    platform_set_drvdata(pdev, fbinfo);    /*让platform_device->dev.driver_data指向struct fb_info*/1 K9 [3 I; N  y2 R
% u: J: Q; d& @5 f( @! h& [
    info = fbinfo->par;                    /*par指向s3c2410fb_info*/
& h) d+ A! y! Z; |' @    info->dev = &pdev->dev;+ _# n1 g& }  c! c8 c0 L1 ?
    info->drv_type = drv_type;
$ l7 K. K/ ]. _+ J7 C1 {7 o7 L8 J- D/ c' p% d- g' h: K+ M
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*获取平台资源*/* f5 B9 P+ V* p' I) _9 W2 W3 V
    if (res == NULL) {
$ W9 }# L  ^8 Q        dev_err(&pdev->dev, "failed to get memory registers\n");: Q# p& b, @+ T% n
        ret = -ENXIO;
# g0 x) _7 T. W5 c3 b$ X  q) i4 c        goto dealloc_fb;9 W1 a5 ]* u& B/ g4 W8 e  l: j0 s
    }
& a+ U/ k& Z5 ?# w! E. g9 z
% u% J8 L% V+ z. ]    size = (res->end - res->start) + 1;                /*IO内存申请*/& R8 C: Z. f4 _! W  W4 C
    info->mem = request_mem_region(res->start, size, pdev->name);   
  a- j) F. i6 K/ B    if (info->mem == NULL) {
$ y0 U2 C7 B& T: ~        dev_err(&pdev->dev, "failed to get memory region\n");
! l0 \! E! D; o8 E        ret = -ENOENT;# F  s6 O2 O4 }9 W' ?
        goto dealloc_fb;6 Z6 T$ t! ^: @% u8 E/ ]
    }/ Z, F0 d0 f4 b; x) N

0 t3 t# O( ~+ P! D/ k5 Y    info->io = ioremap(res->start, size);            /*IO内存映射,获取lcd第一个寄存器的映射地址*/               
  o0 X1 s! B8 ^1 R4 {/ i    if (info->io == NULL) {' U3 g( c. k# u% i; M
        dev_err(&pdev->dev, "ioremap() of registers failed\n");        + U1 X) C. X  q' p5 E) ~! ?
        ret = -ENXIO;    % l' t/ j* \/ X( D4 X9 j
        goto release_mem;
. j- N: b% Z, `$ @  Y0 V    }
- b& T  J) U9 l4 n. d                                            /*irq_base对应的物理地址是0X4D00 0054(寄存器LCDINTPND)*/
1 e& ^0 G- D$ t% y  h    info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);6 l% T8 C- N" r+ P

* z# E7 j* `& ]4 E    dprintk("devinit\n");
% |/ q2 q" h* K* r+ h% v
* x0 O/ T3 v" w# b! N0 K    strcpy(fbinfo->fix.id, driver_name);    /*复制名字*/, t) I: \7 G, z! F- e4 I2 \+ q6 l

% g; F: Z$ ~' Z- Z: c6 h1 h    /* Stop the video */3 H: E# C; W  N$ h0 d) o: m
    lcdcon1 = readl(info->io + S3C2410_LCDCON1);
3 J9 J7 x* l& f+ F- O0 I$ q0 ]    writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1); /*禁止LCD*/' `  t6 N' S* o

/ ?/ l# K0 y! C0 c    fbinfo->fix.type        = FB_TYPE_PACKED_PIXELS;" k0 A9 {7 Q1 q, {& C
    fbinfo->fix.type_aux        = 0;, j4 ?( }# f# C1 {
    fbinfo->fix.xpanstep        = 0;
" J6 ~& _( \  I, E& d: D    fbinfo->fix.ypanstep        = 0;7 w% V- \: m& ]4 U# ~' b
    fbinfo->fix.ywrapstep        = 0;
1 }! r+ P' L) Y/ Y6 `    fbinfo->fix.accel        = FB_ACCEL_NONE;    /* no hardware accelerator    */* F2 [* X- _2 v1 z; X
! v' [" I& V+ v6 A! e! {
    fbinfo->var.nonstd        = 0;
* p; P* l. k0 ^2 Q  G, E) [2 g    fbinfo->var.activate        = FB_ACTIVATE_NOW;
3 r! U: c- t; |6 R  N) ?% c    fbinfo->var.accel_flags     = 0;3 m0 t' R) T# c6 \( |! t# D
    fbinfo->var.vmode        = FB_VMODE_NONINTERLACED;
' R$ W$ T9 Q0 W' x  q, e2 [" J% B. Y4 O
    fbinfo->fbops            = &s3c2410fb_ops;7 }* f% ~& i( v6 ]# f8 k
    fbinfo->flags            = FBINFO_FLAG_DEFAULT;
' ]3 N% n2 E3 {) s6 |  c) G    fbinfo->pseudo_palette      = &info->pseudo_pal;" S" c1 P, i0 a% ?: o

. v4 n3 p! T' J: L: k  U$ z    for (i = 0; i < 256; i++)/ z9 _7 u& K' l# V
        info->palette_buffer = PALETTE_BUFF_CLEAR;( o' y4 G  K. [, B/ @6 J

7 L7 J( D% \3 l' \) H9 S    ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);    /*申请IRQ,快速中断*/
; @) C9 \  O, g8 l, p    if (ret) {8 C' `5 H1 ]. K5 N
        dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);  T0 P9 {: w4 A; P8 F
        ret = -EBUSY;4 ?0 w, h: T( ]  Q6 _2 q1 g7 W8 \
        goto release_regs;( A' a" A" A0 _, j- U
    }
! q# _. Q6 _' w' r& o/ \
, X2 y! z8 {3 Q. M; s# ~1 L    info->clk = clk_get(NULL, "lcd");        /*获取时钟信息*/' G$ I) B2 l) S! j+ C4 x% I
    if (!info->clk || IS_ERR(info->clk)) {
2 L" R- ]6 A1 I/ h! z        printk(KERN_ERR "failed to get lcd clock source\n");3 G6 y: g4 X: y
        ret = -ENOENT;: B5 ~  w4 R6 g, V1 Q- v
        goto release_irq;
2 z2 l7 j8 `! k+ W6 o    }" @& I9 a6 p( y. g) |- C+ ^

% {# ~8 X+ ~' j' y( e1 `$ T    clk_enable(info->clk);                    /*使能时钟*/- [$ P. L5 N$ I- ~
    dprintk("got and enabled clock\n");6 L# a' p! R( a- y
- {4 t# U  {0 b6 j/ b& n1 {
    msleep(1);
/ T: m4 Z0 u8 n+ B
3 n2 S! p! y# L    /* find maximum required memory size for display */
$ v  L' i$ L$ q+ J3 t4 @    /*在多个屏幕中,找出需要的最大memory*/
2 Z; y9 G. s0 s7 h) F' u8 Y7 J    for (i = 0; i < mach_info->num_displays; i++) {6 s: C( j# X5 a6 O: m/ V0 O
        unsigned long smem_len = mach_info->displays.xres;8 \8 x: v+ D% z" e3 d. O
        /*所需的memory空间 = xres * yres * bpp / 8*/
/ \' y" E( R; P7 N        smem_len *= mach_info->displays.yres;
, n- F' ^& v& M4 }/ P        smem_len *= mach_info->displays.bpp;
& N! K  j3 {) h4 ^* v" T        smem_len >>= 3;+ W( }5 h, f/ A5 p
        if (fbinfo->fix.smem_len < smem_len)
3 Q* _: z1 a3 K% J2 X            fbinfo->fix.smem_len = smem_len;" G! ~& o  B; v
    }
" G9 _. e. h3 x. y* c- V# q
2 O/ y" a5 N5 C/ S3 d8 p    /* Initialize video memory */    /*根据上面fix.smem_len的大小,获取DMA映射内存,一致性映射方式*/
: x9 E, x4 \8 b: I    ret = s3c2410fb_map_video_memory(fbinfo);
* g8 D. l3 h) r( }- N/ a/ C    if (ret) {. A, |; q2 j9 N) _7 ^( ^8 P; r+ E
        printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);7 @4 }+ o' Q9 c2 c/ a$ g
        ret = -ENOMEM;
  p7 r1 p, r1 ^  H$ l' z9 I5 m0 v        goto release_clock;
0 @- Q+ t' ^/ ^  q* f    }" m% l1 q$ ^  I- @
0 a- \) _9 l: @6 @3 l: s. ^
    dprintk("got video memory\n");9 j0 c6 D; G  x# T  T

  w% @0 @# r3 k) w' B: o6 s; K    fbinfo->var.xres = display->xres;            /*320*/9 \7 q+ i' S6 s0 i- n
    fbinfo->var.yres = display->yres;            /*240*/   
, ?0 u! u1 @( L, o/ u. x( r7 q& p    fbinfo->var.bits_per_pixel = display->bpp;    /*16*/
8 M7 H( O9 Y5 {; n4 B* z6 e4 G/ s
" I7 o( S- I* p" S9 |: E" x0 z    s3c2410fb_init_registers(fbinfo);            /*LCD寄存器初始化*/        ! `( [$ i/ o0 Q" e: C$ g

) c; m- [$ {. H' h$ p& }    s3c2410fb_check_var(&fbinfo->var, fbinfo);0 v  \$ ~8 K  {  G  C

" y: V0 A. j6 K9 A/ r    ret = register_framebuffer(fbinfo);            /*注册framebuffer*/
* N5 p" x; a( _; r0 h0 ^, _+ z    if (ret < 0) {! z  _% q2 G* z1 t
        printk(KERN_ERR "Failed to register framebuffer device: %d\n",ret);) h; u! m. ~) K4 ~5 }( H
        goto free_video_memory;
3 s( j7 [% }8 F2 `9 l6 F    }
7 F0 s) B2 H! q  V" O! w8 T$ o5 g8 S' K5 i! p! v+ t; v8 Y
    /* create device files */
$ n& ^; F# X5 `    ret = device_create_file(&pdev->dev, &dev_attr_debug); /*添加设备属性*/% w; I, Y- {# I( o  a+ I9 C. o/ u
    if (ret) {( I) ~# X7 M3 C) K+ L- s
        printk(KERN_ERR "failed to add debug attribute\n");# B; z! [7 P3 D9 M5 J
    }
- j& f7 l& c. W9 p- p; ?
0 j: X6 l7 l: K( c7 z& h    printk(KERN_INFO "fb%d: %s frame buffer device\n",
& ^. l1 t  U! o4 Z* d        fbinfo->node, fbinfo->fix.id);
, \4 P) p3 \9 ]
9 f8 O% L3 |1 ^$ M) t* K    return 0;
! F  X. z' c9 ]" M# D' y/*一旦某个步骤发生错误,以注册的相反顺序开始注销*/
( w) R! ~( X. F% R: K( zfree_video_memory:
! G# H: K" M$ d0 J. o    s3c2410fb_unmap_video_memory(fbinfo);; \! S  ~# g. p( x( c! S
release_clock:0 o) x5 J' P; P+ `+ u* V5 `6 U
    clk_disable(info->clk);
9 Y2 ]- [' [1 F$ d    clk_put(info->clk);# O% z) P- p5 M% J9 X8 L) `
release_irq:
7 I% f, p; m5 Y8 i    free_irq(irq, info);+ C2 |1 @0 g2 |, l% J- F; s
release_regs:; @4 [0 V! R4 G0 f7 _' n0 G
    iounmap(info->io);+ a/ R5 |9 D! P( N, R4 m! r
release_mem:8 K% N' W9 j1 L3 Q
    release_resource(info->mem);
. c- X0 w- r4 ]* Z* k    kfree(info->mem);, X$ Q- i8 O8 ?) F: l1 o
dealloc_fb:/ Y3 V( C- D/ P" i1 a! A3 n3 J2 m
    platform_set_drvdata(pdev, NULL);
; I  t2 Z: u3 o4 ]/ z    framebuffer_release(fbinfo);    ( X$ x! b0 {, z0 q1 b
    return ret;+ F6 f) R% n4 W
}9 |" I" E$ n0 b) S! u

  i* r, @# K  T9 g& l这里使用了三个新的数据结构。s3c2410fb_info是驱动程序使用的,里面将保存所有驱动程序所要使用的资源等。而s3c2410fb_display和s3c2410fb_mach_info,是由板级信息,通过platform总线添加到内核中。/ \3 q8 @% }: L' G8 i% P9 L
1 E# E6 W9 I+ k1 C5 `* F
s3c2410fb_display中的成员将被复制到fb_var_screeninfo结构中。
& P! s/ K* Y; r1 j5 _/ Z: v; m& |9 h0 M
该板级信息的定义在arch/ARM/mach-s3c2440/mach-smdk2440.c中,来看下, `. d% B: U! e3 Y- B5 _
% X: Y4 `6 R0 A
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
" V7 ]* \- ~  K( }) x" e
1 q; d8 C4 P( n7 Y        .lcdcon5        = S3C2410_LCDCON5_FRM565 |
2 [6 V& ?) V+ S6 c                          S3C2410_LCDCON5_INVVLINE |
8 ]# l- O4 s9 F                          S3C2410_LCDCON5_INVVFRAME |
1 J# p- ]& O6 T4 d                          S3C2410_LCDCON5_PWREN |, P( u; _2 C0 O
                          S3C2410_LCDCON5_HWSWP,% \4 w! ^; x& `  F# P) u

9 \6 ]' v- s# W: b. K. x) [        .type                = S3C2410_LCDCON1_TFT," `1 R2 Q1 J& K0 `/ U) Y) g

8 \: O" @4 }! ]1 D0 g3 H% o        .width                = 320,//240,# m% u  @9 C3 {- m2 W' u3 _( i7 a& W+ @- v
        .height                = 240,//320,0 N# {* e9 _2 t$ J2 F
+ @: `: \( s5 w1 {6 R! g
        .pixclock        = 149000,//166667, /* HCLK 60 MHz, divisor 10 */* v$ T2 |' D! \+ L5 c+ v" s2 K- o
        .xres                = 320,//240,+ h0 G, l: J; W& f# }9 Z, r  G
        .yres                = 240,//320,4 g0 p* C) J( _, Z5 P" k
        .bpp                = 16,, u) w/ `1 k5 a. v! x
        .left_margin        = 20,; C  `% E: B8 M3 \" T/ _9 Y
        .right_margin        = 38,//8,4 T: [( j  ?' [4 D- f
        .hsync_len        = 30,//4,
4 }) v" N% n' K- g        .upper_margin        = 15,//8,
5 J+ K. N5 l) D$ `( _2 r        .lower_margin        = 12,//7,
+ ]" }  W4 l; c$ y3 X0 ~: y$ [3 p        .vsync_len        = 3,//4,
/ r; M4 F" u, |. h};/ n+ u& G" o2 ~- ^3 r7 ]( ~
: x% s) d' J9 a* x% U
static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
/ p, s2 _* V* G, [) s; o        .displays        = &smdk2440_lcd_cfg,  N( c/ ~- I4 R
        .num_displays        = 1,7 K* F6 u; I5 P" I
        .default_display = 0,
2 F" v2 e0 F; v6 G) A+ f* C$ z+ a8 [1 \
#if 0% @# v& [6 }: Y9 ?
        /* currently setup by downloader */
# n: W" Z8 z9 C$ \) I( E        .gpccon                = 0xaa940659,
6 v4 Z1 |6 @( y7 f  O3 I; Q. N6 h        .gpccon_mask        = 0xffffffff,
6 F4 x. J) [% O1 E        .gpcup                = 0x0000ffff,  a+ `/ ?- W: J" ?) O
        .gpcup_mask        = 0xffffffff,' ?8 G9 ^# A$ o8 X; K. k# s8 x( \
        .gpdcon                = 0xaa84aaa0,
2 b6 F$ A: v/ Z! o# t( i        .gpdcon_mask        = 0xffffffff,; a) I, {7 P  Z6 `5 J
        .gpdup                = 0x0000faff,
: @& Y0 r. N* e4 d8 O        .gpdup_mask        = 0xffffffff,
# L( y6 c! q2 L#endif
7 S1 f! V- Q8 u  {8 c//no, e* {/ P4 g" X! t8 n6 \
//        .lpcsel                = ((0xCE6) & ~7) | 1<<4,
3 I8 \7 Y& y* ~; w$ p* s2 s+ H};
: |: o  F# W+ `6 ?
  c* v1 U" a. _9 J; [6 E2 w6 z* Z这里NOTE:每个LCD屏幕的参数不一样,因此上面的参数会有所不同,这是需要移植的地方。$ C5 n, b' J2 S$ y" C/ \
. G8 E1 t6 f" e
随后,我们看下在probe方法中调用的几个函数。首先是s3c2410fb_map_video_memory。
2 \( E6 e( e& ~* V/ z
: s6 _4 e' l* j' m4 G$ L/*
$ ~% b; M  q  _2 u8 }/ R4 h * s3c2410fb_map_video_memory():, D% Z1 a/ ?0 a9 W% U% I/ l" {
*        Allocates the DRAM memory for the frame buffer.  This buffer is: f4 B$ g5 C9 E. X: R, j
*        remapped into a non-cached, non-buffered, memory region to4 o6 ]7 U: u- z" E
*        allow palette and pixel writes to occur without flushing the, |% I6 F2 c& ~) S6 A$ X0 v- r
*        cache.  Once this area is remapped, all virtual memory9 Q; U# }" J& O9 i5 C
*        access to the video memory should occur at the new region.
' T. k' _, s9 C  P$ [. ?  U/ P */! c! V# D6 Y  q4 ^+ I; ?
static int __init s3c2410fb_map_video_memory(struct fb_info *info)- o7 K) C1 a; M* H0 O$ n. W
{. |* S6 N9 L+ l. u0 c& w" q  l0 ~
        struct s3c2410fb_info *fbi = info->par;/ e% l' _; K% Q( }8 _
        dma_addr_t map_dma;
3 U/ n5 _+ q) |4 ~: }4 |) F5 j; ?0 Q        unsigned map_size = PAGE_ALIGN(info->fix.smem_len);- G# O, u- ~8 _$ U. T1 a

! m7 M) B6 D+ D: Y# Z# w        dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
! s% N7 r1 |4 y9 U                /*分配DMA缓冲区,并保存DMA缓冲区虚拟地址*/
2 d, w: n$ t! |4 _% I        info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
; M  S  O0 l  [                                                   &map_dma, GFP_KERNEL);% h+ y4 j& k- R: s3 ~0 Y* n

6 M  n" G6 ?! f1 L: c. E4 L* R        if (info->screen_base) {
& u$ h; J) V& X* h                /* prevent initial garbage on screen */) q9 a' Y8 j, x
                dprintk("map_video_memory: clear %p:%08x\n",
8 ]; m* X+ Z1 t, ]1 `& _8 B4 P, Q                        info->screen_base, map_size);
4 o- ^: L3 |& ^, ^                memset(info->screen_base, 0x00, map_size);        /*DMA缓冲区清0*/
: X9 h" a' t9 _" j; ~7 d
; f5 R  ]. f: f% k, X$ ]1 K0 [                info->fix.smem_start = map_dma;        /*保存DMA缓冲区物理地址*/7 v- N: g+ d, F$ m$ w

, G$ r! X) Q) G                dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",6 o# H* V; R1 L
                        info->fix.smem_start, info->screen_base, map_size);
3 G" h3 a1 C" i- x' k        }
. S8 F8 {$ J+ F! w3 m* u4 K4 L
$ P0 Z! e' k! \$ S- `6 S& s# x        return info->screen_base ? 0 : -ENOMEM;. H$ u  S. N2 S) ^3 _
}5 I* Z% V* l1 G. S4 h' z
1 Y9 W( Z9 b, [# v1 h
3 D$ b) P9 p' {/ `
该函数根据fix.smem_len的大小,分配了一个DMA缓冲区,保存了该缓冲区的物理地址和虚拟地址。
: Q* e7 O& M9 Y) E
' Z4 w9 m- |' ]2 t接着是s3c2410fb_init_registers:  Q' e/ h+ R# n4 j

1 i+ y- \' Z  C/ m$ }" o- h3 r* R" [% \; O9 n; W
/*7 N; d% k9 K# T! A# O3 L# `
* s3c2410fb_init_registers - Initialise all LCD-related registers
; }7 P$ i0 _3 D8 R% `/ t. U */
7 q8 M0 q1 S4 X' e) m9 Kstatic int s3c2410fb_init_registers(struct fb_info *info)
3 ~" [, |8 c& I{
' I! Z5 [! v3 @: T2 y% f    struct s3c2410fb_info *fbi = info->par;        /*par指向s3c2410fb_info*/
5 u6 U" |$ Z8 J( I) R: \6 A4 ^4 G    struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;, ]) k. ]' p9 L) W7 L; W8 ?
    unsigned long flags;' S0 a  g: M0 Z! Y: j
    void __iomem *regs = fbi->io;: K! M. o5 p9 z! p* L$ ~3 p) a6 D+ B7 j( Q* s
    void __iomem *tpal;
5 o1 ?6 j* y3 G* N% d8 h7 T    void __iomem *lpcsel;6 b/ U' d, p$ m1 K+ X! d
# O9 r. |0 u+ x$ O6 h: G
    if (is_s3c2412(fbi)) {
5 h8 i4 P" b8 z6 |) E) O        tpal = regs + S3C2412_TPAL;3 W4 P6 C# g$ E, S4 j( Y- G' p1 V
        lpcsel = regs + S3C2412_TCONSEL;9 n/ b/ O% F3 ]' o
    } else {0 u5 ^( ]7 {' z' l1 S, d
        tpal = regs + S3C2410_TPAL;9 w. r; y- `( Y# z" J
        lpcsel = regs + S3C2410_LPCSEL;
0 U6 \1 m* y4 n0 u2 Q8 z    }
+ b9 n+ e* D/ {' Y7 z
4 V( L, J( Q% \- X# ^    /* Initialise LCD with values from haret */2 [) l- d& w( r* N: F, a

$ B% x4 q% I4 g+ N. B9 ?; U    local_irq_save(flags);                        /*禁止所有中断*/
; g' T( _7 G1 M' s, R4 u% L9 U
8 B+ O. \. }+ j2 F0 l6 l9 K6 b; t    /* modify the gpio(s) with interrupts set (bjd) */    /*初始化io管脚*/% }  ~0 p: U9 K! E, l

1 W4 G: u$ h' {2 `% Z    modify_gpio(S3C2410_GPCUP,  mach_info->gpcup,  mach_info->gpcup_mask);! ]7 ]  Z/ S$ T! P2 @) |
    modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
2 ~. O1 b$ y9 j0 g    modify_gpio(S3C2410_GPDUP,  mach_info->gpdup,  mach_info->gpdup_mask);: d+ d0 Y6 }% ~% T! u
    modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);6 D6 Y# O) w' N) ]( d) i/ |" A# j8 L2 H
3 R2 F/ }+ B- }7 E/ e0 `. W, m. o
    local_irq_restore(flags);                    /*恢复中断*/
" y# C/ ]( ]. _
$ l, Z; @6 W1 r0 L" ^    dprintk("LPCSEL    = 0x%08lx\n", mach_info->lpcsel);    /*设置TCONSEL,禁止LPC3600*/
3 i( Y1 {+ n; H' e    writel(mach_info->lpcsel, lpcsel);" O% q" S0 W9 K5 N

# E. b5 E! h% M* V9 b* r" q6 [    dprintk("replacing TPAL %08x\n", readl(tpal));' F7 I+ I. F0 s) |( {# O1 n/ Z1 r( l

- \7 W5 a0 {" j2 ~    /* ensure temporary palette disabled */0 N% ?& g! I- ^; V! z3 O, Q. N9 i
    writel(0x00, tpal);    /*禁止调色板*/
7 z  e1 I7 N* w, T2 b+ z9 |
3 E+ N; C: E! x: q4 t% t    return 0;5 o2 z* g1 @, q2 m3 o* j1 t2 y% y
}
  t' ^/ o6 d' B4 b0 `
; g. z5 Z  L; S* B) ^% l1 Pstatic inline void modify_gpio(void __iomem *reg,) H9 m) M( `- ~  C; D6 Q1 o4 ~
                   unsigned long set, unsigned long mask)1 t3 A* ^3 F% n7 {
{
% ]. b0 {3 J* Y( z! B# q. c9 K    unsigned long tmp;
5 @9 d  Q+ o; L* y! f. ]0 u" s( x  y! j8 ?! c- h( l
    tmp = readl(reg) & ~mask;5 ?8 S) ^9 O5 D, F- u
    writel(tmp | set, reg);5 p3 f" o! y. N. z- K
} / @' e3 B+ |8 ]* P2 H6 N" Y1 R/ |, w4 [
最后是s3c2410fb_check_var:8 o2 U9 t9 f! A$ N- B1 |

2 k" g# G. Z) c/*
5 @% t4 F% y  ^2 J  `( | *        s3c2410fb_check_var():6 Y$ V9 _& }. Y( w* A8 f- n
*        Get the video params out of 'var'. If a value doesn't fit, round it up,. }% Y, I) _  ]' G; @5 Q
*        if it's too big, return -EINVAL.
, J3 J( ^. F9 m! @( ^5 ^) j5 R5 O *        检查变量的合法性
$ M( B" p1 J- B& W8 h; i2 g  t */
; \1 e, e- h9 s- ^static int s3c2410fb_check_var(struct fb_var_screeninfo *var,0 ?0 N1 }& K/ O" l
                               struct fb_info *info)8 m0 k4 _. I/ J+ A8 p& g- m" Z
{
; d! J' x% Z6 D& I        struct s3c2410fb_info *fbi = info->par;                        /*par指向s3c2410fb_info*/
+ T! c/ _. ]6 n; [& C% |        struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;/*指向s3c2410fb_mach_info*/
1 S, w$ v, w6 m  w0 W4 X        struct s3c2410fb_display *display = NULL;6 Y! \) A: {7 k6 K( ^' G$ |+ R% w
        struct s3c2410fb_display *default_display = mach_info->displays +
' y2 g8 F- X" L0 b: r7 F. J                                                    mach_info->default_display;# J+ ^4 n+ ]# @- b
        int type = default_display->type;         /*S3C2410_LCDCON1_TFT*/
, v$ R" _4 p7 a$ d& L/ y" u        unsigned i;
9 k1 F6 f/ w2 B# u7 x' P
; @# U9 L% Y6 M( [        dprintk("check_var(var=%p, info=%p)\n", var, info);$ \# v% @0 F/ q: x6 W9 `- S' n$ _
; @. v$ j" h: J
        /* validate x/y resolution */
5 C5 B% {4 L$ L: {0 E/ i        /* choose default mode if possible */                        /*var中的成员在probe中设置*/9 [) C9 W# z3 R3 t
        if (var->yres == default_display->yres &&
2 l5 Y% I* X2 _3 @  Q            var->xres == default_display->xres &&. P) _7 c0 D+ j- H( v' Z
            var->bits_per_pixel == default_display->bpp)
. z' Q, O+ @. T" ~$ W$ V3 k+ \                display = default_display;
. }+ f. C  j& r4 b7 W  |6 u* x5 [& d  {        else
- L$ l" w) v: q/ C4 w1 [6 N                for (i = 0; i < mach_info->num_displays; i++)7 }( b3 {6 C3 A0 M8 b
                        if (type == mach_info->displays.type &&
" \: Y; Z  o5 x( j2 X                            var->yres == mach_info->displays.yres &&
3 z- I5 }$ @. b1 l' ~! k, U                            var->xres == mach_info->displays.xres &&; z. @& _2 P) g5 P) _
                            var->bits_per_pixel == mach_info->displays.bpp) {4 R5 A8 B0 |  W: ^
                                display = mach_info->displays + i;& u% U' _0 V6 B% b
                                break;7 z+ M; i: n. y2 {: {! e1 Q
                        }+ I8 g# ]  W" ~, F& U
  a! A# b6 {7 L' p& z- o
        if (!display) {9 ~! a: j  Y9 K6 m
                dprintk("wrong resolution or depth %dx%d at %d bpp\n",6 R2 P, P) e" b) N% _7 Y) S# i! h
                        var->xres, var->yres, var->bits_per_pixel);
, L; w: x' l, X0 Y                return -EINVAL;
1 G+ k% S: }7 h6 P+ X        }
7 o" U/ H. G, O5 G$ b3 e
, E# v. J' h1 b' p9 H        /* it is always the size as the display */' H" _0 E( ~0 e: K$ q
        var->xres_virtual = display->xres;
" _# c( X' L# U$ |& ^( ~9 ]  Y; o        var->yres_virtual = display->yres;/ n, d' T+ v8 g$ T' {  r* f
        var->height = display->height;8 a' |- i  W% \
        var->width = display->width;: P$ D/ Y5 o0 t, c: h, F. e6 p6 j

5 U; \# B9 \3 E/ H        /* copy lcd settings */( n2 J2 x9 K8 u
        var->pixclock = display->pixclock;) p6 z( A6 d! n( |
        var->left_margin = display->left_margin;( O) x  n9 j% ?8 b# _
        var->right_margin = display->right_margin;. e3 v& {& E( @4 b2 c. Z, c5 `
        var->upper_margin = display->upper_margin;
7 H4 p3 u; e; @        var->lower_margin = display->lower_margin;
  `: k4 ~# Z" t% X' {- l$ p8 C4 y        var->vsync_len = display->vsync_len;
; f( A: z7 {( V2 q" d' t        var->hsync_len = display->hsync_len;
! f9 [: F0 {1 Q6 ^8 y
2 N% n- n& b" K* |        fbi->regs.lcdcon5 = display->lcdcon5;
4 O4 U+ n  t. U1 ~        /* set display type */
2 m) }4 Y. v+ i6 |  }/ v: Z        fbi->regs.lcdcon1 = display->type;
2 M: y- `$ F% E
1 i: b+ l1 L+ W- O  x        var->transp.offset = 0;4 E; n8 V+ _6 D2 t) e6 r
        var->transp.length = 0;
  d4 j) O8 b; k1 F        /* set r/g/b positions */
  X: s% }% W  U  E; q& p        switch (var->bits_per_pixel) {) v8 O9 G; U3 I
        case 1:
6 `" ^  C1 O  p# T        case 2:
8 F: O: Y  }% B. g: G, M        case 4:
  D9 I# s( \7 I" q& f                var->red.offset        = 0;
4 k9 ?8 M2 `7 G1 b& i: I$ n                var->red.length        = var->bits_per_pixel;
* h# ^& s6 b9 }4 A& {! h3 q                var->green        = var->red;
: g5 @& i) e  q- a3 |+ C& ?                var->blue        = var->red;7 k' ]! f& _% y( `+ j. g" ^
                break;: S% M4 F, R# P( V
        case 8:3 |3 \# `2 J' e
                if (display->type != S3C2410_LCDCON1_TFT) {
( R; `+ ]) d- S4 I+ F! r5 v                        /* 8 bpp 332 */
+ w' W/ t& `( `8 b0 E2 V" F+ X7 _7 z                        var->red.length                = 3;: H+ D2 |" o/ X+ l, \! A
                        var->red.offset                = 5;- {5 y& H+ J2 Y$ p* G; E" {
                        var->green.length        = 3;
; U2 K; k* i$ \5 N0 L# K, \) h                        var->green.offset        = 2;
; J  d: o# n& [0 G+ g' v                        var->blue.length        = 2;  Z( w/ ~, Y7 @( S$ o/ z
                        var->blue.offset        = 0;7 ]9 t8 S( I/ j% m0 K
                } else {
! B0 M4 e$ {$ Z                        var->red.offset                = 0;
6 J% ?/ Y5 t- A/ o2 O7 d# s1 _                        var->red.length                = 8;
, n" a3 f/ T% W$ W7 w                        var->green                = var->red;
; T7 ^' k1 h/ {$ N& _                        var->blue                = var->red;
; V/ V' C* T' Y* l                }
' |- f1 b6 i! ~( Y% ~3 D0 M" l: S                break;# Y* K" p5 {( `2 ^' y' W5 u; l
        case 12:0 q, K( @8 f; C, u6 Q
                /* 12 bpp 444 */% L* w" v: o3 x& V4 i! D4 N7 [. y
                var->red.length                = 4;+ h, r0 D* M5 X& U% E4 R( t
                var->red.offset                = 8;
; x$ t, D7 t3 ?4 ?. y                var->green.length        = 4;5 ^0 R8 \2 w& e* [
                var->green.offset        = 4;# D3 G9 r; J# ~, t
                var->blue.length        = 4;; z) i2 O7 \: Z+ F
                var->blue.offset        = 0;; o* r3 {% S6 X4 s; `% \
                break;
) O5 H! G% b! P  q( d0 ^2 x
$ u) k/ O$ C1 ], i2 ]1 ]        default:
3 G) p, N3 j7 W  ?9 O' Z0 ~' G- f, a        case 16:% m5 p- _, q# [7 y) a) s; g# _
                if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) { /*使用565格式*/
% W0 N: W* U9 S" Z% F- e                        /* 16 bpp, 565 format */, @/ G$ I/ l' a" U3 J# V
                        var->red.offset                = 11;8 }) e+ u! {* T$ b
                        var->green.offset        = 5;( I  Z8 `( e7 G9 M& g2 u. W" K3 G
                        var->blue.offset        = 0;
! Q- }1 ^8 U5 N1 ^- w1 j" a4 j                        var->red.length                = 5;+ l) k2 {, T5 G* N1 `5 M  J
                        var->green.length        = 6;
/ S+ ?2 Q1 V- W4 R$ N                        var->blue.length        = 5;  M5 @# r6 N% i
                } else {7 q8 @1 ^7 W& }3 s6 z2 g/ {
                        /* 16 bpp, 5551 format *// A! {! h- ]4 w( a9 E7 _
                        var->red.offset                = 11;
9 j! A0 G- {7 ]+ C1 |4 S                        var->green.offset        = 6;  i' r+ H9 |  z- ~
                        var->blue.offset        = 1;
1 h5 a5 A: B* y6 p$ a                        var->red.length                = 5;" T0 z4 v8 B2 D3 T+ \7 m. `( Z/ J( X7 y6 j$ M
                        var->green.length        = 5;
  U6 t2 v5 ?/ I& B                        var->blue.length        = 5;6 N( D2 q# }. w  w+ L8 V: d
                }# q" F9 {" }9 s* D
                break;
. g" h3 @# F1 Z1 U7 r$ {        case 32:( E$ O  t4 z" ~2 Z
                /* 24 bpp 888 and 8 dummy */
( u" ^1 J  i) G7 Q* a/ I                var->red.length                = 8;
6 ^+ E# Q4 X1 y                var->red.offset                = 16;  W6 X" D; ^! u/ C
                var->green.length        = 8;
% V% |7 @5 g7 {                var->green.offset        = 8;% K2 C1 I3 Y% M8 g8 G
                var->blue.length        = 8;6 Z% n, x( u& E
                var->blue.offset        = 0;0 w3 J+ I+ j. {* D9 x
                break;
" ]& s, @6 g9 _" a2 Q        }
* a' m: T) y& B) @+ j  H        return 0;
( B( w! i+ x. i+ q7 b8 O2 J% e}
" g2 T9 k% b' v  }& j2 ^& x, p* k3 w4 c, o6 A9 u8 T
/* Interpretation of offset for color fields: All offsets are from the right,' L3 c! W6 D5 L" i) s9 ?! f
* inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
( ^1 `. j" E/ _4 H$ t& H * can use the offset as right argument to <<). A pixel afterwards is a bit5 f: A! B2 G  l- i
* stream and is written to video memory as that unmodified.$ R/ X" ~% K8 X& {  h! G' l
*
/ S4 b2 N4 J& U; D2 ~: G) T1 z * For pseudocolor: offset and length should be the same for all color9 ~: Q3 r0 x7 E* c- M& z5 f
* components. Offset specifies the position of the least significant bit  l4 h2 A% q; K5 H. O6 D; ?
* of the pallette index in a pixel value. Length indicates the number4 B# G+ U# z. a$ g2 D7 K0 n
* of available palette entries (i.e. # of entries = 1 << length)., B1 g( v  u6 P! [5 }* L4 h; Q
*/. W7 p7 Q* F$ A. M. i# `8 {. F4 g
struct fb_bitfield {4 ]& W* M, Q3 N
    __u32 offset;            /* beginning of bitfield    */0 [$ L/ g! W9 `# Y$ }6 R* j
    __u32 length;            /* length of bitfield        */
# n7 o/ m9 x, C1 K. g    __u32 msb_right;        /* != 0 : Most significant bit is */ , V7 @) A+ T0 C$ F9 P
                    /* right */
& o$ i2 z3 I2 R  t; y# y3 _};
7 A$ V9 j2 {, g7 C! q该函数主要将板级信息s3c2410fb_display复制到对应的地方,然后根据RGB的模式设置位域。
# @% Q. Y2 Y0 y" `( ]4.3 fb_ops方法
( d' \! t5 ^2 a( l. e在驱动程序中,定义了fb_ops,如下:9 y: Z6 j* c' E9 H! A

: G8 r: W" ]) F! Hstatic struct fb_ops s3c2410fb_ops = {" k5 U; Y+ s: o6 h; ^
        .owner                = THIS_MODULE,
" {$ B3 _, v0 S( _1 r3 F! ?        .fb_check_var        = s3c2410fb_check_var,                /*检查变量的合法性*/
, c0 l  I6 u. Y. |" L1 g        .fb_set_par        = s3c2410fb_set_par,                        /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/
4 i1 g4 l2 s, q+ o7 O. b% C$ ]        .fb_blank        = s3c2410fb_blank,                                /*该方法支持显示消隐和去消隐*/9 M! D6 y1 I0 M& _( @- ?
        .fb_setcolreg        = s3c2410fb_setcolreg,                /*设置颜色寄存器*/
6 E3 W3 B+ O+ r        .fb_fillrect        = cfb_fillrect,                                /*用像素行填充矩形框,通用库函数*/
6 }3 Q$ T5 _9 X. u' [! a; P; M        .fb_copyarea        = cfb_copyarea,                                /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/7 @0 c! d: a% F: b( [# R) A* t7 M
        .fb_imageblit        = cfb_imageblit,                        /*显示一副图像,通用库函数*/
+ h4 y  B3 s& ~0 a};, G2 @% Y( N0 n1 z
9 b: \. P5 E6 I4 q9 g4 l
其中s3c2410fb_check_var在4.2节中已经分析过了,最后三个方法是通用库函数,在这里不作分析。+ H# u( L5 P6 T& l  E
剩余三个也是驱动程序提供的,现在对这三个程序进行分析。# q* M# m. [/ {8 z; t
4.3. 1 s3c2410fb_set_par7 {  s2 ^; ]0 F' m2 }
/*
' [1 Q2 D9 g- m, E' m *      s3c2410fb_set_par - Alters the hardware state.4 @  n$ S/ m" Z  X0 N0 }8 h+ {
*      @info: frame buffer structure that represents a single frame buffer
/ [# [( w1 K7 L! T) ]$ A7 s8 l *                根据var中的值设置LCD控制器的寄存器
* Y% [8 J' _7 v' q# I' m */, ]/ Z' b2 U. Z' p
static int s3c2410fb_set_par(struct fb_info *info)  r* y! h/ o1 S' ]& e" ]' y' t
{
( s: N1 B  C4 A2 k  R, m3 S3 b        struct fb_var_screeninfo *var = &info->var;8 i0 L2 f$ m( b, W

8 v, ]: _6 N# w# [4 O1 p        switch (var->bits_per_pixel) {5 [* D; _% ]8 ?! l
        case 32:3 s9 ?# F0 S6 x" b. b2 g6 ?
        case 16:
) \# n' K% N. P/ k4 T& u* ?* w        case 12:/ E# N$ v3 P' [  T
                info->fix.visual = FB_VISUAL_TRUECOLOR;, l1 F4 M* Q. x
                break;
: f* Q) v9 {+ _8 C3 Q+ \        case 1:
+ e% [& |9 f1 |. o. E4 a' d, c# \                info->fix.visual = FB_VISUAL_MONO01;) v3 Q, o% T/ A/ ^* _
                break;* F% b. K9 r* n
        default:
4 P0 [2 s# I0 `% M6 L8 K7 u                info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
7 j% c2 Z: W: W6 N8 r' j- C                break;$ P! |6 F2 `5 ]; L: `; P
        }8 `2 C  @  A0 k; w: E  w
! a: `/ ]/ J  @; q  ~1 m  [5 h- [
        info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; /* 320*16/8 = 640Bytes */
. z  d; {2 |; B: K3 P3 {+ A5 l
1 q2 x' ?! B2 h2 q( w$ c4 [$ e# W& B        /* activate this new configuration */
) K! l: ]7 _5 f" D6 o' t
/ C7 d7 H8 C/ h6 i; r        s3c2410fb_activate_var(info);
. b) G  ?5 \. X; \' R        return 0;5 s5 C% y0 m; A! _3 J6 B- x4 d) Z
}
4 }( j/ s. f2 t, f  y5 P. y- n0 D该函数根据像素的位数设置了视觉模式,本例为16为,使用真彩色。然后计算了每行的数据元素大小。共240行。
6 a. u5 p; E/ X- r6 N
; M" v; w! T9 T7 \4 z3 h然后调用了s3c2410fb_activate_var来设置控制器并激活LCD。/ U. O1 v5 B' B& E( [

: Q6 @; |9 Y  n  gs3c2410fb_activate_var函数如下:
) o% L- q, h: D1 z: E, `; D# f$ j& k! H; Z6 y- T  _
/* s3c2410fb_activate_var
: `& A" D* z: n; k# m *9 |2 m, p+ H/ o: y
* activate (set) the controller from the given framebuffer
2 q& u! c. Q6 L, K4 ]) D" O; k7 G * information2 e9 ~2 d9 t1 F* W- f# D
*/" G- {  W4 r& [" ~0 H: X
static void s3c2410fb_activate_var(struct fb_info *info)
0 a0 \. N3 g: {2 a5 G  r{0 P' ~2 R) s: w9 @: ]; M
        struct s3c2410fb_info *fbi = info->par;% }/ ?% N. s- r) r& I5 k
        void __iomem *regs = fbi->io;
- E3 A# S5 i  ^        int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/2 Q* W, e( f3 C, ~+ a* o8 B% d
        struct fb_var_screeninfo *var = &info->var;
+ x% ^0 x, ^0 C5 U! @2 g        int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
1 e  ]  S! T( O* {* z& K2 F0 Z0 \) o$ k* ^: t
        dprintk("%s: var->xres  = %d\n", __func__, var->xres);5 K- Z0 V% R7 @
        dprintk("%s: var->yres  = %d\n", __func__, var->yres);" T8 D3 Y7 b: C: y: o) q: H5 ~
        dprintk("%s: var->bpp   = %d\n", __func__, var->bits_per_pixel);( q& e+ C# G) ?; F3 ]: O, Z9 b5 U; F
  t4 I9 k7 u( d" `3 p. R, V
        if (type == S3C2410_LCDCON1_TFT) {
) f0 S3 \9 K$ r; a: H# x: A) j                s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/" \; F. a- w- z2 _
                --clkdiv;! V; [% {) m2 v- c  {
                if (clkdiv < 0)9 G$ `; Y- g* }  d7 i/ b
                        clkdiv = 0;
; A! x2 P4 @6 k# Q7 C# u8 D        } else {
2 v; ?. P: i; `" D7 `                s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);) S1 N8 H2 i6 j1 ~* F0 `
                if (clkdiv < 2)% i0 k# {$ v' V3 l5 }
                        clkdiv = 2;
3 F) |/ e3 w. A; j" }        }
% M$ K4 l7 N$ P$ I3 ~! F8 ?+ c* v. u$ h6 b" u
        fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/8 M" m9 T  l1 o0 A/ G' T4 y
  o& L  o' k/ T0 S  h2 m/ M/ y; I& Q
        /* write new registers */
! L# _# R. V! V$ n! n3 W0 {5 @& P) r& s9 i( A0 a- ^7 c( F/ g
        dprintk("new register set:\n");
- l4 K1 ^) `& d" i7 @% I- X        dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
- t% Q; K# |$ k+ C        dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);! Y' x: P& V# A
        dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
  O) W) k' n' F2 t+ O        dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
9 e& i. u9 Z- F        dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
1 I8 l) V8 G9 \! n        /*把计算好的值填入LCD控制器中*/" e: ]- }( h4 Y  N* r& \( x9 M% a6 y7 e
        writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,       
* S7 p2 h7 T; k                regs + S3C2410_LCDCON1);                                                /*仍然禁止LCD*/
2 d4 a' v: M! }0 t        writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);% n% s. A( i' u% Y# P
        writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
& n: E% F" k/ \' E( c0 {5 O: p        writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
& z4 u) ]4 m  i0 A        writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);% Q6 C. L" a- ~9 m2 t

% I; }" c; E( V) B. t        /* set lcd address pointers */
" S+ M1 }% f* \* b        s3c2410fb_set_lcdaddr(info);                                                /*设置LCD帧缓冲起始地址*/
$ j( T% |5 Z0 v3 j$ n5 T3 s
2 D0 P8 D( w, Z6 V. p) p# M        fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
$ J+ l3 W) C$ y5 |/ A' S- |$ s6 b" B% @        writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);        /*使能LCD*/. }* B  \/ D8 R- @" G% n- a6 E
}" u6 X. U8 H, N
其中调用的三个函数如下:$ y3 w  x' K, V5 E) A; _
static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,$ u$ s) p( {$ r; P, d
                      unsigned long pixclk)
! b" |. C& k7 U7 I1 H) s8 d" P{/ C( G! J+ L) W5 Q/ [3 s
    unsigned long clk = clk_get_rate(fbi->clk);         /*获取当前时钟频率(Hz)*/) t# ?" I, e$ I" k  G
    unsigned long long div;
$ c# V, |$ e" {% e  C
! A2 j# F/ u' U/ j    /* pixclk is in picoseconds, our clock is in Hz
, i/ y& i: |: B+ w     *9 a8 e. |9 Y3 s+ v
     * Hz -> picoseconds is / 10^-129 |2 t; [9 J9 l8 G5 ?
     */
5 i9 o# x/ j# F1 l, H' ?  u
7 P+ u/ d1 j9 f( U: ^7 K" X    div = (unsigned long long)clk * pixclk;7 l1 o* X. O  Y3 Z
    div >>= 12;            /* div / 2^12 */+ e2 h) E+ O! U$ A0 H& z' m. v" Y
    do_div(div, 625 * 625UL * 625); /* div / 5^12 */$ Z0 s: z; ^7 r) [; R- o- V
! y- b* i/ f$ q' S' `) p  C: M
    dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);* k( T  W: V9 {6 b( ]& z/ S$ A  ^
    return div;1 K  j! ?4 ]& J/ K6 p2 K! G/ I
}
' V5 Y  e# c- i4 [* d, s: W7 R& U( E1 M# C9 Z; E
/* s3c2410fb_calculate_tft_lcd_regs% t6 j! O7 x9 m- i, D
*( o' ]5 W$ s) m5 j% C
* calculate register values from var settings$ B3 B" S2 Z/ d2 X( v) P) \  D$ ?
*/( s9 ?  K& ~+ I: H. O& J9 X( j  L6 `
static void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info,7 G9 l0 ?. h, {" ^  I2 r+ C5 U
                         struct s3c2410fb_hw *regs)
2 _1 o, p1 C6 i, T* D{
/ v* B. j4 u5 l) F7 t" b    const struct s3c2410fb_info *fbi = info->par;: u: ?  m, ]' D- X" Q
    const struct fb_var_screeninfo *var = &info->var;: P) a. ^( t8 t

% U9 v& @& i. M- J" W    switch (var->bits_per_pixel) {
9 p8 Y7 O1 _) t3 A* i, P( ?" ]7 p    case 1:
! |) y$ j1 S" M, Y0 S) G* o        regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;" ^2 Q* B9 S/ Z9 S; B% n+ O
        break;) R7 D4 q" F  }  o
    case 2:5 M& T' ]1 x* q) J" h
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;' H' T8 _. S5 W
        break;7 x$ O: F! ]0 o5 B% w2 y
    case 4:% e* ?4 {2 K4 H' x- Q
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;# p6 w; a* ~4 o: Y. e8 a: c5 g
        break;
% K! @5 ]! G0 z4 j; p) o    case 8:- M8 u6 Y, }; p# z, T
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;1 x! E4 X( Z5 `! J# `: r$ c
        regs->lcdcon5 |= S3C2410_LCDCON5_BSWP |
. _8 }9 L+ Q: i4 n% j4 ]                 S3C2410_LCDCON5_FRM565;/ {* K! x5 I2 W' J( @- R: V
        regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;' I3 d* h/ W; p) B0 i! j) ]5 g( e
        break;# \/ b8 h( B6 {3 q
    case 16:5 o) w8 k0 ^. y( A' p
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;( V( ]2 X/ X0 o7 @% p- O4 b
        regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;
/ S% h) K: h0 E0 \        regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;5 h8 N8 w) F, A- z# R( ~: `1 q' ]
        break;# Z6 D0 r0 R( h6 x- ]& E8 i/ X
    case 32:% c; i1 d6 I* J
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;7 U' d. b* U3 M& g7 x- C' W
        regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP |
: p% z, N2 J- o; p3 `/ m; M                   S3C2410_LCDCON5_HWSWP |5 F0 |: I8 d* }3 A1 e
                   S3C2410_LCDCON5_BPP24BL);& d) k: H& q: v5 U' N7 J( y
        break;4 W0 y: ?- z9 P5 b: U2 j
    default:6 y1 l$ P: b( n: d" f  x- G" s/ g
        /* invalid pixel depth */! m5 \3 [7 C8 s/ B7 F
        dev_err(fbi->dev, "invalid bpp %d\n",
+ Y' o& R& N4 H% A            var->bits_per_pixel);, q/ u- G0 j$ P1 L  T' S/ x/ }- ?6 o
    }
4 X4 c* @% |! L  u    /* update X/Y info */
, y% l6 q1 a" ~8 C) s9 q% O7 t. ~    dprintk("setting vert: up=%d, low=%d, sync=%d\n",
; E7 Z# q; Q& z7 S$ Z! W9 p        var->upper_margin, var->lower_margin, var->vsync_len);
0 a) E' O! y" ~
) }0 t- X7 U: _# z- r3 t9 J: G7 r. ]6 {    dprintk("setting horz: lft=%d, rt=%d, sync=%d\n"," q, @4 b( Q6 Y: f" O7 u
        var->left_margin, var->right_margin, var->hsync_len);. \& \2 \' a4 o) @
    /*/ a9 N4 _$ F" s& |
        所有时序参数必须减1,因为在公式中:8 N, h1 P+ B+ x! c8 K9 ~3 H, J( r# q1 H* E( T
        Frame Rate = 1/ [ { (VSPW+1) + (VBPD+1) + (LIINEVAL + 1) + (VFPD+1) } x {(HSPW+1) + (HBPD +1)6 h* d! d6 |; i+ S. W( p2 c- P9 \: j, B
        + (HFPD+1) + (HOZVAL + 1) } x { 2 x ( CLKVAL+1 ) / ( HCLK ) } ]; E5 b. z% @. P! w
    */
3 T9 _0 g: j6 q+ h" W    regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |6 N& e! D6 f3 ?8 ]0 m! `7 S# o
            S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |
3 C* r' s- r! S/ P            S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
/ u( G$ g+ V  r2 ?            S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
2 o8 L! a3 z! i
/ }! n3 ^- L: Y0 W2 o) g    regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
: M$ O  |$ ?1 A" u) x) O            S3C2410_LCDCON3_HFPD(var->left_margin - 1) |
, Y, D+ Q0 x" `6 G( E9 W            S3C2410_LCDCON3_HOZVAL(var->xres - 1);
% n! Y9 d3 l- h1 g" o& ]2 m# W2 }& d2 J
    regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);
9 C1 @6 n5 ~4 k: Z, w; k0 k}
7 H2 r1 G. }2 n# G4 H) z. d% B2 x# l5 a% D. [; `
/* s3c2410fb_set_lcdaddr
. r9 G/ @& F: \: b# g+ U4 @+ J, N *
. v( L5 x1 E+ s4 C! Z$ r * initialise lcd controller address pointers! c/ j* D6 F1 a1 l$ F5 ^
*/) h) W' \0 h- K) B
static void s3c2410fb_set_lcdaddr(struct fb_info *info)+ K8 w1 J' q- b8 x, G: ~
{
* x  j' p1 C+ K        unsigned long saddr1, saddr2, saddr3;8 P! L" O, p2 U' F/ \5 _; c
        struct s3c2410fb_info *fbi = info->par;
6 i  u. z' H; A        void __iomem *regs = fbi->io;2 Z' a* k9 [- I) S
" _( w; F" V" ^- r8 [, C
        saddr1  = info->fix.smem_start >> 1;          /*帧缓冲区起始地址*/, t/ b! J  S/ J  I2 c8 r7 h
        saddr2  = info->fix.smem_start;                       
: M  y- R2 L% d4 x2 x+ j        saddr2 += info->fix.line_length * info->var.yres;% `6 p6 [! d- C$ j/ q7 M5 ~. f
        saddr2 >>= 1;                                                        /*帧缓冲区结束地址*/
* O# [) }5 U  {0 e/ b- t$ G
, N( G$ [- s' e0 |9 v        saddr3 = S3C2410_OFFSIZE(0) |, D. g( f) E, i! b2 w# u
                 S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff); /*offset = 0, pagewidth = 320*/. g, o! q7 \1 \) j! }" H. p
/ M& U/ J; B( [" M5 v
        dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);9 \  y! N* c' V2 p7 B9 m6 y" T
        dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);
; B: D% z4 Q/ F9 [        dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);' d& g$ t4 R9 R6 w3 L3 Q! n
* u1 q7 T  D0 }- A3 U5 `
        writel(saddr1, regs + S3C2410_LCDSADDR1);
( O, J1 `+ z. _0 F/ T0 N' ?        writel(saddr2, regs + S3C2410_LCDSADDR2);
/ H1 ~# [- z+ p* j        writel(saddr3, regs + S3C2410_LCDSADDR3);9 J/ C" s' K9 G+ C7 b
}1 i. U6 \' t  J: V' p; N
s3c2410fb_calc_pixclk用于计算时钟频率。
2 f5 D" A3 _# }* Q7 r1 p  J
. I2 s' K; O% W0 Q1 j' ^0 ms3c2410fb_calculate_tft_lcd_regs设置了LCD的控制寄存器。9 l# u! L* V9 B# q" e' R$ q7 m
' X/ |2 g. x6 l% S' V' s9 t
s3c2410fb_set_lcdaddr设置了LCD的地址寄存器,该寄存器的设置请参考datasheet。
; X& p2 z7 K$ z. H5 o- h! |
' f3 Q$ s, `+ s) z4.3. 2 s3c2410fb_blank5 J6 A  P9 p& D+ [! T9 d' j
该方法完成消隐功能。% e' `7 @. b) u0 S% T' _3 R! `
5 J" ]6 U% R1 I# E
/*
& r! r4 `2 E9 _ *      s3c2410fb_blank
4 H" o- D2 W+ d3 { *        @blank_mode: the blank mode we want.
( w' k) c: d7 {1 { *        @info: frame buffer structure that represents a single frame buffer
1 y- T7 l+ V% C7 Z" Z+ r *
# p5 u% ~  L) c- U' l4 c *        Blank the screen if blank_mode != 0, else unblank. Return 0 if
. H2 ]# H% Y; x4 C2 N! F *        blanking succeeded, != 0 if un-/blanking failed due to e.g. a2 c5 ^9 O: O3 U# l, Z( ?
*        video mode which doesn't support it. Implements VESA suspend/ v' U9 R! |9 ~8 o3 c% f
*        and powerdown modes on hardware that supports disabling hsync/vsync:
9 V- }8 f7 R  N7 K( w *+ c6 x& x: r, k: S( U- c
*        Returns negative errno on error, or zero on success.5 M3 B% \, O! k, Y
*
9 p: [2 p. O" g' p( G: |* _! M/ o */
% k) ]: B- Q3 i, ]static int s3c2410fb_blank(int blank_mode, struct fb_info *info)
- y! N" T3 `3 Z6 S% K% l: K& Z, e{! z7 ~: n( y. p5 c7 S1 _0 g" z
    struct s3c2410fb_info *fbi = info->par;$ [& W: i( {5 N$ g- ~+ E: b
    void __iomem *tpal_reg = fbi->io;' M. d& q2 Q  H, Q7 @* ?0 k! m

8 J) ~  P& s7 X    dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);% p3 @3 x& Q( G9 w- {# T! C( x
  k' \5 H$ o/ R: I/ P4 \
    tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;0 X$ ^' \, _( C; @

% p- Y3 @7 M( l6 }- Z    if (blank_mode == FB_BLANK_POWERDOWN) {    /*消隐*/) u# _- J  i3 l
        s3c2410fb_lcd_enable(fbi, 0);        /*禁止LCD*/! K+ V& h/ I2 A
    } else {( D+ Y! O' y$ j+ E' h+ Q/ O
        s3c2410fb_lcd_enable(fbi, 1);        /*启动LCD*/
" [) b. e% m% E6 P) K2 V7 F    }" N+ E7 l# B3 T) p" W5 I

5 M5 k$ [4 a. l9 Q    if (blank_mode == FB_BLANK_UNBLANK)        /*去消隐*/
% z# d) `8 v. I        writel(0x0, tpal_reg);                /*禁止temporary palette*/
: s' A+ F, f( Y/ b" `8 N    else {                                    /*消隐*/: `3 ~2 V; k: p2 G6 g$ g
        dprintk("setting TPAL to output 0x000000\n");
/ Z6 D: I& s6 \        writel(S3C2410_TPAL_EN, tpal_reg);    /*使能temporary palette,颜色为黑色*/
& J% Q! Z( e: I6 e2 C9 T2 b& u    }/ m) W, U+ A$ b/ r, c  n
" z/ {4 f# Y- K& T/ l( ~$ ~7 E
    return 0;# r' D" m  G$ g7 {
}
0 i" W5 g: V6 i3 i1 Z在消隐时,屏幕将全黑。  G! y$ [3 s) h
" p: u% ?: h8 u
4.3.3 s3c2410fb_setcolreg) j; c  r/ A2 \, s1 `0 v0 O
该函数的功能用于设置LCD的调色板。调色板的概念请看我的转帖:LCD调色板。
2 e3 R6 C2 B/ g6 _* ]
2 E& j! _# w" B& mstatic int s3c2410fb_setcolreg(unsigned regno,
" s, K5 a2 ?4 \                   unsigned red, unsigned green, unsigned blue," X% e( m( `& c! Y
                   unsigned transp, struct fb_info *info)
: i( ~- _3 q: l" q{
- l; C3 t; w  \% V6 P0 q# ^2 S    struct s3c2410fb_info *fbi = info->par;    /*par指向s3c2410fb_info*/. i* }6 h# b1 W: p% Z# J' x4 o' I
    void __iomem *regs = fbi->io;! Z5 X3 ]: e* q% d
    unsigned int val;
- n2 x. L6 b6 E0 n; n/ P0 B
0 }+ o, P% ~2 q+ y( h% `$ J    /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
" \& {0 U1 g2 g5 Q/ V8 R) a- f" t           regno, red, green, blue); */
% I4 y- Z8 ^+ E) d- R3 N. A
) n1 U3 Z' z1 f) e8 c9 e    switch (info->fix.visual) {   
7 b1 f, _* x' i/ m5 v    case FB_VISUAL_TRUECOLOR:    /*使用真彩色*/
4 D3 b9 r0 u  j  r' f; e        /* true-colour, use pseudo-palette */' J- w( {3 S2 U8 ^8 y
8 v* t+ ?6 ]" M
        if (regno < 16) {    /*16种颜色,为什么只用16种颜色???????????*/
" m3 a- C$ {8 p; J/ z! J' t- Z' M, X            u32 *pal = info->pseudo_palette;
. e, h2 T  \6 i  c- A
% t1 ?' l( {# @; d0 \5 J            val  = chan_to_field(red,   &info->var.red);
* W( L) E" |& R0 m! V+ T3 i& q- o            val |= chan_to_field(green, &info->var.green);
7 ~0 `! b5 c, i6 |. ?            val |= chan_to_field(blue,  &info->var.blue);
: g7 f7 Z( s5 y# T8 ?9 O- _! d) B8 }' E) }9 g+ B" t' k% M
            pal[regno] = val;    /*保存颜色值*/% ^8 f3 ]( p7 ^( \: P- K1 O' X
        }. }: L4 m# L* P( B# g6 J1 E
        break;$ G4 n& w) b* \) M0 U' r1 c
5 j6 e# p7 L: b$ ^
    case FB_VISUAL_PSEUDOCOLOR:
$ Q9 I9 U2 |+ K' d# q+ e& s        if (regno < 256) {
3 |# a$ E4 T! D& o* m3 `            /* currently assume RGB 5-6-5 mode */; p3 e  w. }  g4 Z7 N, o+ Z( y
4 S% r$ E% E5 a$ m9 j) }: p& Y
            val  = (red   >>  0) & 0xf800;+ D4 q0 H: |( X, h( q
            val |= (green >>  5) & 0x07e0;
: L0 K% Z% Q, T2 T0 N/ H            val |= (blue  >> 11) & 0x001f;3 L+ }, n9 s& c6 ~8 {5 ]8 s. X

. N$ l* q2 X6 J5 Z  J1 Q6 T& l9 ~            writel(val, regs + S3C2410_TFTPAL(regno));& h/ Z/ n6 h- `' a/ v/ E
            schedule_palette_update(fbi, regno, val);
2 f2 ^$ ^# z; F, m: n        }
# y, \4 y* c7 k, ~. N
( g4 G& T' n* R; k% `  J        break;# ~, U  t% A5 ^3 G7 h5 F/ P

  m' [$ i* t" e' M& g5 @& n    default:
7 Z7 K1 z$ |: x4 F* Q" Z' C4 K        return 1;    /* unknown type */5 x" z0 B0 D# P2 @5 \# t
    }
1 w) ^" F( L+ m
4 h: V1 F! ^5 r4 h! a5 m5 M    return 0;$ q" l$ E2 n% m& `3 D
}
  g& @2 A5 A' n1 T: v/* from pxafb.c */
! V$ `: C9 q8 l3 Fstatic inline unsigned int chan_to_field(unsigned int chan,! M! n* H7 V, f
                     struct fb_bitfield *bf)9 w& r0 V- p* q9 q9 I7 N7 V
{    4 ?2 W5 V8 R* n+ U2 @& f3 U2 ?
    /*下面用到的length和offset在s3c2410fb_check_var函数中设置*/
3 s# @9 f1 j6 L. c    chan &= 0xffff;                /*取低16位*/
/ [- i6 a( G, B2 @9 A    chan >>= 16 - bf->length;   /*取第length为到16位为有效位*/
3 z7 M, P- E9 v    return chan << bf->offset;    /*移动到相应的位置。*/
( I- V  k7 w. H}  Z, l8 J! A2 A' K7 d8 l
我们使用的是真彩色,可以设置16种颜色,颜色的位域值通过调用chan_to_field获得,然后保存了颜色的值。但是比较奇怪的是,这里并没有将值保存到0x4d000400为起始的内存中,不知为何。 反而倒是在伪彩色模式下, 使用了writel(val, regs + S3C2410_TFTPAL(regno))写到内存中,然后调用schedule_palette_update函数来更新palette表。我们来看下。
& ]: \( V' R# e) ?/ i' T. ~0 o# U
& t$ R* U& x2 c( l- pstatic void schedule_palette_update(struct s3c2410fb_info *fbi,
, ]/ F2 n: S* o4 e: i                                    unsigned int regno, unsigned int val)
0 L+ D/ W" o. b6 [3 Q2 R; s{
6 U" R. D$ }5 R- [6 l+ u0 v2 L        unsigned long flags;: \- `2 s/ u8 r
        unsigned long irqen;
5 B6 u+ Y2 N5 c: @3 R) F! q        void __iomem *irq_base = fbi->irq_base;
. V  C3 f4 ?" S" {9 P* A, e
# @) Q  s5 L$ z# b# i$ g  M        local_irq_save(flags);
: o( Y3 I5 Y; O- B& x+ T2 m6 {# h; i/ s: x3 p. ^
        fbi->palette_buffer[regno] = val;+ O3 j% a3 l3 G4 K0 v/ V

3 u" q! R( f: G: U, T( r& f8 a        if (!fbi->palette_ready) {
1 P- E, V$ L+ k& j0 r5 N5 @                fbi->palette_ready = 1;4 o2 ~% @+ F0 `/ [8 \% J. C: P
# h) ^- L: h6 J4 V' W
                /* enable IRQ */4 B. b1 T9 c/ `; [
                irqen = readl(irq_base + S3C24XX_LCDINTMSK);; h+ S) f& N1 H' L' f3 R
                irqen &= ~S3C2410_LCDINT_FRSYNC;
% ]% B5 R' x* \, D7 a                writel(irqen, irq_base + S3C24XX_LCDINTMSK);
9 Y! ~# b; M( D2 B! o5 T" s        }. M2 y* Y! F2 t  W/ c

9 T! Y3 U9 S1 l4 m! C4 r8 f4 P' h9 a$ l        local_irq_restore(flags);0 v' W+ N) h2 }" D  n
}
* t) I1 @' x$ n这个函数的作用就是开启了LCD的帧同步中断,这个中断在VSYNC信号从无效变成有效时产生。当中断产生时,会调用在probe方法中注册的ISR。ISR如下:
- ?* t# g- ?% l, ~$ g/ D  M4 |static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)5 o: s5 k$ d+ y- t0 f
{! Q8 H' Y0 e9 `: i. F- {
        struct s3c2410fb_info *fbi = dev_id;6 Z8 r  \: j1 @) m7 q: r. Z
        void __iomem *irq_base = fbi->irq_base;% O( a1 B1 s% S# i: G: v) j+ u% R
        unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);
) o7 V! ?7 J8 a) b" @% @# wb
; V& N# J) \4 g' U" [        if (lcdirq & S3C2410_LCDINT_FRSYNC) {. _: W* g3 i( L6 w( s# j( _
                if (fbi->palette_ready)
4 r1 y' I/ O6 [1 O3 Y                        s3c2410fb_write_palette(fbi);: Q* t1 w( |: l# J1 i% T# s
+ k4 `/ _, w' \" e. G6 r- m
                writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);
/ Z& b' u- H- z% k3 G* B4 `                writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);
; [- e! c3 t6 ]+ `" y$ U        }! X: N) \% @# U1 R6 [

* {8 ^" P1 d( y# ]% i# p; a        return IRQ_HANDLED;8 |  ?* Y9 b! Y: l+ o
}
" a7 x; O  j# e" I5 p  Y% l4 b1 j: C) A
static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)
/ r" }+ R* x: C3 v{6 R" W$ W4 Z! a! v' t( d' B
    unsigned int i;
6 n& v+ T' Q. Q5 |2 E# q  n    void __iomem *regs = fbi->io;4 U* g# P$ d* G

$ |! {: `) m; b    fbi->palette_ready = 0;  /*清除ready标志*/
' z# E0 b* a6 ]- A9 o5 t
# E1 h" u1 o2 j& ]# l    for (i = 0; i < 256; i++) {6 o7 z9 r7 P6 \" m& a
        unsigned long ent = fbi->palette_buffer;/ o$ Z- _# l1 D3 F% _# \
        if (ent == PALETTE_BUFF_CLEAR)
- \) o. A: u% n4 \            continue;/ q5 I" b$ G$ ?! k) ~- F8 D1 W
% T6 ]6 ?) o) m* d. P6 w1 J
        writel(ent, regs + S3C2410_TFTPAL(i));
# n2 E! T! u9 J5 F! r
9 f7 _) ^! b7 Y! p4 [( i( C/ f0 O        /* it seems the only way to know exactly9 X# a# R. u5 [5 N9 f& f4 @7 C
         * if the palette wrote ok, is to check
5 t2 ]5 Q9 \* F/ Y5 d         * to see if the value verifies ok3 s0 A7 c1 M" R: S8 i7 `7 g/ Y
         */
' Q6 ]0 D; ]5 ]& ^
; J# e+ P, d5 m3 }+ W/ m        if (readw(regs + S3C2410_TFTPAL(i)) == ent)
$ d# H- C1 x. ]( ^            fbi->palette_buffer = PALETTE_BUFF_CLEAR;! J8 z0 D, ^! Z3 K
        else2 a5 a. `6 F6 x4 U; G$ u
            fbi->palette_ready = 1;   /* retry */
6 q, L8 |% X0 W7 V# |( b4 \1 R    }
& M8 o7 o4 [, O( G}5 ?! [& L& |4 [9 q
在中断函数中调用了s3c2410fb_write_palette,该函数对写入内存的颜色值进行检查。如果确认其已经写入,则将palette_buffer中的清除,否则retry。
) d" F) A' ~) y' c
& D" ]  V: T# Q+ d7 r5 I5. 总结( C" L+ T: P) L1 U
本文对frambuffer子系统做了简单的介绍。frambuffer子系统的函数相当之多,在这里是不可能一一介绍的。本文首先介绍了主要的数据结构,
. C& W. ^5 b" ~, u" q7 W/ V, j3 c% w; A2 K
随后分析了frambuffer核心层,在核心层简单的分析了5个API函数,接着,对驱动层做了介绍,驱动层的大多数函数都给出了分析。2 }/ i* ?: w% G, u9 m

; R1 F5 b3 h$ `. H$ {
; i/ n; k" m3 H/ g
! J) R9 n1 j( ~( o- n) n7 M9 l3 O+ [" K3 `3 Y. p+ g- m3 V, f

# z) s# v' V8 x* p; [
7 C& F0 k/ M" \/ P, v& C$ `9 G: }  c
! P0 h/ f; A0 k7 g0 N$ ]; |

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-11-25 22:19 , Processed in 0.296875 second(s), 27 queries , Gzip On.

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

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

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