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