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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
本文将介绍Framebuffer子系统
' Q: F# Q; k% @- o4 k$ n( c, P. q; T- q
目标平台:TQ2440 CPU:s3c24400 X$ Y4 w, n6 |$ T. |6 ^" H
7 a/ c! n; s) i1 y
LCD设备:3.5英寸,分辨率320X240
0 a8 @5 g" R4 H2 K8 l% {' |: v& J
' V  ^- G) e! z5 M
) u5 I& [& G# `1 y) A' k- R" u$ G, V. E! h/ E- @1 A
1. 概述
+ r& P( c: v. P% ~+ k+ J# E8 {: }
Framebuffer,中文名字是帧缓冲,这个帧也就是一副图像所需要的数据。因此,帧缓冲其实就是LCD设备的驱动程序。Linux中,framebuffer子系统框架如下:% P& {) b4 C1 t
) N0 M. q# \: L8 H
$ P# c4 B+ j/ S4 K+ r3 D

0 |; [2 ?6 ]+ n' A核心层的代码以fbmem.c为主,核心层包括许多与具体硬件无关的代码,并且提供了API给用户空间。用户空间使用系统调用,系统调用会使用相应的API函数,最后会调用驱动层实现功能。对于不同的设备,驱动层的代码将有所不同。
/ A/ a3 |$ t& t
$ \, y5 Z/ u" t接下来的内容中,首先给出framerbuffer使用的数据结构;随后简单描述framerbuffer核心层;最后,针对S3C2440,对驱动代码进行分析。7 f% K# U& ]  ^2 ?2 X$ X$ j( A7 V6 U1 g
5 B4 D( B4 B3 R
2. 数据结构
+ O( }5 c0 l9 e- |9 U$ E" a2.1 fb_info 结构
; Y" {* O/ b: D+ |5 e& y) X: ~  该结构是内核用于描述一个特定framebuffer设备。其中包含了几个重要的结构,将在下面介绍。  A9 E/ a/ ?9 i$ @4 L6 U3 V+ l
2 t7 m: V5 g' }% {9 \1 Y
  下列代码位于include/linux/fb.h! q" G- R! X% p
) p/ a: C+ w$ n3 E
struct fb_info {
$ f, E4 Z0 A( o. L- }        int node;( y% s5 s( ^! Z( }7 M$ f7 J
        int flags;6 C! w8 A9 @/ i  w- d4 Y" Q& G
        struct mutex lock;                /* Lock for open/release/ioctl funcs */2 p% K; V8 ^& ]' ]
        struct fb_var_screeninfo var;        /* Current var */
9 [8 d/ E$ s+ B) z) Q( F6 c        struct fb_fix_screeninfo fix;        /* Current fix */
/ M- w' U* k; o0 m! K6 ?        struct fb_monspecs monspecs;        /* Current Monitor specs */
7 c" Y" s- @+ ?        struct work_struct queue;        /* Framebuffer event queue */
; K/ q9 s6 p" t5 D        struct fb_pixmap pixmap;        /* Image hardware mapper */
6 _* J4 c9 ~( d) z, y        struct fb_pixmap sprite;        /* Cursor hardware mapper */
1 e- }( C& n1 t& B+ c9 M8 |9 J        struct fb_cmap cmap;                /* Current cmap */( N/ R4 a; ^2 @6 {% R
        struct list_head modelist;      /* mode list */
- W2 [$ n4 E- b" `4 F5 s. z) C$ W        struct fb_videomode *mode;        /* current mode */
1 }$ S+ x) Y. W4 d
1 S8 w4 D* S/ q5 Q! U#ifdef CONFIG_FB_BACKLIGHT
1 Q+ o# T( M2 v9 f. G. }* J4 H        /* assigned backlight device */
: `( K$ m% ^; z4 {        /* set before framebuffer registration, % P9 ]$ ?1 r+ ?! f! [0 X" [
           remove after unregister */
, E! o( v$ \  s2 O        struct backlight_device *bl_dev;' q8 E% v# h* a1 Y" \
+ N. ]: v9 p6 n. G9 }4 j' s
        /* Backlight level curve */  D+ r6 Q4 E, N
        struct mutex bl_curve_mutex;       
' @6 [' ]( y! ~% ]        u8 bl_curve[FB_BACKLIGHT_LEVELS];
% E0 `$ M3 j8 ~  f1 N# \#endif
. _2 j. b( k& R#ifdef CONFIG_FB_DEFERRED_IO
7 A8 M7 E0 b9 P        struct delayed_work deferred_work;" }( r1 I* T8 H. q7 d" |' ~9 b
        struct fb_deferred_io *fbdefio;* l4 Z: T- h- B6 f6 ]. y2 B: b
#endif" V$ a9 s# R1 U, x4 J7 `% F" b

' f# o$ [8 k6 c# G( X        struct fb_ops *fbops;4 x8 o+ x1 @: b  }
        struct device *device;                /* This is the parent */
+ E- U% Y- m3 X5 f" F0 R8 m- \        struct device *dev;                /* This is this fb device */' E/ J; ^; w0 J& {+ ?$ y& [
        int class_flag;                    /* private sysfs flags */
$ ^$ _% q; ]; }- W( Q5 N#ifdef CONFIG_FB_TILEBLITTING
$ s: y2 \7 j* `* a. j9 n1 d" W        struct fb_tile_ops *tileops;    /* Tile Blitting */
! T! j# @5 q! P#endif: D( n, c4 C* L* ?* W% x" @
        char __iomem *screen_base;        /* Virtual address */$ D( M" z* @7 v
        unsigned long screen_size;        /* Amount of ioremapped VRAM or 0 */
0 A4 {2 K$ u- n& f+ C. a5 w        void *pseudo_palette;                /* Fake palette of 16 colors */
- A4 n$ Q' o7 f& z#define FBINFO_STATE_RUNNING        0
9 F% I  W  C( C$ X; O0 C' l& S#define FBINFO_STATE_SUSPENDED        1
5 j: Q. p, z% a; O        u32 state;                        /* Hardware state i.e suspend */2 y1 \: N# @1 a) {) `& K
        void *fbcon_par;                /* fbcon use-only private area */
- x' P# X4 H& X! ^        /* From here on everything is device dependent */6 F! X1 P: W5 N, O, g+ t! g5 _+ r0 c
        void *par;       
; l2 }) p9 c5 `' u! G- y};$ p, s' z5 m3 M9 m" Z
2.2 fb_fix_screeninfo结构
% }) Q# U9 u& P+ n  该数据结构是不可改变的,也就是说用户空间不能改变该结构中的任何成员,在核心层我们将会看到这点。- G7 Q8 R9 B7 E' j* P

; E1 w5 G. I" a; l2 i& Y7 x  下列代码位于include/linux/fb.h5 w% P" |3 _: f! u/ X' Q8 ~
6 I# T2 Y& |" [- J' W8 A
struct fb_fix_screeninfo {
: [/ a$ _) f6 m3 `/ D( J) p% E6 G        char id[16];                        /* identification string eg "TT Builtin" */1 h6 ]* i" j2 N8 I3 H
        unsigned long smem_start;        /* Start of frame buffer mem */
# V/ v5 o, c+ m' c! Q/ @3 h                                        /* (physical address) */
$ I0 O! [+ i# }* A4 o7 l. X        __u32 smem_len;                        /* Length of frame buffer mem */0 q, g5 x, B- ^" X7 x9 ^
        __u32 type;                        /* see FB_TYPE_*                */
) A0 Y1 S8 v$ k+ Q1 b4 ]        __u32 type_aux;                        /* Interleave for interleaved Planes */( i( x0 C. n5 O  n. f  K7 V
        __u32 visual;                        /* see FB_VISUAL_*                */ ; {! B  U( G# R% a  |0 R, p
        __u16 xpanstep;                        /* zero if no hardware panning  */. z! i7 M- h. [$ M
        __u16 ypanstep;                        /* zero if no hardware panning  */, v! C5 l+ g+ ^% b/ ^& x0 g# D
        __u16 ywrapstep;                /* zero if no hardware ywrap    */2 P( l2 `- r, g2 _; x( I
        __u32 line_length;                /* length of a line in bytes    */" t! X, i/ j8 D
        unsigned long mmio_start;        /* Start of Memory Mapped I/O   */* }3 R. ~2 Y; p2 q" C# ?+ j/ Q
                                        /* (physical address) */5 O5 i% |+ x; O/ x: K7 _. Y8 w
        __u32 mmio_len;                        /* Length of Memory Mapped I/O  */
8 ~% z8 G% x3 X. U        __u32 accel;                        /* Indicate to driver which        */
% }- k. V( a6 f! q                                        /*  specific chip/card we have        */3 V5 w! q7 ]7 R- O; r3 G. v
        __u16 reserved[3];                /* Reserved for future compatibility */, ^( A/ |' @9 N
};
+ \8 `1 C) R$ [5 u% O8 h0 f& H* [& V6 B2 O' f9 I4 g) l8 T" Y
2.3 fb_var_screeninfo结构
% f* Y2 x- R  Q. f( o  t  该数据结构是可以改变的,也就是说用户空间可以改变该结构中的成员。该数据结构中的很多成员就由板级信息复制而来,在驱动代码中我们将会看到这点。$ p6 o: A/ S+ |( o6 U

& g5 u$ |3 B  m. c  下列代码位于include/linux/fb.h
9 r1 m. c* R- V. D/ L
+ y" E4 O( f9 a* m0 Y2 `9 @struct fb_var_screeninfo {
' [. H5 a, n0 O8 ^( v        __u32 xres;                        /* visible resolution                */
' [- a4 P( b  u2 u. o        __u32 yres;7 ]  ~6 @/ t, k4 ~& x/ A9 P
        __u32 xres_virtual;                /* virtual resolution                */
9 M: \8 t$ e* j  B' }, K. t        __u32 yres_virtual;
8 _( E  w3 v  |4 _/ t+ C$ Q- D        __u32 xoffset;                        /* offset from virtual to visible */
2 W1 m/ B& Q# o! i( D" w        __u32 yoffset;                        /* resolution                        */2 F3 E9 C( n& |. j$ r
5 m8 g7 S, L2 P% M/ @% [) u0 D
        __u32 bits_per_pixel;                /* guess what                        */; ]- n$ _, Z- I; y& a+ S8 a: [
        __u32 grayscale;                /* != 0 Graylevels instead of colors */
% |, `$ S* I  \$ Q: x
5 M1 z7 A$ w- O/ ]* U4 Q7 Z        struct fb_bitfield red;                /* bitfield in fb mem if true color, */
! G4 ~( p! }% G# t- m        struct fb_bitfield green;        /* else only length is significant */
1 R8 Z$ ]8 x& v4 g% `5 g7 M8 B8 h+ m  u        struct fb_bitfield blue;" _& {9 p$ v" P/ _, W5 G
        struct fb_bitfield transp;        /* transparency                        */        " J- ?- P" F/ Z8 o( k
& i' p7 X, e. K9 _( y. h) @
        __u32 nonstd;                        /* != 0 Non standard pixel format */
# D! O$ J6 S7 e* g4 O# B
8 @% n6 H- N" P6 \        __u32 activate;                        /* see FB_ACTIVATE_*                */6 j1 M4 s4 _" w8 v# [6 W9 k
9 ?8 a% q9 V! ]; g" E9 {) x
        __u32 height;                        /* height of picture in mm    */
% {: Z8 C: l3 e4 M1 g( C        __u32 width;                        /* width of picture in mm     */3 K' @" P! J: \  G6 R4 Y
- ~" B8 C' w  B6 e- W% E8 n
        __u32 accel_flags;                /* (OBSOLETE) see fb_info.flags */
1 L+ K, K& d7 \1 B7 e
% A: l- b/ l6 a- ~! W8 E        /* Timing: All values in pixclocks, except pixclock (of course) */
$ F$ m" F6 c9 G        __u32 pixclock;                        /* pixel clock in ps (pico seconds) */
! q# J3 k9 _1 g/ N        __u32 left_margin;                /* time from sync to picture        */; q1 B; l1 G* H* z, h  C
        __u32 right_margin;                /* time from picture to sync        */
( x& j+ `. G3 }; ?  w( b! `        __u32 upper_margin;                /* time from sync to picture        */
7 ^, K; N& M7 w$ ^4 r; `3 @        __u32 lower_margin;$ ]  T( G# W2 d3 K# p  w( S
        __u32 hsync_len;                /* length of horizontal sync        */
( W9 ^" w5 i/ M" U- W* L3 M9 R        __u32 vsync_len;                /* length of vertical sync        */! {# K- g, G$ H/ t% E* p7 H
        __u32 sync;                        /* see FB_SYNC_*                */6 M  |& S3 }$ Z
        __u32 vmode;                        /* see FB_VMODE_*                */
6 g- I) c  m0 F' G, B0 N6 Q' B        __u32 rotate;                        /* angle we rotate counter clockwise */
- u& }  X! [4 g: N( u        __u32 reserved[5];                /* Reserved for future compatibility */
! ^- Z1 L9 o; j$ x};: r. B) t, V' }+ K6 W
9 y( P( l/ d  t0 [; a
2.4 fb_ops结构
' a: K- |, n# M  该结构描述了用于fb_info的方法,这些方法中有些是要驱动程序提供的,而有些可以使用内核提供的方法。
' h$ Y0 W! t+ e) e0 g
/ @1 \% S" A1 H7 V 下列代码位于include/linux/fb.h/ |& @" x- ?9 b9 \$ e

6 \% `5 L: T1 R2 A. O/*
! f1 a/ Z, }8 z * Frame buffer operations
; u7 U% S- l; w4 | *! u$ R% H2 e4 \& y
* LOCKING NOTE: those functions must _ALL_ be called with the console+ Y  j( S5 F2 s' C. @
* semaphore held, this is the only suitable locking mechanism we have' T! f$ \, E: v  J2 E
* in 2.6. Some may be called at interrupt time at this point though.! W9 v1 T$ [* d9 _2 H; m3 I" L
*/
. Q+ @! S6 m% b2 y, W, \
! p0 e6 a* d& c0 ?: ~' Ustruct fb_ops {0 X. C0 `% N2 N0 Y$ l. r
        /* open/release and usage marking */
* ]& W$ T. z+ }; d! n* x        struct module *owner;
, L' D8 V; S* n; d        int (*fb_open)(struct fb_info *info, int user);
' o; B  w5 {( n8 ~        int (*fb_release)(struct fb_info *info, int user);- L/ D1 Y* I1 U8 }/ b4 W

! O7 a2 Z, l6 ~& Y$ P        /* For framebuffers with strange non linear layouts or that do not2 e0 J; L$ N" S/ V, I0 r' F
         * work with normal memory mapped access
, {# Y" a# p) J* U& H         */
# {2 j- l; N7 Z! m1 {1 z( N        ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
% \2 x: s8 o# E) G, r6 t% n                           size_t count, loff_t *ppos);: E% g, o5 H+ J. h- `. k
        ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
6 N* X9 `. b0 D3 L* O                            size_t count, loff_t *ppos);
0 q9 [  X6 T' A; t/ y5 Y0 d8 ^7 M' u" A- O8 E$ W0 H
        /* checks var and eventually tweaks it to something supported,
6 w: @+ x5 L% s! r2 C, K         * DO NOT MODIFY PAR */. @9 d4 Z9 S; V' I' k: u7 R
        int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
$ T1 R, j# p- l! _
& `, p1 i: L. [/ E& t. a        /* set the video mode according to info->var */
, \# z, a5 {2 k0 U        int (*fb_set_par)(struct fb_info *info);  \5 O0 P6 ]' n) ~1 \( C7 W& R

; a5 ]0 s4 U/ a+ n" v; Y7 L2 S        /* set color register */
, z# f0 u0 a" P- k5 M9 y. @+ G        int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,; Y. N: g- v8 u4 S4 {* \  L
                            unsigned blue, unsigned transp, struct fb_info *info);
6 h9 B, Y& q. d! U4 Z, e+ M9 D) l" w) O9 c
        /* set color registers in batch */
* Q6 |; F& n2 l" t+ x6 i3 `        int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
. w! u* r2 x& E8 u! T8 L6 m- Y/ Y
2 K  ~, z- J2 C7 F6 v: ]# l$ a7 y        /* blank display */
7 s2 Z& [. J6 w0 A. O" H; x- {        int (*fb_blank)(int blank, struct fb_info *info);: T; t; q, p! F
' V$ S* k, \0 e4 C" d# q" m+ Y7 S
        /* pan display */
+ ^$ h7 L5 Z* M% C" v+ Z        int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);/ ^  ~2 V1 Q$ b, r
0 p/ E& e/ }6 f) v/ k8 P3 W
        /* Draws a rectangle */, ?7 r# H4 R2 [  F# T& B2 ^$ L
        void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
/ d. n# h. I& h) p$ S        /* Copy data from area to another */# O+ A! j: Y0 \
        void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
! b7 `9 F- w6 V        /* Draws a image to the display */
5 g; R  r+ Z1 s$ c  B& F        void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);/ ^/ ^! R4 j: v$ E8 \/ _$ }

+ V0 t. B/ A9 D5 \  c        /* Draws cursor */
0 k8 b% V1 E8 k& N% u- h5 F- l# E        int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);+ A5 q' s( S& n+ k  K
4 A. H7 ~: v, T$ K( a' ], _, a0 Z
        /* Rotates the display */
' a2 L1 j; Z0 e! ~6 d! G        void (*fb_rotate)(struct fb_info *info, int angle);
( u2 u) u" L2 f9 ^/ F/ Q, E1 y! a  G/ f
        /* wait for blit idle, optional */
. F' y9 I7 F, v" W        int (*fb_sync)(struct fb_info *info);% O* L' |8 n: r: I6 o
' R6 f% L7 |* s9 _6 y. h1 z0 E& \
        /* peRForm fb specific ioctl (optional) */8 Y: S& Q3 |7 Y8 Z7 w( T( I  U
        int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
2 S1 G, i4 y" B- y0 k                        unsigned long arg);
+ c6 R* E$ Z2 j$ {' r
1 V6 _# D  d2 N: O# g4 _) v' i        /* Handle 32bit compat ioctl (optional) */
' x/ ?! h# x& r% w! k! w        int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,, f$ T3 v- D; C6 I0 q0 r% D6 B
                        unsigned long arg);
4 t9 u1 O$ @" V+ V5 b; X% p5 }! D; @% n0 |; I1 V
        /* perform fb specific mmap */
% P/ F' G' z, G' V        int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
$ k1 M8 ^$ c8 @- D4 _% k0 D, u( G7 q. Y9 g! h# l) x! d" [
        /* save current hardware state */
- B$ ?6 z/ O7 B) L' K5 ^. p        void (*fb_save_state)(struct fb_info *info);
$ F$ F7 W" g" J5 W: U8 h. j6 `! P+ r! N; h* o. Z# o! u" ~
        /* restore saved state */
% Z+ a3 |# c6 M$ }  O        void (*fb_restore_state)(struct fb_info *info);
* w* c$ Q6 m- r( V+ f# ]" Q! U  z  ^
        /* get capability given var */- n! [$ V% \! R+ C8 h
        void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
' |" L5 e- [; x2 J' z3 D                            struct fb_var_screeninfo *var);3 ]; T: U, N$ @. ^3 X1 S% \
};7 p1 X5 u7 a5 X) Z8 {

( I: V# ~8 g  h
* z  D( ?9 c5 {: U- x3. frambuffer核心层
' S* `7 y" f  E- F* I9 @  K首先来看下frmaebuffer子系统的初始化函数。
; h' n# A/ o- S) i' ]: n: P3 s) v0 ?& n" b# W
3.1 fbmem_init和fbmem_exit( \$ {+ }' O+ X4 N5 F' S
下列代码位于drivers/video/fbmem.c
( D* D0 w$ t9 C% ?" Z; U+ X7 @
0 H+ }( V2 c1 Q% m/**, s* J+ m9 j  p7 d. ]4 X( m
*        fbmem_init - init frame buffer subsystem$ C  L" Q1 K$ d
*. K. Q9 B' Y" j6 y" `8 A
*        Initialize the frame buffer subsystem.
2 i& [; }5 g" R, C& l *
1 X4 [9 `+ g$ m0 S1 g( r/ n *        NOTE: This function is _only_ to be called by drivers/char/mem.c.
+ f8 H! `. N8 X6 {0 Z& k/ e, m9 E *
# y7 {' N1 O$ {: n/ d */2 Q7 j5 ^- T/ m6 r' E3 j2 R
3 a0 N9 ~( L5 o3 o4 z) N* s# L
static int __init
6 Q+ h6 m1 e& [. \7 c& Rfbmem_init(void)" r- \& p5 h! H" ]/ \$ t! R+ O/ D* L
{. C" V: ~  d; _+ i+ ~
        proc_create("fb", 0, NULL, &fb_proc_fops);/ t* x' @- t4 r6 A" t  Z
" X9 A' U. E( B; D/ U- J
        if (register_chrdev(FB_MAJOR,"fb",&fb_fops))                /*注册字符设备,major=29*/, ]" V8 J  V5 e7 m8 m3 t' {8 S
                printk("unable to get major %d for fb devs\n", FB_MAJOR);8 i  M5 C5 |1 d9 x1 A/ }7 _

: z# e% N) a# M- Y        fb_class = class_create(THIS_MODULE, "graphics");        /*创建类*/
: F# i. S6 N9 O1 K. \# M  n        if (IS_ERR(fb_class)) {  K7 z& _) O" l7 a# h% R
                printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
! S- l' w8 U1 \* P; {5 [                fb_class = NULL;
1 [; ]: s0 E$ e% ~        }3 i  o4 y" k# |" O
        return 0;
1 Q* Z5 {5 [; ]' x; V, a}2 w  S  x$ W! R2 \7 ]6 T; W1 D

' h9 z9 h! i! G6 E2 M0 A! m#ifdef MODULE* p; i9 M& D) Z" }; f; L
module_init(fbmem_init);4 \+ ]  m, H/ _. O1 E$ v8 s# z
static void __exit6 d. z: j  |- E5 N& U$ d; Q5 @5 d
fbmem_exit(void)  d! `" E4 y: H6 P" n
{
, P, M! I6 @, C8 r5 i        remove_proc_entry("fb", NULL);5 T1 k: `. c2 G1 A& d
        class_destroy(fb_class);
( L  a" m: ?" S& ^) X! ^  E        unregister_chrdev(FB_MAJOR, "fb");
3 b- J& _- C2 H0 v0 z# S8 |}; C/ g8 k) C1 E* l6 ^; [) v6 `

, s: G) J% |- e0 `! dmodule_exit(fbmem_exit);
5 }" i$ t2 m! j: ]8 r5 vMODULE_LICENSE("GPL");
0 |7 R. a6 z; w' _MODULE_DESCRIPTION("Framebuffer base");/ c3 K. O& l2 G. r* I9 y4 H; Z' Q
#else3 O9 t$ R) \1 f, |
subsys_initcall(fbmem_init);
/ b  Q/ C, n  N+ L3 C: b3 N#endif
) _$ Q/ a0 G9 G0 M
) Z: v1 T* b# g' b( p; V; K$ n9 x! Estatic const struct file_operations fb_fops = {: ?# A* i" i. |- L; R
    .owner =    THIS_MODULE,/ m# f3 W0 `4 G8 ~( o
    .read =        fb_read,# T9 t& S; I- h# `. c# C  L# H& B
    .write =    fb_write,
# L3 _7 n# Z7 C( _# ~* e    .unlocked_ioctl = fb_ioctl,
7 ~9 u. N% H' t#ifdef CONFIG_COMPAT! u0 g3 `3 ]" }8 H+ T, m- v
    .compat_ioctl = fb_compat_ioctl,$ B5 Z5 q: Y: d. h( ]" l
#endif: M. [1 P; p! v" ~) Q6 _2 @2 \
    .mmap =        fb_mmap,- ?" Z+ q! F# m/ z' [; F, @
    .open =        fb_open,
1 ~4 f- F( L, U# e& A: `8 T    .release =    fb_release,1 s  K' O3 g$ \
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
2 |+ O- R" g5 z, |5 t& M0 I0 h0 j    .get_unmapped_area = get_fb_unmapped_area,: |; s3 C+ @1 P
#endif
' r$ R7 X3 f2 I' w#ifdef CONFIG_FB_DEFERRED_IO# `9 N  K9 Q2 [" q* Q" t
    .fsync =    fb_deferred_io_fsync,
# H% _: `" K/ e% F#endif3 n* R& ~3 J4 v" B5 [, Y" [2 H
};  d; u/ {$ d: v- n& U+ c* f7 G
8 o# C. u( R% ^* p* o5 O9 T8 B" H* G
我们看到,如果不是作为模块,那么该初始化程序将在subsys_initcall阶段被调用。初始化时,仅仅注册了一个字符设备,并创建了一个类。通过字符设备,提供了API给用户空间,包open,release,write,read等。
* N& b) A( M' F8 R1 x. n/ a) e0 b  p+ z8 D随后我们看看如何分配一个fb_info结构。& V0 I' k& a* O( g; k

( {3 {5 _1 W2 f% x3.2 framebuffer_alloc
3 t4 _& _! @; W/ f0 P% f: g. ]下列代码位于drivers/video/fbmem.c
. e" f5 ]$ ^7 V# s8 @! P3 C' M* D" ~7 c8 l
/**
! R& @1 H' T, d8 K# }% C * framebuffer_alloc - creates a new frame buffer info structure) |3 \+ M: Q- C
*
" n1 J: h4 u" C * @size: size of driver private data, can be zero* ^! |+ r: y. {8 \( ?
* @dev: pointer to the device for this fb, this can be NULL4 N7 ^2 s3 ~; b# z2 p6 T  z
*8 ^# T5 ?& e: [. X3 b7 t9 Q
* Creates a new frame buffer info structure. Also reserves @size bytes
7 k/ J9 D' N5 w * for driver private data (info->par). info->par (if any) will be
  t1 d1 J! h8 n * aligned to sizeof(long).
! i* O3 w( @* [9 N% Z *
& U. u* K$ y" U * Returns the new structure, or NULL if an error occured.
' }" ~+ B- c; A/ x *& P8 T  D) E7 H; Q0 I( q' P; W0 D
*/) c. F8 r' K4 M. p
struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
/ X! f! c# ~$ t! ^" c* m$ c{0 t; O8 W/ R; B- |+ l% A
#define BYTES_PER_LONG (BITS_PER_LONG/8)/ B* U' I# ?6 m+ D2 r
#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))3 w4 a& e4 m7 m* c' z3 M( F0 s
        int fb_info_size = sizeof(struct fb_info);% s( u' ?, T+ a5 X9 D5 |4 l
        struct fb_info *info;6 B2 k' ^. W, d# ]% ?
        char *p;
# i' {( U/ ~9 Z/ t3 L) X
+ G5 W9 t+ V6 U" {2 m- @+ P4 j        if (size)
1 ?% W6 h! ?6 A' I9 u8 g. W                fb_info_size += PADDING;
( A" R) I! d* l) e3 n3 F; O& H6 a/ ?- k) X
        p = kzalloc(fb_info_size + size, GFP_KERNEL);
6 i" D3 {& i& v& [% z2 b" S, i5 H
, y5 r$ H$ ~2 [( H% O1 T. X5 _6 o& P* P        if (!p)5 d) ]* e6 ^* J/ m6 F, X" r
                return NULL;
! d( Z/ a7 g1 j1 C' d1 w% z: o4 E! ^% D8 S
        info = (struct fb_info *) p;
9 \6 j' R) F) m4 a0 Z9 }: H7 B" D6 u/ X2 C$ |+ l9 ~# m
        if (size)
2 W1 [. e. r$ U8 B2 v                info->par = p + fb_info_size;' X6 C* }8 n3 B6 z7 J3 p' M) V4 i

) L6 D( Q/ b- y$ I. k        info->device = dev;  M. y) G1 s+ k- Z7 w/ o
' G$ ]+ e/ W6 D8 t) X
#ifdef CONFIG_FB_BACKLIGHT% G7 d  e* R8 c
        mutex_init(&info->bl_curve_mutex);2 w% n; E, R1 m  y. B. d4 u; W: V
#endif7 j7 q+ X# K2 g5 g. S
* }; A4 h. {# J# H
        return info;
# B0 ]. H$ q3 g1 P# y, z#undef PADDING
4 H* A& d4 W7 S# E6 [#undef BYTES_PER_LONG. c% a5 b! G% Q7 x% h  p
}
. m& p  d) h3 J  d8 H9 UEXPORT_SYMBOL(framebuffer_alloc);/ @) a8 u1 O: s  U) G
在进行分配时,根据参数size的大小,分配了b_info_size + size的空间,然后让fb_info->par指向size的空间。因此par所指向的空间可视为设备特有的数据。4 z. v4 {. m  F6 n0 A+ M8 I
在分配了fb_info结构之后,需要将它注册到内核中。注册由register_framebuffer完成。我们来看下。
& J- b" I9 \; ^& X0 D! o
/ [. ~1 s* ]1 \/ |' K) c3.3 register_framebuffer! ]4 I- N) Z: r) N: M
下列代码位于drivers/video/fbmem.c
. o7 M" l' I( m5 u  ~7 q5 j8 v9 q) ]- Z3 E; X+ o4 o
/**( {2 l; G3 S  w; n9 \7 x
*        register_framebuffer - registers a frame buffer device
" s" d& I4 V( i *        @fb_info: frame buffer info structure
+ _! U: S: s: h6 g; l& R *0 I) q. d6 ~* z9 {( Z( |* ]5 c
*        Registers a frame buffer device @fb_info.
% _7 [9 b: v4 m3 R *; j( Q* a" g7 s& X& V4 n
*        Returns negative errno on error, or zero for success.
, Y4 X  |# A, O$ Y2 }6 L *% \" K" y# b, d. Q
*/' `$ G6 l6 D+ Z) n+ D
! V9 a- Z  G' x4 y2 F3 G) |) K4 o: @- v
int
1 N( m- j  A8 D* Hregister_framebuffer(struct fb_info *fb_info)6 H! _* `5 o$ a4 @3 S! v
{
2 Z8 i' z' P' b# h1 X/ o        int i;+ Q: T9 w: I4 R, p6 U3 }# x% F$ d* w2 ~
        struct fb_event event;! T- X# `, G4 }! N* s1 c& V
        struct fb_videomode mode;# W2 P: G; X3 p; k' s/ {" M

  O- K' f& s: T% ~- b        if (num_registered_fb == FB_MAX) /*最多32个FB*/% {2 F4 e7 Z: \/ q5 r
                return -ENXIO;
' f" Z% E# w$ U0 O7 g$ g* M0 K& J6 _8 q5 j5 G3 c
        if (fb_check_foreignness(fb_info))) ?6 m+ P* O2 D- z" \% |
                return -ENOSYS;
1 S0 f) [; G$ d- E: }1 j: _. c) t& o6 ]" U
        num_registered_fb++;                /*对注册的FB计数*/
5 \- i; T/ q' C: {        /*寻找第一个空位*/
- z6 r% x) l' `  y& d: m/ X/ B* H        for (i = 0 ; i < FB_MAX; i++)/*FB_MAX=32,也就是最多32个framebuffer*/6 u* L+ m2 ^2 x" h2 l8 W, c- j
                if (!registered_fb)        /*struct fb_info *registered_fb[FB_MAX]*/! N7 o; H; A; x1 j
                        break;
8 c, u# E4 W/ y) s+ Q$ X4 s( K        fb_info->node = i;
2 p1 X/ a/ I/ Y4 z( E8 ?        mutex_init(&fb_info->lock);        /*初始化互斥体*/$ j. g# T; `% _3 h3 ~% U4 {

1 P1 f3 `7 G3 U6 A& y% [        fb_info->dev = device_create(fb_class, fb_info->device,/*创建设备节点,节点名为fbx*/' p! v) Z. T' T% P0 H2 A
                                     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
2 P6 {/ x) N8 Q; u1 k        if (IS_ERR(fb_info->dev)) {7 |7 l" s- ]1 I! G! L3 N
                /* Not fatal */
; Z- v6 b3 S( y0 F                printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));. S" M+ x8 B) d9 b" H
                fb_info->dev = NULL;
* j+ v7 d& b* B" n+ K        } else) U! a) I1 H; D# G
                fb_init_device(fb_info);        /*初始化,在class/graphics/fbx/下创建设备属性*/; p( U- M; A5 J5 o. l7 a- `

. B) P; a. _0 H        if (fb_info->pixmap.addr == NULL) {) \+ _. H; S' z8 b0 i! x. z  A
                fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);/*分配内存,1024 * 8字节*/
. I* g' A; U9 ]2 j                if (fb_info->pixmap.addr) {
" G$ f2 b7 `, H9 q) H3 Q6 q                        fb_info->pixmap.size = FBPIXMAPSIZE;+ i+ s4 U" v- \% ]' M/ L) g' B
                        fb_info->pixmap.buf_align = 1;/ }/ q5 g' A$ q# Q
                        fb_info->pixmap.scan_align = 1;; W. N, q1 C6 |. G* m9 K2 ~# C
                        fb_info->pixmap.access_align = 32;6 }6 ~3 c9 p# ~. v
                        fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;: C& m% Y9 m, E8 b7 W4 z$ a
                }; M2 l$ i6 g( {) _
        }        8 g! _# a7 |$ c3 t
        fb_info->pixmap.offset = 0;
