TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
PC主机:Ubuntu 10.4 和redhat 9.0
8 C; T! _1 V5 W4 e# Z$ f7 Z" w6 k4 C, [
目标板:TQ2440开发板 Linux内核:2.6.30
4 G" d1 W! s3 q' g; u0 m& x- J5 w7 z
屏幕型号:WXCAT35-TG3#001F 分辨率: 320X240
8 e) o0 l* k/ t8 W5 c5 S0 Z$ U# z$ F v4 o6 P1 _
p: d7 d7 b2 ?7 j/ h4 l( M4 Z6 t. V, V! `0 Z
本文将介绍如何移植LCD设备。3 k4 Q( g- f: a+ c% I1 L* d4 f
( ~% A) B# n7 B \2 D2 |+ w+ u
在移植前,先配置下内核,将LCD设备编译进内核。# J) U" ?$ J& V/ M$ h3 ^$ e
' ~+ y+ Z& X$ Y9 I" Q- z
/ ]& G+ [4 V0 l$ _
7 b d4 k: `! j! `- u
j. p6 \" W5 n/ b0 T. a4 _- ~1.移植
. Y* M. g! Q4 v9 _
% ~ U- N, Z, c) V: d% n移植LCD设置只须修改位于arch/ARM/mach-s3c2440/mach-smdk2440.c中的两个结构体的数据。
* m( O( M- r' ~, {; f) X! E, T; D$ w* j' F; n1 \2 F5 U: L. V' Y/ s
1.1 s3c2410fb_display结构+ r% z0 [8 A# V4 L
2 P+ i: r& L p; h修改后的内容如下:/ C: W3 K' q; H* F! D" E- S% ?
& t- m3 m9 Z, ~3 B/ t: ^- l
/* LCD driver info */
: {6 [ i V3 C- m
) J# W) T" ~- @" J; T4 R) P2 tstatic struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {( d- [7 @8 h) E. V7 s
u8 l9 R; }1 ]+ R0 N0 T
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
' B) W. v/ H+ Q( F S3C2410_LCDCON5_INVVLINE |; n: K; N9 w9 v7 ^: |
S3C2410_LCDCON5_INVVFRAME |3 b8 u; ]' q% m T' u8 o T
S3C2410_LCDCON5_PWREN |
! Z+ f6 M9 Z7 k! i: c S3C2410_LCDCON5_HWSWP,
1 N( x [3 ^( F2 i8 O; z& ]# g4 M# ?( |8 C5 {. @
.type = S3C2410_LCDCON1_TFT,
. O# n" E- s: B$ y5 n% f
3 I5 I x$ u, B .width = 320,//240,
0 x! |; L( V' c# h( _ .height = 240,//320,1 {" g2 F$ f- u% `
) C' S0 r* `, P9 s5 F .pixclock = 156250,//166667, /* HCLK 60 MHz, divisor 10 */
; T: q3 G/ ~0 K6 N9 D .xres = 320,//240,
/ A; Q0 M. ?0 S1 t .yres = 240,//320,
# N# l4 Q! G1 h9 d .bpp = 16,' J# M, i2 k. d$ Z# ^1 [& \/ c- ?
.left_margin = 20,: \' W$ ~1 x: i/ [) x
.right_margin = 38,//8,
. ^. W2 a) t! h$ s .hsync_len = 30,//4,. M+ `" I) q r9 f. b H; u
.upper_margin = 15,//8,/ l3 d5 u4 ^ f8 n7 z
.lower_margin = 12,//7,, L1 F7 y6 Y3 Z4 N) U; a3 w
.vsync_len = 3,//4,
6 o S. \/ g8 d- v$ u) S5 f# W' m};
" x! c+ {/ G6 @' A" H
+ E9 x+ I: X' }' \: q; f) e# F2 o S8 }
上面的参数是如何修改的呢?我们来看下。
! a r& N" ]( B9 m: J- S& k; W
* ~# C7 _) g# D4 Atype表示显示模式,这里为TFT模式。
h/ U$ P9 A( e) u+ P/ |9 U. G/ ^/ B* y2 c* k6 p
width和height表示屏幕的分辨率,我的分辨率是320X240。
& {8 D3 Y2 f3 H/ z: h* L; u$ e& X& K1 A) z5 |' `. R2 G, h5 B
xres和yres分别等于width和height。+ k; w+ d( Y) {8 k9 d8 K
5 _, b0 W! x: O( r2 Y9 lbpp表示所每个像素点位数,这里使用16位。
Y1 c" e! W. S7 f" F- O4 C9 v0 R8 D, ~3 ]
left_margin,right_margin,hsync_len,upper_margin,lower_margin,vsync_len这六个参数的值由LCD的手册给出。下图为LCD中的参数:+ S( N" w3 Q$ G
$ L* x# C o) J; x8 [# e
% J/ S& A- l; C
$ k: o8 d3 a: U2 i6 m7 d, d6 I! n5 W4 f ~$ G/ @/ ^7 G
在这里,我给出上面6个参数和LCD手册中数据的对应关系:
' @* \7 z7 y3 W# W/ r
$ D5 C$ @# P2 z' j/ o .left_margin = Hsync front porch = 20% ^& j' b( L8 [( E
) _: Y {8 }! d q! X" s
.right_margin = Hsync back porch = 38% D; Z; u: \" ]7 y- |) i# z2 [
, m/ K1 T6 R0 _( ^. F4 p8 a# | .hsync_len = Hsync pulse width = 30
0 P5 J# c- S3 h- i
7 n2 ?5 q, c$ o: t, ~ .upper_margin = Vsync back porch = 15
" d0 k3 _9 m+ D" u- D# |
/ ^ e' R h2 s5 v5 o. V) k O! \' t3 I .lower_margin = Vsync front porch = 127 y3 Z+ w0 H: s' e. _8 m
5 d% F) U( C2 [- z- p; c
.vsync_len = Vsync pulse width = 3* c% p* P$ M( |0 ^. _ A, O$ n
' b1 L+ F; N7 \1 ~. }. [; y
" F- @: y4 G, O1 M% }$ Q x
pixclock的值是用来计算CLKVAL的。在S3C2440的datasheet中,CLKVAL的计算公式为:& p8 ~0 v7 M( E! ~2 j G
$ z! {* z* W: _, S- B n1 Z
CLKVAL = HCLK / VCLK / 2 -1,而VCLK即为上面图中的Dclk,值为6.4MHz。
: ?+ v, W3 E4 ^: k& Q, r% f4 \/ G, @$ l1 j. Y( v0 T7 ]
接着我们看下驱动程序是如何计算CLKVAL的。
& W3 k/ q; f! X# I- M/ Q* e* E+ x( Q3 I5 X% J$ j0 w
/* s3c2410fb_activate_var" H; r- z2 Y7 u( k5 R2 ^- x
*
( T: T& ~* d& A1 ^, n& M * activate (set) the controller from the given framebuffer9 {* N, L4 g/ b2 x' `0 \2 Z
* information/ w, Z2 z) a# V2 r" k
*/
# K6 m7 b% I sstatic void s3c2410fb_activate_var(struct fb_info *info)+ k6 b; `( q8 t9 w" o( n3 b
{( x" S( D. N' ]
struct s3c2410fb_info *fbi = info->par;/ S+ m2 m4 C: l$ j
void __iomem *regs = fbi->io;7 C$ g5 R/ A& v% L
int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/ b# [5 a. n$ _! E" b
struct fb_var_screeninfo *var = &info->var;1 _% Z b+ J' \* }' l$ u9 p+ m
int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
0 B" L/ n6 u. I' M$ d; V5 X# E. r* o
dprintk("%s: var->xres = %d\n", __func__, var->xres);
- m7 o$ U! `5 @6 p5 K4 c dprintk("%s: var->yres = %d\n", __func__, var->yres);% ~3 V. M2 W) N8 \
dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);) I+ F& n3 i& Y6 a2 }7 y) f, N
8 @' S# h( _6 ?2 _: U1 ] if (type == S3C2410_LCDCON1_TFT) {
' U' c3 v9 j) Y P s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/
) W- q1 m6 B$ v, v1 s @; g --clkdiv;
; B; o/ U; H9 g1 G1 Z# n if (clkdiv < 0)/ V: D. Q: S" F! K
clkdiv = 0;
1 u8 z' ?0 L5 l8 T J7 o } else {
& L. s+ w+ ?3 }- G/ R5 Q2 q s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);& j4 [ c6 [( I. q: h
if (clkdiv < 2)
# V5 f+ n5 Y+ \% t3 c/ K: A& r2 c clkdiv = 2;
7 D0 a: |/ n, a9 h& |/ {6 R }
! C, c0 m2 ^7 t6 ~9 n, E
5 Q0 Z [0 f3 j" x. M, {6 ` fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/
# t5 q4 c. Q: P9 Z7 Q6 @7 Q- W: n
/* write new registers */% k! c! w2 f3 ]% H
# }; F8 b. ~0 [/ k6 W% P/ c
dprintk("new register set:\n");! b4 I, {; E1 X
dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);, s' {/ P2 }. @0 x) T! {
dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
- a0 P) i/ w2 ?5 } dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);0 Q; m/ G& m5 c% B' x; ~* b
dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
6 ]/ X. Z, m' ~& t4 v dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
- p% i! o- a4 q9 j* u8 U2 l/ V /*把计算好的值填入LCD控制器中*/) D9 W8 _2 U% m' O4 k1 u: L
writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID, 5 h2 P; b/ i `0 O5 a- d, l
regs + S3C2410_LCDCON1); /*仍然禁止LCD*/
( J* d! o( p6 {( l) b writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
4 I0 @! m$ P3 F writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
, ]( f" L% r6 i+ l writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
1 o$ ?& ^7 l- l8 R( i5 ]) H writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);3 Z s- w+ ?9 ~
' t. i! k7 @2 c( |: w8 }6 U$ [; [ /* set lcd address pointers */
2 {' ?8 w" v# S f) \ s3c2410fb_set_lcdaddr(info); /*设置LCD帧缓冲起始地址*/2 w& o7 r; ~' ?: Y* T* f
/ Z) H o5 V9 g fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
- h2 J; K/ _0 Q y0 s' [ writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); /*使能LCD*/
. z4 y. J1 y$ Q* C$ Z}) F1 E) S9 k% N
static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,. C2 Q! v! Y" P- h
unsigned long pixclk)" n, e& U3 ]: i1 r5 i' v5 \
{6 C6 M# ?8 v: d8 d& a) J1 w
unsigned long clk = clk_get_rate(fbi->clk); /*获取当前时钟频率(Hz)*/
' z. K2 } X( l5 E+ s$ _ unsigned long long div;
% o' |) Z0 V; H+ W1 B6 B, W, i+ w; x8 K" w1 {9 e# C
/* pixclk is in picoseconds, our clock is in Hz' d1 p# [% o1 E% _3 e2 ?, v
*
* J3 f9 h3 c% `" g5 C1 F7 ~ * Hz -> picoseconds is / 10^-12
' t' N, ? `/ b( _! K0 R. _ v# v' _1 U */; z6 W; _4 V$ W/ P! y9 }0 W7 r
' `6 W1 d4 u# [
div = (unsigned long long)clk * pixclk;
$ {* v8 n, H! u5 ? div >>= 12; /* div / 2^12 */+ R' {2 d/ o; ~$ P6 ?! ~$ g
do_div(div, 625 * 625UL * 625); /* div / 5^12 */8 _+ W3 V _6 g ~& N6 a$ a) y
; v7 [, J, T6 ?$ ~) G& O
dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
9 f F% |; D, N" j return div;
% d. X. g* O+ ~* _$ R, x9 P( d1 C}' C* j3 o: e/ A1 K& m
6 q& ?# k. e; {) l6 Y% e9 m. }. A8 T0 e
首先pixclock作为参数传递给了s3c2410fb_calc_pixclk函数,当该函数执行完以后clkdiv = clk x pixclk / 10^12 / 2。; L# ]4 B S) p
x) @ K( C5 y$ O5 j6 V
随后由于是采用TFT模式,将clkdiv-1。最后得:0 H- B. m& K2 G, r. M- n& a9 @
" {& b9 o# M7 ^9 c1 j
clkdiv = clk X pixclk / 10^12 / 2 - 1,这里的clk即为HCLK,LCD模块使用HCLK作为时钟源。
% j- i/ v3 Q; a3 }3 j5 W( V" T
+ V" P' K0 p; q4 L" [为方便观察,将前面datasheet中的计算公式复制在此:CLKVAL = HCLK / VCLK / 2 -1。+ C/ p1 i5 {, C* O2 ?: O$ b( Z
( c, C9 y2 V7 `我们可以看出1/VCLK = pixclk / 10^12,也就是说pixclk = 10^12 / VCLK。 我们已经知道VCLK=Dclk =6.4MHz,6 s, o6 T- {5 r; R! E4 x7 ^
) C6 B9 m2 S; d) W- _2 r+ a
因此,pixclk=156250。
9 ~+ X- J: n' Q; ~; w( D
; c1 ~8 s, [3 Y5 P) N: I其实在内核的参考文档中有这样一段话:
- C/ L) Y$ l) p6 a& O, \& O+ u9 z' k; \) s) n, \
The speed at which the electron beam paints the pixels is determined by the( I5 L9 O$ ]9 R( @8 L$ J8 {7 I
dotclock in the graphics board. For a dotclock of e.g. 28.37516 MHz (millions
1 i/ B3 h5 h* Y" {. Mof cycles per second), each pixel is 35242 ps (picoseconds) long:' t" i: Q4 _% n
% V+ h% c. |# [% G( H( Y 1/(28.37516E6 Hz) = 35.242E-9 s
6 s6 X) c3 W( u: L0 M# Q7 S- n) ~# C. S- q# M
也就是说VCLK的倒数,再乘10^12即为pixclk。picoseconds单位表示微微秒,即10^12。" }: F) v' Y6 L9 t
M3 E* | O H$ u! y
最后一个参数lcdcon5,它是寄存器LCDCON5的配置信息。
. a5 I4 u* O" |; B n1 w7 a1 ?4 P! w; R
S3C2410_LCDCON5_FRM565表示使用565格式。
# m. I. U; q+ ?( Z, q) ?
& ~& R5 p8 c& d! m) ^4 cS3C2410_LCDCON5_PWREN表示PWREN管脚输出信号使能。
2 y" ]$ \, Q& K( L) O5 u* t8 ?' A8 h/ k
3 e/ G5 b) L+ H6 LS3C2410_LCDCON5_INVVLINE 和S3C2410_LCDCON5_INVVFRAME 表示反转VLINE/HSYNC和VFRAME/VSYNC两个信号线,这个可以通过对比LCD手册和S3C2440的datasheet来得出。" K$ a/ p. g$ ~9 D. ~( N
$ T1 z: Y+ _ p% r: C4 I
S3C2410_LCDCON5_HWSWP:由于使用小端模式,需设置半字交换。+ g1 p+ {# [/ y6 h* l, ~
1 I, [8 ]! c" K6 C% j! j/ X& H# _" r
1.2 s3c2410fb_mach_info结构2 p4 X. L+ S8 u- S$ J
8 z8 Y4 e3 c( y; D5 W+ C
修改后的内容如下:
4 d7 l3 P3 c1 ]0 c+ k: ^+ [4 |5 o
static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {/ \+ K: R+ J- W. S6 E
.displays = &smdk2440_lcd_cfg," ~. ?6 T1 o% _: |
.num_displays = 1,/ W9 m7 m/ {; ^$ @ F1 f
.default_display = 0,
6 V# C! o* [! n- v% G \) j% U* ^1 E0 O+ ]+ }3 e2 X/ M ^
#if 0
5 R- K# x9 c% @( _$ ? /* currently setup by downloader */+ y) Y6 V2 y5 K0 o. D+ z
.gpccon = 0xaa940659,. M0 ^( O! U0 R3 ?& l
.gpccon_mask = 0xffffffff,
0 C6 V& @2 |4 D0 D6 E .gpcup = 0x0000ffff,% m o) r9 j; V" I0 k" R
.gpcup_mask = 0xffffffff,
' D7 X( W- ]# w; K .gpdcon = 0xaa84aaa0,, i( ?6 P" |( o1 M4 V
.gpdcon_mask = 0xffffffff,
, z) r& }; ^: p, Q/ M .gpdup = 0x0000faff,7 w' H5 O* T' V) R, V/ H
.gpdup_mask = 0xffffffff,6 x. Z0 s7 }$ k
#endif
8 s. |; T; G% F# K% b//no1 |6 _; V. v* B* O: ~6 D
// .lpcsel = ((0xCE6) & ~7) | 1<<4,3 L8 L/ g, T- L2 o2 }& f+ d+ Q
}; H6 H" Z% R8 l) v
, A" J) d* H. [- n
做出的唯一修改就是:禁用lpsel,因为如果不是使用三星LPC3600/LCC3600 LCD,必须禁止LPC3600/LCC3600模式(写入0到TCONSEL)。
$ x6 R) W$ |& Hnum_displays 表示有几个LCD设备。2 T3 w9 s+ `/ o
) W3 \1 E* x$ C- L( j
default_display表示这是第几个LCD设备。
0 D0 d) R; A5 \- Y) u. C1 b7 i4 `
4 Y' m) b5 ]) m* g. Y6 F$ {
/ h9 j: p* t8 a1 w5 t O, q, m/ G# [5 W, V4 s
2. 测试' ~7 O- Y& L( C
+ i5 Z4 \; G7 T4 l9 f- ?( ^
在修改过上述两个数据结构之后,直接编译内核,然后下载到开发板上。: t0 F$ `6 K+ l5 S1 x
, o0 o& u. V1 \0 e8 @1 k% O1 v可以执行命令fbset,输出如下:
" G1 b& X% Z; K2 D1 R& ?- Q' v# b' _1 B) s+ y
mode "320x240-58"
7 r* i, K6 p8 t* v5 p/ D% O
" N8 K: v2 q, w# w, u8 `) e # D: 6.400 MHz, H: 15.686 kHz, V: 58.097 Hz/ r7 V! C5 X, m# ^/ U
geometry 320 240 320 240 16! K1 M6 l& t$ f
timings 156250 20 38 15 12 30 3& v# |$ d' g8 t" Z1 O. e
accel false
4 ], \; z5 q5 V1 [6 G# j rgba 5/11,6/5,5/0,0/0
1 M$ X8 c0 }5 G# n: Kendmode( k6 _1 i0 k& s* c) L" Y5 c' G
- k! U7 @! O7 c) A' X0 d
上述中的数值都是我们设置的参数。
. }& k+ x& u+ c( z* ~6 F- Y! H) c. x, x6 d
另外,可以通过下列测试程序进行测试:8 r2 k& ]/ Y1 b) f. q+ s
% |! r* N9 a" _7 y" c3 E#include <linux/fb.h>
8 [0 I9 C0 I' T: r/ _; v/ {#include <unistd.h>
4 ?/ Q8 K- {- P$ P- L#include <sys/mman.h>
5 ^* d% q. g1 g6 |8 M! D& v# r#include <stdio.h> N( P& Q6 w8 v3 a- F% b$ g
#include <fcntl.h>4 `# I* z8 _2 ~, I" |# z8 R; N
//#include <syswait.h>
* x9 d3 H& X" P+ f/ R
: Q8 z" n5 c& rint main()- j; i: U) n6 J* w8 o- l
{
- w1 y6 n# F& h: f int fd, retval, i;
9 m- I% @# M# ^; @( B struct fb_var_screeninfo var;5 I! T; Q/ D( X8 }6 y
struct fb_fix_screeninfo fix;
4 D7 G! O5 n/ O7 y9 k7 H. M; j9 J unsigned short *memstart;
o. A2 L/ K' B3 F, Z5 E" b4 ?( \7 ?, t% v
fd = open("/dev/fb0", O_RDWR);6 E0 l$ \1 P. X) V$ `. m# K. Y7 ^
if(fd < 0){
A& _+ c. L+ l& S! ` printf("open /dev/fb0 failed\n");
& f: l5 P x9 g& Y" ? return -1;! g$ ]0 Y' i% d+ t( E
}
: U9 l9 E0 m' i: _1 { C
, E8 D: s/ p! _ retval = ioctl(fd, FBIOGET_FSCREENINFO, &fix); ^2 H ~" |; Z' k+ ?) [+ j
if(retval < 0){7 Q; z9 _1 H/ {) E( S
printf("ioctl failed\n");
3 s- s. J; u; A2 ~, y) D$ H3 N return -1;. q n/ A& N5 O O
}
, |' E0 x" }8 r9 \
2 w5 ?5 X$ l0 F% I. \4 J) g printf("seme len= %d\n", fix.smem_len);
5 |' c2 P: P' c- h {! b, s
( m7 z% T F# I4 l memstart = mmap(NULL, 240*320*2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
7 P6 N0 b E% y if (memstart == MAP_FAILED){. _1 E& X3 G; [: h4 {
printf("mmap wrong\n");! s. P3 n2 M, O( d9 P
}2 [5 L2 s' u* W9 X4 D4 M6 F+ |
* q; C8 f6 N5 w5 p+ t! M% p. z( g' }
for(; ;){" P" l$ H3 l! ?8 Y
sleep(1);
+ m& L! {4 L$ e for(i = 0; i < (153600>>1); i++)& Q( i8 K2 @4 M/ p) O, r1 o7 ]
*(memstart + i) = 0xf800;
9 Q3 M% R/ t+ F8 J sleep(1);) p1 q! L: i" _& M) o. G* y/ m% S5 M
for(i = 0; i < (153600>>1); i++)
; _) _6 M- K. n *(memstart + i) = 0x0c00;
% B' O4 W2 Y# \ B& Q% i5 F% w }- L+ \5 D0 g( e4 I5 D; a% k+ |8 x
}) n4 f3 J! m: Z; e) |9 N( |
, ]9 N- m- I; C+ m该测试程序是网上的,我只是稍微修改了下。: S# x, U0 j% l4 j
, [! j5 J0 R. p! ]/ z( z" ]: T8 E# M
+ y: @; ?$ [, }# G+ j5 K- f |
|