TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
PC主机:Ubuntu 10.4 和redhat 9.0, H1 G9 n3 e$ }; m: B
% @$ ?0 v$ b; |/ K+ p, Q. d2 S' N目标板:TQ2440开发板 Linux内核:2.6.30
0 _6 K; l. V4 H" r8 f. J0 c4 I4 E& n O3 L( ~/ X% f
屏幕型号:WXCAT35-TG3#001F 分辨率: 320X2402 \' V+ G2 f# z7 i _1 {2 j4 ]: O
( j' W# s# O3 K! c7 A& ^5 i( P9 p X2 n+ d
, ?9 y7 _4 e, i) K
本文将介绍如何移植LCD设备。' [# m1 B1 i- Y' ]
. f" s, j" d4 C; w1 M6 @在移植前,先配置下内核,将LCD设备编译进内核。5 e: y2 Z" `2 t" }7 m' X ^
: | @ O0 b% S2 ?
& J0 Q; d3 P. l/ I( Y
+ Q( g6 }' z# N! n! b
?7 P8 R& x$ V1.移植5 F, p Y- }: K1 _7 }2 _4 V
. R4 x& A3 X3 b0 q4 l移植LCD设置只须修改位于arch/ARM/mach-s3c2440/mach-smdk2440.c中的两个结构体的数据。# d4 a6 a1 R8 C8 I
4 k& |! Z/ L$ z$ l2 S5 w1.1 s3c2410fb_display结构9 H2 @: a; N0 m; z" ~' w
: J! K& I. c8 |) [# x$ x' X9 V6 m修改后的内容如下:- i& ~' }# S5 x6 X
- x& C3 j4 f- G1 ]/* LCD driver info */
( O( s Z) }+ c" v0 b% O- z9 M( y4 ?3 k6 |4 W$ H+ w
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {( B+ X" ^7 K( x! E! ~1 d. R) f T+ L
; ^0 X' U; B- W, u: e
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
- `) U4 o: N# ? S3C2410_LCDCON5_INVVLINE |0 D$ A* H2 U: B/ b4 [. {: n
S3C2410_LCDCON5_INVVFRAME |* q, A( U; a0 }( Z7 D1 b! o- S
S3C2410_LCDCON5_PWREN |( s. h/ c2 q3 Z
S3C2410_LCDCON5_HWSWP,: g: m# ^% ]8 J" @& t, M
e7 y u# G# e, y4 J9 b0 u
.type = S3C2410_LCDCON1_TFT,
{9 [7 x% a! G; f
. ~/ c* D, R3 C! d7 {4 A+ r( n .width = 320,//240,
1 W" q) w- h$ u0 C/ O* F .height = 240,//320,3 k# i0 g% C! V. w; }( t; \& Y
; O( Z0 ]3 A& \5 x" q, { .pixclock = 156250,//166667, /* HCLK 60 MHz, divisor 10 */
. N. D6 I$ [) {, S4 D" H B' A .xres = 320,//240,
4 z3 @5 b7 \1 @* U- ~ .yres = 240,//320,
' F% l! n5 F% u" b .bpp = 16,
1 @0 s1 ] w+ W: F$ e" y .left_margin = 20,4 ?, \% A T1 E4 F
.right_margin = 38,//8,
# V0 d) d) C1 G, h- ?5 g+ q .hsync_len = 30,//4,0 o) x O( e( s8 n1 u! P
.upper_margin = 15,//8,6 _" S2 r9 f" {+ [4 I
.lower_margin = 12,//7,7 i- ^( `+ I- ^. y/ e A: m- ?$ }
.vsync_len = 3,//4,* n" p5 c7 F% k3 L9 j
};
`) h2 w |- D! |# p- v$ w/ I* e" r% N2 v
3 D1 m0 b3 X2 d- C' D. K
上面的参数是如何修改的呢?我们来看下。5 m+ {' q" N8 n' o" k, r
; O/ J7 A+ m" i7 n+ \
type表示显示模式,这里为TFT模式。
; X8 S* S O) Z# J; s4 Y( ^5 _ ]: i' H6 ]' l. g
width和height表示屏幕的分辨率,我的分辨率是320X240。" L0 n8 t+ l- M3 O/ W# m
, S0 Y+ f' q' [1 i; zxres和yres分别等于width和height。8 b4 p5 H# A5 T3 d" H
/ i$ ~. w& }; c0 Y& D1 Hbpp表示所每个像素点位数,这里使用16位。# E( W! t$ s' \7 n5 }) y) ~
' C2 e8 s9 H# Z1 d# oleft_margin,right_margin,hsync_len,upper_margin,lower_margin,vsync_len这六个参数的值由LCD的手册给出。下图为LCD中的参数:2 x3 m2 ]3 S5 i3 l/ B$ N& z" O
4 J# \4 S! a" d
6 z" R m7 l; J5 ]8 i% S
1 F# U' r: U0 D" W! b; X
: q( d. W# a4 k' u在这里,我给出上面6个参数和LCD手册中数据的对应关系:8 o7 [% M# u9 P& }- \2 X1 H: I q" W
4 Q/ X% Q1 A; j7 e' S+ q .left_margin = Hsync front porch = 20
9 H, p7 j. j+ @: Z; l% k9 U) @. n W4 }% m+ }" t
.right_margin = Hsync back porch = 38
5 X% _& f4 \7 @/ X8 S+ H. P
4 ~+ |- J( ?) d2 h. P5 h .hsync_len = Hsync pulse width = 30
% v7 k. s3 r2 q( l2 ^, K
* {9 U, A; L, |) O .upper_margin = Vsync back porch = 15
3 J0 A7 S" T8 H. r8 d
. ?; l' j, q& c- B .lower_margin = Vsync front porch = 12
b9 ^. T$ g) l
' }5 B( N# T( n* g) M- W" l .vsync_len = Vsync pulse width = 3
+ B- g8 D; Z' \, ?
: }4 f" q# Q. i% x1 O2 ]$ {* G* I1 v9 U
pixclock的值是用来计算CLKVAL的。在S3C2440的datasheet中,CLKVAL的计算公式为:
( {/ x j& ]( o; C k
1 o3 ]6 [0 x3 e x* tCLKVAL = HCLK / VCLK / 2 -1,而VCLK即为上面图中的Dclk,值为6.4MHz。
6 Z% {$ R# n# b" S. X
. R" t0 N* S! ^% [0 |3 z接着我们看下驱动程序是如何计算CLKVAL的。
* v! x1 d$ D8 ~6 ~5 p2 P
9 P1 E9 [" P6 N" `/ m* r+ V/* s3c2410fb_activate_var. Y9 U: {2 |. c: d7 ^
*
- r% [' u0 D+ l2 Z# d6 l f Q * activate (set) the controller from the given framebuffer6 y3 ^9 o7 c; C# J
* information
! L7 I6 N$ _+ d9 R w */* `6 N* ^( k/ B5 ]" G; o
static void s3c2410fb_activate_var(struct fb_info *info)0 m" u( x6 B" r' r, j
{* R' P+ D4 _% N5 Y
struct s3c2410fb_info *fbi = info->par;# c" k2 v( Y4 q% i/ n6 M$ ?
void __iomem *regs = fbi->io;) u0 I& O& ^' h; I, M. t
int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/
$ e& o3 ^, I$ _) O0 X# u struct fb_var_screeninfo *var = &info->var;6 u; L8 B% o# r
int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
. v7 k( ]& I! n& E, z# @
5 [* Q3 ?; i; n. h& D& M5 H: f: o8 h dprintk("%s: var->xres = %d\n", __func__, var->xres);
1 p9 { s- c/ M, V dprintk("%s: var->yres = %d\n", __func__, var->yres);+ F4 q3 T. k. m/ e* a
dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);
: N5 u# t! Y* b2 n( p I, M% K B2 w1 S. j# |
if (type == S3C2410_LCDCON1_TFT) {4 _! F0 |3 p: v* d0 n: D2 A
s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/
' ?* {1 D4 c( L3 z( [ --clkdiv;# \, }/ g/ i( F P6 j5 }, N5 u
if (clkdiv < 0)7 J I( u, \/ a2 a2 q* a( Z$ c! P
clkdiv = 0;
" U" i: s: n! B } else {
9 e6 I/ y2 C! s; o9 D% o$ t* H s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);3 {+ {$ U- K5 Q! a" k1 D# v$ z
if (clkdiv < 2)) B4 Z9 A6 K: ]+ }9 }
clkdiv = 2;
. \2 J" k/ z! |' d( X% j }( L0 y" ?1 @" G1 U, i, i6 ]
9 i7 A# R; s) n4 C4 T5 C- |) a fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/
& F l1 e; _+ p, r( J9 N
, R) ^9 L3 T$ p& b( m4 ~ /* write new registers */
' a( d- M* _3 H }4 |/ |7 a% m" V+ m% |
dprintk("new register set:\n");
& ~' G5 k7 d+ W) ]+ \7 { dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
0 F: h# F0 Z5 F3 T0 g3 d. v dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
N# i0 r" c2 J, ` dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);1 H9 f# f; d7 R; t+ i# i# G
dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
7 o+ d. [. H- n2 N+ K I dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);, G3 s) i6 k# a( l. D
/*把计算好的值填入LCD控制器中*/
8 H9 h* O" n- K7 U writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
F+ E% o+ ~& Z! u3 m/ { regs + S3C2410_LCDCON1); /*仍然禁止LCD*/ F2 q/ E* K" R# j$ _
writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
4 D# `; u8 B- ]9 I% ?, \ writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);/ O5 l, `5 l/ H. u; n* I
writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
4 t$ e! p" Q3 U9 k5 A writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);6 c& C2 p! T& r8 C
2 M- ?& \, g0 R1 K) B* q
/* set lcd address pointers */* |; V I* ?( _; a( s3 e9 @; t+ O
s3c2410fb_set_lcdaddr(info); /*设置LCD帧缓冲起始地址*/4 Z2 x2 \7 }# B1 E
7 Z/ x* t; l0 a. }. a
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
9 T& d8 |' W0 Q9 }$ i writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); /*使能LCD*/0 [+ R% G/ ^0 K |4 }& r
}
6 e0 B; }+ J6 V |static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
0 C' }% ` V& C5 T unsigned long pixclk)7 o3 H8 [6 O1 O0 x9 p- S7 U
{
( o# e4 e+ |8 C5 s unsigned long clk = clk_get_rate(fbi->clk); /*获取当前时钟频率(Hz)*/2 W9 {2 [- y% {* ^( D; A
unsigned long long div;
( f- y! P6 E3 I6 ?
0 w3 b: a! V: f2 z3 j /* pixclk is in picoseconds, our clock is in Hz+ U% k: T0 r7 n* n# B
*
9 K8 {: i9 _$ t4 A" j. S) _3 g, R * Hz -> picoseconds is / 10^-12
' m( t, V$ \" q! Z */
: P/ W- t9 ^, N' \
6 i3 {+ w2 \2 O% K" H div = (unsigned long long)clk * pixclk;
& n2 [" g$ `" i# T, O/ M$ L+ A div >>= 12; /* div / 2^12 */
7 V0 N' A) |& u& K4 _$ U do_div(div, 625 * 625UL * 625); /* div / 5^12 */
1 b$ j; Y0 y& v I" c6 v9 H! n+ T2 p( ^
dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
5 W% h% D$ k4 h return div;. [) N r, l- ]
}
+ {4 p; @2 X8 |5 \3 Z
$ l9 _& l) L Y5 j p g( f
- d# Z, d3 j- u) }首先pixclock作为参数传递给了s3c2410fb_calc_pixclk函数,当该函数执行完以后clkdiv = clk x pixclk / 10^12 / 2。, |8 p- i3 ? C/ a. f
& {. }+ c. h3 J, Z/ m
随后由于是采用TFT模式,将clkdiv-1。最后得:
: L# U* v1 |* I5 m5 z N7 c
" @) C" _0 F" |clkdiv = clk X pixclk / 10^12 / 2 - 1,这里的clk即为HCLK,LCD模块使用HCLK作为时钟源。
$ h3 f: i$ v) @( `& F) k7 G
9 n$ E( X/ A6 y- P" m为方便观察,将前面datasheet中的计算公式复制在此:CLKVAL = HCLK / VCLK / 2 -1。
. u% R. t M' S, X
. V$ A( q/ l4 }5 U4 e/ p$ k5 x我们可以看出1/VCLK = pixclk / 10^12,也就是说pixclk = 10^12 / VCLK。 我们已经知道VCLK=Dclk =6.4MHz,
! m: x4 [% r$ D% h# H. q
h, \/ G! Q6 {因此,pixclk=156250。. u, T4 [$ `) B3 R
9 F. p( |1 q; d6 f1 Z其实在内核的参考文档中有这样一段话:
9 k9 J. M* [2 f& I7 V4 [) k8 W# e9 R: c! y; R2 c: K
The speed at which the electron beam paints the pixels is determined by the! Q/ q5 j4 B+ o; X6 j: f5 @
dotclock in the graphics board. For a dotclock of e.g. 28.37516 MHz (millions; D1 n$ T) U \# }! _* Z
of cycles per second), each pixel is 35242 ps (picoseconds) long:$ S7 n" b2 j$ {, M6 b
! J8 S" j; [8 N( C8 Q 1/(28.37516E6 Hz) = 35.242E-9 s
2 Z- D! W! k! ]
1 ~% x7 _) v _3 E+ ]也就是说VCLK的倒数,再乘10^12即为pixclk。picoseconds单位表示微微秒,即10^12。
2 S8 f- e! B- f6 Q
% y6 p2 H' O# X6 d4 y6 @. v8 S: {最后一个参数lcdcon5,它是寄存器LCDCON5的配置信息。
: `) ^* p9 L# R. A( A" w
1 b( |# A3 r" T5 f8 C4 x9 m% w% ]S3C2410_LCDCON5_FRM565表示使用565格式。
; z S& s* n9 `/ C- |. S& n4 k% C# `7 x
S3C2410_LCDCON5_PWREN表示PWREN管脚输出信号使能。
! U9 o# L5 C2 Q1 s2 ~& K: l% `5 u, l- _( x! k8 Z# ?- ~7 H4 R; o
S3C2410_LCDCON5_INVVLINE 和S3C2410_LCDCON5_INVVFRAME 表示反转VLINE/HSYNC和VFRAME/VSYNC两个信号线,这个可以通过对比LCD手册和S3C2440的datasheet来得出。! p- w+ {: {8 k2 F
/ T' _5 j' C9 Y& NS3C2410_LCDCON5_HWSWP:由于使用小端模式,需设置半字交换。# M7 F8 {+ U# ]3 V$ E7 f
- u# W# y5 q, ]- R
6 I& t, d+ {* Z( {; w7 E/ |* }, T1.2 s3c2410fb_mach_info结构6 p. j" n1 n& x' b+ Q, ]
1 }$ [* z; ~9 r9 a, a- C% O. Y$ O修改后的内容如下:
& A4 _. V: c5 J9 r+ Z S ]3 q. E4 X6 O9 _
static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
$ p& E& K3 j( f6 Q+ b6 A( j$ C .displays = &smdk2440_lcd_cfg,
8 Q6 s, ~- M5 D .num_displays = 1,3 d! {) C7 F) K6 y& f- v+ y
.default_display = 0,
6 h* ?# p) ?) O+ F9 L! k) ~3 v. T" [3 X# G- T
#if 02 u% \2 L: C4 f) R- {7 f4 a
/* currently setup by downloader */
, t! S/ Z5 h) Y2 `* S" x1 i .gpccon = 0xaa940659,) F7 A: X% z# x8 d
.gpccon_mask = 0xffffffff,: f$ s: q( d# o4 J8 n0 O% s
.gpcup = 0x0000ffff,
, t& _! _3 N+ G6 m; b3 R7 X$ r .gpcup_mask = 0xffffffff,0 X+ E; v8 |% v! }8 n
.gpdcon = 0xaa84aaa0,4 c; y& h( Q1 Z8 D& d b# y
.gpdcon_mask = 0xffffffff,* P: d j6 u% g9 ^, l4 T
.gpdup = 0x0000faff,0 v3 C' `4 |) C
.gpdup_mask = 0xffffffff,
- o; U8 \/ }& z( P#endif
2 J& k E% @; d1 e. C" _//no6 P& d2 P2 J" E0 G7 o( p1 ?
// .lpcsel = ((0xCE6) & ~7) | 1<<4,
4 ?0 H+ \0 z9 }* c2 f6 l% j* K3 `};
% Q" l5 U9 }7 M+ J8 c' \; M
, }7 R2 R1 n4 M做出的唯一修改就是:禁用lpsel,因为如果不是使用三星LPC3600/LCC3600 LCD,必须禁止LPC3600/LCC3600模式(写入0到TCONSEL)。
8 U2 ]( d7 P n6 K; I+ Fnum_displays 表示有几个LCD设备。
; d4 _: G) H6 f4 ]. _% Q9 ~+ l+ K W$ T6 x, A! x* Z
default_display表示这是第几个LCD设备。: M; D. ? ~1 v$ c+ P
7 _8 ^0 L6 I3 o% V+ h5 n l2 Y. [- y, E2 y$ S2 q* F" n
r: X7 V. e5 }2 g2 `/ E) C
2. 测试
9 q7 V/ B- v/ y7 \* _# B2 U& f, v' n) Y; K- a% w4 _4 c6 w/ g
在修改过上述两个数据结构之后,直接编译内核,然后下载到开发板上。
% A M, c6 e$ J0 e9 _1 b6 F& D0 s) x( V2 H4 Y; S% N0 l' @
可以执行命令fbset,输出如下:& z8 V, \' W+ r1 G# e. `
; {9 I/ y4 d* V0 a! Ymode "320x240-58"
! |* O* Y+ ]" y& t1 `7 @6 o" {6 s
0 `# `7 d) t3 V% I" ~4 w # D: 6.400 MHz, H: 15.686 kHz, V: 58.097 Hz
! z4 [+ c% B+ S' v geometry 320 240 320 240 16
8 w8 q5 Y3 h$ ] timings 156250 20 38 15 12 30 3
6 f, K$ h6 K# l( H8 v( v accel false
! p: B$ |( c! F A0 |& N# O rgba 5/11,6/5,5/0,0/0
7 y) v( U( g$ L7 R. }endmode0 L ?, O I" S9 y9 x
: w: P0 G4 [; I V' l上述中的数值都是我们设置的参数。
$ X" d. i/ U p' c& `
9 z, Z% q) a' l' M0 @& b" X另外,可以通过下列测试程序进行测试:7 v- V+ U) X, L! W9 L6 K
# f% c' A8 E' R, q% _
#include <linux/fb.h>* q- F+ |+ \9 x9 T5 h3 x9 S" `; W& K
#include <unistd.h>0 a" q4 n. C: ~; D; ?- ?; P8 _
#include <sys/mman.h>& C* q: K9 j, Q: B, q
#include <stdio.h>2 O& ?- d2 W& S
#include <fcntl.h>7 ?; i+ }1 E* N" @3 m4 x) Q9 f
//#include <syswait.h>5 d+ [2 ?: E3 V
: M! a! P2 h$ d4 M' j$ u" z0 dint main()
/ ]+ P/ b4 Q$ Y/ q/ u6 |' u$ e{
, b3 d9 W: B9 b S" w$ F int fd, retval, i;* C4 e6 g2 B8 W( e9 b
struct fb_var_screeninfo var;9 e' ~, q, p7 j9 S! r, _
struct fb_fix_screeninfo fix;
; ^/ S4 k3 J" X' b5 k' H3 Y: V unsigned short *memstart;
6 A4 [; S9 N$ c1 m% O4 i0 m7 I' w) A( Y# C/ u9 a9 [ L$ T
fd = open("/dev/fb0", O_RDWR);
5 r, W' @7 T2 i9 X if(fd < 0){, B0 t v5 R+ {
printf("open /dev/fb0 failed\n");0 S' a7 {- u e8 Z1 H
return -1; X; r0 I$ U6 G" P. c) y" v8 S
}
# _& D% {" P* O7 Y& R : F- k2 d! L/ H5 @7 M0 g
retval = ioctl(fd, FBIOGET_FSCREENINFO, &fix);- c9 R! N' X% Z! A/ |& R6 T- U
if(retval < 0){
' t8 _2 Q. s: I( o& ~! [ o printf("ioctl failed\n");/ M& g& r5 R3 M3 B( W
return -1;3 S5 M& [. W* z: c
}2 J! @4 }) @! u" M, b( B) ]' e
' M7 E* w3 O: f
printf("seme len= %d\n", fix.smem_len);
+ E) P% y* e5 ^) n7 {# ^( k' r- `4 K$ T9 L1 s% [% W( B8 W- a' J
memstart = mmap(NULL, 240*320*2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
. O9 U; d7 [, j& f+ e1 _, M4 b6 x if (memstart == MAP_FAILED){
* x; P* S3 _- D, u printf("mmap wrong\n");2 f5 D- ] \$ Z
}7 n2 @: j, |3 X+ {& ]# n3 n1 M
9 Z, x9 ^$ q; y2 Q for(; ;){
9 J8 b6 n8 ?9 P/ ~- u sleep(1);
6 ]1 e8 s- l* Y0 s& u0 d7 B( {) N for(i = 0; i < (153600>>1); i++) j" c5 T+ e. b T9 `9 ]
*(memstart + i) = 0xf800;0 k" }' _: Q, Z" l
sleep(1);
- w; U! L! E; P1 I% T& E for(i = 0; i < (153600>>1); i++)
1 D# D& K5 ]( P- E! m *(memstart + i) = 0x0c00;
- A+ J5 m4 A8 q) d2 T0 o/ X' Z& Z }6 R2 r7 u, f9 h" p. E6 u8 Z) O" \# ~
}
4 v0 j" U/ K3 F4 I" c! f. o2 O4 D- J9 c8 \2 R$ r7 ~ }' D! S
该测试程序是网上的,我只是稍微修改了下。
) l P) R9 R4 V4 P1 L# F
o0 z9 P' [8 L6 H$ {& D+ r# ]( X! j1 b: v( R" @2 a% H: t
! @) B w3 U; S* P& i/ T
|
|