! O( ~# o' D0 P
0 K" b( ]5 g6 v/ W2 R        if (!fb_info->pixmap.blit_x)
; j' v$ ]( N- z# K2 O" I4 }+ a                fb_info->pixmap.blit_x = ~(u32)0;
3 H5 [* B1 `8 U
6 ?1 H$ d. f# F        if (!fb_info->pixmap.blit_y)0 y  @1 X1 v# o& p
                fb_info->pixmap.blit_y = ~(u32)0;
$ r# Y& M" ]' U8 }" i1 N! O9 S) H& m1 J
        if (!fb_info->modelist.prev || !fb_info->modelist.next)        /*该链表没有指向其他节点*/
8 Q) S" c6 S# w/ _8 y                INIT_LIST_HEAD(&fb_info->modelist);        /*初始化链表头*/
! S0 Y6 v. s) ^7 |0 G% V8 |+ j; `0 E( }4 ]8 a
        fb_var_to_videomode(&mode, &fb_info->var);/*转换fb_var_screeninfo成fb_videomode*/
& d: p9 }0 X  l: E1 R4 s        fb_add_videomode(&mode, &fb_info->modelist);/*添加mode至链表中*/9 B2 J+ b1 S" v2 z& K: o
        registered_fb = fb_info;$ q) k+ t" R* @8 e
7 ]8 v8 t2 M8 p# B+ L; O! h
        event.info = fb_info;
