内核:2.6.22.6
LCD:SUMSANG 的 LTV350QV_FOE 3.5 寸 240x320
一点 LCD 的知识:
LCDTFT LCD 电信号部件组成:主要由背光电路和显示电路组成。
背光电路: 3.5 寸 TFT LCD 背光,大都采用白光 LED 作为背光源,一般由 6 个串连的白光 LED 组成(如下
图),驱动电压大概 20V 左右,20mA 电流左右,是一个耗电量很大的 部件。对于电池供电系统,大都采用升
压型 DC/DC 进行驱动,很多厂家都有推出专门针对串连白光 LED 的驱动器。
显示电路:显示电路一般由 Timing Controller、Source Driver、Gate Driver 组成。有的 IC 把 Timing
Controller 和 Source Driver 集成在一起了,也有的 IC 把三个部分都集成了。这三部分电路一般都集成在
TFT LCD 模组里面了,也有的 TFT LCD 把 Timing Controller IC 放到外面了(如 SHARP 的一些 LCD)。
SAMSUNG LTV350QV 的 DRIVER IC 是 S6F2002,
S6F2002 集成了 Timing Controller、Source Driver、Gate Driver 部分和电源管理部分,164RGB X
240 驱动能力,所以对于 320 X 240 QVGA 的分辨率,需要两片 S6F2002。两片 S6F2002,一片作为主控
制器,一片作为从控制器,正是由于 LVT350QV 是有两片 DRIVER IC 驱动的缘故,如果上电时序配合不好,很
容易出现显示异常(一半显示不正常,一半显示正常)。
更多的内容见
这个 LTV350QV 比较特殊的是,它的初始化需要通过 spi 总线写 S6F2002 内部的寄存器,有人说,
LTV350QV 比较麻烦的一点是还必须要进行 SPI 设置,但这也是它比较灵活的一面。
更多 spi 设置信息。
通常的做法是用 gpio 口模拟 spi 总线。
spi 总线有三根线,分别是 CS,SCL,SDI。
至于它们分别接的是 gpio 的哪个口,这得看具体硬件连接,例如在我的板子上,GPCDAT8 GPCDAT9
GPCDAT10 分别连的是 CS,SCL,SDI,后面移植的时候会用到。
移植过程:
在 arch/arm/mach-s3c2410/mach-smdk2410.c 合适地方加上
static struct s3c2410fb_mach_info MY2410_lcd_cfg __initdata = {
.type = S3C2410_LCDCON1_TFT,
.regs ={
.lcdcon1 = S3C2410_LCDCON1_TFT16BPP |
S3C2410_LCDCON1_TFT |
S3C2410_LCDCON1_CLKVAL(0x04),
.lcdcon2 = S3C2410_LCDCON2_VBPD(5) |
S3C2410_LCDCON2_LINEVAL(239) |
S3C2410_LCDCON2_VFPD(4) |
S3C2410_LCDCON2_VSPW(3),
.lcdcon3 = S3C2410_LCDCON3_HBPD(13) |
S3C2410_LCDCON3_HOZVAL(319) |
S3C2410_LCDCON3_HFPD(20),
.lcdcon4 = S3C2410_LCDCON4_MVAL(13) |
S3C2410_LCDCON4_HSPW(18),
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_INVVLINE |
S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_PWREN |
S3C2410_LCDCON5_INVVCLK |
S3C2410_LCDCON5_HWSWP,
},
.width = 320,
.height = 240,
.xres ={
.min = 320,
.max = 320,
.defval = 320,
},
.yres ={
.min = 240,
.max = 240,
.defval = 240,
},
.bpp ={
.min = 16,
.max = 16,
.defval = 16,
},
.gpcup= 0xffffffff, //禁止上拉
.gpcup_mask= 0xffffffff, //_mask 用于将寄存器清零
.gpccon= 0xaa9556a9, //
.gpccon_mask= 0xffffffff,
.gpdup= 0xffffffff,
.gpdup_mask= 0xffffffff,
.gpdcon= 0xaaaaaaaa,
.gpdcon_mask= 0xffffffff,
.lpcsel= 0x00, //LPC3600
};
可以在同目录下的 mach-qt2410.c 复制一个过来再修改,其中 struct 的名字 MY2410_lcd_cfg 可以随便
取,但要与后面的保持一致。
.type = S3C2410_LCDCON1_TFT 表明是 tft 屏,否则内核以为是 stn 的屏,而 stn 最大支持
12bpp,内核会打印 invalid bpp 16bpp 的信息,相应代码在 drivers/vedio/s3c2410fb.c 的
s3c2410fb_activate_var 函数中
lcdconx 的配置指的是 2410 的 lcd 控制寄存器,240x320 的屏配置基本差不多,按照自己屏的数据手册,
参考刘立国的《
S3C2410 下 LCD 驱动程序移植及 GUI 程序编写
》这篇文章,配置一下。
剩下的几个参数指定了,宽度,高度,16bpp 的色度。
.gpcup 和.gpcup_mask 的配置都用全一即可,
其实*_mask 参数作用是将要修改的寄存器参数先清零,这样可以保证后面的设置操作正确。因为设置
操作是置位操作,无法清零。也就是说:如果没有 tmp = readl(reg) & ~mask;这一句,就无法将 11 设
置成 10。见参考文章 2。
.gpccon 依据自己的板子连线确定,我的设为 0xaa9556a9 依次表示
VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND
具体见数据手册。
.lpcsel 表示 LPC3600,关于 LPC3600 见数据手册 lcd controller 部分 的说明,在我的板子上,没有用到。
--------------------------------------------------------------------------------------
在 arch/arm/mach-s3c2410/mach-smdk2410.c 的 static void __init smdk2410_init(void)函数
加上
s3c24xx_fb_set_platdata(&MY2410_lcd_cfg);
这样,刚刚添加的数据就加进内核了。
对于,一般的屏,这样的设置就可以了,但是,LTV350QV 需要特殊的初始化,通过 spi 写里面的寄存
器,所以我们还要修改 drivers/vedio/s3c2410fb.c 里的代码。
-----------------------------------------------------------------
在 drivers/vedio/s3c2410fb.h 加上
#define CS_H __raw_writel(__raw_readl(S3C2410_GPCDAT)|(1<<8), S3C2410_GPCDAT)//MAKE_HIGH(LTV350QV_CS)
#define CS_L __raw_writel(__raw_readl(S3C2410_GPCDAT)&~(1<<8), S3C2410_GPCDAT)//MAKE_LOW(LTV350QV_CS)
#define SCLK_H __raw_writel(__raw_readl(S3C2410_GPCDAT)|(1<<9), S3C2410_GPCDAT) //MAKE_HIGH(LTV350QV_SCL)
#define SCLK_L __raw_writel(__raw_readl(S3C2410_GPCDAT)&~(1<<9), S3C2410_GPCDAT) //MAKE_LOW(LTV350QV_SCL)
#define SDI_H __raw_writel(__raw_readl(S3C2410_GPCDAT)|(1<<10), S3C2410_GPCDAT)//MAKE_HIGH(LTV350QV_SDI)
#define SDI_L __raw_writel(__raw_readl(S3C2410_GPCDAT)&~(1<<10), S3C2410_GPCDAT)//MAKE_LOW(LTV350QV_SDI)
这就是用 gpio 模拟的 spi 的高电平和低电平。
加上
#define LTV350QV_POE 0x1d //屏的 id
typedef struct _LTV350qv_spi_data_{
unsigned char Device_ID; //ID of the device
unsigned short Index; //index of register
unsigned short Structure; //structure to be writed
}LTV350QV_SPI_Data; //定义一个数据结构方便传送
模拟 spi 初始化的思想就是把数据串行的通过 SDI(这里是 GPCDAT10)在 SCL 和 CS 时序下送到寄存器
里。
实现代码如下:(改自开发板的 LCD 测试程序)
void LTV350QV_Register_Write(LTV350QV_SPI_Data regdata)
{
unsigned char i,temp1;
unsigned short temp2;
unsigned int temp3;
//write index
temp1=regdata.Device_ID<<2 | 0<<1 | 0<<0; //register index
temp2=regdata.Index;
temp3=(temp1<<24) | (temp2<<8);
CS_L;
mdelay(1);
for(i=0;i<24;i++)
{
SCLK_L;
if(temp3 & (1<<(31-i)) ) //if is H
SDI_H;
else
SDI_L;
mdelay(1); //setup time
SCLK_H;
mdelay(1); //hold time
}
CS_H;
mdelay(5);
//write instruction
temp1=regdata.Device_ID<<2 | 1<<1 | 0<<0; //instruction
temp2=regdata.Structure;
temp3=(temp1<<24) | (temp2<<8);
CS_L;
mdelay(1);
for(i=0;i<24;i++)
{
SCLK_L;
if(temp3 & (1<<(31-i)) ) //if is H
SDI_H;
else
SDI_L;
mdelay(1);
SCLK_H;
mdelay(1);
}
CS_H;
}
void LTV350QV_Write(unsigned short index, unsigned short regdata)
{
LTV350QV_SPI_Data WriteData;
WriteData.Device_ID=LTV350QV_POE; //0x1d
WriteData.Index=index;
WriteData.Structure=regdata;
LTV350QV_Register_Write(WriteData);
}
void LTV350QV_Power_ON(void)
{
int old_gpccon;
old_gpccon=__raw_readl(S3C2410_GPCCON);
__raw_writel(__raw_readl(S3C2410_GPCCON)&0xffa0ffff,S3C2410_GPCCON);
//清 GPCCON8 9 10
__raw_writel(__raw_readl(S3C2410_GPCCON)|0x00150000,S3C2410_GPCCON); //设
置为输出
CS_H;
SCLK_H;
SDI_H;
mdelay(15);
LTV350QV_Write(9, 0x0000);
mdelay(15);
LTV350QV_Write(9, 0x4000);
LTV350QV_Write(10, 0x2000);
LTV350QV_Write(9, 0x4055);
mdelay(60);
LTV350QV_Write(1, 0x409d);
LTV350QV_Write(2, 0x0284);
LTV350QV_Write(3, 0x0100);
LTV350QV_Write(4, 0x3000);
LTV350QV_Write(5, 0x4003);
LTV350QV_Write(6, 0x000a);
LTV350QV_Write(7, 0x0021);
LTV350QV_Write(8, 0x0c00);
LTV350QV_Write(10, 0x0103);
LTV350QV_Write(11, 0x0301);
LTV350QV_Write(12, 0x1f0f);
LTV350QV_Write(13, 0x1f0f);
LTV350QV_Write(14, 0x0707);
LTV350QV_Write(15, 0x0307);
LTV350QV_Write(16, 0x0707);
LTV350QV_Write(17, 0x0000);
LTV350QV_Write(18, 0x0004);
LTV350QV_Write(19, 0x0000);
mdelay(35);
LTV350QV_Write(9, 0x4a55);
LTV350QV_Write(5, 0x5003);
__raw_writel(old_gpccon,S3C2410_GPCCON);
printk("LTV350QV_Power_ON/n");
}
void LTV350QV_Power_OFF(void)
{
// GON -> 0, POC -> 0
LTV350QV_Write(9, 0x4055);
// DSC -> 0
LTV350QV_Write(5, 0x4003);
// VCOMG -> 0
LTV350QV_Write(10, 0x0000);
mdelay(1);
// AP[2:0] -> 000
LTV350QV_Write(9, 0x4000);
printk("LTV350QV_Power_OFF/n");
}
注意一点的就是,用 GPCDAT8,GPCDAT9,GPCDAT10 作模拟 spi 时,相应的 GPCCON 位要设为输
出,而我的 GPCCON 又作为了 lcd 功能的使用,所以设置时先用变量把 GPCCON 保存起来,设置完之
后再还原。对应代码
int old_gpccon;
old_gpccon=__raw_readl(S3C2410_GPCCON);
__raw_writel(__raw_readl(S3C2410_GPCCON)&0xffa0ffff,S3C2410_GPCCON);
//清 GPCCON8 9 10
__raw_writel(__raw_readl(S3C2410_GPCCON)|0x00150000,S3C2410_GPCCON); //设
置为输出
还用一点,网上有人说,LTV350QV_Write( , )函数的地址前面一个应该是 16 进制的,这和你的具体
写寄存器的函数的实现有关,反正是数据要一位一位的送到每个寄存器。
模拟 spi 初始化的函数写好了后,在 static int __init s3c2410fb_probe 函数里加上我们的初始化函数
void LTV350QV_Power_ON,有人说加在哪个地方无所谓,我是加在
ret = s3c2410fb_init_registers(info);这句话之后,其它地方没试过,应该也可以。
-------------------------------------------------------------------
配置内核
Device Drivers --->
Graphics support --->
Display device support --->
<*> Display panel/monitor support
<*> Support for frame buffer devices
<*> S3C2410 LCD framebuffer support
Console display driver support--->
<*> Framebuffer Console support
[*] Framebuffer Console Rotation
[*] Select compiled-in fonts
[*] VGA 8x8 font
[*] VGA 8x16 font
[*] Mini 4x6 font
[*] Sparc console 8x16 font
[*] Bootup logo--->
--- Bootup logo
[*]Standard 224-color Linux logo
Tips:logo 要选 224 的 color,16 的不好看:-(
再 make,make uImage,下载,启动,应该就看到小企鹅了。^_^
附上换 logo 的方法,来自参考文章 2。
(1)用 GIMP 图像编辑器打开你想要的图像文件,依次选择图像->模式->索引颜色,将颜色改为 224
色;至于图片大小,不要大于你的显示器分辨率就好(我只试过 80*80 和 320*240 的大小),最后将
文件保存为 ppm 格式(ASCii 码),文件名为:logo_linux_clut224.ppm。
(2)将 logo_linux_clut224.ppm 拷贝到/drivers/video/logo 文件夹下,替换原有的文件(记得备份啊,
以防万一)。
(3)重新编译内核,下载,启动。
参考文章:
====
http://blog.csdn.net/dongliqiang2006/article/details/4268981