由于需求,需要用VGA来显示,就开始调试VGA。在FSL官网上下载到Linux的BSP,里面就自带了关于ch7026的VGA输出,然而只适用于FSL官方的EVK硬件平台,其在硬件上有一些特殊的处理。我用的是很普遍的ch7026的VGA转换小板。

硬件平台: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");

         /* read device ID */
msleep(100);
dat = i2c_smbus_read_byte_data(ch7026_client, 0x00);
dev_dbg(&ch7026_client->dev, "read id = 0x%02X\n", dat);
printk("in func lcd_init the var ven_id0=%d\n",dat);
if (dat != 0x54)
   return -ENODEV;
/*write init reg value to ch7026 register*/
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 },

/*
NOTE: The following five repeated sentences are used here to wait memory initial complete, please don't remove...(you could refer to Appendix A of programming guide document (CH7025(26)B Programming Guide Rev2.03.pdf) for detailed information about memory initialization!
*/
{ 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位的,就能显示相应的位×××片。


#################################


一、IC功能

CH7026 是一颗电视信号编码IC,可以输出SDTV ,HDTV,VGA电视信号。

二、调试说明

1、由于CH7026寄存器控制是通过I2C实现的,所以在调试之前先要将I2C调试通,这里我就不详细说明了,主要强调一下I2C地址:CH7026 设备地址跟第5脚 AS有关,如果接地就是76,这是没有移关的地址,所以在程序中我们要用0xec。

2、CH7026复位有两种,硬件复位与软件复位,在一般情况下RESET 脚我们直接拉高就可以了,然后通过写0X03寄存器进行复位。

3、再接下来确定输入的RGB位数,输入的参数值(HTI总行数,HAI可视行数,HO行后肩,HW脉宽,VTI,VAI,VO,VW,LCDCLK值),可以根据驱动去配置。(REG:0XF~OX1A)

4、确定输出的TIMING,(HTO,HAO,HOO,HWO,VTO,VAO,VOO,VWO),REG:0X1B~0X26。

{0x1b, 0x13},//
{0x1c, 0x7a},//hao
{0x1d, 0xb1},//hto
//{0x1e, 0x00},//
{0x1f, 0x01},//hoo

{0x20, 0x0a},//hwo
//----vertical
{0x21, 0x51},
{0x22, 0xdb},//vao
{0x23, 0xa8},//vto
//{0x24, 0x0},
{0x25, 0x0f},//voo->16
{0x26, 0x04},//vwo --->4

这里VGA输出是由800*600 SCALE 而来,只是将显示弄出来,我感觉效果不好。还期待改进。

5、时钟寄存器:0X41~0X52

我们外部时钟源是13M:VGA时钟具体配置入如下:

{ 0x41, 0xA2 },
{ 0x4D, 0x03 },
{ 0x4E, 0x0F },
{ 0x4F, 0x8E },
{ 0x50, 0x92 },
{ 0x51, 0x51 },
{ 0x52, 0x12 },
{ 0x53, 0x13 },

6、位置寄存器:0X33~0X36

 

CH7026有TEST模式的,可以通过TEST模式确定到底是OUTPUT的问题,还是INPUT的问题,TEST模式就是通过芯片产生七彩条输出。