' O/ v* [4 K0 I' y: W: y4 Q        if (!lock_fb_info(fb_info))- a5 [1 k0 Y4 _' q2 g4 R% J9 Y: \+ l
                return -ENODEV;
4 I8 [" r: \0 P  u; t# G. q4 u        fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);/*???*/
" Y, E' X+ t9 G        unlock_fb_info(fb_info);
4 |6 F% V+ D! v0 r! ^+ Q9 `5 D0 D        return 0;  Z4 ]7 w: `( s# C* p6 t8 V' [
}
2 ^7 b! ?! A- l7 y
8 M3 B# X/ ^% q从这个函数我们可以看出,framebuffer子系统只支持32个设备。在创建了设备节点以后,建立设备属性节点,随后将fb_var_screeninfo转换成fb_videomode,最后添加fb_videomode至链表中。( n( O& u. I- l. D' s: Q$ E
我们看下其中调用的函数,首先是fb_init_device。0 y. a9 s! N4 X3 t; a
# f  m! h# d1 x; P# }7 [
下列代码位于drivers/video/fbsysfs.c8 c7 o3 T- q( D; r+ R4 i% G  ^

3 J2 m( W) ^' _% J, Bint fb_init_device(struct fb_info *fb_info)
  o  w% w: s, f4 ~: y{
8 Y# d' \: ~% X        int i, error = 0;
- j0 t/ n; {2 l1 S
  }* b+ O/ _" [' B( f- Q, l7 a        dev_set_drvdata(fb_info->dev, fb_info);: `, w( w  `3 l$ q
" `; y& ~' @1 s0 h$ Q1 Q) [
        fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;* F( o/ T. X$ w

, r1 s* Q8 g  M7 b7 ?. ^* u, h        /*建立设备属性*/
- C# l2 U$ I+ o% J        for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
4 \: s% S$ b. O# M! B" q                error = device_create_file(fb_info->dev, &device_attrs);
4 T  C1 [1 ^( [) m( a$ ^
/ L$ M2 a6 `' }$ l  O4 J& ~% a) l                if (error)
6 }6 V& {9 A/ J. x' b5 j                        break;
* x. X" h* p$ l7 z5 f* b        }9 T9 p# N' o' {: ~$ {

) y- T: M  A) N0 f  A        if (error) {' \. z( j6 @; w) W
                while (--i >= 0): b+ f" ^- F! j$ v- {* c6 G$ ?
                        device_remove_file(fb_info->dev, &device_attrs);
' T6 D7 Z% ~  T* a" T                fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;, {! H5 R! U# n
        }
5 B4 B+ D& R  R, n9 T+ c6 a, d& r& _6 Z) T, \
        return 0;5 m* Q% u4 h/ k
}
; e0 S/ k5 R8 S5 U* Q, o$ q
, l% [0 x% U6 K6 m/* When cmap is added back in it should be a binary attribute
  ?( H1 I! M6 e1 q7 @9 @ * not a text one. Consideration should also be given to converting+ Y6 d! e- a/ ~* b9 H
* fbdev to use configfs instead of sysfs */% Q0 y( z% _1 U! Y. d# {  S
static struct device_attribute device_attrs[] = {% {0 p7 i! q2 T1 ]; e) V
    __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
- U7 \# u/ p+ R* v% y; i    __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
; M: V  I1 A! }" P    __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),. L4 f) A4 t9 O% u% h2 e
    __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
& ~7 l- M' [% U4 z& Q    __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
  M! r- d- v" U- C; G. O    __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
: R+ Q7 X7 I3 N" L0 l9 \    __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),& }5 c- O8 e: h2 x6 \' p8 P* B! K" t# t
    __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),  z# F# d: O1 t, p4 ~9 {6 m3 r4 k
    __ATTR(name, S_IRUGO, show_name, NULL),
' n  z; H& V+ l3 o% J    __ATTR(stride, S_IRUGO, show_stride, NULL),& i9 p: s0 d5 ]$ ^$ P, u
    __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
1 f5 {& ^+ ?/ b4 S! c( P: _: w    __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
% @4 \' d" Y. M$ f( N0 i#ifdef CONFIG_FB_BACKLIGHT
1 a$ S6 P  N3 P    __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),; i1 o" K' }6 L  H
#endif
( A( U. n, ?; b/ `' Y" p: y( l};
2 e. n' G7 R, K! v4 o* o+ s* j8 a0 ~! X
我们可以在/sys/class/graphics/fb0下发现这些属性文件。
1 \# m7 ~/ ~7 i! o$ W  O8 s[root@yj423 fb0]#pwd/ U, [' j3 m! F' l, j$ p
/sys/class/graphics/fb0+ t, x7 o# N! x; M/ A/ H% C
[root@yj423 fb0]#ls+ P3 H4 j: Y$ {% \
bits_per_pixel  cursor          mode            pan             state           uevent$ L' L3 o  j4 f2 {1 a( {
blank           dev             modes           power           stride          virtual_size
0 r: z6 [3 K+ }3 b* C8 c9 qconsole         device          name            rotate          subsystem
/ q( S4 ~+ f& Z! J. a/ v- J$ w6 l  Z5 y: e6 X9 g; q) I" g
% ~+ y+ a" A& x0 F
接着看下fb_var_to_videomode和fb_add_videomode函数。
2 g3 c8 u. _! `' Y+ M+ o$ U
1 V' ^3 i6 _& f' y: A" W5 A1 ]下列代码位于drivers/video/modedb.c和drivers/video/fb.h/ y( s+ h* J& [  ?& _% {

, k& W! a$ H8 R4 lstruct fb_videomode {" B( X* L  P/ u
        const char *name;        /* optional */  S  S: g% h0 @  v; J
        u32 refresh;                /* optional */
, Y% j/ Z" u( K        u32 xres;  R. w7 l7 S$ r, w  C# O( B
        u32 yres;$ x  S3 w  h9 j7 @- U
        u32 pixclock;, z1 v. R6 u' t+ ^
        u32 left_margin;
% i% n& Y( T" m6 b3 X0 {        u32 right_margin;9 B4 R9 g8 ]: [
        u32 upper_margin;
! j% ]0 U% R* y% T8 M7 D# y        u32 lower_margin;
1 l, y( s) m+ @* X2 C2 c) i* W) u. Z        u32 hsync_len;' m1 l/ p; Y: w
        u32 vsync_len;
5 L: b5 g4 [2 _' S3 O7 s0 P$ {        u32 sync;8 a' H; o& d6 e' Q; y
        u32 vmode;9 a! _' e7 ~9 ]) A
        u32 flag;
' `. J0 [7 H4 c};$ ]! h6 Q5 j5 Y  j$ e

. \: ^" _3 w4 l1 h/**
+ C1 _' h  b$ I) m * fb_var_to_videomode - convert fb_var_screeninfo to fb_videomode
9 S8 T0 E# q1 o- Z& ^ * @mode: pointer to struct fb_videomode
( H8 d& K( [" x5 y * @var: pointer to struct fb_var_screeninfo- e. m9 D4 n8 E  \& h; C: b
*/
( h2 j5 }* N# M- |3 Bvoid fb_var_to_videomode(struct fb_videomode *mode,
0 A9 z" E+ t6 F             const struct fb_var_screeninfo *var)
/ l' y5 z8 ?% I0 g' ^* x{. |. F( M# d5 G2 B
    u32 pixclock, hfreq, htotal, vtotal;
1 c& ~; v6 N% r2 m7 d
0 \' x/ c  S, A; W5 z' w    mode->name = NULL;
8 p0 F/ f1 `  w6 g% Y* B    mode->xres = var->xres;8 m- z8 F7 L+ w/ ~- m% D% ?
    mode->yres = var->yres;
% t) U. i4 T0 ]  F    mode->pixclock = var->pixclock;
1 z+ a' k! n; J' Y4 i    mode->hsync_len = var->hsync_len;: Z( \( m0 J& f
    mode->vsync_len = var->vsync_len;% T; @# ~3 C6 x# |/ T
    mode->left_margin = var->left_margin;8 P* p& x9 Y3 J9 w
    mode->right_margin = var->right_margin;
/ f) W  {7 P# G: l    mode->upper_margin = var->upper_margin;
( u5 c- w6 d* e2 u- g- w    mode->lower_margin = var->lower_margin;( b: z. N, K( Z8 Y
    mode->sync = var->sync;. u( o% t! m! N9 z+ m" x4 L2 }
    mode->vmode = var->vmode & FB_VMODE_MASK;9 _! \- q; h7 F1 Y4 v# g
    mode->flag = FB_MODE_IS_FROM_VAR;8 @# c5 g, B7 \) Y( K( E4 u
    mode->refresh = 0;
3 P( X; m6 P2 S5 C- f
6 O, o: S$ A8 T1 f. h$ l' d' z0 k    if (!var->pixclock)
! D4 K& a8 L* W, ]. P6 f$ Z5 [        return;0 A8 G& q7 c$ q# T+ D% M1 p) J) f

# x2 r; }( A1 D* @4 ^) T    pixclock = PICOS2KHZ(var->pixclock) * 1000;
# V' v: E- m4 u! p9 @8 P9 N+ c$ N/ k% D& G* l
    htotal = var->xres + var->right_margin + var->hsync_len +# O/ ]  I- V: U& a+ z
        var->left_margin;8 ^8 d2 m& _. _) [$ [
    vtotal = var->yres + var->lower_margin + var->vsync_len +
, u! U: s% r; A0 Z* g2 X9 i        var->upper_margin;; J- J! w0 B, @6 |5 }: g
3 y( W* U+ i* I7 @, D
    if (var->vmode & FB_VMODE_INTERLACED)! h0 W+ \# H2 q- Y+ f  n; V6 e
        vtotal /= 2;
/ n2 J: ?! A# w- Y9 x0 ^+ t, M0 D$ h    if (var->vmode & FB_VMODE_DOUBLE)- f8 _8 C- p  o
        vtotal *= 2;: V1 m: M' \+ w. Q0 o

2 y  q9 C" w4 z" z    hfreq = pixclock/htotal;5 P0 S) r0 [- b! p$ V5 D! _
    mode->refresh = hfreq/vtotal;. n) k# `- @% T+ A
}, e& w; L; K4 {4 N
- q" K# E% e' t' U
/**! x( c$ C' e" w: e
* fb_add_videomode: adds videomode entry to modelist! q4 k8 Y4 ^5 n) m: Z
* @mode: videomode to add$ U3 q5 D6 ~3 X' {4 a
* @head: struct list_head of modelist: b! x3 U* y) o* _
*
: i+ ~3 X8 a% r  P: w * NOTES:7 N, n+ G4 @! P$ T, N1 j
* Will only add unmatched mode entries" M0 s6 e6 A9 k( [, f
*/4 Y  g+ U7 u+ h* ~' {2 \7 R  s# d
int fb_add_videomode(const struct fb_videomode *mode, struct list_head *head)
, P$ F9 ~5 g! d" G) i3 Y9 T{* S& a% X# r9 }7 _
    struct list_head *pos;
. y6 l+ Z  T; M2 V    struct fb_modelist *modelist;d
9 V" e! l0 t( d1 E    struct fb_videomode *m;
* \" |* n1 @; R6 u$ W' o    int found = 0;; C$ B0 M5 K& Y$ G/ y" ]
    /*遍历所有的fb_modelist,查找mode是否存在*/
- T9 K, _/ _# M( _- J    list_for_each(pos, head) {
+ Q( u2 h9 N3 o0 I. j$ |- x        modelist = list_entry(pos, struct fb_modelist, list);
% `- `# E- M/ o        m = &modelist->mode;! B- r3 I0 p% S& v
        if (fb_mode_is_equal(m, mode)) {    /*比较两个fb_videomode*/
* U& X! f9 o3 }) H+ g            found = 1;                        /*该fb_videomode已存在*/
4 ^! C; p* d; j3 A2 s            break;
; r) r  o! R7 T. N; @        }
) w- S& ]1 [3 `" L: p" Q, o  j5 u    }
# B& a3 B( \8 p" Y7 x6 Z    if (!found) {    /*不存在*/
7 H1 M# w) O" H        modelist = kmalloc(sizeof(struct fb_modelist),    /*分配fb_modelist*/
$ l& x8 A; Z/ W4 G                          GFP_KERNEL);+ @; W1 s/ ?% G# N* C
# W$ F/ r3 v8 D  Z( Y
        if (!modelist)( T$ W1 x8 t$ f3 s8 d  n( N
            return -ENOMEM;
+ O& m0 V! E4 ~        modelist->mode = *mode;            /*保存mode*/
' u( i0 w/ p& i0 t( F+ c        list_add(&modelist->list, head);/*添加mode至链表中*/
' o! p) }8 w9 t7 @0 _    }! C" P. ^' {' f0 w1 K) _- q
    return 0;
3 b9 H4 y( @. M, d% l+ q}% s3 {* k$ e' P# `  w* b6 M% N

9 U7 V; M  |- x1 S% ^/**
$ v; H; `/ e- Z * fb_mode_is_equal - compare 2 videomodes
# E, V' Z! j# C2 S4 z3 l* @: S * @mode1: first videomode6 k1 V( c3 X8 W* z
* @mode2: second videomode
: p' Z8 \) m' ? *: t$ a; ~( z- s+ W9 Q1 b/ k) ^( Z
* RETURNS:5 l% S& U) i$ e
* 1 if equal, 0 if not8 Q8 D( m& D! {: d. ?
*/
; Q4 V8 Q# i) l+ t5 r* iint fb_mode_is_equal(const struct fb_videomode *mode1,% H6 `! z0 b. R
             const struct fb_videomode *mode2)' K. n! V* n6 c
{* r4 D# p' h) T( }  A8 W
    return (mode1->xres         == mode2->xres &&! k: \( w* u7 g, Y
        mode1->yres         == mode2->yres &&2 @0 p) u2 Y0 `, Q: ]
        mode1->pixclock     == mode2->pixclock &&
; u* W9 l5 {1 V        mode1->hsync_len    == mode2->hsync_len &&; b" L) W0 g( w2 ^* e8 q
        mode1->vsync_len    == mode2->vsync_len &&
1 D% Q( T7 c2 M( z% _8 p        mode1->left_margin  == mode2->left_margin &&
6 q3 Z( f9 c/ k        mode1->right_margin == mode2->right_margin &&/ j4 ~( v( O& O2 a
        mode1->upper_margin == mode2->upper_margin &&
1 |* h9 s) u% [$ K$ j& D: b5 E        mode1->lower_margin == mode2->lower_margin &&& k$ T. s0 x4 T( J/ |0 ^5 {
        mode1->sync         == mode2->sync &&! U6 f8 T3 p. ~6 P: v. H
        mode1->vmode        == mode2->vmode);  {4 n' |- R/ m& [: O
}' T/ _- X8 |; }* j' _& f! P

" F! |/ G. w$ A
  M5 p: n* I( Y: z0 [, G2 ~% A6 rfb_var_to_videomode函数只是将fb_var_screeninfo结构转换成fb_videomode结构。而fb_add_videomode函数查找是否该fb_videomode已经存在,如果不存在则添加到列表中。- o# [, |9 C3 m
3.4 字符设备方法  ], h. N$ X% U
  在看过framebuffer子系统建立和注册过程后,我们看下framebuffer留给用户空间的API是怎样实现的。
, z$ B# _- `% I/ z7 n
! Q- q( ~) a3 ^9 M本小结只分析5个常用的方法,即open,release,read,write和ioctl。7 W% e( z, _% l4 |6 ^3 t
; H% h- y1 s: k) a. U5 @% e
  因为所有的方法和struct fb_ops定义的方法有紧密的联系,而该结构的定义由驱动程序给出,在这里我们提前看下在驱动中是如何定义的。
% K6 P3 R4 W4 u! Z7 I/ v  w: ^  N: N+ F5 E
下列代码位于drivers/video/s3c2410fb..c
2 o+ _& |0 @2 R+ N. `* R/ S8 p3 t+ l
static struct fb_ops s3c2410fb_ops = {
# q9 l$ d0 l3 }! F$ P) Q3 q6 ^8 \        .owner                = THIS_MODULE,
" [3 q- [: A2 {, r- [        .fb_check_var        = s3c2410fb_check_var,                        /*检查变量的合法性*/
  q3 I, a5 i0 d* F/ I        .fb_set_par        = s3c2410fb_set_par,                        /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/
8 X$ M! X2 C/ |+ y9 F8 j9 t" d9 K        .fb_blank        = s3c2410fb_blank,                        /*该方法支持显示消隐和去消隐*/
0 X) C' m6 c$ o$ n3 a& J9 p) X# @& n        .fb_setcolreg        = s3c2410fb_setcolreg,                  /*设置颜色寄存器*/
+ E8 ~, F& c/ d( `7 Z        .fb_fillrect        = cfb_fillrect,                                /*用像素行填充矩形框,通用库函数*/4 J7 ^0 X# S) d% c! [  ^
        .fb_copyarea        = cfb_copyarea,                                /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/
8 E- D2 L3 m- {. |        .fb_imageblit        = cfb_imageblit,                        /*显示一副图像,通用库函数*/
* d, a, u2 @9 f" \};# X. C  o& s2 r: n3 H
最下面的三个方法使用的是内核提供的库函数,而上面4个则是由驱动提供。* O* b! N1 H/ X8 r" S4 V% n
3.4.1 open方法* F! m) r% Z0 y' F
下列代码位于drivers/video/fbmem.c
! Z( }! l0 f$ }6 a* q
# _3 w9 ~1 g6 J3 }1 n, }5 j4 v- qstatic int3 r; L' j6 w# o4 H9 s& ^) J
fb_open(struct inode *inode, struct file *file)
6 i& L0 Q# a5 T$ ?+ M__acquires(&info->lock)
- o! Y4 Z% V6 [__releases(&info->lock)
* J0 K! T8 M4 f" f{* d; E# I! v3 b1 X5 u
        int fbidx = iminor(inode);+ J' R# P4 z4 z+ ]3 u4 u% j) ^
        struct fb_info *info;
* W: k& \4 d$ c" F' X( `        int res = 0;
0 i6 F. b, f8 R# u6 A
3 o8 n' L" ]' ?        if (fbidx >= FB_MAX)  i3 D0 l5 J; A$ u; V: r* F8 N
                return -ENODEV;
0 B6 p% R/ s. y2 ~& \        info = registered_fb[fbidx];                /*在register_framebuffer函数中已经设置了元素*/
8 S! ?; ?6 {1 C6 a- A" _3 S/ y        if (!info)
0 M" @/ \7 O3 a' N                request_module("fb%d", fbidx);        /*加载模块,这里不加载*/
8 l0 n( R, d! f! b2 m7 F        info = registered_fb[fbidx];6 R# K3 R1 P' U; s0 M% u+ u# v
        if (!info)" @' s8 F* f& B8 D8 t5 Y* d
                return -ENODEV;
0 R# a- n" a6 N% F; a        mutex_lock(&info->lock);                        /*加锁互斥体*/
. p' b6 Z6 w3 l& F        if (!try_module_get(info->fbops->owner)) {        /*增加模块引用计数*/% b0 k4 D/ o/ d5 ?3 L6 W
                res = -ENODEV;) y, [* k# w5 z4 H" e
                goto out;3 z8 I1 C( U- Y5 ]% Q# G/ G* N* A8 G
        }7 J. `) D+ k. o; d
        file->private_data = info;                /*保存info*/
! n0 d$ ?  v& v/ n& v7 ^" `        if (info->fbops->fb_open) {                /*这里fb_open方法为空*/0 `( f% Z# b: \* i0 F
                res = info->fbops->fb_open(info,1);
2 N" J1 }" F1 U6 F! D' @                if (res)
# _, r: y* Z- }/ X$ N" p                        module_put(info->fbops->owner);
( z  Z4 K: ~. k* \8 f% Z        }
$ y% f/ s# O1 Q' J0 u, a# L#ifdef CONFIG_FB_DEFERRED_IO
, x$ [2 T6 j" H" l        if (info->fbdefio)
" p, K& R5 I& V5 H4 G% i2 M9 y                fb_deferred_io_open(info, inode, file);  C* y$ Z& o! J1 l, H- G; y
#endif
1 M8 O6 {( E" Cout:
4 J" k  ?1 E6 _' P2 q& u$ {$ @( V        mutex_unlock(&info->lock);                /*解锁互斥体*/# V; o" _' x& I' s5 D- o
        return res;
: O$ R9 ?6 N. D) b& _}+ k0 V, a+ k* f5 \% q0 J; N' @4 A* ^  p+ A
主要的一个工作就是增加模块引用计数。还有,程序会判断是否fb_open在驱动中给出,如果有则调用该方法。我们已经知道fb_open没有给出。
  @& r: x9 Z7 s' z$ v7 b8 V3.4.2 release方法5 b' u" |/ e- E1 |
下列代码位于drivers/video/fbmem.c8 S2 O. |5 \# g# x$ T3 w5 F
8 R& I3 W- m) f) K
static int
! d: s+ H  F, e: ?' bfb_release(struct inode *inode, struct file *file)
8 P" j: F$ Y& I__acquires(&info->lock)
& I/ J  k, B! e. Z8 q1 c$ j. ]__releases(&info->lock), t$ V2 N+ n8 t  L) G
{' q# K5 g* K$ u
        struct fb_info * const info = file->private_data;
, _& U' ]* ?( S- T7 L
5 ]2 [6 i3 m; D( _2 y2 ]) e7 G        mutex_lock(&info->lock);
4 p, S: Y+ W; C! @" N/ ?        if (info->fbops->fb_release)        /*这里fb_release为空*/
( @8 u0 ~6 g4 Q* V( D                info->fbops->fb_release(info,1);
9 M0 _. n2 \6 J# r        module_put(info->fbops->owner);        /*减少模块引用计数*/
, k$ m; q! }' g' G3 G        mutex_unlock(&info->lock);; e/ U0 Z3 e: [% j0 `) P
        return 0;6 p" ?( p; K" ?6 f: n4 c' ^+ O
}; g; F) U0 J; U% W) N
和open相反,减少模块引用计数。: T% N! p! ^( c5 w$ x2 m
4 C. O' e! [% h; t2 u- {, V# n
3.4.3 write方法6 s+ J( b  O! W
  通过调用该方法,LCD将显示画面。
. H2 P( F! B" |, b) \% X6 b4 L% y6 D+ \1 u# A- f# }
  下列代码位于drivers/video/fbmem.c9 t& F) J- N6 I4 `7 `$ k
# w4 y4 a6 Z2 v% \, F6 I
static ssize_t8 Q4 a" W9 \2 g9 ?6 g8 H1 b
fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos): X  g5 O% n  Q/ l/ k$ [& e; Q
{. n+ f- i& `  g7 h3 t
    unsigned long p = *ppos;
# o5 c, J% k' F    struct inode *inode = file->f_path.dentry->d_inode;
% g$ @" @. g" N9 g+ |    int fbidx = iminor(inode);
# o( R' {$ z% i: }& p+ z    struct fb_info *info = registered_fb[fbidx];$ S' }! Q, B" c( z/ }$ ~& R" Q0 H
    u32 *buffer, *src;
- Y# n" r5 K2 X' G: V' b1 u    u32 __iomem *dst;% j) }( M3 p9 q8 L& }0 e( B
    int c, i, cnt = 0, err = 0;
( E. N6 q$ A4 Q& Y6 z: Q( T4 {    unsigned long total_size;
% w; @% y4 G. ]* B6 L
) H0 o. z' z5 w6 B9 w    if (!info || !info->screen_base)    /*screen_base在驱动中给出*/! ?- @% [. q, |7 W1 S4 n! I
        return -ENODEV;
% P* m" R7 ^# S
3 N; f$ [% ]6 c+ L    if (info->state != FBINFO_STATE_RUNNING)  S8 {5 `1 e5 b& n% |2 l8 R
        return -EPERM;
) ~3 y% ~% c" i
2 c' u' s& Y$ q0 F( w' e    if (info->fbops->fb_write)    /*没有fb_write方法*/
/ P& K. A6 F/ i        return info->fbops->fb_write(info, buf, count, ppos);
' B: g& B4 Y" T$ Y5 _1 b2 x) P
" w6 {  G5 j0 C* J2 k    total_size = info->screen_size;    /*screen_size没有给出*/: Y$ @1 X7 L- ~( c6 M
' W6 r" _  Z& P* e! h
    if (total_size == 0)
" x/ ?# [5 r5 M2 j        total_size = info->fix.smem_len;/*153600字节,驱动probe方法中计算*/
2 l5 m4 a: F8 `: X' v  u7 a- g7 c
6 b0 {+ l7 i  `: S# b' b+ f# m    if (p > total_size)
7 w8 j3 K: s6 w, e4 c) ?        return -EFBIG;, _* Z( Q1 i+ M# g4 ~; L, B) S

, Y# a: M; }, B: d3 P6 ^    if (count > total_size) {    /*要写入的字节数大于153600*/
3 ^% ~1 o- L' F- K$ @- l        err = -EFBIG;        /*file too big*/7 e* L3 n7 ~7 c2 v$ q6 j) p1 `# X, U
        count = total_size;+ B0 S4 v- e, Q- O  R
    }
# K8 i6 _! F4 |+ p: l0 |; R; \
: d/ }5 D% `# p* G+ K; V    if (count + p > total_size) {/*偏移量加上字节数超出了缓冲区*/
- m$ K$ V9 y) C7 x7 @  Z0 `        if (!err)2 i2 q+ h5 E6 V  U
            err = -ENOSPC;0 {2 D; v0 Q, m; k( f. u

2 Z2 e7 L3 f: ~/ }" c        count = total_size - p;
  X1 V# [: u' H1 R7 j    }
( c- l) n7 W; q
2 @5 P2 T3 J! t; [4 {    /*分配buffer,GFP_KERNEL*/6 p8 d5 H: |# e! e
    buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
0 U$ m, _' |( {) V             GFP_KERNEL);                /' Q$ U& M6 Z2 [. U' q4 f
    if (!buffer)
) j+ n% T; S  I) s        return -ENOMEM;
7 ?' Z. C8 w# A' {( s, h. I. q( W; J+ U7 x5 v
    dst = (u32 __iomem *) (info->screen_base + p);/*修改目的指针*/" q: Z, c5 [* m! [
$ K. J2 [, P% s" V8 b/ u' Q' Z/ W
    if (info->fbops->fb_sync)    /*没有定义fb_sync*/
6 W' W: c; M0 H) Y) _4 r! Y        info->fbops->fb_sync(info);1 r5 j1 U" A0 p3 V

* s, `4 W7 p9 m# \* O& r5 h    while (count) {7 H6 Y  ?) f. w" U) c5 v  R" G
        c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
' n3 G1 T, M4 }) m, y2 E        src = buffer;+ _& {: l& v- G% |, D
) w* F' q1 ~7 e0 n0 p% m
        /*从buf(用户空间)拷贝数据到src中,一开始为1页,最后为count字节*/& N& E8 @* K  l4 U/ O! W; u8 T" e- h
        if (copy_from_user(src, buf, c)) {   
2 w& l1 u1 q5 j' V5 E: P! A            err = -EFAULT;
; O4 f( }0 K+ x            break;+ f: g  @  ^- W3 n
        }4 ?* N' ~8 {  u" B
        /*一次for循环,写入4个字节数据到dst处*/) p) x/ i: f% p0 Z
        for (i = c >> 2; i--; )* y( e: ]: v5 A& H! [# N
            fb_writel(*src++, dst++);
8 r0 ~( c0 A. c; q  j3 _        /*最后还有3个,2个或者1个字节*/
3 G* L. O- y1 Z( q/ S1 w! q0 H        if (c & 3) {
6 S$ O% A5 r  |  ^" f6 i  ?            u8 *src8 = (u8 *) src;5 N1 B  i+ R7 M& ]: G1 a
            u8 __iomem *dst8 = (u8 __iomem *) dst;
8 n: P) x  Q9 g, \$ M- i5 k2 U            /*一次写入一个字节*/! o& }, x8 F/ Z1 j0 s% H" X
            for (i = c & 3; i--; )+ _" E: k0 T8 R
                fb_writeb(*src8++, dst8++);* P2 P" A6 B$ E, j

$ E. l, H: Z8 @* ?; [- U; ^9 T            dst = (u32 __iomem *) dst8;
* [7 E* C9 k; l        }2 h# j0 |: u3 r8 B# O' T
3 M% q5 t) Q2 L' S% n
        *ppos += c;    /*用户空间偏移量增加*/7 ^# H% e: Z4 v7 c- q
        buf += c;    /*用户空间指针增加*/$ i0 Y( h0 _0 F* @# R% |7 C+ o
        cnt += c;    /*修改已发送字节数*/# `* n6 |7 E# O. \
        count -= c;    /*减去1页*/" k/ ~( g( @) `' U9 f
    }% }. Q8 P+ T& @( e( Z% _

# L' n- s/ f7 Y    kfree(buffer);    /*释放buffer*/3 p# ~7 S) i5 W, B* ]
( ^7 Z% _7 o8 ?
    return (cnt) ? cnt : err;
' v7 R3 Q9 Y: E}7 c6 H. V, y% j) a( g$ L. M" s' l; F
4 f) w  h" h* w/ h# r: e4 v
这里,做了一系列的检查之后,开始拷贝数据。这里一个有三个buffer,一个是用户空间提供的buf,一个是在这里新开辟的buffer,还有就是驱动层提供的screen_base。
) F, D. T& i& E5 L" {数据流如下:
! l. z! d" e8 r( a1 |
- D( _+ i! s: n. z - Z6 M. q5 E$ {" R# K) f- Q7 b

; A& s! q* B% J$ X! I! a用户空间的数据首先被复制到buffer中,然后从buffer中复制到screen_base中,最后被映射到LCD上,LCD就显示响应的画面了。: x2 D! q0 o/ |/ ^/ P. u
6 B9 @$ {5 ?! n9 ~5 z9 s
3.4.4 read方法
# d3 `$ U% \, K% y0 _  Z. A该方法用于读取屏幕画面的数据。. l) n3 A, I, w9 g3 @; T

5 N$ B9 [8 E  }9 r. B, ]; D8 ^read和write类似,只是数据流是反响的,就不多做介绍了。" X; s- C' l% J, F
+ W! V2 p! D1 B+ [( N# v: H- M
下列代码位于drivers/video/fbmem.c
  H1 w% c+ r# C1 x
3 j( \/ n* T  F. j8 `9 kstatic ssize_t; @, W, v  R( d' g
fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos), X. Z0 p6 f9 r) A: [$ C. {. j& ]
{
" s( y/ o% N6 g3 K5 {        unsigned long p = *ppos;
: F! E1 }! {7 R7 i6 F2 O' p7 l" |7 a        struct inode *inode = file->f_path.dentry->d_inode;& R! h3 u, |6 K3 p1 @
        int fbidx = iminor(inode);+ ?6 ]; P' u5 i0 \9 `
        struct fb_info *info = registered_fb[fbidx];& a! D  T1 |2 c1 p& M9 P9 o
        u32 *buffer, *dst;% }, ?1 r  r/ l7 D
        u32 __iomem *src;
+ k: [5 l  h' l' {$ o+ n        int c, i, cnt = 0, err = 0;
- f' _& ^5 S! i" x: |        unsigned long total_size;$ a$ F( O7 J3 g! }1 P# u9 R, y

. ]8 y" I2 z9 B% n: f7 Z        if (!info || ! info->screen_base)
1 U: f; N! L6 o. h" E2 N                return -ENODEV;
. R- Q& V! `& D; e7 Y9 @0 D# Q2 @# j' I# ~) b
        if (info->state != FBINFO_STATE_RUNNING)
, F$ L+ z' p: M4 V4 Y                return -EPERM;
9 J6 E' d9 r( q% [9 F& Q9 h/ x  Y, w5 Q! X
        if (info->fbops->fb_read)        /*没有定义fb_read*/$ Q2 l$ C: f6 H/ h% k/ F9 y
                return info->fbops->fb_read(info, buf, count, ppos);5 F* F" b6 }% d% ^
       
! v+ c- C( i3 A  _, I# ?        total_size = info->screen_size;
, i" E8 v+ G9 ^/ \
, \! S# G1 d. @& p$ k9 K* y7 m# J  m        if (total_size == 0)
2 A1 ~3 v" f; F                total_size = info->fix.smem_len;
3 X0 y3 M5 h( J. W" g0 K
# v* ?/ }# i0 o9 |        if (p >= total_size)" D" l9 ?( e: Y) _
                return 0;( N4 r7 ]1 q. E2 f( H* L
. M; u# ^$ j) s4 V+ C
        if (count >= total_size)0 x3 j$ M# r$ w5 I9 w2 n
                count = total_size;
' W  E) K1 M7 U7 J+ ~: c! b& p
9 F! N& L0 m) H0 p        if (count + p > total_size)1 h" A, S  y2 d5 R1 `: x# r
                count = total_size - p;
5 T% X  h: {' r1 |4 G5 S& H9 h8 ^; o3 q9 M& |2 F
        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
* _+ W2 F1 @" F  e                         GFP_KERNEL);
7 f8 z  m5 w, p6 Q9 A4 ~% Y        if (!buffer)
' r6 t3 |' I- R1 M                return -ENOMEM;
& i( b1 u8 M' E- T5 N4 s
: ^  j* f4 B2 B2 e9 C( w2 y        src = (u32 __iomem *) (info->screen_base + p);
5 `* d) k7 Y0 I' }3 a
" p% c+ X, ?8 ]% t* X6 i        if (info->fbops->fb_sync)
9 ^& `# Z: g+ T: A, N6 G" i                info->fbops->fb_sync(info);/*没有定义fb_sync*/
  k4 R. q! r+ d2 y- c$ y+ Y0 X( Y4 _0 p1 p- `
        while (count) {* O, o. V  o4 l& P0 c% k% h
                c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;* L$ w6 W) r' c9 V3 I- d
                dst = buffer;  M9 E* g$ _0 V& W) m& |
                for (i = c >> 2; i--; )7 k( r; d& a2 Z% ^  e! Y
                        *dst++ = fb_readl(src++);2 [& a( [# m9 n% s+ g# y
                if (c & 3) {
- y6 N7 O9 I3 j! E                        u8 *dst8 = (u8 *) dst;8 F0 B; `/ z" D  e/ e; }3 x
                        u8 __iomem *src8 = (u8 __iomem *) src;! f( |1 U) S% |3 Q! e; o8 r9 T
& J/ R* k( i$ d! {# y7 I
                        for (i = c & 3; i--;)
+ Y, w3 O' a5 b) T$ [; Z                                *dst8++ = fb_readb(src8++);
# `2 T2 L7 u4 U5 H, n' H
: `% }: `' ^/ F                        src = (u32 __iomem *) src8;; W8 }! N2 L; G1 }4 O! `6 P
                }! x2 e0 I6 B3 j9 d" `- X
) o- g: U0 U) ^6 J5 K) i, S
                if (copy_to_user(buf, buffer, c)) {# ~4 y% g/ e5 y/ |: _+ a# H
                        err = -EFAULT;
- O$ u7 F8 C' w: j# @3 [% p                        break;+ ?) i- W1 e7 H% }, S  [9 w. x
                }9 c/ }+ h$ V6 j
                *ppos += c;8 S8 D* ~9 t: R8 F
                buf += c;
# R1 t. @6 c) T& k9 \- ?) w                cnt += c;
% {( ?! J. R" C7 s4 R- U- Y/ F) I                count -= c;* ]) Y. i; i' }  l1 Y9 r+ `
        }5 q3 e" s; j' M7 m1 ]1 q
/ g' c& `8 v) u" U' s2 b8 j; {
        kfree(buffer);
  P% l- J( _, {, I. z0 u  A" v: ~1 i; x7 L2 Y
        return (err) ? err : cnt;& v( L0 p9 P) l& S9 b, m4 N9 u
}: }& a% \- b4 I- i  K
3.4.5 ioctl方法4 D- Y- E3 B5 x# {' _+ h% g* b1 G
这里只是简单的看下ioctl方法,这个函数调用很多其他的函数,详细的请自己看吧。, W" W) g/ @! E* d" k

7 u/ e* H" ~' R下列代码位于drivers/video/fbmem.c
* ~/ M! O3 B, a/ w6 x3 W1 T* G7 r  a2 |; _7 c/ W$ T3 W
static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
) y8 L. [3 |- a+ M6 ?- E/ U{
. N. v) }8 ~# P# z        /*获取inode,再获取对应的fb_info*/3 ^) Q2 x8 x8 V+ t, F3 ^
        struct inode *inode = file->f_path.dentry->d_inode;
6 \  d8 L) N+ |/ R, G3 ]9 r! A' H        int fbidx = iminor(inode);        . U8 L7 L5 y3 s1 p& b
        struct fb_info *info = registered_fb[fbidx];2 N3 ]- z( v) k* p% L
6 j% v  t- O) }1 N; C
        return do_fb_ioctl(info, cmd, arg);
" x& X/ x- Y9 r2 r& ^3 ~3 f}" e3 D( [- g. |7 [) R2 g+ E

* D8 k9 [+ \4 g( f* h; @" [, `2 rstatic long do_fb_ioctl(struct fb_info *info, unsigned int cmd,7 v: S7 S. V! }1 z% I
            unsigned long arg)
* y4 a! j1 s, t* I  ~. q{+ h5 s; b' `$ L$ k
    struct fb_ops *fb;
1 H# G. i0 _' i+ N1 U. ^' r! ^    struct fb_var_screeninfo var;
8 ~  H* X( h* x8 a# _. C' ^    struct fb_fix_screeninfo fix;
. ]  N( b, V+ r9 O9 A# q    struct fb_con2fbmap con2fb;
5 w8 h5 f6 X+ ?3 e* F    struct fb_cmap cmap_from;
% Q  @4 }1 `1 g7 X' g$ r    struct fb_cmap_user cmap;. P: q8 M' n+ [2 Q! D/ t8 n3 h' A
    struct fb_event event;8 \$ U: J- a) M9 j! c6 S
    void __user *argp = (void __user *)arg;
2 n9 p7 X$ U4 o: X( w9 k    long ret = 0;0 A4 j* o* `& R3 w

+ ?- m3 e) |: J    switch (cmd) {
( N: }0 k. g( S8 O3 d, c. `- \    /*获取fb_var_screeninfo*/
: L% t0 i( V2 a, f. L' H" _    case FBIOGET_VSCREENINFO:   
& |6 f' U5 _( P+ Z        if (!lock_fb_info(info))    /*加锁互斥体info->lock*/
1 i1 e3 u) ^0 l9 {- N* Z            return -ENODEV;
/ v; s5 z) I% J9 y4 A4 v! Q& @        var = info->var;            /*复制var*/
$ w7 d% X- J5 @" [- m) N        unlock_fb_info(info);
* j1 ^" E" G: E
  l5 v& b( X- B        ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;    /*复制var到用户空间*/7 r- n9 J$ U1 p9 T
        break;" d3 U0 m6 y$ e( c* p
    /*设置fb_var_screeninfo*/, `" c5 J/ ?! p6 ?, |0 B9 ^5 N
    case FBIOPUT_VSCREENINFO:
- K7 _  h" }( x7 P% ?        if (copy_from_user(&var, argp, sizeof(var)))    /*从用户空间获取var*/7 \. G( g9 }3 ^" ~/ u, J) {
            return -EFAULT;: x& S% D1 Z' W% c* r: F! E: O
        if (!lock_fb_info(info)), g" K" f" c* N% M
            return -ENODEV;
% x& M- o0 G  m0 o! G! e        acquire_console_sem();
% ^4 y6 L* e& V" e, J        info->flags |= FBINFO_MISC_USEREVENT;' \9 D5 Z! P/ P" l8 o
        ret = fb_set_var(info, &var);            /*设置var*/2 g6 ?8 c& s4 i  K4 `% ^4 H
        info->flags &= ~FBINFO_MISC_USEREVENT;3 l9 b- x& y3 h" K  v, I
        release_console_sem();
  j' E' ?! ]6 D% f4 l4 W        unlock_fb_info(info);8 j- I' {' ]: r
        if (!ret && copy_to_user(argp, &var, sizeof(var)))
7 f, {6 v. H5 g8 }            ret = -EFAULT;
/ d8 v/ w0 v) C$ j; l3 f        break;
& ?4 f! r1 v  {5 }: E! n1 y    /*获取fb_fix_screeninfo*/    /*fix为不可改变信息,只能获取,不能设置*// O1 k+ n$ g& n
    case FBIOGET_FSCREENINFO:
* f/ Y3 u9 v8 w' D) f( @        if (!lock_fb_info(info))' t6 o" }7 t% |  h7 _/ h
            return -ENODEV;
/ d3 ]7 A5 f, I) d2 C        fix = info->fix;
3 Y# P  y* q- {; H; I0 c( v        unlock_fb_info(info);+ R$ |4 {0 L; _, l4 I- w  h/ `, I  }
4 s+ l; P# M- y6 w
        ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;& Q! v% R( W' d' q5 O
        break;( V+ g( x5 Z% M- E. o  u  f( D
    /*设置fb_cmap*/   
* }, E) @6 [& L# N    case FBIOPUTCMAP:
0 n. J8 ]! s$ g& f8 [        if (copy_from_user(&cmap, argp, sizeof(cmap)))5 [' J& l3 \/ g% ~0 T
            return -EFAULT;
# }) K* I" J5 z        ret = fb_set_user_cmap(&cmap, info);    /*设置fb_cmap*/
/ g! @; t/ K: D7 E# F9 f        break;
$ h# D+ J$ D: @9 Q; h& h& b    /*获取fb_cmap*/   
0 B. `5 P# p: a    case FBIOGETCMAP:
- @  ?) R% @. s  \        if (copy_from_user(&cmap, argp, sizeof(cmap)))
$ H4 ~4 H  [) w            return -EFAULT;
! @1 R7 S& |1 ^+ j/ H' E        if (!lock_fb_info(info))
& e% ~) Z' D. o/ f- Q            return -ENODEV;
& s* k+ `3 z  Q2 f: K  i, M; V        cmap_from = info->cmap;6 K  B( r# k  h
        unlock_fb_info(info);
7 b7 o& w6 E8 U" b, x        ret = fb_cmap_to_user(&cmap_from, &cmap);/*获取fb_cmp*/
; X7 k# l6 m. O7 M; |        break;
; @* j& `7 t& v: T    case FBIOPAN_DISPLAY:2 G- V/ j7 q9 Z+ F, D
        if (copy_from_user(&var, argp, sizeof(var))); _9 c2 J, ]4 d' c
            return -EFAULT;
4 J% k0 Z5 C. N% _3 U# ]7 h# t# X, _) L        if (!lock_fb_info(info))
7 h- a( X; v* e6 P! w2 U( h( B            return -ENODEV;+ I* i- k+ u7 h1 S, h& ]5 D
        acquire_console_sem();
9 O. T' l1 a! p# T; R        ret = fb_pan_display(info, &var);: C) Z* O4 k4 V; |0 }0 L1 f
        release_console_sem();, t0 [+ M8 E) b) l( p/ d
        unlock_fb_info(info);" e% I' r& G3 ^& H. f
        if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))/ t2 `  Y, \$ }% |
            return -EFAULT;
- l1 u( ^7 |" E- X' y2 S        break;" j5 M. @5 l. W* q+ y' d6 U
    case FBIO_CURSOR:- C8 o4 x/ h# ~( T7 C- a# O
        ret = -EINVAL;0 y: y! s9 W4 n( R
        break;
/ i0 |) N( R* K, B% u    case FBIOGET_CON2FBMAP:7 o1 ]2 d! C* Z1 {
        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))0 H; B" m* c! e. I
            return -EFAULT;7 R5 Y5 O: z. t
        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)/ |2 W0 V8 e2 W2 h- A/ t9 ^
            return -EINVAL;; e4 E) ?7 _2 Y
        con2fb.framebuffer = -1;
! ~& `' i" m6 _+ m% Y/ @        event.data = &con2fb;! ~/ ]$ p6 L7 l7 `
        if (!lock_fb_info(info))5 o( F9 S1 p! f, D+ S
            return -ENODEV;" m+ y! }5 D- K
        event.info = info;! X* D7 ~. H; e
        fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
2 w' O3 g$ f& R        unlock_fb_info(info);
4 V* e2 Q' N2 i        ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;7 P9 ?7 D, D! }( h2 e3 B0 c
        break;4 O  N4 b* W) _
    case FBIOPUT_CON2FBMAP:
5 D  F2 }! t3 A( o* V        if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
5 _/ `% ^, n9 B; O2 e            return -EFAULT;, s! O% Q9 j- h
        if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)3 E" _6 r2 i5 _
            return -EINVAL;
8 W; w5 i; p: M0 e0 n9 ~; n        if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)$ G5 |; k( u, Q& Y4 @7 F
            return -EINVAL;
% I" `, P& ?2 \$ J) s        if (!registered_fb[con2fb.framebuffer])
1 T; L% b) V1 R2 G& c7 \5 X            request_module("fb%d", con2fb.framebuffer);5 x" @7 ^- o7 u: E9 C0 _+ \
        if (!registered_fb[con2fb.framebuffer]) {$ C- h1 v3 f) I* T
            ret = -EINVAL;) X7 E! U* M; J8 m5 ]
            break;
2 v2 r) y! v6 }0 i        }
$ u. g# K# G$ O8 }9 F& u$ A; {        event.data = &con2fb;. V6 X3 Y" [9 }) O
        if (!lock_fb_info(info))  W) {4 f  n9 R3 H4 _' w
            return -ENODEV;
8 t* |8 l: l/ q+ W+ Y2 F        event.info = info;
  _" C7 n: ?: H" s3 W& o% Q1 H        ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);4 Q  T( H4 g2 U3 b! H, u+ F4 X8 O, ?
        unlock_fb_info(info);3 W* p: P/ I/ {: J- j  V, |
        break;% j0 V# ~0 R' M8 ?" Q6 @
    case FBIOBLANK:8 j3 ^1 v( \) n7 A
        if (!lock_fb_info(info))
8 f8 T6 z$ X* I0 l! K            return -ENODEV;
# Q* ^: p# \. a" a7 A        acquire_console_sem();
  T: k# W% a# e        info->flags |= FBINFO_MISC_USEREVENT;
/ d4 Y# V/ \4 ?        ret = fb_blank(info, arg);        ?*最后调用驱动提供的s3c2410fb_blank*/
0 D, R+ @+ s' K8 M; \        info->flags &= ~FBINFO_MISC_USEREVENT;
9 v2 }9 ?) t. m8 t! e        release_console_sem();
, g. O6 h. v' T0 s        unlock_fb_info(info);
* G2 N3 c% J& @8 g        break;: c) G1 l9 M3 u7 i. U7 g
    default:
: }, r3 m2 }3 c9 i- v2 z9 F5 p) k' g        if (!lock_fb_info(info))
# G- h$ ~" A5 C- Z            return -ENODEV;
" h# Z& P( r# f& K* M& V' c        fb = info->fbops;
: N/ u7 P; P9 o  K* y9 b  ^        if (fb->fb_ioctl)        /*fb_ioctl为空*/
& `& K0 B" j% @+ a4 W' \            ret = fb->fb_ioctl(info, cmd, arg);
0 {7 X3 |$ b2 p! P6 M$ Z$ t+ p        else
8 O; g/ w- [: A/ Z% ?: [0 _            ret = -ENOTTY;, S1 [1 ]1 Q1 h9 Q  A3 ^; z- q
        unlock_fb_info(info);$ o2 g4 h' A. ~' |
    }8 n8 a5 B1 q( b" z5 q. ~
    return ret;, o( }% L0 C# T$ d) X% ~5 J
}/ n: c0 g  B; o3 L$ M# K- Y& j
正如2.2小结所说的,fb_fix_screeninfo只能获取不能设置,因此,ioctl只提供了获取fb_fix_screeninfo的方法,而没有提供设置fb_fix_screeninfo的方法。
6 F: }5 n3 W1 s& |6 m6 b
* ~2 A+ H5 X5 }9 Z3.5 小结
5 K: _( O6 K' j9 M' y& r  本节对frambuffer的核心层进行了介绍。包括frambuffer子系统的创建,frambuffer的注册和提供给用户空间的5个API函数。下面开始介绍驱动层。
7 t0 o$ G$ F7 H" I. S* C  L; ]4 G. Z9 v5 v
4. 驱动层
2 a, p  C# [) _* S2 Z& m  k本节将开始介绍S3C2440的frambuffer驱动,该驱动源码位于drivers/video/s3c2410fb.c
; N9 l( ]! i- U5 ?
1 b! m2 @  q. o2 L首先来看下驱动模块的初始化和清除函数。, p' U' _$ Z/ p& s

: r& w' U* z5 |, z$ i4 v4.1 s3c2410fb_init和s3c2410fb_cleanup
; C4 |0 _$ ?  H3 \* I. ]static struct platform_driver s3c2410fb_driver = {
3 V' r+ z; a* g& J9 s    .probe        = s3c2410fb_probe,
* e4 C4 B1 l! k% x" F    .remove        = s3c2410fb_remove,
6 f& x7 Z) ~4 g; J( E    .suspend    = s3c2410fb_suspend,
6 k& N( z7 F' c9 Z0 ^+ \. O    .resume        = s3c2410fb_resume,
: H5 J" g; Q6 }- h# h% O3 j    .driver        = {
+ U+ I. X; {# o) i" ?2 Z0 w        .name    = "s3c2410-lcd",
' p' T( _- i" a0 ?9 C        .owner    = THIS_MODULE,( U* M  W! Y& r$ ?
    },9 H. S" J1 {3 S2 B; }2 T
};' u4 B! f! ]0 C5 T9 c

. j  F1 p# [* M9 Z/ i% ]& qint __init s3c2410fb_init(void)
1 K$ n. {- [, h% k{
5 Y7 c3 P8 x- j* L% Y1 b- r        int ret = platform_driver_register(&s3c2410fb_driver);9 v1 C) ]8 Y& j4 f5 S- \
/ z8 d# l5 u4 o& g  ^* z) B
        if (ret == 0)- k3 r7 |' ^  ]3 Y
                ret = platform_driver_register(&s3c2412fb_driver);;4 J4 k: Y" z4 w9 k; K1 B% u

: x: f3 i' j" `  c, R! V3 V' J        return ret;# R. w9 U0 P7 H0 I8 h
}
, @( m4 u" J+ g$ {$ d+ ^+ O2 b* K9 ~) Q/ }4 \: b6 o1 o2 W- f
static void __exit s3c2410fb_cleanup(void)3 I3 `9 Y4 x! }8 ~- j+ Y
{3 @6 R  r2 W9 f- U( J
        platform_driver_unregister(&s3c2410fb_driver);. N( H5 ]& Q( Y9 [1 S7 G
        platform_driver_unregister(&s3c2412fb_driver);* P7 r0 {3 e6 i* Z
}9 Z  c3 X% D/ [+ d7 Z1 d2 t
, o, Q) {2 O/ ^. O2 R+ Y
module_init(s3c2410fb_init);
0 v8 c# W5 E' E; {+ r7 Emodule_exit(s3c2410fb_cleanup);2 z9 {7 b; @7 ]$ P" P7 ^
2 p# z9 S+ B7 L9 t
当platform_driver_register调用的最后会调用probe方法。接下来就来看看probe方法。
. l/ p# f- Y# b7 ~4.2 probe方法
. B0 I$ U6 R1 \6 estruct s3c2410fb_info {* p& x, F2 Z6 E* c
    struct device        *dev;7 b. u& v  a1 i9 Z: V
    struct clk        *clk;
" @: @$ V" H; ^7 u
& q) N0 ?: ?( A# A! k    struct resource        *mem;" g) n: y7 [* \: C$ `: k4 x2 l2 L
    void __iomem        *io;        /*虚拟地址*/( C9 Z7 C7 y. u$ B7 ~9 T# ^
    void __iomem        *irq_base;; t/ v; O  i  @9 n2 ?$ l" @

3 p4 V" {: K1 r; @' p7 s7 J    enum s3c_drv_type    drv_type;
3 V5 r4 m- Z9 @    struct s3c2410fb_hw    regs;
  l" \3 T/ @$ r0 i8 {! P7 i# e' \
    unsigned int        palette_ready;
" n, x- }) r5 @/ t( J, J) _' U) K! r
    /* keep these registers in case we need to re-write palette */
$ }2 [4 W+ |2 _' O: K    u32            palette_buffer[256];) o% [2 j9 W, M9 H; |2 E, C9 i
    u32            pseudo_pal[16];
8 j% P9 _8 s2 r' G9 h};
& V- }% I& N5 J$ h) Y4 b( w" [1 t/ T# w' Y
struct s3c2410fb_mach_info {
7 j+ j. s9 N1 Y2 D- V: K& _* `  a& s0 v; n- [$ [% R
        struct s3c2410fb_display *displays;        /* attached diplays info */
0 @! J! J: [. t, H8 G( n& I        unsigned num_displays;                        /* number of defined displays */
6 c5 C' q5 d# X        unsigned default_display;
/ N2 c% q. d+ `" N1 d% ~% m: L! ?1 J
        /* GPIOs */
2 p8 s* C# S+ j
, c" g8 {/ X7 h+ h8 x4 R# e        unsigned long        gpcup;1 M+ S0 n/ Z4 k$ X
        unsigned long        gpcup_mask;
8 v% s7 ^# X( j" G9 c" f        unsigned long        gpccon;, t/ F* W% e  c0 d! P
        unsigned long        gpccon_mask;
- ?, n  ^0 t# s% o  [        unsigned long        gpdup;& a: S+ R+ r1 R- `" g* X+ ]
        unsigned long        gpdup_mask;1 y3 Z4 \! m) `3 g0 P0 `) B
        unsigned long        gpdcon;: |9 ^1 S6 v$ `* ?2 ^5 s
        unsigned long        gpdcon_mask;
. U/ x: `- B1 z
5 @2 |- l9 H. y8 K        /* lpc3600 control register */
$ Y# {; p! |+ E# f+ T) ~        unsigned long        lpcsel;% m1 ^% N) L2 h# x% y" h# q; G
};
' t" j5 H% ~- i& i& l- u5 i( F7 \4 ?3 E; m
/* LCD description */- J* Y. V3 x3 y4 Q0 _% Z
struct s3c2410fb_display {4 R  m8 o: G+ ]' z* R
    /* LCD type */! h: T1 V2 X# G4 \+ c4 D; W
    unsigned type;
4 Z) s- c) J# O' D6 i( q
+ s3 V) `! {- Q0 C0 U) B. x    /* Screen size */
* Y& ^9 ?( M- n! I, B    unsigned short width;
9 _% b: f, ?: w, X    unsigned short height;
% ^0 D3 r0 f$ H! \1 B1 o+ \7 ?& |/ F) [( ~5 T
    /* Screen info */1 ~, r+ \; n2 F' u9 S0 V1 v
    unsigned short xres;
, e9 t2 B1 u0 c    unsigned short yres;
# s  n& n. {# g  O6 Z7 L0 n    unsigned short bpp;1 n4 A; j" v& f) x" i

- i  ?( }# h' S( ~% t    unsigned pixclock;        /* pixclock in picoseconds */% e, u( A) P5 [6 z  M
    unsigned short left_margin;  /* value in pixels (TFT) or HCLKs (STN) */
3 R8 b7 ~& a5 U    unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */
( e  j* p: E7 Q3 F2 e& Z+ b    unsigned short hsync_len;    /* value in pixels (TFT) or HCLKs (STN) */
/ g" v, B3 o3 |4 S1 G9 r- r! @    unsigned short upper_margin;    /* value in lines (TFT) or 0 (STN) */! b/ N5 p  B9 F: V1 I0 s
    unsigned short lower_margin;    /* value in lines (TFT) or 0 (STN) */6 _7 q% v& M2 G, K6 Z* ]! {
    unsigned short vsync_len;    /* value in lines (TFT) or 0 (STN) */1 I; R, u) N: M+ |

. h2 P! H& V# I0 d    /* lcd configuration registers */( Z- T6 J! K2 I+ V* [+ ]7 v
    unsigned long    lcdcon5;; S% Y+ a) B) s- v# L$ e  H4 r
};
8 f# w  L# {. z$ \2 [. }
3 o: f- f1 O- k; u7 qstatic int __init s3c24xxfb_probe(struct platform_device *pdev,' Q. s) {/ b+ _8 G" A  ]
                  enum s3c_drv_type drv_type)
% k0 t5 n" r+ P9 P6 D. W, f, l{
/ t7 @+ `# ]9 m4 o5 p, R$ p7 R7 J    struct s3c2410fb_info *info;' f7 C" R4 h) s# h/ N) g
    struct s3c2410fb_display *display;
  [' P  [3 N' X: X6 V    struct fb_info *fbinfo;
3 t# V/ L" P: t% w$ z4 ~: E    struct s3c2410fb_mach_info *mach_info;0 k7 |" _6 I: s+ ]
    struct resource *res;/ P2 W$ R9 t) V- B
    int ret;& s$ ^9 D) R, h0 n* r2 H
    int irq;* i( Q8 A6 W& k5 k2 W: ~5 V  g
    int i;  c0 ?! L$ V. h4 r0 ^
    int size;! J0 f8 o7 q6 i+ T3 u; i
    u32 lcdcon1;
5 q6 p% R) b& `% r    /*dev.platform_data由函数s3c24xx_fb_set_platdata(mach-smdk2410.c)设置,指向s3c2410fb_mach_info*/
: X6 m  C. ?2 l    mach_info = pdev->dev.platform_data;   
, y6 p0 }! K/ n0 g    if (mach_info == NULL) {  {: o  h5 J0 _' V6 z2 E* u4 K
        dev_err(&pdev->dev,& Y( n( ~9 F$ p1 n; u- b
            "no platform data for lcd, cannot attach\n");
" H$ Z4 G. T9 F9 B: F  k1 \: G        return -EINVAL;
0 m0 D  s  R/ a4 Q' d: m6 X    }# R# T) P3 W# A  p
                                    /*在mach-smdk2440.c中,default_display=0, num_displays=1*/
; V; `7 \+ t( p    if (mach_info->default_display >= mach_info->num_displays) {     
9 G7 h& t/ L* w6 i: ?$ V8 L( @$ G        dev_err(&pdev->dev, "default is %d but only %d displays\n",
+ Z- N  a6 R) T1 N0 l            mach_info->default_display, mach_info->num_displays);- Z8 ^# \) K3 j' T6 X
        return -EINVAL;
( D% W3 [: k* k6 d0 `, ?' n9 T    }
( R* t2 q: h3 d' j* ]4 h% ~
/ {. h8 X$ c( n6 p  X    display = mach_info->displays + mach_info->default_display;! `' U5 b) }4 g( e! u+ b
4 e7 S4 c3 S3 z8 I
    irq = platform_get_irq(pdev, 0);    /*获取IRQ号,16号中断*/
# d# U1 a' L- o& v: O+ ]    if (irq < 0) {
1 \3 Q, T& f2 i$ ?8 C9 E  T, A        dev_err(&pdev->dev, "no irq for device\n");
! V% ?6 S$ ]5 p7 v6 S6 h        return -ENOENT;
( J  {& {% K1 e' c    }3 I& H4 b  D6 G9 `9 w
                                        /*分配struct fb_info 其中包括sizeof字节的私有数据区*/3 y. C; t/ e- N" Y7 V: U" M8 Q; C# ?& |8 R# S
    fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);( [- k- ]) a, |
    if (!fbinfo)
* ?' b' ]0 O5 }7 ?  |        return -ENOMEM;$ e2 k% Z" }+ Y) L5 `) y

$ ]0 Y0 R  m. i& y7 u    platform_set_drvdata(pdev, fbinfo);    /*让platform_device->dev.driver_data指向struct fb_info*/
( ^2 H$ s% F2 L
* [% f0 Q# N  o+ v, D% o; r    info = fbinfo->par;                    /*par指向s3c2410fb_info*/. x3 k/ h/ ^1 _4 g" _8 f
    info->dev = &pdev->dev;
7 M# n0 [' {$ J( P" k% X    info->drv_type = drv_type;  _0 D' [# F4 V2 j) a
! F7 Z5 W, j1 R0 `* m* Y7 |
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*获取平台资源*// O7 q( [! C" k
    if (res == NULL) {! Q5 a: q3 j0 I" e! W* Q
        dev_err(&pdev->dev, "failed to get memory registers\n");# V* U: H' U4 E- Y1 F' e
        ret = -ENXIO;
/ n) L* B5 q+ U8 p/ E0 n        goto dealloc_fb;' W5 o* c" K! a, T4 N% L+ f
    }% N/ Y" _& z9 k( f! r  s" D$ s; A. ~- y
' \% N( Q) i% M
    size = (res->end - res->start) + 1;                /*IO内存申请*/
0 B5 v  J# I% |    info->mem = request_mem_region(res->start, size, pdev->name);    7 m7 e2 r( d- ?
    if (info->mem == NULL) {5 B5 b$ S5 X$ @; ~
        dev_err(&pdev->dev, "failed to get memory region\n");9 H& d' x9 k6 R/ L- d
        ret = -ENOENT;
: I6 \: C% p* D! `* W$ f1 i        goto dealloc_fb;1 P* i' J! W& N, g" |
    }
( B5 o- w- Z7 F
  |. j9 b2 d  N* k  G. I% b    info->io = ioremap(res->start, size);            /*IO内存映射,获取lcd第一个寄存器的映射地址*/               
& R7 v% f* |/ W! H    if (info->io == NULL) {% g- s# e4 ?& N* a( ~$ `# k
        dev_err(&pdev->dev, "ioremap() of registers failed\n");        
, [  ^9 w5 o) i$ j: Y1 q        ret = -ENXIO;   
- X/ S  X0 x% W  Q/ M+ n1 Y& Y: E        goto release_mem;
  G) c% `5 S" |$ A9 h2 m# d$ e( t    }. o& r9 k2 ?4 W3 s, |; G
                                            /*irq_base对应的物理地址是0X4D00 0054(寄存器LCDINTPND)*/' @% }1 O7 D1 k/ l
    info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
5 Y$ i& j3 X+ f* c* K" D
! v; X: ?5 F- i0 o    dprintk("devinit\n");3 [: A6 y, S% e, R9 ?% w
: [% S' t  A" z, p
    strcpy(fbinfo->fix.id, driver_name);    /*复制名字*/
- ]( G/ s$ U8 s3 A% |( ?9 n* O. d0 L! X( L
    /* Stop the video */- p8 ~3 H- r4 F2 A
    lcdcon1 = readl(info->io + S3C2410_LCDCON1);# \& Z: J( N. Z, v( {2 r( X
    writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1); /*禁止LCD*/
8 p7 V% n* u0 `9 B
: S+ X) z, J+ h2 H9 Z    fbinfo->fix.type        = FB_TYPE_PACKED_PIXELS;! a1 U6 N4 y+ R  Q! u
    fbinfo->fix.type_aux        = 0;
* ~3 @: O3 O6 l$ f2 v$ Y7 A. c8 {9 j    fbinfo->fix.xpanstep        = 0;% W- M# x0 F& u  }1 B9 w9 j
    fbinfo->fix.ypanstep        = 0;% y- [4 N. H( H0 R3 |
    fbinfo->fix.ywrapstep        = 0;
" u/ ^) w$ m( |: y3 K  w% ]) I0 g    fbinfo->fix.accel        = FB_ACCEL_NONE;    /* no hardware accelerator    */
. O) T+ i# z. b, g0 h1 ]$ ?
# w+ h# _1 N8 e( K8 Q$ \' D    fbinfo->var.nonstd        = 0;: {" F6 k/ N9 x+ B
    fbinfo->var.activate        = FB_ACTIVATE_NOW;1 B' u! u5 ^3 A/ {8 m& h
    fbinfo->var.accel_flags     = 0;' N- l) R* o$ v" y8 j; \
    fbinfo->var.vmode        = FB_VMODE_NONINTERLACED;- E8 q/ {+ x$ Q/ F5 l

3 \) r8 _1 F/ I/ _    fbinfo->fbops            = &s3c2410fb_ops;
- W7 A5 A1 m; E. k    fbinfo->flags            = FBINFO_FLAG_DEFAULT;
0 v3 N, v8 R- T. B    fbinfo->pseudo_palette      = &info->pseudo_pal;
9 ?4 M3 Y& _# u
& i& S9 `- W1 K    for (i = 0; i < 256; i++)
# [6 f% A: [2 L7 S1 V) y9 a        info->palette_buffer = PALETTE_BUFF_CLEAR;
; `0 O9 t3 {& j3 D. g
9 D* h; I* j7 ]  ]' D    ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);    /*申请IRQ,快速中断*/- J3 \8 I# ^0 N( T! y% H, @: }9 G3 v
    if (ret) {! X: Z  d4 c; a+ d: Y1 P+ D
        dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
  M6 Y5 I; ]  P5 d6 B7 N$ p; V0 D        ret = -EBUSY;
/ K5 v, A& Y  B9 N7 y4 D        goto release_regs;
" ~0 y5 X/ o% @2 m    }( M. R9 O0 h: O( v* c2 L2 j5 A: k
. s- i2 `7 D* @
    info->clk = clk_get(NULL, "lcd");        /*获取时钟信息*/
' V0 T# B5 Q9 n2 y+ Y7 ~( b# i$ r    if (!info->clk || IS_ERR(info->clk)) {+ n' P6 D9 ]" P+ x
        printk(KERN_ERR "failed to get lcd clock source\n");& g; y+ N4 |2 ~/ k% K7 D5 `8 [
        ret = -ENOENT;
! J, O4 G+ E* [# u9 g4 N        goto release_irq;
  G) ^$ }5 J, V; M+ m+ f1 R8 r8 p    }
8 k8 A/ u8 ]' W: B7 c
- b0 P3 _/ q! \; Y. `  ~( S8 h    clk_enable(info->clk);                    /*使能时钟*/; b- D7 c) r  }, |) G" j  d
    dprintk("got and enabled clock\n");7 f( T  Q' b& [+ c0 U1 Y7 o7 i5 @

" y  f$ {+ B) I" I; R0 T    msleep(1);2 g. Q. L/ J8 \9 \# L

& ~$ J3 g8 D9 }7 e    /* find maximum required memory size for display */* ?1 z* B1 ]: H
    /*在多个屏幕中,找出需要的最大memory*/8 A) W$ S: }. {1 s3 K
    for (i = 0; i < mach_info->num_displays; i++) {) U8 y# F9 N: L
        unsigned long smem_len = mach_info->displays.xres;4 s1 N% }6 {+ ~
        /*所需的memory空间 = xres * yres * bpp / 8*/
% O) Y9 u" J# O) x& i5 m! M        smem_len *= mach_info->displays.yres;3 j1 u4 \8 I- R! s. i% ^
        smem_len *= mach_info->displays.bpp;
+ R+ b7 g- V8 Y        smem_len >>= 3;
1 w! I# ~) ^" i0 j  @        if (fbinfo->fix.smem_len < smem_len)% G: G0 }& w& E/ z5 w( \
            fbinfo->fix.smem_len = smem_len;0 W- t5 M, S% k0 s4 P$ o
    }
$ D# r9 h. X9 q" k" E  }) T3 f. A5 P  E1 m. y! I% m
    /* Initialize video memory */    /*根据上面fix.smem_len的大小,获取DMA映射内存,一致性映射方式*/8 O" u0 q2 l. j* Z
    ret = s3c2410fb_map_video_memory(fbinfo);  n, O9 t  e! t% S) \
    if (ret) {
( ?! [' h" j! w, V8 [1 z        printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);  ^: k* n' h* b: I1 s6 j0 V, G
        ret = -ENOMEM;4 v& M! v4 y/ M; w
        goto release_clock;' x+ O! Y8 h% _; [- u9 M$ X% h2 |3 Q" C3 o
    }! M2 W5 F, y' p2 ^+ x/ p; R* e
8 Y( O) g/ N5 H3 @
    dprintk("got video memory\n");
9 [$ J  B' Z; N' _- J0 P, Y0 X; ^2 i: t# P
    fbinfo->var.xres = display->xres;            /*320*/& A/ ^0 V6 W. R' _- ^
    fbinfo->var.yres = display->yres;            /*240*/   
7 Q& A2 l& E: E# R    fbinfo->var.bits_per_pixel = display->bpp;    /*16*/) ^3 |2 Z+ z% l: `4 P
- J* p/ b6 b/ R3 O% E% t) e& |
    s3c2410fb_init_registers(fbinfo);            /*LCD寄存器初始化*/        - G. e( H) i' p8 i& P5 M) E' M: s

* z6 w5 ^9 L9 u) Q: |) ]    s3c2410fb_check_var(&fbinfo->var, fbinfo);5 f7 j) ?: Y3 n/ K
/ C7 }2 z) z! ]5 R- J8 V% t+ z1 `
    ret = register_framebuffer(fbinfo);            /*注册framebuffer*/5 i) @6 u6 A* u
    if (ret < 0) {
) l2 B2 h" e/ j" \1 w* R        printk(KERN_ERR "Failed to register framebuffer device: %d\n",ret);3 Z/ @, W/ l+ P4 p
        goto free_video_memory;
3 Y- `4 e7 n+ `. y1 j( B    }
# {/ A. S" [9 F! ^$ Y6 x& Z0 ]
$ \5 c1 q4 u; n  g0 B3 p: J    /* create device files */" V  D$ O  P- R
    ret = device_create_file(&pdev->dev, &dev_attr_debug); /*添加设备属性*/$ t& h9 s* K; B$ ^+ p
    if (ret) {
5 _6 i1 R  Y5 U: w        printk(KERN_ERR "failed to add debug attribute\n");
) G- s: q7 }8 x( \: T    }
: E& e- K3 o2 Q# ~9 K
, C8 d6 M; A6 ~) ^( ?    printk(KERN_INFO "fb%d: %s frame buffer device\n",
1 F8 @& A: T. }' Y2 [# @. D        fbinfo->node, fbinfo->fix.id);: u5 _* N2 r0 ~4 w; t' M

& y( S: i7 G' r    return 0;, `& O- N$ R2 Z6 N: Z
/*一旦某个步骤发生错误,以注册的相反顺序开始注销*/
, `! ^) i+ v& d6 u4 d2 g0 rfree_video_memory:
" q! y; ?) @8 M  e; X9 s! a    s3c2410fb_unmap_video_memory(fbinfo);( f' O3 {+ U% q; _" L: {
release_clock:$ p7 N4 U4 U6 Z# q  Z' q# X( Z
    clk_disable(info->clk);
% [0 X# E, M1 v    clk_put(info->clk);& m: B5 H, ]0 `3 O
release_irq:1 l( a9 f8 P8 D
    free_irq(irq, info);
3 Q. f; Y5 _2 M& ^9 e" y; Prelease_regs:3 c; W; T# `2 X5 v' J; o
    iounmap(info->io);5 M! h: S8 U. g
release_mem:* U7 ^  V  R) s% Q7 X
    release_resource(info->mem);9 |" [) f, _7 V+ O! p! h, ^
    kfree(info->mem);# X$ W+ ?- p) j& j, x- ~
