公司最近开发的一款产品用到了ambarella H2平台的一款Soc,众所周知ambarella的H2系列的Soc编码能力很强,最高可达4kp60,捕捉上没有用ambarella开发板推荐的几个捕捉卡,是自己用fpga做的一款捕捉卡, 所以捕捉卡驱动需要自己来写。

捕捉卡驱动其实没有什么东西,就是简单地i2c通信, H2 Soc通过i2c和捕捉卡进行通信, 可以通过check寄存器得到输入源的制式以及audio通道数相关信息,从而进行audio通道数设定并且通过/proc文件系统告诉应用层,从而进行相应制式的捕捉与编码。

至于制式切换方面,捕捉卡这边会向cpu发来一个gpio中断(200ms高电平),捕捉卡驱动捕获到该中断后,重新check一次寄存器值,配置auido相关寄存器并更新/proc下的制式文件,应用层通过不断地poll这个制式文件得知制式改变时,停止原先的捕捉,开启新的制式的捕捉及编码。这种方法有个缺点就是响应速度会慢一些,但是可以优化,比如驱动层通过异步通知的方法告知应用层制式切换了,然后应从层再去check /proc下制式文件。

这个i2c捕捉卡驱动要和ambarella H2 SDK里的捕捉相关驱动集成在一起,这里ambarela SDK里捕捉驱动先略过。

驱动编写

一、修改设备树文件

ambarella/boards/h2_xxx/bsp/h2_xxx.dts

