Linux 2.6.22.19移植到S3C2410(GEC2410)之:LCD驱动移植
参考文献:
1. 《移植Linux2.6.22.2到博创2410-S(s3c2410A)(补:LCD驱动移植)》
http://blog.chinaunix.net/u1/34474/showart_410311.html
2. 《2410 移植LCD 驱动LTV350QV》
http://blog.chinaunix.net/u2/83636/showart_1733013.html
3.《S3C2410 下LCD 驱动程序移植及GUI 程序编写》著作权所有:刘利国,是一篇经典的LCD移植文档,移植前必看。网上google一下就有(PDF版)。
平台信息:
目标板 :GEC2410
OS :Fedora Cor8(FC8)
编译器 :arm-2007q3-53-arm-none-eabi-i686-pc-linux-gnu(4.2.1)
LCD :SUMSANG 的 LTV350QV_FOE 3.5 寸 320x240
移前注意:
如果你移植的是2.6.24的内核,那我想你就别往下看了,跟2.6.22有很多的区别,主要区别有:
一、在2.6.24内核中有了重大的数据结构变化,首先他把结构拆成了二部分,一个结构是s3c2410fb_display另一个结构是s3c2410fb_mach_info,所以先根据这二个数据结构进行拆分上述结构;
二、2.6.24结构中对寄存器lcdcon1-4全部用函数进行了自动设置,我们只需对lcdcon5进行设置,但是并不说明其他的数据不重要,或者不用设置,只是重点在lcdcon5这几个数值上。
关于2.6.24的LCD驱动移植可以参考:
Linux 2.6.24.4移植到S3C2410(nano2410)之:LCD
移植步骤:
在Linux2.6.22.19下移植LCD驱动还是比较简单,只要在arch/arm/mach-s3c2410/mach-smdk2410.c里添加初始化s3c2410的LCD控制器时所需要的参数就好了。(不过LTV350QV的LCD就要另当别论了,下边会有详细介绍)!!!
1. 修改/arch/arm/mach-s3c2410/mach-smdk2410.c文件
1). 添加头文件
#include <asm/arch/fb.h>
2). 添加初始化s3c2410的LCD控制器时所需的参数(这些参数可以直接从同级目录的mach-qt2410.c文件复制并稍作修改,增快移植速度)。
static struct s3c2410fb_mach_info S3C2410_lcd_cfg __initdata = {
.type = S3C2410_LCDCON1_TFT, //这话很重要,看下边特别说明(1)
.regs ={ //看特别说明(2)
.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,
},
//下边设置看特别说明(3)
.gpcup= 0xffffffff, //禁止上拉
.gpcup_mask= 0xffffffff, //_mask 用于将寄存器清零
.gpccon= 0xaa9556a9,
.gpccon_mask= 0xffffffff,
.gpdup= 0xffffffff,
.gpdup_mask= 0xffffffff,
.gpdcon= 0xaaaaaaaa,
.gpdcon_mask= 0xffffffff,
.lpcsel= 0x00, //LPC3600
};
3)添加LCD控制器的寄存器参数设置函数
static void __init smdk2410_init(void)
{
s3c24xx_fb_set_platdata(&qt2410_biglcd_cfg);
platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));
smdk_machine_init();
}
特别说明:
(1)其中的".type = S3C2410_LCDCON1_TFT,"一定要加,否则Linux会认为LCD是STN屏,出现花屏(像下雨一样,蓝色的)。
(2)这里的regs设置不同款式的LCD是不同的,需要根据自己的LCD配置进行设置,你可以参考LCD手册,也可以直接从开发板自带的LCD驱动中COPY过来,开发板自带的驱动一般为 driver/video/s3c2410fp.c 。我就是直接copy的,下边是我开发板自带驱动该部分内容:
.regs = {
.lcdcon1 = (5<<8) | (0<<7) | (3<<5) | (12<<1),
.lcdcon2 = (5<<24) | (239<<14) |(4<<6) | (3),
.lcdcon3 = (13<<19) | (319<<8) | (20),
.lcdcon4 = (13<<8) | (18),
.lcdcon5 = (1<<11) | (1<<10) | (1<<9) | (1<<8) | (0<<6) | (0<<1) | (1),
},
直接使用数字就可以了,不需要每个都用变量来代替!!!
(3)这里的设置可以依据LCD原理图和手册确定,不过更便捷的方式还是跟设置regs一样,直接从开发板自带的driver/video/s3c2410fb.c 的LCD驱动中搜寻出来就好了!!!
其中的*_mask 参数的作用是将要修改的寄存器参数先清零,这样可以保证后面的设置操作正确。因为设置操作是置位操作,无法清零(参考我的参考资料1)。想了解其他各项的作用那就自个儿看手册吧,那里够详细的了!!!
好,mach-smdk2410.c文件修改结束。!!!!
________________________________________
2. 如果你想去除10分钟左右自动关闭LCD显示的功能,那就进行这第二步吧!
方法很简单,注释掉drivers\char\vt.c 的blank_screen_t(unsigned long dummy)的函数内容即可!!!不过我想对于一般的情况就不要去除这功能了,又能省电还能延长LCD使用寿命,干嘛要自找麻烦呢!!!
________________________________________
3. 编译内核,选中所装驱动
#make menuconfig
Device Drivers >
Graphics 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 --->
[*] Standard 224-color Linux logo
________________________________________
4. 如果你不是LTV350QV_FOE 这个系列的LCD,那一般你的工作就完成了,直接make zImage编译内核并烧写就可以了。
________________________________________
不过,如果你不幸或有幸用的正好是LTV350QV_FOE这个系列的LCD,呵呵,保持好良好的心态继续跟我来吧!!!真的,心态很重要!我当时也郁闷的很,很不情愿地继续移植,结果可想而知,我为此付出了很惨烈的代价,整整浪费了我一整天的时间!!!不过也有点怪我那第二篇参考文献,干,写就写好点嘛,该写什么就把那个写清楚点吧!总是这凑一点那凑一点,这含一点,那蓄一点,你以为你很含蓄呀!!!!搞什么鸟子~~~!!!!!
(这个 LTV350QV 比较特殊的是,它的初始化需要通过 spi 总线写 S6F2002 内部的寄存器,有人说,LTV350QV 比较麻烦的一点是还必须要进行 SPI 设置,但这也是它比较灵活的一面。) 这是鸟人说的,我也不知道是不是这样,你们自个儿来判断吧!!!!
5. 好,屁话不说,来吧,我们继续!!!
其实也很简单,也就是在driver/video/s3c2410fb.c中加入个LCD的初始函数而已!!!这个函数我是直接参开发板自带的驱动拼接起来的!!如果你想自己写,那你就自个人参考手册写吧,我就算了,已经很郁闷了!!!!
下边是我根据开发板自带的s3c2410fb.c 驱动拼接起来的初始化LCD的代码,并添加到driver/video/s3c2410fb.c中:(注意:一定要根据自己开发板的驱动来,直接用我的代码,抱歉,我不敢保证你能移植成功~~~~)
第一部分(定义):
typedef struct _LTV350qv_spi_data_{
unsigned char Device_ID; //ID of the device
unsigned int Index; //index of register
unsigned long Structure; //structure to be writed
}LTV350QV_SPI_Data;
//micro for LTV350QV_POE
#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)
#define RST_H __raw_writel(__raw_readl(S3C2410_GPDDAT)|(1<<0), S3C2410_GPDDAT) //MAKE_HIGH(LTV350QV_RST)
#define RST_L __raw_writel(__raw_readl(S3C2410_GPDDAT)&~(1<<0), S3C2410_GPDDAT) //MAKE_LOW(LTV350QV_RST)
第二部分(初始函数):
//***************************************************************
//**********these functions for SUMSUN LTV350QV TFT LCD****************
//***************************************************************
//short delay for about 90*time ns
static void LTV350QV_Short_Delay(u_char time)
{
//u_char i;
//for(i=0;i<time*10;i++) ; //about 180 ns
ndelay(150);
}
static void LTV350QV_Rst(void)
{
RST_L;
mdelay(1);
RST_H;
mdelay(1);
}
static void LTV350QV_Register_Write(LTV350QV_SPI_Data regdata)
{
u_char i,temp1;
u_int temp2;
u_long temp3;
unsigned long flags;
//write index
temp1=regdata.Device_ID<<2 | 0<<1 | 0<<0; //register index
temp2=regdata.Index;
temp3=(temp1<<24) | (temp2<<8);
local_irq_save(flags);
CS_L;
LTV350QV_Short_Delay(1);
for(i=0;i<24;i++)
{
SCLK_L;
if(temp3 & (1<<(31-i)) ) //if is H
SDI_H;
else
SDI_L;
LTV350QV_Short_Delay(1); //setup time
SCLK_H;
LTV350QV_Short_Delay(1); //hold time
}
CS_H;
LTV350QV_Short_Delay(5);
//write instruction
temp1=regdata.Device_ID<<2 | 1<<1 | 0<<0; //instruction
temp2=regdata.Structure;
temp3=(temp1<<24) | (temp2<<8);
CS_L;
LTV350QV_Short_Delay(1);
for(i=0;i<24;i++)
{
SCLK_L;
if(temp3 & (1<<(31-i)) ) //if is H
SDI_H;
else
SDI_L;
LTV350QV_Short_Delay(1);
SCLK_H;
LTV350QV_Short_Delay(1);
}
CS_H;
local_irq_restore(flags);
}
/****************************************
* *
****************************************/
static void LTV350QV_Write(u_int index, u_int regdata)
{
LTV350QV_SPI_Data WriteData;
WriteData.Device_ID=LTV350QV_POE; //0x1d
WriteData.Index=index; //
WriteData.Structure=regdata;
LTV350QV_Register_Write(WriteData);
}
/****************************************
* *power ON sequence
****************************************/
static void LTV350QV_Power_ON(void) //粉色部分的代码请参考自带驱动的pxafb_setup_gpio部分,或者你就跟踪LTV350QV_Power_ON这函数,也能找到具体实现的代码!!!
{
__raw_writel(0xaa9556a9, S3C2410_GPCCON); //Initialize VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND
//LCDVF[0],[1],[2]---output;VD[0],[1],[2]----output.
__raw_writel(0xffffffff, S3C2410_GPCUP); // Disable Pull-up register
//DPRINTK("%s()\n", __FUNCTION__);
LTV350QV_Write(9, 0x0000);
mdelay(150);
LTV350QV_Write(9, 0x4000);
LTV350QV_Write(10, 0x2000);
LTV350QV_Write(9, 0x4055);
mdelay(550);
LTV350QV_Write(1, 0x409d);
LTV350QV_Write(2, 0x0204);
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(200);
LTV350QV_Write(9, 0x4a55);
LTV350QV_Write(5, 0x5003);
__raw_writel(0xaaaaaaaa, S3C2410_GPDCON);
__raw_writel(0xffffffff, S3C2410_GPDUP);
__raw_writel(3, S3C2410_LCDINTMSK); // MASK LCD Sub Interrupt
__raw_writel(0, S3C2410_TPAL); // Disable Temp Palette
__raw_writel(0, S3C2410_LPCSEL); // Disable LPC3600
}
/**********************************
* *power OFF sequence
**********************************/
static void LTV350QV_Powen_OFF(void) //没用到
{
/* GON -> 0, POC -> 0 */
LTV350QV_Write(9, 0x4055);
/* DSC -> 0 */
LTV350QV_Write(5, 0x4003);
/* VCOMG -> 0 */
LTV350QV_Write(10, 0x0000);
mdelay(20);
/* AP[2:0] -> 000 */
LTV350QV_Write(9, 0x4000);
}
//------------------------------------------------------end LTV350-POE
第三部分(把上边的LTV350QV_Power_ON函数加入执行):
加入位置为static int __init s3c2410fb_probe(struct platform_device *pdev),具体位置我是加在:
ret = s3c2410fb_init_registers(info);
ret = s3c2410fb_check_var(&fbinfo->var, fbinfo);
ret = register_framebuffer(fbinfo);
if (ret < 0) {
printk(KERN_ERR "Failed to register framebuffer device: %d\n", ret);
goto free_video_memory;
}
LTV350QV_Power_ON();
其他位置是否可以呢?SORRY,我也不知道,一般可以吧!!!呵呵,如果你兴趣,可以试过!!!!
6. 呵呵,怎么样,这第五步够呛的吧!!!不过没事,也就这些了!!!忘记配置内核的就去配置下,选项跟上边的一样!接着编译内核,烧写开发板,启动。。。。。你是否看见了LCD上显示的可爱的企鹅图像呢???看见了?哈哈。。。那恭喜你!如果没看见,那就请你重新检查下是否哪里漏掉了,但一定要保持好良好心态哦,这个很重要!!!!
启动信息有:
Console: switching to colour frame buffer device 80x40
fb0: s3c2410fb frame buffer device
7. 但你如果想在LCD上显示自己的Logo,那下面我介绍一下自定义Logo的方法:
(1)进入linux的kde图形界面,使用The GIMP 图像编辑器(其他也可以)打开你想要的图像文件,依次选择图像->模式->索引颜色,将颜色改为224色;至于图片大小,不要大于你的显示器分辨率就好,最后将文件保存为ppm格式(ASCii码),文件名为:logo_linux_clut224.ppm。
(2)将logo_linux_clut224.ppm拷贝到/drivers/video/logo文件夹下,替换原有的文件(记得备份啊,以防万一)。
(3)重新编译内核,烧写开发板启动。
8. 好文章分享(网上自己找)
2. LCD移植过程
1042

被折叠的 条评论
为什么被折叠?



