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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
本文将介绍Framebuffer子系统. B. w1 X# W) u  r
0 t: I1 _7 k' |( A
目标平台:TQ2440 CPU:s3c24404 F. d/ ?# w, P5 e
  n" q9 }6 y/ l! \- r9 w# b% u
LCD设备:3.5英寸,分辨率320X240$ t) W2 N4 O9 q* W0 M# c
" w3 W8 A  u8 x& ?
: ~! ?% B; J: y- L3 n; e

7 x; k1 R7 k# x4 d1. 概述0 ^# u. @: \  U6 N' z7 b
9 `4 T$ ?2 @- @& t: n+ v( X1 P7 v
Framebuffer,中文名字是帧缓冲,这个帧也就是一副图像所需要的数据。因此,帧缓冲其实就是LCD设备的驱动程序。Linux中,framebuffer子系统框架如下:- ]1 x* J% b/ B, b4 ?

/ ^" p8 C9 }# \" X 7 Z. ^5 }/ @! S# T$ p4 ~

/ @: Q3 Z, l' V9 E' _( c核心层的代码以fbmem.c为主,核心层包括许多与具体硬件无关的代码,并且提供了API给用户空间。用户空间使用系统调用,系统调用会使用相应的API函数,最后会调用驱动层实现功能。对于不同的设备,驱动层的代码将有所不同。
% N2 O) e  {; y, Z* c1 G: \1 h  o0 f2 Q" n
接下来的内容中,首先给出framerbuffer使用的数据结构;随后简单描述framerbuffer核心层;最后,针对S3C2440,对驱动代码进行分析。
1 Z8 ]+ ?3 U4 s: X7 W7 h
6 N  ~7 q: p  K, O6 y2. 数据结构
7 b8 f5 W- x! J% U) `6 g+ D2.1 fb_info 结构
+ C4 \) I- n) O$ h: s9 Y, O2 {: D  该结构是内核用于描述一个特定framebuffer设备。其中包含了几个重要的结构,将在下面介绍。: w6 b: R. Y3 w) K2 u/ s

- o' w  y& B& G. U! {7 |  下列代码位于include/linux/fb.h: t7 h; _9 v+ t# `3 h
" e3 F( Q0 E, `; P& U* s4 A6 }
struct fb_info {1 I7 U7 C& w3 R5 W5 J, ?# S& u6 }
        int node;
1 j0 n6 y4 O! p$ A- `        int flags;: j, _& T% z, i2 Q0 A* j
        struct mutex lock;                /* Lock for open/release/ioctl funcs */5 w2 P) q* p$ i  I! n/ K
        struct fb_var_screeninfo var;        /* Current var */
. F/ ~( K6 N. M3 D% ^# N        struct fb_fix_screeninfo fix;        /* Current fix */
  t) X% o; F$ s6 T- ^& o        struct fb_monspecs monspecs;        /* Current Monitor specs */. p. F7 G( F/ h1 H
        struct work_struct queue;        /* Framebuffer event queue */* B3 Z, D+ p4 ^, d3 k: s  v
        struct fb_pixmap pixmap;        /* Image hardware mapper *// ?& y: D+ i, X6 q. o8 r+ D9 C1 Y
        struct fb_pixmap sprite;        /* Cursor hardware mapper */1 l2 o" h8 X% c4 x+ ~1 U1 T" x
        struct fb_cmap cmap;                /* Current cmap */
3 k" ?; V+ m& {/ S5 V- |# P' K        struct list_head modelist;      /* mode list */( W- M3 z. T9 \9 _: e% n
        struct fb_videomode *mode;        /* current mode */' R4 }5 w( W- h. }: r6 s; h
, }" |% e' M: ?) O1 S5 \1 H
#ifdef CONFIG_FB_BACKLIGHT
$ k8 k# B4 K$ U% o        /* assigned backlight device */
4 R/ S1 D. F) N8 N        /* set before framebuffer registration, % w4 N3 _8 k7 N0 E
           remove after unregister */1 O- ?# F" W. A$ S0 C
        struct backlight_device *bl_dev;
" g6 \6 X9 R+ n$ j
: P- n& Z! K9 ~        /* Backlight level curve */; [! \# p& Y; B, A" E
        struct mutex bl_curve_mutex;       
3 h# `1 Z3 a' A* o: i, A        u8 bl_curve[FB_BACKLIGHT_LEVELS];
) j" n  ^- W* c* m#endif
7 T/ s" ^! O# Z9 Y( L0 Z#ifdef CONFIG_FB_DEFERRED_IO
& j4 a' c. W, g4 g4 X! H" B        struct delayed_work deferred_work;
  m/ V+ g. S9 f: I# _/ F; O        struct fb_deferred_io *fbdefio;
6 p) `6 i( O+ \( t# Y- c# q#endif
1 a( _; h- A8 }) v& F
6 L. R1 O- U8 P        struct fb_ops *fbops;7 \& n9 y. O& n
        struct device *device;                /* This is the parent */' @  J( n/ t$ {. Y  P' l) _" c
        struct device *dev;                /* This is this fb device */7 I# e  e2 f) R; v; H* }1 c! @6 Q$ ^; ^
        int class_flag;                    /* private sysfs flags */
6 S- h( L) u) l# _; W#ifdef CONFIG_FB_TILEBLITTING8 M' ~6 v4 [6 P5 u6 g0 W
        struct fb_tile_ops *tileops;    /* Tile Blitting */
* v* J5 U, j0 \  x+ m3 y1 u#endif7 s4 I% D9 L6 x5 y  M" \
        char __iomem *screen_base;        /* Virtual address */
9 Q& a3 c) y* Y* ?+ u        unsigned long screen_size;        /* Amount of ioremapped VRAM or 0 */
' ^# N. l( P# m" {* Q% h5 m        void *pseudo_palette;                /* Fake palette of 16 colors */
: I  S: q3 N; d# I, H7 |#define FBINFO_STATE_RUNNING        08 v3 z: B( r, f, W
#define FBINFO_STATE_SUSPENDED        1- L& u5 |7 C, u" X# S
        u32 state;                        /* Hardware state i.e suspend */% B9 S7 W' ]2 H+ b$ E
        void *fbcon_par;                /* fbcon use-only private area */
: D" V4 G- r# {7 Y5 N        /* From here on everything is device dependent */0 {( Q/ \5 s: K
        void *par;        9 h; e: Q* W* |, I4 H
};
  [7 `/ G4 j0 O6 V4 j2.2 fb_fix_screeninfo结构
; H2 q3 o/ \3 m3 `4 [) q4 c  v! y0 J4 d  该数据结构是不可改变的,也就是说用户空间不能改变该结构中的任何成员,在核心层我们将会看到这点。( h1 _2 x# U# ~/ j, U3 Z

8 \5 p! l: J4 J# a  \  下列代码位于include/linux/fb.h
& P( s1 k1 B5 |( z8 W0 h9 E" v  A( e- N7 o: B) T5 k: b
struct fb_fix_screeninfo {
. n1 w9 @0 t. @8 I9 S. s        char id[16];                        /* identification string eg "TT Builtin" */- a; F: U; R/ m3 W* d! B
        unsigned long smem_start;        /* Start of frame buffer mem */4 W) m7 J! j! U; E& @
                                        /* (physical address) */% Q0 V+ N0 O" o- ~, K5 R# S
        __u32 smem_len;                        /* Length of frame buffer mem */
% H+ Q( V. t' M3 m4 E        __u32 type;                        /* see FB_TYPE_*                */) U# O8 S- {. O) ^
        __u32 type_aux;                        /* Interleave for interleaved Planes */
% G2 [8 q) F! K        __u32 visual;                        /* see FB_VISUAL_*                */
) X( u- f: p; v1 Q2 U8 V        __u16 xpanstep;                        /* zero if no hardware panning  */- S6 z8 I4 r7 x& ~$ h" B
        __u16 ypanstep;                        /* zero if no hardware panning  */. N2 }" i+ C7 s1 L
        __u16 ywrapstep;                /* zero if no hardware ywrap    */
6 G) A' W3 T) ^# Y! h        __u32 line_length;                /* length of a line in bytes    */7 ?% k$ F2 r& l. S+ l. V& N1 z
        unsigned long mmio_start;        /* Start of Memory Mapped I/O   */
5 O/ C9 u0 g! x$ v  h                                        /* (physical address) */
. @/ L: n1 A; U) W( l2 j) k' @: y        __u32 mmio_len;                        /* Length of Memory Mapped I/O  */
4 |4 R. A. F, E! ?5 @        __u32 accel;                        /* Indicate to driver which        */* r1 C2 K! |( S' X( r
                                        /*  specific chip/card we have        */5 y0 z& T" G8 M* B/ K8 A
        __u16 reserved[3];                /* Reserved for future compatibility */
% r: v  ?. I/ @* U" s* n};
3 {! ~# ~9 R4 S. \0 J3 x% ?1 n  ~, ~! r5 x$ X3 _$ ?  x
2.3 fb_var_screeninfo结构
! e0 b4 N' ^# J) L  该数据结构是可以改变的,也就是说用户空间可以改变该结构中的成员。该数据结构中的很多成员就由板级信息复制而来,在驱动代码中我们将会看到这点。3 |+ {% A8 w: t' l5 J

+ y. Y) B" \9 `( w  下列代码位于include/linux/fb.h
, f( d4 J$ r5 l: L8 f
& |! E7 }0 h3 }2 c$ D3 H+ N+ mstruct fb_var_screeninfo {
* s5 q: {  f8 U! J: m% [) X        __u32 xres;                        /* visible resolution                */* e$ [# Q4 @8 G1 T! H& G
        __u32 yres;
, R/ V7 o, m& X" T7 {  E3 J        __u32 xres_virtual;                /* virtual resolution                */: N" y3 C/ n% Q/ G' U
        __u32 yres_virtual;
7 _/ M; t5 i: A& x        __u32 xoffset;                        /* offset from virtual to visible */
% I: m9 m7 ^4 e/ k        __u32 yoffset;                        /* resolution                        */  }, F2 w# h1 ~1 n; u2 K$ H
! d7 q8 I4 m4 ]9 z* U1 _
        __u32 bits_per_pixel;                /* guess what                        */
7 x2 [3 y) k. q' P        __u32 grayscale;                /* != 0 Graylevels instead of colors */5 [4 b$ u7 a" E
4 H7 u, _. g8 _3 e* w) H" P
        struct fb_bitfield red;                /* bitfield in fb mem if true color, */
4 V0 ^& r6 P7 b        struct fb_bitfield green;        /* else only length is significant */
: A) Y8 R8 Z1 E! o1 K( p        struct fb_bitfield blue;
9 m9 D" g+ e5 O) I( c+ C% Y  t  \        struct fb_bitfield transp;        /* transparency                        */        7 [9 \" n7 l3 B7 K
. t* u5 n$ @' F4 P* X4 |
        __u32 nonstd;                        /* != 0 Non standard pixel format */
( S+ i" O# _5 M- f% q% K2 p% a% a; h* a; A
        __u32 activate;                        /* see FB_ACTIVATE_*                */$ I* [! B, k4 B' u

! R1 y5 r) s4 q/ d& R        __u32 height;                        /* height of picture in mm    */
8 b# ^! I$ N: d9 L0 }+ w        __u32 width;                        /* width of picture in mm     */
: c% M+ K4 }: J& l- }/ L* x0 b; j1 c0 G& s3 `1 h
        __u32 accel_flags;                /* (OBSOLETE) see fb_info.flags */
% ?5 ], X; D& z5 Q4 k' \: y) u6 {5 o* |. n' x, X& F
        /* Timing: All values in pixclocks, except pixclock (of course) */1 i, j+ @0 _. a; |; g3 u" U# M. l% \
        __u32 pixclock;                        /* pixel clock in ps (pico seconds) */, X  b' {% {4 Q1 \
        __u32 left_margin;                /* time from sync to picture        */# G" S) X3 q4 @8 ?
        __u32 right_margin;                /* time from picture to sync        */$ ~" ^( K9 [2 g
        __u32 upper_margin;                /* time from sync to picture        */& p: d4 b: n' n* Y; ]' P) I9 e' e) o
        __u32 lower_margin;
$ G2 J4 p6 P5 Z" M( V2 T/ R& L, n5 h4 L        __u32 hsync_len;                /* length of horizontal sync        */
8 E+ w. S" A" n4 w        __u32 vsync_len;                /* length of vertical sync        */0 x. w% W/ b9 y
        __u32 sync;                        /* see FB_SYNC_*                */
! p$ M1 z. f" C4 U        __u32 vmode;                        /* see FB_VMODE_*                */
5 n# e" _* r* g- n# \/ L3 L. a0 t        __u32 rotate;                        /* angle we rotate counter clockwise *// P/ r3 C  m: {" U! h
        __u32 reserved[5];                /* Reserved for future compatibility */
9 o1 C3 W! Q0 a};" U5 q. Y8 F2 o' z, ]( k* O
' }# w5 G1 G$ T/ J/ [+ a3 h( {
2.4 fb_ops结构: `* P% ~4 @# s: s; ~" F
  该结构描述了用于fb_info的方法,这些方法中有些是要驱动程序提供的,而有些可以使用内核提供的方法。- b- R% C9 u* K. y5 p" L4 ^
4 m# R3 B8 a: L/ O' ~( Z
下列代码位于include/linux/fb.h
& x. E/ n3 M/ T$ l4 [( l. S3 b: U% }8 y  {/ C0 K% ?, l; U
/*0 b- F& U4 @8 M: `8 A1 Y% |
* Frame buffer operations  _- Z0 l& d/ \, d/ U# [
*+ B4 c" C, \& L
* LOCKING NOTE: those functions must _ALL_ be called with the console
0 K# ]8 K$ {+ E * semaphore held, this is the only suitable locking mechanism we have$ {4 d5 V- L. s, P
* in 2.6. Some may be called at interrupt time at this point though.
/ H: Z. m& _- \/ u */3 J' ^) C! P* U* ^3 t
. I% u- u, x) k
struct fb_ops {
! x4 W8 t& ~. O% T1 @# j        /* open/release and usage marking */' }" ~2 `. `; A* H+ W
        struct module *owner;
6 |- Q) V4 b$ K/ j) a7 v. i        int (*fb_open)(struct fb_info *info, int user);; [2 C7 g8 S3 y7 d3 g. J& `
        int (*fb_release)(struct fb_info *info, int user);
$ R, R* l! C  Y$ q# P
5 v9 t7 i% g5 w2 \' \        /* For framebuffers with strange non linear layouts or that do not
+ ~# ^" M; c- D         * work with normal memory mapped access
4 A8 l! n: v4 v5 f3 u5 D         */. [. _% z0 W1 l4 w
        ssize_t (*fb_read)(struct fb_info *info, char __user *buf,0 R% F/ r8 |5 O' l
                           size_t count, loff_t *ppos);& x5 D( V3 P. R
        ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,+ {" O" i3 P) t0 d' C6 }- ?0 a
                            size_t count, loff_t *ppos);$ d# L/ r# f# ?/ s# U: D
" I, e4 g9 Y6 c* x! P  w
        /* checks var and eventually tweaks it to something supported,
, x2 Z0 f# ~. j         * DO NOT MODIFY PAR */
' m3 B, T- k. g. {" X- V* Z        int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
2 t. t1 ^, \; {8 F, k1 j4 l7 ]# x
2 z( F  [5 `7 C' v0 S% ~6 Q! W        /* set the video mode according to info->var */
1 X; f) m1 z8 y7 U& T, I$ V2 z' W, C        int (*fb_set_par)(struct fb_info *info);
9 j2 E0 [- a6 S7 z# B
  @: r2 t  |7 E8 y7 @# C5 M        /* set color register */
+ z; z6 c3 I# B        int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
0 X1 A7 Q; S5 y! A                            unsigned blue, unsigned transp, struct fb_info *info);
- p# x' \1 t& ?8 Q: L2 D/ I$ |" q- L3 g6 w. u# n: M( h6 P
        /* set color registers in batch */0 @' t/ q: O# ^8 e, U& _- ?
        int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);, i1 x1 A" n5 m% B: `" A. K
! ~- r' S" J) _9 S/ j) a  Y
        /* blank display */. V3 {# X) n" b% b5 i
        int (*fb_blank)(int blank, struct fb_info *info);' w8 C+ [0 B! P8 f8 q

  |0 t! j% U3 g* T4 a3 N        /* pan display */
6 E1 V; G) n7 k  I        int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);+ p; V9 {5 l4 u( r8 l2 A
# A( A8 c4 J0 x
        /* Draws a rectangle */8 |  g. Y' a! n2 z* K5 f
        void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
3 G: x9 X6 i+ I0 Q+ F* H4 b8 H3 V        /* Copy data from area to another */
' m# U( y& X- ^4 {( _        void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);  s) [) F) ?1 j. x8 v* @
        /* Draws a image to the display */  u8 l6 t1 {7 r0 f/ v$ q! @
        void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
3 H" {' `( M$ V2 l$ @: b8 g( ~; a6 |7 v! g2 L; z0 c
        /* Draws cursor */. L6 V1 r) c+ g: p, s' I
        int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
9 J! z% q6 e  C  y$ P
5 H# J; ~; a0 R& Q8 h        /* Rotates the display */
2 U2 y7 ?- p& {2 J, t- v# i% A        void (*fb_rotate)(struct fb_info *info, int angle);
! v/ r: @1 |1 r% y: O9 g" s8 a; x) P* Q6 _' y: a
        /* wait for blit idle, optional */
. H% _# \! ]+ b5 r        int (*fb_sync)(struct fb_info *info);: k# m1 c' W2 R# k6 h

" n" X. Q5 t( B& ?        /* peRForm fb specific ioctl (optional) */
$ ^. C7 k6 e  }        int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,1 ?' l+ Y' k* Q; E0 x% K
                        unsigned long arg);
3 z: [7 v1 d/ ^& q  x. L9 N2 N/ G
        /* Handle 32bit compat ioctl (optional) */
+ a) i+ M1 o4 \! {) w" j( s        int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,. f2 V8 [# Q' t* E; ]
                        unsigned long arg);
6 H0 f# `& W2 m$ \/ d
6 _1 O5 U: D7 S7 j( j" I8 e- _8 a        /* perform fb specific mmap */) {7 Y9 I& e9 @5 R8 c
        int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);: D% _! m3 ^1 x1 x) {6 o4 m1 R5 {
2 s; y' j9 |) R2 O* J; k+ ?9 z
        /* save current hardware state */# A: s9 X1 }0 |  |( e# k3 l& J
        void (*fb_save_state)(struct fb_info *info);3 Y% z4 Y* U/ T* o3 [; V

8 I3 P$ e4 h6 H  X        /* restore saved state */) d6 L% a, r# d; m6 Y. P1 R/ W
        void (*fb_restore_state)(struct fb_info *info);: c7 F, V4 Y& R: s! X6 A! v: P+ d

6 c' L) B* O) h% ~        /* get capability given var */4 L$ G$ o$ ?3 j, e: S7 \; b, j
        void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,' u" u% s3 q, |- s
                            struct fb_var_screeninfo *var);
6 F. v  I/ B3 r3 I% N& ]; A0 A* B};
. p5 s' B1 L$ J% R! c2 I9 G9 F  A- |! ~4 C
0 y' u4 G, c6 V9 k, ^6 x  ]+ f
3. frambuffer核心层, ]6 @- ]2 }+ d5 }7 d. X% b
首先来看下frmaebuffer子系统的初始化函数。, L0 H& B5 \; \! E/ D4 @2 P
& p9 d0 K  ]7 ^- S
3.1 fbmem_init和fbmem_exit9 [+ x+ p$ R  L4 P" ?4 E& @
下列代码位于drivers/video/fbmem.c  ^- B3 d# J) ?/ v6 w5 a

$ t3 y) g& M, y/**9 Q, o6 _, \: x+ q+ G
*        fbmem_init - init frame buffer subsystem4 f9 }6 m) X( Q9 \9 g
*
! S9 [: \- b' A; t7 @: P8 o *        Initialize the frame buffer subsystem.% e6 @  v/ Y" _1 ~# `) M
*
* q5 ]3 W9 u/ O# L: {: _ *        NOTE: This function is _only_ to be called by drivers/char/mem.c.6 W5 z7 @* S% D1 |) r) `; T
*
  I7 ^9 G# q# O! H+ D */) c+ z1 m6 ?& N$ d; ]% U

