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