apb@e8000000 {
i2c0: i2c@e8003000 {
single_vin: ambvin0@ {
compatible = "ambarella,ambvin";
reg = <0x3B>; /* slave address */
interrupt-parent = <&gpio>;
interrupts = < 0x2>; /* gpio26, 下降沿触发*/
};
status = "ok";
};
...

二、编写驱动

捕捉卡i2c的读写地址都是2bytes, 读写数据每次4bytes。0x0004为读写测试寄存器。

下面代码只实现了i2c驱动的注册、i2c读写函数、中断申请。插拔视频源时会触发中断,中断处理函数中对0x0004寄存器进行了读写测试。其他代码为ambarella平台视频捕捉部分的demo,都是hardcode的,可以在中断处理函数里调用dummyfpga_get_format, 并在dummyfpga_get_format里check捕捉卡寄存器,从而设定相应的timing去捕捉,这里不做过多的累述。

#include <linux/module.h>
#include <linux/ambpriv_device.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/bitrev.h>
#include <linux/stat.h>
#include <linux/i2c.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/param.h>
#include <plat/spi.h>
#include <iav_utils.h>
#include <vin_api.h>
#include <plat/clk.h>
#include <plat/gpio.h> #include "dummyfpga_table.c" unsigned int g_audio_mode=;
unsigned int g_cap_cap_w=;
unsigned int g_cap_cap_h=; static int w = ;
MODULE_PARM_DESC(w, "video input width");
module_param(w, int, S_IRUGO); static int h = ;
MODULE_PARM_DESC(h, "video input height");
module_param(h, int, S_IRUGO); static int p = ;
MODULE_PARM_DESC(p, "video input format");
module_param(p, int, S_IRUGO); static int bit = ;
MODULE_PARM_DESC(bit, "video input format bits");
module_param(bit, int, S_IRUGO); struct xilinx_dev {
struct cdev cdev;
struct i2c_client *client;
struct class *cls;
struct mutex rw_mutex;
}; //struct xilinx_dev *xilinx_devp;
static int xilinx_cdev_major = ; static int dummyfpga_set_audio_mode_width_height(struct vin_device *vdev, u32 width, u32 height)
{
g_audio_mode = ;
g_cap_cap_w = width;
g_cap_cap_h = height; return ;
} static int dummyfpga_set_vin_mode(struct vin_device *vdev, struct vin_video_format *format)
{
struct vin_device_config dummyfpga_config;
static int yuv_order = SENSOR_CB_Y0_CR_Y1; memset(&dummyfpga_config, , sizeof (dummyfpga_config)); dummyfpga_config.sensor_id = GENERIC_SENSOR;
dummyfpga_config.interface_type = SENSOR_PARALLEL_LVDS;
if (bit == ) {
dummyfpga_config.bit_resolution = AMBA_VIDEO_BITS_10;
} else {
dummyfpga_config.bit_resolution = AMBA_VIDEO_BITS_8;
}
dummyfpga_config.input_mode = SENSOR_YUV_2PIX;
//dummyfpga_config.input_mode = SENSOR_YUV_1PIX;
dummyfpga_config.plvds_cfg.data_edge = SENSOR_DATA_FALLING_EDGE;
//if (w == 720) {
// dummyfpga_config.plvds_cfg.emb_sync_loc = SENSOR_PARALLEL_SYNC_ACROSS_BOTH;
// } else {
dummyfpga_config.plvds_cfg.emb_sync_loc = SENSOR_PARALLEL_SYNC_LOWER_PIX;
// } dummyfpga_config.plvds_cfg.data_rate = SENSOR_PARALLEL_DATA_RATE_SDR;
dummyfpga_config.plvds_cfg.a8_mode = SENSOR_PARALLEL_NONE_A8_MODE;
dummyfpga_config.yuv_pixel_order = yuv_order;
dummyfpga_config.plvds_cfg.hw_specific = SENSOR_PARALLEL_HW_BUB;
//dummyfpga_config.plvds_cfg.sync_code_style = SENSOR_SYNC_STYLE_ITU656;
//dummyfpga_config.input_format = AMBA_VIN_INPUT_FORMAT_YUV_422_INTLC; if (p == ) {
dummyfpga_config.plvds_cfg.sync_code_style = SENSOR_SYNC_STYLE_ITU656;
dummyfpga_config.input_format = AMBA_VIN_INPUT_FORMAT_YUV_422_PROG;
} else {
dummyfpga_config.plvds_cfg.sync_code_style = SENSOR_SYNC_STYLE_INTERLACE;
dummyfpga_config.input_format = AMBA_VIN_INPUT_FORMAT_YUV_422_INTLC;
} /*
if(format->video_mode == AMBA_VIDEO_MODE_1080P || format->video_mode == AMBA_VIDEO_MODE_1080P50)
{
dummyfpga_config.plvds_cfg.sync_code_style = SENSOR_SYNC_STYLE_ITU656;
dummyfpga_config.yuv_pixel_order = SENSOR_Y0_CB_Y1_CR;
dummyfpga_config.plvds_cfg.emb_sync_loc = SENSOR_PARALLEL_SYNC_LOWER_PIX;
} else if(format->video_mode == AMBA_VIDEO_MODE_1080I || format->video_mode == AMBA_VIDEO_MODE_1080I50) {
dummyfpga_config.plvds_cfg.sync_code_style = SENSOR_SYNC_STYLE_ITU656;
dummyfpga_config.yuv_pixel_order = SENSOR_Y0_CB_Y1_CR;
dummyfpga_config.plvds_cfg.emb_sync_loc = SENSOR_PARALLEL_SYNC_LOWER_PIX;
} else if(format->video_mode == AMBA_VIDEO_MODE_720P || format->video_mode == AMBA_VIDEO_MODE_720P50) {
dummyfpga_config.plvds_cfg.sync_code_style = SENSOR_SYNC_STYLE_ITU656;
dummyfpga_config.yuv_pixel_order = SENSOR_Y0_CB_Y1_CR;
dummyfpga_config.plvds_cfg.emb_sync_loc = SENSOR_PARALLEL_SYNC_LOWER_PIX;
} else if(format->video_mode == AMBA_VIDEO_MODE_576I) {
dummyfpga_config.plvds_cfg.sync_code_style = SENSOR_SYNC_STYLE_ITU656;
dummyfpga_config.yuv_pixel_order = SENSOR_Y0_CB_Y1_CR;
dummyfpga_config.plvds_cfg.emb_sync_loc = SENSOR_PARALLEL_SYNC_LOWER_PIX;
} else if(format->video_mode == AMBA_VIDEO_MODE_480I) {
dummyfpga_config.plvds_cfg.sync_code_style = SENSOR_SYNC_STYLE_ITU656;
dummyfpga_config.yuv_pixel_order = SENSOR_Y0_CB_Y1_CR;
dummyfpga_config.plvds_cfg.emb_sync_loc = SENSOR_PARALLEL_SYNC_LOWER_PIX;
} else {
printk("Unsupport mode %d \n", format->video_mode);
return -1;
}
*/
dummyfpga_config.cap_win.x = ;
dummyfpga_config.cap_win.y = ;
dummyfpga_config.cap_win.width = w;
dummyfpga_config.cap_win.height = h; printk("cap_win_height %d video_format %d \n", dummyfpga_config.cap_win.height, format->format);
dummyfpga_config.video_format = format->format ;
return ambarella_set_vin_config(vdev, &dummyfpga_config);
} static int dummyfpga_set_format(struct vin_device *vdev, struct vin_video_format *format)
{
int rval; rval = dummyfpga_set_vin_mode(vdev, format);
if (rval < )
return rval; printk("############### Debug: dummyfpga_set_format ##############\n");
return ;
} static int dummyfpga_get_format(struct vin_device *vdev)
{
//int rval; vdev->formats->video_mode = AMBA_VIDEO_MODE_AUTO;
vdev->formats->def_start_x = ;//pinfo->cap_start_x;
vdev->formats->def_start_y = ;//pinfo->cap_start_y;
vdev->formats->def_width = g_audio_mode ? g_cap_cap_w:w;//pinfo->cap_cap_w;
vdev->formats->def_height = g_audio_mode ? g_cap_cap_h:h;//pinfo->cap_cap_h;
vdev->formats->default_fps = AMBA_VIDEO_FPS_60;//pinfo->frame_rate;
vdev->formats->max_fps = AMBA_VIDEO_FPS_60;//pinfo->frame_rate;
vdev->formats->ratio = AMBA_VIDEO_RATIO_16_9;//pinfo->aspect_ratio;
if (p == ) {
vdev->formats->format = AMBA_VIDEO_FORMAT_PROGRESSIVE;//pinfo->video_format;
} else {
vdev->formats->format = AMBA_VIDEO_FORMAT_INTERLACE;//pinfo->video_format;
}
vdev->formats->type = AMBA_VIDEO_TYPE_YUV_656;//pinfo->input_type;
if (bit == ) {
vdev->formats->bits = ;
} else {
vdev->formats->bits = ;//pinfo->bit_resolution;
}
//format->sync_start = pinfo->sync_start;
printk("############### Debug: dummyfpga_get_format ##############\n");
return ;
} static int dummyfpga_init_device(struct vin_device *vdev)
{
vin_info("DUMMYFPGA Init\n");
return ;
} static struct vin_ops dummyfpga_ops = {
.init_device = dummyfpga_init_device,
.set_format = dummyfpga_set_format,
.get_format = dummyfpga_get_format,
.set_audio_mode_width_height = dummyfpga_set_audio_mode_width_height
}; /* ========================================================================== */
static int dummyfpga_drv_probe(struct ambpriv_device *ambdev)
{
struct vin_device *vdev;
int rval = ;
vdev = ambarella_vin_create_device(ambdev->name,
DECODER_GS2970, );
if (!vdev)
return -ENOMEM; vdev->intf_id = ;
vdev->dev_type = VINDEV_TYPE_DECODER;
vdev->sub_type = VINDEV_SUBTYPE_SDI;
vdev->default_mode = AMBA_VIDEO_MODE_AUTO;
vdev->default_hdr_mode = AMBA_VIDEO_LINEAR_MODE;
vdev->frame_rate = AMBA_VIDEO_FPS_AUTO; rval = ambarella_vin_register_device(vdev, &dummyfpga_ops,
dummyfpga_formats, ARRAY_SIZE(dummyfpga_formats),
dummyfpga_plls, ARRAY_SIZE(dummyfpga_plls));
if (rval < ) {
ambarella_vin_free_device(vdev);
return rval;
} ambpriv_set_drvdata(ambdev, vdev); vin_info("Dummyfpga Init, with LVDS I/F\n");
return ;
} static int dummyfpga_drv_remove(struct ambpriv_device *ambdev)
{
struct vin_device *vdev = ambpriv_get_drvdata(ambdev); ambarella_vin_unregister_device(vdev);
ambarella_vin_free_device(vdev); return ;
} static struct ambpriv_driver dummyfpga_driver = {
.probe = dummyfpga_drv_probe,
.remove = dummyfpga_drv_remove,
.driver = {
.name = "dummyfpga",
.owner = THIS_MODULE,
}
}; static int xilinx_i2c_write_4bytes(struct xilinx_dev *dev, u8 subaddr[], u8 data[])
{
int rval;
u8 pbuf[];
struct i2c_client *client = dev->client; pbuf[] = subaddr[];
pbuf[] = subaddr[];
pbuf[] = data[];
pbuf[] = data[];
pbuf[] = data[];
pbuf[] = data[]; rval = i2c_master_send(client, pbuf, );
if (rval < ) {
vin_error("addr w failed(%d): [0x%x%x]\n", rval, subaddr[], subaddr[]);
return rval;
} return ;
} static int xilinx_i2c_read_4bytes(struct xilinx_dev *dev, u8 subaddr[], u8 data[])
{
int rval;
struct i2c_msg msgs[];
struct i2c_client *client = dev->client; msgs[].len = ;
msgs[].addr = client->addr;
msgs[].flags = client->flags;
//msgs[0].buf = &subaddr[0];
msgs[].buf = subaddr; msgs[].len = ;
msgs[].addr = client->addr;
msgs[].flags = client->flags | I2C_M_RD;
//msgs[1].buf = &data[0];
msgs[].buf = data; rval = i2c_transfer(client->adapter, msgs, );
if (rval < ) {
vin_error("addr r failed(%d): [0x%x%x]\n", rval, subaddr[], subaddr[]);
return rval;
} return ;
} static int i2c_test_write(struct xilinx_dev *dev)
{
int rval;
u8 subaddr[];
u8 data[]; subaddr[] = 0x00;
subaddr[] = 0x04;
data[] = 0x12;
data[] = 0x34;
data[] = 0x56;
data[] = 0x78;
rval = xilinx_i2c_write_4bytes(dev, subaddr, data);
if (rval < )
return rval; return ;
} static int i2c_test_read(struct xilinx_dev *dev)
{
int rval;
u8 subaddr[];
u8 data[]; subaddr[] = 0x00;
subaddr[] = 0x04;
rval = xilinx_i2c_read_4bytes(dev, subaddr, data);
if (rval < )
return rval;
printk("0x0004=%02x%02x%02x%02x\n", data[], data[], data[], data[]); subaddr[] = 0x00;
subaddr[] = 0x00;
rval = xilinx_i2c_read_4bytes(dev, subaddr, data);
if (rval < )
return rval;
printk("0x0000=%02x%02x%02x%02x\n", data[], data[], data[], data[]); return ;
} static irqreturn_t xilinx_i2c_interrupt(int irq, void *dev_id)
{
struct xilinx_dev *xilinx_devp = (struct xilinx_dev *)dev_id;
i2c_test_write(xilinx_devp);
i2c_test_read(xilinx_devp); return IRQ_HANDLED;
} static const struct file_operations xilinx_fops = {
.owner = THIS_MODULE,
// .read =
// .write =
// .unlocked_ioctl =
// .open =
// .release =
}; static void xilinx_setup_cdev(struct xilinx_dev *dev, int index)
{
int err, devno = MKDEV(xilinx_cdev_major, index); cdev_init(&dev->cdev, &xilinx_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&dev->cdev, devno, );
if (err)
printk(KERN_NOTICE "Error %d adding xilinx_cdev%d", err, index);
} static int xilinx_i2c_proble(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret;
struct xilinx_dev *xilinx_devp;
struct device *dev = &client->dev;
dev_t devno = MKDEV(xilinx_cdev_major, ); printk("xilinx_i2c_probe\n");
if (xilinx_cdev_major)
ret = register_chrdev_region(devno, , "xilinx_cdev");
else {
ret = alloc_chrdev_region(&devno, , , "xilinx_cdev");
xilinx_cdev_major = MAJOR(devno);
}
if (ret < )
return ret; xilinx_devp = kzalloc(sizeof(struct xilinx_dev), GFP_KERNEL);
if (!xilinx_devp) {
ret = -ENOMEM;
goto fail_malloc;
}
xilinx_devp->client = client;
i2c_set_clientdata(client, xilinx_devp); xilinx_setup_cdev(xilinx_devp, );
xilinx_devp->cls = class_create(THIS_MODULE, "xilinx_class");
if (IS_ERR(xilinx_devp->cls)) {
printk("Err: failed in creating xilinx cdev class\n");
return -;
}
device_create(xilinx_devp->cls, NULL, MKDEV(xilinx_cdev_major, ), NULL, "xilinx_fpga");
ret = devm_request_threaded_irq(dev, client->irq, NULL,
xilinx_i2c_interrupt, IRQF_ONESHOT,
client->name, xilinx_devp);
if (ret) {
dev_err(dev, "request irq %d failed: %d\n", client->irq, ret);
return ret;
} else
printk("request irq:%d\n", client->irq); mutex_init(&xilinx_devp->rw_mutex); //i2c_test_write(xilinx_devp);
//i2c_test_read(xilinx_devp); fail_malloc:
unregister_chrdev_region(devno, );
return ret;
} static int xilinx_i2c_remove(struct i2c_client *client)
{
struct xilinx_dev *xilinx_devp;
dev_t devno = MKDEV(xilinx_cdev_major, );
xilinx_devp = (struct xilinx_dev *)i2c_get_clientdata(client);
cdev_del(&xilinx_devp->cdev);
device_destroy(xilinx_devp->cls, devno);
class_destroy(xilinx_devp->cls);
kfree(xilinx_devp);
unregister_chrdev_region(devno, ); return ;
} static const struct i2c_device_id xilinx_idtable[] = {
{"xilinx", },
{},
};
MODULE_DEVICE_TABLE(i2c, xilinx_idtable); static const struct of_device_id xilinx_dt_ids[] = {
{.compatible = "ambarella,ambvin",},
{},
};
MODULE_DEVICE_TABLE(of, xilinx_dt_ids); static struct i2c_driver i2c_driver_xilinx = {
.driver = {
.name = "xilinx",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(xilinx_dt_ids),
//.of_match_table = xilinx_dt_ids,
},
.id_table = xilinx_idtable,
.probe = xilinx_i2c_proble,
.remove = xilinx_i2c_remove, }; static struct ambpriv_device *dummyfpga_device;
static int __init dummyfpga_init(void)
{
int rval = ; rval = i2c_add_driver(&i2c_driver_xilinx);
if (rval < ) {
printk("add xilinx i2c driver failed\n");
return rval;
} dummyfpga_device = ambpriv_create_bundle(&dummyfpga_driver, NULL, -, NULL, -);
if (IS_ERR(dummyfpga_device))
rval = PTR_ERR(dummyfpga_device);
return ;
} static void __exit dummyfpga_exit(void)
{
i2c_del_driver(&i2c_driver_xilinx);
ambpriv_device_unregister(dummyfpga_device);
ambpriv_driver_unregister(&dummyfpga_driver);
} module_init(dummyfpga_init);
module_exit(dummyfpga_exit); MODULE_DESCRIPTION("dummyfpga decoder");
MODULE_LICENSE("GPL");