* c6 X6 M' V/ O* }static int __init& \) C3 i* g) Q* L' Q4 s
fbmem_init(void)& u( g" ]( ^8 \( {1 n1 ?
{
: ]1 d$ u) L; H* g, t        proc_create("fb", 0, NULL, &fb_proc_fops);
8 d, j! B6 o) S0 h* q0 ?/ a# ^( B
% N* O9 w! B2 Q0 u9 l8 ^; F0 z        if (register_chrdev(FB_MAJOR,"fb",&fb_fops))                /*注册字符设备,major=29*/
8 x, `. C& f; c+ s7 h$ C                printk("unable to get major %d for fb devs\n", FB_MAJOR);: }  t6 H* ?# K5 q4 |: e
. [6 A2 t# @# s
        fb_class = class_create(THIS_MODULE, "graphics");        /*创建类*/) y4 Y( j' o+ v0 T
        if (IS_ERR(fb_class)) {4 w7 a. \+ ^& Z9 d* l1 B* g
                printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));  X3 {. N. ?/ s1 f& x$ r/ q- |2 B
                fb_class = NULL;6 O( x8 g5 I, ]8 h/ a% ^
        }
) g( p* `3 o1 t8 {8 V$ f& o5 u( m        return 0;
% |/ B* k. o" }}2 C& D/ q- c( i! y' m2 T

% V# C- Z: Q- F+ q4 z, H#ifdef MODULE
  _# x4 \0 b: n* }9 S. \module_init(fbmem_init);
: {* h5 W' L6 F8 O+ f" lstatic void __exit5 D  P+ b8 C! M/ x
fbmem_exit(void)0 U, G% s9 u9 p
{, I* L& Q% r; [
        remove_proc_entry("fb", NULL);
8 u( R2 `# r5 C  @" Y" t        class_destroy(fb_class);
) h* x- S- ?7 _8 h6 o" G) `        unregister_chrdev(FB_MAJOR, "fb");
% y. r  W9 e( U  C}
) u4 s% O6 H" a8 Y* D0 P& n' ^4 `1 @+ p# V, F% H( N
module_exit(fbmem_exit);' X7 l/ W6 h, I- G3 \" |  @
MODULE_LICENSE("GPL");
; @) t/ x1 C: y" T* d/ J8 |MODULE_DESCRIPTION("Framebuffer base");$ e) M: ^6 s; @0 b2 d6 Y
#else
6 z$ F5 z$ \5 u# Csubsys_initcall(fbmem_init);
) |/ I7 Q. \7 q+ S6 s: h% m, X/ e#endif/ G0 G" S  o: {+ `8 P" O4 T; W

" z* _: l- [$ y6 ~/ m# Vstatic const struct file_operations fb_fops = {
( X, W/ }( ^( ~8 w2 Q    .owner =    THIS_MODULE,
; w" U( _2 i. s, p1 N    .read =        fb_read,6 t: }) y- z# F- {( W0 E
    .write =    fb_write,
( w( P4 g# x2 y/ F, R* j/ _9 p; [# B; @    .unlocked_ioctl = fb_ioctl,
- {- ?* C/ c3 X+ F: W#ifdef CONFIG_COMPAT
; U% _: S& ]' W. N    .compat_ioctl = fb_compat_ioctl,
4 C; @' u$ ?" k* c( ]3 O0 t( u#endif
8 V% s9 j1 j, V- T/ ^    .mmap =        fb_mmap,
) z: X  g1 C( ~- Z: \% g4 r    .open =        fb_open,
/ b: ]0 }2 s: N6 ^0 a7 R    .release =    fb_release,8 C1 t6 u) i2 T9 m( W* S% a1 {
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
2 }" y7 t! e" I# r3 Q) u    .get_unmapped_area = get_fb_unmapped_area," F! M! C3 X% J; C
#endif. Z( ^0 [' C2 S, Z* X: ]( ]5 K8 P
#ifdef CONFIG_FB_DEFERRED_IO+ U) S7 G  T9 q4 T* e, Q! c2 c
    .fsync =    fb_deferred_io_fsync,- c2 W" q8 G; i( y7 o/ a% V( d
#endif
& y" A" d  G0 Q, x# ^};
# ?8 k1 S. i' t
; `, E3 e% p& c# L; h我们看到,如果不是作为模块,那么该初始化程序将在subsys_initcall阶段被调用。初始化时,仅仅注册了一个字符设备,并创建了一个类。通过字符设备,提供了API给用户空间,包open,release,write,read等。/ k* A( F; B5 u# f7 W
随后我们看看如何分配一个fb_info结构。
: x3 Z0 q. n1 K1 t, V; z7 v  q- z; [/ P$ m8 ~. G% Y& @
3.2 framebuffer_alloc% K5 B" {8 O* n- c4 v0 e
下列代码位于drivers/video/fbmem.c; H$ |* ~. g! `. i" O% i8 U+ L1 r
) p- S  s  _* ~2 y3 P
/**% G9 r  v5 j+ a7 `# u" C2 b
* framebuffer_alloc - creates a new frame buffer info structure! I1 C8 @) Q" f  f1 l
*# \( V! J9 a7 d8 p
* @size: size of driver private data, can be zero
( V7 j$ c+ Q( g: k  p4 Y * @dev: pointer to the device for this fb, this can be NULL% C0 O3 Y8 [0 q3 O% T  {- ~
*
  ^7 d! }& v/ K4 ?0 J * Creates a new frame buffer info structure. Also reserves @size bytes, f2 N2 S9 U5 f& V5 B
* for driver private data (info->par). info->par (if any) will be& w* L0 F5 E! ?( m
* aligned to sizeof(long).
7 Z4 Y' G: U) Z* n! F+ P *
$ j, [; t/ x! h0 E * Returns the new structure, or NULL if an error occured.
- Q# }- d6 t1 A3 e *0 H2 [0 U: M# f; n" t
*/
  x$ C; a$ n8 e. d, c5 wstruct fb_info *framebuffer_alloc(size_t size, struct device *dev)
4 u/ _2 m" T' u: Q3 [% N{4 |7 T' O* m# a- h
#define BYTES_PER_LONG (BITS_PER_LONG/8)/ _8 [% W2 C, _( L  Z
#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))) I4 m% j/ b: g+ S( c
        int fb_info_size = sizeof(struct fb_info);
! `% d. a& l8 u( a) R' X& o7 e        struct fb_info *info;8 D3 r4 S( z7 m* l$ ~
        char *p;
" G7 O, q, K5 |+ z. K
. f8 h+ M' G% i. _' q- s6 d        if (size)
2 _6 W* n; ]; l2 _0 S                fb_info_size += PADDING;. Z  p" I. X0 t7 U6 H
5 R' p+ J! E& z8 ]$ g
        p = kzalloc(fb_info_size + size, GFP_KERNEL);
6 G' M. D& }4 q& q( U- Z) @; F# T! |: M! h
        if (!p)
/ q8 p) j) g" D8 B, y, {% z! v                return NULL;# ~: t0 v3 s, ]* v1 R1 N, X2 z4 ^, X

' P& F0 g1 ~" T6 W- ?3 G        info = (struct fb_info *) p;; q! o- f4 e4 y  z

# [1 Q/ S! h& M! l        if (size)
' |0 G; z" k: a9 ^# V; O* t                info->par = p + fb_info_size;6 g  W8 d! p9 z# T+ V% Y
5 O; s" f: b7 ?
        info->device = dev;
& v$ @& C: p+ G4 ]7 j' l; O% [. X1 z7 }% V- j1 d( E; }
#ifdef CONFIG_FB_BACKLIGHT
5 n5 B! r5 M7 v3 N        mutex_init(&info->bl_curve_mutex);' @# h7 \" _9 ^' E: O5 r# o) x
#endif- ^. @4 W. f" x3 A6 P$ `
8 J6 x" {; [' r2 z
        return info;- Y$ d! m* Y7 J
#undef PADDING" R# d$ ]9 l; {. ]% t' u$ {/ W
#undef BYTES_PER_LONG' _$ u! V% j# F# u! |! q! `
}0 ~- }9 I( C$ n8 u* n
EXPORT_SYMBOL(framebuffer_alloc);4 P3 o; V9 ?! h9 ]9 k( p& p" V/ u
在进行分配时,根据参数size的大小,分配了b_info_size + size的空间,然后让fb_info->par指向size的空间。因此par所指向的空间可视为设备特有的数据。
" q1 L) W2 C; b0 f在分配了fb_info结构之后,需要将它注册到内核中。注册由register_framebuffer完成。我们来看下。
: v" u/ I/ n! D$ R! Z
1 b. O% g/ L1 ~# d3 Y3.3 register_framebuffer
7 X% |: H$ q$ l7 Y$ s下列代码位于drivers/video/fbmem.c5 H9 l2 y0 `/ h1 E
' V$ O( Q9 V( s0 _" x+ U( o
/**' N4 R# @" y( r; z: Q! U
*        register_framebuffer - registers a frame buffer device
3 G+ n: Q. @/ K/ b" W+ U$ e *        @fb_info: frame buffer info structure
* ]7 W4 a9 J% k+ V8 a; G *
' Q# J& a* Q  g- y4 V0 w0 m' ]+ m *        Registers a frame buffer device @fb_info.* x- {  r8 T" A6 ]# T$ S
*& O$ M4 z  ]4 o
*        Returns negative errno on error, or zero for success.! D* h$ Q7 [- D
*/ q: z+ s2 |1 Q7 m' O0 K% g
*/0 ?/ r$ Z4 C& C! V' e( U8 `

1 N6 J# N% b2 f: [' O0 V8 pint
; X( S8 d8 }; a! ?register_framebuffer(struct fb_info *fb_info)2 a( F# B3 P& [9 Y9 V- c
{
' @/ B# J- |% i' o- z6 o        int i;
5 R# f- f1 u. T7 X        struct fb_event event;( v9 d! p3 Q# X0 h5 ?3 E7 X# y
        struct fb_videomode mode;
4 T$ d/ Q/ n$ M' @: M( h6 {# Q$ ]' h! v' h
        if (num_registered_fb == FB_MAX) /*最多32个FB*/
3 ]1 ^0 R# G2 x: O5 j" _                return -ENXIO;! j& T* \# m8 O! w! I

5 K) G6 R2 [2 L        if (fb_check_foreignness(fb_info))
5 ^* ~2 U- o( A% ?                return -ENOSYS;
; x1 j$ P# }+ m% `0 A3 ?6 P7 Q3 l( {' @' {+ c  t8 a
        num_registered_fb++;                /*对注册的FB计数*/* R- e/ S/ g% o
        /*寻找第一个空位*/
; @3 {0 `: x5 }2 g% @        for (i = 0 ; i < FB_MAX; i++)/*FB_MAX=32,也就是最多32个framebuffer*/* n( `. s" T3 A
                if (!registered_fb)        /*struct fb_info *registered_fb[FB_MAX]*/
0 \! A! G/ r: s                        break;
0 J7 j7 A9 U3 U. k: @' o( j        fb_info->node = i;3 ]% i. y0 g9 {) m' ]$ D& Y
        mutex_init(&fb_info->lock);        /*初始化互斥体*/4 p7 K, f8 j5 v& g( ]1 b) r9 e

2 \( [% G2 x1 D! k' P9 l: N8 Q        fb_info->dev = device_create(fb_class, fb_info->device,/*创建设备节点,节点名为fbx*/) ~* a: m/ C) v# L' B# N
                                     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
3 ~2 a( F0 Y1 P  n  _        if (IS_ERR(fb_info->dev)) {
5 K4 H# C! J# }, ~4 y$ u- t                /* Not fatal */4 m9 {% W2 W  J' n, J
                printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
1 |  Y8 y' c6 y+ L( z                fb_info->dev = NULL;
4 J/ j* v5 v( @1 I& F        } else4 l( |& U1 u" m0 n
                fb_init_device(fb_info);        /*初始化,在class/graphics/fbx/下创建设备属性*/
8 {' G4 M( O) x* W7 ~2 x
+ h2 F* H9 h. X$ M3 F        if (fb_info->pixmap.addr == NULL) {8 y$ }3 T( y8 ?/ W
                fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);/*分配内存,1024 * 8字节*/% Y* ~, ]: C0 T* f2 x5 v
                if (fb_info->pixmap.addr) {) H6 w0 T; {1 E+ T
                        fb_info->pixmap.size = FBPIXMAPSIZE;# B+ ?, h0 `" z* [/ ^
                        fb_info->pixmap.buf_align = 1;+ a/ d) m4 q6 j6 n% ~1 V
                        fb_info->pixmap.scan_align = 1;
) Y* L5 ^$ k& f' {, _/ m; I' {                        fb_info->pixmap.access_align = 32;: {4 U" G/ m( B2 y' x: M- Y6 q
                        fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;' W/ G% ~9 ]! K$ |% N- ~8 X
                }
: g( b1 C/ x' ]( U- X# E5 H        }       
3 }9 `9 c8 o. T& b' _3 v" @        fb_info->pixmap.offset = 0;
+ F# q5 C8 b9 ]) j$ a( U# f# j' P! x7 v, }( j( w; N. F
        if (!fb_info->pixmap.blit_x)
- v0 t! I- e5 M, N9 c, c6 r                fb_info->pixmap.blit_x = ~(u32)0;
1 r! q- U2 s! H: S0 ~& b6 U* r
4 v0 e  ?5 Y2 t1 C8 h        if (!fb_info->pixmap.blit_y)5 t, E5 p  ?: h( F; ~
                fb_info->pixmap.blit_y = ~(u32)0;4 Y; I+ G0 {4 `

4 G# P$ q# g% C' G* V/ w        if (!fb_info->modelist.prev || !fb_info->modelist.next)        /*该链表没有指向其他节点*/
2 P* V, ~& }7 l                INIT_LIST_HEAD(&fb_info->modelist);        /*初始化链表头*/, v: A* i2 l- k& l% f! m. o

# k8 k; c+ K: t. ~( e        fb_var_to_videomode(&mode, &fb_info->var);/*转换fb_var_screeninfo成fb_videomode*/
: I# K" t/ a2 @& F. U) r* t        fb_add_videomode(&mode, &fb_info->modelist);/*添加mode至链表中*/9 l' d2 D4 Z. k8 t7 C' ]. p
        registered_fb = fb_info;( ~' w9 [1 Q! S0 r" g
" m2 i( i2 J4 Z9 j  B9 A% t
        event.info = fb_info;* A5 i6 s2 q8 L' `& r
        if (!lock_fb_info(fb_info))
2 E% L: u- W' d' V4 \4 V                return -ENODEV;- R. X) C+ e! u* h4 h' k
        fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);/*???*/; |2 B- z; u( B
        unlock_fb_info(fb_info);: A# l, q, i& [) i5 ?& t5 v" ?2 P
        return 0;
: p+ J% I0 e- e% d}
" k2 `7 O5 K4 P
* S- `5 V& {: m* U3 J5 O9 r6 Y从这个函数我们可以看出,framebuffer子系统只支持32个设备。在创建了设备节点以后,建立设备属性节点,随后将fb_var_screeninfo转换成fb_videomode,最后添加fb_videomode至链表中。
& p% q- e8 e. s- U; p我们看下其中调用的函数,首先是fb_init_device。: z4 P5 j' l" _3 Z4 R7 K

: C: _; ^% B: d1 k  s6 m( K) c下列代码位于drivers/video/fbsysfs.c' k: l  I% Y4 T& L/ a+ n  g

1 A( ?  ~6 n5 n2 R6 R0 xint fb_init_device(struct fb_info *fb_info)
+ K' p/ `+ S/ A! C+ l8 q{+ C. J, [" K) U4 R$ O5 E
        int i, error = 0;- F; g8 ?0 P; G: L$ @  r
# Q5 y* D9 S2 {- |5 q9 x# S/ i
        dev_set_drvdata(fb_info->dev, fb_info);
7 ~' a( p* {+ m& ]' K& ~# P$ k) m0 Q4 L$ _, w
        fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;! P( ]7 u/ E* X3 `) z2 }
& K' K. {5 X! w8 T6 G. m- `
        /*建立设备属性*/7 Q( V8 b" X8 P- N8 j  P$ P
        for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {% @# z6 L7 X, [
                error = device_create_file(fb_info->dev, &device_attrs);
9 z% {6 v3 a* \* r3 l  }- Y/ |
) y' n% n6 i* ~; ~/ D) c                if (error)9 A) @: `5 R. L9 {
                        break;& _; {6 ^1 @% V' C; D0 G; f
        }
. o) o- U. k3 I+ s1 m9 L$ ^2 S# H/ n0 S% B) m" r$ X
        if (error) {0 N" @" c6 j, V) u- S
                while (--i >= 0)) k6 D& X% J- H
                        device_remove_file(fb_info->dev, &device_attrs);
+ ?2 |5 S) u# C% S                fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
# u7 J- u  z& {* W; [+ y! j( U  z        }! X: Z; n% h' c: j2 D3 g
* z6 z: E2 D. p
        return 0;
7 V& H5 \* j5 `6 K! g}  B  x/ g3 w* B: \% F8 `

" E1 s  B# ~, v! o7 e* a/* When cmap is added back in it should be a binary attribute
5 J/ n( B: f8 i- u  I6 K * not a text one. Consideration should also be given to converting3 q8 I, R5 q4 r3 n: b0 b
* fbdev to use configfs instead of sysfs */
/ y2 v' G& [9 `6 V& U; S' r8 Mstatic struct device_attribute device_attrs[] = {
& c. v0 N& c$ T. u    __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
5 q: n& }5 [3 e. P( T    __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),+ O2 R. ~/ s* o0 A6 `: n
    __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
! B/ l9 d2 Z2 ~    __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
( r; x- a9 D7 J% U    __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
5 g0 m9 S3 N1 E+ r+ |0 f    __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),. M2 j& O" |6 A1 _6 C% X: q1 q
    __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),4 \$ H5 W. M! z7 X& P  [
    __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),) H' \7 y+ l7 a$ e# A" d2 X
    __ATTR(name, S_IRUGO, show_name, NULL),
  C9 Y0 z+ K/ d6 n0 N% n6 ?    __ATTR(stride, S_IRUGO, show_stride, NULL),  H% @2 m2 ~; @& V
    __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
3 H  S0 U4 _! A" ]    __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
2 n; q" l$ v& u1 |, j#ifdef CONFIG_FB_BACKLIGHT
* R$ L; u  U9 E. B0 X# V. U    __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
! J6 u+ D0 m) [: s+ u3 g#endif
& I: g( O* R( `( T7 c" O};9 v+ M9 @) Q7 B) J1 a! s

  x6 J; _* ], r8 Y; }0 R: _我们可以在/sys/class/graphics/fb0下发现这些属性文件。! P' h& `* i2 L; q0 p  A; m
[root@yj423 fb0]#pwd
/ |& B9 f+ Y% e7 Q' r$ T/sys/class/graphics/fb0
  p- J  v' N5 T" t1 x: }[root@yj423 fb0]#ls, N8 `, l1 v8 e5 n, @, K. \
bits_per_pixel  cursor          mode            pan             state           uevent. O) x& W( q# d! f8 e9 w0 \
blank           dev             modes           power           stride          virtual_size& q, t1 \3 \" n8 e5 p* ~% ]1 E4 g
console         device          name            rotate          subsystem7 r2 ]* J' s6 f3 I

* `; g1 ]/ O! n. Q3 A
# X- [4 f5 H9 e" k+ {% ?$ R接着看下fb_var_to_videomode和fb_add_videomode函数。+ c3 y  A( ]! C* i) ]

" c) {1 V6 f: `! E; X下列代码位于drivers/video/modedb.c和drivers/video/fb.h$ P8 A7 k) ?* F$ \

2 b. N- r& M1 l+ `struct fb_videomode {
; ^2 l' z2 N' i7 S        const char *name;        /* optional */2 S- a5 v) U' N( }% ^# H
        u32 refresh;                /* optional */
! m1 P3 L/ P$ e6 l! \6 x0 O        u32 xres;5 p5 V* _' R# [$ Q
        u32 yres;
+ M* }) i, U1 d3 u  ?        u32 pixclock;) u9 f( d- q! n+ H) _" M' w! `
        u32 left_margin;' m( e3 I4 }# H; u
        u32 right_margin;! @; S# \! @( p" e
        u32 upper_margin;6 U, Y3 [6 p! B7 d) T$ e* h
        u32 lower_margin;: ]3 N- n$ N8 N% A1 N  f. S1 _- j
        u32 hsync_len;
. V/ S+ U5 p* `3 L( N        u32 vsync_len;( k$ _+ Q9 D- b* r8 v8 p! n. [( g: H
        u32 sync;
, ^4 Z2 R4 L8 D0 M7 v: i        u32 vmode;
* b5 W# S( ~! E" k% o8 ]  l+ h        u32 flag;- X& I9 L, |6 C& i7 W% b4 Q
};
0 g4 r" E' v! z! v5 C, T
0 E6 \4 n! t4 ?, W, `9 q( M4 J  _9 d0 n  z/**
9 L; W7 _; w! s0 ? * fb_var_to_videomode - convert fb_var_screeninfo to fb_videomode) f+ G, D) s( m$ R1 g. L" {
* @mode: pointer to struct fb_videomode+ f1 ?- B3 M' p5 y! |
* @var: pointer to struct fb_var_screeninfo
" \! L/ }8 d7 h) P, y' @/ [- H */* a5 R% Z: O- q2 w
void fb_var_to_videomode(struct fb_videomode *mode,
6 ?+ R. _9 h% z- s) B. M             const struct fb_var_screeninfo *var)" C9 T; [) j  [. r6 k
{5 Z8 C! o1 F# x/ L5 q6 `0 x+ X4 `: B
    u32 pixclock, hfreq, htotal, vtotal;
1 \% R1 e" w( d: k5 ^% ^
$ k8 n0 Z- N8 I    mode->name = NULL;9 q# x2 U8 R4 I: b6 s+ x
    mode->xres = var->xres;6 i& D  r3 j/ B/ k
    mode->yres = var->yres;
: G; t; n% Z4 A& X, s* ?, o/ b5 e    mode->pixclock = var->pixclock;
& H, C8 I; e9 J8 j: P( ?6 x  p    mode->hsync_len = var->hsync_len;7 ?- ~4 E# `/ {+ M  {9 v
    mode->vsync_len = var->vsync_len;
, s- a' v- j: S: f6 a' e6 j    mode->left_margin = var->left_margin;
  w' l/ I1 s1 z' E" w; ~$ o    mode->right_margin = var->right_margin;% ~# G  }( L$ h
    mode->upper_margin = var->upper_margin;0 ~( A  |7 e/ P) I
    mode->lower_margin = var->lower_margin;
+ f' O, U# S$ ]9 m    mode->sync = var->sync;
5 K, B% I0 c- E+ W# J    mode->vmode = var->vmode & FB_VMODE_MASK;! t/ l9 H4 I9 u6 ^4 a
    mode->flag = FB_MODE_IS_FROM_VAR;
, d/ G4 l( y( Z8 S) e( ?    mode->refresh = 0;; ]  Z- @/ h1 U( ?6 C) w

$ I% X; ~+ ]" O! u7 r    if (!var->pixclock)
6 I" D$ p2 Y; T5 G: d$ x        return;2 q- }5 I$ f4 M9 Q# r
; E  }6 `" {- K0 S% B( K0 e0 e
    pixclock = PICOS2KHZ(var->pixclock) * 1000;
! A) G4 g0 N  U" [% o
/ @9 ~' s+ c1 r8 s& Y    htotal = var->xres + var->right_margin + var->hsync_len +
( w5 e( m+ C- o$ p; u( f7 n/ \        var->left_margin;
; m" s9 w; E  {$ m+ z    vtotal = var->yres + var->lower_margin + var->vsync_len +
2 ?: D* c* T& U) S        var->upper_margin;
  @, _- B" `2 I+ N6 U+ p4 A* j. e" Y8 P
. H: Z" c% C, K; h    if (var->vmode & FB_VMODE_INTERLACED)4 D8 c9 n$ u4 ~3 [
        vtotal /= 2;$ K4 T- O3 @# Z2 U, D3 Z
    if (var->vmode & FB_VMODE_DOUBLE)( U, Q& V( }( U( N3 e& o
        vtotal *= 2;9 B; w7 V, Q. s0 B; c

2 W- ^7 K3 J9 I% b    hfreq = pixclock/htotal;' D- B* ?& ]* X; \# K+ U" }" i& }
    mode->refresh = hfreq/vtotal;1 `1 |* ^/ F% {8 k
}) b# _% y& n9 Q$ S9 S2 H

2 X) c: K* ~& k( W" T/**
5 ^# `6 L$ w, U9 o * fb_add_videomode: adds videomode entry to modelist- V# P# X, T' ^# C  t5 X, @
* @mode: videomode to add" S; q5 |6 i+ i  g9 S7 I4 B: y2 s
* @head: struct list_head of modelist
' W! b' [' \9 n *! u  {5 ?# F' l  h3 X  z" l
* NOTES:3 n; {4 j2 m# S/ X* ]  ]
* Will only add unmatched mode entries$ `$ @/ i( }: y* Z0 _& j
*/
) }+ ~  N" N7 Yint fb_add_videomode(const struct fb_videomode *mode, struct list_head *head)' p5 M) K5 K4 a, ]
{' M& a% @  x- W/ A. L" R
    struct list_head *pos;
" t4 d- K! x% j+ `% m    struct fb_modelist *modelist;d, ?4 W7 ~  t) @0 z
    struct fb_videomode *m;
/ K0 R/ P3 m! I' J5 I9 F, D    int found = 0;
9 P8 F( X3 u7 s7 d    /*遍历所有的fb_modelist,查找mode是否存在*/
* c/ j* B; ^/ p5 h    list_for_each(pos, head) {
* ^& K. _8 G( \' j! ~) c8 n        modelist = list_entry(pos, struct fb_modelist, list);
- G6 @* y: |% f5 j8 I# ]; u4 o        m = &modelist->mode;
% x, C/ O  P5 d  t( n        if (fb_mode_is_equal(m, mode)) {    /*比较两个fb_videomode*/
2 ]" @& N& g" S# B            found = 1;                        /*该fb_videomode已存在*/. A$ b  [1 H  B, B' B# K0 ?
            break;3 K/ {1 e$ N/ X) ^
        }8 j5 X/ B$ z9 W, V  g" n0 `; Z% C) r
    }
: u; F4 c" _( |    if (!found) {    /*不存在*/
9 R/ h' Z; k( g; Y0 J6 E        modelist = kmalloc(sizeof(struct fb_modelist),    /*分配fb_modelist*/2 x0 b! Z! p5 {
                          GFP_KERNEL);+ @1 O: O7 `' _
7 S+ x. n% I# J1 M, z; T
        if (!modelist); D2 D' o* l+ M# C) V" [
            return -ENOMEM;
5 y- X5 t; [8 u: u9 K  _        modelist->mode = *mode;            /*保存mode*/9 W/ {  p. q3 r: [" \
        list_add(&modelist->list, head);/*添加mode至链表中*/8 x! l# f% z9 L+ A! R8 p
    }/ x' |; Z+ O$ u
    return 0;. m5 Y  Q4 V( A
}
4 f$ P( O0 m1 [/ J& M) v; i0 }9 }3 c0 {$ O) Q% N
/**
* x/ P* g# ?, z' q( [ * fb_mode_is_equal - compare 2 videomodes
5 T  s1 M; i& C  h  U. ?6 h * @mode1: first videomode
" F, [- E5 \% k( E8 b4 I) _# a' _ * @mode2: second videomode
7 I* Z3 j1 p5 @& \ *- _9 [. t$ V; N
* RETURNS:$ X4 u8 B8 P0 E: t" I
* 1 if equal, 0 if not$ v* W7 u8 {9 G& L3 k2 q6 x
*/
* k3 H2 z* H: X$ m- S  y* oint fb_mode_is_equal(const struct fb_videomode *mode1,/ a: S" X* b8 G2 N4 {
             const struct fb_videomode *mode2)  G7 T8 E2 a0 |4 d3 m: Q
{
# T/ l" t4 |9 U+ Y    return (mode1->xres         == mode2->xres &&
, H! I: i7 D* [" {: M' m4 k        mode1->yres         == mode2->yres &&3 M. b  }  F! D3 u9 x
        mode1->pixclock     == mode2->pixclock &&: H# K+ P9 `' U7 f. A3 C
        mode1->hsync_len    == mode2->hsync_len &&
8 d# x: r& j- i4 o. G; c' q& z        mode1->vsync_len    == mode2->vsync_len &&5 Q8 q; x6 B( K
        mode1->left_margin  == mode2->left_margin &&6 G0 i. S. d- F, a$ r6 _+ v
        mode1->right_margin == mode2->right_margin &&. T! P, ^9 _1 L1 V0 {6 @- C" r' y5 X
        mode1->upper_margin == mode2->upper_margin &&
7 R5 [& A' n# @        mode1->lower_margin == mode2->lower_margin &&. a* t3 d5 C  E2 b' t/ }0 l+ p
        mode1->sync         == mode2->sync &&6 x2 D" ^- n4 L: P8 F% Q: V$ t
        mode1->vmode        == mode2->vmode);
6 s7 j: g; w, Z' y) U0 G}7 }% P! r2 X9 o
: |1 s, e& X/ L& A) f- V
. p& \. ~7 k$ Y" m
fb_var_to_videomode函数只是将fb_var_screeninfo结构转换成fb_videomode结构。而fb_add_videomode函数查找是否该fb_videomode已经存在,如果不存在则添加到列表中。% B. V! Z% |7 w
3.4 字符设备方法. T* t2 H9 p/ n9 d( U, p8 B
  在看过framebuffer子系统建立和注册过程后,我们看下framebuffer留给用户空间的API是怎样实现的。6 H. [3 q4 ]8 r5 w$ |
" q) g) f  p6 u3 O
本小结只分析5个常用的方法,即open,release,read,write和ioctl。1 U) _! L. ~  S) K" }4 b
9 o1 {9 I/ e% z& E  N4 U) J
  因为所有的方法和struct fb_ops定义的方法有紧密的联系,而该结构的定义由驱动程序给出,在这里我们提前看下在驱动中是如何定义的。* ^2 M% h$ E# |; T0 W9 L% F

& K; O7 B  {: x% ` 下列代码位于drivers/video/s3c2410fb..c
' u' d  T8 x- W8 A7 @' A+ {$ }9 }4 a" W& N; i# q
static struct fb_ops s3c2410fb_ops = {/ y: ?9 m+ t6 D, h1 T; l6 k
        .owner                = THIS_MODULE,1 ~3 h- q$ }- `: m- q1 F; w
        .fb_check_var        = s3c2410fb_check_var,                        /*检查变量的合法性*/
9 b/ |) X- S+ s; x2 H' Z  k        .fb_set_par        = s3c2410fb_set_par,                        /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/
* h9 |. H' t+ q2 Y% l        .fb_blank        = s3c2410fb_blank,                        /*该方法支持显示消隐和去消隐*/
/ Q" P6 h* T! B7 z1 C" Z        .fb_setcolreg        = s3c2410fb_setcolreg,                  /*设置颜色寄存器*/
- E3 \2 w7 ^  V$ G* D% S        .fb_fillrect        = cfb_fillrect,                                /*用像素行填充矩形框,通用库函数*/1 u! J1 z/ C7 T, Z+ B$ @5 N( _
        .fb_copyarea        = cfb_copyarea,                                /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/
+ \7 e6 a/ J8 ?7 X8 x        .fb_imageblit        = cfb_imageblit,                        /*显示一副图像,通用库函数*/# C" |/ I3 I# [/ x- U' O
};1 W5 K' C, `1 H' ^, `, j6 ^  h& X
最下面的三个方法使用的是内核提供的库函数,而上面4个则是由驱动提供。% F) M% d5 f# H) X# U
3.4.1 open方法
) c" {  o! b& h. j. L下列代码位于drivers/video/fbmem.c7 v% C3 J3 I0 F; e

5 k# y7 [8 O! O8 ~2 k" q( }static int
' T  N( u- l3 o4 v0 p8 c. rfb_open(struct inode *inode, struct file *file)
& t2 ^* N0 f5 D; D6 c__acquires(&info->lock)2 S& Q* K0 H9 {3 Y1 z( U
__releases(&info->lock)
" Z8 ?# o4 d* V/ t* t* [( s* l/ p{- b# v: U0 b, J
        int fbidx = iminor(inode);
! F! o' p+ S$ ~0 [; ?7 F        struct fb_info *info;
& }) m, o+ v; j% b        int res = 0;; N, p4 a2 g, |8 x3 U) ]. x3 H
* \" a( k0 i  ~# w; I
        if (fbidx >= FB_MAX)
/ |4 R% X" q- k" I2 o/ x* _% \                return -ENODEV;
3 N; z9 C  o' L- J        info = registered_fb[fbidx];                /*在register_framebuffer函数中已经设置了元素*/. a( @3 S: Z. s$ a$ u
        if (!info)/ c; C7 C4 R6 W  m6 u# B( _* G
                request_module("fb%d", fbidx);        /*加载模块,这里不加载*/- q$ D$ i4 D/ S2 C8 @# }
        info = registered_fb[fbidx];: \0 t& _! Y" Z
        if (!info)* q& y7 b# ]* _2 Q& _' i
                return -ENODEV;
8 i: P' f/ X2 j" F        mutex_lock(&info->lock);                        /*加锁互斥体*/
. H1 q- ~& v  }' X6 ]        if (!try_module_get(info->fbops->owner)) {        /*增加模块引用计数*/
' u0 {- X, g/ N9 y                res = -ENODEV;
) t  v" e1 E0 e                goto out;
4 ~' X5 y2 y2 w1 G2 v+ D        }! E# T2 r4 I7 }1 m
        file->private_data = info;                /*保存info*/  Y3 @' A3 {" P( t' a; B$ W/ q
        if (info->fbops->fb_open) {                /*这里fb_open方法为空*/# N0 v" J1 R$ G2 L" Y$ t
                res = info->fbops->fb_open(info,1);- p6 R6 t$ J% i8 Y
                if (res)5 G) N: `* d' F- {6 E/ W
                        module_put(info->fbops->owner);
7 M, _! M; }3 Y- D, T6 t" w: ]        }; f8 [0 e  f0 k, Y
#ifdef CONFIG_FB_DEFERRED_IO: p. U8 ]: Y5 h
        if (info->fbdefio)5 c7 g, ^) w0 p7 j0 [* ]) b
                fb_deferred_io_open(info, inode, file);# Y, ]% q/ E  i3 k4 F+ i7 k
#endif
& }5 R5 }0 R/ F, u5 }out:
* `3 o* E4 X! J/ {% z9 c( e/ A        mutex_unlock(&info->lock);                /*解锁互斥体*/
  H; Y9 e. ^% m# S! U        return res;
+ l# S  M, P; z, o}
6 N* |- K- H& s7 `3 c& n+ s; E3 m8 `7 B+ o主要的一个工作就是增加模块引用计数。还有,程序会判断是否fb_open在驱动中给出,如果有则调用该方法。我们已经知道fb_open没有给出。, Q* l4 ?/ ?( ]) H5 j- o3 N4 N1 Y
3.4.2 release方法
- F4 H# h( @2 d8 b! v! t0 H6 Y9 M下列代码位于drivers/video/fbmem.c
2 l* F9 i2 B' c- R( x7 @3 r( H1 p" {/ {' D2 f
static int 1 A6 |/ b) _, c2 b+ S
fb_release(struct inode *inode, struct file *file)
/ N' R7 S, T+ U. C__acquires(&info->lock)
" L2 J1 G9 U1 ^) x! U__releases(&info->lock)
9 i2 s: V0 P, i9 ~{
8 @& v1 R4 \0 B* k, X9 D5 J        struct fb_info * const info = file->private_data;; q: L  ~& v  n" M
- f2 m* M; g/ a) x/ k
        mutex_lock(&info->lock);
0 k2 A: f1 D7 \  q4 @        if (info->fbops->fb_release)        /*这里fb_release为空*/) J5 M/ b! e' h& w% {. v
                info->fbops->fb_release(info,1);
6 R' `8 T4 P0 l# T# z        module_put(info->fbops->owner);        /*减少模块引用计数*/+ o  v( _+ ?6 E
        mutex_unlock(&info->lock);, q9 E/ ^4 ~7 U  o1 R
        return 0;) |+ }  _1 i# Y9 E' c: ~
}
' K& b: q& J2 ]8 S3 N和open相反,减少模块引用计数。
. {' |1 w" @, S8 F4 f9 y
3 D0 O/ m) t+ {8 v8 u: a3.4.3 write方法
" m! }" Z: @2 H8 T: g2 E' {' `- t8 _2 c  通过调用该方法,LCD将显示画面。2 A& u; u. R7 ?" |: @

! F) {6 Q3 S4 P  f  下列代码位于drivers/video/fbmem.c; V1 M, T6 R  M) e" M
( n. j0 A0 u1 r$ [5 ~
static ssize_t; C3 r9 W. S* i* w2 l: Y
fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)6 l+ r2 c1 e% a' N! X
{
( J& K* S& ~# _! j6 D  c, ^    unsigned long p = *ppos;7 C; d' Q4 d, y' Y
    struct inode *inode = file->f_path.dentry->d_inode;+ O! ^! k2 [4 h( k
    int fbidx = iminor(inode);! |# o# Q' o4 C
    struct fb_info *info = registered_fb[fbidx];
% S7 j$ @! |! t  {7 J6 N    u32 *buffer, *src;
/ W  K+ K' ]; f4 g" [( U    u32 __iomem *dst;1 r; m# T" [. e
    int c, i, cnt = 0, err = 0;& x4 Y$ [- i* H2 w* B$ P/ z9 c
    unsigned long total_size;
4 P( c' ?- q  N& U; i
2 d6 T  N2 ^. J; C    if (!info || !info->screen_base)    /*screen_base在驱动中给出*/; i; L; C$ E' s7 V  L  }8 Z
        return -ENODEV;: G! W' G# g" T( w4 h3 A- l' b# {: g! P

* ^9 Y1 M& M. x3 ]" f    if (info->state != FBINFO_STATE_RUNNING)
  L8 J# w) Z  g. L        return -EPERM;
: a0 |- x4 e. n% f+ g# ?. C. n+ G3 ?
    if (info->fbops->fb_write)    /*没有fb_write方法*/& `! {0 S3 x7 Z* U6 {/ t
        return info->fbops->fb_write(info, buf, count, ppos);
: M! H, p! T$ r  B. w0 R3 n/ c$ y3 y% k; q# W% d
    total_size = info->screen_size;    /*screen_size没有给出*/$ ^& A) I- L& V3 ^* B: y
+ G. \$ Y& d7 p& \
    if (total_size == 0); ~! f9 {, n6 i/ o$ [! q
        total_size = info->fix.smem_len;/*153600字节,驱动probe方法中计算*/
& F' E* f8 f& |
! G5 D6 J2 e) m# \; e    if (p > total_size)# W2 g$ ~' Y: w: z- o
        return -EFBIG;
0 i$ @3 Y' ?6 S  w  v% x1 f
/ ^6 B& U: a0 P! l    if (count > total_size) {    /*要写入的字节数大于153600*/
" h& l% R! \! |. [        err = -EFBIG;        /*file too big*/! P- u. j# Z$ G; ]* N4 S! S
        count = total_size;
1 G* M% y; l7 f+ P+ o    }
; p8 B. x3 J: i" R# N
/ G4 O2 V1 d; c5 w9 [    if (count + p > total_size) {/*偏移量加上字节数超出了缓冲区*/
. y& t0 B9 _' W2 e+ i) W. u        if (!err)
/ U+ P7 ]) G: F6 p            err = -ENOSPC;
4 A) a9 _* T( K6 [& Y* p, d
1 k5 r  U2 K6 v1 Y. @# t2 B2 C5 {        count = total_size - p;
# C6 Z' W3 C9 @$ R' S! b+ H    }
& K8 ]& G& q& ]" y8 l, l$ ^$ |1 o' J' v, \6 d9 k
    /*分配buffer,GFP_KERNEL*/
% G8 a) b7 S6 W    buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,% Q4 ~2 c. Z# z0 g
             GFP_KERNEL);                /
) Q( h8 j2 l  ^9 F    if (!buffer). W8 R7 ^3 B  _9 E$ B5 U
        return -ENOMEM;+ W, ]9 d# b1 B7 b8 t5 Q+ A# u
