TA的每日心情 | 怒 2019-11-20 15:22 |
---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
PC主机:Ubuntu 10.4 和redhat 9.0" W" j9 J) h9 p: h$ n9 Y0 \1 d: `
3 y- O. {) [3 J* S" d# @: P/ G目标板:TQ2440开发板 Linux内核:2.6.30
! g* Q" [6 \. X5 D. ?/ i
; u( O; O3 V# X屏幕型号:WXCAT35-TG3#001F 分辨率: 320X240
1 o& w2 q" H+ z, D0 Z) Z* Z; v; |& o' }4 I) l/ N. S b; E
. Y( U8 ?) w3 |$ |# w2 W, X) g# h$ L
5 G4 T' T# S. \6 R- v( |3 X$ v本文将介绍如何移植LCD设备。- n# e$ g6 T- T2 t$ q; P y
( W( M' j: ?+ P3 N; h3 ^
在移植前,先配置下内核,将LCD设备编译进内核。9 ?% p) @, w" C, S) o- i, y
/ P, |; n4 |( @6 ^( w
7 b6 Y+ D: n- m/ ?( T
3 }/ ~7 _( }6 M
8 T( |7 W* v$ v6 |8 l" A1.移植) L& ^* n: K* ], X# V+ c1 \
' T. F0 ]3 c) n6 n! n
移植LCD设置只须修改位于arch/ARM/mach-s3c2440/mach-smdk2440.c中的两个结构体的数据。
2 ^6 k9 Q! l- g4 r: v; |. C4 ?, L6 S& `
1.1 s3c2410fb_display结构
' n! G6 |7 o! e8 W& y% h. m: R: o9 G- R! D
修改后的内容如下:* S' n! J' G) D5 i3 Y/ x
3 ^: T6 n k( G/* LCD driver info */
9 ^1 P1 [2 y! b
% H" L, p+ U, p' t X% W4 a% xstatic struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {3 j' a" Y6 c% }3 i& l, V" e
8 b7 o* T9 s M, B. W. o7 W5 h+ l( K5 ~
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
$ h/ {8 r* U2 V! x& V+ H S3C2410_LCDCON5_INVVLINE |
2 B2 J8 ]% l4 ~/ a3 L+ I5 p3 M S3C2410_LCDCON5_INVVFRAME |5 q6 |- Y# r2 C; o, q
S3C2410_LCDCON5_PWREN |* m+ X* X4 D8 K! x! k% S9 H" k
S3C2410_LCDCON5_HWSWP,
. L2 F. Q( C* _4 v# z/ x6 F4 U2 K1 ?
.type = S3C2410_LCDCON1_TFT,# Y* e0 x' O e* f1 w) _, B. s
: f3 {2 n6 M9 o% e x# e
.width = 320,//240,
, C# \" L- A) W+ R, F .height = 240,//320,, [0 x2 y( p% r" E) p7 r: u! T
5 c' K" G5 q8 z' |, ^- Z
.pixclock = 156250,//166667, /* HCLK 60 MHz, divisor 10 */+ _5 n8 {) s, O* z: N Z) z0 a+ p8 h
.xres = 320,//240,9 X. U6 N, N$ v1 `: P
.yres = 240,//320,5 X* j ` b! ~& w' Z9 R% v/ g A% g3 L
.bpp = 16,- r4 A' z" Q( O6 `. S4 ?9 \
.left_margin = 20,
8 N2 \" B( A6 z$ `8 H .right_margin = 38,//8,
& k5 R1 H% e- e) w: @, d& t, T( o2 ?1 g .hsync_len = 30,//4,
5 G3 _2 Z" K( `# |) Z% H: W .upper_margin = 15,//8,
( ]' p t c, K .lower_margin = 12,//7,$ |0 Y1 o2 `0 i( D( h6 ?+ D5 U1 J
.vsync_len = 3,//4,
' D2 U3 h6 h" y# q7 i1 a% M. \};
/ A6 D- R, ~$ N* K# k# L7 L3 I! d9 h( i- u) o1 ~% K
& e# K) A* s- w( G- _$ g
上面的参数是如何修改的呢?我们来看下。& n7 ]1 v W# `' I
: e. e# W" K( R, L% m0 a
type表示显示模式,这里为TFT模式。3 z/ _0 h. v% H$ R$ ^' b) U; C
- A* ~+ H/ Y4 r) I0 a: Z
width和height表示屏幕的分辨率,我的分辨率是320X240。
' D. c4 h: I% T E
4 q7 \' L8 F* a zxres和yres分别等于width和height。5 j3 ]+ c8 }' t( M& A. R: B
" S+ ]4 c4 j/ d( Bbpp表示所每个像素点位数,这里使用16位。) L& b' l/ q/ C$ R* y8 s
' n" j& }. L8 ^7 m/ r. Hleft_margin,right_margin,hsync_len,upper_margin,lower_margin,vsync_len这六个参数的值由LCD的手册给出。下图为LCD中的参数:4 x& E* G3 @2 V
o7 }! m, C# T
* S- t! K5 \' p8 T/ Q0 m; _7 O6 Q( q4 p: L \4 N, ]. a7 g
% ^9 B& ~0 V3 R0 m9 \
在这里,我给出上面6个参数和LCD手册中数据的对应关系:
; V8 i1 m& K1 I6 j, ?# C! A$ F8 C0 v+ i
.left_margin = Hsync front porch = 20
! E; C) `0 x% A" v& F! i' i2 B4 l' @2 n+ u
.right_margin = Hsync back porch = 38: K+ ?8 W1 z3 R2 q
0 g p$ ~& g) A+ [ V
.hsync_len = Hsync pulse width = 30
% F* H: m+ A# h7 d+ E* ]2 a1 J8 \+ B7 C
.upper_margin = Vsync back porch = 156 u% U" I( y2 l3 D ^4 r0 s7 T
$ s9 i" p% s) k0 t+ i" g1 M% j8 K .lower_margin = Vsync front porch = 12
( A* G$ q' }/ `2 a
+ R4 `0 `! N9 F6 ^8 Q- s .vsync_len = Vsync pulse width = 3
2 L) ^" f% ]& B X4 l8 Q' {# `" W, Z( g$ L) c: j
3 V' l' Z1 u7 F% U4 F# Z
pixclock的值是用来计算CLKVAL的。在S3C2440的datasheet中,CLKVAL的计算公式为:6 B/ ~' `" G: X. Y/ Y5 J$ l6 ^( z
% Z [0 @) f) q" l3 g) JCLKVAL = HCLK / VCLK / 2 -1,而VCLK即为上面图中的Dclk,值为6.4MHz。
9 W' A" j$ w$ \$ S E: \5 a, y1 y3 W* C) @& p* t
接着我们看下驱动程序是如何计算CLKVAL的。
$ |, x. ^3 V/ }1 K2 _$ q; x9 s$ G8 E- A& v
/* s3c2410fb_activate_var
: F+ ~4 s7 s- ^9 ] *' {/ n2 g" z% x3 ]$ {
* activate (set) the controller from the given framebuffer
+ r# ^# K. [8 z7 z. ^ * information
, O: W7 d1 i; j6 F& G. j */
! L& c$ R; }6 g$ [* ?# ?7 ]6 u+ Jstatic void s3c2410fb_activate_var(struct fb_info *info)
! D q; N& [( e9 F# I( m( _& U{( c5 b# c) r) U4 @8 k1 d" K
struct s3c2410fb_info *fbi = info->par;% J- Q" v+ l8 f5 Z k, K1 O
void __iomem *regs = fbi->io;+ ]- m3 |* k2 y# s( l4 W* m
int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/3 o! G1 S9 Y" @+ L- F
struct fb_var_screeninfo *var = &info->var;
. h& E# I4 P( @0 `9 ?1 q int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
( D( q, z: G% m# o! N7 W& K4 J' e8 r6 J
dprintk("%s: var->xres = %d\n", __func__, var->xres);
% @, S K2 B( q5 ?! E: R1 } dprintk("%s: var->yres = %d\n", __func__, var->yres);% m/ \1 ]6 H1 d$ p$ }
dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);
" y" Z6 L; o- C
8 t8 h0 p7 Y5 j& \ if (type == S3C2410_LCDCON1_TFT) {" j$ `0 N' {( }. N4 z# J
s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/, m7 U3 R: h/ o$ [8 e7 Y- [
--clkdiv;
+ w& K7 ]! v+ Y8 W, z3 a if (clkdiv < 0)
# o: C! l0 i& I* c* [ clkdiv = 0;
7 [9 G/ C! I# k) m8 z# {+ P+ D } else {7 K# {0 `/ H& H( X7 c. W+ g2 c
s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);# O j1 u/ o3 q3 e
if (clkdiv < 2)
2 x- ^" t% n' Z clkdiv = 2;
. f( ]4 L, U& Z8 _2 g! ^6 Y }! _# {1 U. |, U# z! o. K) ~
, Z |6 N0 i- S; i6 P2 F" g
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/
7 R# w: r% M* ?7 m/ R8 w ^" H8 w& p9 t& F( K3 [
/* write new registers */3 W" @5 ]) k/ G7 R
2 ~6 ~ h7 R3 |# a( }. Q dprintk("new register set:\n");
' A1 ^4 {3 P' E dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);: ?5 n. e9 P) w( G
dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
, H) s& D; p5 W2 v dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
, }- o e { D; H' E dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
& F; I% m U: \9 F; ~/ } D1 j- s2 V dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);0 W5 k0 O6 X/ ^9 ^7 F- ]# w
/*把计算好的值填入LCD控制器中*/
y" A% u1 X: B7 A" s- U writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID, , f- O' |0 o C/ A! V% d; D
regs + S3C2410_LCDCON1); /*仍然禁止LCD*/- C: N' I2 J6 A
writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
, c: Z# x) |/ v) x, X2 Z+ s writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
1 ]9 @4 A! u8 M writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
+ E- N/ W; ^& b; r writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
M6 S# Y% n3 h& @9 T4 [8 Q6 U4 L1 Q
/* set lcd address pointers */0 m. \3 E3 R2 F; b; A1 U' A
s3c2410fb_set_lcdaddr(info); /*设置LCD帧缓冲起始地址*/: R9 J5 } H; y5 _8 d
" m/ J+ `& u6 o7 O* _& m5 }2 _
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
4 ~! c& D& v5 G& z writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); /*使能LCD*/: h! W7 C5 x3 k; M1 W; a2 N
}' r7 `! F' ?% y6 ~# L
static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
$ k* q- p: z `. S: s/ B/ x unsigned long pixclk)0 k- _5 U8 f/ ?/ \& d: p* |3 a
{9 G; n( d$ I- L! U2 p- k( I8 c
unsigned long clk = clk_get_rate(fbi->clk); /*获取当前时钟频率(Hz)*/
, @+ x+ A, K4 {8 g) N" v0 g unsigned long long div;" _# Y& D* I( f1 L: v# K
! Q. e8 }1 i1 ^% x; t
/* pixclk is in picoseconds, our clock is in Hz
- k) v9 D8 D9 t+ A/ Y7 P2 C *
V7 X1 X' H, A* ~; I; E1 S * Hz -> picoseconds is / 10^-12
) R$ e' o# M! _- \' _5 \" s */" G& K( o$ g$ y; [+ C
+ B5 g. N2 n2 U% u' M: ^; x' e
div = (unsigned long long)clk * pixclk;6 C/ p: @' @6 \6 c( s& R' E3 m
div >>= 12; /* div / 2^12 */
) Z$ L1 z8 w; u do_div(div, 625 * 625UL * 625); /* div / 5^12 */+ P) X3 v) f: {- l' D+ e7 v0 Z3 F
7 [, w# g9 z# d- K0 Q dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);* P& L) N$ U* G5 h0 S
return div;* q8 q1 Z P; D2 H x3 a; N% z; r# x4 j
}( J8 X/ a# v8 Y" o/ E
9 q+ @* m2 E ^. v8 T. }
4 I! \3 g6 R( d. @/ f; A
首先pixclock作为参数传递给了s3c2410fb_calc_pixclk函数,当该函数执行完以后clkdiv = clk x pixclk / 10^12 / 2。! W1 q- S9 n* T1 a3 O
4 Y- L0 z4 A* p+ Z随后由于是采用TFT模式,将clkdiv-1。最后得:) ^- U* F3 V1 N1 z) d
4 B& ~5 B J) H7 J
clkdiv = clk X pixclk / 10^12 / 2 - 1,这里的clk即为HCLK,LCD模块使用HCLK作为时钟源。
3 i5 ?3 g. @( P% A: V
! g/ o1 f! {. o5 I9 u9 L# N; D1 C为方便观察,将前面datasheet中的计算公式复制在此:CLKVAL = HCLK / VCLK / 2 -1。( _$ [2 J4 v" e" B: S( _' \1 q
, y# X* p) Q- l' S0 N7 J) T9 ?
我们可以看出1/VCLK = pixclk / 10^12,也就是说pixclk = 10^12 / VCLK。 我们已经知道VCLK=Dclk =6.4MHz,/ X" g4 y; l. y" y4 H( J8 H/ r2 r
! l7 o/ W( u- [3 j# n' L
因此,pixclk=156250。" Y2 P" s7 H4 B! ~5 {* o) i, I
& |& W8 _* C: M3 t& G, {
其实在内核的参考文档中有这样一段话:. H, i! B; z" l `# P( t
. A ~! y. s8 w5 ?4 e4 q3 @2 E
The speed at which the electron beam paints the pixels is determined by the
2 c, M- G2 q4 L6 kdotclock in the graphics board. For a dotclock of e.g. 28.37516 MHz (millions
6 Z8 F" {( S( N; p zof cycles per second), each pixel is 35242 ps (picoseconds) long:
/ @8 q- L; `; `, ~" S9 e5 j" b/ n% {% s% @5 X) h
1/(28.37516E6 Hz) = 35.242E-9 s
* z# R) i' z) g( X. d
' a5 B6 T6 v- H% S" z. a! l也就是说VCLK的倒数,再乘10^12即为pixclk。picoseconds单位表示微微秒,即10^12。1 J# r/ D1 P! N4 c+ x
, s; A0 L# x/ X, ~最后一个参数lcdcon5,它是寄存器LCDCON5的配置信息。
- A* p* Z! ]& H T( w- m D( j$ R% i, \- X- r9 m! L B
S3C2410_LCDCON5_FRM565表示使用565格式。# h% I' ], B0 x' K
1 {' V: V" V1 i. r$ X
S3C2410_LCDCON5_PWREN表示PWREN管脚输出信号使能。% ?5 s* e& }# A h. n# g3 r l* o# e
' z( b3 k1 n& A8 W8 Z
S3C2410_LCDCON5_INVVLINE 和S3C2410_LCDCON5_INVVFRAME 表示反转VLINE/HSYNC和VFRAME/VSYNC两个信号线,这个可以通过对比LCD手册和S3C2440的datasheet来得出。
* X2 s( ]9 B4 B+ g3 x& g5 O! C6 p% n; J" {$ @% X
S3C2410_LCDCON5_HWSWP:由于使用小端模式,需设置半字交换。) ^/ D) Z( V+ Y5 z/ m& w: P4 R
! @! |( {* C; ^ [" t' x
" t) D: _' B! G, Q D1.2 s3c2410fb_mach_info结构' [/ }! S* ^" a' D* M0 s; q. J
# \5 f7 J' A0 k5 o0 L- M
修改后的内容如下:
& W+ m! ~, Y: p7 X! [ z, j n! A; `
static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
2 ?: G9 h/ s1 \: c1 t D .displays = &smdk2440_lcd_cfg,
# L6 Q% D! c7 _9 d; S$ i .num_displays = 1,
: ? P( N7 v% |8 j' o+ k0 Q .default_display = 0,
7 n$ }6 l' P: b+ M6 V7 M4 n
8 _) h) t; T- J#if 0- M3 W: N& x. d/ o; X. H- x
/* currently setup by downloader */
9 S7 A; ~- ^- {$ c/ f+ I* Z .gpccon = 0xaa940659,
: f, l1 T$ P; w( Z" _ .gpccon_mask = 0xffffffff,3 U- z1 {2 I F; @0 I, c
.gpcup = 0x0000ffff,: s1 d X3 a' n9 h' i: C
.gpcup_mask = 0xffffffff,
: c3 q/ J) X+ [! _2 {/ u$ K .gpdcon = 0xaa84aaa0,; h; E4 ^6 @! C" a* \
.gpdcon_mask = 0xffffffff,
4 o7 ~1 Q5 Z3 y4 Z2 ~ .gpdup = 0x0000faff,
% B; @4 k+ I8 l' j0 r1 q .gpdup_mask = 0xffffffff,4 y: M, i* P* {! o; x% o( t
#endif
! i: G; e9 y$ D/ e2 c( L1 l//no9 A- Z5 m& b+ V5 F. g# |5 {
// .lpcsel = ((0xCE6) & ~7) | 1<<4,
& Z6 R* Q4 Q( A3 x' _! k3 z- V};, r b4 T- |: y! e2 f; f) v
7 Y" M) v) P- F* S+ I7 y8 y做出的唯一修改就是:禁用lpsel,因为如果不是使用三星LPC3600/LCC3600 LCD,必须禁止LPC3600/LCC3600模式(写入0到TCONSEL)。5 [$ Y( _2 y5 S9 m$ Z$ k
num_displays 表示有几个LCD设备。 J. t# q% v' o; @0 o- k" Q
- m7 g" o+ b. w+ |# F
default_display表示这是第几个LCD设备。
% a; t! c5 _. U( [! r7 K; `7 @/ e6 T. g9 W' ]2 T
; ` K2 ]/ I/ j6 k" L. h: N6 l ?3 D; N" _- P7 d
2. 测试/ w2 s6 l, ^* z7 S, X) `" [
) X4 d O) B- d; x在修改过上述两个数据结构之后,直接编译内核,然后下载到开发板上。
8 J+ l( K$ W" z# Z% J$ L* `$ ~
2 J% {/ U6 V+ q. T1 U" h8 }3 ]可以执行命令fbset,输出如下:
0 Q! e3 H, T5 s4 y4 R; g" }1 X$ Q7 h! l8 g
mode "320x240-58"
+ s0 Q$ h* o! k2 G$ e7 i1 o8 C
+ ]- c; ^: Z* p; c7 D" g' I7 M& W( f # D: 6.400 MHz, H: 15.686 kHz, V: 58.097 Hz0 t2 c* e/ d1 n4 V3 l' h
geometry 320 240 320 240 16
3 U& @3 l- y& w" l& N6 d" n6 G; M; u M timings 156250 20 38 15 12 30 3# c' F* D& |& ~4 u! l2 {
accel false* N" I. Q8 \/ S: ?0 B
rgba 5/11,6/5,5/0,0/0* T$ A3 A8 S1 F7 e3 T
endmode! N6 r) @4 n* J! p: t
5 T* G* y. n* F4 |7 k" N' K: r上述中的数值都是我们设置的参数。1 r2 }( u. V) G' p- Q
0 w6 k& D3 z" a# }& F+ }0 ~
另外,可以通过下列测试程序进行测试:
\: Z3 v: b' A d' \7 s
7 X3 D, W$ X; O+ C" v#include <linux/fb.h>9 T8 `+ G- o: ^# v( L7 _% Z9 K; a
#include <unistd.h>$ t# y" E5 o# M" J
#include <sys/mman.h>
# F d( C! h8 g2 W8 n* n+ P; I! u#include <stdio.h>0 e: o7 m- a' q5 d! ]! I2 N+ D
#include <fcntl.h>
# f% i8 O: @0 O% B/ y//#include <syswait.h>
- s3 V9 [0 N" x- H1 l5 l6 y! q. A5 q9 h" F1 u a
int main()+ g. B) K( f6 y% F% z, F9 z
{
: {* d' y9 s) T* M& S int fd, retval, i;, }; ~ _3 R. J3 U: ?
struct fb_var_screeninfo var;
0 U" X7 p$ }6 ?& z O! a( O struct fb_fix_screeninfo fix;
6 ^. I0 w# w, ?, Q5 T unsigned short *memstart;% E3 ~8 Q" S* D a7 r
1 r) [: J3 [# @
fd = open("/dev/fb0", O_RDWR);
% D N$ s; F/ X: v* z if(fd < 0){: K i/ _3 B4 S4 J: f3 j
printf("open /dev/fb0 failed\n");
1 M5 G) S. H8 l return -1;. H0 y7 u' ?4 A3 Q+ M: d- L
}
t3 W+ [" }! B+ H
: x+ o, o# P! A* T+ y% ^ retval = ioctl(fd, FBIOGET_FSCREENINFO, &fix);$ |9 q6 D6 e5 ^* | J0 G
if(retval < 0){+ K) k" S% a( l6 F8 K: W
printf("ioctl failed\n");" D u/ x2 b4 t6 a3 L" O2 v& t5 d# U
return -1;/ P& @% v8 f! L: o* q, w1 K
}; U( t+ v1 T" X9 E
' B+ _& U1 `1 {3 u& _% Y
printf("seme len= %d\n", fix.smem_len);: t- e$ _0 @5 S# K% R1 V9 X2 b
# Z. N f. s1 B8 @4 S, M, z* o2 h. F
memstart = mmap(NULL, 240*320*2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);* W$ _4 X) O9 m; `& C& x
if (memstart == MAP_FAILED){7 Q, V. \ i5 R4 z# M' _
printf("mmap wrong\n");# Z" \3 c5 G& b% K3 T6 Y6 ~
}: v6 E; M8 f$ d
, `. h2 _$ q. X. c! p2 M for(; ;){7 C0 U4 r7 l/ t v2 Y
sleep(1);* t+ \" F" g# X" L9 k9 H9 p
for(i = 0; i < (153600>>1); i++)
/ f; }; M0 f9 d+ R *(memstart + i) = 0xf800;
( z4 [3 Q$ x4 U* @$ m sleep(1);# s1 l! ^6 M4 w& M, u
for(i = 0; i < (153600>>1); i++)
. K2 L& \# |! q- T$ A *(memstart + i) = 0x0c00;
/ }" l2 n/ H; d7 q }
( V, D2 s7 ~2 y7 x+ @}
9 N/ l( A* V% z5 U5 f- V3 X+ H4 ]# N( X, E. `( {
该测试程序是网上的,我只是稍微修改了下。
$ @: K% B% V" @. `! `& E! E* o1 ^- }; I7 ^* m
, N, r$ g/ _ e5 _" V; ~3 X2 M
1 Y4 R h( b$ o3 ]
|
|