驱动加载后,切换视频源制式,中断会触发,在dmesg里会看到中断处理函数里对0x0004寄存器的读写结果。

就先介绍到这里吧,ambarella H2平台坑真的很多,希望你们在以后的开发过程中不会用到这个平台的东西,多用用海思平台,支持国产,O(∩_∩)O哈哈~

最新文章

  1. word转化jpg
  2. 第二篇 基于.net搭建热插拔式web框架(沙箱的构建)
  3. 艺萌TCP文件传输及自动更新系统介绍(TCP文件传输)(四)
  4. 昨天晚上画了个带apple的图:ide插件与php和xdebug通信原理图,周末写1个调试器。
  5. .NET微信公众号开发-1.0初始微信公众号
  6. 【HDU 2203】亲和串
  7. CRT团队组员博客地址统计
  8. Vue.js中Directive知识
  9. C# dynamic类型
  10. mysql批量上传数据
  11. 实战DeviceIoControl 之三:制作磁盘镜像文件
  12. 20165230 2017-2018-2 《Java程序设计》第2周学习总结
  13. HBASE基础知识总结
  14. Python selenium ActionChains 基本操作
  15. JS模态框 简单案例
  16. 2017 Russian Code Cup (RCC 17), Final Round
  17. hide server info
  18. Docker Basic
  19. Win10 开启便签快捷键
  20. tortoise svn 忽略bin、obj等文件夹

热门文章

  1. 【SQL SERVER】索引
  2. SpringBoot 集成ehcache
  3. lvs + keepalived + gninx 高性能负载均衡
  4. Ajax-使用教程
  5. 纯css实现图片或者页面变灰色
  6. 4.用IntelliJ IDEA 创建Maven Web
  7. C Cow XOR 奶牛异或
  8. Python 【基础面试题】
  9. 测试Activity和Fragment的生命周期
  10. flask-文件上传的使用