* C: q+ w0 Q* f+ T) ?
    dst = (u32 __iomem *) (info->screen_base + p);/*修改目的指针*/
( s1 m6 _% Q0 D: ?1 I; ]6 i# O+ Z: R$ F0 F) o4 Z% G2 J
    if (info->fbops->fb_sync)    /*没有定义fb_sync*/
4 D- ]) f  o6 n% q        info->fbops->fb_sync(info);  C2 g& y& H5 S/ T& a' ~

/ @, ]% U4 e3 R+ [    while (count) {
3 U3 |' M2 w2 o. j7 g        c = (count > PAGE_SIZE) ? PAGE_SIZE : count;& m1 ], W5 y" `- J3 @! m
        src = buffer;6 W. I6 z" r6 y% B3 v" J

; G" Y7 ]% g  {/ r% _7 v4 q+ x        /*从buf(用户空间)拷贝数据到src中,一开始为1页,最后为count字节*/
, F: L/ @4 i4 i' h        if (copy_from_user(src, buf, c)) {    / x& l1 {' x; G' E, O: `3 B
            err = -EFAULT;9 [0 i: x+ _+ g2 Q0 h+ C2 u
            break;, I1 f' \& [' h" l
        }
8 Z* N5 C( E) l) N7 [7 z& k9 f. {        /*一次for循环,写入4个字节数据到dst处*/# [! I/ }/ j1 F
        for (i = c >> 2; i--; )* F( D; z/ K+ Z+ [1 D; W: [# N, \/ b
            fb_writel(*src++, dst++);, o6 L: x, p, P' \- f% E) J! O
        /*最后还有3个,2个或者1个字节*/
8 m2 b: f. h! n  s& S: I; ~* T        if (c & 3) {
( Z/ o2 i2 k4 f* }+ b' z            u8 *src8 = (u8 *) src;
. a3 J3 J. P. ?) O            u8 __iomem *dst8 = (u8 __iomem *) dst;2 d  l6 g5 q) E
            /*一次写入一个字节*/- V0 a8 H. P+ g' e/ J! v3 h
            for (i = c & 3; i--; )
! y/ F* ^+ c+ p- O, l) ]- }                fb_writeb(*src8++, dst8++);
+ r  w# m( q! x- `) [
; S/ D. w; F1 \$ K9 [: q            dst = (u32 __iomem *) dst8;
9 y( C0 s: Q7 Q  p        }( S3 W/ X7 L& H9 b" D
4 s& D' r7 m- [( z& C
        *ppos += c;    /*用户空间偏移量增加*/  D0 K( h. l' z9 E& x; ^7 e1 ]# N+ V
        buf += c;    /*用户空间指针增加*/
$ z$ `  X0 g1 I4 }# l9 E7 X& Y        cnt += c;    /*修改已发送字节数*/
5 E' Q1 }+ [& ~9 ^" X        count -= c;    /*减去1页*/2 a  r/ S2 B2 L. L2 a4 I* J
    }& @, b8 ~0 x/ r. Z7 b
& |2 Z& b; B& V+ N* \
    kfree(buffer);    /*释放buffer*/
" Z& c, u% `$ x
1 |& ?; v5 N- o    return (cnt) ? cnt : err;
7 i& v1 u2 D, S" D& e}1 z# D& L  D0 h: Y

4 ]) k% _) w' s& H2 e" S这里,做了一系列的检查之后,开始拷贝数据。这里一个有三个buffer,一个是用户空间提供的buf,一个是在这里新开辟的buffer,还有就是驱动层提供的screen_base。) u* J( z& @. m3 n
数据流如下:1 h  Y9 E" A- M$ ~9 u. G$ L

% X7 L' {$ t" U+ x6 C9 k 3 R/ b: s" l2 t; C% {8 C* U5 R

% g# ?% }/ i* ]" r0 L) L用户空间的数据首先被复制到buffer中,然后从buffer中复制到screen_base中,最后被映射到LCD上,LCD就显示响应的画面了。, F+ r, h( d0 Z7 A
+ C6 _4 D1 e" Z  F3 S
3.4.4 read方法5 v$ V/ q. J) i5 P! _; t# `1 t' |
该方法用于读取屏幕画面的数据。; }* |9 t% E- F- _" S" j
: R$ i& Z3 e2 C* h' B( Q
read和write类似,只是数据流是反响的,就不多做介绍了。
) `' |" A7 ~% }, M' Z5 p
$ u3 I: ~( c  I0 P' j! p下列代码位于drivers/video/fbmem.c2 d# Q5 K5 I0 i- o3 F

+ k. H+ |8 M, p  W& H  b! Fstatic ssize_t7 w& ~% t1 y+ y/ D# Z
fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)$ i$ W, j- |, @' h
{9 X3 u: p( f' h& T. g* m5 k$ J8 ^
        unsigned long p = *ppos;, ?# C+ M9 r$ P6 P  f- w5 ^
        struct inode *inode = file->f_path.dentry->d_inode;
. I& n3 b' v( v. Z& a! n; {2 r        int fbidx = iminor(inode);
5 S) g& t  M" t! X. i! o, n        struct fb_info *info = registered_fb[fbidx];
& f) S% E1 E0 V' `  R+ M        u32 *buffer, *dst;& N1 k+ G3 ~8 w; E' s0 |  X
        u32 __iomem *src;
