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