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