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