4 Z& k0 n3 \. M  O$ ?; n* u1 v        int c, i, cnt = 0, err = 0;% u) C) z. w" L& K" `2 {; |
        unsigned long total_size;
. ?7 l+ e2 D# b5 @
* s, W% F. e* _        if (!info || ! info->screen_base)6 N8 |/ V) t  s  b+ |
                return -ENODEV;) u/ R4 u) x& `

( A- I1 C) y* ~8 c6 t        if (info->state != FBINFO_STATE_RUNNING)
4 n+ J  U) O8 W! R3 d3 T5 d                return -EPERM;
* `3 o* q! I8 ]9 M8 |: q' t+ V( N+ m2 Z8 N9 m
        if (info->fbops->fb_read)        /*没有定义fb_read*/# z, A' ^9 I5 s
                return info->fbops->fb_read(info, buf, count, ppos);* ]% J/ k  s0 U7 A" g6 ?
       
+ B3 ~9 `% n6 q% r/ }6 p1 r$ @        total_size = info->screen_size;/ E' t+ r, x! o0 {$ W# ~5 K
4 M! W/ U6 b% c! v
        if (total_size == 0)
" E" z+ Q( m6 @/ p- ]4 f9 [: y: X0 e+ X5 G                total_size = info->fix.smem_len;4 |+ c* \8 l- D

3 e, E! u$ C0 X; {        if (p >= total_size)5 V& f9 \2 x' x3 z# H
                return 0;7 P" X: e6 t! K* l
9 ~% I8 @. l, F, L# y2 G; f
        if (count >= total_size)4 ~& V3 o5 X) u0 B
                count = total_size;
* ~$ x* r6 K; x
4 j+ j1 N; ~" N  Z& @3 t( P        if (count + p > total_size)
& |4 ]) w/ x. |7 E0 U  }- C0 c                count = total_size - p;. _# m6 c# r( ^9 B/ J8 f( Y- M3 K
. x$ Y2 j; e7 a0 H4 m( m
        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,/ J, ^' l+ W0 N% F" G. q/ U
                         GFP_KERNEL);' P; p% i2 _) f
        if (!buffer)
% s, g  K" l. P6 M9 z                return -ENOMEM;6 Y0 N/ H* X% Z- _0 p
& j3 ]1 s( S" ~7 @- m& I5 R
        src = (u32 __iomem *) (info->screen_base + p);
) \+ l1 T8 L! h6 w: s, z0 B: R0 t, Q2 ]: z
        if (info->fbops->fb_sync)
+ z% F7 P8 N" T                info->fbops->fb_sync(info);/*没有定义fb_sync*/
  O) d' h1 R8 H: g/ c
* F7 U& e5 \- X        while (count) {
& c+ `- m1 t9 J) l  l                c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;# s( ?. x+ n. x5 P3 Y- j
                dst = buffer;
5 ]. J; z* e- `8 n$ E( r) i                for (i = c >> 2; i--; )
9 N, H: h0 p5 B6 `2 p! E                        *dst++ = fb_readl(src++);
. \& e0 J$ o* G, C" j                if (c & 3) {4 y9 w9 ~1 W/ K; G' w6 [9 n
                        u8 *dst8 = (u8 *) dst;
) t, V/ F7 P( `$ v- y* y                        u8 __iomem *src8 = (u8 __iomem *) src;6 y9 T$ L9 p' m4 A5 K* I: w/ v

. F' O# `8 b" E1 [" Q) `& t                        for (i = c & 3; i--;)! }( c" B7 P1 C3 Z2 M! x
                                *dst8++ = fb_readb(src8++);) a3 U; L& M$ b% G7 q( S6 N

2 A" G0 @3 P! m+ |  S. |: ?                        src = (u32 __iomem *) src8;
% ]" R* f8 r+ ]0 F5 N0 U; q7 k                }
5 l+ |" }1 x) B  w3 r2 h) {! \) J3 a3 d5 L' u; f# m# z, \
                if (copy_to_user(buf, buffer, c)) {
' {' C7 V$ c: J: ^                        err = -EFAULT;  q. ?6 }) u# r7 v
                        break;
0 w' }( u4 l4 P6 I. ^! v& ^5 |0 U9 ^$ d                }0 Y6 W0 A* o2 Z+ D
                *ppos += c;
+ [# @, ^6 m: A5 o$ Z' `4 E4 u                buf += c;8 F# l7 s3 N* U) n+ E
                cnt += c;
9 T2 Q- Z6 B: U+ }- W/ F                count -= c;
! \% ~6 s" j+ B7 B4 T        }. C( t" t$ M  }; d
7 T4 {2 W$ R1 u! n7 y4 i
        kfree(buffer);
2 F. V0 u6 J8 }0 q" c/ _5 l, o: i1 J" a' T. D6 P/ a6 a! ~
        return (err) ? err : cnt;; X1 }% v5 l, _- T$ H- C0 g6 o: P4 P
}
2 D2 ]0 U. i  E7 ?3.4.5 ioctl方法! P7 f( N* E8 D& F/ k2 h, }) j
这里只是简单的看下ioctl方法,这个函数调用很多其他的函数,详细的请自己看吧。
( H$ C6 v3 B% h+ t7 o, U- Y" A* Q$ e) W. H
下列代码位于drivers/video/fbmem.c& a: G7 S- s2 V. h

  @& w& m  j; C9 ]static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)+ ^9 M* r2 ~- o
{
0 Q- h" }0 ]2 r+ `% U1 x" r        /*获取inode,再获取对应的fb_info*/) u5 d& X( N* d# T5 g  S- {
        struct inode *inode = file->f_path.dentry->d_inode;
) y: O8 ?7 M( ~, K9 G. Z/ @* d        int fbidx = iminor(inode);        3 _+ g! I/ e# w& m+ l' q/ v" n; ?
        struct fb_info *info = registered_fb[fbidx];
! Q6 L$ q) U# N- N9 L0 Q( J# g) f7 F6 r& @* T9 C, |# S
        return do_fb_ioctl(info, cmd, arg);
/ p- ]  v1 c2 V  w0 I}6 N; @9 x/ e! u( a! _% w" ]
5 x* R* U. ?" W% G0 e' f/ D' h5 i
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,6 c8 f# L6 |9 w# K. o
            unsigned long arg): a  `( i) Q6 B  Z! b3 e
{
1 d  _7 R) U; ^* H3 W9 t9 F9 {- e    struct fb_ops *fb;" i; W+ F" ]# k8 X. D
    struct fb_var_screeninfo var;! ^: S9 Y- `5 W
    struct fb_fix_screeninfo fix;
  E8 o- a- t% `, W    struct fb_con2fbmap con2fb;
; ^$ R- \( A- I! \3 A    struct fb_cmap cmap_from;7 o9 x5 b5 {1 d
    struct fb_cmap_user cmap;2 q3 S: w) F" D- ]/ G
    struct fb_event event;6 B. h, o+ D* R/ j) N
    void __user *argp = (void __user *)arg;5 i" T" U- E! v# f, }0 q( t
    long ret = 0;; }: L6 c$ F" y. I' y$ z# a6 a: K
4 X$ Q( ?8 ~' S* @' g1 l, }
    switch (cmd) {5 }$ }+ S* z0 `% \4 o
    /*获取fb_var_screeninfo*/* ?, p7 @' M5 L) N  o5 _) R
    case FBIOGET_VSCREENINFO:    . S2 P' [9 j9 m* Y
        if (!lock_fb_info(info))    /*加锁互斥体info->lock*/) ]+ _3 s# ~8 [: h& e. x- t
            return -ENODEV;