dealloc_fb:- M" Y" f9 `; y; q1 o7 N) f
    platform_set_drvdata(pdev, NULL);
* M6 x; T: T+ {9 V8 T    framebuffer_release(fbinfo);    - p& _' ?' q( s4 H' w
    return ret;% Q1 q, Z, ^) D: {
}
; [0 `. O/ p$ ^# Y7 H$ Q2 f- z3 n5 C5 L6 B- t9 `) ?; V
这里使用了三个新的数据结构。s3c2410fb_info是驱动程序使用的,里面将保存所有驱动程序所要使用的资源等。而s3c2410fb_display和s3c2410fb_mach_info,是由板级信息,通过platform总线添加到内核中。
3 n) l) u/ H. `% }
. w& M* V; m- c* X2 L* Gs3c2410fb_display中的成员将被复制到fb_var_screeninfo结构中。
/ e( v% r6 s7 P! l5 ?) f
8 w; n1 ?) e$ `该板级信息的定义在arch/ARM/mach-s3c2440/mach-smdk2440.c中,来看下
9 K5 {* G' \9 `$ d# \1 w  U/ b6 v0 o
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
8 S; k  z' W1 ~+ ]1 H# i; W+ m' @! q9 L. [8 {
        .lcdcon5        = S3C2410_LCDCON5_FRM565 |
. U+ C" m2 o( E5 X/ o; u# E                          S3C2410_LCDCON5_INVVLINE |. v4 M/ g, i& o( u
                          S3C2410_LCDCON5_INVVFRAME |. z- G: }. x; J1 n5 _
                          S3C2410_LCDCON5_PWREN |3 ]! r! t  u* m
                          S3C2410_LCDCON5_HWSWP,
, J% J% S$ ~0 A' M9 j' Z5 J) ~) ]
        .type                = S3C2410_LCDCON1_TFT,
