|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
本文将介绍Framebuffer子系统
$ n8 b( e( I8 y, T* S, _8 o# W3 N! P1 F8 s
目标平台:TQ2440 CPU:s3c24406 S( K- ?5 R* w5 M
* Z- s) ?$ Q d3 u1 w5 w) _( xLCD设备:3.5英寸,分辨率320X240; t7 p2 }0 e( Q* d: W! h
% p, U/ S: t% O1 G/ W2 d
( x! H; E7 z9 s9 t/ `2 U
0 e* l# l( m4 r1. 概述0 G" J" o; n5 {; Z. E
/ J! z( J4 G: Y1 |" U1 P) C
Framebuffer,中文名字是帧缓冲,这个帧也就是一副图像所需要的数据。因此,帧缓冲其实就是LCD设备的驱动程序。Linux中,framebuffer子系统框架如下:
z l+ D2 n8 V, E" }; i) U5 a* q% B/ ?9 e5 o' W5 d
; B2 h7 f- q( }4 X" F1 W# g6 B1 ?
+ X; R p! L6 i* G3 J: i$ n/ [8 H核心层的代码以fbmem.c为主,核心层包括许多与具体硬件无关的代码,并且提供了API给用户空间。用户空间使用系统调用,系统调用会使用相应的API函数,最后会调用驱动层实现功能。对于不同的设备,驱动层的代码将有所不同。
, t' S7 R1 W4 {2 e0 x0 z7 x' I) L0 C# X: f
接下来的内容中,首先给出framerbuffer使用的数据结构;随后简单描述framerbuffer核心层;最后,针对S3C2440,对驱动代码进行分析。
5 V1 D) X9 f; d2 b! s' D, H- ^9 ~" V) |7 F f. ]4 O
2. 数据结构
5 \' z, |7 d7 L! P9 m/ ^2.1 fb_info 结构" a+ Z9 R8 T: f: W
该结构是内核用于描述一个特定framebuffer设备。其中包含了几个重要的结构,将在下面介绍。
8 M8 U$ L4 y6 l: \! G8 ]- }- ?1 V) H1 p. x1 o8 s* Z
下列代码位于include/linux/fb.h
( n+ k3 E9 e8 L, D9 H* V9 o% E: a; O. y- w' c9 I- _* G2 ? k
struct fb_info {$ N: {( Y" C+ |; T8 H' I
int node;3 i' n* C3 w' V+ W
int flags;
, r0 \7 F# X$ ~# G$ W' l+ B' d$ s struct mutex lock; /* Lock for open/release/ioctl funcs */. p2 y: ~% P% p. g: m
struct fb_var_screeninfo var; /* Current var */) `; [0 P! v7 d; w3 H8 \
struct fb_fix_screeninfo fix; /* Current fix */
, G" ?/ u8 K, |3 [: `$ f struct fb_monspecs monspecs; /* Current Monitor specs */
9 D. V, Q$ {$ P) \ struct work_struct queue; /* Framebuffer event queue */- O% Q0 |# t+ i- {: n+ D4 }
struct fb_pixmap pixmap; /* Image hardware mapper */
! @0 H* j; S6 d" g, g1 f struct fb_pixmap sprite; /* Cursor hardware mapper */
0 x0 S7 [+ C# k2 H3 ^0 V struct fb_cmap cmap; /* Current cmap */0 a% i( Y+ }# ?7 Z, w2 H
struct list_head modelist; /* mode list */4 Q4 }8 [+ c/ q1 }# y$ J% ~
struct fb_videomode *mode; /* current mode */% n* G/ x) H5 B& ^$ H
' n0 q7 ?, N: ^. b; t0 I) l% G#ifdef CONFIG_FB_BACKLIGHT
8 m- i; g6 _" ]: d7 U; L /* assigned backlight device */' s6 t% e# o0 @# u% P
/* set before framebuffer registration,
7 z6 A, U [( d remove after unregister */
( o3 ]' O- x5 b6 R5 c6 G9 D* I) _ struct backlight_device *bl_dev;
) {1 l4 F, h: y& z z9 s0 V
6 X/ ?- i `; B' } /* Backlight level curve */# T7 j) V' W% }& J
struct mutex bl_curve_mutex;
( R8 s9 V1 d7 A! r% o8 {* B" `$ O' q u8 bl_curve[FB_BACKLIGHT_LEVELS];1 f, b9 X- L m8 ]2 e% ]
#endif
7 K3 D( K7 l$ p; y#ifdef CONFIG_FB_DEFERRED_IO. _3 {, T( v; W+ k5 ]2 B
struct delayed_work deferred_work;
3 q* S& m# U5 D: z/ F struct fb_deferred_io *fbdefio;+ @/ E, W" A) R: G3 g0 \: m' S: O
#endif
: ~0 S9 W9 J' q- j2 c: f7 A8 {$ r2 g1 e' Q/ @2 I
struct fb_ops *fbops;
; a2 ]4 V% b6 `0 h; { struct device *device; /* This is the parent */
M' M5 P; ]% h struct device *dev; /* This is this fb device */, j+ Z& Q4 v& V; w) d3 b( S4 v, \0 d
int class_flag; /* private sysfs flags */
& e' J2 U: Q% w' w- Q6 @#ifdef CONFIG_FB_TILEBLITTING
D$ l* A7 m. T! j$ v' F struct fb_tile_ops *tileops; /* Tile Blitting */; G# B& W" H' E" S* z' F
#endif
7 D# c. q7 Y {$ J, @# q* h& P, Z char __iomem *screen_base; /* Virtual address */* G/ e6 W7 C/ `9 [ B/ E
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
: Q( Q& E; ]& k A6 j' g void *pseudo_palette; /* Fake palette of 16 colors */ 7 r) A9 X2 E0 y, S
#define FBINFO_STATE_RUNNING 07 P6 M/ I# y& o1 T
#define FBINFO_STATE_SUSPENDED 1+ z- Q2 q1 [8 T8 Q
u32 state; /* Hardware state i.e suspend */+ Q+ S! H. [6 T. u) ~4 O: Q4 g
void *fbcon_par; /* fbcon use-only private area *// c: M! [" M6 o
/* From here on everything is device dependent */, u! N# W9 a/ Y4 |; F* f* g% X- a
void *par;
Q- n/ O! W' o) f};! c) l) a- g" ^: C3 K
2.2 fb_fix_screeninfo结构; b" o0 E3 E+ c! E- r2 C [1 i, C
该数据结构是不可改变的,也就是说用户空间不能改变该结构中的任何成员,在核心层我们将会看到这点。* d r% m; L4 ?! I2 x3 L
2 i- H) `& |9 k+ B# Y8 Q
下列代码位于include/linux/fb.h8 m+ p* U6 r8 K6 H7 F' U( y. X
$ e% @2 B' w4 L9 w: d
struct fb_fix_screeninfo {' i( X2 I" u6 }& B4 R
char id[16]; /* identification string eg "TT Builtin" */
- h5 p' b# H1 _8 i4 Q* F7 @ unsigned long smem_start; /* Start of frame buffer mem */' y: {/ ]: C% v: s, s
/* (physical address) */
7 L0 \! r9 ?6 U' m! d* ~5 U; l, z5 K __u32 smem_len; /* Length of frame buffer mem */! _ f* z, E- l' _4 P" W6 [7 ]
__u32 type; /* see FB_TYPE_* */; m9 g3 C. \* L R
__u32 type_aux; /* Interleave for interleaved Planes */
& O$ K) b0 Z. [' `) |4 s __u32 visual; /* see FB_VISUAL_* */
9 l6 B' g) S" e! _ __u16 xpanstep; /* zero if no hardware panning */4 S; ~ W& [8 B( X5 s/ {0 v. }
__u16 ypanstep; /* zero if no hardware panning */
9 E- B) j! ^; {; m __u16 ywrapstep; /* zero if no hardware ywrap */
! F6 U: ~8 }; Y6 u, ~/ a4 @1 o" w __u32 line_length; /* length of a line in bytes *// G3 [8 B" G2 B) B
unsigned long mmio_start; /* Start of Memory Mapped I/O */3 F/ p( {& A; v9 q8 D$ _# |
/* (physical address) */
" f1 n5 d! G" ?" D8 G __u32 mmio_len; /* Length of Memory Mapped I/O */+ [. x6 d/ h2 k1 x9 [
__u32 accel; /* Indicate to driver which */
# t J& R4 N7 P( X /* specific chip/card we have */
# q+ T: w% D1 X __u16 reserved[3]; /* Reserved for future compatibility */$ [6 @% \# q* d
};
; ]7 |* l w* w* B3 @& _" B O2 H- K- Z' A
2.3 fb_var_screeninfo结构
- S9 D6 N, i1 A5 H 该数据结构是可以改变的,也就是说用户空间可以改变该结构中的成员。该数据结构中的很多成员就由板级信息复制而来,在驱动代码中我们将会看到这点。4 A- j8 i! `9 w3 z5 n) J; A% ]
" X& d) p9 |3 \# ?# ~0 P, n 下列代码位于include/linux/fb.h; ^* a% O, g; C. j
. H+ O" y+ l8 a6 E( {struct fb_var_screeninfo {
+ l- c) l/ z7 q, ?+ S$ B7 [ __u32 xres; /* visible resolution */
1 q) \8 U- c+ b3 O3 \9 [- |; O __u32 yres;
+ ~3 X# @' p; { __u32 xres_virtual; /* virtual resolution */
6 F; w( \- z3 D1 S" o8 B/ W __u32 yres_virtual;1 C! O& a( G" k) W0 T' v
__u32 xoffset; /* offset from virtual to visible */
5 C* @+ `+ @2 N: P4 Y# N" B4 z __u32 yoffset; /* resolution */. @8 @. R7 W: {1 a
2 D- [* B; E' m" Y! X! p
__u32 bits_per_pixel; /* guess what */
V& S3 Y, r- G) g4 W. R __u32 grayscale; /* != 0 Graylevels instead of colors */: @: R' [* z. s8 ]
* ^9 s) B3 ^; V9 |5 p struct fb_bitfield red; /* bitfield in fb mem if true color, */9 ^0 |: P5 b3 e( a, C. k) ?0 t6 t4 g
struct fb_bitfield green; /* else only length is significant */' @- f) P2 Q8 w0 ^+ M6 y
struct fb_bitfield blue;5 \+ ` m6 h, ?9 m& R2 b; j! f- \! C
struct fb_bitfield transp; /* transparency */ & a# I9 y9 G" n/ C/ ~
$ ?; q7 D- L5 e( e) T& O0 w- J __u32 nonstd; /* != 0 Non standard pixel format */
' u5 o* a1 i0 J% ~1 e* o7 B6 p( _4 s% V. J! ^- ^' F6 O6 f$ D& l0 b, G. M
__u32 activate; /* see FB_ACTIVATE_* */
' s0 H. V% z1 f
8 I2 Y/ \' L' m __u32 height; /* height of picture in mm */4 i1 S" ~" t C9 t$ o; y
__u32 width; /* width of picture in mm */' ?# u8 \& V1 c4 O$ Q; v
+ v h0 C) A* F* f; M/ C
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */( I2 y/ y3 R6 X1 l( Q
m) ]. l. _! i# H' P d
/* Timing: All values in pixclocks, except pixclock (of course) *// _( O3 U8 H# _7 t* X
__u32 pixclock; /* pixel clock in ps (pico seconds) *// f9 Z7 S% Q8 Y& w7 O+ O
__u32 left_margin; /* time from sync to picture */: `5 J- A( w+ }, s
__u32 right_margin; /* time from picture to sync */
7 p5 ~4 R& l# Y0 V __u32 upper_margin; /* time from sync to picture */
! W0 O7 t) M! @+ b- C% p! D! s5 r __u32 lower_margin;
k& n3 g3 e8 N. v" I __u32 hsync_len; /* length of horizontal sync */ X3 {2 \0 i7 x+ q* H: p3 V$ o
__u32 vsync_len; /* length of vertical sync */
4 \% n6 ?8 D1 P/ U( i __u32 sync; /* see FB_SYNC_* */$ @# H* Q! T6 t& i
__u32 vmode; /* see FB_VMODE_* */% n6 A' p0 v6 H0 m
__u32 rotate; /* angle we rotate counter clockwise */
% r9 ^" H4 g- Z% V __u32 reserved[5]; /* Reserved for future compatibility */% \* w" Z$ D6 ^3 V
};4 B( ?8 x; U! [# J1 w" a- C
8 n8 @; F! J' Z' k7 G2.4 fb_ops结构
: |0 c! Q/ J$ w6 g/ y- } 该结构描述了用于fb_info的方法,这些方法中有些是要驱动程序提供的,而有些可以使用内核提供的方法。
3 N+ r1 j# R' O v) B
3 s$ K6 t g& ?' S% u 下列代码位于include/linux/fb.h0 H. ?( L# P, T
% R% u# t* o4 r) E) k/*% S2 D5 A# x7 e8 [
* Frame buffer operations5 |& y4 k8 d6 q$ C2 n. u7 D9 F
*) A" b4 J7 w# G X9 E/ Y0 J5 u
* LOCKING NOTE: those functions must _ALL_ be called with the console& B! c& T0 k2 U! Z6 v5 |* {
* semaphore held, this is the only suitable locking mechanism we have; B3 i* ]6 T7 m( X+ n# e. A% h
* in 2.6. Some may be called at interrupt time at this point though.& X" G/ o$ Q$ ^* F$ `
*/+ w; M; S3 \+ e- ^# b1 _# I; \
" P8 j K% B' S0 L/ @struct fb_ops {
) }& E5 D- l$ w3 X0 E# A /* open/release and usage marking */
6 P+ m) @+ m8 U6 n1 S" S struct module *owner;
* |" c# N' D' e2 Y7 ^4 N# R int (*fb_open)(struct fb_info *info, int user);
7 H; ~) C, W2 C U0 o, _$ N int (*fb_release)(struct fb_info *info, int user);' Q* o7 E, _9 O* x. m+ U
8 Y/ ?# A2 O1 w1 Z8 ?: \ /* For framebuffers with strange non linear layouts or that do not" I `1 y1 m, p: X" d0 j
* work with normal memory mapped access# C' S' T; _ f
*/
1 d0 z) a, I1 L, O7 O R ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
4 D* p6 Y; K2 [( J( z size_t count, loff_t *ppos);. |, Z) m) I& Y. \8 A$ a
ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,' \' [4 m4 o1 I3 p" Y4 f
size_t count, loff_t *ppos);# B6 l; v) v+ p1 E; H
: x8 R( w' m. b7 @ V( m6 W. J
/* checks var and eventually tweaks it to something supported,
1 k+ M2 {- _- \& Q$ A * DO NOT MODIFY PAR */
2 W( B2 \. a8 N2 f" ~2 u int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);& x% V# _( p+ a% D# Y
5 d( A9 U s3 m( b! w
/* set the video mode according to info->var */
9 y$ Z! b" s3 i- k' T: }5 n int (*fb_set_par)(struct fb_info *info);
5 k! U) o/ [6 F& h3 X6 s( {- T
3 `8 f1 ~4 g6 m. Y /* set color register */
& O+ M! V: m! \6 d7 Z n int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,/ s- N5 Q' H- ]% r0 v
unsigned blue, unsigned transp, struct fb_info *info);7 Z4 R( F2 i/ A! Q: G) v% M2 O5 M$ ~: j
; Y- P! F# Y: O: ]% t$ | /* set color registers in batch */ X* F. a8 p U x1 A* G
int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);- ?8 N, G% H, W9 V8 l& H
- O9 }9 N; @* [0 B4 H+ W8 ` /* blank display */3 e( ]1 `# o) m; h
int (*fb_blank)(int blank, struct fb_info *info);% h1 W+ d: Y$ u7 e3 ~, i* Y
! n* b5 u7 d; {9 d7 @
/* pan display */
5 s2 v0 L* j+ b/ [, s& N" p int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);- b$ B) N% u0 M6 Q
2 b: x$ g( Z* a4 i' B9 L4 @
/* Draws a rectangle */* j. n9 F8 { n
void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
* ]( F5 P6 a0 c3 i /* Copy data from area to another */
; \2 e- C j* a% ^ void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);2 {! T3 q5 k9 C# ], @8 q
/* Draws a image to the display */
7 [- \4 R6 |6 D9 g void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);: N" _7 l$ Y4 O; Q4 B
+ }) c9 c0 q+ [2 v$ _ /* Draws cursor */
* F7 p* i" ~( w% S7 p4 A. ` int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);1 R8 q- F: R/ G+ N
. f$ R8 K0 o# }4 g; D: b5 n
/* Rotates the display */- \+ D$ X; p: I: L, s9 |
void (*fb_rotate)(struct fb_info *info, int angle);9 P0 d7 ? S6 a" y$ h* d
7 J/ m. f6 l3 H: ^! J0 s
/* wait for blit idle, optional */, W2 H+ |* m* G! n
int (*fb_sync)(struct fb_info *info); K& ]4 a% [6 P) h, N& R
) F9 r4 w- ~, \. O G: A' n% E- W /* peRForm fb specific ioctl (optional) */+ k& d* Y" v/ O- } N4 k+ q
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,% T4 _. y# g$ |, i3 n" J! ~9 N& H
unsigned long arg);/ L8 e( [8 l2 |0 D$ D0 b7 L
v e1 u- }& ~' D% Q6 ~' J /* Handle 32bit compat ioctl (optional) */
& b4 Q6 t4 I7 {! T7 J int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
( @! q8 i8 p: X4 b; B: m/ m unsigned long arg);
6 M, p, |; Y% h4 M' R" q6 l2 s% A1 R2 L& {' V. U
/* perform fb specific mmap */6 n* ?& h* D( X5 S j& W+ U" [. t
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);. t* L0 y( v4 T. K: B- D. ^
0 j1 V& x1 p& \+ \- P/ J+ o8 y" J
/* save current hardware state */9 [- k2 a" _ Q: \1 h$ x
void (*fb_save_state)(struct fb_info *info);/ e% x8 R+ v+ @$ x% L# L
, L- L/ h0 g" M3 f+ A
/* restore saved state */
0 x# d' J; H) E, n7 X0 j4 ?/ O void (*fb_restore_state)(struct fb_info *info);8 y+ R1 I: H/ ^: U
~! S5 p6 P" [# R; G% \ l
/* get capability given var */
0 F' c! G# @2 A& U void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,) N& E/ e2 g; d# ?
struct fb_var_screeninfo *var);6 H1 t9 n% n7 B7 g5 H2 R
};
9 |( n% ? O V, y: t# I
# M( o @9 I: s% X% G& @; l% c$ @; `0 g" K* n6 }
3. frambuffer核心层
0 X9 J- N9 x4 p( ^ d首先来看下frmaebuffer子系统的初始化函数。
4 @& |* @& H Y, d( }& m) [
5 f/ L2 P, r" i0 v r- ]5 P, q( g3.1 fbmem_init和fbmem_exit
' G3 D5 g- ^& r! l* p, f y下列代码位于drivers/video/fbmem.c0 I- G( V; r; m/ d6 i: F
, e5 T/ g6 V" @4 o2 E/**# k) z1 z& S# P+ g
* fbmem_init - init frame buffer subsystem
3 J8 y" n$ @5 t9 v. `1 `7 o' r *
% Y: k$ k! b: X. X * Initialize the frame buffer subsystem., A" ~) L" f) Y0 Y
*
$ _. ~+ I" p: t* F, i * NOTE: This function is _only_ to be called by drivers/char/mem.c.' q/ G p4 H& b9 O2 m
*
' G) v' F7 [! { */
# o9 G4 Y7 g7 V/ ? b2 _: Z5 U- v$ d3 `0 t( C& o
static int __init
@& t9 E) x. g- Bfbmem_init(void)
! d" W( P# J8 r- z{9 f4 A7 \4 L. s2 \; ~. O7 j
proc_create("fb", 0, NULL, &fb_proc_fops);$ m: [( k! }1 P; A: h* W
; N' |" J" g2 V; m' G: y, u if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) /*注册字符设备,major=29*/
, {% h; b O4 j. P- M# z" d6 J printk("unable to get major %d for fb devs\n", FB_MAJOR);7 [1 ]8 u3 f3 Z( H# S0 ]
0 p5 N* v3 k& N/ S1 V$ f fb_class = class_create(THIS_MODULE, "graphics"); /*创建类*/
, t) e5 K- u# z7 N% T! c if (IS_ERR(fb_class)) {; T4 K8 L+ z" _0 [ \
printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));$ f2 m* p8 }" |. \8 F. t2 J1 ]
fb_class = NULL;( m" i1 d- z$ ?* Y5 m
}
# e" \9 z6 \$ Q: }; T2 l$ U% m return 0;% x0 m. L5 x+ z1 U
}
! `. U% L9 S6 C0 Q6 Q0 r
& ^% R8 [$ a- ~9 h$ n5 s% _: W#ifdef MODULE6 v' g& Z3 _; V7 J4 A
module_init(fbmem_init);
1 C; f2 ~9 g ?4 zstatic void __exit
! D' J3 w) z7 R7 @. u8 Mfbmem_exit(void)+ G( Z2 O5 ?$ G
{) W/ m# D+ I; T+ ` I! S
remove_proc_entry("fb", NULL);
: m+ L) J: e; f4 d class_destroy(fb_class);
3 s) s& A ]( S6 v+ L5 w" ~ unregister_chrdev(FB_MAJOR, "fb");4 u* p* l" c" Z3 L
} b) b$ c: y) U8 O4 D1 y; {- U6 k
A6 G" @! ^* m" s
module_exit(fbmem_exit);; t2 c& |) s4 A6 J0 {$ ^3 N+ g
MODULE_LICENSE("GPL");
; C8 Y: f9 _. W( XMODULE_DESCRIPTION("Framebuffer base");
% W* T4 M7 M; M3 j1 E* W/ E- v#else
8 Q( \0 g& c' c) n9 G0 osubsys_initcall(fbmem_init);
: I6 @: F5 x6 J3 X#endif
3 c! ^( @$ x* x r# }/ E7 ]9 p: _6 R5 g
static const struct file_operations fb_fops = {0 v! k+ t) n# ^( h, s
.owner = THIS_MODULE,
. L$ G- E1 E. k2 y& W7 f& _# z9 \" D .read = fb_read,
" }4 L: `" u4 B, p .write = fb_write,* S. h5 V* K% g" y8 f
.unlocked_ioctl = fb_ioctl,5 a+ L; k J( B
#ifdef CONFIG_COMPAT
4 @7 `3 }, r, I* w. n; V .compat_ioctl = fb_compat_ioctl,
6 T8 m# H7 E9 c2 f1 |' ^#endif- a5 b* c$ t6 ^* m# W& {0 p8 n) ~, i
.mmap = fb_mmap,
0 f- S: ?! c, Q+ W8 |4 O0 V: L .open = fb_open,
8 D; S! e; S' D) e4 \/ ] .release = fb_release,
4 |2 N+ | ]7 O9 Y#ifdef HAVE_ARCH_FB_UNMAPPED_AREA6 I5 r: _/ \8 U
.get_unmapped_area = get_fb_unmapped_area,
7 @0 O* E) E+ {6 e* a#endif
: h4 ~( Q) z! M$ o* z. L8 Q#ifdef CONFIG_FB_DEFERRED_IO9 e3 g; p) R1 S0 T
.fsync = fb_deferred_io_fsync,
* ^* {9 o! j g#endif6 E0 Y2 U7 D" y8 U8 [! N
};* R& L7 z: y' z4 d3 g6 ~7 \$ N1 w
1 g4 w) h" B$ O, a3 R, R4 o) C我们看到,如果不是作为模块,那么该初始化程序将在subsys_initcall阶段被调用。初始化时,仅仅注册了一个字符设备,并创建了一个类。通过字符设备,提供了API给用户空间,包open,release,write,read等。
3 R6 @' X9 D, F) Y& G9 F/ Q1 `4 L随后我们看看如何分配一个fb_info结构。7 p# |, g3 g# `) s8 z, o9 |6 Z# I
7 l! e3 m( F8 A' x' c3.2 framebuffer_alloc
; i6 L1 @) \/ d9 X* f下列代码位于drivers/video/fbmem.c
1 ^- N' G( @+ h" N' B+ _
3 } r2 g7 L' p0 H6 A! k/**$ _; y, \9 `9 I
* framebuffer_alloc - creates a new frame buffer info structure
, k3 ?5 D2 `8 w7 U0 ?/ A, J4 E8 r3 ^( w *+ v) d) S# R/ v9 s
* @size: size of driver private data, can be zero7 s/ b8 ?% K9 g0 g% D
* @dev: pointer to the device for this fb, this can be NULL7 o9 i. T- J; }: e$ s; ]- g
*# S$ V E( }' M7 X3 {/ X3 w
* Creates a new frame buffer info structure. Also reserves @size bytes* L/ D/ ^$ P& I" R
* for driver private data (info->par). info->par (if any) will be
2 G, J7 @; t. G; ?, q0 x: a * aligned to sizeof(long).0 p7 s/ f5 `# i' s; c) N* Y
*
: U G( j a' \ O9 X9 _. f * Returns the new structure, or NULL if an error occured.7 l2 o1 @/ \; z5 d: a$ |; N
*
& t# b! B* b1 T+ |; {+ Z1 S b */" R1 k& r g r" h/ `
struct fb_info *framebuffer_alloc(size_t size, struct device *dev)+ M4 P2 c m* k: c! V
{
% F6 ^' x/ H) Y, ^/ u0 `* ^#define BYTES_PER_LONG (BITS_PER_LONG/8)
' B9 p# {+ Q" U) n#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))3 B% p; n8 _4 `# @$ O
int fb_info_size = sizeof(struct fb_info);0 k Z1 b9 R7 u
struct fb_info *info;
) i" L: }' ~3 y# x" R, ^! [$ O char *p;) v/ e" `7 V! i7 I }3 X
9 l' F$ o4 M- \ if (size)$ e c# I! G: K$ H* s* ~
fb_info_size += PADDING;' B# ?# q& {" l: O( ?9 A% q; a
. z# E1 D1 t; ]& R. t
p = kzalloc(fb_info_size + size, GFP_KERNEL);& k0 P% c7 d8 P. g5 V" |" z2 `
: D; m" o9 }0 S$ r+ Y- I if (!p)
4 ~# V5 V' ]* b$ v1 X8 Q; C' N return NULL;* _1 p/ }9 C; w# `" \0 Z1 `# b) s
& s7 {# S# e3 V# U' N' u9 L
info = (struct fb_info *) p;* u) c" q" S# }+ A
. |3 ~2 I9 ?2 k9 X: C if (size)
6 _! i# X4 L! L- a% T/ ^ info->par = p + fb_info_size;( D+ s( h4 A. ^, n4 O+ f
; t% ~, k# j5 I1 w" @6 L
info->device = dev;
: r# w" ? k* X) i& D& [8 V4 r7 }, a* V1 n8 e2 O1 Q; |
#ifdef CONFIG_FB_BACKLIGHT
) U/ z6 K2 c+ w& y, S- } mutex_init(&info->bl_curve_mutex);
( E i: m3 b7 i- X) `* O#endif
; S: W8 x$ ^$ H2 k5 @( V5 w7 i
8 E) \3 p6 I: r: J0 G9 x y: L } return info;; M- d u9 R' y
#undef PADDING2 s! k* }/ z0 X/ t2 i' Z
#undef BYTES_PER_LONG7 w# k% H) W3 x3 i& R
}
, Q; H6 q6 N# I# V* {2 sEXPORT_SYMBOL(framebuffer_alloc);
* h* k7 A; |8 ^: @4 h+ ]在进行分配时,根据参数size的大小,分配了b_info_size + size的空间,然后让fb_info->par指向size的空间。因此par所指向的空间可视为设备特有的数据。8 d/ r' H! p; N4 d/ V4 M
在分配了fb_info结构之后,需要将它注册到内核中。注册由register_framebuffer完成。我们来看下。; ^7 e8 s5 Q, J% v9 Z3 D; c
2 W% i" m& q8 X' }; M
3.3 register_framebuffer% ?# q2 J6 ^, W$ E: D3 k
下列代码位于drivers/video/fbmem.c/ A) E# f+ P1 S7 s8 V. P
, Q6 E' V4 y9 } ]$ ^
/**
% `9 E m5 g. c0 Q7 Z * register_framebuffer - registers a frame buffer device, k/ A2 }7 F2 b! g& |$ v
* @fb_info: frame buffer info structure
- O) U3 s, N0 T- _ *
( r$ C7 X$ T8 a, P$ e3 E: ^4 _ * Registers a frame buffer device @fb_info.0 n" P; _, ~# j# L h1 P
*0 ?; s7 e; r7 i4 z) m' L) e/ M# O
* Returns negative errno on error, or zero for success.
4 ] ^% D1 c/ P$ j6 w *
" i1 H) m! l8 m' g8 s) M& W */
9 E! h/ A6 t& A7 t' r: O6 n1 b0 a9 S* ]7 \2 _* v# y* p& H
int
$ i+ {3 f3 @7 p- R5 x/ Aregister_framebuffer(struct fb_info *fb_info)3 L2 s' x; |% @
{0 e7 X( Y; k4 j1 | `+ _
int i;$ q* n; `6 H3 v9 l% a: E* {
struct fb_event event;3 h2 d z4 }! I+ H6 y
struct fb_videomode mode;
! [: W1 c. }% k' y/ A" V3 S
$ p6 s- p" g/ s6 Q1 b if (num_registered_fb == FB_MAX) /*最多32个FB*/
* ^% c+ P4 s5 @/ o) K0 i return -ENXIO;- p I2 |1 S' ^" @8 v4 Q# f
) @7 x' X: ~+ d+ [; d* W if (fb_check_foreignness(fb_info))' X4 p0 ^& S3 H6 K
return -ENOSYS;
5 f! e$ ^1 U4 T) m, b# K1 o
9 f; h- P* Q$ \1 n( { num_registered_fb++; /*对注册的FB计数*/" ]! j7 K' `1 I
/*寻找第一个空位*/
& N* h, U* T' | for (i = 0 ; i < FB_MAX; i++)/*FB_MAX=32,也就是最多32个framebuffer*/
2 v2 [8 q9 `9 f/ c8 p% V( K8 j _ if (!registered_fb) /*struct fb_info *registered_fb[FB_MAX]*/0 F; c+ H' G9 V$ N6 n$ J' u1 Z
break; w0 i* e$ h: F( ]- t" w5 M7 }
fb_info->node = i;+ u# S6 S& e1 |5 R: F
mutex_init(&fb_info->lock); /*初始化互斥体*/2 V0 X0 E/ y' i& p
6 U! }" Q# `% a( Q% t' I
fb_info->dev = device_create(fb_class, fb_info->device,/*创建设备节点,节点名为fbx*/7 }2 V |. g( \& ]+ b/ \1 b
MKDEV(FB_MAJOR, i), NULL, "fb%d", i);3 O5 `8 F% p' Z- q2 T
if (IS_ERR(fb_info->dev)) {
# O- s8 J1 V& [7 i /* Not fatal */
6 ]* o+ Z% s% N* B9 J2 ? printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
( d4 a: \3 _/ M! _8 E7 d5 z fb_info->dev = NULL;
. O0 s* T# L$ q! o# q } else
: g% k _! ] x$ X1 A _ fb_init_device(fb_info); /*初始化,在class/graphics/fbx/下创建设备属性*/
! T3 X1 p( R) x3 H U
8 m6 A! t0 U! p) _) E9 s: J! P% u if (fb_info->pixmap.addr == NULL) {+ d/ Y$ k @+ p% _, [9 o
fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);/*分配内存,1024 * 8字节*/
1 I! s$ M: Q6 t+ V/ \) X if (fb_info->pixmap.addr) {
+ M( E! Z0 r: p @# D fb_info->pixmap.size = FBPIXMAPSIZE;4 m6 y/ a- o5 K) g) b- e4 @* A) ~
fb_info->pixmap.buf_align = 1;% u0 k% f$ `& g! J. w
fb_info->pixmap.scan_align = 1;0 W; `' J% W5 }% q: T% y( _
fb_info->pixmap.access_align = 32;- }# ^9 b- D$ |
fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
- h( T' }! n/ S6 A4 o# } }4 n! a; b. c/ ^$ e
}
! |9 X% F, j3 Q fb_info->pixmap.offset = 0;
! y8 w( n( C& w; d. O* f9 \6 s- b2 t- a( d4 M' H8 ~+ V# W
if (!fb_info->pixmap.blit_x)
5 a+ c& C+ b+ c1 n S7 N8 ] fb_info->pixmap.blit_x = ~(u32)0;
" a7 C: a7 j v' d7 n& t3 j$ K5 W% X) Q6 k3 l7 F1 J/ x5 E2 B
if (!fb_info->pixmap.blit_y)
( d0 N- m* M9 j( \" n+ ] fb_info->pixmap.blit_y = ~(u32)0;# y: t9 w0 C# _ ~6 ]
- E4 D& w) P9 x+ T
if (!fb_info->modelist.prev || !fb_info->modelist.next) /*该链表没有指向其他节点*/
& p8 D a5 a6 j& H# A( A9 f INIT_LIST_HEAD(&fb_info->modelist); /*初始化链表头*/- f/ D$ K9 m+ ~, ]- C; b
2 V# c1 o5 D; X* `# i fb_var_to_videomode(&mode, &fb_info->var);/*转换fb_var_screeninfo成fb_videomode*/
+ v* q& d2 ^5 c5 l: B+ |7 w8 l fb_add_videomode(&mode, &fb_info->modelist);/*添加mode至链表中*/
5 O- @- f l6 j% r* e1 P6 r registered_fb = fb_info;9 w" I3 E3 [& \/ n
/ b, j% a7 D/ N
event.info = fb_info;
t: R2 u6 e" f if (!lock_fb_info(fb_info))' A6 s0 X7 Z4 Y; D+ J
return -ENODEV;
9 J( a* `& u' k" R fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);/*???*/
6 P0 v: k6 C* K8 E0 ] unlock_fb_info(fb_info);! a6 K+ D+ Z0 K
return 0;
( m( z- |1 @3 j) r& f}
1 e. \, l2 B3 E" {* B0 e& z6 @) |, I3 @) |/ A6 C) L q
从这个函数我们可以看出,framebuffer子系统只支持32个设备。在创建了设备节点以后,建立设备属性节点,随后将fb_var_screeninfo转换成fb_videomode,最后添加fb_videomode至链表中。
0 j2 x1 i2 G$ f6 N+ r% A- F6 B [我们看下其中调用的函数,首先是fb_init_device。, E0 l6 f, B3 u/ V# T& e
: r, _: ?, S1 ]( D* S" i
下列代码位于drivers/video/fbsysfs.c
1 {% \8 R" d( O. ^& @9 V& {& n# J( N
7 z3 ~% V2 e- m: U+ Q2 l2 zint fb_init_device(struct fb_info *fb_info)
5 _; S, p0 j$ ^, N9 F( T0 T{
* }- I- w o1 ?$ f5 L% V% J int i, error = 0;
8 L+ _) a0 w& i6 x) m l& E9 J |; [. X9 F5 o$ g
dev_set_drvdata(fb_info->dev, fb_info);! d9 k$ U: }$ x k2 t2 C' g3 @
; K, c* L" ?5 R& w5 Z* J fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;4 Q+ N- s4 _1 U% u! z' g5 y( l
/ E+ a1 c) Z: L3 Q$ ] /*建立设备属性*/7 C/ ~* N9 Q+ v% g6 [7 W
for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {8 l% {2 J" o5 z" {# [. M# O" Y
error = device_create_file(fb_info->dev, &device_attrs);! p7 K$ T) h3 Z- i7 J! o( @
& H: Q- i7 {# \- _- b C if (error), u; J* m+ _) ]/ y) J. L' o
break;
5 e9 V. {( f$ @4 L( ? }
: D' d; Z1 v m. I' P/ {
4 E1 S' O' a2 S, S' t if (error) {" I0 h) J0 F% U$ S
while (--i >= 0)
+ C! l v- G( t d3 c* H R device_remove_file(fb_info->dev, &device_attrs);
' a3 w) R- p0 } fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
" z! J/ n8 z8 X: r6 \3 n }* x g' k2 V: w s3 i) J8 ?
; L$ t* s% t, j8 h3 M: o9 k3 n2 I
return 0;
( i7 j. P( c9 s7 T C f$ E4 x- ?" y}
{7 I* }2 Q# O. g' u" l' O. L) k
/* When cmap is added back in it should be a binary attribute
9 t" o+ d' d; i * not a text one. Consideration should also be given to converting' u' \. X8 Q& h7 S
* fbdev to use configfs instead of sysfs */) b* d& U" W: ^
static struct device_attribute device_attrs[] = {
9 n r+ ^5 C8 I) \ __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
: U4 I! Q+ w$ J __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
* Y. {' `7 [" z* ]( I; |) H __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),* n6 Q- N! Y @+ y) F; t$ ] l
__ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),' Y1 o8 X) d1 M! x1 D
__ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
" N' i* ]% I! q8 k __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),3 H( J: O: Q( B' g
__ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),/ a$ d8 f; ~8 o, k! `/ {
__ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),* @0 q3 S$ j/ c: m u- W% I
__ATTR(name, S_IRUGO, show_name, NULL),
: d- q9 }' v8 G __ATTR(stride, S_IRUGO, show_stride, NULL),) c- q$ n! _4 ^9 X" d
__ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
7 t) t* |% x7 k I1 I {$ i% A __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
* E r5 b K4 Z; \+ a D, N#ifdef CONFIG_FB_BACKLIGHT5 v9 F6 @9 e- i( W0 t+ B0 V% V0 k
__ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),. r" i5 i5 Q3 ?$ e, m; f
#endif
0 |2 ~2 x4 M0 m. g};$ U) [, R( F0 N- Z" O
, c8 A4 I" K# j. c* n( R我们可以在/sys/class/graphics/fb0下发现这些属性文件。; A# d2 _! i( r4 q' N' q5 [$ k7 P
[root@yj423 fb0]#pwd |- h" W8 r: k4 g/ m6 t
/sys/class/graphics/fb0
2 h# ?% ~& b% |5 f[root@yj423 fb0]#ls3 K) |3 q" f* p& ~
bits_per_pixel cursor mode pan state uevent9 y' h) {' W9 w* G
blank dev modes power stride virtual_size1 f/ C% F( M( n$ q5 O5 y
console device name rotate subsystem
' B/ O5 u D V% O: R# w R$ _! e1 ~. W2 I$ n
; L& o. M2 C) K( L6 l! }6 [
接着看下fb_var_to_videomode和fb_add_videomode函数。1 B8 n8 P: x. x
2 G" r2 A6 E$ B下列代码位于drivers/video/modedb.c和drivers/video/fb.h
# [8 a- ~/ W; G/ i: l8 Y# m
! b8 k1 k: o5 I, ostruct fb_videomode {. y. O# ]& R/ E% c
const char *name; /* optional */
5 ?3 d# N% L' v1 G+ p# X' D u32 refresh; /* optional */
9 p; t* F) S# T; H2 F7 X# Q u32 xres;/ l7 N9 ^9 H* J( A4 d! K- s
u32 yres;
8 J5 c) D9 E% f8 c% w u32 pixclock;
/ E3 O: S8 t# q( O: G9 y- K u32 left_margin;+ T& j6 u& j. v4 ]- ?
u32 right_margin;% U: r& P* v) q5 k/ |2 ~4 _
u32 upper_margin;+ ^$ @: A3 t, F
u32 lower_margin;
' o1 G" J, d6 }# }& l3 \4 ` u32 hsync_len;
; y9 w4 @: H7 Z5 H u32 vsync_len;
1 g) ]2 g6 N; a" i u32 sync;; y t3 Z* t- i: Q- }& _
u32 vmode;
9 N# ~6 |) I4 j: } u32 flag;/ `- ~8 Z# N$ Z9 n5 _' W
};, A( H/ b s2 z& [( D: @# J5 b
0 f/ _" W( g+ b5 W* Y4 |5 L! k/**' H' Q4 \1 y% G: W2 P' m( l
* fb_var_to_videomode - convert fb_var_screeninfo to fb_videomode
. m: D, t; E/ K; I8 e. L- g1 p * @mode: pointer to struct fb_videomode
. n+ [+ s: p5 P! N% T * @var: pointer to struct fb_var_screeninfo
! |* f- y9 \8 } } ~! w4 l */) M! W) E0 L5 p" A+ u8 e" p# V
void fb_var_to_videomode(struct fb_videomode *mode,
* J6 | A8 ?1 g0 d. u- l6 h const struct fb_var_screeninfo *var); W' B0 K, U$ G, K2 C2 n/ B& S6 e
{
. [, t7 o$ y5 b1 c; H- \: P4 T+ V u32 pixclock, hfreq, htotal, vtotal;) Z% T8 a7 M# o/ _, ?$ \- W# ?
( Q$ h' b( }2 y& R' d! z- B mode->name = NULL;$ h y' ~6 ]( C5 q1 p
mode->xres = var->xres;' J5 r( c8 u7 n
mode->yres = var->yres;
9 V- a( ?- v% P; J$ N& X mode->pixclock = var->pixclock;
* A3 q7 [% ^7 H3 u4 j mode->hsync_len = var->hsync_len;
& ~% o% z% a) j, [2 B mode->vsync_len = var->vsync_len;
# v4 N- ^8 w( I/ T: U mode->left_margin = var->left_margin;% E: `0 @4 r; \* Y$ t0 R
mode->right_margin = var->right_margin;
0 I0 j7 u. Y0 N0 h$ i mode->upper_margin = var->upper_margin;
1 A8 Y T: S4 c' P1 I/ N, s mode->lower_margin = var->lower_margin;3 g, D$ t3 H0 o1 {9 t& P$ F
mode->sync = var->sync;
% l/ a( Y8 [5 j8 i mode->vmode = var->vmode & FB_VMODE_MASK;
3 H4 u* s `- c* V- m+ }4 A mode->flag = FB_MODE_IS_FROM_VAR;
1 O8 H$ w! H: X9 m mode->refresh = 0;
) ]2 M b8 i' G1 R8 a* y" A( r' A* S+ S: u& S) r
if (!var->pixclock)' E. K8 V3 d# d0 J: s# \* a
return;
& w2 F8 g: d! Z2 {# y# }3 m' H6 O( S
pixclock = PICOS2KHZ(var->pixclock) * 1000;
0 d* ^; V/ V" R8 \* M* z! f0 i* ?4 V# `% O1 p4 A0 P& S9 ?2 y
htotal = var->xres + var->right_margin + var->hsync_len +
* Q: l6 W- w+ _$ q5 J9 U var->left_margin;
( m# A1 h/ Z: V& \: n( X- k vtotal = var->yres + var->lower_margin + var->vsync_len +! w: F% X. C4 J, ~
var->upper_margin;
; Z- R$ {* Q/ I8 |) c R# L7 f+ F: K
if (var->vmode & FB_VMODE_INTERLACED)
L/ w( O5 n* j; z; i `. i3 C vtotal /= 2;
0 k& x; i e: W: [( g' |) z if (var->vmode & FB_VMODE_DOUBLE)( y" a" b- C1 a
vtotal *= 2;, [, L' z! L: X& X9 n& a" T
" D8 g m9 r" a7 q1 M
hfreq = pixclock/htotal;
% w5 V, g0 t2 C" U- A mode->refresh = hfreq/vtotal;
! f& s$ b J" j+ r}
5 m! y7 t6 s" v" O1 j, t$ x, V* A6 r5 c: y0 m$ @2 O
/**
9 O9 L6 O. N; i* m * fb_add_videomode: adds videomode entry to modelist
) t/ I6 _. T. | I * @mode: videomode to add/ U$ L0 @# g' S6 w
* @head: struct list_head of modelist
; `/ s* ?: \; ^6 [ *
' I3 U: W8 B2 B( N * NOTES:1 B: N. w4 D$ ]. B6 a8 H
* Will only add unmatched mode entries; n$ Y; j$ d! z! d( Z% j. B" Z
*/
; ?6 f; W0 q4 }7 H& z- [( W) o Eint fb_add_videomode(const struct fb_videomode *mode, struct list_head *head)
$ E, l4 L5 u5 c) z* p1 G{5 T9 z+ u$ h+ y4 ~ I
struct list_head *pos;, }: V0 H" `7 Z+ f$ V5 v* G/ N
struct fb_modelist *modelist;d
, A* Z6 F `4 } struct fb_videomode *m;3 a. Y: G) T& p
int found = 0;
6 }, n& S9 u: A# l6 u- T /*遍历所有的fb_modelist,查找mode是否存在*/6 _. A* J6 d, U, F
list_for_each(pos, head) {
9 P1 f V" T' L modelist = list_entry(pos, struct fb_modelist, list);
1 Q0 N( C! U" I9 n1 V, w c* z" Q1 n9 _ m = &modelist->mode;
1 X& E& u; e/ S$ M! \ if (fb_mode_is_equal(m, mode)) { /*比较两个fb_videomode*/
: w0 H# @4 J- M+ }1 l7 ?" ]% o) S# [ found = 1; /*该fb_videomode已存在*/
( [5 _6 d7 ^5 I% U& D5 z6 R* \& } break;
$ w% z& Y9 ]7 s8 w }
6 j; P' z% V, e" F2 @7 |% C d8 D2 A } z$ a! Z7 X7 R$ Q
if (!found) { /*不存在*/
1 y5 L5 ?9 m* @, O# H: w$ V modelist = kmalloc(sizeof(struct fb_modelist), /*分配fb_modelist*/: W, N) O& t3 x8 W# ~5 t, d
GFP_KERNEL);0 A. [! K0 k0 K6 g# a
6 p3 x2 b4 ^! [5 Z# {; A if (!modelist)
1 E& D, S5 L) C/ ~: m; h return -ENOMEM;
0 ^' l/ M, ]8 h# M8 D modelist->mode = *mode; /*保存mode*/
7 J$ U/ K, T% H. @5 k6 s/ H list_add(&modelist->list, head);/*添加mode至链表中*/3 A% E: \- e6 @3 _' ^
}
/ H$ ?6 k: B; y1 ~( k return 0;
- D% \/ I% Z' E% ~) @3 X4 q}
, M* k b3 Y- k, f% S4 [( ~% W5 t$ I4 v1 f
/**
: c* `* K0 ?& z# s* m * fb_mode_is_equal - compare 2 videomodes
4 P1 v" y3 D& O) e) U * @mode1: first videomode
5 G Q* C/ m* R * @mode2: second videomode
( i+ ?4 R( K& @( k6 f+ H4 {: p7 j *1 O: h# I% O) J8 w
* RETURNS:
: Q3 v7 `+ n% z6 @ * 1 if equal, 0 if not; ^$ |& J2 |- n& \, e" j
*/
_# `1 x. L/ d- l* Q, k; v) w% Gint fb_mode_is_equal(const struct fb_videomode *mode1,/ V8 A5 K* D+ K+ @* U
const struct fb_videomode *mode2)0 y" p1 i* n7 U4 s' Q
{
9 ]5 ]# [ f5 w' `+ M) l return (mode1->xres == mode2->xres &&
( w1 ?: p& ~7 Y; H0 _ mode1->yres == mode2->yres &&0 ]* Z: g: ]7 Y4 W
mode1->pixclock == mode2->pixclock && @2 d8 d9 g" L2 l
mode1->hsync_len == mode2->hsync_len &&
& ^0 i/ u8 [0 v$ G \ mode1->vsync_len == mode2->vsync_len &&
$ ?( a7 [/ V/ e, G, m mode1->left_margin == mode2->left_margin &&3 V+ s6 ?# m- Z. }3 y! g
mode1->right_margin == mode2->right_margin &&% f9 `& \6 n1 e0 ~9 a
mode1->upper_margin == mode2->upper_margin &&
7 `! e; k1 J/ J mode1->lower_margin == mode2->lower_margin &&
+ \ }/ t( N3 u8 [$ p9 k% C: Q mode1->sync == mode2->sync &&
6 d2 C3 n8 W2 ^6 O: r. o7 x mode1->vmode == mode2->vmode);
* k" Q2 G# J2 l& K, C) [}
: ~' H' q- ~6 ~7 E$ @$ f8 z* {( J& S! [
6 C6 p' O% ]: V7 b6 u" z r& V0 F
fb_var_to_videomode函数只是将fb_var_screeninfo结构转换成fb_videomode结构。而fb_add_videomode函数查找是否该fb_videomode已经存在,如果不存在则添加到列表中。& w5 @4 I8 I2 R' d4 f2 A( }
3.4 字符设备方法
7 h- S6 H/ V* g. @# r6 ] 在看过framebuffer子系统建立和注册过程后,我们看下framebuffer留给用户空间的API是怎样实现的。
0 t* ^& k% U) G( U9 |8 I$ O. ]+ G J. b
本小结只分析5个常用的方法,即open,release,read,write和ioctl。. T; E0 L y$ X, B& s+ A" s
! |+ |: J4 @2 T. W 因为所有的方法和struct fb_ops定义的方法有紧密的联系,而该结构的定义由驱动程序给出,在这里我们提前看下在驱动中是如何定义的。' M6 P; t6 Y, n) \9 N5 m
8 f0 a5 h" t* l3 q7 ? h9 Z 下列代码位于drivers/video/s3c2410fb..c
+ F6 [/ Y, o2 @8 G" a3 X2 Y7 m
. |+ d9 L& T. _9 B9 @static struct fb_ops s3c2410fb_ops = {
; N: e, @; [! \ .owner = THIS_MODULE,
, A$ p# `5 J* Q .fb_check_var = s3c2410fb_check_var, /*检查变量的合法性*/3 l8 ?6 N4 x8 g3 { S1 n4 d
.fb_set_par = s3c2410fb_set_par, /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/
# H2 ]3 J7 t# y8 E, {) _, Z* X .fb_blank = s3c2410fb_blank, /*该方法支持显示消隐和去消隐*/, F, O- [8 n1 [/ \1 y
.fb_setcolreg = s3c2410fb_setcolreg, /*设置颜色寄存器*/
. D' w& p' C% C4 D. r# ? .fb_fillrect = cfb_fillrect, /*用像素行填充矩形框,通用库函数*/
" I( p8 ]2 m$ V! o7 I# p .fb_copyarea = cfb_copyarea, /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/: G) J/ e5 m0 @9 U9 b" F% W
.fb_imageblit = cfb_imageblit, /*显示一副图像,通用库函数*/. L; W: A* p4 y3 y
};
/ X& Q5 [/ Y3 m' q( j. @6 k# r最下面的三个方法使用的是内核提供的库函数,而上面4个则是由驱动提供。, _5 T3 _+ ~" }8 [, k: u
3.4.1 open方法
/ ^1 o# R0 c! c5 M+ v9 Y& m9 k下列代码位于drivers/video/fbmem.c! a$ G9 V& h) B: }8 c
5 U8 z; O3 b3 e
static int) {8 W" ?# b7 n
fb_open(struct inode *inode, struct file *file)
8 z/ x# E/ g4 S/ |5 L__acquires(&info->lock)
; n. ~: w6 J6 O( u, o( [__releases(&info->lock)
* ?6 \7 s( Z7 b1 i7 @{2 H$ y- V: ]" x- `1 E) t7 Z
int fbidx = iminor(inode);2 a. |' f! W& u' v' B6 u0 Y% L
struct fb_info *info;* K* J0 Z- r, H# E
int res = 0;
- ]# w; ]! J, C3 k
7 K4 @7 M+ I2 E; W r- r4 f8 K if (fbidx >= FB_MAX)" c- M: I7 n/ D. @9 j) o
return -ENODEV; }7 Z$ c0 n$ H7 D. ]/ s" [
info = registered_fb[fbidx]; /*在register_framebuffer函数中已经设置了元素*/4 s: D) }) l& U
if (!info)
! ]7 i( G1 V, h; U& Z& }! ]+ G. f request_module("fb%d", fbidx); /*加载模块,这里不加载*/# r, n* T, ~3 C, n1 Y: y) ?! @
info = registered_fb[fbidx];0 N2 i. w$ p; h4 C$ `
if (!info)9 w4 D+ K5 Z7 y; @
return -ENODEV;
- e; w2 k+ u: e4 `3 A" u0 O mutex_lock(&info->lock); /*加锁互斥体*/
- m' j N3 Y1 ]8 L if (!try_module_get(info->fbops->owner)) { /*增加模块引用计数*/
+ ~- }) Q H( T7 f; h res = -ENODEV;7 x2 e0 a- Z7 ?, \& S' t t
goto out;$ ?0 t. f# x; f. Y! ]
}
z% O# `. g# P, C, P1 p file->private_data = info; /*保存info*/! |7 L' U, \; |- T, i+ Y- B
if (info->fbops->fb_open) { /*这里fb_open方法为空*/
9 g7 u1 {6 O7 q5 _* q res = info->fbops->fb_open(info,1);( v' @: |& c8 R, P9 l* ^0 d2 ^+ ]
if (res)
- T$ y# @$ L8 [$ b4 A% c module_put(info->fbops->owner);2 H/ E& Z7 }& T
}# o- B8 a1 A6 i$ y
#ifdef CONFIG_FB_DEFERRED_IO
: N# c5 H6 p; H2 }5 S" c if (info->fbdefio): F: B. F' I- l. u
fb_deferred_io_open(info, inode, file);
7 ?- V, t) j @ |4 y#endif
3 Z/ T6 k% ~, ?) p" k8 m- T: J. ~out:
8 Y" j* y3 z4 i } [9 o" o% A/ y- | mutex_unlock(&info->lock); /*解锁互斥体*/
, u) _( ~0 C7 ~, S4 t; V) } return res;3 E- r( Q& k# n/ N1 s
}
) Q l( w) S; [ X* {主要的一个工作就是增加模块引用计数。还有,程序会判断是否fb_open在驱动中给出,如果有则调用该方法。我们已经知道fb_open没有给出。4 t; z5 |/ b9 N3 G6 p8 y
3.4.2 release方法
U! s0 M9 v# I( j# u0 {' J0 G) J( ~; u下列代码位于drivers/video/fbmem.c7 A& D5 t' t" f. p
( y: Y! d& _4 }
static int , s* i* x4 K! b% U
fb_release(struct inode *inode, struct file *file)
* G" o$ k4 `9 U$ z__acquires(&info->lock)
5 b- g+ C* y! j, Z* d. [7 R__releases(&info->lock)
- j: S# E& i# d0 K3 E5 z$ l M$ a{
7 _) ]0 W+ }" s6 P% S# w6 u$ n struct fb_info * const info = file->private_data;* W3 L7 L$ i' a$ k
9 L8 z" P/ Q. R mutex_lock(&info->lock);, I, O9 y! G1 h$ ~
if (info->fbops->fb_release) /*这里fb_release为空*/* A( Q; V6 Q: |9 v3 A2 b8 F
info->fbops->fb_release(info,1);( B: l$ C3 n5 \* y' Q) w! b
module_put(info->fbops->owner); /*减少模块引用计数*/
1 m, \: W6 `( \) N3 J mutex_unlock(&info->lock);
) L9 t8 ?1 \$ C+ P3 r5 I return 0;, Y" Q" V. b3 m; @
}
2 c3 {3 J/ o' r! e3 P: ^和open相反,减少模块引用计数。) L$ |% {* I2 x$ v
) S4 Y' z1 J/ B) o, b
3.4.3 write方法
6 t( ^* L. |7 \* V' k4 h 通过调用该方法,LCD将显示画面。
+ @" U. H) e9 I1 J N; D7 k& d6 K& B/ d) u: Y& A
下列代码位于drivers/video/fbmem.c
; q1 s/ e* R3 @/ X+ H
. j; |; ^8 P, H) r9 J9 `8 o4 E6 sstatic ssize_t
; L6 `9 N$ F1 y$ C9 afb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)5 x4 g$ Z4 X3 `1 \
{; p9 Y8 Q; P% e7 Z6 x! ~- H
unsigned long p = *ppos;
4 W! a; R- L x) d9 a* o struct inode *inode = file->f_path.dentry->d_inode;
# c% u4 p% }; s int fbidx = iminor(inode);
' p" F( q! Q3 e8 o+ O struct fb_info *info = registered_fb[fbidx];
6 U5 }5 e: X1 n+ U. X u32 *buffer, *src;
8 a2 u# @; O9 u; l0 Z3 S u32 __iomem *dst;
' `5 L, T. G4 y int c, i, cnt = 0, err = 0;; R# K! I) k; ~4 v/ A0 P( [
unsigned long total_size;9 L$ o7 V) {: T. c
6 W) [0 X$ q( g* ?9 } if (!info || !info->screen_base) /*screen_base在驱动中给出*/
* _+ E9 t, b6 y4 m! |1 V return -ENODEV;5 a; b, Q$ s' j8 j& b- @7 M
7 G5 {3 p+ z: w* l- r1 e1 e5 |! ` if (info->state != FBINFO_STATE_RUNNING)
2 l+ n( g. L" R9 e% y. v3 `3 k& n return -EPERM;
% z1 |- Q" }7 U! F1 Q g2 o; @3 t# F1 Q; Y5 u3 g
if (info->fbops->fb_write) /*没有fb_write方法*/
! D3 V5 b# h% W return info->fbops->fb_write(info, buf, count, ppos);
- n2 o: w0 f$ |$ `& w: e- C; O4 q3 r( P; @
total_size = info->screen_size; /*screen_size没有给出*/
8 _2 i) U c: z# q! X: ` Z6 F% L% M8 a4 Q" y% U
if (total_size == 0)
: Z9 \. d6 E* R total_size = info->fix.smem_len;/*153600字节,驱动probe方法中计算*/# s8 v! n& m8 G$ F7 @9 Z
" O+ `3 w" J" u# d% t: `& R
if (p > total_size): x" C0 ?- Q/ N. e3 Q
return -EFBIG;
) Z9 l8 P' D R; `6 s- F
3 a1 h$ J1 R$ p* k if (count > total_size) { /*要写入的字节数大于153600*/# k( _+ x* e! @8 ?7 V& y+ J6 O
err = -EFBIG; /*file too big*/# d" `* ~: [. t$ k$ f9 @
count = total_size;8 p& F% C6 G: |& @4 z/ I* j) V# k
}
5 S) V, a0 A0 z! I' O- `$ s. Z
) s( q" q1 K7 D1 q7 \: x if (count + p > total_size) {/*偏移量加上字节数超出了缓冲区*/ v4 R4 u f) k$ G2 V$ z
if (!err)5 Y: d6 R/ w( |* r- W7 x4 c2 E- d
err = -ENOSPC;
& C) K/ k9 h9 g3 a/ h4 F7 `$ |
$ J) X6 g' w5 m8 M1 P" m count = total_size - p;
5 k5 C4 z0 f. F3 C }4 b, m6 G F! n/ U0 x
: l. W* @% u6 s1 f9 V3 M3 r" J
/*分配buffer,GFP_KERNEL*/
- x7 g# x; I: b" H) X2 ` buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
+ h) \( W8 S0 E% t GFP_KERNEL); /
3 i1 u9 p/ q" Q( F* W if (!buffer). w( @4 ~8 r1 R. Z
return -ENOMEM; |1 z9 S4 F3 _! j
# h) c( Z6 i# w/ U1 k dst = (u32 __iomem *) (info->screen_base + p);/*修改目的指针*/
" v4 H& U: o1 G( i) ~& O
! B! k( x$ ?- `/ L if (info->fbops->fb_sync) /*没有定义fb_sync*/+ O* _$ P# o. G$ E
info->fbops->fb_sync(info);
, h0 o6 I$ R/ V$ `! Y1 q5 W3 Z1 X9 L; Q- b
while (count) {
$ _2 \" h$ [( _" O) ~- g8 J c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
! M9 V8 L; b; n src = buffer;
% a i7 q5 @1 v; x# U5 M
1 Q1 ]8 E5 ? y5 {1 n- q- @ /*从buf(用户空间)拷贝数据到src中,一开始为1页,最后为count字节*/
% J) J- }6 m9 d( ]2 J if (copy_from_user(src, buf, c)) { . ]' B) B! y# c {
err = -EFAULT;* Y' Z1 O% [+ }* S8 S" |
break;0 |3 F4 P7 S1 f% [; M9 G1 h
}
8 e4 b8 k4 z9 G: c; l( S; a /*一次for循环,写入4个字节数据到dst处*/' A5 L8 H i6 d
for (i = c >> 2; i--; )9 z4 B( {. L( q) T( B/ E! G% _4 Y
fb_writel(*src++, dst++);& [4 y3 y# t; j& D( o9 O
/*最后还有3个,2个或者1个字节*/
! |- B1 `& g# \% n/ d. a if (c & 3) {
1 {2 i8 y8 b( F6 d, ] u8 *src8 = (u8 *) src;
8 m! e* j0 Z$ @ ~) J7 A u8 __iomem *dst8 = (u8 __iomem *) dst;# ]! |0 E( m8 I4 V) G
/*一次写入一个字节*/
; `1 A! F/ ]$ Y% ^7 @ for (i = c & 3; i--; )
- f" N- M/ q. Z; M; ] fb_writeb(*src8++, dst8++);. S1 Q. u, V, @: Y% x0 k {3 C& O
- H4 ?! ~9 ]# l7 W3 K) [5 S
dst = (u32 __iomem *) dst8;% Q& U3 F n0 Y
}
% I! J- l3 p, q6 b, U" J1 y. b( a* K! m" B0 T) b* o. w( v
*ppos += c; /*用户空间偏移量增加*/
( X: U4 k7 l8 T buf += c; /*用户空间指针增加*/' Q% ]" Z8 u8 I( R* \$ b
cnt += c; /*修改已发送字节数*/
3 G b j: ~# y: [5 ~; z5 [ count -= c; /*减去1页*/
+ }6 |5 {2 m, \ }
" S2 \: p) A7 _% l0 x& P6 B- G- D; n/ [- T9 h/ _* C. e2 `
kfree(buffer); /*释放buffer*/
2 k6 g' Q# k. J! A* O$ |% H! M' F+ Y% ?: z& x
return (cnt) ? cnt : err;
" m7 c1 `9 ]& P( v6 ~( z6 _* _}
2 r) D* W% }0 H) l$ i! V+ P0 u1 {4 Z
这里,做了一系列的检查之后,开始拷贝数据。这里一个有三个buffer,一个是用户空间提供的buf,一个是在这里新开辟的buffer,还有就是驱动层提供的screen_base。
, t7 c! q L0 T1 W# d数据流如下:
: v T. d3 {8 h2 T6 \# T( q* a: Z
' `. P D' h# |9 s4 ^$ M7 f D( e" W
i9 @. [4 L9 L+ u6 ]3 m$ ^( E" \ U2 E9 i
用户空间的数据首先被复制到buffer中,然后从buffer中复制到screen_base中,最后被映射到LCD上,LCD就显示响应的画面了。2 L' E$ D& z0 w8 }
2 C( W" \+ e, }- g" j+ e3.4.4 read方法- ?. J* E& |8 W5 m
该方法用于读取屏幕画面的数据。9 z {- C$ b& v) v! L3 s/ C7 k$ P
3 h' Q1 o) t& t+ S: A. w8 Q) Fread和write类似,只是数据流是反响的,就不多做介绍了。- {: z* V2 E5 A! l, i- h4 D/ |4 F
5 O. }) b; }% }# T' r下列代码位于drivers/video/fbmem.c
9 y: H4 f8 V1 V! |1 ]) ~' W0 K# v8 h7 I6 v
static ssize_t
3 @6 J# G. Z- e& [6 ifb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)4 J" o& I' t; d: D1 x j3 w
{
. L' T! e F( p0 I4 J% _- f unsigned long p = *ppos;
2 a8 A9 Y. S2 |$ v3 I" f struct inode *inode = file->f_path.dentry->d_inode;0 }& O o$ o5 E6 \/ T! E8 s; Z
int fbidx = iminor(inode);
7 g9 P5 d' E0 u. `+ W$ Z4 z, J struct fb_info *info = registered_fb[fbidx];
- m, h$ M+ U% R4 x9 Z u32 *buffer, *dst;
9 ]7 |9 b) U. A8 m, }# E- r. ~0 X9 j u32 __iomem *src;. @' I7 ^, X0 f8 o4 m
int c, i, cnt = 0, err = 0;
- b' |& U4 s' ]* h1 \, ?5 e5 l unsigned long total_size;
2 E8 u' S: \6 t# j5 z) L ]5 G' U6 }3 [% ^
if (!info || ! info->screen_base)( ?" p; L$ a/ M- ?# }
return -ENODEV; S* [; f* e- E) _
8 {7 g5 M, k7 i) _ if (info->state != FBINFO_STATE_RUNNING)" @# ]. s% w7 G2 b$ j8 P c6 G
return -EPERM;8 f! d2 P5 `' U" x* i( c
Q/ r- t1 n1 x l# k* t
if (info->fbops->fb_read) /*没有定义fb_read*/
, X$ r3 n* P2 e$ h: h return info->fbops->fb_read(info, buf, count, ppos);
! H5 _- b( W( M7 X+ _* D - x$ f# y4 b, w4 O3 \, k/ i8 Y
total_size = info->screen_size;
& J4 N8 H: s' T: m* O
. d+ G& p; H" A( C& K if (total_size == 0)9 Z: J9 h. j+ F- q
total_size = info->fix.smem_len;% e" _! q0 Z/ X9 h# w1 \+ \; n
0 X- O" P6 v4 I
if (p >= total_size)4 ~. M' `, E& A
return 0;3 ]; X- h2 |( D( f4 H
5 a3 U. V; A7 W' j- {; v) s
if (count >= total_size): g0 F" m5 X+ F) h' ~
count = total_size;& D+ j0 {# f' G* y
2 U9 q! \, |( }( B6 a& d7 y
if (count + p > total_size)* ^0 T& J3 Q4 G' }
count = total_size - p;+ N: g, y* N/ ^* @4 y
. l" m. P) ^. ? V! S
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
, e, b7 @& U1 E1 K2 N! t GFP_KERNEL);
: q( ~/ I; R; u1 [) A if (!buffer)& l( Z2 q8 `5 ]- Q% w; [
return -ENOMEM;
- T7 e9 E7 N6 o
1 F8 Y/ I: e9 L src = (u32 __iomem *) (info->screen_base + p);
1 V% x' y8 A! @8 t4 ~
* w! y, p" r3 Q% ]; p6 b if (info->fbops->fb_sync)! t# m, b' Z0 f/ z2 F$ l
info->fbops->fb_sync(info);/*没有定义fb_sync*/
P( w7 s; p1 O ]9 |' T' S4 z
+ T5 D- @( y; j! s! | g while (count) {
( ^8 ?7 M8 O0 f; E1 @, y c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
( a5 R- }, a$ c( a- Z, } dst = buffer;4 Y4 {. q x& |9 O
for (i = c >> 2; i--; )
( |4 `9 k! J' |$ g. |' U; e5 v+ r, n8 F *dst++ = fb_readl(src++);
; ^5 E5 a& |# ^2 F if (c & 3) {7 q# Y. q9 K& `9 X
u8 *dst8 = (u8 *) dst;
; e' e8 O, s s$ u/ Q u8 __iomem *src8 = (u8 __iomem *) src;
& e! o3 Y- d: X/ K# N% x- ^% ~) F( s8 n4 _0 b
for (i = c & 3; i--;)
. y. k1 _9 A" s M6 S. U- D- n *dst8++ = fb_readb(src8++);: {# P2 }- [, h+ k: O6 N
& [0 `; D' f7 }! m6 p8 S
src = (u32 __iomem *) src8;9 a, s* ]2 [/ R, u2 ]
}3 e3 j3 h6 D2 Z
a& K: i$ K+ C/ b
if (copy_to_user(buf, buffer, c)) {& r" m" [5 h" l
err = -EFAULT;
0 J/ g/ |0 R- O, z# c; p3 l break;7 p- I7 i+ L/ L5 [- R+ u3 ?& D
}$ c7 U7 j4 m. j# j# t
*ppos += c;0 b; [1 F5 S6 s( K
buf += c;
% ^: X7 V' c' K. u cnt += c;
3 A; H' j% V: ]6 j5 G3 R& \ count -= c;- e( l8 O- D$ v# t4 ?
}% k8 z& m" V* m4 r" U: l, t4 g! _
( d: \! r4 [5 V4 K kfree(buffer);( @" f. z7 `% Z/ |9 g& ]7 @
+ Q c6 l/ Q1 j1 R* e% k( L9 \/ y
return (err) ? err : cnt;
1 B% j3 p, _ k N- P" G& |}
$ N0 k/ C6 z @$ f+ [, M1 e+ h; L3.4.5 ioctl方法6 [6 S* E# d; n( n
这里只是简单的看下ioctl方法,这个函数调用很多其他的函数,详细的请自己看吧。
& X+ a' P: n9 G) q( L! O6 r) T
& V( U5 M: L0 X1 {6 w3 C5 a" C: M下列代码位于drivers/video/fbmem.c
, Q" U, H5 K3 Q1 @3 E$ E, l( ^5 u& c4 X' w# r8 M3 y3 [
static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)" W0 E8 q5 ?: y: f' I% q1 h
{; h0 G- Y7 I% F" p( v3 o3 v
/*获取inode,再获取对应的fb_info*/9 `7 m: f" R$ C& ] d6 V
struct inode *inode = file->f_path.dentry->d_inode;
& P N6 a5 q/ X: ] int fbidx = iminor(inode);
! R& v9 e5 V" w8 B8 } struct fb_info *info = registered_fb[fbidx];" V- D/ f5 i! Z. e* q
6 B$ l$ F i6 q! C. { B8 @
return do_fb_ioctl(info, cmd, arg);
* V% U0 Y+ q1 `; y}
3 L6 ]. R& i& E$ }4 W: ]* ~: L# ]; m
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,# w5 F. l& h5 t1 X3 @$ l
unsigned long arg). Y8 J$ m( N1 }- L( D0 P' O
{
6 N9 w. ]) Y1 D: W4 D8 p struct fb_ops *fb;
}! Q& c! Y- _/ f9 ^. v struct fb_var_screeninfo var;8 h8 _( N# Y; h+ ?% H
struct fb_fix_screeninfo fix;
: B, Z% T# s, }* J* R' Z struct fb_con2fbmap con2fb;6 n# ~8 `! X6 a+ ^/ b9 o
struct fb_cmap cmap_from;
( }1 y f" w- |+ B8 c# d struct fb_cmap_user cmap;1 n; J; V" h5 d0 w$ _+ f
struct fb_event event;
! I9 x6 J$ A, N; E void __user *argp = (void __user *)arg;
# R! p* |6 `6 ] long ret = 0;
3 J5 O' g- `4 E; J& o7 t, a) `# D3 A2 N9 y7 t
switch (cmd) {* i4 C8 Z9 ^1 E+ [
/*获取fb_var_screeninfo*/
+ T- N L4 O3 `7 k7 d- e case FBIOGET_VSCREENINFO:
6 x! y! i3 L. M* M6 T if (!lock_fb_info(info)) /*加锁互斥体info->lock*/% i$ r/ b: \; l p% J1 ?8 w! u
return -ENODEV;" z$ \0 u: O: C
var = info->var; /*复制var*/2 K% ^& ]7 o9 |* P
unlock_fb_info(info);
9 m/ K9 E/ u% i' _4 U: W U2 Q" L& b/ J, R: T4 v! G) t
ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; /*复制var到用户空间*/9 l y1 O- p, Z+ t
break;4 k" _) Z% W! C8 ^
/*设置fb_var_screeninfo*/. ]9 {# w% v) H& j8 n0 R
case FBIOPUT_VSCREENINFO:
( P% c/ o( ]' W/ X3 J" [3 T if (copy_from_user(&var, argp, sizeof(var))) /*从用户空间获取var*/
; k" j: i0 U# l8 } return -EFAULT;
1 n& G1 o) B4 g5 x8 `" n if (!lock_fb_info(info))
6 T0 g) E. L: i2 C return -ENODEV;" t- Q. R8 v, n+ G( D" D. o$ Q
acquire_console_sem();
. _( S" D( x. I: \' E$ i- g5 s) W( S info->flags |= FBINFO_MISC_USEREVENT;
3 w* H( d2 R0 O8 W ret = fb_set_var(info, &var); /*设置var*/
: @& o' v A6 j. F2 R info->flags &= ~FBINFO_MISC_USEREVENT;, C) S* w# r! @ |- b
release_console_sem();* E! T! h1 s; n9 C; X
unlock_fb_info(info);
1 q# H6 o9 G0 a* {* q$ Q if (!ret && copy_to_user(argp, &var, sizeof(var)))
( y3 D9 S6 x) G; y# J, H+ O ret = -EFAULT;3 b0 ~: p" ]# c8 @! d
break;
2 K( n6 v! y: U8 i) A* L /*获取fb_fix_screeninfo*/ /*fix为不可改变信息,只能获取,不能设置*/3 ]# U+ [/ k# N+ _
case FBIOGET_FSCREENINFO:
2 ~8 k/ e4 X" F5 }+ r4 _ if (!lock_fb_info(info))
3 {: e+ a, `6 d3 V return -ENODEV;$ Q; L% T. a [7 o
fix = info->fix;
8 f9 K# ~: s! E1 F unlock_fb_info(info);
% z) h9 h$ ?# m; `9 T( v7 f; Q8 m6 H T& L, }
ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
, u* Z& m2 _! b4 J+ v; | break;. E0 B9 r. M X! S
/*设置fb_cmap*/ + U, |: \9 R5 s% Q
case FBIOPUTCMAP:
2 P1 G- v1 U4 r% a* J G' @$ b if (copy_from_user(&cmap, argp, sizeof(cmap)))
0 p8 o G$ G5 X/ O return -EFAULT;
3 P/ D, h. {7 r2 v/ h ret = fb_set_user_cmap(&cmap, info); /*设置fb_cmap*/. i6 [( ~: W4 m3 ?
break;
' [9 j( }% e, U- E, E4 r9 T, X2 ^* @ /*获取fb_cmap*/
& i9 w/ J" \+ O$ h case FBIOGETCMAP:1 Q, Z$ ^& e. o" `5 I
if (copy_from_user(&cmap, argp, sizeof(cmap)))
6 I/ R1 S- ~% W6 { e return -EFAULT;
/ @* I5 I' }$ |) o& U. ~! q if (!lock_fb_info(info))
: p9 a8 V- b! O2 z* j5 d( j2 { return -ENODEV;
( B* r# D/ ]" Y6 `5 u) \( W cmap_from = info->cmap;$ O4 ~9 j; {8 g" Y! O) r
unlock_fb_info(info);9 J5 L% P4 p5 R8 r" s5 U
ret = fb_cmap_to_user(&cmap_from, &cmap);/*获取fb_cmp*/0 ^6 N. v3 c- i. J# S$ y5 u
break;
/ ]5 H% x6 u) m/ Z5 Q case FBIOPAN_DISPLAY:
`/ T; }* k# r. v if (copy_from_user(&var, argp, sizeof(var))); n$ G5 |7 A8 [8 b
return -EFAULT; X0 s/ m q# z' }3 ~# A9 _4 r
if (!lock_fb_info(info))' A# D+ S7 d! {6 J: I2 w
return -ENODEV;- r5 z$ A0 G; ?& G( o& E
acquire_console_sem();& ]+ ^; U; z) E T- k
ret = fb_pan_display(info, &var);
! c2 N f& o7 L# m1 e release_console_sem();
* f5 _, R3 K% v4 m, \ unlock_fb_info(info);4 p/ k% h- k" k# ?" n- l& W. M
if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
; \* u2 c: g/ I1 r return -EFAULT;
3 q' k$ W% }+ M- g _2 z break;
T" V( t5 M+ h4 l$ H case FBIO_CURSOR:& r; F+ L% o5 z& ]0 {0 w; n3 ~
ret = -EINVAL;1 X* A" O/ B1 W' Q" ^
break;9 p0 \' m1 ?# e$ P0 Y* Q* t
case FBIOGET_CON2FBMAP:. z x1 O' G4 Y7 P" j8 P
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
; ~2 ?1 U# m" X$ m. M return -EFAULT;
- B1 r+ L! n6 _( H) d" L if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)0 A7 ]% {$ U+ X6 M; q
return -EINVAL;
3 [% Q/ C( _* ]7 D6 \, x1 l( ] con2fb.framebuffer = -1;
: D" R" p) K+ x; F. }' }. ]8 { event.data = &con2fb;
' A2 N7 t) l( m1 \4 P if (!lock_fb_info(info))
0 C* p& B- |. n& ]6 t return -ENODEV;8 z4 H( R% P, S$ _- _6 C
event.info = info;) g& l. I+ s% c6 v( R' m6 t; h
fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);6 P4 h8 R, `+ l; y) e4 W' ~
unlock_fb_info(info);
" n0 Z$ l O1 z" d6 ]& K9 R; k& E2 T ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
+ j# a0 N2 a/ Y+ m5 m4 ~ break;3 j% y/ S- m" x6 O
case FBIOPUT_CON2FBMAP:! e8 H8 \1 W R, @7 p& O
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
6 V2 r2 m6 X5 c# o, O2 X# v return -EFAULT;: n: n6 ^" j4 U
if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)5 [! y& \+ `5 \. V. r
return -EINVAL;! V- u& m/ e, s. W' _% |5 @/ L
if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)5 W4 `/ c8 z; \
return -EINVAL;
4 B( n/ p) j/ T+ |& ~- s: H1 F if (!registered_fb[con2fb.framebuffer])! i( N% [( m: t# ^
request_module("fb%d", con2fb.framebuffer);
5 ?! w' }! j# o; v& p if (!registered_fb[con2fb.framebuffer]) {
/ r* [/ v# ^! f8 @ ret = -EINVAL;6 a$ W- |* _% J" E$ ^
break;
2 Z) b" g7 t& u( h+ L8 M9 f- H }* }) k! l% E: N1 R
event.data = &con2fb;
) p6 q4 T- y8 v& X) p0 P! x k y8 S if (!lock_fb_info(info))
1 Y3 [) z5 x2 A( I6 l return -ENODEV;
( S% z3 V7 ]! q p6 q event.info = info;* q* B9 C( x9 A$ o) J; V. k
ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);
8 ] \4 C: A1 i9 I8 \ unlock_fb_info(info);" {1 v4 j; ^6 C+ ?7 b
break;% b! e2 d3 H5 A/ ]
case FBIOBLANK:
+ O. f' I5 y0 \& Q) Q1 O if (!lock_fb_info(info))6 k; U" t* a2 r9 y
return -ENODEV;# |5 k& G; ]% T( C- ^( |
acquire_console_sem();' K) _6 K8 i5 L8 r( ~
info->flags |= FBINFO_MISC_USEREVENT;
) e( z Q7 p4 w W3 k X6 ~ ret = fb_blank(info, arg); ?*最后调用驱动提供的s3c2410fb_blank*/2 ^4 v! Q. d& h3 E! \
info->flags &= ~FBINFO_MISC_USEREVENT;
* A. P% Y/ i, t3 ]9 U) C release_console_sem();; m3 D+ t) g: _4 [% a, p
unlock_fb_info(info);
0 R& Z. T, H7 t" O M8 Q. k break;; ?$ a( b! E4 P" z) K
default:
2 }4 H5 ` \7 T1 m if (!lock_fb_info(info)): E0 q; e" [- g2 k) }) U
return -ENODEV;- G! \# S! m! `+ J
fb = info->fbops;
4 t: y: @" S* W. A9 z7 _ if (fb->fb_ioctl) /*fb_ioctl为空*/
4 Y& T0 z9 U/ l ret = fb->fb_ioctl(info, cmd, arg);" @( M5 E; Y! }3 e) d- a, r' L
else
/ T1 C) I6 L: `5 Z! V" Q! b ret = -ENOTTY;
; k" j8 J: I# O' Z6 c( w unlock_fb_info(info);. T6 C0 R# R! {, a, G
}
: t/ g% {0 ?4 _/ E7 ? return ret;
+ ^: R; h* _1 C+ Y0 }2 ?9 o+ ~}, `7 H3 q+ R1 _& C
正如2.2小结所说的,fb_fix_screeninfo只能获取不能设置,因此,ioctl只提供了获取fb_fix_screeninfo的方法,而没有提供设置fb_fix_screeninfo的方法。8 P. |7 r& k$ k' k; Q+ ?7 L3 T
' p: w, r" y; f- @$ ?! M8 j6 M3.5 小结
+ t0 W# Q- e: I) {3 S8 A! e( ^, S 本节对frambuffer的核心层进行了介绍。包括frambuffer子系统的创建,frambuffer的注册和提供给用户空间的5个API函数。下面开始介绍驱动层。( F* j/ E: E, p# R9 V& y
X' g+ l" \3 F+ q0 g4. 驱动层
! h% Y/ [( q+ Z5 m7 a本节将开始介绍S3C2440的frambuffer驱动,该驱动源码位于drivers/video/s3c2410fb.c" Z" e/ K! L' p+ p' A; F& g1 N! W8 R. h
& x& W: b f/ e( C( Y
首先来看下驱动模块的初始化和清除函数。
; D1 W7 h+ g) z9 P' {$ T* q4 f1 V. p- k7 V$ \9 Q
4.1 s3c2410fb_init和s3c2410fb_cleanup
P2 Y1 E2 R; n5 e1 Z: E% rstatic struct platform_driver s3c2410fb_driver = {
! a' @% P9 O$ B# ~0 G/ A .probe = s3c2410fb_probe,
$ \4 P* ?3 g: O% V' k$ p: w- n .remove = s3c2410fb_remove,: U+ t. q% z) o, G
.suspend = s3c2410fb_suspend,; i/ `* Q5 {5 Q- v+ u
.resume = s3c2410fb_resume,6 P5 j8 Z. V5 Q' M2 ]' I
.driver = {" _! o7 W4 Y+ s/ `5 H
.name = "s3c2410-lcd",
& Y t& y; [% D. `( n .owner = THIS_MODULE,- F1 j ~" j+ U
},
, o2 Z8 I" l S! h% Q};9 U, p2 b" j+ m, Z
/ K* ^. y8 H9 h# x4 Jint __init s3c2410fb_init(void)
) Y% H: h* o( N5 S& b- I0 E{
9 F& h6 O f5 ^4 C: x int ret = platform_driver_register(&s3c2410fb_driver);
' S; p' G, Z' T0 w
% f, v+ Q3 P& `2 O) u* U4 |1 ` if (ret == 0)3 j# E( t* l0 Z( \! s2 l
ret = platform_driver_register(&s3c2412fb_driver);;
! N& _5 A& u% ~ u
" P& j9 ~5 t1 ^/ V' b# W" X& B1 r- z' v return ret;
7 O( F% f7 [& p" B1 R! c}
3 r& ^% f8 b5 I# x6 R- b0 r& J3 S
static void __exit s3c2410fb_cleanup(void)
& i8 P* ~. `9 ?& G{
. C3 Q% I* X5 T% ?- i, s9 p. J platform_driver_unregister(&s3c2410fb_driver);
; ~- q i+ @# U5 M! V# k4 P8 F1 H! w platform_driver_unregister(&s3c2412fb_driver);5 V; }* B; \0 ?5 O9 ?0 _0 r% [
}+ t a& Q* u. K, o7 ^3 y# P3 `
+ G0 Q4 e7 v0 `. ?module_init(s3c2410fb_init);- p2 D+ ?2 m/ O5 }
module_exit(s3c2410fb_cleanup);7 K5 ^+ ^" T0 Q; [1 W
, w4 ^! ]1 c2 e+ o4 ?当platform_driver_register调用的最后会调用probe方法。接下来就来看看probe方法。5 F5 i' H) j! }- h! @
4.2 probe方法
9 s& e4 R0 M0 estruct s3c2410fb_info {
. U7 W8 \( I$ g$ g struct device *dev;" T3 g2 @8 o- y: e2 p2 I- V" S
struct clk *clk;1 E8 v V) A8 J( }
0 x% a; A l" S: i6 m4 d5 d7 u' x0 | struct resource *mem;
4 |, f9 k1 p$ A void __iomem *io; /*虚拟地址*/
' m7 c: x6 J! B' n6 G void __iomem *irq_base;
) z0 e* x( {6 I' X/ d
5 X2 p* F! |! k$ u, n, Q( O9 E- \ enum s3c_drv_type drv_type;
- c+ R0 t2 g# R$ f9 Z/ @ struct s3c2410fb_hw regs; u7 A' M; d) r0 x7 C
8 g6 X) L* G1 c
unsigned int palette_ready;
. s: I+ R8 F/ [8 U/ ?. R
4 r: c. |, ~ |4 u# r% S6 i6 A+ X& K /* keep these registers in case we need to re-write palette */
% O2 d7 Z1 X4 t0 G+ x4 c u32 palette_buffer[256];
' b. T7 V' Z' A8 N! }' s u32 pseudo_pal[16];8 |% U& M0 k0 I$ E! v
};
K* ~* e# a3 r7 s3 C* b8 X
3 l$ L2 H* F, x3 X( H- Q9 h! X5 A4 ]struct s3c2410fb_mach_info {$ s, _9 {& m' p/ ]7 m
o+ e4 G# Z$ S) Y% v/ w struct s3c2410fb_display *displays; /* attached diplays info */
4 L% Z }; N! O- E unsigned num_displays; /* number of defined displays */
% D4 D& ]0 S9 W L1 c unsigned default_display;
6 i [7 ^ s- t( G% C x, p% q9 E/ L# N+ y! J: d' J" h
/* GPIOs */
& c' z6 s& |4 D3 @. L, Y1 I. G! |/ B
! D5 ?. o9 T" `! C% e: |- ^* `8 y unsigned long gpcup;
' [2 Q0 Q" L0 k! J/ ?" Z% @2 ~ unsigned long gpcup_mask;/ r- w6 y B e
unsigned long gpccon;: d- |# q) c) _: B! a
unsigned long gpccon_mask;
0 B Y8 k" z! o& w+ X, D unsigned long gpdup;4 N# w2 b6 z1 A
unsigned long gpdup_mask;2 V9 u% }% I j- D8 O4 X- P H
unsigned long gpdcon;
% Y4 X' d/ O" X4 U. j unsigned long gpdcon_mask;
- `; X) r* b3 m6 I$ p8 j# V: n, F- r2 I' W3 c, j! ^
/* lpc3600 control register */
( k$ Z# [- d7 |3 } unsigned long lpcsel;
2 ^4 q" z3 B6 q. B0 f6 s};7 r) N1 G" b# V- g: @+ @
: x; i3 e; A, Z
/* LCD description */) G4 D, s, n# |' S7 Y# k8 u
struct s3c2410fb_display {
5 |8 {) q; I% N0 \1 Y /* LCD type */ ?& j( j( `) S$ l; q
unsigned type;, X6 }! C, H* J6 P( r6 ^
7 W) s+ Z" l, B- y$ t+ A
/* Screen size */
9 J7 o3 \% L9 u3 D. w unsigned short width;0 N" D) _& M3 \, [2 @& m+ i5 T
unsigned short height;: s; N) m2 X( y; U
( [, C% q0 }# G6 } /* Screen info */9 b. e9 I( i$ f0 S
unsigned short xres;
) G8 X' S# R- @ unsigned short yres;
+ p0 l/ ]+ F( V5 e unsigned short bpp;+ C# `: Y6 t$ i9 h4 m4 x1 W
V C( V. J# N$ X+ q. d2 F unsigned pixclock; /* pixclock in picoseconds */
- q) h" \: M* `* i unsigned short left_margin; /* value in pixels (TFT) or HCLKs (STN) */
$ N2 K$ z$ U' y y( e2 U unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */ Y0 Z0 [- X7 j* K3 w. n6 D
unsigned short hsync_len; /* value in pixels (TFT) or HCLKs (STN) */
# Q. v( l3 J' S8 d) ]0 q* ^6 j unsigned short upper_margin; /* value in lines (TFT) or 0 (STN) */5 `7 I5 B4 I% f
unsigned short lower_margin; /* value in lines (TFT) or 0 (STN) */
: h, G; Y: B% v unsigned short vsync_len; /* value in lines (TFT) or 0 (STN) */
8 w, x) q0 u) T5 N0 h6 |5 i7 J6 a. O
/* lcd configuration registers */
, \& c+ W8 X" g) V# @2 k/ } unsigned long lcdcon5;
4 Z6 j! V0 F; B! V* L) l0 |9 r};
) e& P+ K/ Q. V% ~$ ~5 D) K" [' G% s5 i
static int __init s3c24xxfb_probe(struct platform_device *pdev,
% L0 X2 K. W; g0 T- R enum s3c_drv_type drv_type)
' R/ D9 v8 o9 s* Z" g3 X{% `, i/ Y i1 Q0 d0 e
struct s3c2410fb_info *info;( M: U. w, G' I. r
struct s3c2410fb_display *display;
3 k0 e( ?, n* z struct fb_info *fbinfo;6 e* g& K4 f. d0 R b4 Q. K$ ]" e8 d
struct s3c2410fb_mach_info *mach_info;
; R( U+ \) I; w% O7 Y: \ struct resource *res;
+ {2 I% X( ~; F; |0 U- @( ?; H int ret;
* ]+ a9 s4 O/ r1 @* g- L, C int irq;3 d5 Q6 d+ [' r3 g9 v% Y: r7 c
int i;+ V: E% Z% A. y+ R4 t. O
int size;
9 ^$ p6 h# D2 E. B( Y u32 lcdcon1;
! E- W, `1 i. _( O2 h+ ~ /*dev.platform_data由函数s3c24xx_fb_set_platdata(mach-smdk2410.c)设置,指向s3c2410fb_mach_info*/
2 l. a" D. U1 k mach_info = pdev->dev.platform_data;
$ H- t" T/ W- z if (mach_info == NULL) {
& e: N8 U7 X4 o dev_err(&pdev->dev,) z' ^# p* F v/ T+ w) V
"no platform data for lcd, cannot attach\n");
2 _/ H9 |, W+ W6 Z return -EINVAL;
+ V! s+ K+ e) T }
" h) k% Y* i \# l' H" o' ^ /*在mach-smdk2440.c中,default_display=0, num_displays=1*/* [! W' L6 T- `& K0 m* f9 {
if (mach_info->default_display >= mach_info->num_displays) {
- L: Z/ Z6 u. I- m dev_err(&pdev->dev, "default is %d but only %d displays\n",% H0 E' }8 {" S0 ^; j
mach_info->default_display, mach_info->num_displays);/ q) q- Y) S# B8 \
return -EINVAL;
. y# F: B) f3 _3 e$ J7 ^% d6 j }- j5 D& }4 Z: Q& F- `, X3 a6 R2 t
; \0 h7 z% v% r; S% r
display = mach_info->displays + mach_info->default_display;! s6 V. H8 w' D) C# Z
/ J" l5 w- j& p# A' A
irq = platform_get_irq(pdev, 0); /*获取IRQ号,16号中断*/! Z, e$ A- R: i
if (irq < 0) {
+ I2 |# a5 L/ L2 u- g9 O/ J" Q dev_err(&pdev->dev, "no irq for device\n");
8 B0 f$ q( y- _. ^ return -ENOENT;. o/ w; D8 P! f$ P- t! l
}
5 y9 u# C. H3 u /*分配struct fb_info 其中包括sizeof字节的私有数据区*/0 I0 Y3 {5 o7 @& ?* |- E! {
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);7 a) x6 Z, H1 o* _
if (!fbinfo)
, O; z$ f2 ?0 q: D" b, }! a return -ENOMEM;
7 p" e" D- u+ F! L/ v
9 ^- N+ r/ A, g* ]) U* F, D platform_set_drvdata(pdev, fbinfo); /*让platform_device->dev.driver_data指向struct fb_info*/1 K9 [3 I; N y2 R
% u: J: Q; d& @5 f( @! h& [
info = fbinfo->par; /*par指向s3c2410fb_info*/
& h) d+ A! y! Z; |' @ info->dev = &pdev->dev;+ _# n1 g& } c! c8 c0 L1 ?
info->drv_type = drv_type;
$ l7 K. K/ ]. _+ J7 C1 {7 o7 L8 J- D/ c' p% d- g' h: K+ M
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*获取平台资源*/* f5 B9 P+ V* p' I) _9 W2 W3 V
if (res == NULL) {
$ W9 }# L ^8 Q dev_err(&pdev->dev, "failed to get memory registers\n");: Q# p& b, @+ T% n
ret = -ENXIO;
# g0 x) _7 T. W5 c3 b$ X q) i4 c goto dealloc_fb;9 W1 a5 ]* u& B/ g4 W8 e l: j0 s
}
& a+ U/ k& Z5 ?# w! E. g9 z
% u% J8 L% V+ z. ] size = (res->end - res->start) + 1; /*IO内存申请*/& R8 C: Z. f4 _! W W4 C
info->mem = request_mem_region(res->start, size, pdev->name);
a- j) F. i6 K/ B if (info->mem == NULL) {
$ y0 U2 C7 B& T: ~ dev_err(&pdev->dev, "failed to get memory region\n");
! l0 \! E! D; o8 E ret = -ENOENT;# F s6 O2 O4 }9 W' ?
goto dealloc_fb;6 Z6 T$ t! ^: @% u8 E/ ]
}/ Z, F0 d0 f4 b; x) N
0 t3 t# O( ~+ P! D/ k5 Y info->io = ioremap(res->start, size); /*IO内存映射,获取lcd第一个寄存器的映射地址*/
o0 X1 s! B8 ^1 R4 {/ i if (info->io == NULL) {' U3 g( c. k# u% i; M
dev_err(&pdev->dev, "ioremap() of registers failed\n"); + U1 X) C. X q' p5 E) ~! ?
ret = -ENXIO; % l' t/ j* \/ X( D4 X9 j
goto release_mem;
. j- N: b% Z, `$ @ Y0 V }
- b& T J) U9 l4 n. d /*irq_base对应的物理地址是0X4D00 0054(寄存器LCDINTPND)*/
1 e& ^0 G- D$ t% y h info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);6 l% T8 C- N" r+ P
* z# E7 j* `& ]4 E dprintk("devinit\n");
% |/ q2 q" h* K* r+ h% v
* x0 O/ T3 v" w# b! N0 K strcpy(fbinfo->fix.id, driver_name); /*复制名字*/, t) I: \7 G, z! F- e4 I2 \+ q6 l
% g; F: Z$ ~' Z- Z: c6 h1 h /* Stop the video */3 H: E# C; W N$ h0 d) o: m
lcdcon1 = readl(info->io + S3C2410_LCDCON1);
3 J9 J7 x* l& f+ F- O0 I$ q0 ] writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1); /*禁止LCD*/' ` t6 N' S* o
/ ?/ l# K0 y! C0 c fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;" k0 A9 {7 Q1 q, {& C
fbinfo->fix.type_aux = 0;, j4 ?( }# f# C1 {
fbinfo->fix.xpanstep = 0;
" J6 ~& _( \ I, E& d: D fbinfo->fix.ypanstep = 0;7 w% V- \: m& ]4 U# ~' b
fbinfo->fix.ywrapstep = 0;
1 }! r+ P' L) Y/ Y6 ` fbinfo->fix.accel = FB_ACCEL_NONE; /* no hardware accelerator */* F2 [* X- _2 v1 z; X
! v' [" I& V+ v6 A! e! {
fbinfo->var.nonstd = 0;
* p; P* l. k0 ^2 Q G, E) [2 g fbinfo->var.activate = FB_ACTIVATE_NOW;
3 r! U: c- t; |6 R N) ?% c fbinfo->var.accel_flags = 0;3 m0 t' R) T# c6 \( |! t# D
fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
' R$ W$ T9 Q0 W' x q, e2 [" J% B. Y4 O
fbinfo->fbops = &s3c2410fb_ops;7 }* f% ~& i( v6 ]# f8 k
fbinfo->flags = FBINFO_FLAG_DEFAULT;
' ]3 N% n2 E3 {) s6 | c) G fbinfo->pseudo_palette = &info->pseudo_pal;" S" c1 P, i0 a% ?: o
. v4 n3 p! T' J: L: k U$ z for (i = 0; i < 256; i++)/ z9 _7 u& K' l# V
info->palette_buffer = PALETTE_BUFF_CLEAR;( o' y4 G K. [, B/ @6 J
7 L7 J( D% \3 l' \) H9 S ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info); /*申请IRQ,快速中断*/
; @) C9 \ O, g8 l, p if (ret) {8 C' `5 H1 ]. K5 N
dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret); T0 P9 {: w4 A; P8 F
ret = -EBUSY;4 ?0 w, h: T( ] Q6 _2 q1 g7 W8 \
goto release_regs;( A' a" A" A0 _, j- U
}
! q# _. Q6 _' w' r& o/ \
, X2 y! z8 {3 Q. M; s# ~1 L info->clk = clk_get(NULL, "lcd"); /*获取时钟信息*/' G$ I) B2 l) S! j+ C4 x% I
if (!info->clk || IS_ERR(info->clk)) {
2 L" R- ]6 A1 I/ h! z printk(KERN_ERR "failed to get lcd clock source\n");3 G6 y: g4 X: y
ret = -ENOENT;: B5 ~ w4 R6 g, V1 Q- v
goto release_irq;
2 z2 l7 j8 `! k+ W6 o }" @& I9 a6 p( y. g) |- C+ ^
% {# ~8 X+ ~' j' y( e1 `$ T clk_enable(info->clk); /*使能时钟*/- [$ P. L5 N$ I- ~
dprintk("got and enabled clock\n");6 L# a' p! R( a- y
- {4 t# U {0 b6 j/ b& n1 {
msleep(1);
/ T: m4 Z0 u8 n+ B
3 n2 S! p! y# L /* find maximum required memory size for display */
$ v L' i$ L$ q+ J3 t4 @ /*在多个屏幕中,找出需要的最大memory*/
2 Z; y9 G. s0 s7 h) F' u8 Y7 J for (i = 0; i < mach_info->num_displays; i++) {6 s: C( j# X5 a6 O: m/ V0 O
unsigned long smem_len = mach_info->displays.xres;8 \8 x: v+ D% z" e3 d. O
/*所需的memory空间 = xres * yres * bpp / 8*/
/ \' y" E( R; P7 N smem_len *= mach_info->displays.yres;
, n- F' ^& v& M4 }/ P smem_len *= mach_info->displays.bpp;
& N! K j3 {) h4 ^* v" T smem_len >>= 3;+ W( }5 h, f/ A5 p
if (fbinfo->fix.smem_len < smem_len)
3 Q* _: z1 a3 K% J2 X fbinfo->fix.smem_len = smem_len;" G! ~& o B; v
}
" G9 _. e. h3 x. y* c- V# q
2 O/ y" a5 N5 C/ S3 d8 p /* Initialize video memory */ /*根据上面fix.smem_len的大小,获取DMA映射内存,一致性映射方式*/
: x9 E, x4 \8 b: I ret = s3c2410fb_map_video_memory(fbinfo);
* g8 D. l3 h) r( }- N/ a/ C if (ret) {. A, |; q2 j9 N) _7 ^( ^8 P; r+ E
printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);7 @4 }+ o' Q9 c2 c/ a$ g
ret = -ENOMEM;
p7 r1 p, r1 ^ H$ l' z9 I5 m0 v goto release_clock;
0 @- Q+ t' ^/ ^ q* f }" m% l1 q$ ^ I- @
0 a- \) _9 l: @6 @3 l: s. ^
dprintk("got video memory\n");9 j0 c6 D; G x# T T
w% @0 @# r3 k) w' B: o6 s; K fbinfo->var.xres = display->xres; /*320*/9 \7 q+ i' S6 s0 i- n
fbinfo->var.yres = display->yres; /*240*/
, ?0 u! u1 @( L, o/ u. x( r7 q& p fbinfo->var.bits_per_pixel = display->bpp; /*16*/
8 M7 H( O9 Y5 {; n4 B* z6 e4 G/ s
" I7 o( S- I* p" S9 |: E" x0 z s3c2410fb_init_registers(fbinfo); /*LCD寄存器初始化*/ ! `( [$ i/ o0 Q" e: C$ g
) c; m- [$ {. H' h$ p& } s3c2410fb_check_var(&fbinfo->var, fbinfo);0 v \$ ~8 K { G C
" y: V0 A. j6 K9 A/ r ret = register_framebuffer(fbinfo); /*注册framebuffer*/
* N5 p" x; a( _; r0 h0 ^, _+ z if (ret < 0) {! z _% q2 G* z1 t
printk(KERN_ERR "Failed to register framebuffer device: %d\n",ret);) h; u! m. ~) K4 ~5 }( H
goto free_video_memory;
3 s( j7 [% }8 F2 `9 l6 F }
7 F0 s) B2 H! q V" O! w8 T$ o5 g8 S' K5 i! p! v+ t; v8 Y
/* create device files */
$ n& ^; F# X5 ` ret = device_create_file(&pdev->dev, &dev_attr_debug); /*添加设备属性*/% w; I, Y- {# I( o a+ I9 C. o/ u
if (ret) {( I) ~# X7 M3 C) K+ L- s
printk(KERN_ERR "failed to add debug attribute\n");# B; z! [7 P3 D9 M5 J
}
- j& f7 l& c. W9 p- p; ?
0 j: X6 l7 l: K( c7 z& h printk(KERN_INFO "fb%d: %s frame buffer device\n",
& ^. l1 t U! o4 Z* d fbinfo->node, fbinfo->fix.id);
, \4 P) p3 \9 ]
9 f8 O% L3 |1 ^$ M) t* K return 0;
! F X. z' c9 ]" M# D' y/*一旦某个步骤发生错误,以注册的相反顺序开始注销*/
( w) R! ~( X. F% R: K( zfree_video_memory:
! G# H: K" M$ d0 J. o s3c2410fb_unmap_video_memory(fbinfo);; \! S ~# g. p( x( c! S
release_clock:0 o) x5 J' P; P+ `+ u* V5 `6 U
clk_disable(info->clk);
9 Y2 ]- [' [1 F$ d clk_put(info->clk);# O% z) P- p5 M% J9 X8 L) `
release_irq:
7 I% f, p; m5 Y8 i free_irq(irq, info);+ C2 |1 @0 g2 |, l% J- F; s
release_regs:; @4 [0 V! R4 G0 f7 _' n0 G
iounmap(info->io);+ a/ R5 |9 D! P( N, R4 m! r
release_mem:8 K% N' W9 j1 L3 Q
release_resource(info->mem);
. c- X0 w- r4 ]* Z* k kfree(info->mem);, X$ Q- i8 O8 ?) F: l1 o
dealloc_fb:/ Y3 V( C- D/ P" i1 a! A3 n3 J2 m
platform_set_drvdata(pdev, NULL);
; I t2 Z: u3 o4 ]/ z framebuffer_release(fbinfo); ( X$ x! b0 {, z0 q1 b
return ret;+ F6 f) R% n4 W
}9 |" I" E$ n0 b) S! u
i* r, @# K T9 g& l这里使用了三个新的数据结构。s3c2410fb_info是驱动程序使用的,里面将保存所有驱动程序所要使用的资源等。而s3c2410fb_display和s3c2410fb_mach_info,是由板级信息,通过platform总线添加到内核中。/ \3 q8 @% }: L' G8 i% P9 L
1 E# E6 W9 I+ k1 C5 `* F
s3c2410fb_display中的成员将被复制到fb_var_screeninfo结构中。
& P! s/ K* Y; r1 j5 _/ Z: v; m& |9 h0 M
该板级信息的定义在arch/ARM/mach-s3c2440/mach-smdk2440.c中,来看下, `. d% B: U! e3 Y- B5 _
% X: Y4 `6 R0 A
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
" V7 ]* \- ~ K( }) x" e
1 q; d8 C4 P( n7 Y .lcdcon5 = S3C2410_LCDCON5_FRM565 |
2 [6 V& ?) V+ S6 c S3C2410_LCDCON5_INVVLINE |
8 ]# l- O4 s9 F S3C2410_LCDCON5_INVVFRAME |
1 J# p- ]& O6 T4 d S3C2410_LCDCON5_PWREN |, P( u; _2 C0 O
S3C2410_LCDCON5_HWSWP,% \4 w! ^; x& ` F# P) u
9 \6 ]' v- s# W: b. K. x) [ .type = S3C2410_LCDCON1_TFT," `1 R2 Q1 J& K0 `/ U) Y) g
8 \: O" @4 }! ]1 D0 g3 H% o .width = 320,//240,# m% u @9 C3 {- m2 W' u3 _( i7 a& W+ @- v
.height = 240,//320,0 N# {* e9 _2 t$ J2 F
+ @: `: \( s5 w1 {6 R! g
.pixclock = 149000,//166667, /* HCLK 60 MHz, divisor 10 */* v$ T2 |' D! \+ L5 c+ v" s2 K- o
.xres = 320,//240,+ h0 G, l: J; W& f# }9 Z, r G
.yres = 240,//320,4 g0 p* C) J( _, Z5 P" k
.bpp = 16,, u) w/ `1 k5 a. v! x
.left_margin = 20,; C `% E: B8 M3 \" T/ _9 Y
.right_margin = 38,//8,4 T: [( j ?' [4 D- f
.hsync_len = 30,//4,
4 }) v" N% n' K- g .upper_margin = 15,//8,
5 J+ K. N5 l) D$ `( _2 r .lower_margin = 12,//7,
+ ]" } W4 l; c$ y3 X0 ~: y$ [3 p .vsync_len = 3,//4,
/ r; M4 F" u, |. h};/ n+ u& G" o2 ~- ^3 r7 ]( ~
: x% s) d' J9 a* x% U
static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
/ p, s2 _* V* G, [) s; o .displays = &smdk2440_lcd_cfg, N( c/ ~- I4 R
.num_displays = 1,7 K* F6 u; I5 P" I
.default_display = 0,
2 F" v2 e0 F; v6 G) A+ f* C$ z+ a8 [1 \
#if 0% @# v& [6 }: Y9 ?
/* currently setup by downloader */
# n: W" Z8 z9 C$ \) I( E .gpccon = 0xaa940659,
6 v4 Z1 |6 @( y7 f O3 I; Q. N6 h .gpccon_mask = 0xffffffff,
6 F4 x. J) [% O1 E .gpcup = 0x0000ffff, a+ `/ ?- W: J" ?) O
.gpcup_mask = 0xffffffff,' ?8 G9 ^# A$ o8 X; K. k# s8 x( \
.gpdcon = 0xaa84aaa0,
2 b6 F$ A: v/ Z! o# t( i .gpdcon_mask = 0xffffffff,; a) I, {7 P Z6 `5 J
.gpdup = 0x0000faff,
: @& Y0 r. N* e4 d8 O .gpdup_mask = 0xffffffff,
# L( y6 c! q2 L#endif
7 S1 f! V- Q8 u {8 c//no, e* {/ P4 g" X! t8 n6 \
// .lpcsel = ((0xCE6) & ~7) | 1<<4,
3 I8 \7 Y& y* ~; w$ p* s2 s+ H};
: |: o F# W+ `6 ?
c* v1 U" a. _9 J; [6 E2 w6 z* Z这里NOTE:每个LCD屏幕的参数不一样,因此上面的参数会有所不同,这是需要移植的地方。$ C5 n, b' J2 S$ y" C/ \
. G8 E1 t6 f" e
随后,我们看下在probe方法中调用的几个函数。首先是s3c2410fb_map_video_memory。
2 \( E6 e( e& ~* V/ z
: s6 _4 e' l* j' m4 G$ L/*
$ ~% b; M q _2 u8 }/ R4 h * s3c2410fb_map_video_memory():, D% Z1 a/ ?0 a9 W% U% I/ l" {
* Allocates the DRAM memory for the frame buffer. This buffer is: f4 B$ g5 C9 E. X: R, j
* remapped into a non-cached, non-buffered, memory region to4 o6 ]7 U: u- z" E
* allow palette and pixel writes to occur without flushing the, |% I6 F2 c& ~) S6 A$ X0 v- r
* cache. Once this area is remapped, all virtual memory9 Q; U# }" J& O9 i5 C
* access to the video memory should occur at the new region.
' T. k' _, s9 C P$ [. ? U/ P */! c! V# D6 Y q4 ^+ I; ?
static int __init s3c2410fb_map_video_memory(struct fb_info *info)- o7 K) C1 a; M* H0 O$ n. W
{. |* S6 N9 L+ l. u0 c& w" q l0 ~
struct s3c2410fb_info *fbi = info->par;/ e% l' _; K% Q( }8 _
dma_addr_t map_dma;
3 U/ n5 _+ q) |4 ~: }4 |) F5 j; ?0 Q unsigned map_size = PAGE_ALIGN(info->fix.smem_len);- G# O, u- ~8 _$ U. T1 a
! m7 M) B6 D+ D: Y# Z# w dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
! s% N7 r1 |4 y9 U /*分配DMA缓冲区,并保存DMA缓冲区虚拟地址*/
2 d, w: n$ t! |4 _% I info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
; M S O0 l [ &map_dma, GFP_KERNEL);% h+ y4 j& k- R: s3 ~0 Y* n
6 M n" G6 ?! f1 L: c. E4 L* R if (info->screen_base) {
& u$ h; J) V& X* h /* prevent initial garbage on screen */) q9 a' Y8 j, x
dprintk("map_video_memory: clear %p:%08x\n",
8 ]; m* X+ Z1 t, ]1 `& _8 B4 P, Q info->screen_base, map_size);
4 o- ^: L3 |& ^, ^ memset(info->screen_base, 0x00, map_size); /*DMA缓冲区清0*/
: X9 h" a' t9 _" j; ~7 d
; f5 R ]. f: f% k, X$ ]1 K0 [ info->fix.smem_start = map_dma; /*保存DMA缓冲区物理地址*/7 v- N: g+ d, F$ m$ w
, G$ r! X) Q) G dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",6 o# H* V; R1 L
info->fix.smem_start, info->screen_base, map_size);
3 G" h3 a1 C" i- x' k }
. S8 F8 {$ J+ F! w3 m* u4 K4 L
$ P0 Z! e' k! \$ S- `6 S& s# x return info->screen_base ? 0 : -ENOMEM;. H$ u S. N2 S) ^3 _
}5 I* Z% V* l1 G. S4 h' z
1 Y9 W( Z9 b, [# v1 h
3 D$ b) P9 p' {/ `
该函数根据fix.smem_len的大小,分配了一个DMA缓冲区,保存了该缓冲区的物理地址和虚拟地址。
: Q* e7 O& M9 Y) E
' Z4 w9 m- |' ]2 t接着是s3c2410fb_init_registers: Q' e/ h+ R# n4 j
1 i+ y- \' Z C/ m$ }" o- h3 r* R" [% \; O9 n; W
/*7 N; d% k9 K# T! A# O3 L# `
* s3c2410fb_init_registers - Initialise all LCD-related registers
; }7 P$ i0 _3 D8 R% `/ t. U */
7 q8 M0 q1 S4 X' e) m9 Kstatic int s3c2410fb_init_registers(struct fb_info *info)
3 ~" [, |8 c& I{
' I! Z5 [! v3 @: T2 y% f struct s3c2410fb_info *fbi = info->par; /*par指向s3c2410fb_info*/
5 u6 U" |$ Z8 J( I) R: \6 A4 ^4 G struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;, ]) k. ]' p9 L) W7 L; W8 ?
unsigned long flags;' S0 a g: M0 Z! Y: j
void __iomem *regs = fbi->io;: K! M. o5 p9 z! p* L$ ~3 p) a6 D+ B7 j( Q* s
void __iomem *tpal;
5 o1 ?6 j* y3 G* N% d8 h7 T void __iomem *lpcsel;6 b/ U' d, p$ m1 K+ X! d
# O9 r. |0 u+ x$ O6 h: G
if (is_s3c2412(fbi)) {
5 h8 i4 P" b8 z6 |) E) O tpal = regs + S3C2412_TPAL;3 W4 P6 C# g$ E, S4 j( Y- G' p1 V
lpcsel = regs + S3C2412_TCONSEL;9 n/ b/ O% F3 ]' o
} else {0 u5 ^( ]7 {' z' l1 S, d
tpal = regs + S3C2410_TPAL;9 w. r; y- `( Y# z" J
lpcsel = regs + S3C2410_LPCSEL;
0 U6 \1 m* y4 n0 u2 Q8 z }
+ b9 n+ e* D/ {' Y7 z
4 V( L, J( Q% \- X# ^ /* Initialise LCD with values from haret */2 [) l- d& w( r* N: F, a
$ B% x4 q% I4 g+ N. B9 ?; U local_irq_save(flags); /*禁止所有中断*/
; g' T( _7 G1 M' s, R4 u% L9 U
8 B+ O. \. }+ j2 F0 l6 l9 K6 b; t /* modify the gpio(s) with interrupts set (bjd) */ /*初始化io管脚*/% } ~0 p: U9 K! E, l
1 W4 G: u$ h' {2 `% Z modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);! ]7 ] Z/ S$ T! P2 @) |
modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
2 ~. O1 b$ y9 j0 g modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);: d+ d0 Y6 }% ~% T! u
modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);6 D6 Y# O) w' N) ]( d) i/ |" A# j8 L2 H
3 R2 F/ }+ B- }7 E/ e0 `. W, m. o
local_irq_restore(flags); /*恢复中断*/
" y# C/ ]( ]. _
$ l, Z; @6 W1 r0 L" ^ dprintk("LPCSEL = 0x%08lx\n", mach_info->lpcsel); /*设置TCONSEL,禁止LPC3600*/
3 i( Y1 {+ n; H' e writel(mach_info->lpcsel, lpcsel);" O% q" S0 W9 K5 N
# E. b5 E! h% M* V9 b* r" q6 [ dprintk("replacing TPAL %08x\n", readl(tpal));' F7 I+ I. F0 s) |( {# O1 n/ Z1 r( l
- \7 W5 a0 {" j2 ~ /* ensure temporary palette disabled */0 N% ?& g! I- ^; V! z3 O, Q. N9 i
writel(0x00, tpal); /*禁止调色板*/
7 z e1 I7 N* w, T2 b+ z9 |
3 E+ N; C: E! x: q4 t% t return 0;5 o2 z* g1 @, q2 m3 o* j1 t2 y% y
}
t' ^/ o6 d' B4 b0 `
; g. z5 Z L; S* B) ^% l1 Pstatic inline void modify_gpio(void __iomem *reg,) H9 m) M( `- ~ C; D6 Q1 o4 ~
unsigned long set, unsigned long mask)1 t3 A* ^3 F% n7 {
{
% ]. b0 {3 J* Y( z! B# q. c9 K unsigned long tmp;
5 @9 d Q+ o; L* y! f. ]0 u" s( x y! j8 ?! c- h( l
tmp = readl(reg) & ~mask;5 ?8 S) ^9 O5 D, F- u
writel(tmp | set, reg);5 p3 f" o! y. N. z- K
} / @' e3 B+ |8 ]* P2 H6 N" Y1 R/ |, w4 [
最后是s3c2410fb_check_var:8 o2 U9 t9 f! A$ N- B1 |
2 k" g# G. Z) c/*
5 @% t4 F% y ^2 J `( | * s3c2410fb_check_var():6 Y$ V9 _& }. Y( w* A8 f- n
* Get the video params out of 'var'. If a value doesn't fit, round it up,. }% Y, I) _ ]' G; @5 Q
* if it's too big, return -EINVAL.
, J3 J( ^. F9 m! @( ^5 ^) j5 R5 O * 检查变量的合法性
$ M( B" p1 J- B& W8 h; i2 g t */
; \1 e, e- h9 s- ^static int s3c2410fb_check_var(struct fb_var_screeninfo *var,0 ?0 N1 }& K/ O" l
struct fb_info *info)8 m0 k4 _. I/ J+ A8 p& g- m" Z
{
; d! J' x% Z6 D& I struct s3c2410fb_info *fbi = info->par; /*par指向s3c2410fb_info*/
+ T! c/ _. ]6 n; [& C% | struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;/*指向s3c2410fb_mach_info*/
1 S, w$ v, w6 m w0 W4 X struct s3c2410fb_display *display = NULL;6 Y! \) A: {7 k6 K( ^' G$ |+ R% w
struct s3c2410fb_display *default_display = mach_info->displays +
' y2 g8 F- X" L0 b: r7 F. J mach_info->default_display;# J+ ^4 n+ ]# @- b
int type = default_display->type; /*S3C2410_LCDCON1_TFT*/
, v$ R" _4 p7 a$ d& L/ y" u unsigned i;
9 k1 F6 f/ w2 B# u7 x' P
; @# U9 L% Y6 M( [ dprintk("check_var(var=%p, info=%p)\n", var, info);$ \# v% @0 F/ q: x6 W9 `- S' n$ _
; @. v$ j" h: J
/* validate x/y resolution */
5 C5 B% {4 L$ L: {0 E/ i /* choose default mode if possible */ /*var中的成员在probe中设置*/9 [) C9 W# z3 R3 t
if (var->yres == default_display->yres &&
2 l5 Y% I* X2 _3 @ Q var->xres == default_display->xres &&. P) _7 c0 D+ j- H( v' Z
var->bits_per_pixel == default_display->bpp)
. z' Q, O+ @. T" ~$ W$ V3 k+ \ display = default_display;
. }+ f. C j& r4 b7 W |6 u* x5 [& d { else
- L$ l" w) v: q/ C4 w1 [6 N for (i = 0; i < mach_info->num_displays; i++)7 }( b3 {6 C3 A0 M8 b
if (type == mach_info->displays.type &&
" \: Y; Z o5 x( j2 X var->yres == mach_info->displays.yres &&
3 z- I5 }$ @. b1 l' ~! k, U var->xres == mach_info->displays.xres &&; z. @& _2 P) g5 P) _
var->bits_per_pixel == mach_info->displays.bpp) {4 R5 A8 B0 | W: ^
display = mach_info->displays + i;& u% U' _0 V6 B% b
break;7 z+ M; i: n. y2 {: {! e1 Q
}+ I8 g# ] W" ~, F& U
a! A# b6 {7 L' p& z- o
if (!display) {9 ~! a: j Y9 K6 m
dprintk("wrong resolution or depth %dx%d at %d bpp\n",6 R2 P, P) e" b) N% _7 Y) S# i! h
var->xres, var->yres, var->bits_per_pixel);
, L; w: x' l, X0 Y return -EINVAL;
1 G+ k% S: }7 h6 P+ X }
7 o" U/ H. G, O5 G$ b3 e
, E# v. J' h1 b' p9 H /* it is always the size as the display */' H" _0 E( ~0 e: K$ q
var->xres_virtual = display->xres;
" _# c( X' L# U$ |& ^( ~9 ] Y; o var->yres_virtual = display->yres;/ n, d' T+ v8 g$ T' { r* f
var->height = display->height;8 a' |- i W% \
var->width = display->width;: P$ D/ Y5 o0 t, c: h, F. e6 p6 j
5 U; \# B9 \3 E/ H /* copy lcd settings */( n2 J2 x9 K8 u
var->pixclock = display->pixclock;) p6 z( A6 d! n( |
var->left_margin = display->left_margin;( O) x n9 j% ?8 b# _
var->right_margin = display->right_margin;. e3 v& {& E( @4 b2 c. Z, c5 `
var->upper_margin = display->upper_margin;
7 H4 p3 u; e; @ var->lower_margin = display->lower_margin;
`: k4 ~# Z" t% X' {- l$ p8 C4 y var->vsync_len = display->vsync_len;
; f( A: z7 {( V2 q" d' t var->hsync_len = display->hsync_len;
! f9 [: F0 {1 Q6 ^8 y
2 N% n- n& b" K* | fbi->regs.lcdcon5 = display->lcdcon5;
4 O4 U+ n t. U1 ~ /* set display type */
2 m) }4 Y. v+ i6 | }/ v: Z fbi->regs.lcdcon1 = display->type;
2 M: y- `$ F% E
1 i: b+ l1 L+ W- O x var->transp.offset = 0;4 E; n8 V+ _6 D2 t) e6 r
var->transp.length = 0;
d4 j) O8 b; k1 F /* set r/g/b positions */
X: s% }% W U E; q& p switch (var->bits_per_pixel) {) v8 O9 G; U3 I
case 1:
6 `" ^ C1 O p# T case 2:
8 F: O: Y }% B. g: G, M case 4:
D9 I# s( \7 I" q& f var->red.offset = 0;
4 k9 ?8 M2 `7 G1 b& i: I$ n var->red.length = var->bits_per_pixel;
* h# ^& s6 b9 }4 A& {! h3 q var->green = var->red;
: g5 @& i) e q- a3 |+ C& ? var->blue = var->red;7 k' ]! f& _% y( `+ j. g" ^
break;: S% M4 F, R# P( V
case 8:3 |3 \# `2 J' e
if (display->type != S3C2410_LCDCON1_TFT) {
( R; `+ ]) d- S4 I+ F! r5 v /* 8 bpp 332 */
+ w' W/ t& `( `8 b0 E2 V" F+ X7 _7 z var->red.length = 3;: H+ D2 |" o/ X+ l, \! A
var->red.offset = 5;- {5 y& H+ J2 Y$ p* G; E" {
var->green.length = 3;
; U2 K; k* i$ \5 N0 L# K, \) h var->green.offset = 2;
; J d: o# n& [0 G+ g' v var->blue.length = 2; Z( w/ ~, Y7 @( S$ o/ z
var->blue.offset = 0;7 ]9 t8 S( I/ j% m0 K
} else {
! B0 M4 e$ {$ Z var->red.offset = 0;
6 J% ?/ Y5 t- A/ o2 O7 d# s1 _ var->red.length = 8;
, n" a3 f/ T% W$ W7 w var->green = var->red;
; T7 ^' k1 h/ {$ N& _ var->blue = var->red;
; V/ V' C* T' Y* l }
' |- f1 b6 i! ~( Y% ~3 D0 M" l: S break;# Y* K" p5 {( `2 ^' y' W5 u; l
case 12:0 q, K( @8 f; C, u6 Q
/* 12 bpp 444 */% L* w" v: o3 x& V4 i! D4 N7 [. y
var->red.length = 4;+ h, r0 D* M5 X& U% E4 R( t
var->red.offset = 8;
; x$ t, D7 t3 ?4 ?. y var->green.length = 4;5 ^0 R8 \2 w& e* [
var->green.offset = 4;# D3 G9 r; J# ~, t
var->blue.length = 4;; z) i2 O7 \: Z+ F
var->blue.offset = 0;; o* r3 {% S6 X4 s; `% \
break;
) O5 H! G% b! P q( d0 ^2 x
$ u) k/ O$ C1 ], i2 ]1 ] default:
3 G) p, N3 j7 W ?9 O' Z0 ~' G- f, a case 16:% m5 p- _, q# [7 y) a) s; g# _
if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) { /*使用565格式*/
% W0 N: W* U9 S" Z% F- e /* 16 bpp, 565 format */, @/ G$ I/ l' a" U3 J# V
var->red.offset = 11;8 }) e+ u! {* T$ b
var->green.offset = 5;( I Z8 `( e7 G9 M& g2 u. W" K3 G
var->blue.offset = 0;
! Q- }1 ^8 U5 N1 ^- w1 j" a4 j var->red.length = 5;+ l) k2 {, T5 G* N1 `5 M J
var->green.length = 6;
/ S+ ?2 Q1 V- W4 R$ N var->blue.length = 5; M5 @# r6 N% i
} else {7 q8 @1 ^7 W& }3 s6 z2 g/ {
/* 16 bpp, 5551 format *// A! {! h- ]4 w( a9 E7 _
var->red.offset = 11;
9 j! A0 G- {7 ]+ C1 |4 S var->green.offset = 6; i' r+ H9 | z- ~
var->blue.offset = 1;
1 h5 a5 A: B* y6 p$ a var->red.length = 5;" T0 z4 v8 B2 D3 T+ \7 m. `( Z/ J( X7 y6 j$ M
var->green.length = 5;
U6 t2 v5 ?/ I& B var->blue.length = 5;6 N( D2 q# }. w w+ L8 V: d
}# q" F9 {" }9 s* D
break;
. g" h3 @# F1 Z1 U7 r$ { case 32:( E$ O t4 z" ~2 Z
/* 24 bpp 888 and 8 dummy */
( u" ^1 J i) G7 Q* a/ I var->red.length = 8;
6 ^+ E# Q4 X1 y var->red.offset = 16; W6 X" D; ^! u/ C
var->green.length = 8;
% V% |7 @5 g7 { var->green.offset = 8;% K2 C1 I3 Y% M8 g8 G
var->blue.length = 8;6 Z% n, x( u& E
var->blue.offset = 0;0 w3 J+ I+ j. {* D9 x
break;
" ]& s, @6 g9 _" a2 Q }
* a' m: T) y& B) @+ j H return 0;
( B( w! i+ x. i+ q7 b8 O2 J% e}
" g2 T9 k% b' v }& j2 ^& x, p* k3 w4 c, o6 A9 u8 T
/* Interpretation of offset for color fields: All offsets are from the right,' L3 c! W6 D5 L" i) s9 ?! f
* inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
( ^1 `. j" E/ _4 H$ t& H * can use the offset as right argument to <<). A pixel afterwards is a bit5 f: A! B2 G l- i
* stream and is written to video memory as that unmodified.$ R/ X" ~% K8 X& { h! G' l
*
/ S4 b2 N4 J& U; D2 ~: G) T1 z * For pseudocolor: offset and length should be the same for all color9 ~: Q3 r0 x7 E* c- M& z5 f
* components. Offset specifies the position of the least significant bit l4 h2 A% q; K5 H. O6 D; ?
* of the pallette index in a pixel value. Length indicates the number4 B# G+ U# z. a$ g2 D7 K0 n
* of available palette entries (i.e. # of entries = 1 << length)., B1 g( v u6 P! [5 }* L4 h; Q
*/. W7 p7 Q* F$ A. M. i# `8 {. F4 g
struct fb_bitfield {4 ]& W* M, Q3 N
__u32 offset; /* beginning of bitfield */0 [$ L/ g! W9 `# Y$ }6 R* j
__u32 length; /* length of bitfield */
# n7 o/ m9 x, C1 K. g __u32 msb_right; /* != 0 : Most significant bit is */ , V7 @) A+ T0 C$ F9 P
/* right */
& o$ i2 z3 I2 R t; y# y3 _};
7 A$ V9 j2 {, g7 C! q该函数主要将板级信息s3c2410fb_display复制到对应的地方,然后根据RGB的模式设置位域。
# @% Q. Y2 Y0 y" `( ]4.3 fb_ops方法
( d' \! t5 ^2 a( l. e在驱动程序中,定义了fb_ops,如下:9 y: Z6 j* c' E9 H! A
: G8 r: W" ]) F! Hstatic struct fb_ops s3c2410fb_ops = {" k5 U; Y+ s: o6 h; ^
.owner = THIS_MODULE,
" {$ B3 _, v0 S( _1 r3 F! ? .fb_check_var = s3c2410fb_check_var, /*检查变量的合法性*/
, c0 l I6 u. Y. |" L1 g .fb_set_par = s3c2410fb_set_par, /*将参数写入LCD控制器,该函数由帧缓冲核心调用*/
4 i1 g4 l2 s, q+ o7 O. b% C$ ] .fb_blank = s3c2410fb_blank, /*该方法支持显示消隐和去消隐*/9 M! D6 y1 I0 M& _( @- ?
.fb_setcolreg = s3c2410fb_setcolreg, /*设置颜色寄存器*/
6 E3 W3 B+ O+ r .fb_fillrect = cfb_fillrect, /*用像素行填充矩形框,通用库函数*/
6 }3 Q$ T5 _9 X. u' [! a; P; M .fb_copyarea = cfb_copyarea, /*将屏幕的一个矩形区域复制到另一个区域,通用库函数*/7 @0 c! d: a% F: b( [# R) A* t7 M
.fb_imageblit = cfb_imageblit, /*显示一副图像,通用库函数*/
+ h4 y B3 s& ~0 a};, G2 @% Y( N0 n1 z
9 b: \. P5 E6 I4 q9 g4 l
其中s3c2410fb_check_var在4.2节中已经分析过了,最后三个方法是通用库函数,在这里不作分析。+ H# u( L5 P6 T& l E
剩余三个也是驱动程序提供的,现在对这三个程序进行分析。# q* M# m. [/ {8 z; t
4.3. 1 s3c2410fb_set_par7 { s2 ^; ]0 F' m2 }
/*
' [1 Q2 D9 g- m, E' m * s3c2410fb_set_par - Alters the hardware state.4 @ n$ S/ m" Z X0 N0 }8 h+ {
* @info: frame buffer structure that represents a single frame buffer
/ [# [( w1 K7 L! T) ]$ A7 s8 l * 根据var中的值设置LCD控制器的寄存器
* Y% [8 J' _7 v' q# I' m */, ]/ Z' b2 U. Z' p
static int s3c2410fb_set_par(struct fb_info *info) r* y! h/ o1 S' ]& e" ]' y' t
{
( s: N1 B C4 A2 k R, m3 S3 b struct fb_var_screeninfo *var = &info->var;8 i0 L2 f$ m( b, W
8 v, ]: _6 N# w# [4 O1 p switch (var->bits_per_pixel) {5 [* D; _% ]8 ?! l
case 32:3 s9 ?# F0 S6 x" b. b2 g6 ?
case 16:
) \# n' K% N. P/ k4 T& u* ?* w case 12:/ E# N$ v3 P' [ T
info->fix.visual = FB_VISUAL_TRUECOLOR;, l1 F4 M* Q. x
break;
: f* Q) v9 {+ _8 C3 Q+ \ case 1:
+ e% [& |9 f1 |. o. E4 a' d, c# \ info->fix.visual = FB_VISUAL_MONO01;) v3 Q, o% T/ A/ ^* _
break;* F% b. K9 r* n
default:
4 P0 [2 s# I0 `% M6 L8 K7 u info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
7 j% c2 Z: W: W6 N8 r' j- C break;$ P! |6 F2 `5 ]; L: `; P
}8 `2 C @ A0 k; w: E w
! a: `/ ]/ J @; q ~1 m [5 h- [
info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; /* 320*16/8 = 640Bytes */
. z d; {2 |; B: K3 P3 {+ A5 l
1 q2 x' ?! B2 h2 q( w$ c4 [$ e# W& B /* activate this new configuration */
) K! l: ]7 _5 f" D6 o' t
/ C7 d7 H8 C/ h6 i; r s3c2410fb_activate_var(info);
. b) G ?5 \. X; \' R return 0;5 s5 C% y0 m; A! _3 J6 B- x4 d) Z
}
4 }( j/ s. f2 t, f y5 P. y- n0 D该函数根据像素的位数设置了视觉模式,本例为16为,使用真彩色。然后计算了每行的数据元素大小。共240行。
6 a. u5 p; E/ X- r6 N
; M" v; w! T9 T7 \4 z3 h然后调用了s3c2410fb_activate_var来设置控制器并激活LCD。/ U. O1 v5 B' B& E( [
: Q6 @; |9 Y n gs3c2410fb_activate_var函数如下:
) o% L- q, h: D1 z: E, `; D# f$ j& k! H; Z6 y- T _
/* s3c2410fb_activate_var
: `& A" D* z: n; k# m *9 |2 m, p+ H/ o: y
* activate (set) the controller from the given framebuffer
2 q& u! c. Q6 L, K4 ]) D" O; k7 G * information2 e9 ~2 d9 t1 F* W- f# D
*/" G- { W4 r& [" ~0 H: X
static void s3c2410fb_activate_var(struct fb_info *info)
0 a0 \. N3 g: {2 a5 G r{0 P' ~2 R) s: w9 @: ]; M
struct s3c2410fb_info *fbi = info->par;% }/ ?% N. s- r) r& I5 k
void __iomem *regs = fbi->io;
- E3 A# S5 i ^ int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/2 Q* W, e( f3 C, ~+ a* o8 B% d
struct fb_var_screeninfo *var = &info->var;
+ x% ^0 x, ^0 C5 U! @2 g int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
1 e ] S! T( O* {* z& K2 F0 Z0 \) o$ k* ^: t
dprintk("%s: var->xres = %d\n", __func__, var->xres);5 K- Z0 V% R7 @
dprintk("%s: var->yres = %d\n", __func__, var->yres);" T8 D3 Y7 b: C: y: o) q: H5 ~
dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);( q& e+ C# G) ?; F3 ]: O, Z9 b5 U; F
t4 I9 k7 u( d" `3 p. R, V
if (type == S3C2410_LCDCON1_TFT) {
) f0 S3 \9 K$ r; a: H# x: A) j s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/" \; F. a- w- z2 _
--clkdiv;! V; [% {) m2 v- c {
if (clkdiv < 0)9 G$ `; Y- g* } d7 i/ b
clkdiv = 0;
; A! x2 P4 @6 k# Q7 C# u8 D } else {
2 v; ?. P: i; `" D7 ` s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);) S1 N8 H2 i6 j1 ~* F0 `
if (clkdiv < 2)% i0 k# {$ v' V3 l5 }
clkdiv = 2;
3 F) |/ e3 w. A; j" } }
% M$ K4 l7 N$ P$ I3 ~! F8 ?+ c* v. u$ h6 b" u
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/8 M" m9 T l1 o0 A/ G' T4 y
o& L o' k/ T0 S h2 m/ M/ y; I& Q
/* write new registers */
! L# _# R. V! V$ n! n3 W0 {5 @& P) r& s9 i( A0 a- ^7 c( F/ g
dprintk("new register set:\n");
- l4 K1 ^) `& d" i7 @% I- X dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
- t% Q; K# |$ k+ C dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);! Y' x: P& V# A
dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
O) W) k' n' F2 t+ O dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
9 e& i. u9 Z- F dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
1 I8 l) V8 G9 \! n /*把计算好的值填入LCD控制器中*/" e: ]- }( h4 Y N* r& \( x9 M% a6 y7 e
writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
* S7 p2 h7 T; k regs + S3C2410_LCDCON1); /*仍然禁止LCD*/
2 d4 a' v: M! }0 t writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);% n% s. A( i' u% Y# P
writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
& n: E% F" k/ \' E( c0 {5 O: p writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
& z4 u) ]4 m i0 A writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);% Q6 C. L" a- ~9 m2 t
% I; }" c; E( V) B. t /* set lcd address pointers */
" S+ M1 }% f* \* b s3c2410fb_set_lcdaddr(info); /*设置LCD帧缓冲起始地址*/
$ j( T% |5 Z0 v3 j$ n5 T3 s
2 D0 P8 D( w, Z6 V. p) p# M fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
$ J+ l3 W) C$ y5 |/ A' S- |$ s6 b" B% @ writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); /*使能LCD*/. }* B \/ D8 R- @" G% n- a6 E
}" u6 X. U8 H, N
其中调用的三个函数如下:$ y3 w x' K, V5 E) A; _
static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,$ u$ s) p( {$ r; P, d
unsigned long pixclk)
! b" |. C& k7 U7 I1 H) s8 d" P{/ C( G! J+ L) W5 Q/ [3 s
unsigned long clk = clk_get_rate(fbi->clk); /*获取当前时钟频率(Hz)*/) t# ?" I, e$ I" k G
unsigned long long div;
$ c# V, |$ e" {% e C
! A2 j# F/ u' U/ j /* pixclk is in picoseconds, our clock is in Hz
, i/ y& i: |: B+ w *9 a8 e. |9 Y3 s+ v
* Hz -> picoseconds is / 10^-129 |2 t; [9 J9 l8 G5 ?
*/
5 i9 o# x/ j# F1 l, H' ? u
7 P+ u/ d1 j9 f( U: ^7 K" X div = (unsigned long long)clk * pixclk;7 l1 o* X. O Y3 Z
div >>= 12; /* div / 2^12 */+ e2 h) E+ O! U$ A0 H& z' m. v" Y
do_div(div, 625 * 625UL * 625); /* div / 5^12 */$ Z0 s: z; ^7 r) [; R- o- V
! y- b* i/ f$ q' S' `) p C: M
dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);* k( T W: V9 {6 b( ]& z/ S$ A ^
return div;1 K j! ?4 ]& J/ K6 p2 K! G/ I
}
' V5 Y e# c- i4 [* d, s: W7 R& U( E1 M# C9 Z; E
/* s3c2410fb_calculate_tft_lcd_regs% t6 j! O7 x9 m- i, D
*( o' ]5 W$ s) m5 j% C
* calculate register values from var settings$ B3 B" S2 Z/ d2 X( v) P) \ D$ ?
*/( s9 ? K& ~+ I: H. O& J9 X( j L6 `
static void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info,7 G9 l0 ?. h, {" ^ I2 r+ C5 U
struct s3c2410fb_hw *regs)
2 _1 o, p1 C6 i, T* D{
/ v* B. j4 u5 l) F7 t" b const struct s3c2410fb_info *fbi = info->par;: u: ? m, ]' D- X" Q
const struct fb_var_screeninfo *var = &info->var;: P) a. ^( t8 t
% U9 v& @& i. M- J" W switch (var->bits_per_pixel) {
9 p8 Y7 O1 _) t3 A* i, P( ?" ]7 p case 1:
! |) y$ j1 S" M, Y0 S) G* o regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;" ^2 Q* B9 S/ Z9 S; B% n+ O
break;) R7 D4 q" F } o
case 2:5 M& T' ]1 x* q) J" h
regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;' H' T8 _. S5 W
break;7 x$ O: F! ]0 o5 B% w2 y
case 4:% e* ?4 {2 K4 H' x- Q
regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;# p6 w; a* ~4 o: Y. e8 a: c5 g
break;
% K! @5 ]! G0 z4 j; p) o case 8:- M8 u6 Y, }; p# z, T
regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;1 x! E4 X( Z5 `! J# `: r$ c
regs->lcdcon5 |= S3C2410_LCDCON5_BSWP |
. _8 }9 L+ Q: i4 n% j4 ] S3C2410_LCDCON5_FRM565;/ {* K! x5 I2 W' J( @- R: V
regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;' I3 d* h/ W; p) B0 i! j) ]5 g( e
break;# \/ b8 h( B6 {3 q
case 16:5 o) w8 k0 ^. y( A' p
regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;( V( ]2 X/ X0 o7 @% p- O4 b
regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;
/ S% h) K: h0 E0 \ regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;5 h8 N8 w) F, A- z# R( ~: `1 q' ]
break;# Z6 D0 r0 R( h6 x- ]& E8 i/ X
case 32:% c; i1 d6 I* J
regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;7 U' d. b* U3 M& g7 x- C' W
regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP |
: p% z, N2 J- o; p3 `/ m; M S3C2410_LCDCON5_HWSWP |5 F0 |: I8 d* }3 A1 e
S3C2410_LCDCON5_BPP24BL);& d) k: H& q: v5 U' N7 J( y
break;4 W0 y: ?- z9 P5 b: U2 j
default:6 y1 l$ P: b( n: d" f x- G" s/ g
/* invalid pixel depth */! m5 \3 [7 C8 s/ B7 F
dev_err(fbi->dev, "invalid bpp %d\n",
+ Y' o& R& N4 H% A var->bits_per_pixel);, q/ u- G0 j$ P1 L T' S/ x/ }- ?6 o
}
4 X4 c* @% |! L u /* update X/Y info */
, y% l6 q1 a" ~8 C) s9 q% O7 t. ~ dprintk("setting vert: up=%d, low=%d, sync=%d\n",
; E7 Z# q; Q& z7 S$ Z! W9 p var->upper_margin, var->lower_margin, var->vsync_len);
0 a) E' O! y" ~
) }0 t- X7 U: _# z- r3 t9 J: G7 r. ]6 { dprintk("setting horz: lft=%d, rt=%d, sync=%d\n"," q, @4 b( Q6 Y: f" O7 u
var->left_margin, var->right_margin, var->hsync_len);. \& \2 \' a4 o) @
/*/ a9 N4 _$ F" s& |
所有时序参数必须减1,因为在公式中:8 N, h1 P+ B+ x! c8 K9 ~3 H, J( r# q1 H* E( T
Frame Rate = 1/ [ { (VSPW+1) + (VBPD+1) + (LIINEVAL + 1) + (VFPD+1) } x {(HSPW+1) + (HBPD +1)6 h* d! d6 |; i+ S. W( p2 c- P9 \: j, B
+ (HFPD+1) + (HOZVAL + 1) } x { 2 x ( CLKVAL+1 ) / ( HCLK ) } ]; E5 b. z% @. P! w
*/
3 T9 _0 g: j6 q+ h" W regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |6 N& e! D6 f3 ?8 ]0 m! `7 S# o
S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |
3 C* r' s- r! S/ P S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
/ u( G$ g+ V r2 ? S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
2 o8 L! a3 z! i
/ }! n3 ^- L: Y0 W2 o) g regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
: M$ O |$ ?1 A" u) x) O S3C2410_LCDCON3_HFPD(var->left_margin - 1) |
, Y, D+ Q0 x" `6 G( E9 W S3C2410_LCDCON3_HOZVAL(var->xres - 1);
% n! Y9 d3 l- h1 g" o& ]2 m# W2 }& d2 J
regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);
9 C1 @6 n5 ~4 k: Z, w; k0 k}
7 H2 r1 G. }2 n# G4 H) z. d% B2 x# l5 a% D. [; `
/* s3c2410fb_set_lcdaddr
. r9 G/ @& F: \: b# g+ U4 @+ J, N *
. v( L5 x1 E+ s4 C! Z$ r * initialise lcd controller address pointers! c/ j* D6 F1 a1 l$ F5 ^
*/) h) W' \0 h- K) B
static void s3c2410fb_set_lcdaddr(struct fb_info *info)+ K8 w1 J' q- b8 x, G: ~
{
* x j' p1 C+ K unsigned long saddr1, saddr2, saddr3;8 P! L" O, p2 U' F/ \5 _; c
struct s3c2410fb_info *fbi = info->par;
6 i u. z' H; A void __iomem *regs = fbi->io;2 Z' a* k9 [- I) S
" _( w; F" V" ^- r8 [, C
saddr1 = info->fix.smem_start >> 1; /*帧缓冲区起始地址*/, t/ b! J S/ J I2 c8 r7 h
saddr2 = info->fix.smem_start;
: M y- R2 L% d4 x2 x+ j saddr2 += info->fix.line_length * info->var.yres;% `6 p6 [! d- C$ j/ q7 M5 ~. f
saddr2 >>= 1; /*帧缓冲区结束地址*/
* O# [) }5 U {0 e/ b- t$ G
, N( G$ [- s' e0 |9 v saddr3 = S3C2410_OFFSIZE(0) |, D. g( f) E, i! b2 w# u
S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff); /*offset = 0, pagewidth = 320*/. g, o! q7 \1 \) j! }" H. p
/ M& U/ J; B( [" M5 v
dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);9 \ y! N* c' V2 p7 B9 m6 y" T
dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);
; B: D% z4 Q/ F9 [ dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);' d& g$ t4 R9 R6 w3 L3 Q! n
* u1 q7 T D0 }- A3 U5 `
writel(saddr1, regs + S3C2410_LCDSADDR1);
( O, J1 `+ z. _0 F/ T0 N' ? writel(saddr2, regs + S3C2410_LCDSADDR2);
/ H1 ~# [- z+ p* j writel(saddr3, regs + S3C2410_LCDSADDR3);9 J/ C" s' K9 G+ C7 b
}1 i. U6 \' t J: V' p; N
s3c2410fb_calc_pixclk用于计算时钟频率。
2 f5 D" A3 _# }* Q7 r1 p J
. I2 s' K; O% W0 Q1 j' ^0 ms3c2410fb_calculate_tft_lcd_regs设置了LCD的控制寄存器。9 l# u! L* V9 B# q" e' R$ q7 m
' X/ |2 g. x6 l% S' V' s9 t
s3c2410fb_set_lcdaddr设置了LCD的地址寄存器,该寄存器的设置请参考datasheet。
; X& p2 z7 K$ z. H5 o- h! |
' f3 Q$ s, `+ s) z4.3. 2 s3c2410fb_blank5 J6 A P9 p& D+ [! T9 d' j
该方法完成消隐功能。% e' `7 @. b) u0 S% T' _3 R! `
5 J" ]6 U% R1 I# E
/*
& r! r4 `2 E9 _ * s3c2410fb_blank
4 H" o- D2 W+ d3 { * @blank_mode: the blank mode we want.
( w' k) c: d7 {1 { * @info: frame buffer structure that represents a single frame buffer
1 y- T7 l+ V% C7 Z" Z+ r *
# p5 u% ~ L) c- U' l4 c * Blank the screen if blank_mode != 0, else unblank. Return 0 if
. H2 ]# H% Y; x4 C2 N! F * blanking succeeded, != 0 if un-/blanking failed due to e.g. a2 c5 ^9 O: O3 U# l, Z( ?
* video mode which doesn't support it. Implements VESA suspend/ v' U9 R! |9 ~8 o3 c% f
* and powerdown modes on hardware that supports disabling hsync/vsync:
9 V- }8 f7 R N7 K( w *+ c6 x& x: r, k: S( U- c
* Returns negative errno on error, or zero on success.5 M3 B% \, O! k, Y
*
9 p: [2 p. O" g' p( G: |* _! M/ o */
% k) ]: B- Q3 i, ]static int s3c2410fb_blank(int blank_mode, struct fb_info *info)
- y! N" T3 `3 Z6 S% K% l: K& Z, e{! z7 ~: n( y. p5 c7 S1 _0 g" z
struct s3c2410fb_info *fbi = info->par;$ [& W: i( {5 N$ g- ~+ E: b
void __iomem *tpal_reg = fbi->io;' M. d& q2 Q H, Q7 @* ?0 k! m
8 J) ~ P& s7 X dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);% p3 @3 x& Q( G9 w- {# T! C( x
k' \5 H$ o/ R: I/ P4 \
tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;0 X$ ^' \, _( C; @
% p- Y3 @7 M( l6 }- Z if (blank_mode == FB_BLANK_POWERDOWN) { /*消隐*/) u# _- J i3 l
s3c2410fb_lcd_enable(fbi, 0); /*禁止LCD*/! K+ V& h/ I2 A
} else {( D+ Y! O' y$ j+ E' h+ Q/ O
s3c2410fb_lcd_enable(fbi, 1); /*启动LCD*/
" [) b. e% m% E6 P) K2 V7 F }" N+ E7 l# B3 T) p" W5 I
5 M5 k$ [4 a. l9 Q if (blank_mode == FB_BLANK_UNBLANK) /*去消隐*/
% z# d) `8 v. I writel(0x0, tpal_reg); /*禁止temporary palette*/
: s' A+ F, f( Y/ b" `8 N else { /*消隐*/: `3 ~2 V; k: p2 G6 g$ g
dprintk("setting TPAL to output 0x000000\n");
/ Z6 D: I& s6 \ writel(S3C2410_TPAL_EN, tpal_reg); /*使能temporary palette,颜色为黑色*/
& J% Q! Z( e: I6 e2 C9 T2 b& u }/ m) W, U+ A$ b/ r, c n
" z/ {4 f# Y- K& T/ l( ~$ ~7 E
return 0;# r' D" m G$ g7 {
}
0 i" W5 g: V6 i3 i1 Z在消隐时,屏幕将全黑。 G! y$ [3 s) h
" p: u% ?: h8 u
4.3.3 s3c2410fb_setcolreg) j; c r/ A2 \, s1 `0 v0 O
该函数的功能用于设置LCD的调色板。调色板的概念请看我的转帖:LCD调色板。
2 e3 R6 C2 B/ g6 _* ]
2 E& j! _# w" B& mstatic int s3c2410fb_setcolreg(unsigned regno,
" s, K5 a2 ?4 \ unsigned red, unsigned green, unsigned blue," X% e( m( `& c! Y
unsigned transp, struct fb_info *info)
: i( ~- _3 q: l" q{
- l; C3 t; w \% V6 P0 q# ^2 S struct s3c2410fb_info *fbi = info->par; /*par指向s3c2410fb_info*/. i* }6 h# b1 W: p% Z# J' x4 o' I
void __iomem *regs = fbi->io;! Z5 X3 ]: e* q% d
unsigned int val;
- n2 x. L6 b6 E0 n; n/ P0 B
0 }+ o, P% ~2 q+ y( h% `$ J /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
" \& {0 U1 g2 g5 Q/ V8 R) a- f" t regno, red, green, blue); */
% I4 y- Z8 ^+ E) d- R3 N. A
) n1 U3 Z' z1 f) e8 c9 e switch (info->fix.visual) {
7 b1 f, _* x' i/ m5 v case FB_VISUAL_TRUECOLOR: /*使用真彩色*/
4 D3 b9 r0 u j r' f; e /* true-colour, use pseudo-palette */' J- w( {3 S2 U8 ^8 y
8 v* t+ ?6 ]" M
if (regno < 16) { /*16种颜色,为什么只用16种颜色???????????*/
" m3 a- C$ {8 p; J/ z! J' t- Z' M, X u32 *pal = info->pseudo_palette;
. e, h2 T \6 i c- A
% t1 ?' l( {# @; d0 \5 J val = chan_to_field(red, &info->var.red);
* W( L) E" |& R0 m! V+ T3 i& q- o val |= chan_to_field(green, &info->var.green);
7 ~0 `! b5 c, i6 |. ? val |= chan_to_field(blue, &info->var.blue);
: g7 f7 Z( s5 y# T8 ?9 O- _! d) B8 }' E) }9 g+ B" t' k% M
pal[regno] = val; /*保存颜色值*/% ^8 f3 ]( p7 ^( \: P- K1 O' X
}. }: L4 m# L* P( B# g6 J1 E
break;$ G4 n& w) b* \) M0 U' r1 c
5 j6 e# p7 L: b$ ^
case FB_VISUAL_PSEUDOCOLOR:
$ Q9 I9 U2 |+ K' d# q+ e& s if (regno < 256) {
3 |# a$ E4 T! D& o* m3 ` /* currently assume RGB 5-6-5 mode */; p3 e w. } g4 Z7 N, o+ Z( y
4 S% r$ E% E5 a$ m9 j) }: p& Y
val = (red >> 0) & 0xf800;+ D4 q0 H: |( X, h( q
val |= (green >> 5) & 0x07e0;
: L0 K% Z% Q, T2 T0 N/ H val |= (blue >> 11) & 0x001f;3 L+ }, n9 s& c6 ~8 {5 ]8 s. X
. N$ l* q2 X6 J5 Z J1 Q6 T& l9 ~ writel(val, regs + S3C2410_TFTPAL(regno));& h/ Z/ n6 h- `' a/ v/ E
schedule_palette_update(fbi, regno, val);
2 f2 ^$ ^# z; F, m: n }
# y, \4 y* c7 k, ~. N
( g4 G& T' n* R; k% ` J break;# ~, U t% A5 ^3 G7 h5 F/ P
m' [$ i* t" e' M& g5 @& n default:
7 Z7 K1 z$ |: x4 F* Q" Z' C4 K return 1; /* unknown type */5 x" z0 B0 D# P2 @5 \# t
}
1 w) ^" F( L+ m
4 h: V1 F! ^5 r4 h! a5 m5 M return 0;$ q" l$ E2 n% m& `3 D
}
g& @2 A5 A' n1 T: v/* from pxafb.c */
! V$ `: C9 q8 l3 Fstatic inline unsigned int chan_to_field(unsigned int chan,! M! n* H7 V, f
struct fb_bitfield *bf)9 w& r0 V- p* q9 q9 I7 N7 V
{ 4 ?2 W5 V8 R* n+ U2 @& f3 U2 ?
/*下面用到的length和offset在s3c2410fb_check_var函数中设置*/
3 s# @9 f1 j6 L. c chan &= 0xffff; /*取低16位*/
/ [- i6 a( G, B2 @9 A chan >>= 16 - bf->length; /*取第length为到16位为有效位*/
3 z7 M, P- E9 v return chan << bf->offset; /*移动到相应的位置。*/
( I- V k7 w. H} Z, l8 J! A2 A' K7 d8 l
我们使用的是真彩色,可以设置16种颜色,颜色的位域值通过调用chan_to_field获得,然后保存了颜色的值。但是比较奇怪的是,这里并没有将值保存到0x4d000400为起始的内存中,不知为何。 反而倒是在伪彩色模式下, 使用了writel(val, regs + S3C2410_TFTPAL(regno))写到内存中,然后调用schedule_palette_update函数来更新palette表。我们来看下。
& ]: \( V' R# e) ?/ i' T. ~0 o# U
& t$ R* U& x2 c( l- pstatic void schedule_palette_update(struct s3c2410fb_info *fbi,
, ]/ F2 n: S* o4 e: i unsigned int regno, unsigned int val)
0 L+ D/ W" o. b6 [3 Q2 R; s{
6 U" R. D$ }5 R- [6 l+ u0 v2 L unsigned long flags;: \- `2 s/ u8 r
unsigned long irqen;
5 B6 u+ Y2 N5 c: @3 R) F! q void __iomem *irq_base = fbi->irq_base;
. V C3 f4 ?" S" {9 P* A, e
# @) Q s5 L$ z# b# i$ g M local_irq_save(flags);
: o( Y3 I5 Y; O- B& x+ T2 m6 {# h; i/ s: x3 p. ^
fbi->palette_buffer[regno] = val;+ O3 j% a3 l3 G4 K0 v/ V
3 u" q! R( f: G: U, T( r& f8 a if (!fbi->palette_ready) {
1 P- E, V$ L+ k& j0 r5 N5 @ fbi->palette_ready = 1;4 o2 ~% @+ F0 `/ [8 \% J. C: P
# h) ^- L: h6 J4 V' W
/* enable IRQ */4 B. b1 T9 c/ `; [
irqen = readl(irq_base + S3C24XX_LCDINTMSK);; h+ S) f& N1 H' L' f3 R
irqen &= ~S3C2410_LCDINT_FRSYNC;
% ]% B5 R' x* \, D7 a writel(irqen, irq_base + S3C24XX_LCDINTMSK);
9 Y! ~# b; M( D2 B! o5 T" s }. M2 y* Y! F2 t W/ c
9 T! Y3 U9 S1 l4 m! C4 r8 f4 P' h9 a$ l local_irq_restore(flags);0 v' W+ N) h2 }" D n
}
* t) I1 @' x$ n这个函数的作用就是开启了LCD的帧同步中断,这个中断在VSYNC信号从无效变成有效时产生。当中断产生时,会调用在probe方法中注册的ISR。ISR如下:
- ?* t# g- ?% l, ~$ g/ D M4 |static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)5 o: s5 k$ d+ y- t0 f
{! Q8 H' Y0 e9 `: i. F- {
struct s3c2410fb_info *fbi = dev_id;6 Z8 r \: j1 @) m7 q: r. Z
void __iomem *irq_base = fbi->irq_base;% O( a1 B1 s% S# i: G: v) j+ u% R
unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);
) o7 V! ?7 J8 a) b" @% @# wb
; V& N# J) \4 g' U" [ if (lcdirq & S3C2410_LCDINT_FRSYNC) {. _: W* g3 i( L6 w( s# j( _
if (fbi->palette_ready)
4 r1 y' I/ O6 [1 O3 Y s3c2410fb_write_palette(fbi);: Q* t1 w( |: l# J1 i% T# s
+ k4 `/ _, w' \" e. G6 r- m
writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);
/ Z& b' u- H- z% k3 G* B4 ` writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);
; [- e! c3 t6 ]+ `" y$ U }! X: N) \% @# U1 R6 [
* {8 ^" P1 d( y# ]% i# p; a return IRQ_HANDLED;8 | ?* Y9 b! Y: l+ o
}
" a7 x; O j# e" I5 p Y% l4 b1 j: C) A
static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)
/ r" }+ R* x: C3 v{6 R" W$ W4 Z! a! v' t( d' B
unsigned int i;
6 n& v+ T' Q. Q5 |2 E# q n void __iomem *regs = fbi->io;4 U* g# P$ d* G
$ |! {: `) m; b fbi->palette_ready = 0; /*清除ready标志*/
' z# E0 b* a6 ]- A9 o5 t
# E1 h" u1 o2 j& ]# l for (i = 0; i < 256; i++) {6 o7 z9 r7 P6 \" m& a
unsigned long ent = fbi->palette_buffer;/ o$ Z- _# l1 D3 F% _# \
if (ent == PALETTE_BUFF_CLEAR)
- \) o. A: u% n4 \ continue;/ q5 I" b$ G$ ?! k) ~- F8 D1 W
% T6 ]6 ?) o) m* d. P6 w1 J
writel(ent, regs + S3C2410_TFTPAL(i));
# n2 E! T! u9 J5 F! r
9 f7 _) ^! b7 Y! p4 [( i( C/ f0 O /* it seems the only way to know exactly9 X# a# R. u5 [5 N9 f& f4 @7 C
* if the palette wrote ok, is to check
5 t2 ]5 Q9 \* F/ Y5 d * to see if the value verifies ok3 s0 A7 c1 M" R: S8 i7 `7 g/ Y
*/
' Q6 ]0 D; ]5 ]& ^
; J# e+ P, d5 m3 }+ W/ m if (readw(regs + S3C2410_TFTPAL(i)) == ent)
$ d# H- C1 x. ]( ^ fbi->palette_buffer = PALETTE_BUFF_CLEAR;! J8 z0 D, ^! Z3 K
else2 a5 a. `6 F6 x4 U; G$ u
fbi->palette_ready = 1; /* retry */
6 q, L8 |% X0 W7 V# |( b4 \1 R }
& M8 o7 o4 [, O( G}5 ?! [& L& |4 [9 q
在中断函数中调用了s3c2410fb_write_palette,该函数对写入内存的颜色值进行检查。如果确认其已经写入,则将palette_buffer中的清除,否则retry。
) d" F) A' ~) y' c
& D" ] V: T# Q+ d7 r5 I5. 总结( C" L+ T: P) L1 U
本文对frambuffer子系统做了简单的介绍。frambuffer子系统的函数相当之多,在这里是不可能一一介绍的。本文首先介绍了主要的数据结构,
. C& W. ^5 b" ~, u" q7 W/ V, j3 c% w; A2 K
随后分析了frambuffer核心层,在核心层简单的分析了5个API函数,接着,对驱动层做了介绍,驱动层的大多数函数都给出了分析。2 }/ i* ?: w% G, u9 m
; R1 F5 b3 h$ `. H$ {
; i/ n; k" m3 H/ g
! J) R9 n1 j( ~( o- n) n7 M9 l3 O+ [" K3 `3 Y. p+ g- m3 V, f
# z) s# v' V8 x* p; [
7 C& F0 k/ M" \/ P, v& C$ `9 G: } c
! P0 h/ f; A0 k7 g0 N$ ]; |
|
|