TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
PC主机:Ubuntu 10.4 和redhat 9.0' G& |5 C+ g5 e" _! y
/ | p5 a$ O. B
目标板:TQ2440开发板 Linux内核:2.6.30
2 D* v z* M8 r, _% R; H( [8 G1 O+ o( M3 S5 i. J, g0 v. a
屏幕型号:WXCAT35-TG3#001F 分辨率: 320X240
# s- `: f' F$ e1 u. o4 S- b7 w2 X7 D$ ?$ z/ |: \" r
7 J. x$ J+ {- N. Y/ E6 L
$ g2 @5 v4 ?2 @
本文将介绍如何移植LCD设备。6 y- c$ X* g4 P, ?1 x* i% N" g
# v$ B- G1 ]# r" p) {& ?
在移植前,先配置下内核,将LCD设备编译进内核。; M, M/ s# l4 o
; ~3 Z' R9 S4 [4 ]/ {7 m
/ E4 W M: e9 n+ N+ k9 C" \0 @
. c5 ^7 q- J. h
/ z* b0 t+ S/ s, B% A3 k4 E# t
1.移植- Q; V( J) ~( g9 t' x# j, J9 r2 _$ l8 s
}! H6 {* q- M, O9 V移植LCD设置只须修改位于arch/ARM/mach-s3c2440/mach-smdk2440.c中的两个结构体的数据。
$ r/ t2 t# Q( m3 ^5 x2 f4 m, x! l, w" l+ f, |
1.1 s3c2410fb_display结构6 v; w3 x7 z; Z0 U4 `# ~4 f7 j
$ G" O: Q0 }) V) v
修改后的内容如下:' p W' s, S1 b0 T6 ^- M( J, T
2 j& S+ Q& B3 L; T" X. P8 O) O ~
/* LCD driver info */
, z8 o X c9 D
6 O' d& L, {5 r( fstatic struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
# L, P v) O [% L* B* ]& }4 {3 D, Y6 ^* w% i) b& [1 s
.lcdcon5 = S3C2410_LCDCON5_FRM565 |$ r. q% g, k( }7 G. J! t# l
S3C2410_LCDCON5_INVVLINE |0 \' v4 u% @3 O3 @
S3C2410_LCDCON5_INVVFRAME |
6 N0 }% w% p/ F: a" t. \' h S3C2410_LCDCON5_PWREN |
2 U) ?$ }$ G: p S3C2410_LCDCON5_HWSWP,* L8 K' B `, A8 s& c
! j- C' f) I6 }/ ]' x9 N1 J .type = S3C2410_LCDCON1_TFT,( K6 ?0 l% W" N/ Y* n
, K+ ~! r l; q- g7 I
.width = 320,//240,
& {- ^. z8 F+ N. A' ] .height = 240,//320,) G9 k& _; o$ I/ T! S
# R; ]4 ]* ?; D) W4 S/ I3 \: p# ]
.pixclock = 156250,//166667, /* HCLK 60 MHz, divisor 10 */( C3 S' ~6 ?( G0 }' ^6 c' ]
.xres = 320,//240,
# X" d0 g; p5 ` .yres = 240,//320,
, N! }% J) j8 k+ X; E .bpp = 16,) p9 I+ s" l3 E: V8 S" \
.left_margin = 20,
: v# s" b' e S2 g) a .right_margin = 38,//8,/ a ~* ]& k+ ~; R b' v, B5 D
.hsync_len = 30,//4,) _$ q, ]. C- i! J) ]+ B
.upper_margin = 15,//8,+ r: `, ]/ b# `$ K3 j4 M# s' R' t
.lower_margin = 12,//7,
9 p# q+ H7 k: H7 }% L* }7 t .vsync_len = 3,//4,1 ?; B: P$ c5 D% s2 p* w* ]+ d R4 J
};1 X; W1 W: B7 m; E. }
6 G) K" q6 h! T; N. l; u+ W
2 k" s4 C9 ^6 r上面的参数是如何修改的呢?我们来看下。9 Z# R8 t6 f( p+ K8 m/ h3 o
3 j% c, g; U1 S V" Y9 p& e3 ]
type表示显示模式,这里为TFT模式。3 Z& I. q! _* {( {; h1 P3 g1 X2 L
$ j% @6 T ^6 O, r# X8 ^width和height表示屏幕的分辨率,我的分辨率是320X240。
8 q: D3 q, S e- x
. J! V: Y. E$ b6 k# Sxres和yres分别等于width和height。7 n1 Q& E1 q3 j6 p
: G) {1 o1 B8 r9 Ibpp表示所每个像素点位数,这里使用16位。
- q. y! K9 q+ \9 P
8 u" T( l0 q- Z3 l4 B7 Mleft_margin,right_margin,hsync_len,upper_margin,lower_margin,vsync_len这六个参数的值由LCD的手册给出。下图为LCD中的参数:
% {( k/ ]7 C2 D, K1 S+ s4 c
3 r4 R- m; B; S8 D6 \' |6 @
4 o) C3 V' |6 I/ y" Y
3 V$ l, A) ~& u) X) K
8 l6 B. x% ~$ w( I( P% B, t" _
在这里,我给出上面6个参数和LCD手册中数据的对应关系:7 Q5 s9 C& m' M: S) N+ y( x+ C. `
( E9 @" ]" }6 C
.left_margin = Hsync front porch = 204 s9 _. o0 w5 _
$ r% e+ W0 Q8 I# } .right_margin = Hsync back porch = 38
: X! q+ ~5 {% E
9 k# A* S- ^5 Q. q0 G2 c2 ?0 W .hsync_len = Hsync pulse width = 305 p: X; [) c+ S
7 K2 {% n# `" ^
.upper_margin = Vsync back porch = 15
+ M' ?; s! W2 e7 D) L) O
0 K. `, B/ u9 }3 }2 \" n" ?- B .lower_margin = Vsync front porch = 12
5 S/ [- J' T' ^3 c5 S5 [: w; m+ h; n/ {/ y4 \4 G
.vsync_len = Vsync pulse width = 3
- w% | V/ z! j6 P l
" L- Y) Q+ t: ]
; e( M9 X) i {% V( l6 d4 [pixclock的值是用来计算CLKVAL的。在S3C2440的datasheet中,CLKVAL的计算公式为:& L- j; D* O6 q( {
- X% K( h1 b$ n V0 UCLKVAL = HCLK / VCLK / 2 -1,而VCLK即为上面图中的Dclk,值为6.4MHz。
; X9 O4 A8 X% L1 @% H
9 u& _4 b. c! K2 Z; w接着我们看下驱动程序是如何计算CLKVAL的。
' D7 G0 U7 i9 r+ o6 l$ H$ M8 f0 _& d! _
/* s3c2410fb_activate_var2 `! g( ?+ ~2 u+ \( Y: C0 N
*
+ x: f; a8 Z9 D5 ?2 `/ @. u! _& f * activate (set) the controller from the given framebuffer
! [' c$ S a; L! w4 k$ Q * information
$ K K+ s- K+ S; T2 N */
1 u$ q! f4 F( K. P) `6 h4 ?static void s3c2410fb_activate_var(struct fb_info *info)4 I8 R3 f/ `5 B
{5 C2 F- G5 g; c9 |; J+ `
struct s3c2410fb_info *fbi = info->par;6 }4 R1 T: l( } Z) k6 ^
void __iomem *regs = fbi->io;
% u. ]. S& H/ T' }2 u8 ~ J4 j int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; /*regs.lcdcon1在s3c2410fb_check_var设置*/$ z# D% T% Z. u$ |' `% t
struct fb_var_screeninfo *var = &info->var;
9 g4 b& A1 G$ [+ m7 } int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2;
9 K7 c4 N* [! i, q( Y b$ _7 C3 U, N0 w( \1 a( Y
dprintk("%s: var->xres = %d\n", __func__, var->xres);/ Q8 X' c! K- S( o0 Z( ]
dprintk("%s: var->yres = %d\n", __func__, var->yres);3 l8 N4 C1 Q; ^6 p$ E8 r
dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);
/ Y" N6 @) i5 o* m! ^+ m f3 r; t* k9 |
if (type == S3C2410_LCDCON1_TFT) {$ R" W- ^% ?' ^. k; ?1 H
s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);/*根据var,计算出控制寄存器需要设置的值*/
- k3 b Z e$ ]2 J9 S6 I- Y$ ? --clkdiv;
# r7 H7 ?7 ^- k A if (clkdiv < 0)& K j" @0 L5 x
clkdiv = 0;
$ v* f0 q4 P0 m } else {
/ J; m4 w8 g* T$ u$ N s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
& V: E* I/ }$ s4 h/ w5 x: O* {) d% N, O if (clkdiv < 2)
8 z" H( [# X8 W3 }8 _ clkdiv = 2;
3 |' f d1 @, S- d3 y" W1 y1 P3 I }
4 O$ O3 b1 L- z2 Z% C+ X" y# L& p
fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);/*设置CLKVAL*/% B1 j* N( Q; e5 W6 y4 f/ ~1 v5 Q$ ?
: r* ?% }( p# i- f /* write new registers */2 q7 I& f+ S' t$ l
0 _" J; j" {( p" I
dprintk("new register set:\n");9 j& r8 k: t0 ?8 F O* x2 e
dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
; ^8 ~9 L3 e# ~# S% @ dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
; q, f$ z6 W7 X0 `$ b% E' z3 y dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
' b& z: x: S4 p/ M: } dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);5 M& ]0 w9 V9 v( ~
dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
8 `( ]% W2 F7 o$ ^* r8 Y4 q1 | /*把计算好的值填入LCD控制器中*/% ]0 H) `: ^+ N1 M# x
writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID, # n1 @, X; [+ ^3 v# G& o" Y
regs + S3C2410_LCDCON1); /*仍然禁止LCD*/3 D8 u* u) {7 x G3 E) H8 I
writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);1 k, ~& ]" Y& @
writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
, ~4 B+ G& P7 U, U writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);2 s& C; U; ?- T c+ J5 r6 q; D3 Z- x
writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);" E/ Z& L- K. D: u
. g5 f$ m+ F( }$ t; h
/* set lcd address pointers */
+ x9 Y w- G- ~2 t s3c2410fb_set_lcdaddr(info); /*设置LCD帧缓冲起始地址*/) @* Z$ R+ t. f* m
G- `+ n& H4 K+ {- h f" L fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
* z; b) C4 L/ e3 J) W writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); /*使能LCD*/' x" Z- N' I2 j1 A) c2 \6 D- \5 e
}
$ E3 k5 s, ^! s- f! Jstatic unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,8 }! o. E4 U0 c2 O9 ]7 R, j" v
unsigned long pixclk)
# ], h/ V% e% E8 a, U{ G* X8 d" T# H7 i6 _
unsigned long clk = clk_get_rate(fbi->clk); /*获取当前时钟频率(Hz)*/
9 E7 d& R3 n: F9 }+ k* j5 P1 l unsigned long long div;
8 m) u5 G7 ^/ q8 h# G
7 n6 h5 n( h& H$ s /* pixclk is in picoseconds, our clock is in Hz; B' C4 W5 v0 n: z
*. U: @, q0 o5 f8 e) I0 t
* Hz -> picoseconds is / 10^-12
( W# ?2 P9 b- E- O0 Y, `/ L */0 h& O5 f, T- L) }% s. t
5 Y6 W4 X9 ^! R8 o9 f div = (unsigned long long)clk * pixclk;
# t" k6 I# m. U* N r div >>= 12; /* div / 2^12 *// ~( x( ]' i, t. O+ A6 u# j# S- _6 V
do_div(div, 625 * 625UL * 625); /* div / 5^12 */
* h o( \. S1 c4 i
& P' W2 G) I4 U q3 d1 E dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);1 c4 f; P' q4 p/ V
return div;8 P, U9 p& |4 q' J! H0 p/ E
}
+ E+ Y5 ?. p8 `1 {
( p# F9 s* R% V2 U- a- v: Y
5 S2 E0 A+ j* P- g! G" F$ |首先pixclock作为参数传递给了s3c2410fb_calc_pixclk函数,当该函数执行完以后clkdiv = clk x pixclk / 10^12 / 2。" k0 d8 j( W& ?& T9 W% ?' R( C
. I# u5 t ~; b; w随后由于是采用TFT模式,将clkdiv-1。最后得:& O( y3 _- c7 R" L' e/ R
! F0 A. L4 ?" H& y3 i9 }clkdiv = clk X pixclk / 10^12 / 2 - 1,这里的clk即为HCLK,LCD模块使用HCLK作为时钟源。9 P; o) s3 {3 r, |
# Q6 ^- ]; p- i: D) \" V) w1 \为方便观察,将前面datasheet中的计算公式复制在此:CLKVAL = HCLK / VCLK / 2 -1。$ g0 X& O. }7 A7 L K7 H% W
' m( j0 f0 K, M: a Z8 W# d我们可以看出1/VCLK = pixclk / 10^12,也就是说pixclk = 10^12 / VCLK。 我们已经知道VCLK=Dclk =6.4MHz,
9 D# r l8 e8 I. z
* v$ ~8 U7 W8 k; }3 r因此,pixclk=156250。
! {4 }( x+ j3 B% C0 O5 {7 M$ C1 F7 R& x
其实在内核的参考文档中有这样一段话:
" x/ s5 i- C7 M; Y, Y$ P' X, p" w* y
The speed at which the electron beam paints the pixels is determined by the
5 P: U$ {/ n. udotclock in the graphics board. For a dotclock of e.g. 28.37516 MHz (millions/ ]4 ?+ ]; T9 q( X: C
of cycles per second), each pixel is 35242 ps (picoseconds) long:
9 ]: {! G {$ x( t5 n
2 M) m: c3 G! {; O1 j! P5 I3 S 1/(28.37516E6 Hz) = 35.242E-9 s
3 ]& `# o$ r# F9 Y$ L( i% t) u
; V% T% T4 m8 v& ]" t3 C$ `( }6 _: }也就是说VCLK的倒数,再乘10^12即为pixclk。picoseconds单位表示微微秒,即10^12。
5 n" u5 a3 R1 C: L: ~8 e3 a; @2 ]' Q" Q. D; G
最后一个参数lcdcon5,它是寄存器LCDCON5的配置信息。
/ P1 A/ O o& L7 V
$ L, s) P7 S" t \5 k, h5 TS3C2410_LCDCON5_FRM565表示使用565格式。
) k- @% F. g0 V) G& p
# J x' C: E+ w2 j4 S- z( {1 AS3C2410_LCDCON5_PWREN表示PWREN管脚输出信号使能。
! p+ w6 n% t$ J; X& Y
$ A3 x7 Y8 n; q& j5 KS3C2410_LCDCON5_INVVLINE 和S3C2410_LCDCON5_INVVFRAME 表示反转VLINE/HSYNC和VFRAME/VSYNC两个信号线,这个可以通过对比LCD手册和S3C2440的datasheet来得出。2 v- w' V9 w& d6 E/ ?( p' r( f
& k, w1 q6 o {
S3C2410_LCDCON5_HWSWP:由于使用小端模式,需设置半字交换。+ K: a8 i) f* b7 j7 J. r
% A6 S) o1 F u" m
' B* n/ b) i. a c& Y- M! x
1.2 s3c2410fb_mach_info结构; Q) ^, `: I) O9 @' c: p/ x
" Q( l. ~* L3 S3 K2 U修改后的内容如下:
+ Y- U( A- `- h" M
5 r2 x* S* Y# w- t- P9 p* Hstatic struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {* N0 K% k B* [9 W. M& |8 J
.displays = &smdk2440_lcd_cfg,6 m8 ?1 k8 h Q: H
.num_displays = 1," ]7 Y6 z6 d# X" ~5 W
.default_display = 0,
( E. F: g+ j: [7 m& ?, B% O4 X/ c" X* w
#if 0 b9 v# a w1 o( d' {$ p
/* currently setup by downloader */3 T* B- I% U5 }2 {2 w' Z
.gpccon = 0xaa940659,! M' y/ f5 x6 `3 R( e
.gpccon_mask = 0xffffffff,' p: l# p9 S4 m) U: N2 Q3 U ~& o
.gpcup = 0x0000ffff,
* t$ y' h1 W! R# J, K. d; D .gpcup_mask = 0xffffffff,0 U! i- v, T- k% A, k
.gpdcon = 0xaa84aaa0,& d6 R; u' d) ^' V: |9 r* T: ]
.gpdcon_mask = 0xffffffff,# J) @+ H' G+ r) j+ }* L" C( l/ o
.gpdup = 0x0000faff," j" `% I& d6 Z7 T" A( k l8 [
.gpdup_mask = 0xffffffff,
; V# T Q% X- R7 m. F4 U4 s#endif
) u: U4 i- |' i0 P/ o' e( l//no% U, ]& L6 h$ Q4 C$ F( }; K% ]
// .lpcsel = ((0xCE6) & ~7) | 1<<4,) @$ ]$ P5 j. j0 b1 j% N4 J; N
};0 @/ e. h- F: [9 K% I' X5 Y3 O
' @. ]' E9 S, n( M2 u# h6 K( m9 [
做出的唯一修改就是:禁用lpsel,因为如果不是使用三星LPC3600/LCC3600 LCD,必须禁止LPC3600/LCC3600模式(写入0到TCONSEL)。7 b6 }3 o8 y; L/ ~& E6 q, R+ J
num_displays 表示有几个LCD设备。
) E7 M2 L. z& k7 w6 D2 Z/ H7 I/ T2 Q3 X1 r5 }% m
default_display表示这是第几个LCD设备。
+ \( \: [/ I( k! b+ ^8 B- H/ G3 e7 y3 {3 Q1 b% R! W& d
0 p* B/ f& t M9 b: J# m
: e2 P8 | F; F0 C2. 测试 a; p( u! E! I( S- ~5 v2 M2 l+ z
; o' Z# ~8 w* C3 k在修改过上述两个数据结构之后,直接编译内核,然后下载到开发板上。! ^$ U# K( T/ U3 |$ b
& Z) o0 d2 P" y; D" T. ^
可以执行命令fbset,输出如下:* d$ \ |5 l" g5 k6 h( n
8 |6 P% ^+ y3 d* u' ]
mode "320x240-58"
% f6 b5 a1 @' t) I, ~7 K& I& O4 x# e5 z8 @- z% t+ [4 i6 m* O
# D: 6.400 MHz, H: 15.686 kHz, V: 58.097 Hz
. b B9 K7 Z& i/ y6 Y geometry 320 240 320 240 166 L. T h2 P# Y% S: J
timings 156250 20 38 15 12 30 3
# x; o( L8 `( ` i6 b1 g8 f accel false
3 |4 `( R. P ^1 k rgba 5/11,6/5,5/0,0/0
6 l( n$ t/ a' E( rendmode
" J4 n9 t8 A9 H1 w3 _$ K
9 K6 \& y7 Q) B% [1 w上述中的数值都是我们设置的参数。 L5 Y0 m; O7 W- ~
7 I5 P4 I* T$ y; O, L! F另外,可以通过下列测试程序进行测试:' T7 F) [; K7 ^
7 W' z8 _- F# ?; [% e#include <linux/fb.h>
: _/ b" z6 A9 e3 o9 l/ \3 T. R#include <unistd.h>
/ q9 X* k' r- n. @# l#include <sys/mman.h>
" |- `8 [& k+ x! i3 c#include <stdio.h>
8 T& u2 [. H( O; u e' c" k#include <fcntl.h>
3 j! m9 Z. M* N+ [//#include <syswait.h>
% h: A& W9 u/ B5 x2 k! v5 \3 r3 D W! \/ F0 ]# K% x, B( N
int main()
/ U* P# a- S% P{6 ]$ B5 n4 m! r
int fd, retval, i;( n( }" ]. H4 T" w! ]! L9 a. ?
struct fb_var_screeninfo var;
' u' y, A# R! |) c" G) q9 ?6 N# y struct fb_fix_screeninfo fix;
: X( W. H5 V; H- E unsigned short *memstart;2 k# ]! _7 _: A7 u
; b* j8 H6 [) R" B6 H$ F' e/ {% g fd = open("/dev/fb0", O_RDWR);* u) n3 l+ Z4 C0 c; ?
if(fd < 0){5 ]" e& y6 }4 S
printf("open /dev/fb0 failed\n");/ ^+ V" H+ J/ a5 F1 l
return -1;
7 I4 Z: _+ O# `* e' B. V }
$ e) j9 h0 f! E, U- k4 W, b
' J& k4 t1 j% \8 w retval = ioctl(fd, FBIOGET_FSCREENINFO, &fix);" e2 l$ D. i( o; s7 V
if(retval < 0){
7 J$ q/ b+ K. f2 B3 c; l1 t printf("ioctl failed\n");
6 y* T ?# @: F return -1;: M- _$ V7 j' u% q. N
}
: R0 L. V% ]) R. [+ {' R: K9 N* l! A& k
printf("seme len= %d\n", fix.smem_len);
0 O F, W9 F8 {- J8 G2 P; h& {7 M' X/ D A& u
memstart = mmap(NULL, 240*320*2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
6 x( S0 r! T& Y& M% r6 p4 T/ Q7 B# W if (memstart == MAP_FAILED){' \& J! E/ ?! p* d3 ?
printf("mmap wrong\n");. v8 w1 b( w# |
}
% G- C6 O+ L$ d& }# ` ' p) L9 D4 d, A" w6 d4 s w
for(; ;){
8 S2 C! O$ @/ A3 i4 J- Q3 B/ L sleep(1);
7 E% \. I% E, w0 G% q! x7 A* G for(i = 0; i < (153600>>1); i++)
+ d/ c$ D' p* z *(memstart + i) = 0xf800;
7 _2 X+ E! b9 G: B% R1 Z# p sleep(1);
" Y& W% n, N, f o0 T for(i = 0; i < (153600>>1); i++)0 g" b& n& M4 I/ I' C/ Q! W* Y
*(memstart + i) = 0x0c00;
; S8 _4 C; ], N0 j- S y4 C8 s: E }
( |4 U9 Y& o5 ^}; p) u0 d9 Q, z) Q
& W0 W) P# C a1 Z4 o O/ j; f该测试程序是网上的,我只是稍微修改了下。
7 F* S% ?6 {. s* }8 d$ E$ d9 D$ _9 s3 W+ }6 N
. j4 v: J* w. h: n+ m
9 h* B) \5 p6 z( @, P% E" k5 N V |
|