基于avd7181c解决视频输入效果差的问题<一>---驱动移植、调试手记
做过全志A10平台的人都知道,在视频输入方面,虽然有4路TV Decoder,但是做的效果真的不敢恭维。笔者基于全志平台做车载互动娱乐系统以及车载导航主机,客户对视频输入效果有强烈要求,怎么办呢?
加芯片弥补平台的不足。笔者选用的是AVD7181C芯片,可以支持CVBS\S-Video\YPbPr\RGB等多种输入格式,通过该芯片可以输出YUV 4:2:2信号、656信号。YPbPr输入信号的情况下,输出YUV 4:2:2信号,CVBS视频输入的情况下,输出656信号。刚好这两种格式全志平台的CSI都可以支持。那怎么调试呢?
如果想快速的调试出图像,从下到上自己做一套会非常耗费时间,也不利于调试,那还是利用现有的资源来做。笔者就是基于android camera APK来完成前期调试的,我们就把AVD7181C芯片当着是一颗camera芯片,利用camera应用来完成yuv的数据存取以及显示,我们负责把底层打通就可以了。
第一步:基于一个具体camera驱动移植到AVD7181C上。笔者基于gc0308驱动来做的。复制一份改个名字,把里面所有关于gc0308寄存器设置的数组都先清空,这个时候代码就会减少几百行,留着下面的驱动框架,iic操作sensor_read, sensor_write接口也留着。在其他地方都可以大刀阔斧的砍下去,留一个空函数即可。大结构代码如下:
static const struct v4l2_subdev_core_ops sensor_core_ops = { .g_chip_ident = sensor_g_chip_ident, .g_ctrl = sensor_g_ctrl, .s_ctrl = sensor_s_ctrl, .queryctrl = sensor_queryctrl, .reset = sensor_reset, .init = sensor_init, .s_power = sensor_power, .ioctl = sensor_ioctl,};static const struct v4l2_subdev_video_ops sensor_video_ops = { .enum_mbus_fmt = sensor_enum_fmt,//linux-3.0 .try_mbus_fmt = sensor_try_fmt,//linux-3.0 .s_mbus_fmt = sensor_s_fmt,//linux-3.0 .s_parm = sensor_s_parm,//linux-3.0 .g_parm = sensor_g_parm,//linux-3.0};static const struct v4l2_subdev_ops sensor_ops = { .core = &sensor_core_ops, .video = &sensor_video_ops,};/* ----------------------------------------------------------------------- */static int sensor_probe(struct i2c_client *client, const struct i2c_device_id *id){ struct v4l2_subdev *sd; struct sensor_info *info; struct regval_list regs; int ret = -1; int i = 0; avd7181c_dev_dbg("fuction=%s, line=%d\n",__FUNCTION__, __LINE__); info = kzalloc(sizeof(struct sensor_info), GFP_KERNEL); if (info == NULL) return -ENOMEM; sd = &info->sd; v4l2_i2c_subdev_init(sd, client, &sensor_ops); info->ccm_info = &ccm_info_con; info->gain = 0; info->wb = 0; info->clrfx = 0; avd7181c_dev_dbg("fuction=%s, line=%d\n",__FUNCTION__, __LINE__); init_avd7181c_proc(); gsd = sd; return 0;}static int sensor_remove(struct i2c_client *client){ struct v4l2_subdev *sd = i2c_get_clientdata(client); v4l2_device_unregister_subdev(sd); kfree(to_state(sd)); return 0;}static const struct i2c_device_id sensor_id[] = { { "7181c", 0 }, { }};MODULE_DEVICE_TABLE(i2c, sensor_id);static struct i2c_driver sensor_driver = { .driver = { .owner = THIS_MODULE, .name = "7181c", }, .probe = sensor_probe, .remove = sensor_remove, .id_table = sensor_id,};
第二步:根据需要的视频输入格式写好对应寄存器设置。如果需要支持CVBS就加一个cvbs格式的一长串寄存器,ypbpr也一样。笔者以ypbpr为例,列出部分寄存器设置:
static const struct regval_list reg_ypbpr[]={{{0x05},{0x01}},// ; PRIM_MODE = 001b COMP{{0x06},{0x06}},// ; VID_STD for 525P 2x1{{0x03},{0x08}},{{0xC3},{0x46}},// ; ADC1 to Ain4, ADC0 to Ain6, {{0xC4},{0xB5}},// ; ADC2 to Ain5 and enables manual override of mux{{0x1D},{0x47}},// ; Enable 28.63636MHz crystal{{0x3A},{0x11}},// ; Set Latch Clock 01b. Power down ADC3.{{0x3B},{0x81}},// ; Enable Internal Bias {{0x3C},{0x3d}},// ; PLL QPUMP to 011b {{0x6B},{0x83}}, //0xc3,//0x83,// ; 422 8bit out {{0xC9},{0x00}},// ; SDR mode#if 1{{0x73},{0xD0}},// ; Enable Manual Gain and set CH_A gain{{0x74},{0xE6}},// ; Set CH_A and CH_B Gain - 0FAh{{0x75},{0x81}},// ; Set CH_B and CH_C Gain{{0x76},{0xa0}},// ; Set CH_C Gain{{0x77},{0x06}},{{0x78},{0x08}},{{0x79},{0x02}},{{0x7A},{0x00}},#endif...........}
第三部:在设置fmt的接口中去控制输入格式。在sensor_s_fmt函数中可以去控制是选择CVBS还是ypbpr,当然是通过这个接口中传递下来的参数来控制。这部分完全可以自定义,笔者介绍一下ypbpr 480P情况下的设置:
info->fmt = sensor_fmt; info->width = 720; info->height = 480; /*write reg_ypbpr setting*/ ret = sensor_write_array(sd, reg_ypbpr , ARRAY_SIZE(reg_ypbpr)); //reg_ypbpr avd7181c_dev_dbg("==========sensor_write at wirte reg_ypbpr ret=%d\n", ret);
第四步:为了调试方便增加便捷调试功能。怎么才能在运行的时候,可以很便利的去修改寄存器,读出当前设置的寄存器值呢?可以通过proc来完成。通过串口来输入控制命令,这样想读哪个寄存器就读哪个寄存器,想怎么调试方便,就可以按需要很方便的去调试。
static void init_avd7181c_proc(void){ struct proc_dir_entry *read_r, *write_r; avd7181c_dev_dbg("%s\n", __func__); dir = proc_mkdir("avd7181ctest", NULL); read_r = create_proc_entry("read_r", 0666, dir); if (read_r) read_r->write_proc = avd7181cTest_proc_read; write_r = create_proc_entry("write_r", 0666, dir); if (write_r) write_r->write_proc = avd7181cTest_proc_write;}
第五步:在system_config1.fex中添加配置。主要是打开csi,设置名称、iic地址等,还有就是复位、standby供电的gpio设置。
csi_used = 1csi_mname = "7181c"csi_twi_id = 1csi_twi_addr = 0x42csi_if = 0csi_d15 = csi_reset = port:PH13<1><default><default><0>csi_power_en = port:PI11<1><default><default><0>csi_stby = port:PH18<1><default><default><0>csi_reset_b =
通过以上几步,我们已经搭建好一个初步的AVD7181C驱动框架,编译通过就可以跑测试了。笔者使用DVD输出ypbpr信号给AVD7181C,在camera apk中查看图像。先贴一副截屏图像:
在这个调试过程中,笔者遇到了很多的问题,多问问,多试试,一步步解决,总能解决好。笔者将在后续文章中介绍遇到的AVD7181C iic读寄存器兼容性问题、CVBS输入情况下CSI驱动的修改等。敬请期待!