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