# `: U7 o. v; |$ f! E, R, y1 [
0 e' ?1 I9 T& {; |        .width                = 320,//240,( ?* J8 b* p. G) P$ e4 V
        .height                = 240,//320,9 u: P6 f0 M. n7 c# a# ^! O

0 ^$ @5 J9 }, D: }" G& ^" x        .pixclock        = 149000,//166667, /* HCLK 60 MHz, divisor 10 */
( a; w/ Y) W" k4 v& C        .xres                = 320,//240,, H( B: }* f/ g2 Q8 c4 o
        .yres                = 240,//320,+ g2 V  x4 y/ ^; X( r7 F
        .bpp                = 16,- k% D' G( S( S) g1 k4 c
        .left_margin        = 20,# u+ t: I  B* n4 ~/ k2 P2 k
        .right_margin        = 38,//8,8 e: ?' W) t& g  j3 G0 Y8 Q4 ^
        .hsync_len        = 30,//4,$ n7 x5 l4 \2 |. ~/ b
        .upper_margin        = 15,//8,
5 P" B6 x3 v) L+ v        .lower_margin        = 12,//7,
6 x  r% ?; p5 S& O# c        .vsync_len        = 3,//4,
/ j6 K, j% F2 v3 Y: {1 x' Z};
% f, J. W1 M( ^+ t: ^1 ]: d6 J( Q& W) E+ i! M' P% f
static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
* }$ t% I/ K  k0 L8 {: O0 R        .displays        = &smdk2440_lcd_cfg,
$ [" S+ M8 Y* \$ F. x, l1 P9 v& r) O        .num_displays        = 1,
  V5 R. F  u8 |6 A2 d! j/ D        .default_display = 0,
! b# `2 K. ?4 H# r2 T
* b) V- {3 z2 J% O4 `" }#if 00 k; P' R/ B# B+ A/ w' c3 U
        /* currently setup by downloader */
5 ?. N; W7 O$ |' L8 ~        .gpccon                = 0xaa940659,
8 O+ f" Q% y* ]& l- [# q$ m        .gpccon_mask        = 0xffffffff,
0 r1 e+ v& H! h3 `3 J1 ~9 @        .gpcup                = 0x0000ffff,1 i; Q, n5 X  o; N. s
        .gpcup_mask        = 0xffffffff,
& r' K3 E; f. \( N! X0 v9 n$ K- L        .gpdcon                = 0xaa84aaa0,
$ h/ o1 ^4 Q2 L) F7 s1 H        .gpdcon_mask        = 0xffffffff,
/ }/ m4 b, z  h8 ?! v; r        .gpdup                = 0x0000faff,+ r3 Y0 K5 X* l9 Z
        .gpdup_mask        = 0xffffffff,
