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