8 R* i% {0 `& G" N# i# B( `        var = info->var;            /*复制var*/
+ b" O# M; m% V5 O        unlock_fb_info(info);1 N7 a* Z! w  F; N+ e

! C, p; @, T+ O5 _) t! W        ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;    /*复制var到用户空间*/- x- |% s7 G5 z- {
        break;* F; D, W" x2 ~
    /*设置fb_var_screeninfo*/
- X2 T1 r) ^# ]" g' ?4 K% X6 ~    case FBIOPUT_VSCREENINFO:
* R, O; o: n9 B  p        if (copy_from_user(&var, argp, sizeof(var)))    /*从用户空间获取var*/7 r& v3 z( j" B; K+ q1 K* }6 e! C
            return -EFAULT;( J& I3 l: V( O: L
        if (!lock_fb_info(info)), |( [7 o" X6 V$ L. f2 H
            return -ENODEV;
9 K- \: i) Y1 t; b; _# ~        acquire_console_sem();4 @' D, d8 y0 p2 ]9 X& j
        info->flags |= FBINFO_MISC_USEREVENT;7 m7 i% Z- [6 i. @
        ret = fb_set_var(info, &var);            /*设置var*/+ B+ S+ g$ E& w9 V2 j
        info->flags &= ~FBINFO_MISC_USEREVENT;. u0 \9 r- D7 _6 \/ ?, M
        release_console_sem();, G' ^* s% Q1 ]
        unlock_fb_info(info);
" ~. f: W3 j$ ~+ [! W" z        if (!ret && copy_to_user(argp, &var, sizeof(var)))+ k) [+ t/ T7 ], C9 x
            ret = -EFAULT;% O' v2 ]2 r4 c$ q0 Y5 M$ a! R
        break;
4 y& |7 h  {3 l* l    /*获取fb_fix_screeninfo*/    /*fix为不可改变信息,只能获取,不能设置*/8 s& F1 D! A) n3 G; Q' V7 j2 k6 H5 b6 x# V
    case FBIOGET_FSCREENINFO:
9 G5 n& ~0 d. `& T+ A3 ^        if (!lock_fb_info(info))
+ F' h: K1 H$ i& ?            return -ENODEV;
; [7 P5 h- H) n2 X        fix = info->fix;
5 N! z. i$ w, T5 P0 I$ W        unlock_fb_info(info);0 y) U4 j8 E% l
9 m& t8 x! n! `+ ?
        ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
" r1 a# n0 {8 o0 a7 B8 c# F        break;
4 Z3 `0 x; x  U+ ^, x# Z    /*设置fb_cmap*/    4 }* [( `5 c0 b' [3 N3 k0 |4 c
    case FBIOPUTCMAP:
, \% R& n& f+ n% P4 A1 L        if (copy_from_user(&cmap, argp, sizeof(cmap)))% ^5 S7 M; M& c7 T. M( Y; s
            return -EFAULT;
7 V9 x( C( S0 x. n        ret = fb_set_user_cmap(&cmap, info);    /*设置fb_cmap*/6 y9 T1 }% f( a# f
        break;
+ ^1 W8 ~, i: t    /*获取fb_cmap*/    $ j% }- b( n& B: Q! y  S' o) q; ~: _
    case FBIOGETCMAP:
, l9 H7 @0 I; I5 E( ]  U- A4 F        if (copy_from_user(&cmap, argp, sizeof(cmap)))
$ w! e+ |% v0 T* U            return -EFAULT;
: M& U1 \8 M& E5 X  w( z6 b/ a        if (!lock_fb_info(info))4 Z" |; y$ h5 M2 s4 P; q& O0 O
            return -ENODEV;
) O; T+ d' ~' W# G' E  a+ X6 B        cmap_from = info->cmap;( I9 O2 X, ~; D
        unlock_fb_info(info);
$ A4 `, I% ?4 M/ W' h$ B        ret = fb_cmap_to_user(&cmap_from, &cmap);/*获取fb_cmp*/- Y" z! \6 ^! ~' P- J7 H( a
        break;
9 h: v( B1 E  e% a. ]    case FBIOPAN_DISPLAY:
7 A' H. p" i+ R' b( C+ V        if (copy_from_user(&var, argp, sizeof(var)))- Y& M$ }" `( e1 A; r
            return -EFAULT;
- U; H1 A8 `# L5 ~        if (!lock_fb_info(info))
; d! N  h+ c; d# L& Y            return -ENODEV;
- ^6 b+ {' S! c) X! F& l' F        acquire_console_sem();
; Z+ l7 U1 B( ~% |' x! ]/ K        ret = fb_pan_display(info, &var);
# F" l8 m; E, [# [- C3 R        release_console_sem();
/ @# h8 M* L/ F2 k) p        unlock_fb_info(info);8 D" O# i" x# A2 {
        if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))( W9 U5 J% B8 ^4 E6 \" U/ P: V$ h
            return -EFAULT;
) m2 B. c& e/ M2 y        break;
1 e5 M; Z: e  `% {% [    case FBIO_CURSOR:7 O; d. Y, ^7 r0 H) u# Z
        ret = -EINVAL;, a- @9 T  Q* C4 ~1 r. J
        break;' F% O8 y% t: m' _* D7 J1 I+ ]  B
    case FBIOGET_CON2FBMAP:
/ Z+ n/ M7 n' Y1 o& N        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
9 e0 O) K$ d. Q4 p6 |            return -EFAULT;7 V- E9 R2 @2 S; w
        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)0 f4 L6 e* M: Y8 D: _4 A
            return -EINVAL;
. s4 d7 C' b5 \, e) Q        con2fb.framebuffer = -1;
/ c; f( C8 G% v) ^8 ^! ~3 z        event.data = &con2fb;5 ?. _$ [& e# B. S
        if (!lock_fb_info(info))
2 Y" v& k  n: D: p2 D4 ^            return -ENODEV;
' `& c; ]' E. v4 k1 `8 Y        event.info = info;
# z5 |+ h1 @2 n: a        fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
, C% V  T) b7 [) D; c' |; Q" `        unlock_fb_info(info);! e; f8 a% t+ \
        ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;( E' Z6 ~: K4 C
        break;
  l" q4 J9 c; j" F! S' |+ ]    case FBIOPUT_CON2FBMAP:4 c* t0 q& b6 }" b1 D+ c
        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
* e  V7 b4 F8 A+ N            return -EFAULT;
8 J" Y' J! l# A; D/ V        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)8 N& U& A0 d" `# y- D: o
            return -EINVAL;
6 S$ a( z, w( {  i        if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
+ L. ^0 S) @' |4 d( p1 ?) P            return -EINVAL;
. \  e9 f/ ~# y; s6 s9 @  ^1 `        if (!registered_fb[con2fb.framebuffer])
  X  U9 M; u7 R  i$ ?* M  G            request_module("fb%d", con2fb.framebuffer);
1 X0 {4 Z- h) o+ h& S        if (!registered_fb[con2fb.framebuffer]) {! {) Z5 n$ p7 m! H, J) L
            ret = -EINVAL;1 P+ ]" O- p% w1 k4 n' A( _& v8 s
            break;
/ k; B5 m: q/ e        }# d2 a# S6 U( b/ J
        event.data = &con2fb;
& r6 K# I4 k+ X        if (!lock_fb_info(info))( g$ D- h) d9 d" k; O" V
            return -ENODEV;
5 q1 E  _+ E8 W        event.info = info;
4 k/ `3 T, q1 ]% J% D8 V% \( q        ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);4 C+ R2 N5 t1 D
        unlock_fb_info(info);
) R2 i5 L- Y$ [8 C+ L9 ?) m3 Q        break;
0 `3 u) [+ d3 \/ h8 r" c    case FBIOBLANK:
/ a, P4 l( L8 w' T        if (!lock_fb_info(info))
* f+ @+ ^& b9 ~6 ]! K: C6 ~- C            return -ENODEV;
$ k2 K  R9 E) d5 o# I0 v        acquire_console_sem();
( S! N; ]! q  d        info->flags |= FBINFO_MISC_USEREVENT;
+ P5 W# J# n% C8 A        ret = fb_blank(info, arg);        ?*最后调用驱动提供的s3c2410fb_blank*/
; B; B9 j2 a" |3 ]- X        info->flags &= ~FBINFO_MISC_USEREVENT;
% B3 i% b& R+ ?" M, s- M8 k        release_console_sem();
4 W5 B2 F2 i# k4 k/ G% e# Y        unlock_fb_info(info);4 I2 m+ w6 D2 X  K
        break;
3 [+ g9 U. t: U' X7 E    default:4 c& g+ P( Z/ K  U! r
        if (!lock_fb_info(info))
3 F# E! f8 n" n6 v# x            return -ENODEV;
2 n, H$ R! x1 g! q& {0 Y% g        fb = info->fbops;# H3 j- c5 H4 x0 L6 c( w
        if (fb->fb_ioctl)        /*fb_ioctl为空*/
$ @* z6 g5 K3 g) u, Z            ret = fb->fb_ioctl(info, cmd, arg);; j4 Q0 ~9 o* c' `6 ^( c' c4 c
        else
5 b; s5 e# a+ J* F% M            ret = -ENOTTY;
0 A' q1 Y# b% h( l        unlock_fb_info(info);
2 {$ i  d! r7 ]    }
2 A* `  S* m8 M0 d3 P    return ret;
7 q$ K7 c% r4 M; l* f. B}/ H0 x- z0 U3 M% ?! g2 g6 c; c; r
正如2.2小结所说的,fb_fix_screeninfo只能获取不能设置,因此,ioctl只提供了获取fb_fix_screeninfo的方法,而没有提供设置fb_fix_screeninfo的方法。5 Y" T0 ~1 x4 S  u. V+ E
) n& e0 a% `) {  _
3.5 小结
# q3 l* Q0 N3 W$ ~  本节对frambuffer的核心层进行了介绍。包括frambuffer子系统的创建,frambuffer的注册和提供给用户空间的5个API函数。下面开始介绍驱动层。
/ j; K0 {2 z" D
9 ]- n; [! g( r' R! {; n4. 驱动层( x$ c0 y/ x- k2 l4 B
本节将开始介绍S3C2440的frambuffer驱动,该驱动源码位于drivers/video/s3c2410fb.c
; l$ G) L4 _/ y% f6 t2 U
' h3 D& P  l7 Q2 j9 j' T' Q首先来看下驱动模块的初始化和清除函数。# _7 _7 s6 c! t( P, R3 Y( j: O
% T- i6 s+ w$ J- W0 i* U
4.1 s3c2410fb_init和s3c2410fb_cleanup# ~& D# p0 z* ]$ o. X
static struct platform_driver s3c2410fb_driver = {+ ^, X6 `. ?0 [
    .probe        = s3c2410fb_probe,5 L8 d5 q( d) U
    .remove        = s3c2410fb_remove,
3 l7 n0 T! ?8 y  t" q  G4 @    .suspend    = s3c2410fb_suspend,
1 N! ^; t: y  v4 \; X4 j    .resume        = s3c2410fb_resume,# X% C' [0 e7 \/ y* v
    .driver        = {
, [2 T# V5 y" q* c        .name    = "s3c2410-lcd",
# R9 \) W4 o0 T( u9 n3 k- I        .owner    = THIS_MODULE,
2 R- V" ?5 y2 g+ n; E: n    },
0 B* F9 F$ n- \3 V4 u% ?0 ^9 i};7 b+ i: B6 c1 Q0 _: {! p* e

' j9 x. N$ b8 R1 H* sint __init s3c2410fb_init(void)
2 O1 H: o/ c2 R8 h! O# W{" K0 }/ x' G% [
        int ret = platform_driver_register(&s3c2410fb_driver);
$ ?4 a, R, ^( R$ B' v6 |& S, S5 r
3 ~6 a% f* u7 k' _" I$ n* l! w        if (ret == 0)+ ?6 [% f5 d* x" p, j0 J; Q
                ret = platform_driver_register(&s3c2412fb_driver);;/ c! I# [; l& T

/ Z  @; \  P3 K/ m, G8 A        return ret;
8 D0 [" H, a6 d, C  N3 K5 j9 [}
# I( z( M& P" G5 _" R* y; O- a' |% E) S* E$ n
static void __exit s3c2410fb_cleanup(void)
! X7 d0 d- Y/ R: E1 Y{
4 A$ q: [0 [& P+ J5 z2 Q        platform_driver_unregister(&s3c2410fb_driver);) f+ n$ x' Y" L9 G7 [) w0 Z
        platform_driver_unregister(&s3c2412fb_driver);7 G( L6 s6 ?+ y, j1 L
}
8 |% o6 o+ K& A9 [4 p0 }/ ^. [) U
module_init(s3c2410fb_init);
/ b6 X) W/ o! _1 M$ Kmodule_exit(s3c2410fb_cleanup);; X  ~/ E+ y1 |0 d# I2 O9 @
' E8 Q3 d5 U" {* E
当platform_driver_register调用的最后会调用probe方法。接下来就来看看probe方法。2 U2 ^3 c) H& I
4.2 probe方法
8 K: z8 \9 s% R. P5 S, Z& dstruct s3c2410fb_info {1 {# T- N! P+ a" K
    struct device        *dev;4 K7 r# \: V/ W8 D2 c
    struct clk        *clk;
9 e9 p+ @: Z5 E7 i  X. k  x1 R2 m0 {3 K
    struct resource        *mem;. B8 N2 C- Q( s5 g% ]( q
    void __iomem        *io;        /*虚拟地址*/
: F" F" G0 t2 N0 d: U8 h% K8 y    void __iomem        *irq_base;. i! ?1 ?: R8 s. G$ u
; }  G( o: ]; m3 Z6 p
    enum s3c_drv_type    drv_type;8 Q' D* L4 e' Z8 h( ?% w7 y
    struct s3c2410fb_hw    regs;9 L1 T2 w$ e4 b, J

+ M" M( d9 L0 f! i4 T    unsigned int        palette_ready;
* D: b) f( u; ?
4 G: O  ]) u  _: S* A  f2 P+ s% d    /* keep these registers in case we need to re-write palette */
5 s3 P, }# p1 x' n9 N4 m2 [% v    u32            palette_buffer[256];$ k! a3 p7 l' @) e! c
    u32            pseudo_pal[16];
! k" Y0 k7 [) o# u" h};
' E" W, @( U; Y/ u: q' }  y: J# M( n1 b+ Q2 b0 J
struct s3c2410fb_mach_info {) a$ x7 l! y9 n: i; Y% w
, e1 h! b) R# J) e. J* I8 d
        struct s3c2410fb_display *displays;        /* attached diplays info */% E" s1 ?3 K/ B7 L$ y6 J$ ^/ P4 {! H2 o8 W
        unsigned num_displays;                        /* number of defined displays */
6 z9 z  j" D' u0 L; i! _. h# j        unsigned default_display;
1 m; L0 Q# q$ u0 K. p& `! J$ U- L# B# D5 I9 }/ b. Q; B
        /* GPIOs */) j. S/ ]# e: I9 N, w9 U

+ N. q6 l" g3 A9 A        unsigned long        gpcup;) r" V% S; @- @5 H2 K; w( G" p, E
        unsigned long        gpcup_mask;$ B# t, [" q8 q( T$ U7 x- f: |
        unsigned long        gpccon;' Y' {  b& C( K$ n& ]- D. H
        unsigned long        gpccon_mask;6 x' y1 ?4 m- y: U
        unsigned long        gpdup;* O9 l' b  t; H, H
        unsigned long        gpdup_mask;! `: g& x) @" m. f( r  j
        unsigned long        gpdcon;
/ I, @$ b) Q- V1 ~1 Y! m        unsigned long        gpdcon_mask;
  Q: b2 `; _) t4 s% Z$ n9 J& Y5 A5 i' D
        /* lpc3600 control register */
, I( W6 q% I- h+ ], L8 a4 t% x# X        unsigned long        lpcsel;
. N( @+ i+ Y" m; u3 |1 h1 d) M9 c/ k};
& q3 ?* X2 |5 ]& {& L
+ j) d* \3 m2 B' }1 c/* LCD description */: `( v) S0 U) s. @: X9 H
struct s3c2410fb_display {
3 \( G5 t/ r8 r4 v# L/ t, ~# k8 W- y    /* LCD type */% h4 f, f; x8 l( N& l8 ^& c
    unsigned type;  Z6 t  R  E, `" C& q
: t3 C* H1 C8 E3 ?" k2 c! ~/ S3 o/ s
    /* Screen size */6 V7 ?( n( i5 `/ e
    unsigned short width;: i- t$ \, m9 l* V4 [! o; r2 x, y: T
    unsigned short height;2 x. S3 E$ ~, y4 k% }" \6 |
* [  O) A: T- P2 G) J+ b
    /* Screen info */
1 T9 ~, G8 L; [5 M3 e* i    unsigned short xres;
* g8 z, m2 S% h6 R$ M/ X8 g    unsigned short yres;! |1 |5 J. U3 P7 V
    unsigned short bpp;2 h$ r; D8 u' C# ?8 H! h5 a
  q4 b8 J: g/ p
    unsigned pixclock;        /* pixclock in picoseconds */! f1 ~1 ?- m/ T; C
    unsigned short left_margin;  /* value in pixels (TFT) or HCLKs (STN) */
$ [0 G2 L8 Q1 n+ p& Y    unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */% }/ |8 X, E- d* x: K0 g; r( T
    unsigned short hsync_len;    /* value in pixels (TFT) or HCLKs (STN) */. N* _) M' I+ t, O: P9 R: b
    unsigned short upper_margin;    /* value in lines (TFT) or 0 (STN) */& n4 k5 W4 q$ }/ ?3 H3 T! B6 K
    unsigned short lower_margin;    /* value in lines (TFT) or 0 (STN) */
$ j$ N) F5 q% T1 `    unsigned short vsync_len;    /* value in lines (TFT) or 0 (STN) */
. P/ P3 A1 t- {9 p4 `% v3 j5 }- y7 G
& m/ W% z0 y& }+ h# C    /* lcd configuration registers */8 y9 A: D- C) Z0 C
    unsigned long    lcdcon5;
* q- e# r( I  _! x' S! S};3 H7 V* m! w/ A  L. k; d9 x
1 q9 I# E  C3 _6 e+ v
static int __init s3c24xxfb_probe(struct platform_device *pdev,
- z3 b- N' N; ]4 P                  enum s3c_drv_type drv_type)
7 o+ H. T: n* y" F; H{3 ]; s5 G4 r) t  n# x+ b4 z9 l& c. S
    struct s3c2410fb_info *info;$ U: c" k) s: s+ n
    struct s3c2410fb_display *display;+ N. W% v, i3 F0 F  h8 [* z9 i
    struct fb_info *fbinfo;
: _+ v# O  \' |: z& y3 R5 U    struct s3c2410fb_mach_info *mach_info;
- d& J5 @# R/ Z/ v7 R, X    struct resource *res;( c9 \- S5 x# S  P5 y. p- o
    int ret;$ h, I0 U/ E% ~. u3 _* a
    int irq;
; L: y  T* h/ }; ?; D    int i;. ]( s- X8 M5 Y5 z
    int size;9 F# v; _4 ]; U; |% ^5 ^; D
    u32 lcdcon1;
; A0 \8 h" i# a8 r2 B' v8 h0 I+ Z    /*dev.platform_data由函数s3c24xx_fb_set_platdata(mach-smdk2410.c)设置,指向s3c2410fb_mach_info*/" v) }* a, O7 ^+ h
    mach_info = pdev->dev.platform_data;   
" z' h2 p/ J. O2 Z    if (mach_info == NULL) {+ }' F. F" w6 u" G0 g. e1 o, v6 v
        dev_err(&pdev->dev," P" @9 G4 x( O6 _8 \  ?
            "no platform data for lcd, cannot attach\n");
! b; N" a6 M: |        return -EINVAL;6 Y6 l: C# ~8 y5 E' Y9 k& W
    }
% B9 r# h: m. P7 K                                    /*在mach-smdk2440.c中,default_display=0, num_displays=1*/
& \& @6 G3 L5 U' u& U4 e    if (mach_info->default_display >= mach_info->num_displays) {     ! H6 h9 ^. y" K0 J
        dev_err(&pdev->dev, "default is %d but only %d displays\n",
9 [: v& F2 k! k+ x0 [            mach_info->default_display, mach_info->num_displays);
: m# O& `; K2 V0 _, H: ~2 X        return -EINVAL;, u6 K9 C1 f1 u5 h! v  C
    }
$ m; p5 z4 d1 o! K, u. m* Q# v
( L: a8 ?* x. I6 a7 D) D# Z2 N/ u, {    display = mach_info->displays + mach_info->default_display;
3 d3 b; P" [+ @- z2 {8 a8 @. X0 ]" o$ }7 L2 r  c, e! F- b
    irq = platform_get_irq(pdev, 0);    /*获取IRQ号,16号中断*/
3 T$ a$ O+ _$ f5 X$ }1 X! d& Z    if (irq < 0) {/ n' |+ R/ q* ?; x9 ^# o  F8 j
        dev_err(&pdev->dev, "no irq for device\n");
2 f- V. p: J3 P0 n        return -ENOENT;
9 h3 K# _) g& ~* u6 E6 k    }
+ d) G" K- c1 Y4 Y" P5 H* U3 y                                        /*分配struct fb_info 其中包括sizeof字节的私有数据区*/: T& j) c/ C# g* [) G  F0 q
    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);5 F$ h/ L& ]! r8 x7 ~, l
    if (!fbinfo)$ N! f% u1 D. ?  V3 A
        return -ENOMEM;4 _6 P" e/ m; L! c8 T: w

+ |' L* m' c+ k2 D% H9 S    platform_set_drvdata(pdev, fbinfo);    /*让platform_device->dev.driver_data指向struct fb_info*/
# F) q5 x% k* P5 `/ g7 l
$ d: @' @" r: ]2 D    info = fbinfo->par;                    /*par指向s3c2410fb_info*/
+ f/ _* @) Q- l    info->dev = &pdev->dev;. {9 W) p* o1 e+ y) Q" l
    info->drv_type = drv_type;2 Z* R' l( V! i
& K% v1 e4 f' w" y0 ]
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*获取平台资源*/' K) y  p# X, Q$ f' F; n, e
    if (res == NULL) {
5 ]; I9 n- i) M& S% v9 ?        dev_err(&pdev->dev, "failed to get memory registers\n");
/ F. j1 Q' e% j6 z& o1 Q! S        ret = -ENXIO;: Q# k5 _7 o3 B( f& E
        goto dealloc_fb;  R4 Q& G% n- k# i9 {; z8 t
    }
