TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
3 R- H" ]* f* y5 v8 Y% S& ~
上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构。
% C$ |3 c9 Q% ]+ d5 B, h
H0 a+ C* ]# a2 i0 z& K; B1 O上一节文章:linux lcd设备驱动剖析一
# ?0 m0 k4 X. y6 E( a$ l9 k, p( S: K# l4 z
上一节讲到probe函数就没继续往下深究了,这一节里,我们来详细分析s3c24xxfb_probe函数,整体分析如下:
5 F( K, }1 I w- }1 F+ u% p5 B1 Z/ ] k. ~7 @: q9 Q9 R
* X3 ~9 G6 }- B7 N5 |. u
static int __init s3c24xxfb_probe(struct platform_device *pdev,! ^4 l" D1 J1 `" G8 R" O8 v" g
enum s3c_drv_type drv_type), e% F# h' X }+ Z
{
( k2 }9 _+ p% A struct s3c2410fb_info *info;4 I8 E9 C# c J+ D2 w
struct s3c2410fb_display *display;( M, @0 H# c& q4 n- t
struct fb_info *fbinfo;9 `/ y( x* L% @4 D
struct s3c2410fb_mach_info *mach_info; /* 包含s3c2410fb_display */" R4 C" }* ^1 q) i
struct resource *res;, Z$ n: j' c- u2 e4 w
int ret;' D; E& S. J& z& W, y
int irq;
: K* m! O4 P4 `$ q int i;
& \1 J; W I! P int size;1 h8 Y7 \ y4 q. p" h5 y2 ^' D
u32 lcdcon1;6 X* n3 k8 }/ c! p+ V9 w6 {7 V! ]8 }
5 ~9 m0 x" t+ l4 b" J /* s3c24xx_fb_set_platdata()里会设置platform_data
- R8 S1 m' S& H0 h, C * tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);9 D5 o& M5 l) `5 D) e% `
* 所以这里传入来的platform_data就是tq2440_fb_info结构体实例
& d9 f6 i. q! }* ^/ p( |2 ]5 y */
' d+ A, r" m; Z$ g! i: n5 c mach_info = pdev->dev.platform_data;
0 A+ j6 L; R$ M" c
- W/ v- O5 U& B: q1 m: D- n /* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL */
! p h5 U! u* |: u" F if (mach_info == NULL) {# C8 C* m/ a1 x s; f
dev_err(&pdev->dev,+ H2 I9 O2 n. k2 z! t' Y
"no platform data for lcd, cannot attach\n");
( B' r/ r" j7 H* e5 o return -EINVAL; /* 表示无效的参数 */4 [. ^% t2 |0 t9 k
}& _6 w5 |, G& o9 G
1 [" |2 S# J [) x: A /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */
, s- J9 c7 V1 K1 N8 d if (mach_info->default_display >= mach_info->num_displays) {% `/ N8 ~) R* _! T
dev_err(&pdev->dev, "default is %d but only %d displays\n",
: L4 A: A3 t7 d+ E( }5 ?3 U mach_info->default_display, mach_info->num_displays);( E9 d7 m* y: a. R) M# v/ b6 w- w
return -EINVAL;; s! W3 H! U9 I2 s# p/ }8 C
}
( k& Q* g/ h4 i" E 9 ?/ s T: X2 G) K& F) o/ c
/* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */( P% c7 g2 h) \; F6 w* z0 S2 u* J
display = mach_info->displays + mach_info->default_display;/ I& J( x2 [ d9 b
0 i9 v7 X6 w7 ] /* 通过平台设备platform_device获得IRQ h2 Z# d, Q) S$ a5 J o$ K A
* platform_get_irq其实是调用platform_get_resource(dev, IORESOURCE_IRQ, num)" j% {- E6 ?# p
*/. [: ?5 p6 Z- d* ` U
irq = platform_get_irq(pdev, 0);. L0 F$ M: P- m2 O0 b R
if (irq < 0) {
m) O) h: G, A7 B dev_err(&pdev->dev, "no irq for device\n");
2 e5 d4 W: p8 J. R5 ?: r& V) K return -ENOENT;5 ^0 _: ?8 A3 t. ]% I+ I2 G1 t
}, C; r7 u* V# A
3 [! L( j9 s4 ]- M! U, t
/* 分配一个fb_info结构体,第一参数不为0表示,额外多申请的空间1 @( y+ I8 }; x2 h/ T
* 用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据! W. t. @' p7 p/ C& U' R, Q E
* 比如:clk,resource,io,irq_base,drv_type等额外信息
' }& X- v( @+ O; ?* q */
0 h, y1 n* ^. B fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);4 S0 {$ z V$ m7 L" V& F, ?
if (!fbinfo)+ c; z( q# J! H( a
return -ENOMEM; /* 返回NULL表示失败 */
g- a' D A/ g' s' s- D 7 S& R+ Z b2 ?+ `9 c8 I, S: ?
/* 相当于pdev->dev->driver_data = fbinfo */
1 d5 S. r4 z* C5 n& [ platform_set_drvdata(pdev, fbinfo);0 W$ U2 T! ?3 o* j. a
. @5 Q# f& o2 k0 ~' K0 [( n3 h
/* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */
& y' l2 c( r' P. F3 _' U0 R) M: ^6 B info = fbinfo->par; /* 将私有数据赋给info指针变量 */0 F' v) t% J* y+ c- H
info->dev = &pdev->dev; /* 指定struct s3c2410fb_info中dev为平台设备中的dev */
: A4 d9 W) w: i) ~ info->drv_type = drv_type; /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */
0 n% Y6 R) J' Q% r 7 g X% }" ~4 N' f, ^
/* 通过平台设备platform_device获得资源(IO) */1 @! F, g# G$ N( u% ]
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);+ ~8 g& Y& f+ |. J1 B, J2 {
if (res == NULL) {
) w- f" {4 _2 I" ?& F, u dev_err(&pdev->dev, "failed to get memory registers\n");1 n# f0 ]$ x# F+ v1 M! K' q* m
ret = -ENXIO;! k! q8 J6 T$ s' \
goto dealloc_fb;
6 M9 h9 A' k8 b/ L, y }% _0 m5 Q3 o; d$ ]
8 [ n0 S8 L# f! U! D. u size = (res->end - res->start) + 1; /* 资源的大小 */
5 Q ]; t8 {4 ~ 5 c7 I6 G/ a9 W5 i& W/ O1 A
/* 申请以res->start地址开始大小为size的I/O内存 */8 o1 W+ d1 `$ L+ u
info->mem = request_mem_region(res->start, size, pdev->name);* I, w: i6 N1 v$ G: X! ]
if (info->mem == NULL) {
' P; N; w( n( W r dev_err(&pdev->dev, "failed to get memory region\n");
[0 q+ _: M C% ]) y. g& {- U# r ret = -ENOENT;
/ g- }1 r: ? Q/ ] goto dealloc_fb;
4 u: [: ?6 |/ ]6 l+ j# X& b! L4 z }
5 {1 y' ]# F% T . k! |' d9 ?1 p5 G- I s
/* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */
6 H# p7 m, ]; \- b( r) ]' r info->io = ioremap(res->start, size);! f# N5 I. ]& `" U2 P
if (info->io == NULL) {2 v3 w# G$ S( S2 ]/ A% I8 r
dev_err(&pdev->dev, "ioremap() of registers failed\n");
, m% C" l2 T6 b7 r" e4 A ret = -ENXIO;6 J2 Y9 ]# F. g' T" H+ x; |. l3 F6 T
goto release_mem;6 W) q- I; K; i; W+ C
}
* t* Z! k1 x6 ~0 z; {) A* j& P * a& ]+ @1 ?( @) c$ c- U
/* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */: ]- j. `. U1 v
info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE); J' T6 }5 p$ O* _8 E" c, W
2 R; x: L( v/ A8 @8 [ dprintk("devinit\n");( u$ F0 L: g+ U9 g: ?% E
# i$ m7 J3 A' e. @- ], `
/* 驱动名,fbinfo->fix.id = s3c2410fb */
9 C+ _ L3 L2 f$ P7 l$ z0 n; ? strcpy(fbinfo->fix.id, driver_name);
- o: K' l6 I3 U5 v7 Z/ N# z
7 g O2 x; ~, D0 v9 B /* Stop the video */
4 q" W7 q0 B; x/ m4 p: K lcdcon1 = readl(info->io + S3C2410_LCDCON1);
; h2 W# r: s, e /* 禁止Video output */
. A, O1 q6 n% w; F* ~2 \4 B writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
% W9 k* R3 u: O9 I) N8 @ 4 E( Z/ n' R2 x1 a" e6 a* z
/* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */
- b6 g, ]$ x- ?1 O. m- Z# L fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
. x& d, Q. ~6 J4 w- |% I, j3 c fbinfo->fix.type_aux = 0;, o) {5 F! K1 g5 ~
fbinfo->fix.xpanstep = 0;
6 y" X Q5 h f fbinfo->fix.ypanstep = 0;( w: d, `6 m8 U4 h) d9 d8 u
fbinfo->fix.ywrapstep = 0;
1 i3 A' ^" h4 G& T/ f fbinfo->fix.accel = FB_ACCEL_NONE; /* 无硬件加速 */) M! M+ A/ O2 M! {5 U* `
+ m+ a% `0 R; D- z9 E
/* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */5 Q9 S6 i' i6 Q& K0 y+ q" x( Q+ [) r
fbinfo->var.nonstd = 0;
/ e' ^3 k" ^. P fbinfo->var.activate = FB_ACTIVATE_NOW;
+ ~1 C0 Z( A, t% T fbinfo->var.accel_flags = 0;
* r6 L2 u( u5 n& X/ v. ~' H1 l fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
, ~9 U* j% H- A 0 p% a. q- `: v" W
/* 设置fb_ops结构体 */
4 Z! {4 |0 N3 u! d7 |6 B6 T fbinfo->fbops = &s3c2410fb_ops;
& t" X% c: W' ^2 C) c
; L. ]8 M" Q% t1 ?1 G* R4 \ fbinfo->flags = FBINFO_FLAG_DEFAULT;
- x! s) W! C& U' e 5 O& U: [8 j9 _" A4 W9 C
/* 设置假调色板 *// r' j9 p1 b' S' g7 G' c: b
fbinfo->pseudo_palette = &info->pseudo_pal;3 Y3 @! r- ^) z, y8 J+ [
* i; h g& b" v G" }( f" x
/* palette_buffer = 0x80000000,清空调色板 */
4 y, K/ X; p' ^0 z7 s; X for (i = 0; i < 256; i++)+ O" N. [' f! z0 u! X3 z
info->palette_buffer = PALETTE_BUFF_CLEAR;# ~2 x: o, [ j5 x2 T1 h: w
' i/ _0 P0 C& P( l /* 申请中断,s3c2410fb_irq是中断处理函数 */- \, t+ ]6 J! n& j
ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
8 h j9 h1 S# [ [' r$ H4 x& T if (ret) {7 N: y7 @: E' {& a6 \2 g. i
dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
' {- v. i, h3 Q8 L0 [" G( h" d ret = -EBUSY;
_5 T! ?. V$ w# j% Q goto release_regs;
3 ~/ C: _1 h1 u, L" z8 ~, J }- s5 ~4 ?& P( y- s$ C2 \& g% N+ K
3 g8 s4 ?. ~% d" O
/* 获取lcd时钟 */
: T H) \) A b: f info->clk = clk_get(NULL, "lcd");
2 D0 y* ^+ x; w: { if (!info->clk || IS_ERR(info->clk)) {
5 b6 L0 l; E2 M2 S printk(KERN_ERR "failed to get lcd clock source\n");
% h M y. f1 f) z/ S# v ret = -ENOENT;
$ T9 P r( L/ }! R" ]6 U goto release_irq;
' a- s% f% c$ V } Z5 r1 W; U g
' ~6 P& S" b; j0 M& ] s /* 使能lcd时钟 *// M' m# I1 i l$ U9 [2 w* k
clk_enable(info->clk);
8 ]; L3 ~5 Z# z, ~ dprintk("got and enabled clock\n");
. m/ B+ J. i8 L/ F/ g; n' p2 j$ t
3 ~" G& G* I" [/ o* B5 o msleep(1);
) q3 b, Y- e/ t' S / \- [' ~& d- @$ G1 Q
/* find maximum required memory size for display */
0 `" A" D# U8 {2 i1 s$ u 4 T6 v1 V6 W( m) u: `
/* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,5 w( o4 |3 \ f% ^
* 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。1 C& L) @7 P# s4 l5 v
* 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。7 c' S7 I3 t& g" j5 v
*/9 q* l# a2 ]- U, I6 \4 P
for (i = 0; i < mach_info->num_displays; i++) { /* 这里mach_info->num_displays = 1 */
/ h# m/ x- d3 {0 s' ~9 T unsigned long smem_len = mach_info->displays.xres; /* x方向分辨率 */
0 l4 v: D8 w5 r5 j9 [, B
+ O9 I3 V$ L8 ~ h9 L, ^# R9 Y smem_len *= mach_info->displays.yres; /* y方向分辨率 */
* e/ I1 O' M8 z! x) O! [ smem_len *= mach_info->displays.bpp; /* bpp */& {2 l6 r& f( w& z7 m
smem_len >>= 3; /* smem_len除以8 */
& M) n& V# v" { if (fbinfo->fix.smem_len < smem_len); T7 e8 `$ j0 Q* n) a$ Q
fbinfo->fix.smem_len = smem_len;
0 i l6 p; `( s7 x: x# [: J. a }1 U( W4 @# i+ K" [; Z
+ ? e4 ^2 _& u /* Initialize video memory */6 N7 c( K* A6 P4 N$ ]
ret = s3c2410fb_map_video_memory(fbinfo); /* 分配显存 */2 h* W0 K- J; n1 j
if (ret) {/ l; z) O1 L& E) Y( M4 U' Q) t
printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);, m% i8 W B+ @$ s" p1 ~
ret = -ENOMEM;
$ Z, W( j6 w8 e& [4 M f" a goto release_clock;
& G( a, D( w- y+ {$ d1 Q. M }
; R, \& y q. @/ @8 ~' f) c U - o% L! D/ T9 q' O
dprintk("got video memory\n");
" i* V0 V5 n q; P0 Z* F# o' {, B $ V8 _, G$ X" d) j* m1 N7 M
/* display指向tq2440_lcd_cfg */, A. Q' ` a9 I" ?
fbinfo->var.xres = display->xres; /* 设置x方向的分辨率 */0 p5 _6 Y! I. H$ X/ u
fbinfo->var.yres = display->yres; /* 设置y方向的分辨率 */# V+ D- l* T0 {/ @. L# e
fbinfo->var.bits_per_pixel = display->bpp; /* 设置bpp位数 */! ~- v! m& x. C& h
0 O7 j9 o3 ]' ~6 u# R' a! ?2 V' L /* 初始化LCD相关的寄存器 */1 d5 |+ l% c( M5 ^7 u& H' j
s3c2410fb_init_registers(fbinfo);
2 n |3 J5 g0 K- X. s
5 g* w) _- I; V1 s6 ~2 S /* 检查可变参数 */) K8 z) F& A% ^
s3c2410fb_check_var(&fbinfo->var, fbinfo);+ b4 Q& l: q+ D4 q" t
5 o3 O: l/ t ~
/* 注册fb_info结构体 */
6 |4 u3 o4 S, D/ c l! ^ ret = register_framebuffer(fbinfo);7 |0 r" @: D) |2 h; |6 Y$ v) ]
if (ret < 0) {1 H4 A: F- I' K/ v9 [" L) x7 y
printk(KERN_ERR "Failed to register framebuffer device: %d\n",
+ F* t6 N1 E9 Y- ?, C. Q% Z6 B# K j ret);
% o3 l7 N" o: P1 W5 U+ y goto free_video_memory;
2 G" p4 }8 ]. b }
1 n* g0 k, J# } t% d7 Q+ n
0 Z& w- G0 E* n3 i; l: u( f /* create device files */5 Y L- c+ Q; m6 t2 k8 e% x
ret = device_create_file(&pdev->dev, &dev_attr_debug);) {# A9 h8 E L* J& G$ b0 d' v
if (ret) {* v. q# r9 B9 Y* j9 O6 o
printk(KERN_ERR "failed to add debug attribute\n");% @0 o7 k, {2 i" x/ d5 D
}
( p1 u- R8 Z6 b; U* S @% b3 u 0 @: R$ N" k3 ^9 j1 |2 i
8 n6 y/ Y" z$ w! Y$ Z
/* TQ2440开发板内核启动时打印的信息,fb0: s3c2410fb frame buffer device */0 _( d5 G, e% D F1 m# P
printk(KERN_INFO "fb%d: %s frame buffer device\n",
" ?8 l x6 u1 t) l; y fbinfo->node, fbinfo->fix.id);7 L0 Z2 N" u3 ]8 h; t
0 Q$ w9 P* N* B
return 0;
$ z. f. ^8 ?1 o/ ^% t+ U3 R % u2 h* _; `9 g5 z0 L1 o' P" e
free_video_memory:
- ?; m. E) S! q% e s3c2410fb_unmap_video_memory(fbinfo);
7 a* t: r3 P5 }/ g4 |- Zrelease_clock:
" T: v: F$ L5 W: p( D0 ?4 e7 J# e clk_disable(info->clk); /* 禁止lcd时钟 */3 H, }. a! `) F4 F% i! S/ |# ?
clk_put(info->clk); /* 删除lcd时钟 */1 e# v$ m) S# ^. f
release_irq:" L1 ^' S* Y/ r" L8 Z
free_irq(irq, info); /* 释放IRQ */
* Z1 `8 [6 \; [) S9 M drelease_regs:
/ Z( D: i/ S# a h0 ]6 N7 \* K0 e7 Z0 H iounmap(info->io); /* 解除映射 */
0 k$ Y" ^$ H& D- J; [7 ?/ arelease_mem:3 Q! `2 q% d! x. g- |" J% ?$ e( I" b& a
release_resource(info->mem); /* 释放资源 */. Q' k3 L4 ]4 H9 h7 `
kfree(info->mem); /* 释放刚申请的内存 */
8 g( p3 T3 r5 l3 L2 F$ T! Idealloc_fb:
1 Q" X# e, Q9 O# |0 |- l+ }5 Q platform_set_drvdata(pdev, NULL); /* 相当于pdev->dev->driver_data = NULL */
3 [! m* E- p* H' K& A# R4 M0 e framebuffer_release(fbinfo); /* 释放fb_info结构体 */
3 T% a m# O3 ^$ ^2 k8 ^# w" g return ret;8 s2 y& g& ]0 ]" |. ^
}: `+ g6 M/ {) X( e/ L$ |
拆分详解:
5 x1 [( ^' u& I1 Z0 u一、获得平台数据( A( P: B! N$ L2 V
0 ^2 j, J, T) P8 V1 C" x6 T2 S8 A) E4 t& O
mach_info = pdev->dev.platform_data;3 j: B$ d( K0 d% h" }
* Q0 a: Y+ {5 U' y
/* 执行完上面的语句后mach_info指向tq2440_fb_info结构体,而不为NULL */& y+ j" g: Y# s# B* r
if (mach_info == NULL) {* _( J" ^2 ^2 b- r0 e
dev_err(&pdev->dev,# `, q, X$ O' ^$ D1 Y
"no platform data for lcd, cannot attach\n");
# C; G/ E7 i7 a5 u( z9 X2 f return -EINVAL; /* 表示无效的参数 */
3 a2 F9 D, Z& ~ }
6 ^: ^3 K0 |. W! X. Vs3c24xx_fb_set_platdata()里会设置platform_data,tq2440_machine_init()函数调用s3c24xx_fb_set_platdata(&tq2440_fb_info);
4 |6 G# L4 p* q6 y9 P所以这里传入来的platform_data就是tq2440_fb_info结构体实例。
7 k7 [# p& [. `$ Y; T
! x1 V9 [* a/ Q$ Fstatic void __init tq2440_machine_init(void)! l" Q' h. m) l3 m' W
{( V# r- b$ y: g; h7 _
/* 初始化tq2440_fb_info实体结构体 */: G' B& u# U; e. U+ B% T8 r. X
s3c24xx_fb_set_platdata(&tq2440_fb_info);
, D$ u! k3 A1 k0 }! `7 n4 n s3c_i2c0_set_platdata(NULL);$ K ?$ c7 n! s- a9 U
+ T* o! A7 N. t4 [9 K0 ` n) M /* 添加tq2440_devices到内核,它是platform_device类的设备 */8 W: B% K- P) ^" f8 C" ?# w' u9 B
platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));: I6 C8 I) h3 H" |: G1 m4 S9 i
EmbedSky_machine_init();- G* |- {5 H: I, t/ r
s3c2410_gpio_setpin(S3C2410_GPG12, 0);" ^. h( y3 n0 L$ q
s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPIO_OUTPUT);# V m6 @6 I2 W' ]+ H3 b
s3c24xx_udc_set_platdata(&EmbedSky_udc_cfg);) U7 o u/ `( E0 J8 c. s
}
& B- V; G7 @7 c/ F- C2 Ps3c24xx_fb_set_platdata函数将tq2440_fb_info拷贝到s3c2410fb_mach_info,并将s3c_device_lcd.dev.platform_data指向tq2440_fb_info
; B# z0 w/ D& `- L h& Y: H0 Y w4 g, x. Z* I
void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
7 W: j- C, X1 h) K{6 O0 p3 V: X( i( X. c5 _9 l9 m
struct s3c2410fb_mach_info *npd;
$ O7 ^* o. U/ \, c 2 j: A: M" }% ~5 U! S
/* 申请分配s3c2410fb_mach_info大小的内存 */1 e- s: F4 p- }6 b9 y" U M3 e6 R
npd = kmalloc(sizeof(*npd), GFP_KERNEL);
7 l% V) ]. F+ O% t+ y if (npd) {
' f, [; Y! g$ o" t& E* ~ /* 拷贝s3c2410fb_mach_info型实体给npd */
: i2 X( y! E" M9 b. c( ?$ @ mEMCpy(npd, pd, sizeof(*npd));
( d. n0 \: G* m. P9 q/ F ) _9 k+ H1 u9 L( c L
/* 最后将s3c2410fb_mach_info型实体赋给platform_data */0 ~3 N* @; ^2 l0 O% E7 f2 p
s3c_device_lcd.dev.platform_data = npd;
. a. X q0 W2 `( K4 U; ~! g' X+ h% O } else {8 C. a8 k3 @1 D0 e: B8 }
printk(KERN_ERR "no memory for LCD platform data\n");
0 s! A2 b& _" _5 p }
0 e9 B+ O- H# k}
( V6 [' m# {% R4 e: B0 D二、设置s3c2410fb_display指向tq2440_lcd_cfg e0 E& V& V! h$ m$ l: W
2 g4 r, t5 ` H9 h0 a8 {& I6 e /* tq2440_fb_info设置了default_display = 0,num_displays = 1,故这句不会执行 */" k+ n5 l8 I1 T2 _( |: }) H* ~9 J
if (mach_info->default_display >= mach_info->num_displays) {
2 h( l8 {, Y1 K% Z# M G dev_err(&pdev->dev, "default is %d but only %d displays\n",4 ^4 h; Q2 T% P/ [, g
mach_info->default_display, mach_info->num_displays);( h9 w& _) A4 \
return -EINVAL;: U2 O- d8 T) E) n, E0 m
}; x' i; Q5 Z% T
! W+ R7 {2 w# [3 g /* display指向tq2440_lcd_cfg,关于LCD屏相关参数的设置 */2 o& B( i) c% ?/ s# N) m, d' D
display = mach_info->displays + mach_info->default_display;
3 V k6 H- R% f- U+ w( G3 T) O三、获得IRQ资源
( w5 a0 ]6 `( [3 X: m% [6 f; |8 R4 y2 z, B/ }
irq = platform_get_irq(pdev, 0);4 Q& k2 c) b: g9 y
if (irq < 0) {
5 ^8 {. n1 H& [) G% F dev_err(&pdev->dev, "no irq for device\n");
: F* m1 O( t) g return -ENOENT;
7 F% L3 m9 }6 | }) o" o# @/ y, k. j5 X* E. \
四、分配fb_info内存
: h$ n- Y( A {; ?+ Y( Gfbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);( D1 \% |9 ?) p/ E
if (!fbinfo)/ F+ r% L- l/ q, `) [% s- i1 l
return -ENOMEM; /* 返回NULL表示失败 */
- c, k; c7 H: w# T# Yframebuffer_alloc第一参数不为0表示,额外多申请的空间,用来存放额外的数据,这里用来存放s3c2410fb_info额外的数据,比如:clk,resource,io,irq_base,drv_type等额外信息。0 E; p8 [( ~( Y$ M+ V
五、设置s3c2410fb_info结构体
1 N8 {: B5 o0 e: k$ Y `4 y' z# p& W+ {3 W1 P. |; q
/* 相当于pdev->dev->driver_data = fbinfo */( a3 a& H7 ]3 }! K9 ~
platform_set_drvdata(pdev, fbinfo);
! h+ N( c9 e3 H7 D 9 y( |6 C0 |" M0 R1 _' o7 X2 j
/* 在framebuffer_alloc函数里info->par指向了额外多申请内存空间的首地址 */5 l: r# U0 E3 d7 _" B, L) k0 W- ~" M
info = fbinfo->par; /* 将私有数据赋给info指针变量 */
$ _6 b' K p" ?6 C: ^ info->dev = &pdev->dev; /* 指定struct s3c2410fb_info中dev为平台设备中的dev */( C0 s( C9 P$ j: G' ]
info->drv_type = drv_type; /* 驱动类型, DRV_S3C2410还是DRV_S3C2412 */( n e0 ]) Q0 \) X& q' p
六、获取IO资源,映射IO
5 m. a; z5 p. }; J- k
: ^* f `; b. |7 _- h( E/* 通过平台设备platform_device获得资源(IO) */
" \7 r5 a& s3 N( V: D" P) d. N5 n res = platform_get_resource(pdev, IORESOURCE_MEM, 0);6 l2 E/ j5 C0 V0 ?* U' ^8 C: C
if (res == NULL) {, ]" p# |+ @$ ~+ Z4 b
dev_err(&pdev->dev, "failed to get memory registers\n");
% d; w& I: N% ]' A W7 a" h ret = -ENXIO;( q+ _9 K) U# c, y" ^
goto dealloc_fb;
0 @1 j) ] }' D }
9 A! { i" Z% D: Q, F
+ E' ^ D7 a3 [6 r: H$ a size = (res->end - res->start) + 1; /* 资源的大小 */
! l |$ R. I& S Z
8 k- R2 Z$ _! Q /* 申请以res->start地址开始大小为size的I/O内存 */5 V5 a1 Y- Z5 n0 t
info->mem = request_mem_region(res->start, size, pdev->name);7 \* P$ F0 _, y6 f
if (info->mem == NULL) {
4 a' B \% b- F/ o D* H' C dev_err(&pdev->dev, "failed to get memory region\n");+ u8 ]' K4 p. d# c/ P+ g+ h2 c
ret = -ENOENT;
$ Q- w! p# ~' } goto dealloc_fb;
4 S) b9 Y& B% E' t, G5 Y! P }8 P% e2 w0 j% b6 h) ?1 {( p
+ I; t3 B' Y. t' g) O8 J4 D+ C /* 映射I/O地址,其实就是将S3C2440的LCD首寄存器(LCDCON1)的物理地址映射为虚拟地址 */- R+ t: s O Q! f& g
info->io = ioremap(res->start, size);# V0 L" b8 i1 E; |# \% k. R2 [
if (info->io == NULL) { M$ }& v" T. @4 u5 ~# i- p7 n
dev_err(&pdev->dev, "ioremap() of registers failed\n");
+ H; e. @5 [1 J3 _5 t ret = -ENXIO;
) A5 P& y9 H- q7 j- D goto release_mem;
2 o% z$ U" H* _: u }
; b7 t3 ^/ s, o: R7 }; r5 a0 _
& ^1 t0 X/ g p. u0 G% b/ G3 ^# x /* 这里相当于info->irq_base = info->io + 0x54,刚好是LCDINTPND寄存器的地址 */
) Z g- P# ~5 m1 Q" a& M3 M/ P info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE); ]- m. { Q' l0 H& y+ N
七、读写LCDCON1,禁止视频数据输出 Y0 S5 F. u6 i( k* ~# }( g2 R$ T; X
, p% A) z9 D5 W7 N
/* Stop the video */
. }1 x/ s, R! }: v( B lcdcon1 = readl(info->io + S3C2410_LCDCON1);
& C& ^& X6 L( G* B% r8 ^1 O /* 禁止Video output */
; s- E! B1 ]) \+ Y! R writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);- N% u8 S/ L' f0 j) o- p
八、设置fb_info结构体的固定参数(fb_fix_screeninfo),可变参数(fb_var_screeninfo),fbops结构体,flags,假调色板(pseudo_palette)等
, g+ |, H" N' d- K$ w4 @9 l- [; N) Q' f! v/ ^! ?
/* 设置fb_info结构体通用的固定参数fb_fix_screeninfo结构体 */4 b. ]" J% J8 b& M' A
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
* i5 n# m: j0 u/ o fbinfo->fix.type_aux = 0;5 ?" v z7 a# v6 j
fbinfo->fix.xpanstep = 0;0 _1 B" W. `0 O' }/ T# q9 s
fbinfo->fix.ypanstep = 0;
1 C7 |/ X0 m8 x( D0 U% ^( j j fbinfo->fix.ywrapstep = 0;
! W6 \* W- Y- A* B fbinfo->fix.accel = FB_ACCEL_NONE; /* 无硬件加速 */
& i$ b2 }/ E, z6 J+ `9 A6 b 6 p. u7 p. H* Q# R. t
/* 设置fb_info结构体通用的可变参数fb_var_screeninfo结构体 */
. k8 k/ [* `& x6 T& W! u# B5 } fbinfo->var.nonstd = 0;
: R( X6 u6 G6 y, C fbinfo->var.activate = FB_ACTIVATE_NOW;
1 ~8 a4 V \7 @" @; o( L fbinfo->var.accel_flags = 0;. @& n' @3 B, b: Q
fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
0 ]3 U& ~3 `5 c3 y% r5 o: X& y
5 F' Y& z8 m1 Y /* 设置fb_ops结构体 */5 v% p7 W6 G. H# f, [' M d$ s- d
fbinfo->fbops = &s3c2410fb_ops;4 {/ I4 v9 }+ T8 e- ]& V5 H6 L
3 e5 X6 ]2 u b( a fbinfo->flags = FBINFO_FLAG_DEFAULT;
0 q% ]1 ]* f5 S0 ^3 c 8 i: n8 ]" ?: D
/* 设置假调色板 */* X1 Y" f7 P9 A; G" X. R+ \- l
fbinfo->pseudo_palette = &info->pseudo_pal;
1 U8 Z) c' N [( C- @0 v ' j! p! Q- G% H' A _5 ]8 \& L& q
/* palette_buffer = 0x80000000,清空调色板 */
" P- [1 C/ S+ l D4 Y/ N for (i = 0; i < 256; i++)
3 B" P" h- D W0 x2 c* L info->palette_buffer = PALETTE_BUFF_CLEAR;
5 ^* c) ~1 [/ t九、申请中断、获取LCD时钟,使能LCD时钟
1 s' o' }' h( P' L8 A7 O /* 申请中断,s3c2410fb_irq是中断处理函数 */4 d1 m0 B. ?0 F5 e
ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
3 e% R: L: e/ ]; }4 J1 F if (ret) {
( F' ^" N$ U; D) q+ t' I dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
3 x" a; M8 f4 w/ c. i$ {# R l6 U ret = -EBUSY;. P1 O, a' J5 U7 Y
goto release_regs;+ J6 a' |2 }- x" }" B
}
! t: @& z8 i @* \; f # P" f0 H: R7 @( [* X
/* 获取lcd时钟 */
# g9 F; ?7 N% z) r- e4 L5 r! Y info->clk = clk_get(NULL, "lcd");: ?2 H* I: ?1 t, z8 {& V/ J
if (!info->clk || IS_ERR(info->clk)) {8 L6 G; j/ }' a) a
printk(KERN_ERR "failed to get lcd clock source\n");) i5 N O6 m7 D0 {
ret = -ENOENT;8 A q& n/ w; H3 f% E4 s5 U' `8 W
goto release_irq;
, O( A) v/ l) W4 G8 t1 d) I, x }! t: e6 x' s0 y+ S5 o" K/ R/ U
( S) v6 ^# _% w; S1 y& u8 W
/* 使能lcd时钟 */
+ e6 T7 K" w1 ?/ g+ p6 d0 _/ e" E2 g clk_enable(info->clk);
' ~ J+ l5 A$ I0 U3 H dprintk("got and enabled clock\n");2 p& \; U. x5 r* ^. {9 m' B3 ~
十、计算显存大小、分配显存内存
. G' ~! Y9 z6 Y! ^- T6 D
2 b% v/ L7 g) b8 c6 x7 D /* 计算出lcd的显存大小,显存大小为width * height * bpp所以还要左移3位,
% ^* w* R9 ^/ | * 即刚好一帧大小空间,前面计算出来的是多少bit,计算出显存为多少字节。) W0 \) X: W( v3 W" Q
* 显示配置有可能有多个,所以呢,这个for循环计算出的是最大显存大小。2 o% L# b, e3 [6 z: ~) m5 u9 O
*/
: Q+ z7 U. R8 ^) w* m for (i = 0; i < mach_info->num_displays; i++) { /* 这里mach_info->num_displays = 1 */& E8 \" ~% S8 f2 ~' c
unsigned long smem_len = mach_info->displays.xres; /* x方向分辨率 */
( P" ?! ]" O. A3 t0 j
* Y8 I/ W5 w) j2 v) i1 X smem_len *= mach_info->displays.yres; /* y方向分辨率 */
! k! [& s3 }* J* N8 ?$ e smem_len *= mach_info->displays.bpp; /* bpp */
& @! U- ~2 _1 l! ?1 v" s smem_len >>= 3; /* smem_len除以8 */1 w! l6 A# ]/ X+ p
if (fbinfo->fix.smem_len < smem_len)
( u) ]2 Z2 A4 M3 p! K9 y fbinfo->fix.smem_len = smem_len;
. S2 V/ t- l W8 G O8 O3 { }
$ k2 }3 b$ [- U ; u6 n3 a& v& Y, y+ V, H
/* Initialize video memory */3 k. J. u, q# [$ w4 t& d* }
ret = s3c2410fb_map_video_memory(fbinfo); /* 分配显存 */; W/ g' u% a+ V! {$ c* y
if (ret) {
8 G/ V- V( x P% O& T2 M* R7 @ printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
8 m& k' G0 B7 F4 N: i% c# p ret = -ENOMEM;
9 v0 n9 m2 `# g2 v; C+ } goto release_clock;
- {0 M) {" y6 p0 O6 @/ N# Z }
3 ?0 K) k" _5 r; b* S# U5 A* g十一、设置fb_info结构体中的可变参数的x、y分辨率以及BPP为tq2440_lcd_cfg中的x、y分辨率和BPP
* o I9 K6 w! W1 h* O1 e* E0 A
& b3 f1 v1 H! t4 `& x /* display指向tq2440_lcd_cfg */& T1 b/ ^' v* ]: D5 l& h" z
fbinfo->var.xres = display->xres; /* 设置x方向的分辨率 */1 _3 d$ z. x4 P7 f9 a0 K0 Y
fbinfo->var.yres = display->yres; /* 设置y方向的分辨率 */) i) h* T1 K! j
fbinfo->var.bits_per_pixel = display->bpp; /* 设置bpp位数 */
, _* l' o6 }7 _9 R% @十二、LCD相关寄存器的设置和fb_info的可变参数的检测
* f9 r0 q* q# D' T* B4 n) T! U
/ X0 P) h' |- l: [. A* X/* 初始化LCD相关的寄存器 */
& k- m+ w- [: a" p% J s3c2410fb_init_registers(fbinfo);6 C* o$ w& \* `
5 C2 }0 F3 f* u4 x+ ^* v1 } /* 检查可变参数 */
5 S- }( z8 j8 i6 a- v" f s3c2410fb_check_var(&fbinfo->var, fbinfo);) k# j5 Q5 f' k3 }# q( \+ {6 a: f: `
十三、注册fb_info结构体
- X) ]; E! e- V1 e5 j9 @9 n. m- i2 l; J5 y8 z. C3 c
/* 注册fb_info结构体 */
! a. d; \9 I/ _' S1 @ [: s6 b ret = register_framebuffer(fbinfo);
# o5 v: s2 \! m! g( e if (ret < 0) {% ^$ C: X- W: t* h1 n! ^
printk(KERN_ERR "Failed to register framebuffer device: %d\n",$ H" N0 ~5 M/ Y3 I! C% e9 y
ret);0 O% G& k/ w1 [+ L5 T5 t
goto free_video_memory;) _2 {7 G% G3 H' L1 e
} U5 ?! m1 I' x" ]( g
; b0 q2 o8 K0 |* n |
|