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