5 n4 S7 h2 S8 F9 I* i4 r2 I6 |3 E) A; |' A  O- G* v
    size = (res->end - res->start) + 1;                /*IO内存申请*/8 [; S: E% v' D9 t9 I% m9 a
    info->mem = request_mem_region(res->start, size, pdev->name);      r$ j! V+ u% ?$ o
    if (info->mem == NULL) {/ K: H  `/ ]! H( F8 P2 h
        dev_err(&pdev->dev, "failed to get memory region\n");$ N6 E. W2 ~2 T. l  g; ?
        ret = -ENOENT;* y+ R# l1 q$ A
        goto dealloc_fb;
& J6 `4 B; z8 `( D    }
% c+ c# l% y. r" p* ?
7 g+ u& V# n) C. ?7 j    info->io = ioremap(res->start, size);            /*IO内存映射,获取lcd第一个寄存器的映射地址*/               
. Y9 k, X% v" h0 p/ \    if (info->io == NULL) {
) }( E3 h& t2 F6 b) c        dev_err(&pdev->dev, "ioremap() of registers failed\n");        
1 ^6 D: q$ N; x        ret = -ENXIO;    6 G) L. ]  i6 a' Y- Y$ ]
        goto release_mem;2 \% f3 Y7 M5 w3 H
    }
/ z  d/ Q# o. k  W, M                                            /*irq_base对应的物理地址是0X4D00 0054(寄存器LCDINTPND)*/3 ?; l8 o6 P" q, ^0 ~
    info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
- c' e. p! h" `2 E; S  x/ F# i6 Q2 s/ b
    dprintk("devinit\n");' P  e( `$ e% q
' H; T" r" N6 e& r0 P1 V& N  |
    strcpy(fbinfo->fix.id, driver_name);    /*复制名字*// ]" y8 j5 P+ ?" F9 _% q2 ?
; M+ {! d8 A7 D* _- ^" d
    /* Stop the video */: r  U+ {2 V' Q. x- |! w5 }
    lcdcon1 = readl(info->io + S3C2410_LCDCON1);
$ h( z: o' F1 _% e/ Q, v    writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1); /*禁止LCD*// s) @/ D( i& E* M4 J

7 ^! P  E3 k" B: S# s) A5 w    fbinfo->fix.type        = FB_TYPE_PACKED_PIXELS;6 [, ~/ t( u/ [) m  W0 ~
    fbinfo->fix.type_aux        = 0;  l% J9 Y1 j! O8 y
    fbinfo->fix.xpanstep        = 0;  @& G6 G6 n' o1 v
    fbinfo->fix.ypanstep        = 0;
5 H# _2 Z- T  r0 Z& ?( c    fbinfo->fix.ywrapstep        = 0;4 V* L+ f+ K# \0 M1 G
    fbinfo->fix.accel        = FB_ACCEL_NONE;    /* no hardware accelerator    */7 f* x* ]! V! E0 }* w4 p
. w: D6 F3 H0 ]
    fbinfo->var.nonstd        = 0;8 O* F( \( L- H' K+ y6 u
    fbinfo->var.activate        = FB_ACTIVATE_NOW;
; ^* H- A5 i+ y$ z; G0 D+ l    fbinfo->var.accel_flags     = 0;
( c. z. i# s7 Y9 a' |; I    fbinfo->var.vmode        = FB_VMODE_NONINTERLACED;2 e0 b5 H5 A2 B
9 m' I% E8 b# m0 a. ~; V
    fbinfo->fbops            = &s3c2410fb_ops;9 z/ N# B% B+ r% X6 g2 c' O
    fbinfo->flags            = FBINFO_FLAG_DEFAULT;
, c7 `2 g. w6 ]  R9 M    fbinfo->pseudo_palette      = &info->pseudo_pal;+ g. M! U' i- @4 x6 _
4 \( q& w% f. P  o# k
    for (i = 0; i < 256; i++)
; R: Q* [  a9 S; `% X# Q+ b* T( I        info->palette_buffer = PALETTE_BUFF_CLEAR;1 z3 q0 f6 P" X) z
* L- {6 T! p7 m
    ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);    /*申请IRQ,快速中断*/% l: w. i- L/ C, _# |8 {! n
    if (ret) {
( \' m2 k8 B  M4 ^- T        dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);& ^* U1 l$ J0 S# L* S
        ret = -EBUSY;
6 m( O- `% S$ n: f. |. e8 _% K        goto release_regs;9 J# ^5 w: J; S& }) X3 u
    }7 ]0 X9 ^* z; i8 u2 ^

* J, w2 `" ^8 b4 j0 G, X! A/ [    info->clk = clk_get(NULL, "lcd");        /*获取时钟信息*/5 f0 g6 z" ~; k* j2 M0 W2 n
    if (!info->clk || IS_ERR(info->clk)) {2 t1 D+ {" `; b) m, U' g
        printk(KERN_ERR "failed to get lcd clock source\n");. n3 e8 f+ Z0 [& @7 y
        ret = -ENOENT;
: N2 f7 Y3 w# N2 T) D# |0 a$ b- r        goto release_irq;
6 [& U! `5 K# z# [' U+ e8 l    }4 J9 a3 r8 N/ h4 {4 b
% W& _! A* D; H" f7 u% U
    clk_enable(info->clk);                    /*使能时钟*/) ]7 k6 S* c4 ]" \
    dprintk("got and enabled clock\n");
# L" Q) `7 d& ^' p) s2 N: A6 u- A/ H, _, ?# H
    msleep(1);
. O8 r; V  j2 X# ^1 B7 l9 D5 n# z/ a6 Z4 h2 U
    /* find maximum required memory size for display */! j% ~  z! r5 c, O! B, M( N
    /*在多个屏幕中,找出需要的最大memory*/
6 ?5 |- @7 |; L    for (i = 0; i < mach_info->num_displays; i++) {
6 H) t  s" n7 Q/ P: ]) C  j9 @7 ~        unsigned long smem_len = mach_info->displays.xres;- {' o/ o2 i8 ]+ w( b9 `
        /*所需的memory空间 = xres * yres * bpp / 8*/6 l3 v) s7 q6 y' F5 `7 D- {
        smem_len *= mach_info->displays.yres;
  e' j  O4 _( |3 h% i        smem_len *= mach_info->displays.bpp;' U) E9 }& v9 [, N4 m
        smem_len >>= 3;
6 l. U5 y8 ]& @        if (fbinfo->fix.smem_len < smem_len)
8 A2 N) K, l9 k0 X$ E, P            fbinfo->fix.smem_len = smem_len;7 A2 L% h, z) J+ j) e
    }% a% U  b! H! X/ _/ w

% h# i0 n; H7 E, }. ?5 l    /* Initialize video memory */    /*根据上面fix.smem_len的大小,获取DMA映射内存,一致性映射方式*/0 q  C; ]: Q) p' H; X- x# o
    ret = s3c2410fb_map_video_memory(fbinfo);
( i: x1 B5 E+ E' a' J! ^" c    if (ret) {& h) C+ N* U+ l; F+ |+ \+ O
        printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
- ?. @4 b! \7 S5 {        ret = -ENOMEM;/ O7 U! k1 p1 l  @9 E
        goto release_clock;
, [5 O! W8 _% e. ^" ~1 N    }
4 x$ }. Q* ~8 d; z3 @
6 f' i6 W8 l# f# R    dprintk("got video memory\n");+ i$ U% X: I' ], l4 }% d) ]: S
' k% ]4 U$ N) \1 A4 a9 C
    fbinfo->var.xres = display->xres;            /*320*/
) R( v8 F% S: `, @    fbinfo->var.yres = display->yres;            /*240*/    # X$ Q$ a" }' f# Q5 C+ V
    fbinfo->var.bits_per_pixel = display->bpp;    /*16*/6 P" h/ A  ?( @, S+ ?: ?
. \8 Q! h/ R; T5 K* P
    s3c2410fb_init_registers(fbinfo);            /*LCD寄存器初始化*/        
% Z- `) g# a3 D# A1 `* Z# s% k: [* v/ u- m) F! f' @% W8 P8 {
    s3c2410fb_check_var(&fbinfo->var, fbinfo);1 W, b9 I5 P4 Z6 }
8 e; j7 r- x. L
    ret = register_framebuffer(fbinfo);            /*注册framebuffer*/
  U& N7 Z3 T. [  R  {! K    if (ret < 0) {
( d9 K. {/ s9 n8 o0 V/ l        printk(KERN_ERR "Failed to register framebuffer device: %d\n",ret);- q3 W/ a% r  y+ M$ I
        goto free_video_memory;
( i( u; @7 ~  s  |% P) M# G0 ]! m% A    }
5 K3 w3 g9 W8 }# V
, F% a/ U- Y( Y0 o# S  a0 _* s    /* create device files */; y" ^0 k3 K2 C( t
    ret = device_create_file(&pdev->dev, &dev_attr_debug); /*添加设备属性*// O+ C$ @3 v5 T) m3 o& m
    if (ret) {
1 Z9 q* e$ v+ z# [3 ~        printk(KERN_ERR "failed to add debug attribute\n");
  O: ?+ u7 }# f/ ?    }% |6 f, S9 k- u/ a
! I1 p" p0 ~/ b0 E* O9 H2 q% w
    printk(KERN_INFO "fb%d: %s frame buffer device\n",
* g3 k5 T5 O3 z$ n# e3 `        fbinfo->node, fbinfo->fix.id);
, L  c2 Y* n( X, T8 B, m
- a1 G' s: i4 e( T( b4 N4 x    return 0;% y8 R" ^1 d: ~% J9 W0 ?) O
/*一旦某个步骤发生错误,以注册的相反顺序开始注销*/2 i3 ?$ ^  ^2 ^' x  M: ^8 r
free_video_memory:
% i/ ~9 b. b3 \) b4 }+ y    s3c2410fb_unmap_video_memory(fbinfo);
$ ?, Q& u) H- y0 Yrelease_clock:$ \; G5 S, |% U8 j4 W; X4 V. e
    clk_disable(info->clk);
. F" ]: g0 S% b1 C    clk_put(info->clk);% l: w" ]& i! C$ o
release_irq:
1 `7 x" |4 q( y1 p& D, C! n3 Z    free_irq(irq, info);- L' N+ x" M! s& y/ J  B. m
release_regs:1 [, o/ e: w9 |" k+ ^, z2 G* J
    iounmap(info->io);
( q$ a, l2 y9 Urelease_mem:  ?7 s4 g# F5 i
    release_resource(info->mem);' Z7 A5 i2 J6 `, }
    kfree(info->mem);
6 Z4 H& B) @! D9 V0 R" X1 odealloc_fb:
! d) L! S' R6 F. V0 f    platform_set_drvdata(pdev, NULL);) M, D; W1 v, z7 }
    framebuffer_release(fbinfo);   
, [' t3 o( L  f" ~" l/ U    return ret;( b1 n4 R/ j1 Y
}" E; u* M( b) m5 T

) t& w9 j3 r2 w- m) g这里使用了三个新的数据结构。s3c2410fb_info是驱动程序使用的,里面将保存所有驱动程序所要使用的资源等。而s3c2410fb_display和s3c2410fb_mach_info,是由板级信息,通过platform总线添加到内核中。
- y% [& V" ^: @# F# m% o, L: H3 k  y& d: w
s3c2410fb_display中的成员将被复制到fb_var_screeninfo结构中。! j# \$ T/ M  i6 L/ ]

& M7 j0 V0 c! ]该板级信息的定义在arch/ARM/mach-s3c2440/mach-smdk2440.c中,来看下
* V- ^5 C2 t" j6 K, Y5 t
4 ]/ [! ?4 F' d& C0 ]* rstatic struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
% U" `) M$ ]( Y& B# f- v# Q
- ?- I' k( ]2 \" Z- q3 J; ~        .lcdcon5        = S3C2410_LCDCON5_FRM565 |# I5 P3 R5 P4 O& M* m/ J9 z
                          S3C2410_LCDCON5_INVVLINE |2 `+ a( f) B6 }  c! \
                          S3C2410_LCDCON5_INVVFRAME |
0 S: Y9 d; o- s" q' H                          S3C2410_LCDCON5_PWREN |" E( j+ s; b# V4 R4 T. R
                          S3C2410_LCDCON5_HWSWP,) |+ m! A/ U+ h) M' P( n% @
, }( S6 ^* F6 ~2 g6 a: Z9 U/ k, t
        .type                = S3C2410_LCDCON1_TFT,* D' Q' c0 X/ o4 L: B0 Y0 z8 a( A- T

( e6 E* N6 Q: z, e' b        .width                = 320,//240,* l) g: q2 O9 o0 _; Z
        .height                = 240,//320,
: ]3 Z* L- z. i' ?. W* L
1 c, \7 R& T0 W3 }% q2 {        .pixclock        = 149000,//166667, /* HCLK 60 MHz, divisor 10 */
/ |( `: H$ j  \/ Z% s        .xres                = 320,//240,
4 z1 q; U+ [& \' N        .yres                = 240,//320,
: V+ H* W4 K! G4 n* K$ |9 Q. a2 Y        .bpp                = 16,# N: I6 G( Q: v, c
        .left_margin        = 20,
  G# I' H4 b0 A' B4 A! R9 Y5 H3 [: A6 M        .right_margin        = 38,//8,9 q$ r% K2 b8 S# L" M1 d
        .hsync_len        = 30,//4,
2 }! s3 H$ J( U! G5 U* a% B        .upper_margin        = 15,//8,# a( Z- I9 y& W
        .lower_margin        = 12,//7,
: [, a; q$ [8 R# K! M        .vsync_len        = 3,//4,
) f/ L* @& g$ A2 P; b/ o};' C1 _( g  e7 f( h0 L' c

' e) }; m- T- G  o2 h  Q" w. ?static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {: J5 L% `: `0 q0 E
        .displays        = &smdk2440_lcd_cfg,; i4 N! _" t$ E& _0 t  l
        .num_displays        = 1,
) K$ S3 V: B0 g& f7 t0 F) b9 G        .default_display = 0,
% L- T, Q: L* ^7 w9 p  T9 }
3 z( H) T! c( {, {& R#if 00 v: U# b( F) z% v0 }7 A' l
        /* currently setup by downloader */
! N; ~/ M& b% y9 Q  Q+ u& R' [1 `6 \        .gpccon                = 0xaa940659," J* \! @! }9 s+ _9 _- Y" s0 @1 K
        .gpccon_mask        = 0xffffffff,
- ~' v9 {  v2 ^- z4 ?$ z* Z9 c; M        .gpcup                = 0x0000ffff,
( \8 s; c2 @5 ^6 r- S, U& Z        .gpcup_mask        = 0xffffffff,: n: e$ s. o0 F7 K, H* x3 |- r6 g
        .gpdcon                = 0xaa84aaa0,& i( G( H# p+ ^+ s1 ^% V) N; k
        .gpdcon_mask        = 0xffffffff,
# a+ C. E5 p+ N1 A8 u9 F/ f; ?5 a        .gpdup                = 0x0000faff,
' g. z) ?# u+ S# F" c        .gpdup_mask        = 0xffffffff,( c+ k9 [9 R& Z7 U& p2 m
#endif
% B& w1 v. j7 I% S/ b//no
* d; u' \" j  ^5 s//        .lpcsel                = ((0xCE6) & ~7) | 1<<4,& z. S" D. A7 `7 m
};! x3 N+ c' y3 P
* e3 h3 _' C, g. T2 M  P1 p9 n
这里NOTE:每个LCD屏幕的参数不一样,因此上面的参数会有所不同,这是需要移植的地方。
; W$ G0 o& M2 y. o
0 J, |% D6 H9 {, e5 W8 J随后,我们看下在probe方法中调用的几个函数。首先是s3c2410fb_map_video_memory。
9 V9 W/ {7 m! K. `. d
. t  U, Y" G/ ^/ l. X, j2 j  }/*% A! u* _& W- i9 T+ J; l6 z
* s3c2410fb_map_video_memory():# @; Y% b: i. L
*        Allocates the DRAM memory for the frame buffer.  This buffer is2 U! b+ d4 p1 e- c; ]
*        remapped into a non-cached, non-buffered, memory region to! B! A  ^: V+ E' O
*        allow palette and pixel writes to occur without flushing the2 N0 V) K' U5 p; O
*        cache.  Once this area is remapped, all virtual memory# ]2 P6 R) w/ M) r' V3 |; H
*        access to the video memory should occur at the new region.; @9 t  Q0 k$ `# P9 `6 y, F3 T3 _  M
*/' a6 x" f* u; d4 x+ _' {# Q" W
static int __init s3c2410fb_map_video_memory(struct fb_info *info)/ E, t- j' G0 K. N
{$ o; e" D! X5 P
        struct s3c2410fb_info *fbi = info->par;
) T8 k  V' |. Q. P/ a% P        dma_addr_t map_dma;/ s1 e; t' ?% [7 S& P
        unsigned map_size = PAGE_ALIGN(info->fix.smem_len);0 Y4 h6 A& j4 D
; ~1 f% s/ ^, @! y! g
        dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);8 Q  o2 {8 j7 ]: ]. l
                /*分配DMA缓冲区,并保存DMA缓冲区虚拟地址*/6 s0 a. U  K' {9 r5 `2 p
        info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
6 q. J) d7 Q3 }. `! `0 Q                                                   &map_dma, GFP_KERNEL);) s$ w! \9 F; K) s7 m6 ]! v

' @. f5 |. _; c- L# r        if (info->screen_base) {
: R1 B0 l! k: k- [; u- e                /* prevent initial garbage on screen */
- [, {6 j5 ]8 y. S) r- P* D                dprintk("map_video_memory: clear %p:%08x\n",6 V' B! k8 Q; }( Q
                        info->screen_base, map_size);5 f- w) @  t8 P$ P( c
                memset(info->screen_base, 0x00, map_size);        /*DMA缓冲区清0*/
9 H( P" C9 A5 z* C  X% n/ x  `* G" W! D2 l: J2 Z- ~7 a: ]
                info->fix.smem_start = map_dma;        /*保存DMA缓冲区物理地址*/
& p  K: E( o( ]  I2 t* S1 [+ v3 {
                dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
) h8 _0 q5 u. c8 M# P                        info->fix.smem_start, info->screen_base, map_size);' O( Z6 p2 `  ^6 z
        }
8 k+ E% Q# u1 L: S/ u
) o5 G- i* t+ P1 Y: i        return info->screen_base ? 0 : -ENOMEM;) t" ~/ N% s; h" Q* b5 `  G
}4 i) S% H+ l- Q
9 j; K5 s9 i6 W( C, R; }# J% A, l
- e# Y: Z2 i! C+ \% t& V
该函数根据fix.smem_len的大小,分配了一个DMA缓冲区,保存了该缓冲区的物理地址和虚拟地址。
* l: \( U. c7 g2 v9 ?: k% Q0 n: ?4 u+ W) R: Q
接着是s3c2410fb_init_registers:
% A7 y* K7 v' H! d3 c9 _5 P9 {  f
1 P6 j0 u, J: e, {3 W3 @8 k5 G+ c; i1 r1 f  L
/*! r2 S# T7 a. x0 I
* s3c2410fb_init_registers - Initialise all LCD-related registers3 F7 u  q6 o6 k# T
*/8 _0 G5 s1 h4 A/ G+ i! h, Z
static int s3c2410fb_init_registers(struct fb_info *info)0 D  j( [; O- I* S
{" n2 k5 j; k- A/ q! F+ N
    struct s3c2410fb_info *fbi = info->par;        /*par指向s3c2410fb_info*/
' m) c) ~: a4 v4 p5 K+ l    struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;6 j5 Z& |$ A2 ]( Q& X( i
    unsigned long flags;
. H2 J8 U% h5 S8 l6 P% \    void __iomem *regs = fbi->io;/ b1 t, _4 g  j2 v
    void __iomem *tpal;& u4 \: C1 h6 Q! j  ^$ B  f
    void __iomem *lpcsel;
+ b$ _5 K. B1 ]" N( F% T5 Z+ o9 Y  o
    if (is_s3c2412(fbi)) {0 b8 Z5 f) L# @( d  r1 ^! h$ \
        tpal = regs + S3C2412_TPAL;3 f" F2 w4 V2 w; W
        lpcsel = regs + S3C2412_TCONSEL;
* H- P& d! v0 s  ?0 @& h' B6 y    } else {
: ~7 K5 w- F/ X$ E8 J& W        tpal = regs + S3C2410_TPAL;: P3 V0 a; w2 E
        lpcsel = regs + S3C2410_LPCSEL;1 E* C2 e( N; K8 w/ K
    }3 i6 v3 F# O! z6 e* ^7 \

9 {( C8 ~5 Q$ H0 M/ Q/ |    /* Initialise LCD with values from haret */
& e3 ?0 }" w9 g3 A. l
- z0 {, {1 q: s: Z% s" \    local_irq_save(flags);                        /*禁止所有中断*/
* h8 Q) a& m- y# ~1 p$ i3 }2 {9 C( a7 g" a; n* m# J
    /* modify the gpio(s) with interrupts set (bjd) */    /*初始化io管脚*/
. |" k3 J0 Y% V2 P
# R; l7 s1 n: _* p) P8 X7 f    modify_gpio(S3C2410_GPCUP,  mach_info->gpcup,  mach_info->gpcup_mask);& ?" B% `2 I& d3 l# D# Q+ V* {" a
    modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);1 l% I* B) k" i  r" O
    modify_gpio(S3C2410_GPDUP,  mach_info->gpdup,  mach_info->gpdup_mask);
( b" O- ~3 q3 F% t/ f    modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);* ]. h  D6 j: @
: J5 x7 }5 F7 @+ x, S3 X
    local_irq_restore(flags);                    /*恢复中断*/
' E# g3 C+ C; X; n* x8 ~
5 @5 d' S2 ?' C    dprintk("LPCSEL    = 0x%08lx\n", mach_info->lpcsel);    /*设置TCONSEL,禁止LPC3600*/
, r: F3 o5 S) A& K    writel(mach_info->lpcsel, lpcsel);
9 \- j1 v4 p2 q9 I4 l( X
' p( R) H, s, G4 }7 Z5 F/ X8 M7 k' k    dprintk("replacing TPAL %08x\n", readl(tpal));1 A  K5 ?0 |: @
# b* v# C% M5 f" f
    /* ensure temporary palette disabled */% s8 d' _0 \7 x
    writel(0x00, tpal);    /*禁止调色板*/
7 X. o8 r) F) d" w" p6 I2 Z$ S0 Z
$ T: c! Q2 V+ S! [    return 0;, O8 r5 z( u, ~( P# d" P/ I8 }
}. \6 b, r# K$ R. V( r
( u2 a  l0 g  i, |6 W# x; e
static inline void modify_gpio(void __iomem *reg," v2 }7 ?; k7 o  P; |% s, R  m
                   unsigned long set, unsigned long mask)
" b' ?5 i' B% D. d{( W: R3 H% o0 v: j0 r2 l( H
    unsigned long tmp;
, Y3 F( A# M' K9 s+ f) k& b9 N, S7 T
0 D1 j/ g! u+ V    tmp = readl(reg) & ~mask;
8 U% e* S+ h9 u    writel(tmp | set, reg);! U4 k: m) e* Q; @) u% G
}
/ O/ @& B. S+ E2 m" d; Y最后是s3c2410fb_check_var:
* P1 ]* f7 N1 t
. A) L$ Y3 A' }+ a/*( y* `9 g& K! E% \, w
*        s3c2410fb_check_var():! Q4 ~/ ]& A6 t7 Z+ z) i  \
*        Get the video params out of 'var'. If a value doesn't fit, round it up,, n4 k; x$ s# \
*        if it's too big, return -EINVAL.% r& V; _$ F- l1 j/ o) n0 O
*        检查变量的合法性2 Q* q: ?. X3 k# Z1 R" Y4 F
*/, B0 `! g/ G+ C# L- V; b4 w' y( I
static int s3c2410fb_check_var(struct fb_var_screeninfo *var,% E$ M' Z6 B$ U3 F/ F( l
                               struct fb_info *info)
9 l# \, c# R6 w2 E- M  o4 a{* g+ U4 ]$ `8 z$ r/ {: N
        struct s3c2410fb_info *fbi = info->par;                        /*par指向s3c2410fb_info*// a3 k- t; r8 m7 _2 v! Z* U
        struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;/*指向s3c2410fb_mach_info*/
% ?( B* N' [8 T: [, X        struct s3c2410fb_display *display = NULL;
# j9 \7 A6 r  w# x1 Q6 u, ~3 X        struct s3c2410fb_display *default_display = mach_info->displays +
* V% I- ^& Z. J3 G, V                                                    mach_info->default_display;& ^" N4 {! q* C* ?
        int type = default_display->type;         /*S3C2410_LCDCON1_TFT*/
4 ]5 z" s, I8 U) h( U" f( {! L        unsigned i;
' H# U/ E% o) u. g
7 ~. [' E1 g* s" Q( @! N; g( N  |/ x        dprintk("check_var(var=%p, info=%p)\n", var, info);
1 Q9 }) O; u9 ?
( n* Q( n) d' x# S        /* validate x/y resolution */9 Q$ Q% [4 [6 p. k2 ^$ K4 i
        /* choose default mode if possible */                        /*var中的成员在probe中设置*/8 z; K, h  [; `, L/ Z
        if (var->yres == default_display->yres &&
  `0 V/ @! e8 u% @3 g- ]            var->xres == default_display->xres &&8 D" B; ?' w( K% o
            var->bits_per_pixel == default_display->bpp)% x) J& V- e0 z8 H9 P
                display = default_display;2 |# r( Y1 H3 {: n- K
        else
& n9 s, U; K, ]$ h                for (i = 0; i < mach_info->num_displays; i++)
6 @7 w2 n/ a1 ?0 F                        if (type == mach_info->displays.type &&) b' J, {% J5 N
                            var->yres == mach_info->displays.yres &&; ~; i& E* n) x; s8 H
                            var->xres == mach_info->displays.xres &&$ X6 Q- z& [6 \# m/ G2 x7 P; t! L
                            var->bits_per_pixel == mach_info->displays.bpp) {
% s' l" Z& A$ Z1 Q8 f                                display = mach_info->displays + i;: w2 x7 d9 z, q9 i$ H+ i
                                break;% C. Q+ q6 s2 L0 g! O  g8 I. B
                        }
