硬件平台:ARM11(imx51)+ch7026
软件平台:Linux2.6.31
目的:用VGA替代LCD屏幕输出,同时能够很流畅的播放视频。
首先分析一下驱动代码的的运行流程,如下的入口函数:
static int __init ch7026_init(void)
{
u8 err;
err=platform_driver_register(&lcd_driver);
if (err != 0)
{
printk("ch7026_init
platform_driver_register failed.\n");
}
//return i2c_add_driver(&ch7026_driver);
err = i2c_add_driver(&ch7026_driver);
if (err != 0)
{
printk("i2c_add_driver
failed.\n");
}
return 0;
}
其中,原版程序只有被注释掉的//return
i2c_add_driver(&ch7026_driver); 这一行。
一.
为什么要先添加LCD的驱动注册呢?这就涉及到硬件上供电的问题,看之后的代码便名了。先看lcd_driver下的probe函数
static int __devinit lcd_probe(struct platform_device
*pdev)
{
int i;
struct mxc_lcd_platform_data *plat =
pdev->dev.platform_data;
if (plat) {
if (plat->reset)
plat->reset();
io_reg =
regulator_get(&pdev->dev,
plat->io_reg);
if (!IS_ERR(io_reg)) {
regulator_set_voltage(io_reg, 1800000, 1800000);
regulator_enable(io_reg);
} else {
io_reg =
NULL;
}
core_reg =
regulator_get(&pdev->dev,
plat->core_reg);
if (!IS_ERR(core_reg)) {
regulator_set_voltage(core_reg, 2500000, 2500000);
regulator_enable(core_reg);
} else {
core_reg =
NULL;
}
analog_reg =
regulator_get(&pdev->dev,
plat->analog_reg);
if (!IS_ERR(analog_reg))
{
regulator_set_voltage(analog_reg, 2775000, 2775000);
regulator_enable(analog_reg);
} else {
analog_reg =
NULL;
}
msleep(100);
}
for (i = 0; i < num_registered_fb; i++) {
if
(strcmp(registered_fb[i]->fix.id, "DISP3 BG") == 0)
{
lcd_init_fb(registered_fb[i]);
fb_show_logo(registered_fb[i], 0);
lcd_poweron(registered_fb[i]);
} else if
(strcmp(registered_fb[i]->fix.id, "DISP3 FG") == 0)
{
lcd_init_fb(registered_fb[i]);
} else if
(strcmp(registered_fb[i]->fix.id, "DISP3 BG - DI1")
== 0) {
lcd_init_fb(registered_fb[i]);
fb_show_logo(registered_fb[i], 0);
lcd_poweron(registered_fb[i]);
}
}
fb_register_client(&nb);
return 0;
//return
lcd_probe(&pdev->dev);
}
其中里面的几个寄存器配置io_reg ,core_reg ,analog_reg
有很重要的作用,在内核源码中看看到。下面的一个for循环中多次用到一个函数
lcd_init_fb,其实就是LCD相关的部分。可以看到代码如下:
static void lcd_init_fb(struct fb_info *info)
{
struct fb_var_screeninfo var;
memset(&var, 0, sizeof(var));
fb_videomode_to_var(&var,
&video_modes[0]);
var.activate = FB_ACTIVATE_ALL;
acquire_console_sem();
info->flags |= FBINFO_MISC_USEREVENT;
fb_set_var(info, &var);
fb_blank(info, FB_BLANK_UNBLANK);
info->flags &=
~FBINFO_MISC_USEREVENT;
release_console_sem();
}
还有那个现实的video_mode也很关键,决定了现实的大小,模式等。可以再fb.h中找到相关的定义。
static struct fb_videomode video_modes[] = {
{ "CLAA-VGA", 60, 800, 600, 25000, 48, 24, 23, 3, 128, 4,
FB_SYNC_HOR_HIGH_ACT |
FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED,
FB_MODE_IS_VESA
},
};
二.第二个驱动注册, err =
i2c_add_driver(&ch7026_driver);才是真正的7026芯片的驱动注册。以上都是LCD相关的驱动和配置。还是按照以上的步骤,先从probe函数开始,按照代码运行的顺序进行分析,这样即使出错,也好知道代码运行到那个地方了。
static int
__devinit ch7026_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
ch7026_client = client;
int ret ;
ret = ch7026_chip_init();
if (ret
< 0)
goto err;
err:
if (io_reg)
regulator_disable(io_reg);
if (core_reg)
regulator_disable(core_reg);
if (analog_reg)
regulator_disable(analog_reg);
return ret;
}
其实在probe函数里面就注册了一个client,之后便是ch7026的寄存器值的初始化ch7026_chip_init();。先看看ch7026_chip_init函数内部的。
static int
ch7026_chip_init(void)
{
int i;
int dat;
dev_dbg(&ch7026_client->dev,
"initializing CH7026\n");
msleep(100);
dat = i2c_smbus_read_byte_data(ch7026_client, 0x00);
dev_dbg(&ch7026_client->dev, "read
id = 0xX\n", dat);
printk("in func lcd_init the var ven_id0=%d\n",dat);
if (dat != 0x54)
return -ENODEV;
for (i = 0; i < REGMAP_LENGTH; i++) {
if
(i2c_smbus_write_byte_data(ch7026_client, reg_init[i][0],
reg_init[i][1]) < 0)
//if (i2c_writebyte
(reg_init[i][0], reg_init[i][1]) < 0)
return
-EIO;
}
return
0;
}
先读取寄存器的ID,也就是读取寄存器0x00的值,7026的ID为84,也就是0x54.读取到ID正常以后,便进行寄存器值的初始化,也就是写一些值进去,i2c_smbus_write_byte_data。这个具体的初始化可以查看7026的手册(CH7025(26)B
Programming Guide Rev2.03.02.pdf),也可以直接使用CH7025(26)B
RegSet(2.03).exe这个工具来生成,不过还是需要做一些补充的,最好是看着用户手册来配置寄存器。
reg_init[][2] = {
{ 0x02, 0x01 },
{ 0x02, 0x03 },
{ 0x03, 0x00 },
{ 0x04, 0x39 },
{ 0x07, 0x3F },
{ 0x08, 0x08 },
{ 0x09, 0x80 },
{ 0x0C, 0x00
},
//这是配置哪种模式输出
{ 0x0D, 0x28 },
{ 0x0E, 0x0C },
{ 0x0F, 0x1A },
{ 0x10, 0x80 },
{ 0x11, 0xB6 },
{ 0x12, 0x40 },
{ 0x13, 0x7E },
{ 0x15, 0x11 },
{ 0x16, 0xE0 },
{ 0x17, 0x30 },
{ 0x19, 0x3D },
{ 0x1B, 0x23 },
{ 0x1C, 0x20 },
{ 0x1D, 0x20 },
{ 0x1F, 0x25 },
{ 0x20, 0x80 },
{ 0x21, 0x12 },
{ 0x22, 0x58 },
{ 0x23, 0x74 },
{ 0x25, 0x0A },
{ 0x26, 0x04 },
{ 0x37, 0x20 },
{ 0x39, 0x20 },
{ 0x3B, 0x20 },
{ 0x41, 0xA2 }, //根据晶振的大小来配置,我的为13M
{ 0x4D, 0x03 },
{ 0x4E, 0x0F },
{ 0x4F, 0x8E },
{ 0x50, 0x92 },
{ 0x51, 0x51 },
{ 0x52, 0x12 },
{ 0x53, 0x13 },
{ 0x55, 0xE5 },
{ 0x5E, 0x80 },
{ 0x7D, 0x62 },
{ 0x04, 0x38 },
{ 0x06, 0x71 },
{ 0x03, 0x00 },
{ 0x03, 0x00 },
{ 0x03, 0x00 },
{ 0x03, 0x00 },
{ 0x03, 0x00 },
{ 0x06, 0x70 },
{ 0x02, 0x02 },
{ 0x02, 0x03 },
{ 0x04, 0x00 },
};
将驱动放到内核中,启动后就会看到一个小企鹅的图像。
碰到如下问题及解决方法:
1.
通过跟踪i2c的驱动都运行正常,ID也读取到了,是84,但是VGA不显示。说明lcd_on中的电源没有打开,在lcd_on函数中添加如下部分,
if (core_reg)
regulator_enable(core_reg);
if (io_reg)
regulator_enable(io_reg);
// 将寄存器的电源打开
2.驱动加载成功后,reboot重启时,在bootloader启动时,还有信号输入到VGA,但是启动后,就提示没有检测到信号输入,需要修改寄存器初始化的值。
主要是:0x0c ,0x0d,0x15这三个地方的值,参照手册将其配置成为自己所需要的模式。
3.若是驱动加载成功后,能看到图片,但是播放视频的时候很卡,或者说播放的帧率很小。
需要修改video_mode中的值,其中800,600是大小,之后的四个参数是边距,分别对应左右上下,之后的就是同步模式的参数。
"CLAA-VGA", 60, 800, 600, 25000, 48, 24, 23, 3, 128, 4,
FB_SYNC_HOR_HIGH_ACT |
FB_SYNC_VERT_HIGH_ACT,
FB_VMODE_NONINTERLACED,
FB_MODE_IS_VESA
4.若驱动什么等都加载成功,也能放视频,但是只能显示单一位色的图片,则需要修改fb_ipu3.c中的位值,一般命名为default_bpp,将他的值改为16或24位的,就能显示相应的位色图片。
|