TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
PC主机:Ubuntu 10.4 和redhat 9.00 y! ]2 s# a" u* T
0 |% V* P: w/ `1 r目标板:TQ2440开发板 Linux内核:2.6.300 e7 G! Z( r" ?; f4 ` F. z O M2 f
$ Y4 P9 S9 V: N屏幕型号:WXCAT35-TG3#001F 分辨率: 320X240
: v3 b+ F" k. F) q( ^' f5 q% j, {" |
$ T$ t/ V! @2 [6 m; Z+ G" n# F) q. E( L
4 e. ]7 H% r, a) B( g/ x t# J, m; u0 A3 Q3 [, Q
本文将介绍如何移植LCD设备。4 @: z4 U {! O% m/ c
$ y, f7 ]' K0 E* n+ y; n0 v# R
在移植前,先配置下内核,将LCD设备编译进内核。
/ A" z+ |- r1 g$ z* m( Z
3 e+ \# i; _& i* I5 v3 w
9 l- A2 g) L3 b, X7 \, D v& H3 d% f* O# U4 ~. g
- Y9 ^ {' j# T) Z5 v
1.移植
3 Y4 \3 C6 D6 P6 y. I b& ?) _- t5 j
移植LCD设置只须修改位于arch/ARM/mach-s3c2440/mach-smdk2440.c中的两个结构体的数据。
) [- O1 L7 }; h3 { _, V7 `
8 L7 J% l3 d1 o) y5 C$ Y( \1.1 s3c2410fb_display结构8 u W7 [0 h- b w6 v
0 W# w9 x* u. ?/ V
修改后的内容如下:0 {9 C3 N- W( m Y, d' g
8 E ]5 x/ ^% H1 A
/* LCD driver info */2 N Y5 D' Y1 P) Z
7 v1 ?$ O9 _7 c
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {' I! X- b/ [# D# t1 b( I
8 v; s4 _/ l+ L7 } h) o3 b
.lcdcon5 = S3C2410_LCDCON5_FRM565 |: {; }, A* L' g0 K
S3C2410_LCDCON5_INVVLINE |
% H7 i! [7 y1 _4 w- `. M' k S3C2410_LCDCON5_INVVFRAME |
0 d/ m6 W1 k, Z |; y3 Q8 S5 [ S3C2410_LCDCON5_PWREN |
' H- Y1 S. P* A r+ V4 y S3C2410_LCDCON5_HWSWP,
4 v0 \* c. \7 N' _, s; m
% x" I# k F: A .type = S3C2410_LCDCON1_TFT,+ f$ s# q( @& v3 |
! R: O# P- |! a
.width = 320,//240,& @' e# j1 d+ Z2 p
.height = 240,//320,' q' b5 @& h: ~/ j F% u/ r
4 q, w# K) T' |% [$ g# O$ ?0 |- F
.pixclock = 156250,//166667, /* HCLK 60 MHz, divisor 10 */% k2 L# D. ^$ x* J8 Y, [
.xres = 320,//240,8 ^# G- H0 p! A5 t# B
.yres = 240,//320,2 ^5 N3 j5 ~) V2 c
.bpp = 16,
' C& X( i j m& N- u' a/ C .left_margin = 20,
* g4 h! d F* |$ _ .right_margin = 38,//8,
3 Q8 d( n. c" y7 f2 f$ b. _ .hsync_len = 30,//4,& c$ D! c% z/ m! v, q' [. I$ f
.upper_margin = 15,//8,, _% n6 Z9 F1 V% Z* E" l
.lower_margin = 12,//7,: o3 k+ u( I! [. c9 Y8 W/ v3 s
.vsync_len = 3,//4,9 i& v2 A# }8 ?1 O b- H: X* U+ R) J
};( a& s6 i. ]$ I! t5 M. u
7 U6 x( i! r s! C$ x+ B0 I$ q* z# s# p- r
上面的参数是如何修改的呢?我们来看下。0 |+ m, U( \0 _8 R$ e# o/ x+ C7 R
4 J# Z& u* K5 t! jtype表示显示模式,这里为TFT模式。
" H8 N6 ]/ D$ A$ V. f9 y. ? M+ Y% ]& w6 y' F( Z/ M9 @
width和height表示屏幕的分辨率,我的分辨率是320X240。) ~2 L) F. M4 E$ [
7 p5 U! |+ A- Y S3 f* sxres和yres分别等于width和height。
, Z( l, [3 [& x* V# x5 o$ j% R, U0 V1 X _! P. K+ w6 S2 q
bpp表示所每个像素点位数,这里使用16位。
+ s w& z3 ?# |0 l' x% b( F3 N% L+ _2 }% D6 i/ i: [. e
left_margin,right_margin,hsync_len,upper_margin,lower_margin,vsync_len这六个参数的值由LCD的手册给出。下图为LCD中的参数:
* D; B0 y* i% `
+ w% M4 s7 Y/ H
5 f1 q d* J2 _4 O# w& M9 X
$ ?( U% v0 g0 D6 @' }
% Z5 H. R. P1 B在这里,我给出上面6个参数和LCD手册中数据的对应关系:
* V% q J R2 D' E: P( |! e
. v, T# O# v; n .left_margin = Hsync front porch = 20 t# F; K B2 `$ W' g
8 _; o5 k$ q; v4 l$ e! L .right_margin = Hsync back porch = 38( |5 }# K* B" ?* X
1 F9 u- I/ o. @ .hsync_len = Hsync pulse width = 307 z) R$ I; Z& B0 B' m; b7 u8 s3 W
0 P3 p, O2 `5 F2 L O5 c) I+ ^7 q
.upper_margin = Vsync back porch = 15
7 b4 j5 K) R6 C- o2 _
* ^; \. E @( Z* x8 T .lower_margin = Vsync front porch = 12; g! ?8 b3 s0 f4 u8 @
1 W. G$ }8 Q5 k4 [+ c) i
.vsync_len = Vsync pulse width = 3
4 J% n" m" r" Y$ k V
3 }& C V2 M: @0 P( K$ {( ?, N
3 L1 F4 l' |1 t. D9 b; o; }; [pixclock的值是用来计算CLKVAL的。在S3C2440的datasheet中,CLKVAL的计算公式为:
4 n6 \0 D8 H( \3 H1 |9 p, h, L) s T+ ~' b$ @$ _# _; ]
CLKVAL = HCLK / VCLK / 2 -1,而VCLK即为上面图中的Dclk,值为6.4MHz。. K$ _7 v. h6 V6 ~
: i4 o5 t& o- G% E7 X7 M3 Q* |5 C
接着我们看下驱动程序是如何计算CLKVAL的。
& `; Z$ ?4 @1 J' M0 j5 O6 R$ M9 u/ s, E/ Q& E0 u" W( E ^
/* s3c2410fb_activate_var3 h C- p& m2 k0 O. |' s" S2 _
* w0 ]% P A' I: O6 s+ \
* activate (set) the controller from the given framebuffer
0 a5 }" i" W# l7 X, E8 H * information+ M) d. L0 X# h
*/ [' o. j& ]7 g- g2 T; H' T
static void s3c2410fb_activate_var(struct fb_info *info)
4 M) ^' z! u9 F6 H/ O{9 o/ B. O8 Y) e2 u
struct s3c2410fb_info *fbi = info->par;
% X- E% Y' `$ U' r. D: Y void __iomem *regs = fbi->io;) p: E7 _$ J- U# B: |
int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/" ^! p3 ]$ f$ I! o+ ]6 b3 q
struct fb_var_screeninfo *var = &info->var;! ~0 t: P4 S; N ~; y1 a0 _- L
int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;, G4 @4 S( s: x7 e0 R
- `( v! H* m1 W' g8 n( k; Y dprintk("%s: var->xres = %d\n", __func__, var->xres);
5 c" ?0 F! g# w* ?; ^8 o dprintk("%s: var->yres = %d\n", __func__, var->yres);" s9 q0 q7 o$ o. a5 r
dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);% u, _( {8 e' ~. r, \" p# k
# M* D" C: h+ {! {: D8 u, W if (type == S3C2410_LCDCON1_TFT) {
7 L; w# B5 V& T5 O s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/! L9 P, J9 k U0 v2 t8 s. l2 H
--clkdiv;0 o; S3 n2 ^' l( }0 L
if (clkdiv < 0); V3 b3 a' J8 J& {% l( d( Z7 ~4 }* T
clkdiv = 0;' E- H7 ~3 A1 W3 n2 d! E( W& v
} else {; m0 F' X: n& D2 C! f% W$ b* [
s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs); [9 ~! Q4 b9 G2 P
if (clkdiv < 2)) ^2 Y h- c' Y4 F
clkdiv = 2;
& L4 y* X( c _4 X6 p9 J }
0 [- v) P6 r. t: _9 ^2 f) \8 J' j) L1 q9 h7 i+ A7 {
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/
( d# f6 b9 |; S+ i" G
1 L* U5 l2 P1 U% x* o' \1 a /* write new registers */; Y, o- X) j: N
& A, Z: V+ l2 D O) h9 }; v) H1 \4 E
dprintk("new register set:\n");
1 }4 d; U7 i0 Q dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
* k E3 D! S. p* Q3 T' O# W. H& M dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
+ ^: O# `2 L3 G dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);; W" J6 ~) _& g$ l1 F: I
dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
6 j3 ?0 L6 K9 l$ a# e) z8 ^2 V dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);% C- _( P* R. c7 G6 o8 b+ ?0 U
/*把计算好的值填入LCD控制器中*/
6 _3 C7 S }- b writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID, ; m# C3 U! t& ?; a" p0 \7 T
regs + S3C2410_LCDCON1); /*仍然禁止LCD*/
' [* M; ^6 X: D1 I writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
/ L- _) C6 F6 u' U$ n writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);$ I8 h! k/ {' T3 a4 y! S4 @) O6 o
writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
: t# B0 U- ]( E) @2 b& y+ y writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
( T: R' i. ~+ }6 U6 i& m7 p* |/ ~2 ~$ \7 ?7 j+ J9 {' E
/* set lcd address pointers */
3 H! K8 S* ]! a( L1 g s3c2410fb_set_lcdaddr(info); /*设置LCD帧缓冲起始地址*/
! B, a! M. M' b5 d0 v
4 u/ p9 K `% H2 z: d fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,. H- g+ L, W% @, A9 E
writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); /*使能LCD*/' a, I6 O% G- k# x$ \
}
8 x; u7 M! n- w: hstatic unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
5 t+ V1 |/ R9 v unsigned long pixclk)1 O, _4 p7 F* }, a4 _, G; ` r
{
! d. P' U' m6 B& U2 L unsigned long clk = clk_get_rate(fbi->clk); /*获取当前时钟频率(Hz)*/
! Y- U# v( U* w2 s unsigned long long div;4 @% M! x$ P- x$ w
" J9 J& s" `; D# g7 Y6 P
/* pixclk is in picoseconds, our clock is in Hz
* O) V) Y" ?9 c *# y1 K S0 v; J# ~4 H
* Hz -> picoseconds is / 10^-12
4 g v9 \5 H+ q- h */
5 k5 k% E& O! _# W) U% N- i+ }9 X- R
div = (unsigned long long)clk * pixclk;4 j N4 }" N, O) X; \
div >>= 12; /* div / 2^12 */
8 v- |0 a* w$ W; A do_div(div, 625 * 625UL * 625); /* div / 5^12 */
. w+ ~6 f$ r1 s/ j a0 c
, f, B. d' U6 \; K m* b7 x dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
: L4 @+ F" P2 d* x2 i4 K return div;+ n$ J _, D1 O3 Z/ d
}
. Y4 i+ L) M1 i( g" ~% N8 {, }) n! `
* w1 O2 d1 [+ s
首先pixclock作为参数传递给了s3c2410fb_calc_pixclk函数,当该函数执行完以后clkdiv = clk x pixclk / 10^12 / 2。) H6 J) l0 w* U( w6 s, m, \$ \
! r( _0 f8 P) {1 Y
随后由于是采用TFT模式,将clkdiv-1。最后得:
. Q/ @' U1 f: s& L- R. ^! d2 |
clkdiv = clk X pixclk / 10^12 / 2 - 1,这里的clk即为HCLK,LCD模块使用HCLK作为时钟源。, g! t$ I O+ y; ]
8 g B9 q: M4 E0 f1 {, N
为方便观察,将前面datasheet中的计算公式复制在此:CLKVAL = HCLK / VCLK / 2 -1。. q* c6 b( Q( M; ?4 F
( V' Y0 Y' w; i0 `我们可以看出1/VCLK = pixclk / 10^12,也就是说pixclk = 10^12 / VCLK。 我们已经知道VCLK=Dclk =6.4MHz,! r# s A2 s; V9 J* W
7 `* p ~, a. k2 c- f. O5 L. l5 o
因此,pixclk=156250。0 [( w/ z9 K- O V( R* {3 z2 b
% y E: Z5 W" N$ l1 v/ r其实在内核的参考文档中有这样一段话:5 {# ]' g0 g6 H7 l* h2 q
2 ^4 |' ]8 L* U( H; G4 `0 c! I
The speed at which the electron beam paints the pixels is determined by the1 ^' w7 g" q4 z- m8 }2 u) K
dotclock in the graphics board. For a dotclock of e.g. 28.37516 MHz (millions
Q, l+ [" K) N7 |; Q3 X" S# b# r, Gof cycles per second), each pixel is 35242 ps (picoseconds) long:4 h+ I1 u! J/ I4 `- o. r( u9 c
$ a, e) Q$ o. J7 F: V
1/(28.37516E6 Hz) = 35.242E-9 s
' i0 Y2 I/ m7 x1 s# G8 g: x) C: i; O; G$ e$ z$ r9 |4 @
也就是说VCLK的倒数,再乘10^12即为pixclk。picoseconds单位表示微微秒,即10^12。
, D; ~4 I. d7 c P! {
, H8 ~6 U+ q; _最后一个参数lcdcon5,它是寄存器LCDCON5的配置信息。, R; G! r1 Z$ }3 e7 Q% a
) c, g$ Z z7 P( ^* F- o( O( }
S3C2410_LCDCON5_FRM565表示使用565格式。
: g) B' S6 j: R& B; u, r
& O, w9 `* w( `. {5 X7 k3 eS3C2410_LCDCON5_PWREN表示PWREN管脚输出信号使能。7 X, E$ v$ L4 I7 }& a) I
! }* I8 N& P5 A3 i( Q! K9 B/ _S3C2410_LCDCON5_INVVLINE 和S3C2410_LCDCON5_INVVFRAME 表示反转VLINE/HSYNC和VFRAME/VSYNC两个信号线,这个可以通过对比LCD手册和S3C2440的datasheet来得出。
" \* k6 F! _2 L: @) R0 J% }0 T& r4 ^5 B9 ^
S3C2410_LCDCON5_HWSWP:由于使用小端模式,需设置半字交换。, |( y7 P& P6 A% w7 n" w
' Y( M4 t4 M: Y% V0 r
+ i* I7 {* |' O" e8 ]8 Q1.2 s3c2410fb_mach_info结构
7 X* j; @+ \+ W6 ^( c$ l3 i7 U2 Y
k" a( _- P# g R% N Z4 G3 ~* e修改后的内容如下:8 s! U7 d' m2 `: D( F
* F- o# w- y* mstatic struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
" M$ T; L# F5 N .displays = &smdk2440_lcd_cfg,6 k; U- p& \8 `0 A. E
.num_displays = 1,! P( u# _1 s1 K# g
.default_display = 0,
5 B9 Y; [$ ?: y% F& }
# I% m5 x: \% E8 J$ Q. t* v4 n" y#if 0
9 |' E) F+ J) p2 z& _1 N- q7 G /* currently setup by downloader */2 \8 u3 t( o0 h8 f
.gpccon = 0xaa940659,
) M! C; J, n+ w# t. W$ A, b .gpccon_mask = 0xffffffff,+ O" C, a& o( q' L
.gpcup = 0x0000ffff,4 {7 N5 j" S- | \; A G, h/ ?: H
.gpcup_mask = 0xffffffff,9 Q3 j/ J. x7 M6 ]) y, Y( i
.gpdcon = 0xaa84aaa0,
3 A% n6 l( i: X; O* H- W! p .gpdcon_mask = 0xffffffff,$ ^7 F3 O% o. e' ^: B* J6 z& b
.gpdup = 0x0000faff,% U. d/ y- n, i; p
.gpdup_mask = 0xffffffff,1 J6 G# K" I8 _9 F+ l: Y# K
#endif" c) p; t; {; j! V- v6 M% c! n
//no
& u5 k+ b$ G3 M( B// .lpcsel = ((0xCE6) & ~7) | 1<<4,9 j0 w5 x3 f' G. |
};0 s; ?% s. \" j
X" M3 ~% j, |- `$ U
做出的唯一修改就是:禁用lpsel,因为如果不是使用三星LPC3600/LCC3600 LCD,必须禁止LPC3600/LCC3600模式(写入0到TCONSEL)。$ M; ?5 L; w m; w
num_displays 表示有几个LCD设备。
$ J$ a+ u; B/ g6 k U! S
; I4 I3 |0 _% q( i5 ^0 }4 h* Cdefault_display表示这是第几个LCD设备。& E+ K& A* R; Z4 h
% E: t" D" U7 N5 ^
- W2 d: I9 O+ M& Z9 a! w
# _- K- m+ g* y) N/ z& |# S2. 测试
! c' j* H1 |: G# V- ^; X
5 b; v! z1 n p. m; V在修改过上述两个数据结构之后,直接编译内核,然后下载到开发板上。. U( S( A7 E* w, }
- ~- d" j# T0 L* o) D( j: a: g
可以执行命令fbset,输出如下:# g) F7 F% k+ ?7 z1 U! S* Q7 m3 A3 y
" P- ^) i7 N4 x4 p6 C
mode "320x240-58"
8 k/ k) r8 N P# B& D7 @* D1 s2 M' k3 l' C- j% X- }
# D: 6.400 MHz, H: 15.686 kHz, V: 58.097 Hz! {( I$ a7 p4 s: M
geometry 320 240 320 240 16
0 M4 T& `$ e4 H; u W, _& B timings 156250 20 38 15 12 30 32 v, a- W1 r( _$ m
accel false
7 F; u `/ D8 J. `' ] rgba 5/11,6/5,5/0,0/0- n% a2 y0 u/ ~% b) P
endmode
( x7 q" ^) U# h) @, O+ M
( y# `- M/ H7 y) h6 g上述中的数值都是我们设置的参数。) s) J& R; K' o" r) P4 t( {
7 T7 [: z: t. }; M) B6 A+ {
另外,可以通过下列测试程序进行测试:" }7 s! [/ m9 L& B2 N5 H
) Q$ T: m, D' e0 b8 U+ M
#include <linux/fb.h>
" d# Q. l. w$ q0 [3 |#include <unistd.h>
; g' @5 H5 F/ U% M( ?#include <sys/mman.h>
; k$ r- K, z6 X) v" y#include <stdio.h>7 \! R" h: l$ r( B, J' U
#include <fcntl.h> a0 d0 P0 Q, ~$ a" m
//#include <syswait.h>
8 B4 }, B% l3 ^: B- A9 r( {: Z& ?" p3 L1 W9 ~
int main()
/ D2 H) X, y0 b& Q8 ^0 t2 H" t9 R, _{& K ?) X$ Y' Q0 v9 U/ j8 H3 v! ]
int fd, retval, i;
) C; h3 }9 d5 Y: L; \2 T3 G3 N! o struct fb_var_screeninfo var;
& J% g- L9 O( Z [8 u0 [- ~, b struct fb_fix_screeninfo fix;# @$ A8 L- {1 J& ^. ?+ u+ L+ f! O0 m
unsigned short *memstart;, ^9 P; l3 H2 J8 D( @; e! c
$ |" R8 X$ P. U1 U% _ fd = open("/dev/fb0", O_RDWR);
# A* p9 D8 e! p, T if(fd < 0){
4 D& S, E$ P4 K1 a printf("open /dev/fb0 failed\n");7 [( g- U; u/ w( o, ~
return -1;/ g$ l( h. k8 u, s8 ]
}9 B" S& M. k; `: e; }: q9 d
2 }) T( |! }* P0 x retval = ioctl(fd, FBIOGET_FSCREENINFO, &fix);0 p$ L, U9 q+ P8 F" g# e
if(retval < 0){
% J c# [% S: k( }; O. g& ` printf("ioctl failed\n");
9 [9 ]4 d3 n1 B2 R& F; D$ i+ f return -1;1 U6 U1 @: g3 z8 X
}
: ~3 t% l, B0 N* z$ y7 [. x D( J& P+ Q% z
printf("seme len= %d\n", fix.smem_len);& S0 J/ Q& I$ O; D& \! m, C% c% S
3 _) {: y4 j/ P4 S9 J memstart = mmap(NULL, 240*320*2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);. k2 H) ~- e& J( b3 r4 ^
if (memstart == MAP_FAILED){% Q5 n' p! e) n4 L: O F2 x( D
printf("mmap wrong\n");
* c0 I# z o) L9 _6 q1 f) } [ }
, o, @! q* y' L$ R . D2 V6 n% e7 }
for(; ;){
- Q3 F% c! r* } sleep(1);" z, U# a, ?% {( ^) a. e
for(i = 0; i < (153600>>1); i++)
% p0 G* u2 F2 H! x" n6 g/ m *(memstart + i) = 0xf800;
! `7 l9 k+ E9 P% a6 R7 D3 w# @# p2 d sleep(1);
& b! Q. ]4 ]% I5 ]) d for(i = 0; i < (153600>>1); i++)/ q, W. f4 k# I( O) k
*(memstart + i) = 0x0c00;" M6 X: {: f; d# T+ y' _. H+ R- z4 U* f
}
' p. }1 n& A- W4 L( A# A8 {/ R}
: Z. g+ y- c9 C
, L* y2 c1 q! L) S$ I4 c) R1 p) \该测试程序是网上的,我只是稍微修改了下。2 ]3 e' Z4 }$ N x" E
7 g0 ?( c9 ~% q- v! d v! W1 C- G- C) W% v6 E' e
1 I( b& D3 I# g+ X( D |
|