4 @; z; X% w4 D3 m1 E+ k
1 d& A3 M$ C# M+ _4 s' f        if (!display) {
/ T4 h% a  ^4 p8 p1 _- ^1 m+ |                dprintk("wrong resolution or depth %dx%d at %d bpp\n",% i: Q+ {2 {; R1 R8 w: s
                        var->xres, var->yres, var->bits_per_pixel);
8 M# b9 b9 l1 W1 m                return -EINVAL;
, c1 w: t+ X1 @4 Z: f$ G7 R        }
' y9 H- |- y  b  ~0 L
5 O0 d$ `% ~! e5 |4 ^" `/ N; Z" J        /* it is always the size as the display */& h, [# P2 u1 a+ T) z3 c
        var->xres_virtual = display->xres;
2 a7 J5 {# _( v# r' m, n        var->yres_virtual = display->yres;8 D  ?3 ?. V1 K$ S3 m
        var->height = display->height;
0 l  s" K1 T: {( N* j' _# h        var->width = display->width;" G! F* L. ~# f( f6 l
/ s+ m/ k; q) e/ H6 V4 o1 G
        /* copy lcd settings */
9 @( L- V2 y  K6 E% ~        var->pixclock = display->pixclock;! g4 R# z3 b' p/ Z9 a
        var->left_margin = display->left_margin;
  ]: X  [4 @% g1 S        var->right_margin = display->right_margin;
. A$ M0 `4 I5 T        var->upper_margin = display->upper_margin;5 _( d: S  u1 T. W. M
        var->lower_margin = display->lower_margin;
: Y( ?$ r4 Z" K% R: V        var->vsync_len = display->vsync_len;9 q: a% v1 |: H' A( ~) P
        var->hsync_len = display->hsync_len;
2 o; L( g( U0 D" q9 K. p6 |
2 A6 @/ X3 [9 N* g* n        fbi->regs.lcdcon5 = display->lcdcon5;1 O8 Z* V- z( R7 W
        /* set display type */# e0 u9 F" {5 l8 c
        fbi->regs.lcdcon1 = display->type;7 Q& T) h% K& C3 Q9 z$ |
9 g6 }+ S. N2 S# Z, K
        var->transp.offset = 0;0 M6 W) e' s4 h5 A8 L& o/ `
        var->transp.length = 0;  x1 k6 z- R9 X1 n9 M1 K3 C( x
        /* set r/g/b positions */' y. b3 t& _; I( A
        switch (var->bits_per_pixel) {
- D- J: G9 Y- _        case 1:
) v) @+ B3 M$ o9 r: a/ n$ p* E        case 2:
1 {5 Y8 s, X7 ?! P* Z3 T        case 4:
% M2 J$ r! o' W1 E& x                var->red.offset        = 0;
$ o! D  e9 O0 s: U                var->red.length        = var->bits_per_pixel;; A, ], D  z6 Z7 O
                var->green        = var->red;6 ^: m; ~4 d1 U. R& N
                var->blue        = var->red;6 C  n* y& K2 L8 J3 ]
                break;# A+ b0 o" m! n' f
        case 8:
% N3 W% U" v) M7 U                if (display->type != S3C2410_LCDCON1_TFT) {
" {3 J8 H( Z0 t! {                        /* 8 bpp 332 */: E' G' S5 P: [! U" |
                        var->red.length                = 3;
' v" a+ L3 t0 a  D5 k& L5 W5 K' e1 `! q- L                        var->red.offset                = 5;
3 X+ ?4 W3 q5 ~! X4 X* v                        var->green.length        = 3;
/ t* C2 d: _* }' [. @* L9 b                        var->green.offset        = 2;* _0 `. U( m% V
                        var->blue.length        = 2;
) ~  Q) m/ n4 o  `                        var->blue.offset        = 0;
0 @. t) ^( O# W/ z2 D4 x                } else {) t" t6 P  F9 B8 R7 K# u/ u
                        var->red.offset                = 0;
2 P8 S( c; D' K0 d# P                        var->red.length                = 8;
% }3 V: u  |& f" j1 o, A                        var->green                = var->red;+ Q5 o6 s9 \) L# V; E) n/ ~
                        var->blue                = var->red;
! y; {6 |7 B0 a, T% Q, j: H6 w+ Z* }( w                }
. o: t+ s0 {9 }+ c; E  O                break;# H- w& ~) ?* X
        case 12:
8 F$ ]3 Q+ Z3 N! g8 J                /* 12 bpp 444 */2 x! P5 F0 B( V/ n* r
                var->red.length                = 4;+ L; f+ y9 {5 x! \
                var->red.offset                = 8;
9 Q8 m3 U- I7 a$ Q' u                var->green.length        = 4;1 C9 m% }0 i# S
                var->green.offset        = 4;
4 l" l, b; r' X                var->blue.length        = 4;6 @1 E! z# V' ^: j( V) f7 z
                var->blue.offset        = 0;3 T" e$ V6 d, g1 I/ l
                break;$ ~' ^- Y; E! `8 ]& K# D. l

) @# t- \$ \( `7 C  o, h& _/ ?, O9 f        default:
/ r6 e+ ?4 i% G6 s) F/ g        case 16:3 F* |: G9 F' u9 f) L
                if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) { /*使用565格式*// W1 u2 q9 b( w0 _! o
                        /* 16 bpp, 565 format */
( B2 b6 U: h; k7 E5 P+ Y                        var->red.offset                = 11;+ n) F* J" K+ m. C: q
                        var->green.offset        = 5;) U2 M: j( w# P/ N
                        var->blue.offset        = 0;+ F$ O" E6 N9 M) e& c
                        var->red.length                = 5;
2 o, c: O& U( J; k7 A6 D                        var->green.length        = 6;
) h6 Z3 r' i, l                        var->blue.length        = 5;
. I# g# |# Y7 B6 }- [                } else {
4 Q: r0 e; ]: |! i& |                        /* 16 bpp, 5551 format */$ n- e$ b* l* b( R* B: J9 O( X3 s
                        var->red.offset                = 11;  c! e7 |/ j; s! v1 M
                        var->green.offset        = 6;
" x9 t1 h# S. t; N% R# m, e* r5 Z2 p                        var->blue.offset        = 1;
# w8 ?$ Q6 Y- e  V3 c3 g; _                        var->red.length                = 5;
" }0 p1 l$ m+ Y                        var->green.length        = 5;
0 z+ A: k* G! F( `% W                        var->blue.length        = 5;8 m. Y+ v( J5 P  C8 D3 g* U
                }
1 A( `6 |3 U/ U& c$ @                break;. A& v- F8 S" c2 I  A0 e% `
        case 32:- N+ J$ R9 Z7 v+ b# G  a: Z+ T9 G
                /* 24 bpp 888 and 8 dummy */
! M8 X9 {9 P8 d6 Q' t" m                var->red.length                = 8;
6 J; Y0 }; i- v: _& f8 u                var->red.offset                = 16;
* s9 l5 q0 D5 f& Y3 [5 U% {                var->green.length        = 8;
1 |7 {; p& a2 K7 ?5 G1 B7 T                var->green.offset        = 8;
& @0 u" {* \4 R# z% d' }+ k& b                var->blue.length        = 8;5 X& J  M/ @& U6 j
                var->blue.offset        = 0;* f3 j, j1 W5 Y; I3 H% H0 P
                break;
: u8 q0 X* t/ e/ U        }: W# y  ?; k1 T0 |4 S
        return 0;
2 v. @2 l( e) d, E! E}/ F0 {% a& j+ a8 g/ T) }" h' z
1 ]3 F  F3 Q5 Q- a1 y# J* H
/* Interpretation of offset for color fields: All offsets are from the right,
+ }! I0 X3 I: |; d * inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
' P+ q0 X: ?/ S) t * can use the offset as right argument to <<). A pixel afterwards is a bit
& o' ~# z% K' r3 F; U: [: p& R * stream and is written to video memory as that unmodified.& O( `; _) B& O7 P
*; D8 n% v4 \7 q3 C, c
* For pseudocolor: offset and length should be the same for all color
) @" O& @( c1 M * components. Offset specifies the position of the least significant bit
1 W2 j7 ?" U+ o) b$ I * of the pallette index in a pixel value. Length indicates the number
* Y2 E- t( O8 C * of available palette entries (i.e. # of entries = 1 << length).
" g# [, C  |4 b' j8 t */" d1 ~! m5 X! U' m0 c
struct fb_bitfield {( n/ J1 S2 b& o; ]
    __u32 offset;            /* beginning of bitfield    */- Q8 L& T$ y( r, ^  E
    __u32 length;            /* length of bitfield        */
; `- R8 a+ H* L1 u1 [' j    __u32 msb_right;        /* != 0 : Most significant bit is */ 2 a, S5 \: I! @" k
                    /* right */ ! j% M9 z, ]1 E% u/ [& Y
};1 t# |! B# ^1 P9 t6 n- x
该函数主要将板级信息s3c2410fb_display复制到对应的地方,然后根据RGB的模式设置位域。2 k$ d. m5 b9 u8 a7 P) I
4.3 fb_ops方法* ~3 @. h  t( L9 U; m
在驱动程序中,定义了fb_ops,如下:
( ~; f7 C, }, j+ h: E4 `' V
9 t3 j. B9 h) ?7 W* F# ^" Istatic struct fb_ops s3c2410fb_ops = {# m; P: O, K$ i* e4 m- u
        .owner                = THIS_MODULE,
& l! t  M$ h2 B6 ]7 S. N* v) B3 ~        .fb_check_var        = s3c2410fb_check_var,                /*检查变量的合法性*/
' u6 p, v; O; c6 o) i        .fb_set_par        = s3c2410fb_set_par,                        /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/
: a7 O1 p& {' z2 b. ^% ~2 B        .fb_blank        = s3c2410fb_blank,                                /*该方法支持显示消隐和去消隐*/
) Q5 N, G" E5 `& g( K        .fb_setcolreg        = s3c2410fb_setcolreg,                /*设置颜色寄存器*/
" _% z9 l, V4 W7 Y- ^" A        .fb_fillrect        = cfb_fillrect,                                /*用像素行填充矩形框,通用库函数*/
+ g3 ?; w1 h4 E4 _1 X% u        .fb_copyarea        = cfb_copyarea,                                /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/
8 d* S6 }( g* T: o/ l3 _  d2 }        .fb_imageblit        = cfb_imageblit,                        /*显示一副图像,通用库函数*/
1 b7 ?, O$ k1 L3 J" D};
' [' R  O, m: ~& _9 c( l
( |) r- e, w+ i) m. f5 T, q# c其中s3c2410fb_check_var在4.2节中已经分析过了,最后三个方法是通用库函数,在这里不作分析。- S. i: U! S. ^. a" t
剩余三个也是驱动程序提供的,现在对这三个程序进行分析。" F3 U% Q, Y9 a, b6 P8 ^8 O
4.3. 1 s3c2410fb_set_par
7 A" [" e& w/ {8 s/ i/*& n( r6 C( ]4 n2 x" K. k9 o! g- ^& |
*      s3c2410fb_set_par - Alters the hardware state.
: _) P) C4 o% [+ N. Q: C/ L *      @info: frame buffer structure that represents a single frame buffer' B. v0 C4 M- ]2 P+ B
*                根据var中的值设置LCD控制器的寄存器
1 B3 a9 a! `0 ^; ] */$ f; A# G; d6 E2 l
static int s3c2410fb_set_par(struct fb_info *info)
5 n' k! t3 ]4 W& {8 w" h{; E! [- r4 \: w
        struct fb_var_screeninfo *var = &info->var;" {7 W2 u7 C8 X+ g9 u' N0 x# b* P
4 g" t0 F  J, P$ \: t" K, M/ o
        switch (var->bits_per_pixel) {
8 l% L! s' O4 J        case 32:- v3 k3 o4 s- Y6 e
        case 16:
& R6 c+ Y( v" Y! J* x: u& d' I        case 12:& e1 b8 d! d" @8 ~
                info->fix.visual = FB_VISUAL_TRUECOLOR;  X1 [( S3 \" K- u7 v3 \
                break;
" u' U( X  j. z% }. _5 k4 M        case 1:) J2 u' L4 s: Q4 K* K
                info->fix.visual = FB_VISUAL_MONO01;7 Y' C( i8 g( r& E( ?0 _
                break;  c. z' x9 h7 _7 D" Q4 \. O
        default:
$ J, R% E2 H: a' E' X% k) e                info->fix.visual = FB_VISUAL_PSEUDOCOLOR;9 G0 ~* Z: X0 ?/ C4 J8 P
                break;
% `, }' v) P- ?/ J# d        }3 a7 c" \7 _. q2 \
: L  e' M8 S  {  C' x
        info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; /* 320*16/8 = 640Bytes */" J6 J  @) h% [: [( D- F6 n8 j

. ?/ |' i9 K" R8 o% ^* R        /* activate this new configuration */
# |* a) ?2 L. B+ M$ M: ^
0 b! k3 R8 ~+ _( E0 W) N8 T8 w, ?        s3c2410fb_activate_var(info);
1 g1 o& B* |" X$ M4 X5 w        return 0;
6 Q' e7 n! d9 \1 j}
1 W4 _# C0 E/ Q. m+ ^该函数根据像素的位数设置了视觉模式,本例为16为,使用真彩色。然后计算了每行的数据元素大小。共240行。
$ w& _; H2 z' q: u) Q% B  Z1 l$ ]* d- ^9 d  S
然后调用了s3c2410fb_activate_var来设置控制器并激活LCD。
. @$ c7 Q6 n0 V. I* C1 @! s, s3 _/ l! ~. W9 {8 i$ V' k
s3c2410fb_activate_var函数如下:7 _8 ]% U& C" X
7 q' v! Q* `" Y( z5 r; Z2 U9 o: u; C7 X
/* s3c2410fb_activate_var5 G- ^( N2 M2 a
*  ~- E" ?# @! u. A3 ]0 ]/ t% f2 G4 \
* activate (set) the controller from the given framebuffer
2 e7 ?4 @. z  k9 F4 M * information
: S( s. q# x6 ~: U5 C( E  i7 n0 f */
/ S3 l2 Q. J# i) g4 `# Mstatic void s3c2410fb_activate_var(struct fb_info *info)8 ?  p, ~3 _: L  i
{
' o$ q9 ?* K8 F6 j, b. X  J. q        struct s3c2410fb_info *fbi = info->par;
# X" P9 X1 ^6 x7 v        void __iomem *regs = fbi->io;
1 P- F$ p' T/ y        int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/" H# U2 E  q% L
        struct fb_var_screeninfo *var = &info->var;
" Q/ _+ U* P) x3 I) w% V        int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;' q8 `9 x% T6 w  }% y7 n) i+ z
1 k6 C' A* C/ H* ~, j8 z: B
        dprintk("%s: var->xres  = %d\n", __func__, var->xres);2 q/ i) Y% `; T
        dprintk("%s: var->yres  = %d\n", __func__, var->yres);
+ D1 Q" y! j, ^        dprintk("%s: var->bpp   = %d\n", __func__, var->bits_per_pixel);
3 s2 I, N/ F- D$ A9 d- w, b+ l9 V2 |0 @  P( i7 f4 |4 S
        if (type == S3C2410_LCDCON1_TFT) {
% B: ]5 k4 l0 ^" N. W9 J9 l" \                s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/& ], n) s2 z: X5 Z
                --clkdiv;
1 \$ M' j2 O' n9 F                if (clkdiv < 0)
: t  K2 v9 g. i- c- I, v4 i                        clkdiv = 0;$ N7 t3 y2 @# y' |
        } else {" [; z6 F4 K/ L( A
                s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);% G# e0 l5 w( P) \8 C" x; K( S* A
                if (clkdiv < 2)
+ T) U8 E4 T  h. B& s                        clkdiv = 2;8 _! M1 p/ k, s" m: w. l9 {; Q
        }! v3 \- H$ `8 E* M' L6 k0 `
" o8 O: q$ v& T) O8 g7 e) G! N( \
        fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/8 T" q, ?4 c+ O) J! }3 K$ r! J

  b0 {9 }5 l" n: A        /* write new registers */
; x/ ~7 t$ y! X2 b; j9 B! |6 f) {$ J7 f9 H1 l; F
        dprintk("new register set:\n");
, X6 t- E0 _5 t+ O9 I9 z: n        dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
4 @* z# C8 J) F        dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
' a6 d- ?; X4 ~+ ?6 A: }7 I        dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);# @: i/ I! c- g' I5 k9 p7 g4 N
        dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);+ H$ |% }5 \3 }/ r# ?- V: N
        dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
/ o7 O! \% l3 [1 l        /*把计算好的值填入LCD控制器中*/' l6 s# j* k! y0 y- @/ j* |
        writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,        3 y+ m& T! v' _" X2 X
                regs + S3C2410_LCDCON1);                                                /*仍然禁止LCD*/
+ A+ n) G8 r+ v3 }* V# D        writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);6 S( T" |* d! @- L+ ]
        writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
. C/ y5 F3 ~( L        writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);6 C- N  P5 F: m) x+ e( M# K  P% H
        writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
" Z5 g& h0 z* K( i% L2 w3 [: S1 e7 s8 e, Y8 t  s
        /* set lcd address pointers */3 G# r1 z  {) P3 p& |) E* W6 ~3 w, q
        s3c2410fb_set_lcdaddr(info);                                                /*设置LCD帧缓冲起始地址*/
/ V& X' o8 R; s. F$ X6 N6 o
: M2 `  `7 j/ E. W, N9 ?" n8 [. ~! ?        fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,+ Y+ L* q3 c- c1 a! c9 f: M& _
        writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);        /*使能LCD*/
; Y# N( E- X1 R}
4 I; l& Z. ]& |# u其中调用的三个函数如下:2 b3 l- g9 g% m6 C/ t
static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,: ?0 Z+ @% p) _" X
                      unsigned long pixclk)
& b* C3 z: w6 N3 u0 Z  b, o  `{
+ @5 h1 D2 l5 P# m/ X5 y, i    unsigned long clk = clk_get_rate(fbi->clk);         /*获取当前时钟频率(Hz)*/
0 E- ?* u$ r" |2 ?3 L5 T: p    unsigned long long div;' C, ~$ p, l9 w# X+ ]5 H

( D0 k" p3 ]8 N2 \! l    /* pixclk is in picoseconds, our clock is in Hz% s* n9 s& c) {
     *
" n. c( t) m. y. S( T7 `     * Hz -> picoseconds is / 10^-12
+ M& ]" W7 O" J9 H8 q; r" @1 j" N     */
, x5 [, ^& O- c5 T9 Y  X9 [  t0 U% E; q, }2 |0 G# I, b2 [' g
    div = (unsigned long long)clk * pixclk;; b/ l+ B1 _' T' v0 K
    div >>= 12;            /* div / 2^12 */
$ j# C8 f' K$ y3 g) `    do_div(div, 625 * 625UL * 625); /* div / 5^12 */
/ w7 a8 I( n2 v, \1 f( |
4 m5 o# L# g0 G4 [4 w/ ]3 M( p% u4 ?    dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);  b8 ^! [2 E0 j& R/ Z/ v7 H
    return div;
& e1 d: c/ E! u9 k( T) g& l3 ~}2 J6 G, M; w% S% @; c
- X" _- l, u: J4 ]
/* s3c2410fb_calculate_tft_lcd_regs
! j. s( j* X7 w4 o *, j% g7 ]& h6 d* E7 X& r$ P8 W
* calculate register values from var settings; o' y: ?1 w4 @  O3 n
*/
+ l/ g/ V! B% e% ?( xstatic void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info,! x; r- W" R; Q  R# ^; j' P6 U+ k6 `
                         struct s3c2410fb_hw *regs)
. y# C7 T$ W/ z0 S6 \3 B{- {0 [  E% j$ l
    const struct s3c2410fb_info *fbi = info->par;
6 s' S* ]* ]  P4 R    const struct fb_var_screeninfo *var = &info->var;3 `" }9 ?: u8 R. N% K" c
9 C% L/ M# Z# M3 @0 v: h% {/ f) x
    switch (var->bits_per_pixel) {5 d% ~8 S  R+ S7 l- X# ^) m& x6 T% P
    case 1:
4 W% s% z# E2 t3 O& r        regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;6 `. M* z8 s, Z4 X7 i- q
        break;& o) ]! R) \6 i* z7 r# U1 L
    case 2:
# R3 \9 ?0 h) D8 W8 n; N, Z3 k# K        regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;
0 X" Z& i4 I; g0 s: X        break;
- k# D9 w0 J6 _4 G    case 4:
, Z' b/ l0 U+ P- D        regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;
. e. W: r8 @7 N5 A! x        break;
7 r( U+ Y2 ^5 x- v8 j    case 8:3 R& @3 G% |3 U0 Y+ a
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
9 f, Q7 Z3 V9 G+ ]        regs->lcdcon5 |= S3C2410_LCDCON5_BSWP |
1 _4 b$ B- z6 J, @# z9 h                 S3C2410_LCDCON5_FRM565;
' v% G% q- P; @* J" Z6 N        regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;, c8 \& f- A& c3 v& @# x
        break;2 c7 o8 D0 v" ^- L4 F- T7 n( f
    case 16:
1 F! f9 P4 C! ~) p9 A* l        regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;. L# T: V: Q2 y, N" E% i
        regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;
' o' ~1 K8 q9 q9 T$ \0 f7 s/ g        regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;
1 I0 ]. j; D- M9 n4 R& _# e        break;& F, _( o' O0 |5 ^- n# x- S9 u1 ]
    case 32:
9 w- r6 h' o- Q6 `        regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;2 K  k1 T1 m) N& u/ s! i8 L
        regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP |* n4 e1 r+ ?) Q! j& r8 T% u; Q
                   S3C2410_LCDCON5_HWSWP |8 X+ n% S3 f, ^! q! G( a
                   S3C2410_LCDCON5_BPP24BL);) w0 }! D3 e& |0 }- R% o% w
        break;
