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