! J& V* z# Y; X2 U2 _6 E#endif' G& n! \8 ~5 K1 t) \  ?1 \
//no
3 _0 D  S8 `- `//        .lpcsel                = ((0xCE6) & ~7) | 1<<4,
# c; |  [: [0 c$ P, {; e};
7 D7 C5 F. n+ B& k( k$ P
, T+ k3 i3 B. g& q5 N) k( |/ x这里NOTE:每个LCD屏幕的参数不一样,因此上面的参数会有所不同,这是需要移植的地方。. I/ l6 t, v7 s
# l; ]. ]. K+ q8 |. U4 S2 C8 S
随后,我们看下在probe方法中调用的几个函数。首先是s3c2410fb_map_video_memory。
0 o) x1 w! w3 n8 [% h7 V# P, V. d" b( Y( o
/*$ z  @, n( o' v8 g0 l- Z( M
* s3c2410fb_map_video_memory():
8 q' R6 u. K/ \3 l4 b8 W9 i *        Allocates the DRAM memory for the frame buffer.  This buffer is
* a7 `3 u2 f" h* m& r, f+ M/ b *        remapped into a non-cached, non-buffered, memory region to2 g0 V; }+ |8 E: D1 _/ \2 v
*        allow palette and pixel writes to occur without flushing the; x8 U3 ~+ j' u( O2 c; \) X# Q. t
*        cache.  Once this area is remapped, all virtual memory
- W  Z3 n$ Y3 d  C- {) X *        access to the video memory should occur at the new region.
7 b2 c1 S/ d/ l/ s( W6 q2 Y */: I3 t0 L% [7 G' U  \3 j
static int __init s3c2410fb_map_video_memory(struct fb_info *info); Y4 @* V$ K: x, p+ W/ E3 h
{
- R8 ~1 h! U& C$ I. o# Y        struct s3c2410fb_info *fbi = info->par;
4 p' B" _0 J, {" z" q; m* U        dma_addr_t map_dma;
6 `! b% n* X9 H# j$ w* i+ m# q        unsigned map_size = PAGE_ALIGN(info->fix.smem_len);0 N1 S; X' u  E. k* e, C

) ]' W) [: v$ }+ t! ~$ ~  d9 H3 b* C, [        dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
$ N! V8 f1 s' S8 c8 _; M, l* y                /*分配DMA缓冲区,并保存DMA缓冲区虚拟地址*/
7 ?3 \& P6 f; O7 z7 S        info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
4 ]: e. T4 \! N& V6 D' ^0 ]                                                   &map_dma, GFP_KERNEL);
7 m) \, U8 e" B4 b* b% ~5 m0 \7 U" z
        if (info->screen_base) {
: _5 L; b4 z/ j- I# i1 [                /* prevent initial garbage on screen */
3 N( W/ a: f; i' b+ X0 ]                dprintk("map_video_memory: clear %p:%08x\n",( R. w% R, s  j3 K9 @6 r
                        info->screen_base, map_size);
2 W6 b1 _; m- V: A$ p                memset(info->screen_base, 0x00, map_size);        /*DMA缓冲区清0*/. d7 r( O) m# t5 I% t* P0 p
+ n/ O5 o, i. b9 X: w( N
                info->fix.smem_start = map_dma;        /*保存DMA缓冲区物理地址*/9 E' {! C8 X% y3 \$ V3 h

! G9 S, A( f# ~9 m8 E3 B3 [" X7 Q                dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",* R& E: _& \' l/ L3 D- d
                        info->fix.smem_start, info->screen_base, map_size);8 B' A$ n, G6 f+ P) ^6 F
        }
! n9 Z! I, L5 w: y. u
' K* v3 G! ~- V$ N  H& o        return info->screen_base ? 0 : -ENOMEM;
% d6 @1 W) z6 I! h' x6 H}
5 D' a5 f) k& C9 i* k
# f: l$ I. k, F  x& V8 G  r7 e- {8 |5 X
该函数根据fix.smem_len的大小,分配了一个DMA缓冲区,保存了该缓冲区的物理地址和虚拟地址。$ N: |* e: e; x6 y
1 f, \: d: X/ M% Z2 a: Z5 J0 X" K& v
接着是s3c2410fb_init_registers:
5 u; N/ N2 W. Q, a1 t, M7 x5 z
" |! A/ }3 |; {2 @& [" ]/ e' t2 ?" g
/*
8 Q- G6 O6 B$ P# A$ o7 U5 | * s3c2410fb_init_registers - Initialise all LCD-related registers( L: c! ?% w( x5 s* y" A
*/. \! D' o/ j. Y9 S8 B
static int s3c2410fb_init_registers(struct fb_info *info), `! ?% T3 V! }: _, h8 @& y" o' \2 r3 z0 q
{- l# e. {* q$ s; t9 V. n
    struct s3c2410fb_info *fbi = info->par;        /*par指向s3c2410fb_info*/- f) M! y: M, ^' M4 U0 R7 z/ u
    struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
& q7 u! d( J/ l; r/ t2 }" j    unsigned long flags;
/ i* O. q) B1 t5 b- D- D    void __iomem *regs = fbi->io;$ h% e: @0 }4 k
    void __iomem *tpal;+ R/ {9 o( O* q% L
    void __iomem *lpcsel;
1 D" A  C4 I) A
7 k+ I2 C) y( T% m    if (is_s3c2412(fbi)) {
* F" J2 L0 ?6 I        tpal = regs + S3C2412_TPAL;: W2 a4 f/ P7 J  T- ~
        lpcsel = regs + S3C2412_TCONSEL;
9 l, c6 Y" k5 X    } else {
4 r2 n: D9 }  u: T* ?7 V        tpal = regs + S3C2410_TPAL;# |( \5 o* P" g  V
        lpcsel = regs + S3C2410_LPCSEL;
, M+ n$ J6 l) {  v: V- X$ A& f    }
2 x) X% p5 L; G7 u9 V$ S: U% r. A4 I4 N3 x) u) l; |
    /* Initialise LCD with values from haret */
9 x$ Q# v5 {; J: ^" {+ c4 `9 Y" L) z1 [% E7 _  q, I4 @
    local_irq_save(flags);                        /*禁止所有中断*/
6 G5 a7 k( `- a& s* V4 [
3 |4 R5 ]$ a6 @& }) L    /* modify the gpio(s) with interrupts set (bjd) */    /*初始化io管脚*/2 ?- N3 f9 ?0 q' Z8 w! A! m1 Q4 j

, G9 X  V& \0 O$ o7 }    modify_gpio(S3C2410_GPCUP,  mach_info->gpcup,  mach_info->gpcup_mask);
# d- w6 N/ K+ X    modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
; i1 |' P. f; P( G" J    modify_gpio(S3C2410_GPDUP,  mach_info->gpdup,  mach_info->gpdup_mask);
$ y1 l- }  z( O* T    modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);# L' ^5 S7 X: U" z( q9 j
; o! q9 c- r+ a& h
    local_irq_restore(flags);                    /*恢复中断*/
' d: ^) U4 o0 I& p! z  ^3 g8 i! M; c1 |3 @% x* t$ ^
    dprintk("LPCSEL    = 0x%08lx\n", mach_info->lpcsel);    /*设置TCONSEL,禁止LPC3600*/9 x. s4 p+ z+ w
    writel(mach_info->lpcsel, lpcsel);
6 t/ d7 r3 m/ |0 U& U; ~+ d# ~4 P; |) r9 u3 o* }8 @, D9 c
    dprintk("replacing TPAL %08x\n", readl(tpal));9 Z  B2 I% Q& E9 B! v5 y

  `2 W  R) A# B7 Q3 p! h7 P    /* ensure temporary palette disabled */+ a/ t4 K1 g! X1 a1 i% c
    writel(0x00, tpal);    /*禁止调色板*/
$ _# T: ~7 ]0 o6 g! X: J" \+ {
  l, D2 H" b2 o4 V/ Q    return 0;
