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