TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
PC主机:Ubuntu 10.4 和redhat 9.0; t/ V3 m/ o# f4 R' W0 c
0 W d b+ D9 @1 t- O' L! w
目标板:TQ2440开发板 Linux内核:2.6.30
2 V" {( D( Q- V( B0 }
6 v( r9 D) j) Y屏幕型号:WXCAT35-TG3#001F 分辨率: 320X240% o& W# _6 |+ d! ~% U5 \; s7 W- N
2 \9 W* Y! d( l/ B5 M2 G h
, W3 l9 P# H* P2 }
2 R' q X: ?; H" y( v本文将介绍如何移植LCD设备。
v! w$ J$ c" L* |& B x6 ~; F
5 ~( Z- p' Z. i2 I在移植前,先配置下内核,将LCD设备编译进内核。; z# ?" |( D4 N8 t. k+ [
* S2 n7 T& c% W. n6 ^" s6 _
0 ]2 H- N" e1 v1 |5 i2 t2 c0 s: O n* p; \1 r4 E* V0 Y0 M
9 x" z: }7 B7 [+ J6 V p0 {
1.移植
' i7 m2 ^2 D. y; v
' t8 z3 E% k9 [9 M( H8 h移植LCD设置只须修改位于arch/ARM/mach-s3c2440/mach-smdk2440.c中的两个结构体的数据。
/ e: z& q- @" w7 M0 F' s: E# I4 J4 H) Z5 U/ [+ b$ o# g& r7 V
1.1 s3c2410fb_display结构
$ u. `* A4 |7 l u( j8 S
7 j: ^& I2 y) n. E修改后的内容如下:
* o% ^) b- D/ \7 D& O" |5 n; B! o" Z- ]% `
/* LCD driver info */# |* F6 v5 h5 g9 [2 R% I5 o
' p5 S8 V1 j/ q: y4 gstatic struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
# ?& L# C; v1 q! m7 b, `( N: ~2 t' R1 U' S# @6 H( t C
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
) Y6 @% I7 z9 T/ p, s2 V8 V& v S3C2410_LCDCON5_INVVLINE |; U. s9 S6 [! M5 ^ e
S3C2410_LCDCON5_INVVFRAME |
+ U2 X8 D0 N* h) Y1 }2 w1 {) N, Y S3C2410_LCDCON5_PWREN |3 A- [6 q- }. f- s- h& L8 O( m
S3C2410_LCDCON5_HWSWP,: \1 \9 w& [+ w, x
( Z. w8 |( A" I0 z! {( C+ y+ b. W5 |7 J
.type = S3C2410_LCDCON1_TFT,6 {( d/ c% j1 i
7 A N* k& s- C" U8 S( ~ .width = 320,//240,
2 H1 b& J+ \9 [ .height = 240,//320,4 k; a" Y3 N) y: E8 a# r, B
0 v) E& F$ u1 A9 P& ^, ^
.pixclock = 156250,//166667, /* HCLK 60 MHz, divisor 10 */ @# ~$ l/ w5 a: D( u9 A
.xres = 320,//240,7 M: E @" z$ U0 W% M/ s4 T
.yres = 240,//320,5 Z5 V2 e# W2 b) W" r
.bpp = 16," ?: F8 d5 \' i
.left_margin = 20,! f' z9 ^3 P% w
.right_margin = 38,//8,# @0 ?" E. O) w/ ^, h
.hsync_len = 30,//4,
! q! V" B* w$ Z) @* g .upper_margin = 15,//8,- a% ]1 M& j; q ^7 [* x9 |* x8 e
.lower_margin = 12,//7,1 s1 @# a! c" h, d8 s" {
.vsync_len = 3,//4,
: I) h' b4 U0 ? f- d6 i};, Y- |9 N, d# G1 X" H7 w& T0 W
% M2 `7 x* Q, _
6 N. i2 B% P, w6 f% \$ j上面的参数是如何修改的呢?我们来看下。( z0 [# X" S/ h3 N$ w8 ~8 l
( d) m& d% O% _- ?1 [. O
type表示显示模式,这里为TFT模式。
7 e: G9 I, _% f6 Z( E: _/ F
/ r8 O0 f0 ~$ ?0 h0 [7 j3 Z- swidth和height表示屏幕的分辨率,我的分辨率是320X240。
0 f- P5 @ e$ c3 u9 S8 b, _, {7 h& F
xres和yres分别等于width和height。$ F( j! s8 e7 u% z- A& z( L
# v' r) f0 h7 N3 K ebpp表示所每个像素点位数,这里使用16位。
) ~% K; n9 {1 C( ?
. W, @0 y5 S. R1 _8 aleft_margin,right_margin,hsync_len,upper_margin,lower_margin,vsync_len这六个参数的值由LCD的手册给出。下图为LCD中的参数:5 _2 m& V `" s, v4 l' k
. I! |1 @, i3 R8 n* w
: }7 i& t& \+ O) ^/ M' V- D$ k
9 N- H( P* e9 m* @1 N( ~( U. A1 c2 b* P9 G, d
在这里,我给出上面6个参数和LCD手册中数据的对应关系:, m* {. e! J3 Y% `0 \
' p: w, S/ s( V, U. H" ^7 }8 r .left_margin = Hsync front porch = 20
% X9 f& v6 [& y
8 U7 j4 X6 {9 w! Q! H; ] .right_margin = Hsync back porch = 385 M y# t+ ^' |2 s, F3 ^
@$ }. L6 [- h7 y& P
.hsync_len = Hsync pulse width = 30
3 m# A4 p7 w# Q/ Q4 v& c. [4 `5 j3 p9 M3 t
.upper_margin = Vsync back porch = 15
2 N! E! h# }. }' W7 r" f Q7 ?8 z! J& n
.lower_margin = Vsync front porch = 12
7 m* u* x) V0 l; b. g8 [0 x/ C
# g4 t9 i# ]' o$ |1 o .vsync_len = Vsync pulse width = 3* b$ s. O% t: i& B7 b* \ z7 {
7 W* P& z6 F1 q! Q) B e6 O9 @7 B! p
( N& ~0 J Y$ x; hpixclock的值是用来计算CLKVAL的。在S3C2440的datasheet中,CLKVAL的计算公式为:
$ c' k) J' ~! t! M9 L9 B* V
- b5 s d- v4 T+ A& F% T2 e7 ]4 ECLKVAL = HCLK / VCLK / 2 -1,而VCLK即为上面图中的Dclk,值为6.4MHz。- N0 s8 @' B8 w$ C" h
7 {. n- N! S, }* u
接着我们看下驱动程序是如何计算CLKVAL的。
" d# H; g+ T' I8 h P6 T4 L, ~; M( ]! w0 N' s& l$ \& N
/* s3c2410fb_activate_var
0 u' `0 y3 b# r2 i5 C *
" m# j% U$ Q: E- L& z; E2 r2 c* f * activate (set) the controller from the given framebuffer+ L3 H4 y# g$ L" H3 V& o) k; e H
* information% t7 a* I4 F" C
*/
8 M- O( L: v' E: j0 Kstatic void s3c2410fb_activate_var(struct fb_info *info)7 A& i5 J. N2 _& h: y
{# v( R% N% ~& R- ^
struct s3c2410fb_info *fbi = info->par;
9 {; b+ q1 t1 \* g0 R void __iomem *regs = fbi->io;. d9 J7 `% I$ g% E& K2 a0 m
int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/' k# M4 @$ f( ^
struct fb_var_screeninfo *var = &info->var;
- O, C7 o8 y2 i6 x/ C# I int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
2 E) a% Z' }5 }5 y
: Q U! a( D, {7 |8 T9 T dprintk("%s: var->xres = %d\n", __func__, var->xres);7 w- H" B9 N& Z/ c D& K6 t
dprintk("%s: var->yres = %d\n", __func__, var->yres);% W' P) Z; F3 [* m4 A! e) \
dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);, A7 j+ X0 d5 D' u9 g
: m2 E5 Y6 n+ g$ X: }
if (type == S3C2410_LCDCON1_TFT) {9 g U: y3 \- J- }( G, e
s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/4 m4 @: z4 q# d
--clkdiv;
, j) V! K5 O. T if (clkdiv < 0)2 F- G. i- a& I) z9 _
clkdiv = 0;7 U, `; d3 Y4 o7 R4 K
} else {
! t1 k) k# k& {7 D s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);* r- S$ W8 U6 V. u0 Q8 a) N b
if (clkdiv < 2)
( ^3 ~8 G: p# @& F+ i: m2 G clkdiv = 2;' p& l3 I% `9 s G- F( h3 a
}
6 y* C0 ^/ R) R3 P' O' l! n, y T1 g, H: \. {- v- M: a9 G8 `
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/ o( }9 z+ K2 W9 H4 F) g
]: M; F8 q; a' U1 u
/* write new registers */
' ]" n$ l* I% a4 N* y
- O/ n4 H0 H- b+ E4 q1 Z( e4 h dprintk("new register set:\n");
, q' N. P' u `( u$ v+ N dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);. Q- I" R% G8 G
dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
8 J4 H: Y5 D' K# W# a" _ dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
, E1 N2 U4 w; O6 u$ Y6 p dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
1 e% A8 E9 o/ e9 S8 v dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
# e" L: ^+ [0 q* I: r v /*把计算好的值填入LCD控制器中*/
0 T, V @2 M# B7 n( r' M) A writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
, \7 j$ Q3 q F8 u regs + S3C2410_LCDCON1); /*仍然禁止LCD*/
. e% ?2 _: W1 Z# X3 R writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
1 W5 r/ A3 o( j( d* K writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3); f; l( q* _" {% v4 t
writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
I5 \; @! l5 y2 z! u& j, Q1 @ writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);% P- w: W; [ n l' R
E1 J. y4 v, B8 }
/* set lcd address pointers */
% e0 h9 t; q# S9 x1 B* K7 @) E s3c2410fb_set_lcdaddr(info); /*设置LCD帧缓冲起始地址*/% z& _3 A: O5 B! S
2 `9 H. q; @ i1 C- y- @: b# c: ^ fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
8 I g6 ^. E) ]0 K+ ~! E4 K writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); /*使能LCD*/9 Q' s- T h4 x& j
}
* W$ {3 Y3 K, l, B8 m& x& bstatic unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
, I/ l# n6 x3 C! v+ W. ^- h% Q3 u7 U unsigned long pixclk)
! A9 Z& [/ q* {- L) V4 c) \{7 N/ p& H2 H% V- g5 q
unsigned long clk = clk_get_rate(fbi->clk); /*获取当前时钟频率(Hz)*/1 t, s' D- T' l2 ?2 W; E. F
unsigned long long div;
7 [3 d# l3 @1 y# f' ^; ]' G$ e% h2 U- K
/* pixclk is in picoseconds, our clock is in Hz
; w, N! V: O$ b; b$ J, F$ D *' b% J$ V' Y ]% {/ R. G6 ^
* Hz -> picoseconds is / 10^-12; V |6 w* U4 y; Z9 u
*/* D" `3 G S! s
: D, K7 I* U: P' e+ X
div = (unsigned long long)clk * pixclk;5 [7 a! p' g$ s+ q' n
div >>= 12; /* div / 2^12 */8 `5 q& Z. w2 R- H5 U$ ^
do_div(div, 625 * 625UL * 625); /* div / 5^12 */
% Y. T# P2 A7 n1 f' X
2 r/ w' _2 i, V& ~" R4 i dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);/ \5 W; W$ G) B- d; S b" S5 L
return div;
, O" H5 g7 c% }}7 F- o( c' Q0 L: F' p
; g( V( x- |: _3 ?. m
& ?) j8 y) U* h首先pixclock作为参数传递给了s3c2410fb_calc_pixclk函数,当该函数执行完以后clkdiv = clk x pixclk / 10^12 / 2。4 w+ y& T" |) i; a4 K
, q2 g) U! q7 k U0 Z8 y5 g随后由于是采用TFT模式,将clkdiv-1。最后得:
. c) y, @1 N, m1 I7 Q6 \, L3 G1 z2 d7 w9 c$ x ?" n8 T" T; F
clkdiv = clk X pixclk / 10^12 / 2 - 1,这里的clk即为HCLK,LCD模块使用HCLK作为时钟源。 x, n7 \9 z5 o
( i6 W$ f6 E. }/ e5 I2 `为方便观察,将前面datasheet中的计算公式复制在此:CLKVAL = HCLK / VCLK / 2 -1。
4 Q2 R* a V8 t9 b- x
9 N0 I' h' _/ \7 N2 k$ w我们可以看出1/VCLK = pixclk / 10^12,也就是说pixclk = 10^12 / VCLK。 我们已经知道VCLK=Dclk =6.4MHz,
' R0 Z N5 f' V+ o9 }2 m4 d$ h: \7 h& Q I
因此,pixclk=156250。% U6 f; b& S4 |
6 l$ }1 n9 f1 i2 }2 s% T其实在内核的参考文档中有这样一段话:
1 T; M3 i5 q8 F2 N! F/ E
1 y, d0 D) `3 P3 E2 |1 U! [The speed at which the electron beam paints the pixels is determined by the% J- _, @1 P2 e# ~
dotclock in the graphics board. For a dotclock of e.g. 28.37516 MHz (millions- b! `8 ?6 V4 y6 M' ]
of cycles per second), each pixel is 35242 ps (picoseconds) long:
4 _1 Z, A' T4 ?& \1 p9 `* i
* Y& V4 b, W* Q2 Y* q4 r: N 1/(28.37516E6 Hz) = 35.242E-9 s
! ]" L2 d4 w" v' I5 T* c/ t, l L) I9 h( z3 U5 K; J* r
也就是说VCLK的倒数,再乘10^12即为pixclk。picoseconds单位表示微微秒,即10^12。
5 ^& v T' `, v" A3 L; s$ e2 f+ \' G5 _& q
最后一个参数lcdcon5,它是寄存器LCDCON5的配置信息。
5 @' Y* P ~. Y B0 Y* W' i7 k% `+ Q) a# Y9 z( C6 ~0 s
S3C2410_LCDCON5_FRM565表示使用565格式。3 [) }& j3 u7 b" w$ G' D
$ P! L+ M3 g$ ?. U* q
S3C2410_LCDCON5_PWREN表示PWREN管脚输出信号使能。
% ^) Q, D* L% `' G& h8 e2 I' Z9 N
S3C2410_LCDCON5_INVVLINE 和S3C2410_LCDCON5_INVVFRAME 表示反转VLINE/HSYNC和VFRAME/VSYNC两个信号线,这个可以通过对比LCD手册和S3C2440的datasheet来得出。
+ P1 X& ~4 w3 m0 a. N8 I% t* y! x% A% t' a( o6 h( u% s! p I' q2 L
S3C2410_LCDCON5_HWSWP:由于使用小端模式,需设置半字交换。
3 G8 Y# s* P/ a- q, o+ l: C+ ?
* x0 q7 |' }% p) j: _0 Q
6 z( @2 [7 s$ e5 l: u' C: n' r1.2 s3c2410fb_mach_info结构
: S& H' W; j) w+ e. C' }* `6 ~
$ |- Y8 X, O4 E; w( J9 |修改后的内容如下:
) P9 l/ p' F% M6 I4 Q
; e7 }- k2 _6 n8 q M6 Ustatic struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
# q" t$ `- D0 C2 { .displays = &smdk2440_lcd_cfg,; d. C9 V* G2 T% D* |9 E! z
.num_displays = 1,
7 z( [/ l& S; }: f% S1 E% T/ m .default_display = 0,
5 s2 H6 @: k$ G% G
- q3 c6 I0 I4 Y#if 0
+ q6 T( O$ O9 T2 _0 i /* currently setup by downloader */! t4 L# R& w3 `0 j7 s7 y
.gpccon = 0xaa940659,6 Q5 v' T) n, e& {
.gpccon_mask = 0xffffffff,- L/ `% p6 b1 J% n1 p- J
.gpcup = 0x0000ffff,( Q/ C; n3 a, \$ ~ `
.gpcup_mask = 0xffffffff,( F( ^2 I, Q8 G- q
.gpdcon = 0xaa84aaa0,
; E' C1 v* D0 V5 c) J/ U! c .gpdcon_mask = 0xffffffff,/ W1 _8 T6 }6 z! q* j8 G( N u5 b
.gpdup = 0x0000faff,
2 P7 a3 [' W! }6 |( X f .gpdup_mask = 0xffffffff,6 i. ~# A$ I) n: w
#endif$ R3 B2 d& u& ]4 A7 y
//no" R* r6 M x0 g8 E" }. n8 c
// .lpcsel = ((0xCE6) & ~7) | 1<<4,3 S% n( O/ l3 m1 {) i4 A
};& z0 E# V. u# o4 \5 l
( B" ?, N; y8 I( b* a6 T- P- i
做出的唯一修改就是:禁用lpsel,因为如果不是使用三星LPC3600/LCC3600 LCD,必须禁止LPC3600/LCC3600模式(写入0到TCONSEL)。
$ H, E5 Q9 ~& Y4 k3 mnum_displays 表示有几个LCD设备。 R, w7 r: {" p6 I' r
- e: R7 j) T( Z3 `( ~3 G
default_display表示这是第几个LCD设备。4 }2 s* I$ e6 G2 ?
5 T# D0 A5 C0 @3 d0 |- O8 t* z- k# w6 S, v+ t4 E+ O
2 P& q' J+ \% R! j. K2. 测试4 ?" |; W y8 a
& A& s5 ~+ {) W/ _( \在修改过上述两个数据结构之后,直接编译内核,然后下载到开发板上。
% i9 x1 d, P; H7 y8 @- q( x' }) Y5 O. y
可以执行命令fbset,输出如下:1 D+ s+ T! v# C4 `% u) ^! f; p
g8 u( Z4 `7 y/ s' ?
mode "320x240-58"
- c# N, ?) d( U) t5 @5 l/ v3 ]2 i* u3 W! e
# D: 6.400 MHz, H: 15.686 kHz, V: 58.097 Hz0 z( z/ ^5 A7 O' R
geometry 320 240 320 240 16, B6 i- D5 D2 l
timings 156250 20 38 15 12 30 3) H2 B8 @# F$ U9 e) i. O9 f5 g
accel false. i. N6 [) s' F X9 Z2 p3 l
rgba 5/11,6/5,5/0,0/0
: L/ U' M. T {/ b+ [! Uendmode
: A* h7 n# u. Z) V/ f: ]) _, I
+ l6 u- ~0 F$ }( q上述中的数值都是我们设置的参数。+ i7 T9 Q Y9 n) T* U4 U
: C0 o2 J# ^- E8 V. H
另外,可以通过下列测试程序进行测试:
" J' q1 N) f" j" m' u9 K
1 ^8 ?, @; R; ]* ~+ U2 z#include <linux/fb.h>% w s( h* x7 F$ C7 H' U; Y
#include <unistd.h>
5 l d- `/ V1 W/ h# N#include <sys/mman.h>
. {1 w1 I( u: g8 x8 m#include <stdio.h>
0 [- \) j. t* A5 o#include <fcntl.h>0 w7 R7 A( i' o0 G; D2 Y$ o* L) H
//#include <syswait.h>
1 h6 O) l8 v. g/ r/ ?. e* `; N- f6 W$ ~; u3 t7 K6 q5 \
int main()# r7 o$ ?" P/ t5 h1 N$ |
{3 R) E0 m6 @" q; r7 P; A1 M
int fd, retval, i;
5 q" L. T1 o9 ], c5 R$ y( W- x struct fb_var_screeninfo var;
& G* {6 w' p6 l; H8 r& B struct fb_fix_screeninfo fix;
; M, Z1 |) h6 s3 B9 ] unsigned short *memstart;
6 N8 t2 \- |4 [% v: |* l
4 ^2 @# M- |; N: y, L fd = open("/dev/fb0", O_RDWR);
l# O" D: {8 V, ?1 k5 i$ D if(fd < 0){
; T/ n/ m. A/ t; M- k printf("open /dev/fb0 failed\n");, g1 c9 e e, u9 `5 [: z0 r" W2 Z
return -1;
2 \: s0 j$ B! m: A }! x8 ~. a5 Y7 `3 p
) j2 \* Q+ x" B7 } retval = ioctl(fd, FBIOGET_FSCREENINFO, &fix);
2 [& _" D& k# ~# d3 [" X& e1 C; T if(retval < 0){! |) y( ~4 f3 }. ] [4 `
printf("ioctl failed\n");' {. f8 b) _! }0 I5 X7 y
return -1;
2 q0 M* S7 w0 b+ h( Z }
5 x1 d$ L7 T6 \. m
* j( _" J$ ?, S printf("seme len= %d\n", fix.smem_len);
$ x2 ~: ]# {4 Z/ v% j
, w7 d, Y* H+ r1 X7 i memstart = mmap(NULL, 240*320*2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);; c/ ~+ Y4 f5 I' r1 F% G6 z
if (memstart == MAP_FAILED){0 s- A% \6 ?- P: o% F5 n y
printf("mmap wrong\n");7 e# ~6 l7 a8 S( Y& k! r+ b8 F! J
}
- j0 |$ s8 R5 x3 o+ k1 H5 N; Q ' _! h2 A( Q; B, ~8 E
for(; ;){
8 C& j' m5 z+ g( F# w- Y% m sleep(1);
' u( I" k" Q; w# S for(i = 0; i < (153600>>1); i++) y$ j% Q" [: n. F" E8 ]1 b
*(memstart + i) = 0xf800;9 [2 U& O( B; J# U- y2 I: g1 u# R
sleep(1);% p1 ]* q" m h7 G2 \0 h. }- l2 B
for(i = 0; i < (153600>>1); i++)
7 e4 K* b* a" M$ Z% J2 l" F *(memstart + i) = 0x0c00;
3 I0 A" X- q# w4 M: ?6 B }6 _6 v. X4 }4 R) [' U8 {
}
$ r0 c, q/ r. o) ?8 e9 i
. p R& l2 H8 I9 O该测试程序是网上的,我只是稍微修改了下。0 p2 C6 ~" D- R+ n
% f+ w$ ?( Y* L$ j/ r3 V8 z) @0 i* Y2 {. [" _$ ?+ x- I
, g" T6 b' i4 q |
|