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