/ ?. e9 [+ T% K0 T4 q# _7 \* ^    default:
! {# u" w0 V3 v; m1 X* V& r        /* invalid pixel depth */
' p/ D9 V7 }  L% s! [        dev_err(fbi->dev, "invalid bpp %d\n"," [! w8 n* g0 n' g  Z$ T0 q( y
            var->bits_per_pixel);/ L9 U" l2 d! ]- x# ~
    }6 P8 j1 j& U/ l" b/ @
    /* update X/Y info */5 X( Y& u* ?( N3 ]  T
    dprintk("setting vert: up=%d, low=%d, sync=%d\n",
! M: d* z9 i/ t        var->upper_margin, var->lower_margin, var->vsync_len);
. }- ~5 Y9 W$ R: |
% g* A4 k9 w9 o; {6 m, v    dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",. m1 ^# Q2 V# [
        var->left_margin, var->right_margin, var->hsync_len);" y4 |: ^- \& L0 l# Z0 {' f0 B( s
    /*
) e9 o: Y: }. [) K        所有时序参数必须减1,因为在公式中:
( i3 j% D; _! v& w        Frame Rate = 1/ [ { (VSPW+1) + (VBPD+1) + (LIINEVAL + 1) + (VFPD+1) } x {(HSPW+1) + (HBPD +1)$ b2 Q1 O- X3 K, C
        + (HFPD+1) + (HOZVAL + 1) } x { 2 x ( CLKVAL+1 ) / ( HCLK ) } ]
+ J' A4 H  D: }    */  U$ Q# H5 [$ W- J% z5 ^+ w
    regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |$ z, M: W/ z6 R
            S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |6 }. x5 ~4 B) i) e
            S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
( G4 k7 R" Q6 V            S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
- r  W! G8 ]0 b" s" q& v( r9 L- L
% k1 e8 d" A% m( x* w# o    regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |) s, o% o/ n* R9 Q9 s$ f7 T$ I
            S3C2410_LCDCON3_HFPD(var->left_margin - 1) |
4 f1 x# e! b: I0 a; I# g            S3C2410_LCDCON3_HOZVAL(var->xres - 1);. }( _) S' _2 K( {6 y% c" }1 j( q

) U7 A" ?; v, o- t/ r. c+ F7 M    regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);! A" b. b& d3 s8 ~2 @+ K
}
, K- q# r- l" g1 A
( r9 @1 Y( d! `( T$ z- H6 B2 b3 M/* s3c2410fb_set_lcdaddr, Z0 C+ B7 y3 A% P
*
- \0 I* Q4 F8 _& y$ j * initialise lcd controller address pointers2 b& X6 A2 N- O+ }
*/+ T( g7 ^: k; S
static void s3c2410fb_set_lcdaddr(struct fb_info *info)
+ h* P1 {. @. g, _* f* `2 r7 s{
8 ~) u; h" ?- Q- \+ U6 ^# z        unsigned long saddr1, saddr2, saddr3;
6 b" O; A; V8 k# Y, f  G        struct s3c2410fb_info *fbi = info->par;2 E9 f9 W1 S6 A- s" `& a  H; G
        void __iomem *regs = fbi->io;1 A* }2 ~% L4 Q
& R5 K0 Y( g8 `  B: I0 T  \8 X
        saddr1  = info->fix.smem_start >> 1;          /*帧缓冲区起始地址*/" S" o* [* \8 t2 I5 W! C
        saddr2  = info->fix.smem_start;                       
6 ~& C% v! D4 t# y        saddr2 += info->fix.line_length * info->var.yres;/ t, A* C& ?$ E% H( T
        saddr2 >>= 1;                                                        /*帧缓冲区结束地址*/& G1 t$ c6 `3 W

: v6 ~  M) N( O# V8 H$ V        saddr3 = S3C2410_OFFSIZE(0) |- d- v2 m! Q# `( M2 r  b# A) a6 M. q# o
                 S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff); /*offset = 0, pagewidth = 320*/
( L  x& m; Z; F* z
/ s4 S& }8 o( X2 K" J        dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);9 P( I+ v* Q  x; G2 v+ D
        dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);) W: B1 G0 z  j* E; T' b
        dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);4 b# t' v: E! b3 W1 Q2 f7 U

! g5 i& O: v, w- R; a  D& ?. a0 ?        writel(saddr1, regs + S3C2410_LCDSADDR1);- d/ z1 f) P) L/ W8 [9 N- Z& a
        writel(saddr2, regs + S3C2410_LCDSADDR2);
5 f* ~7 E6 }) ~! m& ?6 {        writel(saddr3, regs + S3C2410_LCDSADDR3);% Z: `6 h6 r6 \, H+ z  V0 t
}, W/ X& {$ V( L. M
s3c2410fb_calc_pixclk用于计算时钟频率。4 Q2 G# G+ }6 e( H

* x* C5 A& w" E! g( As3c2410fb_calculate_tft_lcd_regs设置了LCD的控制寄存器。/ d5 u$ g  p2 I# m4 Z9 B* J
+ t( ?* ^& ?, i9 O& E
s3c2410fb_set_lcdaddr设置了LCD的地址寄存器,该寄存器的设置请参考datasheet。/ f9 ~( j# y4 Q1 T) H* \+ {& d0 q
4 G/ L8 D9 v3 X7 d$ M8 l
4.3. 2 s3c2410fb_blank
9 C: P' Z$ ]- z该方法完成消隐功能。) g* D& \  s- U8 X* |8 Z

6 `8 `4 o! F7 r5 Z$ o7 M7 z/*2 F7 e: L, o' z  j! L! k
*      s3c2410fb_blank
! _4 n# H6 Q/ o( [6 Y+ n# k6 l *        @blank_mode: the blank mode we want.
2 r: A, O" R3 u. O3 y& A  h- Y *        @info: frame buffer structure that represents a single frame buffer4 |# Y( y; _- r0 A/ j
*
- d) ?+ p( S9 a* j: s) ?& m8 H *        Blank the screen if blank_mode != 0, else unblank. Return 0 if* d! R6 U/ v: g/ |, E
*        blanking succeeded, != 0 if un-/blanking failed due to e.g. a
4 A9 a: h# U. ? *        video mode which doesn't support it. Implements VESA suspend/ o; Q3 q- ?. b# W5 u- _
*        and powerdown modes on hardware that supports disabling hsync/vsync:
5 `/ Y1 \' i" _ *
$ @( m, i% A* X& X8 V/ ^ *        Returns negative errno on error, or zero on success.
/ f! J! _) d& G  m. _ *
9 E* ^" Z, |8 d* e! {& @  F */
' r. ?# T' q. C7 Q! O8 jstatic int s3c2410fb_blank(int blank_mode, struct fb_info *info)
% n! b- F  D! d8 O4 v{, P! O( L, ~0 a( s5 O9 s- H: ?
    struct s3c2410fb_info *fbi = info->par;
, b6 _! e3 U5 D9 E' x, L$ V$ F    void __iomem *tpal_reg = fbi->io;
' P* ]; q( C3 ~6 K
7 u- [1 J' i( e6 E2 z. r2 X    dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);
* W5 l: r' B; D# I7 g5 k9 h
$ `4 Y0 i8 O  h- T. k" x" ~    tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;
: N$ Q7 m. i1 P8 D) c7 D2 i8 a( Y: p/ E- R4 C$ Z1 X  D4 k
    if (blank_mode == FB_BLANK_POWERDOWN) {    /*消隐*/
9 z) Z  e2 @; ^6 ^( |) I  U9 w        s3c2410fb_lcd_enable(fbi, 0);        /*禁止LCD*/+ e' L5 q) F, s& q2 z. a. q7 Z
    } else {
( W3 P( i8 ~1 g: }, E        s3c2410fb_lcd_enable(fbi, 1);        /*启动LCD*/% ~& p; P8 ~  J! M4 _1 j
    }
" g/ [# H$ S( E- O$ y  C
# f% ~: n! y( F( _    if (blank_mode == FB_BLANK_UNBLANK)        /*去消隐*/% V0 M2 j7 n, I$ W
        writel(0x0, tpal_reg);                /*禁止temporary palette*/6 y" a8 m. q7 E# p! M+ q' a9 b6 `
    else {                                    /*消隐*/4 Q" T  a  i2 n& u& a$ e
        dprintk("setting TPAL to output 0x000000\n");& U# R) }, m4 Z& ]$ L" {) K  C% x  t
        writel(S3C2410_TPAL_EN, tpal_reg);    /*使能temporary palette,颜色为黑色*/. I8 \& N; S( r3 j
    }2 f. L; f# {, l: y" X* Q) e1 j

9 B2 p. r4 Y, N- D9 ~$ h1 e    return 0;
! Q- d/ ^/ H& e3 ^; `5 t( ^}% Z* e! ?/ |( U% y
在消隐时,屏幕将全黑。/ f2 L8 S) j3 \1 Y9 N8 k

9 H1 ?" r& K! @4 Z4 q4.3.3 s3c2410fb_setcolreg1 U; w. j4 e7 M$ O
该函数的功能用于设置LCD的调色板。调色板的概念请看我的转帖:LCD调色板。
/ Y1 D( ~4 w0 a. S& m+ l
8 n4 l+ O* u* J2 v5 y. \- Lstatic int s3c2410fb_setcolreg(unsigned regno,
! S5 q5 |: b& j6 a6 t/ \4 D                   unsigned red, unsigned green, unsigned blue,# l% C& v2 h; l
                   unsigned transp, struct fb_info *info)
( p7 q, P3 f) N3 K, d% N* N{
% C- i& D) Q) F    struct s3c2410fb_info *fbi = info->par;    /*par指向s3c2410fb_info*/
3 d2 J; E$ q5 f    void __iomem *regs = fbi->io;' w4 L. V3 U: _; o) a. P( H
    unsigned int val;
  G+ k* V# {0 Y, k7 q- |8 ]" b3 T, |* M3 B
    /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
8 _7 t0 T% o. \& {; {6 z) K# d$ \3 {           regno, red, green, blue); */
9 e) s0 D) b! S# ~" n  U7 S8 n2 E/ [) P) r( E  Q2 I
    switch (info->fix.visual) {   
) Y( b1 k4 G1 l    case FB_VISUAL_TRUECOLOR:    /*使用真彩色*/
# }$ ?" h5 ?& ?- ~1 a        /* true-colour, use pseudo-palette */
! |2 Y5 `1 b& s  [, X' _* z6 Q# i. B7 M% t, Q/ F. D
        if (regno < 16) {    /*16种颜色,为什么只用16种颜色???????????*/% C! q( _4 t: P7 z  A- ~2 i7 ~
            u32 *pal = info->pseudo_palette;5 Y& L# Y5 N. }' D
; \/ x4 x/ o% ^. `8 r0 w& G: d8 o
            val  = chan_to_field(red,   &info->var.red);* M. K1 Q6 e/ m$ b2 Y# j% d
            val |= chan_to_field(green, &info->var.green);: u% s5 N1 Y& I1 E: v2 I* N2 n* p
            val |= chan_to_field(blue,  &info->var.blue);' C! _; e" ?3 _9 C2 Y5 d: J
/ Q- I1 d3 r5 c6 ^& O% {; a
            pal[regno] = val;    /*保存颜色值*/" m: e1 |$ a* n: ]9 o, s; ^
        }
$ T6 ?- w9 @. U! ^4 A. e        break;
0 @+ P0 C6 B, D/ ^0 _) s- y3 g3 H3 |
    case FB_VISUAL_PSEUDOCOLOR:; X+ n2 O2 u( _% o
        if (regno < 256) {
/ r  ?/ x% a9 o7 m9 L: D3 k5 s            /* currently assume RGB 5-6-5 mode */9 ~2 R" ]5 u- k5 Z4 _! @% L# G$ q

1 ?, P+ N- D/ A, B, H            val  = (red   >>  0) & 0xf800;2 a( C6 ]0 S( f& \+ E* m0 t' ^
            val |= (green >>  5) & 0x07e0;
/ q% p3 [, V+ K- r$ c4 w, i) `            val |= (blue  >> 11) & 0x001f;
! g$ u; Z+ e; Z  k) m7 x! q- r0 R& A% H3 f* F  m7 O# v, u" _
            writel(val, regs + S3C2410_TFTPAL(regno));
9 d; Z9 n% {9 r/ y9 @3 @: {            schedule_palette_update(fbi, regno, val);5 Z: p- J" Z* S$ F! ^
        }$ d& Z$ E  b! k1 H1 [: ^" B, L

; f% H- N- b4 d0 c/ w        break;, d+ q- m% ?. ^5 r% k6 Z' ?. W

8 M% ]) r+ L# ^    default:+ d8 o9 d  n; t$ ~/ s; @
        return 1;    /* unknown type */! r. Q/ K2 N9 L, c- t/ ?) E6 ~# d; y
    }
6 p% q# q$ n1 m# h  P8 Z
0 V1 k$ \( H& S  t' h* O1 k6 ?- N    return 0;) E4 d# `+ p8 n7 o5 Z
}
' O& h  ^8 W* u7 ^8 j/* from pxafb.c */
) e  k4 e$ x; U) S5 T! B2 p7 }" k1 Y- `- sstatic inline unsigned int chan_to_field(unsigned int chan,
4 q- M, d$ W) _) y" V. y- t% A                     struct fb_bitfield *bf)! R# x0 V7 C6 k/ R6 E
{   
  a8 W/ u# m, z' f, H    /*下面用到的length和offset在s3c2410fb_check_var函数中设置*/0 x$ ~& y$ ~+ d- a0 b
    chan &= 0xffff;                /*取低16位*/3 u9 U  {$ n( r9 E( v
    chan >>= 16 - bf->length;   /*取第length为到16位为有效位*/
9 ~* O% i- X" @# L# v$ N/ h% q    return chan << bf->offset;    /*移动到相应的位置。*/
4 N! r2 I. n3 k& B5 L6 l3 F, j  Z8 x}
. k6 f) e+ t3 I# g% w我们使用的是真彩色,可以设置16种颜色,颜色的位域值通过调用chan_to_field获得,然后保存了颜色的值。但是比较奇怪的是,这里并没有将值保存到0x4d000400为起始的内存中,不知为何。 反而倒是在伪彩色模式下, 使用了writel(val, regs + S3C2410_TFTPAL(regno))写到内存中,然后调用schedule_palette_update函数来更新palette表。我们来看下。5 O8 _6 I9 Z7 ]) U: I* y% |( G6 g4 h

7 f( j7 K! Y& u0 M$ q$ a* hstatic void schedule_palette_update(struct s3c2410fb_info *fbi,
) [- L" e6 Y( u" g7 @                                    unsigned int regno, unsigned int val)
* X% c1 h" b6 [{
9 o' B* l( }# w3 T  L* s- b        unsigned long flags;9 ~8 H. A& x9 }% @" O1 C" W
        unsigned long irqen;9 B! N5 e; C. p3 B
        void __iomem *irq_base = fbi->irq_base;  D. Z5 |9 Y/ _/ f. E/ Q5 l5 l- v5 g

* \9 ~- [+ r( ]* f/ m- X        local_irq_save(flags);
3 Z4 f' L. f& _" X4 `( [5 i) P& D2 x9 R$ J1 M4 o1 m
        fbi->palette_buffer[regno] = val;
8 w& ~' N0 Q, x4 s: C8 m0 j8 x+ w5 ~4 B; `8 V: X" w2 A
        if (!fbi->palette_ready) {, X2 C* w- ]$ M9 g
                fbi->palette_ready = 1;" _9 X% h+ _5 I/ |( r3 P" U) W9 e

: m% ]$ d. l/ p0 l                /* enable IRQ */; _. [' Q) z& C6 O
                irqen = readl(irq_base + S3C24XX_LCDINTMSK);
! c; k* j1 I4 E# |5 A' l                irqen &= ~S3C2410_LCDINT_FRSYNC;
7 R- I3 E7 o# ]$ h                writel(irqen, irq_base + S3C24XX_LCDINTMSK);
  r1 Q" w$ s+ ^9 S1 p        }1 W) ], q1 a; [% d
  h# r' P2 S- ~* O! P& o1 J
        local_irq_restore(flags);0 S2 Y4 u- q/ c8 I# W6 ~$ f
}
6 a& d3 n$ j! C/ `这个函数的作用就是开启了LCD的帧同步中断,这个中断在VSYNC信号从无效变成有效时产生。当中断产生时,会调用在probe方法中注册的ISR。ISR如下:
5 R, s% ]1 g( ]3 j8 R. K7 i( J* nstatic irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
9 z2 D0 [& e. m  M9 f5 X1 S{, b" }& ?6 [- u
        struct s3c2410fb_info *fbi = dev_id;3 o1 |5 c5 E2 `* m: ]# ^: y% ?" g% b
        void __iomem *irq_base = fbi->irq_base;7 I1 ^1 N2 `# B2 |5 _) a( h& a
        unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);
: s% q; ], K8 h! s4 X  Pb) U* S1 ^; u# A9 f1 }1 M7 z$ j2 w, }
        if (lcdirq & S3C2410_LCDINT_FRSYNC) {
( x' L. z2 ]8 c. ?                if (fbi->palette_ready)2 w8 V3 d3 r. O
                        s3c2410fb_write_palette(fbi);
9 I; E( b$ l. q0 I2 C0 _8 ]# X3 r3 p' T0 x- b
                writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);% B# x- R; F1 H9 e% \) ], b
                writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);' V) J; U* a3 }6 _
        }
  O' s8 b9 d( ^: c3 k! h& @) @
! O+ S' y: W! ]        return IRQ_HANDLED;
& ~3 ]- g- f  ?}, t" X, l3 j+ p1 N* A: W
8 G- h6 S1 w9 B  u
static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)
! j& w7 f) c; E$ S8 \( h{
, D* m* p! p- K    unsigned int i;
" A) S" X! |. Z  K( O2 P1 f    void __iomem *regs = fbi->io;8 v5 k$ A$ J% d. c% Y% Q
2 q+ Y4 R/ i& d; I# [5 T
    fbi->palette_ready = 0;  /*清除ready标志*/4 x. s9 l5 `6 B5 p) a

, Z0 X2 z) }  U) ?' V) ^* B$ B    for (i = 0; i < 256; i++) {
8 o. G. o. ~( m" O/ C8 P. G        unsigned long ent = fbi->palette_buffer;* L+ q1 U/ ]# |+ o4 a
        if (ent == PALETTE_BUFF_CLEAR)
/ r$ \& _5 A2 \. m6 q            continue;
" z* C( I/ x, W3 y
3 P- ^. M9 W8 {, \        writel(ent, regs + S3C2410_TFTPAL(i));
$ T9 j1 U) T# }7 w  Y9 `# |
- _$ }( ?, [  c) D; i" |        /* it seems the only way to know exactly
  u) N! l$ B  {0 s- U         * if the palette wrote ok, is to check! _$ g& u2 a/ [! E' G0 b
         * to see if the value verifies ok* ~' s- w7 D2 p8 b9 c2 {3 }1 n
         */
2 E4 d, q+ V: y: |- z! |; i* Y6 M: u$ ~0 ?* m) p' Z
        if (readw(regs + S3C2410_TFTPAL(i)) == ent), t% x* N$ V8 z# w7 |# o# r, |
            fbi->palette_buffer = PALETTE_BUFF_CLEAR;; I# C4 W8 m, i. S  W' }
        else
, v" J- n2 J# _; K            fbi->palette_ready = 1;   /* retry */5 L, ]9 x4 T7 \
    }
* n$ J1 b& v- P3 G; k- [( ?! Z}9 s' h+ J8 Y0 o% }! K$ Q% Y
在中断函数中调用了s3c2410fb_write_palette,该函数对写入内存的颜色值进行检查。如果确认其已经写入,则将palette_buffer中的清除,否则retry。9 b3 N4 M4 K3 J: s0 i

( V9 Z) h" {# v5. 总结+ T) l2 @5 ~% \9 M9 A
本文对frambuffer子系统做了简单的介绍。frambuffer子系统的函数相当之多,在这里是不可能一一介绍的。本文首先介绍了主要的数据结构,
  m: o- m0 v! x. O& J( b7 r3 {8 F2 l$ E+ x- F$ y* c
随后分析了frambuffer核心层,在核心层简单的分析了5个API函数,接着,对驱动层做了介绍,驱动层的大多数函数都给出了分析。
; f2 g3 c8 Z% S1 ]6 L" ~1 G+ {7 K/ Q) r7 t9 C

( b# v/ |5 Z- r: n0 p( b3 V
( P8 X/ l/ {3 s3 N# ]  h: j6 p7 C& u2 g3 f# [! N# b+ x
8 H5 F' W) Q- [0 ^+ j) n
- a" S) Y3 P9 r& a3 R- L
: i7 ^, y9 i5 ]

1 d) I" y6 m3 F7 }

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-11-25 14:03 , Processed in 0.296875 second(s), 26 queries , Gzip On.

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

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

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