TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
PC主机:Ubuntu 10.4 和redhat 9.0% T; t$ Y5 ?. v& G( `( j
1 k2 y- t9 O, M4 V v" l3 ]目标板:TQ2440开发板 Linux内核:2.6.30! v* _- `0 z: W; E
/ q j5 T/ U# H( a8 H屏幕型号:WXCAT35-TG3#001F 分辨率: 320X240 o0 R: P! Q% J+ w
& Y& t' X4 U0 I V! B
2 }, f0 u, v( q2 P( ? e" a
b; M- |9 n# z- F
本文将介绍如何移植LCD设备。9 I. Y$ O; }. y& l' X
; }( | u0 v4 w; Q: c; ^
在移植前,先配置下内核,将LCD设备编译进内核。' c" o' E1 r5 p0 S3 R* V; P
: B* f8 T2 I* o
& u* a, y, f$ F' x/ T! ^
# |; `' @/ k2 Z2 ~" }- @$ w# l) J) D) t4 y) t( r( j
1.移植0 z; X P6 [ e8 E" v; |# d7 M5 c$ x
) G# Q) s/ s4 n
移植LCD设置只须修改位于arch/ARM/mach-s3c2440/mach-smdk2440.c中的两个结构体的数据。7 o$ v: C$ }" C% ^7 r/ E1 s9 i
) `# U) Z9 E7 _7 H% G1.1 s3c2410fb_display结构
' W% {" T( `2 {7 M) ]# n+ ?
8 \6 T( G/ ~( A; M+ u+ \修改后的内容如下:
3 q+ ^: c6 I: |$ w7 K
8 M5 ^. U4 S1 p/* LCD driver info */
' v* y1 J( Q7 ?) U. [- B: r5 {
6 ?; a0 f7 m; Kstatic struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {) _6 D4 a( G! w S3 ~8 B
! l/ \$ o. [0 |! ~
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
5 u# i7 s( z) W S3C2410_LCDCON5_INVVLINE |
9 g( Y, ?* t# M1 R0 u2 C S3C2410_LCDCON5_INVVFRAME |# P8 ~* z c& P1 P5 @+ G- \7 q
S3C2410_LCDCON5_PWREN |
/ C6 A7 M0 z8 w, a- _ S3C2410_LCDCON5_HWSWP,' a0 a8 d( g! ^8 F s
/ ~: ]* n4 T! U% @# [
.type = S3C2410_LCDCON1_TFT, v6 G% ]. Y2 e! }$ _; |
; k/ Y' B7 _0 y$ L& t3 z4 q+ w) m) t: S .width = 320,//240," W+ K2 m! h& A, M/ S" _; m6 ?; C
.height = 240,//320,
a! e, O* U& S1 t" d" Q7 I0 {
; k3 ]( t3 {" ~/ D& x7 @ R0 y .pixclock = 156250,//166667, /* HCLK 60 MHz, divisor 10 */
$ ~& R& \. l: e0 q% E .xres = 320,//240,
) ^. \' W+ V: ?, Z0 z( N .yres = 240,//320,8 \$ M8 f- I$ H& A3 ^. v5 M9 W
.bpp = 16,# ^+ T$ t1 c7 p- v" [7 d
.left_margin = 20,
" d; `! m2 b0 O9 o0 D .right_margin = 38,//8,
3 t o# ?/ s" ^1 N. X; j9 |2 {/ t .hsync_len = 30,//4,7 s5 }9 T; [, X, v
.upper_margin = 15,//8,% Q5 F- }3 ~2 |
.lower_margin = 12,//7,
& b! s" U, z h* O .vsync_len = 3,//4,5 s4 U: l: l, p8 G( v
};
# n$ \3 A' G; `' X; s o+ k: I/ s l& M
1 w% Z0 O0 s5 ]0 p+ u. ^% u
上面的参数是如何修改的呢?我们来看下。
& {/ Z2 Z& V# w# s/ x3 f) c1 G. }
5 x' P; P" r" P8 q% Btype表示显示模式,这里为TFT模式。
+ x! T4 G0 {+ M, U, ]8 {* B0 ^; U+ `
5 o6 A$ G4 x6 `( A) N# @& Jwidth和height表示屏幕的分辨率,我的分辨率是320X240。" Q. H3 n5 D; [" e: I% w, _) [
& p. s0 V5 r2 ~/ {* s. j% u
xres和yres分别等于width和height。' {% z0 | x% N4 Y" W
9 { p( P+ s) E' _! W5 E8 S1 m
bpp表示所每个像素点位数,这里使用16位。) p# _4 h7 N' L, w& B4 g" D
- E5 j. q6 W; c4 W8 [ \
left_margin,right_margin,hsync_len,upper_margin,lower_margin,vsync_len这六个参数的值由LCD的手册给出。下图为LCD中的参数:- I; M3 m7 [/ Y/ A# p$ M5 a
" P- S6 ~* R" M. h9 X: j
4 W" Y- Y" z _+ ?
) r2 }$ F- [2 r7 Z% h( X5 ^( ]- @* v8 ~, N3 U
在这里,我给出上面6个参数和LCD手册中数据的对应关系:& Y4 y9 N7 m: E1 e
* i* d8 V1 R! l: M5 _ .left_margin = Hsync front porch = 20! B7 T+ N' ?" t$ F
# p1 v+ J6 k- m
.right_margin = Hsync back porch = 38
1 `, e B( J6 }
6 T( m0 z, n! C7 G. d# p% I .hsync_len = Hsync pulse width = 30; ^! ` F4 H" ^+ F; e0 e
( I$ _1 u( P5 I" }3 h- ?# s( s T
.upper_margin = Vsync back porch = 15
6 |: |! C! H( `: H/ g' \/ I' `& I) T
.lower_margin = Vsync front porch = 12
* B% K2 c- Y+ @8 H
) B; b1 d: |* R! k$ M .vsync_len = Vsync pulse width = 3
0 R% p- v- L5 o0 j, `2 F" v
: f+ J8 b" g& l8 h) M! @% f. }# V( }/ p) t% p
pixclock的值是用来计算CLKVAL的。在S3C2440的datasheet中,CLKVAL的计算公式为:* L( e3 R1 g R
, F7 A9 l0 d6 A; Y- C
CLKVAL = HCLK / VCLK / 2 -1,而VCLK即为上面图中的Dclk,值为6.4MHz。( |. ~% H& A* G1 \! q% c
1 i: D6 ]" R0 A
接着我们看下驱动程序是如何计算CLKVAL的。
$ s2 W4 g: w. v( |5 W' t$ ~0 W
6 \; U8 A' x; O, c# i' i/* s3c2410fb_activate_var
2 v( E' |! o; \0 G/ c *' h$ A. v/ R5 j" } N
* activate (set) the controller from the given framebuffer
0 o6 E$ X1 i B+ e * information2 \+ e" Z. |' }3 {) D3 [
*/. p2 r) \5 L, q$ ^# w' G
static void s3c2410fb_activate_var(struct fb_info *info)
' o6 L6 w; |: S @7 _{
5 b x! E, ^5 @ struct s3c2410fb_info *fbi = info->par;5 Q: K% P& M+ y: B8 q8 e
void __iomem *regs = fbi->io;( k0 W/ ^2 x6 `( i( v* n( q, X8 C
int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/
( I6 J; o" o1 V( C/ N% F* {+ [ struct fb_var_screeninfo *var = &info->var;
, m w9 \1 N9 j/ t int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;: Y: P# b/ q" u; ]) c
! N+ ?. o/ S. _# i- T/ ]/ U dprintk("%s: var->xres = %d\n", __func__, var->xres);
. k4 T% ]- `( O- k* u, I dprintk("%s: var->yres = %d\n", __func__, var->yres);5 X; S; u8 t( n$ A
dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);# |6 K( t! Z0 x
3 p1 O8 f8 {5 C! j
if (type == S3C2410_LCDCON1_TFT) {( }( W6 t7 K3 w( u
s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/
6 g3 h" l$ ]" I& z# A( m- E --clkdiv;$ P# D# X4 u) J& w- r
if (clkdiv < 0)
, M! U; z0 u7 K! b, a H$ U clkdiv = 0;8 e W0 ~. M5 }3 m. k0 c9 W
} else {
5 O5 o. R6 s/ b+ i! Q s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
7 F2 |8 b6 X" ]" d if (clkdiv < 2). r% t5 E" a+ [# t6 @
clkdiv = 2;1 l+ Y K/ k: m% U6 ^
}
4 K" @# N* g8 Z+ h! I; u% r
7 O8 W& \2 F3 p" ?$ b) r1 R4 f6 _ fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/7 |3 Z: h7 ?: G: D7 c: B
+ Y% h% I0 m: m- ]3 V% f$ {. Z F /* write new registers */# L1 M2 x5 p1 O# R
5 Y3 ~& G; x: P3 n2 K* f dprintk("new register set:\n");
- S; k+ T2 |9 r A; T dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
* _4 Z0 ?2 R7 a7 P9 M9 C( S) U6 L dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);1 Z# s7 I8 F) l% b( m
dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);, g2 u8 V. `& o1 M4 x( {
dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);* P1 _+ A+ B! Z2 {/ I
dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);* }1 n5 a' _; e1 J) n" _" M5 o/ o
/*把计算好的值填入LCD控制器中*/
0 D' S- l! N; H# x. y5 [6 a writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID, / ~; |$ z$ l/ u$ ^4 v* C# T. r
regs + S3C2410_LCDCON1); /*仍然禁止LCD*/+ r/ e( r2 A) D
writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
9 g; I1 X/ f2 c. {" R7 W0 ^ writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
5 k: k! a. S% N# |3 I' k writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);6 A( L$ W6 f8 E5 o; l
writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);* n9 s+ t9 z8 n1 j) P
2 P; k( f% f- b( y. C" X /* set lcd address pointers */
% i) A# L- p# P* f& e" m: [) ^2 D5 B s3c2410fb_set_lcdaddr(info); /*设置LCD帧缓冲起始地址*/
/ r$ j2 I- l/ j0 d$ @) z; y0 s: R
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,8 K6 W4 U$ K- O' x: R
writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); /*使能LCD*/
- O( ]8 { g/ w2 A}
7 o9 w! A, Z1 Z) Y5 B' ~0 astatic unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
# h: B# D* U; J& ?; H0 A* ~ unsigned long pixclk)
. V3 m. E1 R9 q' n# Z- v7 Z2 l: S{( V& C2 o3 ^" B+ ?2 n5 p) ]
unsigned long clk = clk_get_rate(fbi->clk); /*获取当前时钟频率(Hz)*// Z" ~. Y7 s4 K8 L& t$ O6 I4 ^
unsigned long long div;
: e- M. l% u8 y5 Y5 a; X6 k/ A. C% F5 R8 @! a& ~1 D, b/ [
/* pixclk is in picoseconds, our clock is in Hz) |) Y$ ]. y9 r8 L e1 p
*
# M# L1 w( f( w# c) L * Hz -> picoseconds is / 10^-12, j+ d# v+ ?$ j$ Q3 x8 Z+ W
*/: D G. G1 ]& ]) y/ P( J( a
1 k4 X$ {- Q* b) I div = (unsigned long long)clk * pixclk;! J# u" P( }- q' b
div >>= 12; /* div / 2^12 */$ t6 t, C8 G- j* W) W' l
do_div(div, 625 * 625UL * 625); /* div / 5^12 */) b( D1 e* g: V; @4 d% `3 ?
3 ]2 G8 N' w* v6 l! Z dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);0 r1 g; l* b& Q3 \& \2 }; x: S& b
return div;$ C+ A, F N3 D1 ?; b2 p4 m9 h
}
1 F8 a( X! _( a& E: p$ h2 j& e w ^8 T+ M
; q' b- S9 W- t首先pixclock作为参数传递给了s3c2410fb_calc_pixclk函数,当该函数执行完以后clkdiv = clk x pixclk / 10^12 / 2。( X5 b' S! n1 {8 u. q/ r
, h& r N' M- b1 V随后由于是采用TFT模式,将clkdiv-1。最后得:
" t! m1 r' Z4 H+ x6 x. Q
1 k& p- J6 U/ ^+ t: e9 Y2 yclkdiv = clk X pixclk / 10^12 / 2 - 1,这里的clk即为HCLK,LCD模块使用HCLK作为时钟源。3 f4 F, R+ e" r
8 W2 k/ A; p O
为方便观察,将前面datasheet中的计算公式复制在此:CLKVAL = HCLK / VCLK / 2 -1。
3 x. s! | u4 ?* V7 G% u% m
X/ e+ Q1 J7 R$ K$ ^我们可以看出1/VCLK = pixclk / 10^12,也就是说pixclk = 10^12 / VCLK。 我们已经知道VCLK=Dclk =6.4MHz,
3 X! g0 o4 s. W/ E5 O
) f' Y/ X6 ~; B+ I, \% V5 G6 }因此,pixclk=156250。
0 Z% H2 ~+ o. h: _ ]: o# n) v/ |5 P
其实在内核的参考文档中有这样一段话:9 Q- p _% Q: P
7 s; }, ?9 k9 IThe speed at which the electron beam paints the pixels is determined by the5 A( L; Z- e. ^/ z* c6 L
dotclock in the graphics board. For a dotclock of e.g. 28.37516 MHz (millions
7 g( m) v" p+ S+ I5 Tof cycles per second), each pixel is 35242 ps (picoseconds) long:
3 Q6 i! n. z9 _* B/ R) _: `0 l) L% u: c" m# S; T2 G5 d
1/(28.37516E6 Hz) = 35.242E-9 s
l+ |) D$ C9 C5 G3 E9 h3 P1 \- j" D
也就是说VCLK的倒数,再乘10^12即为pixclk。picoseconds单位表示微微秒,即10^12。/ \/ d) c' C5 i
( D7 W' |7 F X; S6 @最后一个参数lcdcon5,它是寄存器LCDCON5的配置信息。
( }& \3 H9 ~. U% W1 J. ?
' `7 L- B* D$ |4 i7 B& J9 bS3C2410_LCDCON5_FRM565表示使用565格式。
+ S, _- K% V# H' D1 [7 _& ^5 c, m$ V' p2 n1 P
S3C2410_LCDCON5_PWREN表示PWREN管脚输出信号使能。; q4 x. @: t+ a2 P
; j+ K5 `6 z0 s0 V" p
S3C2410_LCDCON5_INVVLINE 和S3C2410_LCDCON5_INVVFRAME 表示反转VLINE/HSYNC和VFRAME/VSYNC两个信号线,这个可以通过对比LCD手册和S3C2440的datasheet来得出。+ ]* v" z; L7 Y: ~/ Z0 M% T
. @. S/ J# P, TS3C2410_LCDCON5_HWSWP:由于使用小端模式,需设置半字交换。 u* L w9 i: b
3 ?: W+ T Q; `
( ?& N2 B2 i" A z, p1.2 s3c2410fb_mach_info结构
. ^: Y0 z( F' \ c, o6 x
" ]8 N/ B. ?8 i W" g修改后的内容如下:
( X5 k1 w7 ?( e1 j, o
6 c6 k4 V0 D0 nstatic struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
7 g9 L9 K& P1 S: G: h( i. F8 B& s .displays = &smdk2440_lcd_cfg,
. a- T; [; O: @- x/ h# F9 l .num_displays = 1," b) G' w+ Y9 _
.default_display = 0,! W" v6 F3 ]% U7 F5 K: C& W+ p9 C
2 |8 q6 H2 C# m5 l+ z! j. O) {
#if 0* a" \/ F( O+ ^9 D3 o) |8 ^
/* currently setup by downloader */
/ U1 J o/ U: Q" ]+ S" a ] .gpccon = 0xaa940659,: Y8 F' G) x, R! p( L, l! t k
.gpccon_mask = 0xffffffff,. L- [6 h4 h8 K- i3 m; C3 ]
.gpcup = 0x0000ffff,
) e2 |7 V1 c/ ? .gpcup_mask = 0xffffffff,
! R, O1 j$ v- P! g; u* A .gpdcon = 0xaa84aaa0,$ q) h. ]( r+ e
.gpdcon_mask = 0xffffffff,
- |- J: V" i# t0 T .gpdup = 0x0000faff,; M" U" u! d" l
.gpdup_mask = 0xffffffff,
- b% ~ Z9 K: f* n4 K {, e0 A* e#endif$ c$ t9 m4 H8 j( M
//no' | C: V) w' L3 f0 M% p3 n
// .lpcsel = ((0xCE6) & ~7) | 1<<4,
0 O. @& U2 n3 |) o. u};
4 d: h- z/ Q6 \
4 l5 @; H: Z: F% ]- B做出的唯一修改就是:禁用lpsel,因为如果不是使用三星LPC3600/LCC3600 LCD,必须禁止LPC3600/LCC3600模式(写入0到TCONSEL)。1 \, j: x/ g, G0 D: f4 Y
num_displays 表示有几个LCD设备。
1 r- r' u9 ?2 o
7 ^% e- t* v+ ^) x3 V4 Xdefault_display表示这是第几个LCD设备。! P* B" R3 t2 k- x. j
3 r0 X, l& S2 Q5 c. f2 t/ K
0 J0 W7 d$ ?2 x
" K; `: e2 v5 K4 w2. 测试* U; d! j9 m( B3 O+ ]
" }. ]. }/ |: S% u在修改过上述两个数据结构之后,直接编译内核,然后下载到开发板上。
% G+ _4 O7 L! x- n9 a* q3 Z' G( X1 Y+ ~, z( C1 p. y
可以执行命令fbset,输出如下:
) D6 @( |9 j7 W9 ]) z% h9 G& l% w4 U4 M: X& R
mode "320x240-58"
( A9 Q9 P' ~ |# J8 b
+ I8 Z8 a# }# h4 B7 P9 e8 Z2 M9 r # D: 6.400 MHz, H: 15.686 kHz, V: 58.097 Hz
& T* J$ t& \3 M5 R4 w y7 o. C geometry 320 240 320 240 16
7 r3 a! q: l. b timings 156250 20 38 15 12 30 37 T) ^! n0 q9 v8 ?+ q
accel false
6 K, \- b) k9 g rgba 5/11,6/5,5/0,0/0
( }, D2 x( x) W W5 D2 uendmode: p/ r) x3 {2 m
4 Y% b! w/ H* J1 w; H
上述中的数值都是我们设置的参数。
- s9 n0 h. L7 ]* ], _0 z3 B/ V, G1 m9 K. P
另外,可以通过下列测试程序进行测试:
, i" e6 q. Y# J) A
% c( S$ e# m0 k1 n( c#include <linux/fb.h>
+ L+ ^4 F: y2 w7 Z! y. ?- X" B/ U2 b3 k#include <unistd.h>
! H- l" K n9 c* {6 D9 |0 C0 y' u#include <sys/mman.h>* I1 s* S- p) b& \& Y
#include <stdio.h>- ]' r2 G+ E J0 B1 s
#include <fcntl.h>
$ ~3 V+ S/ t( V8 V6 G//#include <syswait.h>
) n. C# r0 k2 F/ B' Z; \/ x3 v# v) R( S/ h" B4 h# K
int main()
% h' r6 V0 x" b/ n( u& k{; h+ N. }/ T, _( Q/ _
int fd, retval, i;, V$ u8 v; N& W) X- u: x; T8 j+ r
struct fb_var_screeninfo var;# d6 M+ U* R. n
struct fb_fix_screeninfo fix;
1 k) [2 i* m$ E unsigned short *memstart;! ?* K- {* `+ q5 e* {0 }
9 j# s% N& E- C! p* N
fd = open("/dev/fb0", O_RDWR);/ T+ B N. p/ b" r
if(fd < 0){3 o) f% {6 z# G1 Q9 K8 C
printf("open /dev/fb0 failed\n");
F: h6 I0 K: e: a5 A) q! ` u1 k& |$ f return -1;" P7 X( r) }9 t* E3 J, U
}
0 _; l5 Q2 A- S9 _2 f: o: Y% M
7 g& y% q+ i/ w' x1 N- Q- S1 D retval = ioctl(fd, FBIOGET_FSCREENINFO, &fix);
* i! b* A) i, _% ] if(retval < 0){
8 J" ?0 b) k' U" m% U9 y4 Q! t printf("ioctl failed\n"); D& s( x4 P) \0 F# u0 G
return -1;. V: J& r5 @6 r, z+ v
}
% Z+ a/ x% H3 N" h' }6 s; X5 l0 }5 c3 `- k% G
printf("seme len= %d\n", fix.smem_len);+ o* `' d" O- {2 B* T- ?
. y) Z, b+ J u0 Z memstart = mmap(NULL, 240*320*2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
' t9 p, V5 h! X if (memstart == MAP_FAILED){2 d8 G8 U7 ^& p9 Y5 p0 n" N
printf("mmap wrong\n");( S6 T) n0 f$ P! J& S
}; e/ g8 T* n/ L* q0 G% i7 n
( W5 X& w) X, b
for(; ;){
" \. E8 o, p; Q. E) Q5 H sleep(1);/ F" l% V: Q: J
for(i = 0; i < (153600>>1); i++)
6 P, H; X0 p6 Y7 U D *(memstart + i) = 0xf800;$ A8 @! b; z% i. s G9 D# l9 Y
sleep(1);
! Z, \5 @; L2 `! J% c4 K. ]% O/ ` for(i = 0; i < (153600>>1); i++)* b6 a9 F9 N7 k5 e" X6 X* @, }- M
*(memstart + i) = 0x0c00; |' c' g. t% `: o: r" ], R1 O
}2 q; M0 G4 S) e/ A5 g) `4 \8 V
}
7 C6 a$ C2 [; Q
+ }. Z4 l5 W' m& e R% W: W- [+ ~该测试程序是网上的,我只是稍微修改了下。% f9 R9 B4 z- @. W$ U$ `
+ E+ O2 z" d: l% U2 C1 g
( t5 y4 n: Q+ N5 U9 a
! i& t2 R. n0 A |
|