' e6 l# @& Y6 t4 |) |}
; R3 q; n. \! i0 r
8 T5 z( M+ D- r6 r, {static inline void modify_gpio(void __iomem *reg,1 @& M1 e  m2 h) Q6 A
                   unsigned long set, unsigned long mask)
/ k! T. J+ W2 R, b+ |! X{
& T+ V0 O+ @, k. o1 E2 z    unsigned long tmp;! Z+ v5 n" s  c( b. Q/ L3 H  _6 ~

0 }1 [8 \- h6 f1 W8 H3 g0 d) I    tmp = readl(reg) & ~mask;+ g+ I7 f$ O, }* \
    writel(tmp | set, reg);( t0 a" u( D5 N2 m1 n! U3 w& q
}
6 R4 w% I4 {4 p, l最后是s3c2410fb_check_var:! z: c/ G; Y- O$ n! d( n" ^& y6 U
& H  r: i% U' k: ?# Y5 q- f& c6 }
/*/ i, ]1 w) f: _6 u
*        s3c2410fb_check_var():
3 R& k1 {/ r! y( T. i *        Get the video params out of 'var'. If a value doesn't fit, round it up,
/ H9 g0 x( Z! u: D1 t1 X7 b, Z9 X, d- | *        if it's too big, return -EINVAL.
. N2 p* g9 p7 y6 C4 d *        检查变量的合法性
* d& S0 t/ T0 q8 y2 C */
+ i7 f/ ^2 g* E; H" d- t/ Nstatic int s3c2410fb_check_var(struct fb_var_screeninfo *var,
: ]( n' d# a9 i$ {                               struct fb_info *info)
& w' I3 G" y6 B* l$ N4 e{, P( C( p4 R. ?% x/ [4 L6 z
        struct s3c2410fb_info *fbi = info->par;                        /*par指向s3c2410fb_info*/
) p$ x, H5 w, G! N' U& h        struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;/*指向s3c2410fb_mach_info*/
6 l8 n# }1 J( z+ k. X; X) h. Z        struct s3c2410fb_display *display = NULL;: L7 C( t" w7 Z6 ]
        struct s3c2410fb_display *default_display = mach_info->displays +
! ^/ b" @. [  A                                                    mach_info->default_display;4 k7 l+ [$ [4 O7 u3 s. k
        int type = default_display->type;         /*S3C2410_LCDCON1_TFT*/
  H- [: l, ]( J7 \        unsigned i;
6 i, M3 k9 A$ g1 O8 {2 `
; Q$ d# Z7 M; l        dprintk("check_var(var=%p, info=%p)\n", var, info);
; A& o7 C8 O5 e. C, o- u& T1 e! F4 g7 V) u1 x+ M, q
        /* validate x/y resolution */
. |; |3 ~0 ~7 n( z( p        /* choose default mode if possible */                        /*var中的成员在probe中设置*/
/ n" {1 V% C% [: X2 f$ c        if (var->yres == default_display->yres &&
4 i+ t( Z+ w# u+ \, A, [            var->xres == default_display->xres &&' }1 G) O! x, x2 T8 g7 w% _+ [, O
            var->bits_per_pixel == default_display->bpp)' y8 w& N2 o" v8 H# }( j. D
                display = default_display;
) q2 d  q' {; c4 C8 L) k% j        else
* P# q8 t; R8 r" D8 X, |% o                for (i = 0; i < mach_info->num_displays; i++)
% z; Z  X! @" m* D% n. D; O6 H; G% W                        if (type == mach_info->displays.type &&9 r1 c5 l2 P" _/ O
                            var->yres == mach_info->displays.yres &&
6 B/ V$ e3 ~. L- I0 W                            var->xres == mach_info->displays.xres &&
6 T7 ^/ M2 q& l; n                            var->bits_per_pixel == mach_info->displays.bpp) {: t8 |% I7 d; w  o6 B# n4 Z$ x9 I2 \
                                display = mach_info->displays + i;
$ k$ O+ E  c$ M7 g                                break;# X8 e- U6 v  S# O8 ~$ s) Y
                        }, I: N* t; f* u2 T' ~3 u& N
5 ?" V; ~+ L0 ?( A1 O% W( K4 [
        if (!display) {
  i  M& S! ]& X% E                dprintk("wrong resolution or depth %dx%d at %d bpp\n",
/ }( M/ @% j1 L: _7 L7 n% M                        var->xres, var->yres, var->bits_per_pixel);$ a$ a0 g& |; l( [; v
                return -EINVAL;4 w2 [, @+ o8 R5 R
        }8 ?! p" @) r. t# Y
1 z- M- p7 Y6 d% F5 \% o% g
        /* it is always the size as the display */
& L" x6 y) y8 C: ~; ^6 G) b# m        var->xres_virtual = display->xres;
+ X% P, M1 L" d! _# V% F        var->yres_virtual = display->yres;% ^* M) a% O9 X0 I+ x4 B) A
        var->height = display->height;* D3 @5 t; ?! @) N
        var->width = display->width;  \0 h1 |5 C! e& ?6 q  m

' b8 h. C7 K+ E        /* copy lcd settings */
. G( r  f/ h  d# u2 b        var->pixclock = display->pixclock;
: }5 L* _# k, m2 {  v) p        var->left_margin = display->left_margin;
3 U1 Z: C' k6 ?& @+ S        var->right_margin = display->right_margin;8 d7 r3 M0 h. [7 u' ^
        var->upper_margin = display->upper_margin;
: {. U* w7 E# S& P: |        var->lower_margin = display->lower_margin;) D- N3 Y6 J& k/ x: Z
        var->vsync_len = display->vsync_len;" i5 M9 S' e% b5 N7 p/ B
        var->hsync_len = display->hsync_len;
6 k0 B! d1 k# C/ z4 C
2 j8 @9 O- u* w- K7 J        fbi->regs.lcdcon5 = display->lcdcon5;
% d- T& [8 I# z2 h7 J        /* set display type */% v7 b( ]$ ^5 h; c) A+ ~4 K
        fbi->regs.lcdcon1 = display->type;
; [+ O" d2 z- ?/ A  ~  c: {) O
3 s' V; ^4 A3 ]5 X        var->transp.offset = 0;+ Z4 w3 K4 ]" ?
        var->transp.length = 0;
/ _6 ~3 h3 W4 C5 J) [" o6 W! O        /* set r/g/b positions */
( N/ K$ {) C1 f" \7 B        switch (var->bits_per_pixel) {, e- C7 W" T, z( }
        case 1:
/ u) H6 f+ r. X2 p: {" k) |  T5 Q        case 2:
. V: S5 O4 o* L: C7 b3 Q        case 4:  Z& s8 W2 A  k6 \- _
                var->red.offset        = 0;/ g# F6 ^: o1 b6 u; _$ C8 B+ a
                var->red.length        = var->bits_per_pixel;( ^0 X4 |: M" G/ r1 g) g5 |* {
                var->green        = var->red;) K* K5 i( v2 D* X7 ^% f
                var->blue        = var->red;8 G1 _, H" O8 o. d: ]
                break;( \2 ]6 l  f# g4 }
        case 8:
# o; C( M; e) {: l( [                if (display->type != S3C2410_LCDCON1_TFT) {7 y+ o5 a7 u+ p4 w, a, ~: e9 s
                        /* 8 bpp 332 */
3 ^4 v! `% q- n$ y6 k                        var->red.length                = 3;/ S$ U0 o; b/ Q! i6 V' h/ u
                        var->red.offset                = 5;
9 y/ P: Z: H: h) H, _                        var->green.length        = 3;; ~6 p0 K1 C. V' z
                        var->green.offset        = 2;( ~& U) A# B' {  U, x9 r
                        var->blue.length        = 2;
( \( ?; V9 ]" L                        var->blue.offset        = 0;
+ C& R( l3 `& Z, |                } else {  E; X: R0 @' d. `
                        var->red.offset                = 0;& V4 q2 O  `0 ]) s* U7 _- |
                        var->red.length                = 8;
! M' G, l- C4 ?) b                        var->green                = var->red;1 a1 z+ |5 Z9 T- A0 X, C. b
                        var->blue                = var->red;
" M- @' @# _/ |7 I3 ]2 Y                }5 |1 N: p! P" I7 y' \
                break;
0 Z, f# N# W# G3 ~& s- C        case 12:
0 K8 V% D% S% V8 b3 H7 s                /* 12 bpp 444 */
8 P4 I" v# J* d" E2 {* j, c" H' F% r6 v                var->red.length                = 4;
( [" t- C8 a) B: O/ q9 ^. p: }% F/ w                var->red.offset                = 8;: \4 y+ W# o! V% d" {9 D9 a/ P  e
                var->green.length        = 4;$ c$ n, v9 g& j6 M8 B
                var->green.offset        = 4;
8 k( l+ M9 `2 j2 f                var->blue.length        = 4;
4 A3 O5 Z+ a$ J4 P& Q6 v% Q                var->blue.offset        = 0;$ p8 i; J7 g6 A* A9 K, ]9 [- x
                break;
6 q( t5 l" y' j
5 ^- _0 ~2 H2 f3 O        default:
& \1 Q. B& q1 I/ d        case 16:
/ x7 A4 L0 y5 F. T9 x                if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) { /*使用565格式*/
+ U) X, [: ~0 V. C% j" ?5 V                        /* 16 bpp, 565 format */
5 K( p" w1 R" A3 W' J                        var->red.offset                = 11;  \. s6 A0 N, K; Q
                        var->green.offset        = 5;  j$ K2 u$ Z) k1 U+ J, f
                        var->blue.offset        = 0;
: v0 Y  o6 Z" G& J6 T6 X- @" R                        var->red.length                = 5;3 Z$ g, r; }. W# s
                        var->green.length        = 6;2 X+ s+ O& ?) ^) i/ T
                        var->blue.length        = 5;
) s, Y& a2 D( l                } else {
2 O. v4 Q; D& R( t0 X                        /* 16 bpp, 5551 format */" R2 z4 ?2 y% I, k" M' L2 @6 E$ m
                        var->red.offset                = 11;
3 |- y/ x3 `# F$ c) }3 y& I% p                        var->green.offset        = 6;
' c# i( o7 D  i) V                        var->blue.offset        = 1;
: M# `2 ]. G) j; D( W8 I& P0 k                        var->red.length                = 5;$ k+ |+ q7 R' F" ^
                        var->green.length        = 5;1 r5 F/ u& X" {5 T+ B
                        var->blue.length        = 5;
; E3 `1 s4 _" v6 J+ h# U! {                }
( H! ?; L" s. b/ Q+ w. R0 q9 S1 R! f                break;% Y" ~9 ^  s3 ^) [
        case 32:
; U7 Z8 y5 V. J" [$ T% b! q                /* 24 bpp 888 and 8 dummy */$ n8 ^7 V; E: n
                var->red.length                = 8;
. x. E) }( u8 r$ v                var->red.offset                = 16;
5 `8 D2 {* F* W8 ~& B; L) A% H                var->green.length        = 8;) X1 m# L% A. s+ u
                var->green.offset        = 8;
+ u1 U& s5 k% O                var->blue.length        = 8;: b5 ?3 u# F1 }6 V9 l: {
                var->blue.offset        = 0;
0 \( H1 d! L1 L# X                break;: f  o5 i' o" D
        }8 p& Q9 k8 f$ j, [
        return 0;
5 \) I. W& f+ F' f) c}) N* k" V7 V  n

- l/ F5 k; g) c9 {/* Interpretation of offset for color fields: All offsets are from the right,4 G4 V# }/ B8 K. ~" r( o
* inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
& n5 P1 L1 T3 D * can use the offset as right argument to <<). A pixel afterwards is a bit
! ~* z* g0 k! G/ F& p * stream and is written to video memory as that unmodified.: f6 y3 C1 r/ H: j6 P
*/ o+ n2 N+ F2 d4 z3 s
* For pseudocolor: offset and length should be the same for all color
5 n% R: `8 m: @ * components. Offset specifies the position of the least significant bit5 }. M- H2 u# Y5 @
* of the pallette index in a pixel value. Length indicates the number' h/ y- K" x, i( }( H$ H
* of available palette entries (i.e. # of entries = 1 << length).
, b8 A& J. [# w  u6 U9 Y */; M, H) l+ Y( ?5 a6 A, G
struct fb_bitfield {+ q  X$ D8 ?4 Z9 k6 b; b  w
    __u32 offset;            /* beginning of bitfield    */& p/ T4 Z. s. h: N  W
    __u32 length;            /* length of bitfield        */
- \1 _7 e8 Y- I    __u32 msb_right;        /* != 0 : Most significant bit is */
$ z; L1 M, ]5 `- K, h                    /* right */
1 E& L4 A5 V; \. _' J4 U};" y8 }8 S" |' [& z* u+ E
该函数主要将板级信息s3c2410fb_display复制到对应的地方,然后根据RGB的模式设置位域。7 K  U1 s" M& [
4.3 fb_ops方法
4 E0 P& C( L4 g1 o' j# x7 e在驱动程序中,定义了fb_ops,如下:
+ w4 p  g" h+ g. D% u, B5 g
+ d  p: H- o& S- mstatic struct fb_ops s3c2410fb_ops = {
0 V& h5 U# ^9 `, Q" o8 K        .owner                = THIS_MODULE,
5 B) }6 _, [, h  ]& X8 H- f        .fb_check_var        = s3c2410fb_check_var,                /*检查变量的合法性*/6 ]9 M) t! L/ M4 M7 ^
        .fb_set_par        = s3c2410fb_set_par,                        /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/4 u+ m* [+ [% D1 C
        .fb_blank        = s3c2410fb_blank,                                /*该方法支持显示消隐和去消隐*/8 ?, Q3 q: R3 B0 ?; z+ k5 p7 r0 \
        .fb_setcolreg        = s3c2410fb_setcolreg,                /*设置颜色寄存器*/
- x3 t) y) m% d2 @; Z        .fb_fillrect        = cfb_fillrect,                                /*用像素行填充矩形框,通用库函数*/+ Z4 s+ ]; P# A; D
        .fb_copyarea        = cfb_copyarea,                                /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/) a% c, ~- S1 o" R
        .fb_imageblit        = cfb_imageblit,                        /*显示一副图像,通用库函数*/
8 u/ c7 O. Q. e3 i% g6 R! F  o. Z4 ?4 U};
# H/ b, y. U0 `1 T! B! Q1 n3 g9 g$ R+ I# m$ P9 d
其中s3c2410fb_check_var在4.2节中已经分析过了,最后三个方法是通用库函数,在这里不作分析。
) w% v/ {+ W! s5 o( V3 G剩余三个也是驱动程序提供的,现在对这三个程序进行分析。
0 V+ D' o$ |* p4.3. 1 s3c2410fb_set_par$ N& [' Z, _$ b% }: t9 g
/*8 b/ g8 _& z, H( P1 a2 W
*      s3c2410fb_set_par - Alters the hardware state.
- P4 S9 T7 g+ I& ]1 E- X *      @info: frame buffer structure that represents a single frame buffer. I( Z) X; h& R
*                根据var中的值设置LCD控制器的寄存器3 V8 T1 y3 g6 J( B
*/
5 l5 X/ j( F9 p9 u1 s$ M$ istatic int s3c2410fb_set_par(struct fb_info *info)
' m0 f1 i: T# T- Z) v{
/ n8 b  S& S2 E$ ?9 o% y        struct fb_var_screeninfo *var = &info->var;3 \  G5 K' l2 M: R5 G; n" r$ ^4 T2 Q
$ R$ c" C4 ~0 p) O, q% u
        switch (var->bits_per_pixel) {' i5 P  k6 U4 F4 U9 x5 a
        case 32:
, M( q! P0 v% g4 a8 h" o& [        case 16:
2 f$ n6 V9 {# z+ w2 b  N+ T        case 12:
% ]* e9 p' X3 Q/ ~                info->fix.visual = FB_VISUAL_TRUECOLOR;
2 P) c) c7 |3 R+ m) O$ F' m0 }$ g                break;) Q) H7 P% z7 a$ m$ c; {) Y; d; \* q
        case 1:
2 }, U5 m5 H( V                info->fix.visual = FB_VISUAL_MONO01;* c3 F& z5 D; X# k% H+ P
                break;
* u: }. ^& b( @' r# Q& p        default:7 `6 w( H7 C0 Y" R5 k4 G3 |+ u
                info->fix.visual = FB_VISUAL_PSEUDOCOLOR;/ R$ @. D* P( n1 Y$ ^
                break;
% A& C  M) l) }# z- H3 L6 S        }
- b4 F4 C! `, w" B: Z. }4 J
/ m1 e3 @7 y0 d4 s, X        info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; /* 320*16/8 = 640Bytes */
8 @5 C' V0 g: `( W+ s; \! q' f3 k' |1 e% u
        /* activate this new configuration */
, G+ k; K/ P4 w# B1 P7 Q6 o% I- e- s# r3 u  J1 j# m& ?
        s3c2410fb_activate_var(info);2 y0 J0 p  n1 v
        return 0;, |; s" Q& R' @; m0 `; K" n
}  ~( L6 i- L) u1 R( h
该函数根据像素的位数设置了视觉模式,本例为16为,使用真彩色。然后计算了每行的数据元素大小。共240行。
/ \( D/ ]: v' G& {7 N, _- C7 r. P* {9 l+ f/ c0 L, k. i4 u/ O( K
然后调用了s3c2410fb_activate_var来设置控制器并激活LCD。
" E5 c( o2 _2 k. H
3 i* n* z2 X  t4 a1 P3 ws3c2410fb_activate_var函数如下:
7 s8 W0 s$ E5 q
& J- W3 N% m( ^7 L/* s3c2410fb_activate_var. a1 ]5 v- g0 l3 [" e
*
6 P) Z9 r- U) ^4 _2 r% ] * activate (set) the controller from the given framebuffer
2 \, ~' B! u! }  K+ J, E$ U- J. n( \6 n * information4 F7 O+ d( p& s$ R6 u* I% A* w
*/2 t6 q% V9 k) g1 L
static void s3c2410fb_activate_var(struct fb_info *info)* j/ e# D1 u6 X" V
{
9 x2 K2 y' X* n2 f: L  s; a# T9 n        struct s3c2410fb_info *fbi = info->par;
. i, \  D4 k- w$ ], F& _) m        void __iomem *regs = fbi->io;5 O* p: Y8 u) `6 S* m
        int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/; {& K3 E( L# A: l3 v
        struct fb_var_screeninfo *var = &info->var;
9 ]& z  ^% g- X8 a        int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
6 D2 g. b  T2 A8 E4 t1 r8 U/ L/ G5 K' h7 Z) t/ H
        dprintk("%s: var->xres  = %d\n", __func__, var->xres);
5 j% y4 \. C# \# n& {        dprintk("%s: var->yres  = %d\n", __func__, var->yres);
7 u7 u6 l! W: d$ W        dprintk("%s: var->bpp   = %d\n", __func__, var->bits_per_pixel);
* O* ^- J) l8 W* e; j  J
' Y$ |9 _4 e, V5 j        if (type == S3C2410_LCDCON1_TFT) {: r8 j3 R" G) w, ^; a$ h
                s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/
6 Y' j: S1 J. T1 n- |$ S+ ]                --clkdiv;
" t# [3 s8 [5 U                if (clkdiv < 0)# c, ^* i5 _  I% H' L+ I1 t
                        clkdiv = 0;5 W2 M5 z" m  w# w, j: K" `, Z
        } else {
  ^$ K6 `9 A$ r+ Q                s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
# x8 a' y, X& Q% H) o- V                if (clkdiv < 2)2 B( @1 h2 _# J4 w! k9 k5 [( `9 E
                        clkdiv = 2;& p% F7 ]# }1 v$ ]2 [" L
        }( r% a9 P# O2 @& X0 d: }7 \# K, k
1 b: L9 z% e) n8 m7 K1 ^( f
        fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/
. R$ \, M- c  ?% Q) J! L' a
. v% V# O" @% a0 ]' }! \        /* write new registers */
. r# x) \0 N/ W* h9 y9 d) m3 l; Z% Y9 f* B6 S9 C
        dprintk("new register set:\n");
; ^. s' R: Q/ t  e0 H1 B. @        dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
5 P/ T/ c( X# p1 I4 p% _        dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);6 C# l* _1 p4 s+ C% `+ `( D
        dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);7 c5 B2 K9 @* w  ?4 C  H
        dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
( K$ S& l9 Z' M' ^2 j        dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
' z/ `% H# _3 a        /*把计算好的值填入LCD控制器中*/+ c% ^: f6 M2 a. l  X! ^. k/ ?
        writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,       
5 z3 O; p* z( U2 M9 Z                regs + S3C2410_LCDCON1);                                                /*仍然禁止LCD*/
% U, n+ U( b& x: r        writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
' E. i8 ~! S* {( c+ V( x        writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);! D6 H% S) f$ O
        writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);6 m* h  R% |; _1 u8 U
        writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);( l9 q0 G% L! g1 ]( E3 F6 X& W

8 R' ^1 ~0 q8 v$ b$ u        /* set lcd address pointers */
+ D5 t! n- P' W5 z+ F; a        s3c2410fb_set_lcdaddr(info);                                                /*设置LCD帧缓冲起始地址*/
% V. q5 L4 }0 Z& v; w' F7 k$ U& K# {9 c5 N  P: Z0 n# Z3 W3 X" W
        fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
$ M9 I6 k- w& ^. s+ `3 ~1 I        writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);        /*使能LCD*/
- V# s% E7 G, S4 O2 ]}% V2 v4 y( L) s8 ~9 E. v5 B* W. Y
其中调用的三个函数如下:
# [2 t3 p" h) }, A9 ?static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,7 m8 s. @3 r9 H. A
                      unsigned long pixclk)
# x( v9 C# H& x& ^{
/ p' A, {* x' a: L+ p4 X8 j, Q# k    unsigned long clk = clk_get_rate(fbi->clk);         /*获取当前时钟频率(Hz)*/% f) B  ^( S  }% L* [7 f& J
    unsigned long long div;
, u( N* M3 H0 Q4 V7 h( W
6 G* g7 B# t- H    /* pixclk is in picoseconds, our clock is in Hz
0 L& a3 z  u# M4 v$ R) m, S     *
+ b8 t& k7 g1 T; e: O     * Hz -> picoseconds is / 10^-125 m3 Y# |4 o9 Q4 L# \5 Y/ a
     */' I1 p, Y% R8 S$ A& U* _. J
5 C) j# q( u" B$ K4 g4 S
    div = (unsigned long long)clk * pixclk;
; b$ Q$ [! g% C/ f& V" @( t    div >>= 12;            /* div / 2^12 */
. T- j+ q1 y, E  R    do_div(div, 625 * 625UL * 625); /* div / 5^12 */* @) X+ c5 z5 C' c# x
6 Q+ A3 G0 D* U3 _  I$ @
    dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);, l5 y# F' _! t' I, _( K
    return div;
$ N8 t$ P7 u" a, B9 I* n}; \0 D- ~5 B. ~/ A# z
( t3 ~* O, S3 k9 i# {9 Q, M
/* s3c2410fb_calculate_tft_lcd_regs
! e2 V! E! ?4 v *& l! b* z+ n4 z3 G4 a& S
* calculate register values from var settings
5 `9 i0 P1 A* R/ K */7 U" n  s* `3 {( l& O5 l
static void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info,
' {* `" Y% K, }                         struct s3c2410fb_hw *regs)/ g% d1 h4 s; q2 r1 ^
{
1 Y6 ], f2 v. T$ K    const struct s3c2410fb_info *fbi = info->par;$ V5 W- i4 L: V0 g) C
    const struct fb_var_screeninfo *var = &info->var;9 S- T  j' q- A$ e& x3 w, U

