TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
PC主机:Ubuntu 10.4 和redhat 9.0/ w, h) \. V9 {% e
2 q4 C9 ]/ `9 j/ H: O
目标板:TQ2440开发板 Linux内核:2.6.307 W2 n5 E- u: \, Z' _. L
: r2 b& H8 _6 n0 _& x9 J屏幕型号:WXCAT35-TG3#001F 分辨率: 320X240
7 ^: p- Z) J% F: n. P! q6 z& G0 N6 N
% K$ V' i) D- N$ x! Q' l
% p% ]5 ]; _/ S# a# d$ v7 E' h8 ~/ ? Q; X8 |
本文将介绍如何移植LCD设备。
' Z% Z9 `- u( K1 `3 L- ~6 n$ G( P; h4 b5 z" k
在移植前,先配置下内核,将LCD设备编译进内核。: @$ t- ?; u: J7 j8 g
4 u# }" ^/ [6 u8 o
! H2 G/ }+ \ A( R2 j; }8 j
+ u1 p& I4 h. Q0 `: U) b. t
% f- C+ n% e& b1 G- Q& w5 G3 L1.移植# C5 k" f1 b8 w! \0 X; H
e0 v; _/ m9 Y% h% Z3 f移植LCD设置只须修改位于arch/ARM/mach-s3c2440/mach-smdk2440.c中的两个结构体的数据。; \8 B6 x3 x( T5 P" a1 f) @
5 D1 r* d1 ]2 A8 P$ a7 g1.1 s3c2410fb_display结构
% k) Q u% t# Q# _* c% T2 I* @& q
修改后的内容如下:
+ z% E3 [2 C. q0 y* b! R. a6 G
9 {4 u- s- w7 M/* LCD driver info */ p# i2 N# @# {" N2 b5 F& R
: Q8 [6 s$ L: ?# g% M) ]: o1 @static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
5 g- g/ g" J: H% l) E. X$ \
) H7 }" \- s9 o9 E& V$ |! `& R+ G7 F .lcdcon5 = S3C2410_LCDCON5_FRM565 |
& M4 U0 B! f! Y S3C2410_LCDCON5_INVVLINE |' A) E+ ?1 S+ U0 Y0 D" ?$ X
S3C2410_LCDCON5_INVVFRAME |
% V& s4 {6 l+ C' Y3 p, X, Z. x( m S3C2410_LCDCON5_PWREN |
, v! M- @9 u, }2 ?4 h# S" P S3C2410_LCDCON5_HWSWP,
# \( P" A1 x. @) E! u
: H0 P, D9 J4 l( M .type = S3C2410_LCDCON1_TFT,
/ o5 w6 Z O7 Z# S" T
s( f$ I8 F7 Y) E( o! D" e .width = 320,//240,5 m m' J. N& e3 c3 Y/ j& Z8 R
.height = 240,//320,7 o" t( j+ h# Y
9 E" z; w) E( v* b6 P .pixclock = 156250,//166667, /* HCLK 60 MHz, divisor 10 */8 l8 L3 \* I/ Y. _) u" r
.xres = 320,//240,
% t K- c7 S" u .yres = 240,//320,
5 L) o# M! t! l( s8 w& t .bpp = 16,
" Y: Y8 ?: ?+ }+ ^! w& a% w .left_margin = 20,6 P$ ?1 a3 ?3 e% g, ?
.right_margin = 38,//8,
% p+ ]8 p: o: T$ e .hsync_len = 30,//4,/ r. i7 }$ C8 L8 {
.upper_margin = 15,//8,
( T$ S5 v! g; p( O6 X .lower_margin = 12,//7,/ T- E. z9 j& b% J- C4 w
.vsync_len = 3,//4, Y0 j# X/ t0 H `+ `# t7 [
};; m' x5 U& y& J* ]3 |
% b. p0 B( r; Y
: _5 S% ^) T \; A6 D. p上面的参数是如何修改的呢?我们来看下。
' R7 d1 o/ ^; X, D/ X" J+ O( A" o2 A
type表示显示模式,这里为TFT模式。
`4 y% Z5 G5 k. M+ v6 G# @( X2 b: i) w2 A L, l0 f
width和height表示屏幕的分辨率,我的分辨率是320X240。
8 A& h: H0 d5 c& X% e5 f
+ ~4 B% o1 O* _" W, l$ w6 dxres和yres分别等于width和height。
9 Q. m5 [( \7 C3 w, P' c0 Z. d3 W, Z% B# N9 f
bpp表示所每个像素点位数,这里使用16位。
/ R" j# C0 }, K8 [/ ^! k) d* o) _7 E7 |
2 f. b7 \, o- ?& Wleft_margin,right_margin,hsync_len,upper_margin,lower_margin,vsync_len这六个参数的值由LCD的手册给出。下图为LCD中的参数:, P6 O* z+ R% O4 {/ F) J
/ d8 n' Q# y7 m) T: J; k3 ]! q
' k7 K+ v) O; D. l
, a) k: o0 X; K8 }$ I6 V
" t- _7 s3 F- H
在这里,我给出上面6个参数和LCD手册中数据的对应关系:
5 o) E: g8 L. y0 V" M" \; W* b" X' H' @1 |/ g" k* v2 G5 c; j5 p
.left_margin = Hsync front porch = 20( m/ z4 y% S( S* y* m' Z. E
" N. N/ ?3 D+ l% O. ^$ @0 Q .right_margin = Hsync back porch = 38
6 c& H* ?! \0 O* m @$ c- j: U
/ e% ~4 o# \3 k .hsync_len = Hsync pulse width = 304 ^+ j/ G' [1 @# [1 U }8 G# o
4 b' {: v% n' k: F' c+ L9 L- v r# b
.upper_margin = Vsync back porch = 15, P8 r/ N+ Z. h
! P+ L( |' P/ H7 e+ F9 f) V
.lower_margin = Vsync front porch = 12
& L$ R5 ]* a" o3 v% h7 s+ y g/ R" }: v# r2 d& }0 ~
.vsync_len = Vsync pulse width = 3$ {, t! ~; L- b$ j
. P0 G. B$ ?7 x# |" D$ w
8 l+ O* P: t- r& H" N, T% e, w, f" B9 Ppixclock的值是用来计算CLKVAL的。在S3C2440的datasheet中,CLKVAL的计算公式为:9 J1 u, E8 Y! Q5 T. z, C
" w3 H+ g2 W! h; R
CLKVAL = HCLK / VCLK / 2 -1,而VCLK即为上面图中的Dclk,值为6.4MHz。
) K) r. A$ v. q+ k" S& X2 K% l9 v2 ~0 p
接着我们看下驱动程序是如何计算CLKVAL的。
2 h- q* f' u8 U4 e/ U. ?7 l& |% ~7 e2 E9 m$ @
/* s3c2410fb_activate_var
( z, k" u7 L( e- ]* M( e0 U/ F *
9 `2 P5 \2 w2 ^) _5 v: d * activate (set) the controller from the given framebuffer
1 w8 i* E$ r7 T0 y+ { * information/ M7 G( k' c e4 B& B2 B
*/
$ ]+ N: x# a3 c7 G: h: ~% F- Vstatic void s3c2410fb_activate_var(struct fb_info *info)
# T2 k' V4 d4 D, A6 @( y e{
- \: s; k6 S7 O5 ] struct s3c2410fb_info *fbi = info->par;
q5 ^" J. w+ P5 Z' J7 R7 Y void __iomem *regs = fbi->io;# l5 @, |# K, r5 V) |! K/ |- {' c
int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/" o7 g- H* ~0 u: r( H$ r$ K+ X
struct fb_var_screeninfo *var = &info->var;
. c2 y6 M& a ?. {7 X/ Q6 k int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;% M5 {) r/ R Q# l/ L9 M$ y
& }# O) w" j1 C6 M5 w! A
dprintk("%s: var->xres = %d\n", __func__, var->xres);
, n9 Y E$ L1 F( q dprintk("%s: var->yres = %d\n", __func__, var->yres);% h' N+ N+ s3 g X& F; `
dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);& b3 F. w% d2 ^% Y7 X% L
2 e7 M! e0 A- L
if (type == S3C2410_LCDCON1_TFT) {6 o, ]" a) ^- T& M/ I A
s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/
+ N2 a+ i9 m# c --clkdiv;) y- c7 [& K1 t9 B1 `* h
if (clkdiv < 0)5 m# v7 A) T' m% z& `+ v
clkdiv = 0;& C0 @" R" W7 g# `
} else {8 ]& e+ k# b/ }
s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
L ]/ X6 i, N# | if (clkdiv < 2)- N) W: H" n9 L V$ _% a) h( s
clkdiv = 2;0 |5 B+ S0 r8 o: v
}
0 k+ w% X( ~ y0 p4 {3 A0 M) Q0 K' T& G/ H$ i
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/
4 t6 A5 m) W) [( b5 C- ?. d& B# }. _9 w' M
/* write new registers */( \- }1 t. M, }- H& r$ e
* a R% W: Q9 H, O* j! A) k/ e
dprintk("new register set:\n");
- F/ g, D6 B4 c dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
" t/ M# _* s y$ A" V/ K dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);# \' A4 _6 @, [- {: ~$ U( `/ I$ R
dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
- p: y- }3 u: S. e+ o1 S: @ dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
: G6 Z J: _; i# u2 X dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);$ c$ w0 D6 a+ }: b S1 q C4 q4 k
/*把计算好的值填入LCD控制器中*/% T! y% ?. P3 D" W
writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
+ x/ J: S, a1 q" h- @( s regs + S3C2410_LCDCON1); /*仍然禁止LCD*/
" B6 P% N4 ~7 y- K8 S5 H writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
$ p( s& O7 Y7 S/ Z* T writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
) W. @" D$ {) J7 \/ o writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);. B$ N# t" G, Q& i8 N9 N
writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
0 X$ S' r8 \( D- J% r) f: G/ M+ f' J4 U& E; K
/* set lcd address pointers */
2 ]7 {: q4 T$ h" S N) }* g' r s3c2410fb_set_lcdaddr(info); /*设置LCD帧缓冲起始地址*/% H U8 G/ ~( ~% I
0 ^3 f5 W7 b# z fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
+ a: \2 x; g& J4 O( m writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); /*使能LCD*/; ~$ Q3 L7 i2 z0 f& E
}& n/ j6 l2 i% Z5 n
static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
8 [% t( U2 ~+ X" Y) ^4 Y0 O unsigned long pixclk)
" a3 u2 {+ l2 W$ X4 n{# D% q* I" d" j' w
unsigned long clk = clk_get_rate(fbi->clk); /*获取当前时钟频率(Hz)*/
. X# [* M* Q2 n* h unsigned long long div;" {3 k6 M" o$ @3 f! r- R/ y1 x
1 O4 o! ^" F! O# r- U! Y! X v /* pixclk is in picoseconds, our clock is in Hz
" F; i- G. V; B% r( b9 R *. J7 W9 |* }8 f6 U" `
* Hz -> picoseconds is / 10^-127 j8 _8 U4 Z* L$ |* @0 c9 W$ M
*/+ R! T3 G: i9 D! m$ o
4 P/ Y1 t1 v0 Y+ F# F9 M$ E div = (unsigned long long)clk * pixclk;
# f2 v6 x8 f F8 Z; V div >>= 12; /* div / 2^12 */
8 z' M2 p7 `6 P2 \. @ do_div(div, 625 * 625UL * 625); /* div / 5^12 */
+ h( e5 e( i8 G7 a; `* |7 J' x" f+ n/ s- ?
dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
1 [" \; S) w/ }( g5 L return div;
, q0 C- ~6 r( a% k/ ?1 t4 `* ]}% }" D% n+ i8 B' G3 c' V
# u2 n2 P* J% ?7 H* q
) J4 s: _: O* k
首先pixclock作为参数传递给了s3c2410fb_calc_pixclk函数,当该函数执行完以后clkdiv = clk x pixclk / 10^12 / 2。" E. V9 D# N4 P6 P( r
1 S1 w# Y7 R+ a* j) i- P: J随后由于是采用TFT模式,将clkdiv-1。最后得:2 `) I Z, c) V7 g, y3 E1 m
/ [8 R& V) v7 E Q& v0 Y4 }% Vclkdiv = clk X pixclk / 10^12 / 2 - 1,这里的clk即为HCLK,LCD模块使用HCLK作为时钟源。* z6 N2 [; s7 b# ~2 ], ~9 H
; ]# D* W) k$ v# m
为方便观察,将前面datasheet中的计算公式复制在此:CLKVAL = HCLK / VCLK / 2 -1。7 u3 A4 w2 `8 C( W D% O
5 `) n. i( T7 e9 U* \
我们可以看出1/VCLK = pixclk / 10^12,也就是说pixclk = 10^12 / VCLK。 我们已经知道VCLK=Dclk =6.4MHz,
; l w, D T+ h3 d# i( ?% g b3 f
; J1 W$ x! A' V& ]; g因此,pixclk=156250。+ i: N" `/ M" z' H
) D9 j: x, K& g" T5 A+ l
其实在内核的参考文档中有这样一段话:
. f x1 ], t4 q2 y5 i1 ~4 q' Y# S$ r
The speed at which the electron beam paints the pixels is determined by the3 p% d7 U9 g* p0 ?9 `
dotclock in the graphics board. For a dotclock of e.g. 28.37516 MHz (millions
' a2 Z, D+ C! d8 kof cycles per second), each pixel is 35242 ps (picoseconds) long:
/ t! Z& w2 m& v
. s0 w. g2 [1 c6 `* T/ a; L 1/(28.37516E6 Hz) = 35.242E-9 s# t, J# K* w t1 a4 A8 b
' ]( q. ~1 H% Q3 O' H
也就是说VCLK的倒数,再乘10^12即为pixclk。picoseconds单位表示微微秒,即10^12。1 W) {5 Y4 o5 D2 C4 W
$ i- x# ?. U* p最后一个参数lcdcon5,它是寄存器LCDCON5的配置信息。* [& F, ~/ I) T7 `7 Z. ^
+ u& _( |) p; A" _$ V
S3C2410_LCDCON5_FRM565表示使用565格式。
9 _* A; w0 z1 v* j; y: o' C0 Z$ [
( p$ D/ L3 M: e) _% KS3C2410_LCDCON5_PWREN表示PWREN管脚输出信号使能。
) P! k* I: b! F9 _/ F
3 ] s4 o4 i0 S4 r6 r; OS3C2410_LCDCON5_INVVLINE 和S3C2410_LCDCON5_INVVFRAME 表示反转VLINE/HSYNC和VFRAME/VSYNC两个信号线,这个可以通过对比LCD手册和S3C2440的datasheet来得出。" M+ C& d0 _/ }" P6 K
6 R- x, ~* L) q
S3C2410_LCDCON5_HWSWP:由于使用小端模式,需设置半字交换。) T) W8 ~, L& a0 U) l$ |
3 @( ]% K2 u, @. z2 [; F2 P
5 B1 L5 `+ u3 T [) l2 P5 |/ F1.2 s3c2410fb_mach_info结构( w- g( M3 O) c% Y) X0 _, c' g
Z# H% J9 G; x# n修改后的内容如下:0 C- D; w* |& b7 S
0 e) I& x7 `- M) A7 |static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
6 J, {8 M" \" d( Y8 y# Z- ]: `/ O4 s. L .displays = &smdk2440_lcd_cfg,
( m3 J4 g- a; s @5 s7 O6 q* Y .num_displays = 1,
5 X' g& s" B1 R) ?7 U .default_display = 0, e: J: B5 [* H V% T( l
( O; O( r5 U# ?! M8 j3 L# j
#if 0
" P, \$ R2 I: P/ } /* currently setup by downloader */6 I, {8 @3 y# ]8 T
.gpccon = 0xaa940659,
8 z7 D6 \7 x* c .gpccon_mask = 0xffffffff,5 }" l4 f* o$ o7 a. _ c* f' |" n
.gpcup = 0x0000ffff,- ~* w2 W2 [5 |% b
.gpcup_mask = 0xffffffff," @/ F. J& a3 g! |) R+ D* |% k
.gpdcon = 0xaa84aaa0,
, A! c. {; p( z* Y0 W0 o6 k9 P .gpdcon_mask = 0xffffffff,$ \& m/ ?) A2 I+ ]
.gpdup = 0x0000faff,5 R3 q1 D. Z, \
.gpdup_mask = 0xffffffff,
: C9 m! C) l% n! ~) n#endif
" r! N z- p7 s& n5 s6 L//no
5 U2 h' _9 n, {$ \4 b// .lpcsel = ((0xCE6) & ~7) | 1<<4,
: f6 |, v# y5 Z, ~! q1 h};
2 R8 ]! U8 C' Y% ]# V% P" d
+ m/ h; J, n$ }6 ^* T做出的唯一修改就是:禁用lpsel,因为如果不是使用三星LPC3600/LCC3600 LCD,必须禁止LPC3600/LCC3600模式(写入0到TCONSEL)。
, R+ e& _9 ^ F1 ?1 ?4 H" cnum_displays 表示有几个LCD设备。
" F; x) l) R8 _# n7 f5 V- S, K4 k7 q- M' C5 `, b
default_display表示这是第几个LCD设备。6 b, j/ ^% {$ ]/ R3 h M7 w
b# Y, w5 z2 @+ J: M
7 @6 b V. a: h# F
7 L4 @7 {1 t% _7 k; w2. 测试: Z" A9 O" T5 f J- ~
" w. d! y j3 B) p# B! R在修改过上述两个数据结构之后,直接编译内核,然后下载到开发板上。
. ]" N, s& O, M2 }! Z6 C+ b+ y1 K: ?/ g, x0 o: Y* T. G
可以执行命令fbset,输出如下:* f& c. J6 _6 ?+ n
: h6 F$ j8 R' F3 ?" z7 R. D
mode "320x240-58"" \2 j; p k# Z5 A& ]* O: \1 x8 P
( ]9 U& O8 [4 R/ J9 w, S
# D: 6.400 MHz, H: 15.686 kHz, V: 58.097 Hz+ T/ {. i- E/ s0 C
geometry 320 240 320 240 16; a6 U5 m% a* {7 P
timings 156250 20 38 15 12 30 3& _' v) G: Z! P; c7 f' K6 v: i/ M
accel false8 g/ s8 V$ r: C7 I
rgba 5/11,6/5,5/0,0/0% g& q$ G3 T& E0 P2 v# G% C
endmode! e ]- k& M& @" o- _
5 q' Q2 r* k, |& A u上述中的数值都是我们设置的参数。
3 X& j6 {4 k' t; g! z2 p1 N8 I2 i) ^
另外,可以通过下列测试程序进行测试:+ G C+ e! v: ^+ \9 _
3 W% v7 Y) `( ?* C, w# l#include <linux/fb.h>& d( l, T# o' ]
#include <unistd.h>
3 A- ` m; @ m/ t0 C, `#include <sys/mman.h>
) Q* i/ s6 V! Q#include <stdio.h>6 W/ o+ r# y' R- e7 ^# A
#include <fcntl.h>/ O8 Q6 u: w" Y2 R
//#include <syswait.h>' T' w. F8 m) V- v! M- ^5 x
% O* s' `( ~2 @7 \* ~% v: A7 w3 Gint main()! N) I( Z" p" z( p
{* p$ K3 d' C5 K% ^
int fd, retval, i;
E+ n2 u* n$ F5 _- i$ D struct fb_var_screeninfo var;# A$ X; C& D8 m4 `0 J; y
struct fb_fix_screeninfo fix;, U& ]% s2 R# C: i& e1 R3 r
unsigned short *memstart;
q. Z W4 H* Z; `8 X
1 ?. E. S( p* l. I/ P& l fd = open("/dev/fb0", O_RDWR);
. c+ j- h8 r" o: l6 ]" h/ a if(fd < 0){
) [5 A! O2 _% K4 m9 d printf("open /dev/fb0 failed\n");; T* I \: z/ Z% P6 V; N
return -1; E# V( g8 N5 a+ A8 U
}/ z# F% |" R: V+ a' i
. B$ R) b9 \/ \+ b& V, @ E. [ retval = ioctl(fd, FBIOGET_FSCREENINFO, &fix);/ |8 Q- J2 ^1 v- g( R( U
if(retval < 0){7 j C6 q: X5 F. P! `% D- I
printf("ioctl failed\n");
) R% x- i& |5 P7 ?& y- ~ O" x return -1;
8 b8 x2 N$ v) u" P1 ] }
4 z- P) X* V) }0 W) k" G4 N0 ?3 u, @: y6 J5 `( _! n: F% u
printf("seme len= %d\n", fix.smem_len);* b; a$ i0 t7 ]4 w& p' W
1 W0 s, o7 [. z1 t( R% m memstart = mmap(NULL, 240*320*2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);* o: {5 N o' p5 C
if (memstart == MAP_FAILED){1 A9 v" i' C5 u: ?) ` t7 @
printf("mmap wrong\n");
/ H5 U; o& A8 S* u7 Z }0 t+ d. B. E5 N
$ h+ ]* l ?5 q2 S2 k* {# `" _6 W0 E
for(; ;){$ Y1 I1 h& i8 H8 U l
sleep(1);. ` m; S0 ?; g: E% U2 o4 k
for(i = 0; i < (153600>>1); i++)
( n; N: c! I1 ]8 a+ \% d, g *(memstart + i) = 0xf800;+ P( d2 a& x, X! G* I! C. _
sleep(1);
; F* T& w6 `+ _$ { for(i = 0; i < (153600>>1); i++)
9 c5 B& ]0 J/ e *(memstart + i) = 0x0c00;
# Y) t9 O8 ~ X2 H# o3 B# A }5 j( [9 {- ^+ p. ?/ J6 ~# I+ x
}
" a: M; s: N) q* z% I8 Z) {. J% { _
该测试程序是网上的,我只是稍微修改了下。
y" _- b9 I+ ~) ^1 s1 w
4 D) Y, V! \! Y) C3 s! x0 E5 H0 G$ }9 }- E- P* {) a
, p: h; F5 E; K, \, `& h3 E; M
|
|