zoukankan      html  css  js  c++  java
  • 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@01 {
                    compatible = "ambarella,ambvin";
                    reg = <0x3B>;    /* slave address */
                    interrupt-parent = <&gpio>;
                    interrupts = <26 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=0;
    unsigned int g_cap_cap_w=0;
    unsigned int g_cap_cap_h=0;
    
    static int w = 1920;
    MODULE_PARM_DESC(w, "video input width");
    module_param(w, int, S_IRUGO);
    
    static int h = 1080;
    MODULE_PARM_DESC(h, "video input height");
    module_param(h, int, S_IRUGO);
    
    static int p = 1;
    MODULE_PARM_DESC(p, "video input format");
    module_param(p, int, S_IRUGO);
    
    static int bit = 10;
    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 = 0;
    
    static int dummyfpga_set_audio_mode_width_height(struct vin_device *vdev, u32 width, u32 height)
    {
        g_audio_mode = 1;
        g_cap_cap_w = width;
        g_cap_cap_h = height;
    
        return 0;
    }
    
    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, 0, sizeof (dummyfpga_config));
    
        dummyfpga_config.sensor_id = GENERIC_SENSOR;
        dummyfpga_config.interface_type = SENSOR_PARALLEL_LVDS;
        if (bit == 10) {
            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 == 1) {    
            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 
    ", format->video_mode);
            return -1;
        }
    */
            dummyfpga_config.cap_win.x = 0;
            dummyfpga_config.cap_win.y = 0;
            dummyfpga_config.cap_win.width = w;
            dummyfpga_config.cap_win.height = h;
    
            printk("cap_win_height %d video_format %d 
    ", 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 < 0)
            return rval;
    
        printk("############### Debug: dummyfpga_set_format ##############
    ");
        return 0;
    }
    
    static int dummyfpga_get_format(struct vin_device *vdev)
    {
            //int rval;
    
            vdev->formats->video_mode = AMBA_VIDEO_MODE_AUTO;
            vdev->formats->def_start_x = 0;//pinfo->cap_start_x;
            vdev->formats->def_start_y = 0;//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 == 1) {
                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 == 10) {
                vdev->formats->bits = 10;
            } else {
                vdev->formats->bits = 8;//pinfo->bit_resolution;
            }
            //format->sync_start = pinfo->sync_start;
            printk("############### Debug: dummyfpga_get_format ##############
    ");
            return 0;
    }
    
    static int dummyfpga_init_device(struct vin_device *vdev)
    {
        vin_info("DUMMYFPGA Init
    ");
        return 0;
    }
                    
    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 = 0;
            vdev = ambarella_vin_create_device(ambdev->name,
                            DECODER_GS2970,  0);
            if (!vdev)
                    return -ENOMEM;
    
            vdev->intf_id = 0;
            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 < 0) {
                    ambarella_vin_free_device(vdev);
                    return rval;
            }
    
            ambpriv_set_drvdata(ambdev, vdev);
    
            vin_info("Dummyfpga Init, with LVDS I/F
    ");
            return 0;
    }
    
    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 0;
    }
    
    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[6];
        struct i2c_client *client = dev->client;
    
        pbuf[0] = subaddr[0];
        pbuf[1] = subaddr[1];
        pbuf[2] = data[0];
        pbuf[3] = data[1];
        pbuf[4] = data[2];
        pbuf[5] = data[3];
    
        rval = i2c_master_send(client, pbuf, 6);
        if (rval < 0) {
            vin_error("addr w failed(%d): [0x%x%x]
    ", rval, subaddr[0], subaddr[1]);
            return rval;
        }
    
        return 0;
    }
    
    static int xilinx_i2c_read_4bytes(struct xilinx_dev *dev, u8 subaddr[], u8 data[])
    {
        int rval;
        struct i2c_msg msgs[2];
        struct i2c_client *client = dev->client;
    
        msgs[0].len   = 2;
        msgs[0].addr  = client->addr;
        msgs[0].flags = client->flags;
        //msgs[0].buf   = &subaddr[0];
        msgs[0].buf   = subaddr;
    
        msgs[1].len   = 4;
        msgs[1].addr  = client->addr;
        msgs[1].flags = client->flags | I2C_M_RD;
        //msgs[1].buf   = &data[0];
        msgs[1].buf   = data;
    
        rval = i2c_transfer(client->adapter, msgs, 2);
        if (rval < 0) {
            vin_error("addr r failed(%d): [0x%x%x]
    ", rval, subaddr[0], subaddr[1]);
            return rval;
        }
    
        return 0;
    }
    
    static int i2c_test_write(struct xilinx_dev *dev)
    {
        int rval;
        u8 subaddr[2];
        u8 data[4];
    
        subaddr[0] = 0x00;
        subaddr[1] = 0x04;
        data[0] = 0x12;
        data[1] = 0x34;
        data[2] = 0x56;
        data[3] = 0x78;
        rval = xilinx_i2c_write_4bytes(dev, subaddr, data);
        if (rval < 0)
            return rval;
    
        return 0;
    }
    
    static int i2c_test_read(struct xilinx_dev *dev)
    {
        int rval;
        u8 subaddr[2];
        u8 data[4];
        
        subaddr[0] = 0x00;
        subaddr[1] = 0x04;
        rval = xilinx_i2c_read_4bytes(dev, subaddr, data);
        if (rval < 0)
            return rval;
        printk("0x0004=%02x%02x%02x%02x
    ", data[0], data[1], data[2], data[3]);
        
        subaddr[0] = 0x00;
        subaddr[1] = 0x00;
        rval = xilinx_i2c_read_4bytes(dev, subaddr, data);
        if (rval < 0)
            return rval;
        printk("0x0000=%02x%02x%02x%02x
    ", data[0], data[1], data[2], data[3]);
    
        return 0;
    }
    
    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, 1);
        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, 0);
    
        printk("xilinx_i2c_probe
    ");
        if (xilinx_cdev_major) 
            ret = register_chrdev_region(devno, 1, "xilinx_cdev");
        else {
            ret = alloc_chrdev_region(&devno, 0, 1, "xilinx_cdev");
            xilinx_cdev_major = MAJOR(devno);
        }
        if (ret < 0)
            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, 0);
        xilinx_devp->cls = class_create(THIS_MODULE, "xilinx_class");
        if (IS_ERR(xilinx_devp->cls)) {
            printk("Err: failed in creating xilinx cdev class
    ");
            return -1;
        }
        device_create(xilinx_devp->cls, NULL, MKDEV(xilinx_cdev_major, 0), 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
    ", client->irq, ret);
            return ret;
        } else 
            printk("request irq:%d
    ", client->irq);
    
        mutex_init(&xilinx_devp->rw_mutex);
    
        //i2c_test_write(xilinx_devp);
        //i2c_test_read(xilinx_devp);
    
    fail_malloc:
        unregister_chrdev_region(devno, 1);
        return ret;
    }
    
    static int xilinx_i2c_remove(struct i2c_client *client)
    {
        struct xilinx_dev *xilinx_devp;
        dev_t devno = MKDEV(xilinx_cdev_major, 0);
        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, 1);
        
        return 0;
    }
    
    static const struct i2c_device_id xilinx_idtable[] = {
        {"xilinx", 0},
        {},
    };
    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 = 0;
    
        rval = i2c_add_driver(&i2c_driver_xilinx);
        if (rval < 0) {
            printk("add xilinx i2c driver failed
    ");
            return rval;
        }
    
        dummyfpga_device = ambpriv_create_bundle(&dummyfpga_driver, NULL, -1, NULL, -1);
        if (IS_ERR(dummyfpga_device))
            rval = PTR_ERR(dummyfpga_device);
        return 0;
    }
    
    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哈哈~

  • 相关阅读:
    CSS3详解:transform
    js解决checkbox全选和反选的问题
    Scroll文字滚动js
    PAT乙级 解题目录
    PAT 1005 继续(3n+1)猜想 (25)(代码)
    PAT 1004 成绩排名 (20)(代码)
    PAT 1002 写出这个数 (20)(代码)
    PAT 1001 害死人不偿命的(3n+1)猜想 (15)(C++&JAVA&Python)
    PAT 1045 快速排序(25)(STL-set+思路+测试点分析)
    PAT 1050 螺旋矩阵(25)(代码)
  • 原文地址:https://www.cnblogs.com/wanglouxiaozi/p/11759961.html
Copyright © 2011-2022 走看看