: P( U( X" ^! E$ P    switch (var->bits_per_pixel) {3 H  k$ a! Y1 \& T0 [* f+ B
    case 1:- q' |" Z# b8 K7 v
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;4 f! F: d) g! B* S7 X# i
        break;
( l6 P! k8 f& ~6 U' E7 E- x    case 2:6 W: w* l* u" J5 k: U  i' h
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;
) n& L. P4 N* v* t1 Z  Z        break;  J0 c+ |$ d  r3 W' |" k
    case 4:1 x2 T; J, J( A" |) j/ }
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;
! n0 R$ z$ u. L. a        break;
) n. B8 |2 j1 B, _3 c+ d    case 8:
6 b) V5 T) U" X0 v2 Y% \: A* x+ R6 A        regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
, r, q! d5 U! _4 Q        regs->lcdcon5 |= S3C2410_LCDCON5_BSWP |7 U* a4 ^& {: r# C4 m- f7 d
                 S3C2410_LCDCON5_FRM565;- i$ i0 @& L; C7 [4 f5 L. l0 n; O
        regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;% n8 q$ q; k& M
        break;9 a3 |2 V7 |3 Q& o& \
    case 16:" R1 V0 f6 y0 u/ Y+ o
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;
2 o/ p# t: m* `3 {& W7 T6 l        regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;* H" n  Q9 @; a7 {- r# V
        regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;5 Q' l) n# L# D1 c+ l* j9 G
        break;% s4 s8 w: G; b5 v, C; F
    case 32:, v1 V; g$ p$ m, K; K6 |$ i& R
        regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;" v9 s! \' O( E8 Z4 @: O
        regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP |
9 t- R( e; H( P" g) d; }' T                   S3C2410_LCDCON5_HWSWP |; s* Q0 I; q8 ~* v. U' e
                   S3C2410_LCDCON5_BPP24BL);
6 G4 E9 g9 ~2 D1 x% u        break;
( Q; }8 w, P( u3 N  z" o    default:
8 ]6 v$ G" y& c/ e4 a5 w  f        /* invalid pixel depth */
! t+ P" h7 e( }        dev_err(fbi->dev, "invalid bpp %d\n",/ A# {% {+ h8 S) D* C. V, k) t
            var->bits_per_pixel);
+ L0 v2 g/ X) u/ `    }% w4 d9 y5 I/ A4 d! f" F
    /* update X/Y info */
2 G8 z( s) L. Q6 `$ e  Y    dprintk("setting vert: up=%d, low=%d, sync=%d\n",
: b" E1 b. A- X        var->upper_margin, var->lower_margin, var->vsync_len);
) Z5 l1 |( d4 w6 F# K+ \+ s2 n1 ]- y1 Y+ v  v- }+ O
    dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
7 _3 b, x3 n3 U3 {2 ?: g1 v7 m        var->left_margin, var->right_margin, var->hsync_len);, \6 \2 }4 w# c
    /*3 W: h. h4 Y9 X
        所有时序参数必须减1,因为在公式中:' S2 E9 |- a' x! z
        Frame Rate = 1/ [ { (VSPW+1) + (VBPD+1) + (LIINEVAL + 1) + (VFPD+1) } x {(HSPW+1) + (HBPD +1): @7 P" x/ F" R( ?' ]# I# X) {
        + (HFPD+1) + (HOZVAL + 1) } x { 2 x ( CLKVAL+1 ) / ( HCLK ) } ]7 N9 _& t, r6 w
    */. e0 l$ T+ e0 K2 e! D
    regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |
) Q" y$ J; B" x) ?) N$ q            S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |
* ~4 ~* f; z9 x" _# C/ f            S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
9 O5 z9 E1 @5 D' s- F) e, r) Q8 [            S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
% J* |# t& J, g) k; X& e  _* r3 l
) c: y* d; g7 e3 U    regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
  T0 j3 s- X+ `3 g! S            S3C2410_LCDCON3_HFPD(var->left_margin - 1) |
% j" g) l8 e' m6 z: }            S3C2410_LCDCON3_HOZVAL(var->xres - 1);
0 }4 P# e# v0 X  K3 F" e$ v4 x
# Y& K2 l  N( N; W& E0 M8 ^9 L; C    regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);6 q- T5 b& B0 {
}
7 F$ R% L% c4 V: f  b) o" f' R
: u/ P/ F9 l6 `$ H/* s3c2410fb_set_lcdaddr) H8 @3 }/ E4 s; L7 T
*
) C- s. }7 k* c. t * initialise lcd controller address pointers
6 H1 W3 P2 D  ~7 h0 X& i */
/ p$ x" G8 a6 ~4 l1 a+ Ustatic void s3c2410fb_set_lcdaddr(struct fb_info *info)( g# R( @# ^; H
{
0 C( `0 |1 [( c, S, h' p        unsigned long saddr1, saddr2, saddr3;3 n. l* O# M, h, Y
        struct s3c2410fb_info *fbi = info->par;& i2 @- h9 N" R, d6 T
        void __iomem *regs = fbi->io;
( A& \$ B7 B3 l. h5 @, U; L* w: A4 M  l; a7 \3 \2 f2 v- e/ @+ g
        saddr1  = info->fix.smem_start >> 1;          /*帧缓冲区起始地址*/6 J, \  I  @( {, F
        saddr2  = info->fix.smem_start;                       
) @  [7 v9 i+ \: U/ M7 W& [        saddr2 += info->fix.line_length * info->var.yres;
! b6 L+ A! X: x8 q  C6 `        saddr2 >>= 1;                                                        /*帧缓冲区结束地址*/$ _/ B; K( j( _- {+ h

$ G0 a4 j, U3 u        saddr3 = S3C2410_OFFSIZE(0) |: n( C- g, n2 {
                 S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff); /*offset = 0, pagewidth = 320*/
$ O2 X7 S; D4 j+ J5 z8 B- I1 G3 b
% d% t5 B8 s0 f0 a        dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);2 O7 Z+ q' c2 d2 ?0 P
        dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);
& ]! f% J! v5 J2 g        dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);
3 X2 G$ n' b5 a' b* P) M/ z; C
8 K  @; M) w" o; ?: l        writel(saddr1, regs + S3C2410_LCDSADDR1);% f$ I8 u8 t, F+ F, ^8 F8 r; T
        writel(saddr2, regs + S3C2410_LCDSADDR2);
5 a$ \! `% J  R8 R  P  ~        writel(saddr3, regs + S3C2410_LCDSADDR3);
) l8 h7 e9 v9 K1 f# ~( z' X( n}
1 f( ?( z8 g+ F. K: f7 D5 As3c2410fb_calc_pixclk用于计算时钟频率。
3 @7 c1 e/ _2 \% I" F9 G# @* v! Q9 l4 G- q# T* Z
s3c2410fb_calculate_tft_lcd_regs设置了LCD的控制寄存器。! S, r6 h7 q  K0 ]. h9 ]  p9 c1 a* G

% u  E4 c5 g1 m. ds3c2410fb_set_lcdaddr设置了LCD的地址寄存器,该寄存器的设置请参考datasheet。: J, J, S  o  E) O+ ?  _9 W0 D
; q5 g6 Y1 N0 ?7 g- c, F7 ]
4.3. 2 s3c2410fb_blank; {" n+ i7 u  y0 I& s1 o6 A% e
该方法完成消隐功能。) [' x/ a& i% S' y7 ^9 O. E" Z6 }

- W8 g3 o7 L; W3 P6 H: t( Y! `/*. T1 Q, s+ D8 E3 [  t% V
*      s3c2410fb_blank
" J# b* b5 ?1 C *        @blank_mode: the blank mode we want.
" n" [3 _5 v/ n *        @info: frame buffer structure that represents a single frame buffer
8 C& J- y: {" N! _+ \ *) l% F) I* E) c0 a2 T
*        Blank the screen if blank_mode != 0, else unblank. Return 0 if
( _7 E3 ~1 ?5 c' c' m3 q *        blanking succeeded, != 0 if un-/blanking failed due to e.g. a
: n. O% q! w- t# }4 y: [; c *        video mode which doesn't support it. Implements VESA suspend: L% A0 E$ R$ y
*        and powerdown modes on hardware that supports disabling hsync/vsync:
6 m6 ?2 L: i6 v9 |9 x2 v */ Z* ]5 a/ }) [' b
*        Returns negative errno on error, or zero on success.
/ T8 ^( C! \! T( L *! q3 o% C7 C7 b2 @% O
*/
4 Q7 i: n) U' W5 sstatic int s3c2410fb_blank(int blank_mode, struct fb_info *info)( L3 ]) p) [% x  [
{' i1 L: T! g  Q8 m( E5 Y
    struct s3c2410fb_info *fbi = info->par;5 @( A2 R( o6 k! [" o0 T
    void __iomem *tpal_reg = fbi->io;
, c1 h7 c3 J; t6 h1 v% w
6 |7 `' g) w. S- K: o9 W    dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);  @0 t1 \+ V6 l+ i4 R
0 v3 |/ x5 G3 z. c3 M( i& Z
    tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;
$ i" G/ ?" N* H- L+ J3 D% u3 h% T
2 K% u5 V3 N" |' L/ R    if (blank_mode == FB_BLANK_POWERDOWN) {    /*消隐*/) n3 O. y+ u- |4 {8 z, e
        s3c2410fb_lcd_enable(fbi, 0);        /*禁止LCD*/
7 l4 m; G8 A0 j$ V& w    } else {7 Q' f& g# A/ g: C7 p- R
        s3c2410fb_lcd_enable(fbi, 1);        /*启动LCD*/) }2 e- {2 p" |$ Z1 K5 r9 U, ~
    }
4 V: q( w- \# Q  S4 E# j
/ }  K+ e9 Z" Q* Z/ K  l9 J    if (blank_mode == FB_BLANK_UNBLANK)        /*去消隐*/7 N' i% h$ u) Y2 H5 h
        writel(0x0, tpal_reg);                /*禁止temporary palette*/  d. E% A: C% `7 w
    else {                                    /*消隐*/
' u. t; r# q5 Y4 H% Z        dprintk("setting TPAL to output 0x000000\n");$ ?% w/ ?: P( ]6 U, b6 z$ e
        writel(S3C2410_TPAL_EN, tpal_reg);    /*使能temporary palette,颜色为黑色*/
4 e8 {+ [2 f3 U4 _/ C5 F    }3 W" s$ Q  J: m
  \3 k0 T" Q* D: ]  n$ U
    return 0;
2 _+ M# m. ?! w5 j( G9 ~! N# v}. Y! u; [! \$ F& ?! \
在消隐时,屏幕将全黑。- V% J' ^0 @( p9 ^, F# S: ^
2 D' i; t% ~" o! N
4.3.3 s3c2410fb_setcolreg
  e8 |5 r" M! e5 `, m该函数的功能用于设置LCD的调色板。调色板的概念请看我的转帖:LCD调色板。" j0 ]/ x. v6 ~( Q

6 m! j) g5 |  c. |. a4 N1 Zstatic int s3c2410fb_setcolreg(unsigned regno,
+ T5 M& k4 u! g& j                   unsigned red, unsigned green, unsigned blue,
2 \" y' X/ Q  H/ N( I- D                   unsigned transp, struct fb_info *info)
3 j: r1 z$ \) Z5 H5 Z{
. J! |7 D3 ~- ?& D% I, w+ @# ?    struct s3c2410fb_info *fbi = info->par;    /*par指向s3c2410fb_info*/
) a' z9 z0 m8 B; u. |    void __iomem *regs = fbi->io;
, f" |2 r/ e' Z( @: a    unsigned int val;
! Z) ]! t9 C) l! h1 D6 B. A) P& I& X  V! L. }+ w
    /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
$ j, R+ f. p: w( W. {, i           regno, red, green, blue); */
. V( e$ Y9 g6 z4 v' D! w
" ~8 e8 A6 t. @6 O0 S    switch (info->fix.visual) {    " j, I+ R) S& x* `4 F
    case FB_VISUAL_TRUECOLOR:    /*使用真彩色*/& p$ T( U3 X+ \3 @1 ^6 l
        /* true-colour, use pseudo-palette */, V5 h, Q+ b  ^1 N* |8 I/ U% E" o
1 u. c; ]/ R. W. e$ y# B8 t" r
        if (regno < 16) {    /*16种颜色,为什么只用16种颜色???????????*/
& X& W2 @6 k+ {8 e            u32 *pal = info->pseudo_palette;
  w) v$ N5 P+ P8 Z5 N3 M4 ^
: F; s+ N- m2 t" ^7 M+ W            val  = chan_to_field(red,   &info->var.red);
* [# ]7 ?) M) \% V            val |= chan_to_field(green, &info->var.green);% U5 I" A/ c4 y3 h: {* d
            val |= chan_to_field(blue,  &info->var.blue);8 S' Y0 H9 T4 ^. h  B! _

. J( r7 o7 L7 V% n+ Q& M, k            pal[regno] = val;    /*保存颜色值*/7 I' {8 R. G9 R6 H) Z2 H3 |0 L
        }2 e; [( y4 I3 @3 m% S
        break;
9 n3 H, Q/ [7 l7 _8 x! @
% L- k' H) E; ^& M9 T( b    case FB_VISUAL_PSEUDOCOLOR:  G' y/ ?" M  {$ |# i# K, _
        if (regno < 256) {
" R8 B! i' e/ o( i  w" I            /* currently assume RGB 5-6-5 mode */
; p2 H2 H' A5 T4 p% R5 L7 J1 [3 i  @. O5 M& c4 |
            val  = (red   >>  0) & 0xf800;+ b2 h3 O8 e% w- M! E& }
            val |= (green >>  5) & 0x07e0;
% Q4 L3 w) }) S$ r9 A" V4 I            val |= (blue  >> 11) & 0x001f;6 m' Z/ M& a6 y" e

8 b7 g$ a% g( u$ k4 p            writel(val, regs + S3C2410_TFTPAL(regno));
: ?5 \7 z( b/ m* N7 j! v            schedule_palette_update(fbi, regno, val);
3 w6 n- Z7 s$ C        }' ~7 A0 a' h. \8 M. V; K
/ Z1 i- f) [, ~% T
        break;
9 p+ g5 C9 n) I2 M! K  R6 o
, `" e# y8 H* j0 Z8 K3 F2 `. N+ o    default:
3 D7 g! C1 M% Y# {( l9 Z& [        return 1;    /* unknown type */- S3 @# {! i  T3 j) y4 z
    }
' i1 S* b4 V* ?; w0 K( C, @5 V5 N1 p/ c3 |/ s6 t  O' K
    return 0;
, W1 c. \, P3 g1 W* f}
7 b; S: w* b1 z+ B7 {4 z$ f  i/* from pxafb.c */
8 b2 A0 Y" L$ l: Zstatic inline unsigned int chan_to_field(unsigned int chan,: \: @) D; |- W9 Z/ n7 {
                     struct fb_bitfield *bf)
3 R* V4 u! T1 ^! [* u% Y{    1 P( R% w  T" k; V( q" k
    /*下面用到的length和offset在s3c2410fb_check_var函数中设置*/
* V) E8 Z; z  w    chan &= 0xffff;                /*取低16位*/$ p; d; N) v$ E+ h0 s
    chan >>= 16 - bf->length;   /*取第length为到16位为有效位*/$ a! _2 s' D5 Q  ?7 S6 b$ v6 A' E
    return chan << bf->offset;    /*移动到相应的位置。*/
( K6 ^9 S: w; S/ L. [}
% }) Y0 l5 y+ C我们使用的是真彩色,可以设置16种颜色,颜色的位域值通过调用chan_to_field获得,然后保存了颜色的值。但是比较奇怪的是,这里并没有将值保存到0x4d000400为起始的内存中,不知为何。 反而倒是在伪彩色模式下, 使用了writel(val, regs + S3C2410_TFTPAL(regno))写到内存中,然后调用schedule_palette_update函数来更新palette表。我们来看下。% K, Q6 [5 N/ Z; A; @( ]+ s

; V3 g, C: A" M" z5 M. Pstatic void schedule_palette_update(struct s3c2410fb_info *fbi,
* ]# q1 x* q) X. \& f5 S( L                                    unsigned int regno, unsigned int val)
9 O/ O# v# y5 L- S{; w# ?- \! Z4 p2 `' |. A! T; \
        unsigned long flags;! ]5 U$ ^: z$ a8 K1 m  C
        unsigned long irqen;5 L" ^$ A3 G0 Q7 \6 @* `
        void __iomem *irq_base = fbi->irq_base;
% v7 T# o' Q% ~8 G5 n5 _$ s
) H1 F9 V* E4 T2 g" H) j3 ]        local_irq_save(flags);
: U, j7 L% ?- x  t* r
3 ?7 E# B- W. M        fbi->palette_buffer[regno] = val;
1 N6 H9 Q/ h( g" c$ K
3 u  ~6 J1 [' S5 e$ Y- \        if (!fbi->palette_ready) {) y! y8 k3 G8 ?3 \9 a- ^0 z; j
                fbi->palette_ready = 1;5 ^( _7 c0 p; f# g, D, V$ T* u

1 O0 [/ n. v/ B, U8 w0 ?) z7 @  A                /* enable IRQ */8 Q1 ^! l1 b  j6 ~1 ]+ \
                irqen = readl(irq_base + S3C24XX_LCDINTMSK);
2 p: W3 G$ Y0 p* `7 O2 o! n                irqen &= ~S3C2410_LCDINT_FRSYNC;
" k  L; d& p: k4 k$ k' R; j+ W                writel(irqen, irq_base + S3C24XX_LCDINTMSK);6 ]: i! d, {; Z6 B$ j% y0 o3 t
        }
" A( q3 @  V9 R2 n
7 {4 I( X% @+ f7 g4 @: d5 x        local_irq_restore(flags);
, [; g8 ?" d9 d8 f5 v}
$ \% ?- _; a! C8 t) e$ F  y这个函数的作用就是开启了LCD的帧同步中断,这个中断在VSYNC信号从无效变成有效时产生。当中断产生时,会调用在probe方法中注册的ISR。ISR如下:
* \# x& c" _( L: y, F  G1 Nstatic irqreturn_t s3c2410fb_irq(int irq, void *dev_id)) J9 z4 E1 C6 x" E6 M; h5 |/ z
{
0 i0 L6 }- [4 F- ]  F        struct s3c2410fb_info *fbi = dev_id;6 \1 j# ]4 B& r
        void __iomem *irq_base = fbi->irq_base;
+ l6 Q! `% r+ H" g9 w0 k' q        unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);0 \' q7 F, _9 a* k/ S) t
b
1 t1 L( D  h% ]! b% g* D7 ?        if (lcdirq & S3C2410_LCDINT_FRSYNC) {
. \0 S1 i+ e6 l, M3 E; t                if (fbi->palette_ready)
: K6 s3 q3 ^1 {! F4 ?( N7 n5 y( S                        s3c2410fb_write_palette(fbi);
& ?* ?& I2 J- B9 u2 G' ~! r3 P: z
! ~3 ], r' w; o, ]8 n( s7 v                writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);% [) F* k7 h& [& I3 T& n
                writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);
6 C$ o! Z6 ?" E6 o        }, {# E3 [3 j- ?0 l
, n, r: U7 e% b) X
        return IRQ_HANDLED;6 M, F" s9 @/ Y# {  ~! s4 T6 R
}8 t. z5 k7 }2 y8 J4 _) T

0 J- R- [$ {; ~$ C3 \$ tstatic void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)
1 z2 q" [. ]4 H{
7 m# q) e9 D- p/ r- N    unsigned int i;/ M" V1 G! N  O- G
    void __iomem *regs = fbi->io;
/ @% y$ K$ ?3 X) k$ b
/ i7 J5 H0 u- ^/ B8 n    fbi->palette_ready = 0;  /*清除ready标志*/
) x6 V0 \0 P$ a  P# _& ^. s: H9 S9 D
    for (i = 0; i < 256; i++) {; ~& l( j! ^, O3 Y* P
        unsigned long ent = fbi->palette_buffer;. P0 O* p% [! T; M6 ^
        if (ent == PALETTE_BUFF_CLEAR)
& B* A; F+ `# a# C' }3 [            continue;7 o4 L, _5 N  u3 ^% o
) O; \: ?: J# Z+ d" E# _6 Z
        writel(ent, regs + S3C2410_TFTPAL(i));
( @8 Y/ U( f% [" [& S  N! j& Q7 q8 `0 q: v6 W/ O# z+ C! x$ ^
        /* it seems the only way to know exactly8 Y0 O6 j8 F; ^  z& ?- {) d' R4 D9 l5 T
         * if the palette wrote ok, is to check
% g/ V# b+ m% m; Z& f         * to see if the value verifies ok  \; X( @. d  t: @, f9 B8 P
         */
) H" G  d" ]( Q+ E
2 m8 V  U9 a- d% V" e        if (readw(regs + S3C2410_TFTPAL(i)) == ent)
3 Z5 L1 t- z: Y$ G2 F. m# `            fbi->palette_buffer = PALETTE_BUFF_CLEAR;" P8 L& y$ K, @' A/ z$ O
        else
, Y% L8 k3 o4 |$ z; R; L' T            fbi->palette_ready = 1;   /* retry */
% M; j3 c& M5 q& }    }
) c" y# Y- m  b9 t$ p, B' A" B}
8 }& W% s, h. s+ g2 T* [. j6 ?6 p在中断函数中调用了s3c2410fb_write_palette,该函数对写入内存的颜色值进行检查。如果确认其已经写入,则将palette_buffer中的清除,否则retry。* a, F3 s# _2 O: @, n* x( ?
# n% |4 X' ^' d( o4 p
5. 总结
2 H/ Y; q" v( m# {! c本文对frambuffer子系统做了简单的介绍。frambuffer子系统的函数相当之多,在这里是不可能一一介绍的。本文首先介绍了主要的数据结构,
8 E0 \' a. e; N# [; N. H( w
0 h2 i7 u0 w4 x# ~( }3 |5 J  i" D随后分析了frambuffer核心层,在核心层简单的分析了5个API函数,接着,对驱动层做了介绍,驱动层的大多数函数都给出了分析。
+ G% o  N. f! Y2 ]  f" D
1 f$ q# J2 P% A: a  S4 G  l
" e2 d) V7 e8 a; Y3 U# ?, |
* h8 J9 a3 F5 i3 V0 N0 ~! r& u9 k, T/ H2 B
; S: h: N% `' B2 E! _( ?; w6 \) P
# j0 M& g9 c. Q

# ]. g2 J7 [. y# z8 S4 T4 _" b6 ?+ {

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-10-26 21:10 , Processed in 0.281250 second(s), 26 queries , Gzip On.

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

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

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