TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
PC主机:Ubuntu 10.4 和redhat 9.0
8 X q: m' y2 [9 K" f" O
6 F2 `3 O: b0 j; ~; }目标板:TQ2440开发板 Linux内核:2.6.30
m3 R1 E2 B: U# F
; N$ |8 f/ H3 P, O E( s1 u% E( n屏幕型号:WXCAT35-TG3#001F 分辨率: 320X240. \: q8 b8 y- f( }2 J' [6 r/ X
2 R. k' S5 h' b2 g$ P) h8 ?2 M7 R/ K7 J; C0 R9 G, s! r
H0 v) `" C- k% G4 I/ H6 X
本文将介绍如何移植LCD设备。! l! m1 T4 D: c7 ~
* E7 ]5 r* r( D9 e' H5 U) s在移植前,先配置下内核,将LCD设备编译进内核。
0 ?2 v8 z/ C; N7 b7 N9 X' @ g; S; W& L
' t$ E6 q' g s! R# n, ^& B
9 g' b. }/ M( x. v: f& Y6 m4 f
1 g R* a( _1 R" O. _! F1.移植: k& X Z ?' {0 o. {
8 q O$ z& B! c- l* o4 M移植LCD设置只须修改位于arch/ARM/mach-s3c2440/mach-smdk2440.c中的两个结构体的数据。
: i3 e9 Y! h; r* l& w, J: \! a8 ~1 ~7 u1 R; q4 C2 r
1.1 s3c2410fb_display结构* z5 t* p) E6 r
* ?; J2 u9 ~0 [& e: w2 T/ K9 P
修改后的内容如下:
2 D# ]" b# o8 v- a+ g* G3 v& J9 [$ k5 z* m) w9 Z* y: N6 U# b* `
/* LCD driver info */$ a( X/ @4 m3 G: q5 a% k
z) B, y$ ?! T+ L. g& v
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
0 L. p! g3 o" q- s1 `; B
2 Q/ m* S- D! f {- i" F; L .lcdcon5 = S3C2410_LCDCON5_FRM565 |( }/ f* W. b7 B% h2 A# `3 e# c
S3C2410_LCDCON5_INVVLINE |
2 \/ i; }/ q v0 {/ N3 U S3C2410_LCDCON5_INVVFRAME |4 ?% r& p7 D2 b0 s
S3C2410_LCDCON5_PWREN |
" j6 d1 i0 p5 T* t$ P S3C2410_LCDCON5_HWSWP,. R1 E, E( U3 x2 Z
2 s6 z* O \# o4 Z& |# h8 u0 s
.type = S3C2410_LCDCON1_TFT,
0 _0 g _, @& [3 H; e+ g0 ~* }. N6 s. a
.width = 320,//240,
4 Z4 A) c- l1 C/ c: l* X .height = 240,//320,
# N0 ?) k" @, {( T) l% V" P( X7 `9 e0 i
.pixclock = 156250,//166667, /* HCLK 60 MHz, divisor 10 */6 i+ ~2 F2 r# ] L4 s4 q
.xres = 320,//240,
8 C f* }; N2 M7 y .yres = 240,//320,% b$ U, k' _ X0 ]8 k9 _
.bpp = 16,
2 ^' B2 y$ I' k9 G5 ` .left_margin = 20,7 B8 n) r$ O; |4 M# b% S2 m
.right_margin = 38,//8,3 U. k4 i2 \8 y" {* S
.hsync_len = 30,//4,
2 h5 ?3 p6 K# w# j5 C% f8 j+ q .upper_margin = 15,//8,
7 @7 A! p: l& P+ R1 X# C/ r .lower_margin = 12,//7,
1 |- T8 j1 E8 o' j% | .vsync_len = 3,//4,0 G+ y( R q0 I/ n7 ]
};
6 S% d" y7 u( o% R) N# I9 Y! X( C9 ^% r: L2 y' `- z
+ K7 c9 k4 _5 l$ s0 ]% W' t( T! i上面的参数是如何修改的呢?我们来看下。
9 e; `; |- h% D: h- L: c6 L/ P9 k( b' ^+ B
type表示显示模式,这里为TFT模式。
4 H: \7 I, O! N' f
' E& ^8 x- j* `5 E* v: X: U9 [; hwidth和height表示屏幕的分辨率,我的分辨率是320X240。! j& ~6 t. ~9 n& s! n v
% {8 I5 y6 w4 z* M7 Y ~$ [xres和yres分别等于width和height。7 p1 [0 k+ `) v+ Y+ ~% s- ~
4 l2 v g$ p E# G9 U! wbpp表示所每个像素点位数,这里使用16位。
, X+ u- N' }9 w8 A0 A3 d6 e x+ A" l: H$ p
left_margin,right_margin,hsync_len,upper_margin,lower_margin,vsync_len这六个参数的值由LCD的手册给出。下图为LCD中的参数:
- K) C8 k% i8 V$ i8 \9 s0 F
- S1 O) T) L+ t% T
+ U# N8 e! O7 q9 z3 I- l8 Q" I7 B% Q+ [+ m
) n7 Z) q: ~% S" L2 a在这里,我给出上面6个参数和LCD手册中数据的对应关系:* i$ B0 _5 t i3 V
- T# p% S) \- K3 M4 M .left_margin = Hsync front porch = 20! h5 C5 v9 F6 l; _7 K* ` \
/ R M2 ]+ ]9 N7 I* K
.right_margin = Hsync back porch = 38; S6 U/ c7 A; _6 h: D* T
& q( j4 u9 q% |2 h$ P' r
.hsync_len = Hsync pulse width = 306 n c& W E" \/ E# q) t& {
+ p" F% Z. N! p! A7 G# i1 p6 ]
.upper_margin = Vsync back porch = 15# t0 U+ E( d% ?! M
2 p( Y. X/ N. Z! J .lower_margin = Vsync front porch = 12
3 Q3 A; p7 b9 b( S% h
0 I! C; Y) q3 ], g+ B8 ]' v0 J .vsync_len = Vsync pulse width = 3* U6 @* S9 W& U+ Q7 F
) w) h1 n& x. A) v& u$ C3 n+ u3 _0 _: V& ^( P1 {
pixclock的值是用来计算CLKVAL的。在S3C2440的datasheet中,CLKVAL的计算公式为:4 j( [( B# {/ _$ L2 `* b
, a: ?0 [$ O! j: J8 S* {% B
CLKVAL = HCLK / VCLK / 2 -1,而VCLK即为上面图中的Dclk,值为6.4MHz。
& J) a# t9 Z& p z
5 y) P0 i6 I( s6 H# b0 J" B接着我们看下驱动程序是如何计算CLKVAL的。9 i9 m, P/ ]& f e
% H% X Y( B5 h" B) [) C/* s3c2410fb_activate_var% o1 Y* u9 `9 q8 u
*
3 h! r4 Z/ _& Q * activate (set) the controller from the given framebuffer
3 z2 F5 P9 [4 `8 g$ A * information
7 x0 w j+ q% c6 q. C3 S) l5 n */
: [9 I( i6 D7 C! Vstatic void s3c2410fb_activate_var(struct fb_info *info)9 i! q% B% A# J9 b$ T
{/ l( k+ P/ \; c6 K' k
struct s3c2410fb_info *fbi = info->par;
: _4 t- N: r; W2 N void __iomem *regs = fbi->io;
2 N u+ M2 }1 ` int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/* o% L6 z/ g- i2 U5 K; ~$ I. l9 M
struct fb_var_screeninfo *var = &info->var;
) K& n0 d1 |) c1 J9 i3 |: \$ P. p int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
6 t9 v5 W$ M$ j1 o) u3 g
8 t- T" E( y& U8 b dprintk("%s: var->xres = %d\n", __func__, var->xres);
- V+ e3 L7 ?1 y8 f* w& j dprintk("%s: var->yres = %d\n", __func__, var->yres);% E2 ^( `$ e( \) J
dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);& n/ ?, z* S# q R$ f
% v4 U, {% {7 Y3 W( c2 W' A
if (type == S3C2410_LCDCON1_TFT) {
& u# V( r5 c1 o$ [# l, @( d& m s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/
2 P/ n/ y- t+ w8 e Y6 P- ~ --clkdiv; f( Z/ X/ s8 m) ~* d. j7 }2 m- }
if (clkdiv < 0)
6 \9 F, ?" t: `, m) H5 y clkdiv = 0;- W5 _& K e+ F& t
} else {; G7 a2 d. v4 f: r6 E7 U
s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
; N; ]1 K' O/ T% Q8 z if (clkdiv < 2)
/ j7 y# _ Q% R3 o7 V" k. B5 q% I% z clkdiv = 2;, |) c3 X0 N* F2 J2 t Z
}
$ L, r2 r3 [- T, N
) |5 E( f3 ?9 V m9 k fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/* J7 L3 k& y/ [9 R' E! U
$ }5 [) S1 F# V- i /* write new registers */
2 O/ ~( j' X2 G9 b) a; t
# f8 W/ }! C# o3 G# }, T dprintk("new register set:\n");
, |5 I2 K$ D$ Z8 @1 r9 A dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
; v% r1 E+ w' o; u3 K dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
; K% Q) c( u r& u dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
, J) a' O; l, m* W) l dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
( `& I1 a5 {1 K+ o9 E* Y& _) E dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);1 C' t& o5 m7 E/ b* F) O3 P9 ]
/*把计算好的值填入LCD控制器中*/5 X2 A+ M0 D; z
writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
' j, _% V, R/ t P5 G regs + S3C2410_LCDCON1); /*仍然禁止LCD*/
; ?( ?+ |% G) ]& f! z6 W& h a writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);+ ~( V; ^$ q4 Y5 R. f
writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
T% W6 X7 a. F' D: S/ p- E3 T writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
7 `4 P2 I! N+ M4 G( N* a writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
) C" b3 S7 Z, ]1 H( X
, f" D' K. J9 M% Z1 i( `- K /* set lcd address pointers */" K; I4 o7 `$ @: ?( ~% O2 p& j5 k
s3c2410fb_set_lcdaddr(info); /*设置LCD帧缓冲起始地址*/. G" {2 q1 }/ g! ?) t
! F9 f ?( J' J; q fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,' j2 {5 ~: V: ?! b, o
writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); /*使能LCD*/6 ^/ s+ @! \6 G i3 v) z% h
}
$ L& f7 l. \( Xstatic unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,4 k- e" M$ k+ i9 C$ o+ x
unsigned long pixclk). n1 v8 _4 q! v" m/ F- u4 r+ G6 w
{
, b$ K2 J, b3 p* o u' a* ? unsigned long clk = clk_get_rate(fbi->clk); /*获取当前时钟频率(Hz)*/$ I( J* Q$ g) ~; c
unsigned long long div;
% D0 h/ {1 @$ d
% M3 {) T) o9 W- u /* pixclk is in picoseconds, our clock is in Hz5 E; y* l/ y7 n y# v$ r
*
3 F- B8 ]% I7 a' o- s7 c* x * Hz -> picoseconds is / 10^-12
+ I/ z( B3 N4 v; L */
" s0 }) Q+ ^. ?3 B$ s% r0 G Y2 \ V* o4 ]+ m) U8 x
div = (unsigned long long)clk * pixclk;
% b6 E2 Q6 l1 F5 z* k, v div >>= 12; /* div / 2^12 */
X: M& [1 G7 Y do_div(div, 625 * 625UL * 625); /* div / 5^12 */, o. Z. g e8 G. _( _# N6 I
! f1 }+ n; l0 B, j' l; d1 i dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);6 Z' G E1 Z6 K& j0 u
return div;6 d+ ^/ j. `1 z
}* z" l6 M1 q# n& ^
) j. ?" H. J4 K6 Q$ `9 ]8 f% r/ [, l5 V9 X5 ?
首先pixclock作为参数传递给了s3c2410fb_calc_pixclk函数,当该函数执行完以后clkdiv = clk x pixclk / 10^12 / 2。. Z' C( s# Z/ _1 Q7 ?, A" F" z
, Q6 d' G. Z$ L% N1 ^
随后由于是采用TFT模式,将clkdiv-1。最后得:9 p+ A/ \/ V' Q! b4 ]; r( s
. ?7 y4 I5 r: q* x, R1 H
clkdiv = clk X pixclk / 10^12 / 2 - 1,这里的clk即为HCLK,LCD模块使用HCLK作为时钟源。
7 o/ E7 ^0 G7 {* l2 C$ t+ E/ M6 C }% p' C1 V: Q, o5 ^
为方便观察,将前面datasheet中的计算公式复制在此:CLKVAL = HCLK / VCLK / 2 -1。
# D) I, D, B* g# d4 |: Q6 R# Y8 Z' n" j4 S8 G6 d: E
我们可以看出1/VCLK = pixclk / 10^12,也就是说pixclk = 10^12 / VCLK。 我们已经知道VCLK=Dclk =6.4MHz,; x( Z' v7 |8 \* L1 P$ g
1 l) A! N/ W1 X% x, `$ q9 V0 B2 d
因此,pixclk=156250。. e* r* V! z' M& ?1 e* O
4 K5 f7 ^3 r5 F4 W
其实在内核的参考文档中有这样一段话:$ v0 B/ @( h7 O$ ?5 Q( K
( Z9 L- k7 Q! _! J( P, HThe speed at which the electron beam paints the pixels is determined by the1 x* G3 f' n, |, t$ {6 U
dotclock in the graphics board. For a dotclock of e.g. 28.37516 MHz (millions
4 N% j5 O8 O3 V1 _& P& Fof cycles per second), each pixel is 35242 ps (picoseconds) long:
. b7 [0 U* p* i6 n3 M/ A9 L3 _2 f3 `$ A; a
1/(28.37516E6 Hz) = 35.242E-9 s6 w: R3 t: Q1 \$ |
. x' H" Z6 G' A
也就是说VCLK的倒数,再乘10^12即为pixclk。picoseconds单位表示微微秒,即10^12。$ {7 {; q' g! `/ n1 w
1 w4 |8 F; G3 o. ?$ s8 z最后一个参数lcdcon5,它是寄存器LCDCON5的配置信息。
6 s4 Z- N6 y: z
5 v' D7 E/ L8 {' B1 X& lS3C2410_LCDCON5_FRM565表示使用565格式。
( E' Q2 }8 V1 X) p- H" q
2 G9 b5 r' t; t- u/ c) {) CS3C2410_LCDCON5_PWREN表示PWREN管脚输出信号使能。
& U; s! ~5 @: I9 B) }% N! D: b
S3C2410_LCDCON5_INVVLINE 和S3C2410_LCDCON5_INVVFRAME 表示反转VLINE/HSYNC和VFRAME/VSYNC两个信号线,这个可以通过对比LCD手册和S3C2440的datasheet来得出。
4 o: I6 X" m" ^6 A" K' R7 R* O/ m; G& W' R" p; E& Z
S3C2410_LCDCON5_HWSWP:由于使用小端模式,需设置半字交换。/ O2 f' v8 Y: M# W* v7 }
}6 E# s7 i3 Q+ [) W, a( U
- K5 `7 `6 V9 |1 _9 E/ m9 A* v* |# b1.2 s3c2410fb_mach_info结构
; U# |6 `# G3 Q6 T$ M& E5 U! ]* Y8 @' {; v5 b$ r! D
修改后的内容如下:5 Z- p7 [8 X1 ^# d6 C1 D" D3 X
0 C8 P# X3 s& W C( X S
static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {9 R" `+ c4 Z2 D
.displays = &smdk2440_lcd_cfg,
1 z/ g$ Y: ^* F4 I4 M .num_displays = 1,2 D) `& g1 q3 C o2 h$ d1 r
.default_display = 0,4 H: f; i2 k& t. W
5 g. u" Z! c6 |3 ]! a
#if 0
! Z; s3 O. p; D4 `: S /* currently setup by downloader */
2 i; ^5 ^& f* j3 ?1 I .gpccon = 0xaa940659, [* _4 Q# z/ h) }
.gpccon_mask = 0xffffffff,' D) t; o. A8 m& F5 I- I) g( {# x" ?
.gpcup = 0x0000ffff,
6 F5 \/ b2 E& q# W .gpcup_mask = 0xffffffff,
" S A6 F. Y0 W3 [6 m .gpdcon = 0xaa84aaa0,
$ {; W0 y/ L4 t/ |) `8 W1 M' R4 H .gpdcon_mask = 0xffffffff,8 ?. f8 j7 W4 ^: w/ k' ]: a8 ?' d
.gpdup = 0x0000faff,
, U% K C8 C- K5 Z .gpdup_mask = 0xffffffff,
1 g0 y+ {9 e I: G9 G#endif/ B7 d, ~8 n; U3 T9 j. ?' a
//no& J, a3 l, f$ n
// .lpcsel = ((0xCE6) & ~7) | 1<<4,) ^% {$ t4 j- M) s3 A( P2 t; h. y. X
};
, I! M/ a# Z' \( `0 T/ w' G
% p0 W+ r9 N/ }- B, Q2 z做出的唯一修改就是:禁用lpsel,因为如果不是使用三星LPC3600/LCC3600 LCD,必须禁止LPC3600/LCC3600模式(写入0到TCONSEL)。4 O" h3 t0 f; H1 w. w
num_displays 表示有几个LCD设备。
. q8 \% ]* Y- N. A6 A0 ^) }! a3 [# T+ O/ \5 [0 q: h4 v C4 {
default_display表示这是第几个LCD设备。
; x$ P) s" f @! }5 F2 _8 l* C
- W) {3 | y/ M( B3 Y
% z9 d0 O) d! B
2. 测试4 N! L% M$ N) E% c* V6 m( _: e+ f1 R
% [$ m: O+ ~- f9 w9 y在修改过上述两个数据结构之后,直接编译内核,然后下载到开发板上。
; \5 f1 L' X8 k) s% [1 c0 [' A! J6 A1 `
可以执行命令fbset,输出如下:3 V; b2 n+ c! m3 X. C l+ t" j( S
* h6 @( L+ D8 Y# V }/ w
mode "320x240-58"
) o/ e8 S/ M' x0 O4 A9 _4 Q5 v: }! q, J9 h
# D: 6.400 MHz, H: 15.686 kHz, V: 58.097 Hz2 D1 \: z* l/ Y
geometry 320 240 320 240 16
7 |. w; q' E) l+ s! f timings 156250 20 38 15 12 30 3: Q7 ~( B! p$ Z. X) s% S/ y9 `; p
accel false) X/ ^% f8 |5 E" ^
rgba 5/11,6/5,5/0,0/0
" R7 ?% Z. l% Sendmode
3 M. ?6 B& e6 e6 c; W, V* W. Q3 r- T* r, v+ ?- `+ e
上述中的数值都是我们设置的参数。
e8 ^" [; w4 _
2 J) s R. w$ [" L另外,可以通过下列测试程序进行测试:
5 ^' T6 b% f' b2 x, w
* c. s) g' R1 }. b8 w+ ^* k#include <linux/fb.h>
* V) z$ l" L% z# y% {! c6 X" c4 j#include <unistd.h>
n" A% h3 P" X7 e! {) X& I#include <sys/mman.h>
% X/ V0 M, ?0 D3 \) z! Q) @ K#include <stdio.h>4 a( z! f: h1 ~& ` q/ h0 }2 k
#include <fcntl.h>* w6 n/ P, b' X) P
//#include <syswait.h>
, I) i; L" e( Z" m7 {5 B* O7 ]# W4 Y2 A) t7 I" r% T
int main()4 e5 y6 p& h% M& [8 w, @
{
# [( V. D6 E3 \5 l% b j& W: i int fd, retval, i;$ K" x! Z1 V0 _" [0 j: }/ @
struct fb_var_screeninfo var;; E9 \9 c- A, W* _6 h7 `) a
struct fb_fix_screeninfo fix;* v, v0 t' }8 S5 n- b8 n
unsigned short *memstart;- ^0 Z- }# e3 l1 m/ I; M
, j5 v A4 e& t6 F9 [
fd = open("/dev/fb0", O_RDWR);
, P; g$ Z' j- W: d! {' ]. r if(fd < 0){, e- Y* k* k! t- ?
printf("open /dev/fb0 failed\n");
! L4 L9 x& s3 O/ P9 k5 y return -1;1 w# b+ u" f' }+ e4 T3 G
}
* a" F7 I) F8 D, B% n: I ' k3 q/ V$ g! y% s: X/ V
retval = ioctl(fd, FBIOGET_FSCREENINFO, &fix);
. N! U* c: u) P) U; D+ T Z/ f if(retval < 0){
6 ]3 H* ^$ a6 a printf("ioctl failed\n");
- i9 z" r! u3 N, m+ G return -1;1 G* b" r# N- w$ ?! {) w# S
}) i. l' Z) C% O1 R6 S# g* @ M" T
U. v$ r" }7 b P8 B: T printf("seme len= %d\n", fix.smem_len);3 I9 O; n" L2 n4 J8 R
# N5 q; W" O# ]" |+ f. ` memstart = mmap(NULL, 240*320*2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
6 ~9 }! m) r' i* I3 L if (memstart == MAP_FAILED){
3 K( M0 S, O1 z/ @( c7 E printf("mmap wrong\n");
# f, A6 F2 ^0 o) O+ v) \7 N$ p7 a: q }9 d( G. r2 k W* o% _# @# T
: N A5 f4 L7 {0 T6 P
for(; ;){
# r1 x2 D. H. ?# ] sleep(1);) L9 L1 i" [+ d
for(i = 0; i < (153600>>1); i++)
# Y' E3 q$ ^/ Y) J1 x1 ^+ S *(memstart + i) = 0xf800;3 L2 V; P* Q( m7 q! h# s5 s7 m/ d
sleep(1);
. j8 o3 I2 p6 x g+ i, t ^/ K# Z for(i = 0; i < (153600>>1); i++): a9 `2 o K: }( n8 U
*(memstart + i) = 0x0c00;
" h4 v, m. V+ v# v# p& T }
6 D0 n& D& a$ ^0 N4 F# c: [}
5 i3 Y4 _' A4 A B
. S' j5 T/ i; F. b- J, K该测试程序是网上的,我只是稍微修改了下。: C6 ^9 N3 ~/ R: h7 x, b0 z% s- v
& Q0 _8 z. I. W5 {6 e- z i
" W! N$ |3 I- F# Z) t. a, |7 e# S8 {* t( y
|
|