ambarella H2平台fpga捕捉卡驱动案例
公司最近开发的一款产品用到了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哈哈~
ambarella H2平台fpga捕捉卡驱动案例的更多相关文章
- amba H2平台用PWM控制LCD背光
ambarella H2系列Soc的GPIO口能作PWM使用的个数有限(GPIO0-GPIO3),从PRM里GPIO: Function Selection章节可以得到如何配置GPIO为PWM功能. ...
- 芯灵思SinlinxA33开发板 Linux平台总线设备驱动
1.什么是platform(平台)总线? 相对于USB.PCI.I2C.SPI等物理总线来说,platform总线是一种虚拟.抽象出来的总线,实际中并不存在这样的总线. 那为什么需要platform总 ...
- Linux平台总线设备驱动
1. 平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备(没有挂到真实总线的设备)与驱动进行了管理,这样提高了程序的可移植性. 2. 平台总 ...
- Android平台开发-WIFI 驱动移植 -- 详细
一.WIFI的基本架构(代码路径) 1.WIFI Settings应用程序: packages/apps/Settings/src/com/android/settings/wif ...
- 嵌入式Linux驱动案例之中的一个
前几天解决一个嵌入式Linux驱动问题,做为一个案例进行记录. 本案例是一个CPU通过LocalBus总线訪问外围一个设备,详细设备是一个DSP器件.在实际应用中,性能要求非常高,对数据訪问速度提出比 ...
- Java版 家政服务 社区服务 家装服务平台 源码 有案例 可定制
产品说明: 家装服务平台.社区服务平台.服务类型的平台--公司成熟产品 包括工匠注册.资质认证.发布服务产品.会员注册.预约服务.工匠定价.执行服务.服务完毕填写工作日志上传现场照片.会员确认服务.返 ...
- ambarella H2 添加文件到ext4文件系统
方法1: ambarella/rootfs目录下有skeleton(骨架)目录,此目录下就是文件系统的各个目录, [root@jz4775dev]# ls skeleton/ bin debug de ...
- 案例分析:大数据平台技术方案及案例(ppt)
大数据平台是为了计算,现今社会所产生的越来越大的数据量,以存储.运算.展现作为目的的平台.大数据技术是指从各种各样类型的数据中,快速获得有价值信息的能力.适用于大数据的技术,包括大规模并行处理(MPP ...
- USB-Blaster CPLD FPGA Intel 驱动安装不上的问题,文件的哈希值不在指定的目录文件中,的解决办法,其实很简单
intel的官网的驱动安装文档: https://www.intel.com/content/www/us/en/programmable/support/support-resources/down ...
随机推荐
- 电脑网络诊断显示Win10无法与设备或资源(DNS)通信解决办法
最近是做多错多还是人有点儿衰神附体,软件,电脑系统,各种问题层出不穷,今天早上打开电脑发现不少软件都无法联网,神马百度商桥,腾讯浏览器,百度云...昨天百度商桥打不开还以为是软件出了问题,因为火狐浏览 ...
- Activiti网关--并行网关
1.什么是并行网关 并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进 入和外出顺序流的: fork 分支: 并行后的所有外出顺序流,为每个顺序流都创建一个并发分支. ...
- Docker常用yml
GitLib version: '3.1' services: web: image: 'twang2218/gitlab-ce-zh:11.0.5' restart: always hostname ...
- MFC之使用blat发送邮件
blat的下载地址:http://www.blat.net 我用它进行了smtp服务的邮件发送.这里我使用的qq邮箱,qq邮箱使用的密码是授权码,可以再qq邮箱设置里面开启smtp服务.下载下来是文件 ...
- 下载腾讯视频mp4格式
import time import subprocess import argparse def command(cmd, timeout=60): ''' :param cmd: 执行命令cmd, ...
- LVS 集群与存储《路由转发》
LVS 集群与存储<路由转发> 集群简介 u 什么是集群 • 一组通过高 ...
- fastfdfs搭配nginx
fastfdfs搭配nginx 下载fastdfs-nginx-module 模块 wget https://github.com/happyfish100/fastdfs-nginx-module/ ...
- go 错误处理与测试
Go 没有像 Java 和 .NET 那样的 try/catch 异常机制:不能执行抛异常操作.但是有一套 defer-panic-and-recover 机制(参见 13.2-13.3 节). Go ...
- 安卓动画(Animation使用)
安卓的Animation视图动画的使用非常简单,并且对象适用于一般控件. 具体使用步骤如下. Button/TextView/EditText/ImageView/Bitmap ..... obj ...
- sprigboot 异常 Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].Tomc...
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start com ...