autochip videoin 驱动

目录

videoin_open_device

 data_path_open

data_path_do_open_lock

 vis_buffer_done_register注册回调函数

videoin_s_ctrl

videoin_set_fd_buffer

videoin_set_fd_buffer_ext

videoin_buf_fd_to_dma_buf

data_path_que_buffer

vis_buffer_queue

videoin_start_streaming

mipi_kthread

data_path_reset

 data_path_flush_buffer

 mipi_flush_buffer

Enable MIPI clk cfg 资源

初始化mipi 注册中断函数

调用解串器驱动进行start_stream

mipi_irq_handler 中断函数处理

将free buf 插入到vis_list_buf

将dma buf物理地址写入到mipi 寄存器中

从vis_list_buf中获取free buf

创建dma_buf

 videoin_set_fd_buffer_ext

Enqueue buffer

将dma buf 添加到vis_list_buf  中断时获取buf 

vb2_core_qbuf &vb2_dqbuf

Enqueue buffer

填充vb2_buffer

Videoin 中断处理

Mipi 中断触发

mipi_buffer_done_handler

buffer_done_handler_without_lock

callback_to_v4l2

 videoin_buffer_complete

 videoin_process_buffer_complete 获取plane buffer的数据

 vb2_buffer_done 唤醒dequeue buffer

Dequeue buffer 

 vb2_core_dqbuf

 __vb2_get_done_vb

 __vb2_wait_for_done_vb 等待vb2_buffer_done唤醒

fill_v4l2_buffer填充buffer数据到用户空间,


videoin_open_device

/*
 * open device only in first times.always channel 0 or channel full
 */
static int videoin_open_device(struct logical_device *lg_dev)
{
        int ret = -1;
        int ch_idx = 0;

        vss_printk(VSS_LOG_INFO, "enter dayu 0.0.3.0 is_avm_mode %d",lg_dev->is_avm_mode);
        mutex_lock(&(lg_dev->lock));
        lg_dev->open_count ++;
        if (1 == lg_dev->open_count) {
                lg_dev->state = VIDEOIN_CHANNEL_STATE_FILE_OPENED;
        }
        if (lg_dev->state == VIDEOIN_CHANNEL_STATE_FILE_OPENED
                && 1 == lg_dev->open_count) {
                ret = data_path_open(&lg_dev->hw_path);
                if (ret < 0) {
                        mutex_unlock(&lg_dev->lock);
                        vss_printk(VSS_LOG_ERR, "data_path_open error");
                        return -EFAULT;
                }
                lg_dev->state = VIDEOIN_CHANNEL_STATE_READY;
        } else {
                vss_printk(VSS_LOG_INFO, "ch->state = %d ch->open_count = %d ",
                                        lg_dev->state,lg_dev->open_count);
        }
        data_path_get_channel_source_info(&lg_dev->hw_path->hw_ch[lg_dev->ch_id], lg_dev->port_id);
        if (lg_dev->is_avm_mode) {
                for (ch_idx = 1; ch_idx < CHANNEL_ID_MAX; ch_idx++) {
                        data_path_get_channel_source_info(&lg_dev->hw_path->hw_ch[ch_idx], lg_dev->port_id);
                }
        }
        mutex_unlock(&(lg_dev->lock));
        return ret;
}

 data_path_open

/*
 * limit : one channel only do data_path_open one times
 */
int data_path_open(struct hw_data_path **p_hw_path)
{
        int ret = 0;
        struct hw_data_path *hw_path = *p_hw_path;
        enum user_data_path dp =  hw_path->dp;
        vss_printk(VSS_LOG_INFO, "enter");
        mutex_lock(&hw_path->lock);
        ret = data_path_inc_open_count(p_hw_path);
        if (ret < 0) {
                mutex_unlock(&hw_path->lock);
                vss_printk(VSS_LOG_ERR, "do data_path_inc_open_count failed");
                return -1;
        }
        if (data_path_can_open_hw(hw_path)) {
                int ch_idx = 0;
                /*
                * one data path will do one times
                */
                ret = data_path_do_open_lock(hw_path);
                if (ret < 0) {
                        vss_printk(VSS_LOG_ERR, "dp(%d) data_path_do_open_lock faild ",dp);
                        ret = data_path_dec_open_count(hw_path);
                        if (ret < 0) {
                                vss_printk(VSS_LOG_ERR, "dp(%d) data_path_dec_open_count failed",dp);
                        }
                        data_path_reset_hw_path(hw_path);
                        mutex_unlock(&hw_path->lock);
                        return -1;
                } else {
                        vss_printk(VSS_LOG_INFO, "dp(%d) open success",dp);
                }
                for (ch_idx = CHANNEL_ID_0 ; ch_idx < CHANNEL_ID_MAX; ch_idx++) {
                        if (hw_path->hw_ch[ch_idx]->is_channel_enable) {
                        //初始化vis_list_buf
                                vis_init_dp_channel(dp, ch_idx);
                        }
                }
                ret = sensor_drv_open(dp,CHANNEL_ID_0);
                if (ret < 0) {
                        hw_path->hw_status = VIDEOIN_STATUS_IDLE;
                        mutex_unlock(&hw_path->lock);
                        vss_printk(VSS_LOG_ERR, "sensor_drv_open error\n");
                        return -1;
                }
        } else {
                vss_printk(VSS_LOG_INFO, "dp(%d) has been opend %d times",dp,hw_path->opened_count);
        }
        mutex_unlock(&hw_path->lock);
        vss_printk(VSS_LOG_INFO, "leave");
        return ret;
}

data_path_do_open_lock

初始化g_vis_ctrl

注册回调函数

videoin_buffer_complete

videoin_make_vip_event

void vis_init_dp_channel(enum user_data_path dp,enum channel_id ch_id)
{
        int port_idx = 0;
        int vis_buf_idx = 0;
        struct list_buf *vis_list_buf =
                &g_vis_ctrl[dp].vis_output_hal[ch_id].vis_list_buf[DMA_PORT_1];
        debug_queue[dp][ch_id] = 0;
        vss_printk(VSS_LOG_INFO, "enter with dp(%d) ch_id(%d) ", dp,ch_id);
        for (port_idx = 0; port_idx < DMA_PORT_MAX; port_idx++) {
                INIT_LIST_HEAD(&vis_list_buf[port_idx].todo_list);
                spin_lock_init(&vis_list_buf[port_idx].todo_list_lock);
                vis_list_buf[port_idx].cur_todo_buf_idx = 0;
                memset(&vis_list_buf[port_idx].vis_buf_entry[vis_buf_idx], 0,
                        sizeof(vis_list_buf[port_idx].vis_buf_entry));
        }
        g_vis_ctrl[dp].vip_drop[ch_id] = true;
        g_vis_ctrl[dp].is_switch_source[ch_id] = false;
        vss_printk(VSS_LOG_INFO, "leave with dp(%d) ch_id(%d) ", dp,ch_id);
}
EXPORT_SYMBOL(vis_init_dp_channel);

 vis_buffer_done_register注册回调函数

int vis_buffer_done_register(enum user_data_path dp, vis_pfn_buffer_done buffer_done)
{
        int ret = VSS_NO_ERROR;

        vss_printk(VSS_LOG_DEBUG, "Enter\n");

        if (buffer_done == NULL) {
                vss_printk(VSS_LOG_ERR, "notify function is NULL!\n");
                ret = -VSS_INVALID_PARAM;
                goto ERR;
        }

        g_vis_ctrl[dp].pfn_buffer_done = buffer_done;

        vss_printk(VSS_LOG_DEBUG, "Leave\n");

        return ret;
ERR:
        vss_printk(VSS_LOG_DEBUG, "Leave\n");

        return ret;
}
EXPORT_SYMBOL(vis_buffer_done_register);

videoin_s_ctrl

static int videoin_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
{
        struct video_device *vdev = video_devdata(file);
        struct logical_device *lg_dev = video_get_drvdata(vdev);
        int ret = 0;
        vss_printk(VSS_LOG_DEBUG, "enter with id(%d) value(%d)", ctrl->id, ctrl->value);

        switch (ctrl->id) {
        case V4L2_CID_SET_FD:
                vss_printk(VSS_LOG_DEBUG, "V4L2_CID_SET_FD with device(%d)", lg_dev->port_dev);
                if (videoin_set_fd_buffer(vdev, lg_dev, ctrl->value)) {
                        vss_printk(VSS_LOG_ERR, "videoin_set_fd_buffer error!");
                        return -EFAULT;
                }

videoin_set_fd_buffer

static int videoin_set_fd_buffer(struct video_device *vdev, struct logical_device *lg_dev, int value)
{
        int buf_fd = value & 0xFFFFFF;
        u8 buf_idx = (value >> 24) & 0xFF;
        return videoin_set_fd_buffer_ext(vdev, lg_dev, lg_dev->port_id, buf_idx, buf_fd);
}

videoin_set_fd_buffer_ext

//分配流式DMA空间地址 p_dma_buf->dma_address p_dma_buf->y_virt

//将分配的dma的虚拟地址保存到hw_ch->output_buf_addr中

static int videoin_set_fd_buffer_ext(struct video_device *vdev,
                                                struct logical_device *lg_dev,
                                                                int stream_id,
                                                                int buf_idx,
                                                                int buf_fd)
{
        int ret = -1;
        struct videoin_stream_info *pstream_info = NULL;
        struct videoin_dma_buf_info *p_dma_buf = NULL;
        struct capture_priv priv_data;
        struct device *dev = vdev->v4l2_dev->dev;
        struct hw_channel *hw_ch = lg_dev->hw_path->hw_ch[lg_dev->ch_id];
        u16 width = hw_ch->output_info[stream_id].out_width;
        u16 height = hw_ch->output_info[stream_id].out_height;
        u16 stride_y = 0;
        u16 stride_c = 0;
        u32 offset_line = 0;
        u8 data_fmt = hw_ch->output_info[stream_id].data_fmt;
        struct videoin_buf_addr *buf_addr = &hw_ch->output_buf_addr[stream_id][buf_idx];

        vss_printk(VSS_LOG_DEBUG, "buf_fd is %d, buf_idx is %d, stream_id is %d",
                                                buf_fd, buf_idx, stream_id);
        if (STREAM_CNT_MAX <= stream_id) {
                vss_printk(VSS_LOG_ERR, "error with buf_idx(%d) stream_id(%d)",
                                                        buf_idx, stream_id);
                return -EINVAL;
        }
        pstream_info = &lg_dev->video_port_info->stream_info[stream_id];
        p_dma_buf = &pstream_info->dma_output_buf[buf_idx];
        stride_y = pstream_info->line_stride[0];
        stride_c = pstream_info->line_stride[1];
        offset_line = pstream_info->port_info.offset_line;
        if (hw_ch->is_vip_enable) {
                dev = videoin_get_vip_dev();
        }
        //将user buf指向lg_dev的dma_output_buf
        ret = videoin_buf_fd_to_dma_buf(p_dma_buf,dev,buf_fd);
        if(ret < 0) {
                vss_printk(VSS_LOG_ERR, " analyse fd -> buf_address fail");
                return -EINVAL;
        }
        //将buf_addr保存p_dma_buf地址
        buf_addr->y_virt = p_dma_buf->y_virt;
        //将dma addr 转换成YUV的地址
        data_path_dma_addr_to_YUV_addr(width, height, data_fmt, offset_line, stride_y, stride_c,
                p_dma_buf->dma_address, lg_dev->hw_path->is_avm_mode[stream_id], buf_addr);
        vss_printk(VSS_LOG_DEBUG, "stream_id(%d) dma addr(0x%lx) virt addr(0x%lx) offset_line(%d)",
                stream_id, (unsigned long int)buf_addr->y, buf_addr->y_virt, offset_line);
        //2. que this buffer to vis accorrding to hw path
        memset(&priv_data, 0, sizeof(struct capture_priv));
        //绑定stream id 和buf index
        priv_data.stream_id = stream_id;
        priv_data.buf_idx = buf_idx;
        if (data_path_que_buffer(priv_data, &lg_dev->hw_path->hw_ch[lg_dev->ch_id]) < 0) {
                vss_printk(VSS_LOG_ERR, "mipi2_path_que_buffer error");
                goto fail;
        }
        pstream_info->is_buf_ready = true;
        return 0;

fail:
        videoin_free_dma_buffer(p_dma_buf);
        return -EFAULT;
}

videoin_buf_fd_to_dma_buf

分配流式DMA空间地址 p_dma_buf->dma_address p_dma_buf->y_virt

将分配的dma的虚拟地址保存到hw_ch->output_buf_addr中

int videoin_buf_fd_to_dma_buf(struct videoin_dma_buf_info *dma_buf_info,
                                                struct device *dev, int buf_fd)
{
#ifndef CONFIG_AVIN_USE_RESERVED_MEM
        int i;
        int count = 0;
        struct scatterlist *sg;
#endif
        struct videoin_dma_buf_info *p_dma_buf_info = dma_buf_info;
        struct sg_table *sg_table_temp = NULL;
        //通过fd 获取dma_buf
        p_dma_buf_info->dma_buffer = dma_buf_get(buf_fd);
        if (IS_ERR_OR_NULL(p_dma_buf_info->dma_buffer)) {
                vss_printk(VSS_LOG_ERR, "dma_buf_get error with buf_fd(%d) ",
                                                                        buf_fd);
                dma_buf_info->dma_buffer = NULL;
                return -EFAULT;
        }
        //将dma_buffer与dev绑定
        p_dma_buf_info->dma_attachment = dma_buf_attach(dma_buf_info->dma_buffer,
                                                                        dev);
        if (NULL == p_dma_buf_info->dma_attachment) {
                vss_printk(VSS_LOG_ERR, "dma_buf_attach error with buf_fd(%d)",
                                                                        buf_fd);
                goto fail;
        }
        //生成sg_table
        sg_table_temp = dma_buf_map_attachment(p_dma_buf_info->dma_attachment,
                                                        DMA_BIDIRECTIONAL);
        if (NULL == sg_table_temp) {
                vss_printk(VSS_LOG_ERR, "dma_buf_map_attachment error  ");
                goto fail;
        }
        p_dma_buf_info->dma_sg_table = clone_sgt(sg_table_temp);
        //创建流式DMA
        count = dma_map_sg(dev, p_dma_buf_info->dma_sg_table->sgl,
                                p_dma_buf_info->dma_sg_table->nents,
                                DMA_BIDIRECTIONAL);
        vss_printk(VSS_LOG_DEBUG, "dev(%p) count=%d  nents=%d ",dev, count,
                                        p_dma_buf_info->dma_sg_table->nents);
        for_each_sg(p_dma_buf_info->dma_sg_table->sgl, sg,
                                p_dma_buf_info->dma_sg_table->nents, i) {
                vss_printk(VSS_LOG_DEBUG, " length=%u dma_addr=0x%lx phy=0x%lx",
                                        sg->length,
                                        (long unsigned int)sg_dma_address(sg),
                                        (long unsigned int)sg_phys(sg));
        }
        //获取物理地址
        p_dma_buf_info->dma_address = sg_dma_address(
                                        p_dma_buf_info->dma_sg_table->sgl);
       //将物理地址转换成虚拟地址
        p_dma_buf_info->y_virt = (uintptr_t)sg_virt(p_dma_buf_info->dma_sg_table->sgl);
        return 0;
fail:
        videoin_free_dma_buffer(p_dma_buf_info);
        return -1;
}

data_path_que_buffer

int data_path_que_buffer(struct capture_priv priv_data, struct hw_channel **p_hw_ch)
{
        u8 stream_id = priv_data.stream_id;
        u8 buf_idx = priv_data.buf_idx;
        struct hw_channel *hw_ch = *p_hw_ch;
        enum channel_id ch_id = hw_ch->ch_id;
        struct hw_data_path *hw_path = container_of(p_hw_ch ,struct hw_data_path, hw_ch[ch_id]);
        enum user_data_path dp = hw_path->dp;
        struct port_buffer vis_buf;
        struct videoin_buf_addr *output_buf_addr = NULL;

        mutex_lock(&hw_path->lock);
        if ((STREAM_BUF_CNT_MAX == buf_idx) && (STREAM_CNT_MAX == stream_id)) {
                mutex_unlock(&hw_path->lock);
                vss_printk(VSS_LOG_DEBUG, "buf_idx=CNT_MAX & stream_id=CNT_MAX, means this queue just for v4l2 buffer list\n");
                return 0;
        }
        switch(hw_path->hw_status)
        {
                case VIDEOIN_STATUS_READY:
                case VIDEOIN_STATUS_STARTED:
                        break;
                default:
                        vss_printk(VSS_LOG_WARNING, "dp(%d) queue buffer in wrong status(%d)", dp, hw_path->hw_status);
                        mutex_unlock(&hw_path->lock);
                        return -1;
        }
        if ((STREAM_BUF_CNT_MAX == buf_idx) || (STREAM_CNT_MAX == stream_id)) {
                mutex_unlock(&hw_path->lock);
                vss_printk(VSS_LOG_DEBUG, " buf_idx(%d) stream_id(%d)\n", buf_idx, stream_id);
                return 0;
        }
        output_buf_addr = &(hw_ch->output_buf_addr[stream_id][0]);
        if (0 == output_buf_addr[buf_idx].y) {
                mutex_unlock(&hw_path->lock);
                vss_printk(VSS_LOG_ERR, "stream(%d), idx(%d) addr is null, may not set_fd yet", stream_id, buf_idx);
                return -1;
        }
        memset(&vis_buf, 0, sizeof(struct port_buffer));
        vis_buf.plane_cnt   = output_buf_addr[buf_idx].plane_cnt;
        vis_buf.vir_addr[0] = output_buf_addr[buf_idx].y;
        vis_buf.vir_addr[1] = output_buf_addr[buf_idx].cb;
        vis_buf.vir_addr[2] = output_buf_addr[buf_idx].cr;
        vis_buf.y_virt      = output_buf_addr[buf_idx].y_virt;
        vis_buf.buf_idx     = buf_idx;
        vss_printk(VSS_LOG_DEBUG, "vis_buf.buf_idx=%d  pstream_info->port_id=%d  ch_id=%d y=0x%lx cb=0x%lx cr=0x%lx\n",
                                        vis_buf.buf_idx,stream_id,ch_id,vis_buf.vir_addr[0],vis_buf.vir_addr[1],vis_buf.vir_addr[2]);
        if (vis_buffer_queue(dp, stream_id, &vis_buf,ch_id)) {
                vss_printk(VSS_LOG_WARNING, "vis_buffer_queue error\n");
        }
        mutex_unlock(&hw_path->lock);
        return 0;
}

vis_buffer_queue

将buf 插入到vis_output_hal[ch_id].vis_list_buf

int vis_buffer_queue(enum user_data_path dp, enum dma_port port, struct port_buffer *user_buf,enum channel_id ch_id)
{
        int ch_id_idx = 0;
        struct buf_info buf;
        vss_printk(VSS_LOG_DEBUG, "Enter\n");
        if ( 0== (debug_queue[dp][ch_id] % QUEUE_DEBUG_INTERVAL))
        {
                vss_printk(VSS_LOG_INFO, "dp(%d) ch_id(%d) queue count(%d)!\n", dp, ch_id, debug_queue[dp][ch_id]);
        }
        debug_queue[dp][ch_id] ++;

        switch (g_vis_ctrl[dp].vis_output_type[port])
        {
                case OUTPUT_TYPE_IND:
                       // 将lg_dev 的 userbuf数据保存到buf中
                        memset(&buf,0,sizeof(struct buf_info));
                        buf.y_addr    = user_buf->vir_addr[0];
                        buf.cb_addr   = user_buf->vir_addr[1];
                        buf.cr_addr   = user_buf->vir_addr[2];
                        buf.y_addr_virt = user_buf->y_virt;
                        buf.plane_cnt = user_buf->plane_cnt;
                        buf.buf_idx   = user_buf->buf_idx;
                        buf.ch_id     = ch_id;
                        buf.auser = 0;
                        buf.dp = dp;
                        buf.stream_id = port;
                        buf.state = BUF_STATE_FREE;
                        list_push(&g_vis_ctrl[dp].vis_output_hal[ch_id].vis_list_buf[port], &buf);
                        vis_debug_fps(dp, ch_id, port);
                        if (g_vis_ctrl[dp].is_vip_enable[ch_id]) {
                                vis_debug_vip_fps(dp, ch_id);
                        }
                        break;
                case OUTPUT_TYPE_FULL:
                        {
#ifdef DO_TEST
                                struct test_sequence *temp = NULL;
                                struct test_sequence *temp1 = NULL;
#endif
                                struct test_sequence *list_entry = NULL;
                                unsigned long flags;

                                spin_lock_irqsave(&avm_list_irq_lock, flags);
                                memset(&g_test_seq[dp][g_list_conunt[dp][port] + VIS_BUF_CNT * port],0,sizeof(struct test_sequence));
                                list_entry = &g_test_seq[dp][g_list_conunt[dp][port] + VIS_BUF_CNT * port];
                                list_entry->index = user_buf->buf_idx;
                                list_add_tail(&list_entry->list,&g_sequence_list[dp][port]);
                                for (ch_id_idx = CHANNEL_ID_0; ch_id_idx < CHANNEL_ID_MAX; ch_id_idx++)
                                {
                                        vss_printk(VSS_LOG_DEBUG, "ch_id_idx=%d ",ch_id_idx);
                                         // 将lg_dev 的 userbuf数据保存到buf中
                                        memset(&buf,0,sizeof(struct buf_info));
                                        buf.y_addr = user_buf->vir_addr[0]
                + g_vis_ctrl[dp].vis_output_hal[ch_id_idx].ch_buf_off[port][ch_id_idx*3+0];
                                        buf.cb_addr = user_buf->vir_addr[1]
                + g_vis_ctrl[dp].vis_output_hal[ch_id_idx].ch_buf_off[port][ch_id_idx*3+1];
                                        buf.cr_addr = user_buf->vir_addr[2]
                + g_vis_ctrl[dp].vis_output_hal[ch_id_idx].ch_buf_off[port][ch_id_idx*3+2];
                                        buf.plane_cnt = user_buf->plane_cnt;
                                        buf.buf_idx   = user_buf->buf_idx;
                                        buf.ch_id = ch_id_idx;
                                        buf.auser = 0;
                                        buf.dp = dp;
                                        buf.stream_id = port;
                                        buf.state = BUF_STATE_FREE;
                                        list_push(&g_vis_ctrl[dp].vis_output_hal[ch_id_idx].vis_list_buf[port], &buf);
                                        vis_debug_fps(dp, ch_id_idx, port);
                                        if (g_vis_ctrl[dp].is_vip_enable[ch_id_idx]) {
                                                vis_debug_vip_fps(dp, ch_id_idx);
                                        }
                                        vss_printk(VSS_LOG_DEBUG, "buf_idx=%d stream_id=%d  ch_id=%d y=0x%lx cb=0x%lx cr=0x%lx",
                                        buf.buf_idx, buf.stream_id, buf.ch_id, buf.y_addr, buf.cb_addr, buf.cr_addr);
                                }
                                vss_printk(VSS_LOG_DEBUG, " push idx: %u   ",user_buf->buf_idx);
#ifdef DO_TEST
                                list_for_each_entry_safe(temp, temp1, &g_sequence_list[dp][port], list)
                                {
                                        vss_printk(VSS_LOG_INFO, " idx: %u       ",temp->index);
                                }
#endif
                                g_list_conunt[dp][port]++;
                                g_list_conunt[dp][port] %= VIS_BUF_CNT;
                                spin_unlock_irqrestore(&avm_list_irq_lock, flags);
                                g_buf_max_cnt[dp][port] = max(g_buf_max_cnt[dp][port],buf.buf_idx);
                        }
                        break;
                default:
                        return -1;
        }
        vss_printk(VSS_LOG_DEBUG, "Leave \n");
        return VSS_NO_ERROR;
}
EXPORT_SYMBOL(vis_buffer_queue);

videoin_start_streaming

创建线程初始化mipi

/**
* videoin_start_streaming : Starts the DMA engine for streaming
* @vb: ptr to vb2_buffer
* @count: number of buffers in driver
*/
static int videoin_start_streaming(struct vb2_queue *vq, unsigned int count)
{
        int ret = -1;
        struct logical_device *lg_dev = vb2_get_drv_priv(vq);
        enum channel_id ch_id = lg_dev->ch_id;
        struct hw_channel **hw_ch = &lg_dev->hw_path->hw_ch[ch_id];
        struct vb2_queue_mgr *queue_mgr = &lg_dev->queue_mgr[VIDEOIN_VIDEO_INDEX];
        struct videoin_cap_buffer *buf = NULL;
        struct videoin_cap_buffer *tmp = NULL;
        vss_printk(VSS_LOG_INFO, "enter, device_type(%d), buffer num(%d),state(%d)", lg_dev->port_dev, count, lg_dev->state);
        if (VIDEOIN_CHANNEL_STATE_FILE_OPENED == lg_dev->state){
                // HW is not power one. Power on first.
                videoin_open_device(lg_dev);
        }
        if (VIDEOIN_CHANNEL_STATE_READY != lg_dev->state) {
                vss_printk(VSS_LOG_ERR, "port_dev(%d) is in wrong state(%d). Can't start streaming!!!",
                                        lg_dev->port_dev, lg_dev->state);
                goto fail;
        }
        vss_printk(VSS_LOG_DEBUG, "is_vip_enable(%d) is_notify_alloc_buf(%d)",
                                        (*hw_ch)->is_vip_enable,(*hw_ch)->is_notify_alloc_buf);
        /* one channel has a queue_mgr*/
        queue_mgr->cur_frm = NULL;
        /* one channel only do data path start one times depend on ch->state*/
        //创建初始化线程 初始化MIPI
        ret = data_path_start(&lg_dev->hw_path);
        if (ret < 0) 
                vss_printk(VSS_LOG_ERR, "data_path_start error");
                goto fail;
        } else if (1 == ret) {
                data_path_enable_stream(hw_ch, lg_dev->port_id);
        }
        //wake_up_reset_path(MIPI_2);
        lg_dev->state = VIDEOIN_CHANNEL_STATE_STARTED;
        vss_printk(VSS_LOG_INFO, "leave, device_type(%d), buffer num(%d)  state(%d)", lg_dev->port_dev, count,lg_dev->state);
        return 0;
fail:
        list_for_each_entry_safe(buf, tmp, &queue_mgr->dma_queue, list) {
                list_del(&buf->list);
                vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
        }
        return -EFAULT;
}
 * one data path will do data_path_start one times
 */
int data_path_start(struct hw_data_path **p_hw_path)
{
        int ret = 0;
        int ch_idx = 0;
        struct hw_data_path *hw_path = *p_hw_path;
        enum user_data_path dp = hw_path->dp;
        bool is_used_four_channel = false;
        vss_printk(VSS_LOG_INFO, "enter dp(%d) will be startd in status(%d)", dp, hw_path->hw_status);
        mutex_lock(&hw_path->lock);
        data_path_inc_start_count(hw_path);
        if (!data_path_judge_can_do_start_hw(hw_path)) {
                mutex_unlock(&hw_path->lock);
                return 1;
        }
        switch (hw_path->hw_status)
        {
                case VIDEOIN_STATUS_READY:
                        break;
                case VIDEOIN_STATUS_STARTED:
                        vss_printk(VSS_LOG_WARNING, "dp(%d) has been started !", dp);
                        mutex_unlock(&hw_path->lock);
                        return 1;//1 modify started!
                default:
                        vss_printk(VSS_LOG_WARNING, "dp(%d) can't be started in status(%d)!", dp, hw_path->hw_status);
                        goto fail_start;;
        }
        if (NULL == hw_path->thread_handle) {
                //创建初始化MIPI的线程
                if (MIPI_1 == dp) {
                        hw_path->thread_handle = kthread_create(mipi_kthread, (void *)(hw_path), "mipi_thread1");
                } else if(MIPI_2 == dp){
                        hw_path->thread_handle = kthread_create(mipi_kthread, (void *)(hw_path), "mipi_thread2");
                } else {
                        hw_path->thread_handle = kthread_create(bt656_kthread, (void *)(hw_path), "bt656_kthread");
                }
                if (IS_ERR(hw_path->thread_handle)) {
                        hw_path->thread_handle = NULL;
                        vss_printk(VSS_LOG_ERR, "create thread function fail");
                } else {
                        wake_up_process(hw_path->thread_handle);
                }
        } else {
                vss_printk(VSS_LOG_ERR, "thread has been  created !!");
        }
#ifdef  SRC_USE_TPG
        if (BT656 == dp) {
                vis_tpg_set(dp, COLOR_BAR_720P30, true);
        }
        else {
                vis_tpg_set(dp, COLOR_BAR_720P30, true);
        }
#else
        vis_tpg_set(dp, COLOR_BAR_720P30, false);
#endif
        for (ch_idx = CHANNEL_ID_0 ; ch_idx < CHANNEL_ID_MAX; ch_idx++) {
                struct hw_channel *hw_ch = hw_path->hw_ch[ch_idx];
                if (hw_ch->is_vip_enable)
                        vis_vip_init_dp_channel(dp, ch_idx);
        }
        for (ch_idx = CHANNEL_ID_0; ch_idx < CHANNEL_ID_MAX; ch_idx++) {
                struct hw_channel *hw_ch = hw_path->hw_ch[ch_idx];
                if (!hw_ch->is_vip_enable) {
                        break;
                }
        }
        if (CHANNEL_ID_MAX == ch_idx) {
                is_used_four_channel = true;
        }
        /*
         *  default mipi and sensor config all the same
         */
         if (!hw_path->is_do_common_config) {
                ret = vis_bt656_mipi_init(dp,false);
                if (ret < 0) {
                        vss_printk(VSS_LOG_ERR, "vis_bt656_mipi_init error");
                        goto fail_start;;
                }
                ret = data_path_common_config(hw_path, false);
                if (ret < 0) {
                        vss_printk(VSS_LOG_ERR, "data_path_common_config error");
                        goto fail_start;;
                }
                hw_path->is_do_common_config = true;
        }
        for (ch_idx = CHANNEL_ID_0 ; ch_idx < CHANNEL_ID_MAX; ch_idx++) {
                struct hw_channel *hw_ch = hw_path->hw_ch[ch_idx];
                if (hw_ch->is_vip_enable && (!hw_ch->is_notify_alloc_buf)) {
                        struct vip_info info;
                        memset(&info, 0, sizeof(info));
                        info.evt_type = VIDEOIN_INIT_VIP;
                        info.is_avm_mode = false;
                        info.is_interlace = hw_ch->in_param.vis_in_param.is_interlace;
                        info.src_width = hw_ch->in_param.vis_in_param.width;
                        info.src_height = hw_ch->in_param.vis_in_param.height;
                        info.dp = hw_path->dp;
                        info.ch_id = ch_idx;
                        if (NTSC == hw_ch->in_param.vis_in_param.fmt) {
                                info.src_width = g_format_list[NTSC].width;
                                if (hw_ch->is_vip_enable) {
                                        info.src_height = g_format_list[NTSC].height * 2;
                                } else {
                                        info.src_height = g_format_list[NTSC].height;
                                }
                        } else if (PAL == hw_ch->in_param.vis_in_param.fmt){
                                info.src_width = g_format_list[PAL].width;
                                if (hw_ch->is_vip_enable) {
                                        info.src_height = g_format_list[PAL].height * 2;
                                } else {
                                        info.src_height = g_format_list[PAL].height;
                                }
                        }
                        if (info.is_interlace &&
                                (info.is_avm_mode || is_used_four_channel)) {
                                info.is_need_filed_detect = true;
                        }
                        if((BT656 == dp) && info.is_interlace && (hw_path->is_bt601)) {
                                info.is_need_filed_detect = true;
                        }
                        data_path_init_vip_buffer(hw_ch);
                        videoin_make_vip_event(&info);
                        hw_ch->is_notify_alloc_buf = true;
                        if (!hw_ch->vis_inter_buf.is_buf_ready){
                                ret = wait_event_interruptible_timeout(hw_path->vip_wq_alloc,
                                hw_ch->vis_inter_buf.is_buf_ready , msecs_to_jiffies(500));
                                if (ret == 0) {
                                        vss_printk(VSS_LOG_ERR, "timeout on prepare vip buf");
                                        hw_ch->is_vip_enable = false;
                                } else if (ret < 0) {
                                        vss_printk(VSS_LOG_ERR, "wait event error");
                                        hw_ch->is_vip_enable = false;
                                } else {
                                        vss_printk(VSS_LOG_INFO, "vip buf prepare done");
                                }
                        }
                }
        }
        data_path_config_pq(hw_path);
        for (ch_idx = CHANNEL_ID_0 ; ch_idx < CHANNEL_ID_MAX; ch_idx++) {
                if(hw_path->hw_ch[ch_idx]->is_channel_enable) {
                        data_path_channel_config(&hw_path->hw_ch[ch_idx]);
                }
        }
        ret = data_path_do_start_lock(hw_path);
        if (ret < 0) {
                vss_printk(VSS_LOG_ERR, "dp(%d) data_path_do_start_lock error!", dp);
                goto fail_start;
        }
        hw_path->hw_status = VIDEOIN_STATUS_STARTED;
        mutex_unlock(&hw_path->lock);
        vss_printk(VSS_LOG_INFO, "dp(%d) is started successfully.", dp);
        return ret;
fail_start:
        data_path_dec_start_count(hw_path);
        mutex_unlock(&hw_path->lock);
        vss_printk(VSS_LOG_DEBUG, "leave");
        return ret;
}

mipi_kthread 

static int mipi_kthread(void *param)
{
        struct hw_data_path *hw_path = (struct hw_data_path *)param;
        vss_printk(VSS_LOG_INFO, "enter");
        while(!kthread_should_stop()) {
                vss_printk(VSS_LOG_DEBUG, "mipi_kthread before wait");
                wait_event_interruptible(hw_path->reset_wq,(hw_path->reset_count > 0)
                                                                        || (hw_path->is_exit_reset_thread)
                                                                        || (hw_path->is_need_flush_buffer));
                vss_printk(VSS_LOG_DEBUG, "mipi_kthread after wait");
                if (hw_path->is_exit_reset_thread) {
                        break;
                }
                if (hw_path->is_stopped_with_no_signal && hw_path->is_need_flush_buffer) {
                        data_path_flush_buffer_with_request(hw_path);
                        msleep(30);
                        continue;
                }
                if (hw_path->reset_count > 0) {
                mutex_lock(&hw_path->lock);
                vis_set_channel_is_switch_source(hw_path->dp, 0, true);
                vis_set_channel_is_switch_source(hw_path->dp, 1, true);
                vis_set_channel_is_switch_source(hw_path->dp, 2, true);
                vis_set_channel_is_switch_source(hw_path->dp, 3, true);
                videoin_set_reset_status(hw_path->dp,true);
                if (VIDEOIN_STATUS_STARTED == hw_path->hw_status) {
                        vss_printk(VSS_LOG_INFO, "mipi_kthread doing reset enter");
                        if (mipi_exc_get_condition(hw_path)) {
                                data_path_reset_by_overflow(hw_path);
                        } else {
                                data_path_reset(hw_path);
                        }
            vss_printk(VSS_LOG_INFO, "mipi_kthread doing reset leave");
                }
                videoin_set_reset_status(hw_path->dp,false);
                vis_set_channel_is_switch_source(hw_path->dp, 0, false);
                vis_set_channel_is_switch_source(hw_path->dp, 1, false);
                vis_set_channel_is_switch_source(hw_path->dp, 2, false);
                vis_set_channel_is_switch_source(hw_path->dp, 3, false);
                mutex_unlock(&hw_path->lock);
                }
                if (kthread_should_stop()) {
                        break;
                }
        }
        vss_printk(VSS_LOG_INFO, "leave");
        return 0;
}

data_path_reset

void data_path_reset(struct hw_data_path *hw_path)
{
        enum user_data_path dp = hw_path->dp;
        int ch_idx = 0;
        int ret = -1;
        vss_printk(VSS_LOG_INFO, "data path is working reconfig & restart mipi path");
        mipi_disable_output(dp);
        msleep(100);
        data_path_do_stop_lock(hw_path,true);
        sensor_drv_feature_control(dp, 0, SENSOR_DISABLE_CLK);
        data_path_flush_buffer(hw_path);
        for (ch_idx = CHANNEL_ID_0; ch_idx < CHANNEL_ID_MAX; ch_idx ++) {
                if (hw_path->hw_ch[ch_idx]->is_vip_enable && hw_path->is_avm_mode[DMA_PORT_1]
                        && hw_path->hw_ch[ch_idx]->is_interlace) {
                        vip_flush_buffer();
                        break;
                }
        }
        hw_path->reset_count = 0;
        /*start the data path again */
        //初始化mipi
        ret = vis_bt656_mipi_init(dp,true);
        if (ret < 0) {
                vss_printk(VSS_LOG_ERR, "vis_bt656_mipi_init error");
                return ;
        }
        data_path_common_config(hw_path, true);
        for (ch_idx = CHANNEL_ID_0 ; ch_idx < CHANNEL_ID_MAX; ch_idx++) {
                if (hw_path->hw_ch[ch_idx]->is_channel_enable) {
                        data_path_channel_config(&hw_path->hw_ch[ch_idx]);
                }
        }
        data_path_config_pq(hw_path);
        if (data_path_do_start_lock(hw_path) < 0 ) {
                vss_printk(VSS_LOG_ERR, "dp(%d) start failed !",hw_path->dp);
        }
        /*enable sensor clk*/
        sensor_drv_feature_control(dp, 0, SENSOR_ENABLE_CLK);
}

 data_path_flush_buffer

static void data_path_flush_buffer(struct hw_data_path *hw_path)
{
        int ch_idx = 0;
        enum user_data_path dp = hw_path->dp;
        for (ch_idx = CHANNEL_ID_0; ch_idx < CHANNEL_ID_MAX; ch_idx ++) {
                if (!hw_path->hw_ch[ch_idx]->is_channel_enable) {
                        continue;
                }
                if (hw_path->hw_ch[ch_idx]->is_port_enable[0]){
                        mipi_flush_buffer(dp, ch_idx, MOP_1);
                }
                if (hw_path->hw_ch[ch_idx]->is_port_enable[1]) {
                        mipi_flush_buffer(dp, ch_idx, MOP_2);
                }
        }
}

 mipi_flush_buffer

int mipi_flush_buffer(enum data_path path, enum channel_id ch_id, enum mop_id port_id)
{
        int ret = VSS_NO_ERROR;
        int i = 0;
#if defined __KERNEL__
        unsigned long flags = 0;
#endif
        struct buf_info *_buf_info = NULL;
        vss_printk(VSS_LOG_INFO, "enter");
#if defined __KERNEL__
        spin_lock_irqsave(&mipi_irq_lock, flags);
        vss_printk(VSS_LOG_INFO, "after mipi_irq_lock");
#endif

        /** after rpp disabled, no buffer done irq will occured.
                the two buffers in avdif register and shadow register may not give back to upper user forever
                this will cause upper buffer flow err, so call buffer done callback twice here actively.**/
        for(i = 0; i < 2; i++) {
                _buf_info = g_cur_buf[path][CHANNEL_ID_MAX * port_id + ch_id];
                if (NULL != _buf_info) {
                        ret = mipi_done_buf_handler[path](path, port_id, ch_id, _buf_info);
                        if (ret < 0) {
                                vss_printk(VSS_LOG_ERR, "execute mipi done buf handler callback failed");
                        } else {
                                #if defined __KERNEL__
                                vss_printk(VSS_LOG_INFO, " data_path = %d ch_id = %d port_id = %d buf_index = %d",path,ch_id,port_id,_buf_info->buf_idx);
                                #endif
                        }
                } else {
                        vss_printk(VSS_LOG_DEBUG, "data_path = %d ch_id = %d  port_id = %d _buf_info =%p ",
                                                                                path,ch_id,port_id,_buf_info);
                }
                g_cur_buf[path][CHANNEL_ID_MAX * port_id + ch_id] = g_next_buf[path][CHANNEL_ID_MAX * port_id + ch_id];
        }
#if defined __KERNEL__
        spin_unlock_irqrestore(&mipi_irq_lock, flags);
#endif
        vss_printk(VSS_LOG_INFO, "leave ");

        return ret;
}

Enable MIPI clk cfg 资源

int vis_bt656_mipi_init(enum data_path dp, bool is_overflow)
{
        int ret = VSS_NO_ERROR;
        static int irq = 0;
        unsigned long flags;
        bool is_vip_enable = false;
        int ch_idx = 0;
        struct clk_reset_info *clk_info =  get_videoin_clk_reset_info();
        vss_printk(VSS_LOG_DEBUG, "enter dp=%d is_overflow =%d ",dp,is_overflow);
        spin_lock_irqsave(&vis_irq_lock, flags);
        if (1 == atomic_add_return(1,&init_flag)) {
                vss_printk(VSS_LOG_DEBUG, "Init vis crtl the first time, should enable the clock and request irq.\n");
                if (!is_overflow) {
                        __vis_clock_enable();
                }
                if (0 == irq)
                {
                        __vis_request_irq();
                        irq++;
                }
        }
        spin_unlock_irqrestore(&vis_irq_lock, flags);
        switch (dp) {
        case DP_MIPI_1:
                reset_control_assert(clk_info->rst_videoin_mipi1);
                reset_control_deassert(clk_info->rst_videoin_mipi1);
                reset_control_assert(clk_info->rst_videoin_mipi1_cfg);
                reset_control_deassert(clk_info->rst_videoin_mipi1_cfg);
                clk_prepare_enable(clk_info->clk_videoin_mipi1);
                clk_prepare_enable(clk_info->clk_videoin_mipi_cfg);
                break;
        case MIPI_2:
                reset_control_assert(clk_info->rst_videoin_mipi2);
                reset_control_deassert(clk_info->rst_videoin_mipi2);
                reset_control_assert(clk_info->rst_videoin_mipi2_cfg);
                reset_control_deassert(clk_info->rst_videoin_mipi2_cfg);
                clk_prepare_enable(clk_info->clk_videoin_mipi2);
                clk_prepare_enable(clk_info->clk_videoin_mipi_cfg);
                break;
        case BT656:
                reset_control_assert(clk_info->rst_videoin_bt656);
                reset_control_deassert(clk_info->rst_videoin_bt656);
                clk_prepare_enable(clk_info->clk_videoin_bt656);
                break;
        default:
                return -1;
        }
        switch (dp) {
                case DP_MIPI_1:
                case DP_MIPI_2:
                        mipi_init(dp);
                        break;
                case DP_656:
                        dp656_init();
                        break;
                default:
                        vss_printk(VSS_LOG_ERR, "Wrong data path(%u)!", dp);
                        ret = -VSS_INVALID_PARAM;
        }
        for (ch_idx = 0; ch_idx < CHANNEL_ID_MAX; ch_idx++) {
                if (g_vis_ctrl[dp].is_vip_enable[ch_idx]) {
                        is_vip_enable = true;
                        break;
                }
        }
        if(is_vip_enable && !is_overflow) {
                reset_control_assert(clk_info->rst_videoin_vip);
                reset_control_deassert(clk_info->rst_videoin_vip);
                clk_prepare_enable(clk_info->clk_videoin_vip);
                vip_init();
        }
        if( !is_overflow )
                memset(g_vis_buf_ch_is_write, 0, sizeof(g_vis_buf_ch_is_write));
        vss_printk(VSS_LOG_DEBUG, "leave dp=%d ",dp);
        return ret;
}

初始化mipi 注册中断函数

int mipi_init(enum data_path path)
{
        enum channel_id ch = CHANNEL_ID_0;
        int ret = 0;

        vss_printk(VSS_LOG_INFO, "enter");

        if ((DP_MIPI_1 != path) && (DP_MIPI_2 != path)) {
                vss_printk(VSS_LOG_ERR, "the input param is invalid, path = %d", path);
                return -VSS_INVALID_PARAM;
        }

        mipi_done_buf_handler[path] = NULL;
        mipi_free_buf_request[path] = NULL;
        mipi_exc_notify[path]       = NULL;

        /*reset submodules*/
        _mipi_reset(path, 1u);
        _mipi_reset(path, 0u);

        /*clear all irq*/
        _clear_all_irq(path);

        /*init csirx*/
        ret =  csirx_init(path);
        if (VSS_NO_ERROR != ret) {
                vss_printk(VSS_LOG_ERR, "init csirx module failed and path = %d", path);
                return ret;
        }

        /* rpp_lite_init */
        ret = rpp_init(path);
        if (VSS_NO_ERROR != ret) {
                vss_printk(VSS_LOG_ERR, "init rpp module failed and path = %d", path);
                return ret;
        }

        /* avdif_write_init do same to avdif_1 & avdif_2 */
        ret = avdif_hw_init(path, DP_MIPI_AVDIF_1);
        if (ret < 0) {
                vss_printk(VSS_LOG_ERR, "init avdif1 failed and path = %d", path);
                return ret;
        }

        if (!g_request_irq[path]) {
                for (; ch < CHANNEL_ID_MAX; ch++) {
                     //注册中断函数
                        _mipi_request_irq(path, ch);
                }
                g_request_irq[path] = true;
        }
        vss_printk(VSS_LOG_INFO, "leave");
        return ret;
}

调用解串器驱动进行start_stream

int data_path_do_start_lock(struct hw_data_path *hw_path)
{
        int ret = 0;
        enum channel_id ch_id = CHANNEL_ID_0;
        enum user_data_path dp = hw_path->dp;
        struct vis_irqs_info *tmp_irq = get_vis_irqs_info();
        enable_irq(tmp_irq->dp[dp].ch[CHANNEL_ID_0].eop_num);
        enable_irq(tmp_irq->dp[dp].ch[CHANNEL_ID_1].eop_num);
        enable_irq(tmp_irq->dp[dp].ch[CHANNEL_ID_2].eop_num);
        enable_irq(tmp_irq->dp[dp].ch[CHANNEL_ID_3].eop_num);
        ret = data_path_vis_start(hw_path);
        if (ret < 0) {
                vss_printk(VSS_LOG_ERR, "data_path_vis_start error");
                return -1;
        }
#ifdef CONFIG_AVIN_USE_RESERVED_MEM
        if (hw_path->hw_status != VIDEOIN_STATUS_STARTED) {
                ret = sensor_drv_start(dp, ch_id);
                if (ret < 0) {
                        vss_printk(VSS_LOG_ERR, "sensor_drv_start error");
                        return -1;
                }
        } else {
                sensor_drv_feature_control(dp, ch_id, SENSOR_ENABLE_CLK);
        }
#else
        ret = sensor_drv_start(dp,ch_id);
        if (ret) {
                vss_printk(VSS_LOG_ERR, "sensor_drv_start error");
                return -1;
        }
#endif
        if (MIPI_2 == dp || MIPI_1 == dp) {
                //mdelay(60);
                mipi_rpp_start(dp);
        }
        return ret;
}

mipi_irq_handler 中断函数处理

获取mipi中断状态

调用mipi_buffer_done_handler将buf_info 插入到vis_list_buf

调用mipi_buffer_request从vis_list_buf 获取 buf_info

最后将获取到数据buf_info 保存到更新g_next_buf

#if defined __KERNEL__
static DEFINE_SPINLOCK(mipi_irq_lock);
static irqreturn_t mipi_irq_handler(int irq_num, void *param)
#elif defined  __VISS__
static void mipi_irq_handler(int irq_num)
#endif
{
        uint32_t irq_status = 0;
        void __iomem *base = NULL;
        bool is_bot_field = false;
        enum data_path path_id = DP_MIPI_1;
        enum channel_id ch_id = CHANNEL_ID_0;
        enum mop_id   port_id = MOP_1;
        int ret = 0;
        struct vis_irqs_info *tmp_irq;
        struct buf_info *_buf_info;
#if defined __KERNEL__
        unsigned long flags = 0;
        spin_lock_irqsave(&mipi_irq_lock, flags);
#endif
        tmp_irq = get_vis_irqs_info();
        for (path_id = DP_MIPI_1; (path_id <= DP_MIPI_2) ; path_id ++) {
                for (ch_id = CHANNEL_ID_0; (ch_id < CHANNEL_ID_MAX); ch_id ++) {
                        if (irq_num == tmp_irq->dp[path_id].ch[ch_id].eop_num)
                        {
                                goto irq_found;
                        }
                }
        }
#if defined __KERNEL__
        spin_unlock_irqrestore(&mipi_irq_lock, flags);
        return IRQ_HANDLED;
#elif defined  __VISS__
        return;
#endif

irq_found:

        //vss_printk(VSS_LOG_DEBUG, "path_id is %d, ch_id is %d", path_id, ch_id);
        base = get_path_base_addr(path_id);

        irq_status = VSS_READ32((REG_DP_MIPI_CH0_MIS + ch_id * (REG_DP_MIPI_CH1_MIS - REG_DP_MIPI_CH0_MIS)));
        irq_status &= (MIPI_CTRL_MIS_IRQ_VI_FRAME_END_MASK
                                        | MIPI_CTRL_MIS_AVDIF_2_PIC_END_CH_MASK
                                        | MIPI_CTRL_MIS_AVDIF_1_PIC_END_CH_MASK);
        //vss_printk(VSS_LOG_DEBUG, "path_id is %d, ch_id is %d irq_status is %d\n", path_id, ch_id,irq_status);
        if (irq_status & MIPI_CTRL_MIS_AVDIF_2_PIC_END_CH_MASK) {
                port_id = MOP_2;
                /*clear interrupt flag*/
                VSS_WRITE32((REG_DP_MIPI_CH0_ICR + ch_id * (REG_DP_MIPI_CH1_ICR - REG_DP_MIPI_CH0_ICR)), MIPI_CTRL_MIS_AVDIF_2_PIC_END_CH_MASK);

                /*send buffer to control layer*/
                _buf_info = g_cur_buf[path_id][CHANNEL_ID_MAX * port_id + ch_id];

                if (VSS_NO_ERROR == csirx_check_bot_field(path_id, &is_bot_field)) {
                        _buf_info->is_bot_field = is_bot_field;
                }
                 //调用mipi_buffer_done_handler
                ret = mipi_done_buf_handler[path_id](path_id, port_id, ch_id, _buf_info);

                if (ret < 0) {
                        vss_printk(VSS_LOG_DEBUG, "execute mipi done buf handler callback failed");
                }
                g_cur_buf[path_id][CHANNEL_ID_MAX * port_id + ch_id] = g_next_buf[path_id][CHANNEL_ID_MAX * port_id + ch_id];

                /*request a new buffer from control layer*/
                _buf_info = mipi_free_buf_request[path_id](path_id, port_id, ch_id);
                /*give new requested buffer to  the writer*/
                avdif_hw_buffer_set(path_id, port_id, ch_id, _buf_info);
                g_next_buf[path_id][CHANNEL_ID_MAX * port_id + ch_id] = _buf_info;
        }

        if (irq_status & MIPI_CTRL_MIS_AVDIF_1_PIC_END_CH_MASK) {
                port_id = MOP_1;
                /*clear interrupt flag*/
                VSS_WRITE32((REG_DP_MIPI_CH0_ICR + ch_id * (REG_DP_MIPI_CH1_ICR - REG_DP_MIPI_CH0_ICR)), MIPI_CTRL_MIS_AVDIF_1_PIC_END_CH_MASK);
                //判断y_addr_shadow 是否 y_addr相等
                avdif_hw_buffer_judge(path_id,DP_MIPI_AVDIF_1,ch_id);
                /*send buffer to control layer*/
               // _buf_info保存 g_cur_buf地址,g_cur_buf保存g_next_buf地址,g_next_buf保存_buf_info
                _buf_info = g_cur_buf[path_id][CHANNEL_ID_MAX * port_id + ch_id];
               

                if (VSS_NO_ERROR == csirx_check_bot_field(path_id, &is_bot_field)) {
                        _buf_info->is_bot_field = is_bot_field;
                }
                //将buf_info 插入到vis_list_buf
                ret = mipi_done_buf_handler[path_id](path_id, port_id, ch_id, _buf_info);
                if (ret < 0) {
                        vss_printk(VSS_LOG_DEBUG, "execute mipi done buf handler callback failed");
                }
                g_cur_buf[path_id][CHANNEL_ID_MAX * port_id + ch_id] = g_next_buf[path_id][CHANNEL_ID_MAX * port_id + ch_id];
                
                //调用mipi_buffer_request
                /*request a new buffer from control layer*/
                //获取vis_output_hal 的vis_list_buf->buf
                _buf_info = mipi_free_buf_request[path_id](path_id, port_id, ch_id);
                /*give new requested buffer to  the writer*/
                //将vis_output_hal 的vis_list_buf->buf中的yuv地址写入到yuv的寄存器中
                avdif_hw_buffer_set(path_id, port_id, ch_id, _buf_info);
                //将buf保存到g_next_buf
                g_next_buf[path_id][CHANNEL_ID_MAX * port_id + ch_id] = _buf_info;
        }


        if (irq_status & MIPI_CTRL_MIS_IRQ_VI_FRAME_END_MASK) {
                /*clear interrupt flag*/
                VSS_WRITE32((REG_DP_MIPI_CH0_ICR + ch_id * (REG_DP_MIPI_CH1_ICR - REG_DP_MIPI_CH0_ICR)), MIPI_CTRL_MIS_IRQ_VI_FRAME_END_MASK);
        }
        //vss_printk(VSS_LOG_DEBUG, "leave");
#if defined __KERNEL__
        spin_unlock_irqrestore(&mipi_irq_lock, flags);
        return IRQ_HANDLED;
#endif
}

将free buf 插入到vis_list_buf


//#define DEBUG_PRINT_BUF
/**
 * Dma port: to be added.
 **/
static int mipi_buffer_done_handler(enum data_path dp,
                                        enum mop_id id,
                                        enum channel_id ch_id,
                                        struct buf_info *buf)
{
        unsigned long flags;
        enum dma_port port_id = (enum dma_port)id;
        enum user_data_path user_dp = USER_DP_MAX;
        bool is_top = false;
        enum videoin_device port_dev = VIDEOIN_DEVICE_MAX;
#ifdef DEBUG_PRINT_BUF
        int i = 0;
        char buf_src[100];
#endif
        user_dp = dp == DP_MIPI_1 ? MIPI_1 : MIPI_2;
        spin_lock_irqsave(&vis_irq_lock, flags);
        is_top = g_vis_ctrl[user_dp].filed_is_top[ch_id];
        g_vis_ctrl[user_dp].sequence[ch_id][port_id]++;
        spin_unlock_irqrestore(&vis_irq_lock, flags);
#ifdef DEBUG_PRINT_BUF
        if (0 != buf->y_addr_virt && CHANNEL_ID_0 == ch_id) {
                memcpy(buf_src,(char *)buf->y_addr_virt,50);
                vss_printk(VSS_LOG_INFO,"dp(%d) ch_id(%d) port(%d) buf_idx(%d)",
                                dp, ch_id, port_id, buf->buf_idx);
                for(i=0; i < 50; i++) {
                        pr_info( "0x%02X " , buf_src[i]);
                        if(24 == i) {
                                pr_info( "\n");
                        }
                }
                pr_info( "\n");
                memset((void *)buf->y_addr_virt,buf->buf_idx,0x32);
        }
#endif
        if (BUF_STATE_INVALID!= buf->state) {
                switch(g_vis_ctrl[dp].vis_output_type[port_id])
                {
                case OUTPUT_TYPE_IND: {
                        struct vip_info info;
                        memset(&info, 0, sizeof(struct vip_info));
                        dp_channel_to_device(dp, ch_id, port_id, false, &port_dev);
                        videoin_debug_set_frmae_cnt(port_dev, g_vis_ctrl[user_dp].sequence[ch_id][port_id]);
                        if (g_vis_ctrl[user_dp].is_interlace && g_vis_ctrl[user_dp].is_vip_enable[ch_id]) {
                                if (g_vis_ctrl[user_dp].is_used_four_channel) {
                                        if (g_vis_ctrl[user_dp].buffer_count[ch_id] < 16) {
                                                info.dp = user_dp;
                                                info.ch_id = ch_id;
                                                info.evt_type = VIDEOIN_DO_FILED_PROESS;
                                                info.is_avm_mode = false;
                                                info.buf_idx = buf->buf_idx;
                                                g_vis_ctrl[user_dp].make_v4l2_event(&info);
                                                g_vis_ctrl[user_dp].inter_buf[ch_id][buf->buf_idx] = buf;
                                                vss_printk(VSS_LOG_DEBUG, "dp=%d buf_idx=%d ch_id=%d ",user_dp,buf->buf_idx,buf->ch_id);
                                                g_vis_ctrl[user_dp].buffer_count[ch_id]++;
                                                goto exit;
                                        }
                                        if (!g_vis_ctrl[user_dp].waiting_detect_top_bottom[ch_id]) {
                                                if ((g_vis_ctrl[user_dp].buffer_count[ch_id]%2) != 0) {
                                                        list_push(&g_vis_ctrl[user_dp].vis_output_inter[ch_id].vis_list_buf, buf);
                                                        g_vis_ctrl[user_dp].buffer_count[ch_id]++;
                                                        goto exit;
                                                } else {
                                                        if (g_vis_ctrl[user_dp].is_first_time_set[ch_id]) {
                                                                g_vis_ctrl[user_dp].next_filed_property[ch_id] = is_top;
                                                                g_vis_ctrl[user_dp].is_first_time_set[ch_id] = false;
                                                        }
                                                        buf->is_bot_field = g_vis_ctrl[user_dp].next_filed_property[ch_id];
                                                        g_vis_ctrl[user_dp].next_filed_property[ch_id] = !buf->is_bot_field;
                                                }
                                        } else {
                                                list_push(&g_vis_ctrl[user_dp].vis_output_inter[ch_id].vis_list_buf, buf);
                                                g_vis_ctrl[user_dp].buffer_count[ch_id]++;
                                                goto exit;
                                        }
                                }
                                if ((buf->is_bot_field == g_vis_ctrl[dp].last_filed_property[ch_id])
                                        && g_vis_ctrl[dp].is_queued_vip ){
                                        list_push(&g_vis_ctrl[dp].vis_output_inter[ch_id].vis_list_buf, buf);
                                        return 0;
                                }
                        }
                        g_vis_ctrl[user_dp].valid_buffer_cnt[ch_id]++;
                        buffer_done_handler_without_lock(user_dp, ch_id, port_id, buf);
                break;
                }
                case OUTPUT_TYPE_FULL: {
                        struct vip_info info;
                        memset(&info, 0, sizeof(struct vip_info));
                        dp_channel_to_device(dp, ch_id, port_id, true, &port_dev);
                        videoin_debug_set_frmae_cnt(port_dev, g_vis_ctrl[user_dp].sequence[ch_id][port_id]);
                        if (g_vis_ctrl[user_dp].is_interlace && g_vis_ctrl[user_dp].is_vip_enable[ch_id]) {
                                if(g_vis_ctrl[user_dp].buffer_count[ch_id] < 16 ) {
                                        info.dp = user_dp;
                                        info.ch_id = ch_id;
                                        info.evt_type = VIDEOIN_DO_FILED_PROESS;
                                        info.is_avm_mode = false;
                                        info.buf_idx = buf->buf_idx;
                                        g_vis_ctrl[user_dp].make_v4l2_event(&info);
                                        g_vis_ctrl[user_dp].inter_buf[ch_id][buf->buf_idx] = buf;
                                        vss_printk(VSS_LOG_DEBUG, "dp=%d buf_idx=%d ch_id=%d ",user_dp,buf->buf_idx,buf->ch_id);
                                        g_vis_ctrl[user_dp].buffer_count[ch_id]++;
                                        goto exit;
                                }
                                if (!g_vis_ctrl[user_dp].waiting_detect_top_bottom[0]
                                && !g_vis_ctrl[user_dp].waiting_detect_top_bottom[1]
                                && !g_vis_ctrl[user_dp].waiting_detect_top_bottom[2]
                                && !g_vis_ctrl[user_dp].waiting_detect_top_bottom[3]) {
                                        if ((g_vis_ctrl[user_dp].buffer_count[ch_id]%2) != 0) {
                                                list_push(&g_vis_ctrl[user_dp].vis_output_inter[ch_id].vis_list_buf, buf);
                                                g_vis_ctrl[user_dp].buffer_count[ch_id]++;
                                                goto exit;
                                        } else {
                                                if (g_vis_ctrl[user_dp].is_first_time_set[ch_id]) {
                                                        g_vis_ctrl[user_dp].next_filed_property[ch_id] = is_top;
                                                        vss_printk(VSS_LOG_DEBUG, " monitor field :%d  check field:%d buf_cnt:%d ch_id:%d",
                                                                buf->is_bot_field,g_vis_ctrl[user_dp].next_filed_property[ch_id],g_vis_ctrl[user_dp].buffer_count[ch_id],ch_id);
                                                        g_vis_ctrl[user_dp].is_first_time_set[ch_id] = false;
                                                }
                                                if (0 == buf->ch_id) {
                                                        if (buf->is_bot_field != g_vis_ctrl[user_dp].next_filed_property[ch_id]) {
                                                                vss_printk(VSS_LOG_DEBUG, " monitor field :%d  check field:%d buf_cnt:%d,check failed!",
                                                                        buf->is_bot_field,g_vis_ctrl[user_dp].next_filed_property[ch_id],g_vis_ctrl[user_dp].buffer_count[ch_id]);
                                                        }
                                                }
                                                buf->is_bot_field = g_vis_ctrl[user_dp].next_filed_property[ch_id];
                                                g_vis_ctrl[user_dp].next_filed_property[ch_id] = !buf->is_bot_field;
                                        }
                                } else {
                                        g_vis_ctrl[user_dp].buffer_count[ch_id]++;
                                        list_push(&g_vis_ctrl[dp].vis_output_inter[ch_id].vis_list_buf,buf);
                                        goto exit;
                                }
                        }
                        g_vis_ctrl[user_dp].valid_buffer_cnt[ch_id]++;
                        buffer_done_handler_without_lock(user_dp, ch_id, port_id, buf);
                break;
                }
                case OUTPUT_TYPE_MAX:
                        return 0;
                }
        } else {
                g_vis_ctrl[user_dp].garbage_cnt[ch_id][port_id]++;
                switch (g_vis_ctrl[dp].vis_output_type[port_id]) {
                case OUTPUT_TYPE_IND: {
                        dp_channel_to_device(dp, ch_id, port_id, false, &port_dev);
                        videoin_debug_set_garbage_cnt(port_dev, g_vis_ctrl[user_dp].garbage_cnt[ch_id][port_id]);
                break;
                }
                case OUTPUT_TYPE_FULL: {
                        dp_channel_to_device(dp, ch_id, port_id, true, &port_dev);
                        videoin_debug_set_garbage_cnt(port_dev, g_vis_ctrl[user_dp].garbage_cnt[ch_id][port_id]);
                break;
                }
                case OUTPUT_TYPE_MAX:
                        return 0;
                }
                vss_printk(VSS_LOG_DEBUG, "use garbage frames ch_id = %d !!!!!!",ch_id);
        }
exit:
        return 0;
}

将dma buf物理地址写入到mipi 寄存器中

int avdif_hw_buffer_set(enum data_path path_id, enum avdif_id id, enum channel_id ch_id, struct buf_info *buf)
{
        int ret = VSS_NO_ERROR;
        u32 offset = 0;
        void __iomem *base = NULL;

        if ((path_id >= DP_MAX) || (ch_id >= CHANNEL_ID_MAX) || (id >= AVDIF_ID_MAX)) {
                vss_printk(VSS_LOG_WARNING, "ErrID Path(%d) AVDIF_ID:(%d) CH(%d)\n", path_id, id, ch_id);

                return (-VSS_INVALID_PARAM);
        }
        if (NULL == buf) {
                vss_printk(VSS_LOG_WARNING, "Buf Info Error\n");

                return (-VSS_INVALID_PARAM);
        }

        base = _avdif_reg_offset[id] + get_path_base_addr(path_id);
        switch (id) {
        case DP_MIPI_AVDIF_1:
        case DP_MIPI_AVDIF_2:
        case DP_656_AVDIF:
                offset = REG_AVDIFW_AWUSER + AVDIFW_CH_AUSR_REG_OFFSET * ch_id;

                switch (path_id) {
                case DP_MIPI_1:
                        if (DP_MIPI_AVDIF_1 == id) {
                                buf->auser = SMMU_STREAM_ID_INPUT1_MIPI1_PORT1;
                        } else {
                                buf->auser = SMMU_STREAM_ID_INPUT1_MIPI1_PORT2;
                        }
                        break;
                case DP_MIPI_2:
                        if (DP_MIPI_AVDIF_1 == id) {
                                buf->auser = SMMU_STREAM_ID_INPUT1_MIPI2_PORT1;
                        } else {
                                buf->auser = SMMU_STREAM_ID_INPUT1_MIPI2_PORT2;
                        }
                        break;
                case DP_656:
                        buf->auser = SMMU_STREAM_ID_INPUT1_BT656;
                        break;
                default:
                        break;
                }
                VSS_WRITE32(offset, buf->auser & AVDIF_AUSR_REG_MASK);
#if defined __KERNEL__
                vss_printk(VSS_LOG_DEBUG, "ch_id =%d buf->auser =0x%lx buf->y_addr=0x%lx  buf->cb_addr=0x%lx  buf->cr_addr=0x%lx\n",
                                ch_id, buf->auser & AVDIF_AUSR_REG_MASK, buf->y_addr & AVDIF_ADDR_REG_MASK,
                                buf->cb_addr & AVDIF_ADDR_REG_MASK, buf->cr_addr & AVDIF_ADDR_REG_MASK);
#elif defined __VISS__
                vss_printk(VSS_LOG_DEBUG, "ch_id =%d buf->auser =0x%x buf->y_addr=0x%x  buf->cb_addr=0x%x  buf->cr_addr=0x%x\n",
                                ch_id, buf->auser & AVDIF_AUSR_REG_MASK, buf->y_addr & AVDIF_ADDR_REG_MASK,
                                buf->cb_addr & AVDIF_ADDR_REG_MASK, buf->cr_addr & AVDIF_ADDR_REG_MASK);
#endif

                offset = REG_AVDIFW_Y_BA + AVDIFW_CH_ADDR_REG_OFFSET * ch_id;
                VSS_WRITE32(offset, buf->y_addr & AVDIF_ADDR_REG_MASK);
                offset = REG_AVDIFW_CB_BA + AVDIFW_CH_ADDR_REG_OFFSET * ch_id;
                VSS_WRITE32(offset, buf->cb_addr & AVDIF_ADDR_REG_MASK);
                offset = REG_AVDIFW_CR_BA + AVDIFW_CH_ADDR_REG_OFFSET * ch_id;
                VSS_WRITE32(offset, buf->cr_addr & AVDIF_ADDR_REG_MASK);
                break;

        case VIP_3D_CURRENT_AVDIF_READ:
        case VIP_3D_PRE_AVDIF_READ:
        case VIP_DI_CUR_AVDIF_READ:
        case VIP_DI_PRE_AVDIF_READ:
        case VIP_DI_HISTORY_AVDIF_READ:
                VSS_WRITE32(REG_AVDIFR_ARUSER, buf->auser & AVDIF_AUSR_REG_MASK);
                VSS_WRITE32(REG_AVDIFR_BA, buf->y_addr & AVDIF_ADDR_REG_MASK);
                break;

        case VIP_3D_DENOISE_AVDIF_WRITE:
        case VIP_DI_HISTORY_AVDIF_WRITE:
        case VIP_VOP1_AVDIF:
        case VIP_VOP2_AVDIF:
        //将dma 物理地址写入到mipi 寄存器中
                VSS_WRITE32(REG_AVDIFW_AWUSER, buf->auser & AVDIF_AUSR_REG_MASK);
                VSS_WRITE32(REG_AVDIFW_Y_BA, buf->y_addr & AVDIF_ADDR_REG_MASK);
                VSS_WRITE32(REG_AVDIFW_CB_BA, buf->cb_addr & AVDIF_ADDR_REG_MASK);
                VSS_WRITE32(REG_AVDIFW_CR_BA, buf->cr_addr & AVDIF_ADDR_REG_MASK);
                break;

        default:
                ret = (-VSS_INVALID_PARAM);
                break;
        }

        return ret;
}

从vis_list_buf中获取free buf

//获取流式DMA的 dma_buf addr

static struct buf_info * mipi_buffer_request (enum data_path dp,
                                   enum mop_id id,
                                   enum channel_id ch_id)
{
        enum user_data_path  user_dp = USER_DP_MAX;
        enum dma_port port_id = DMA_PORT_MAX;
        struct vis_buf *pbuf = NULL;
#ifdef DEBUG_PRINT_BUF
        int i = 0;
        char buf_src[100];
#endif
        port_id = (enum dma_port)id;
        user_dp = dp == DP_MIPI_1 ? MIPI_1 : MIPI_2;
        /*
        * drop_frames must equ even
        */
        if (g_vis_ctrl[user_dp].is_switch_source[ch_id]) {
                pbuf = &g_vis_garbage_buffer;
                vss_printk(VSS_LOG_INFO, "is_switch_source is true");
        } else if (g_vis_ctrl[user_dp].drop_frames > 0) {
                pbuf = &g_vis_garbage_buffer;
                vss_printk(VSS_LOG_INFO, " g_vis_ctrl[dp].drop_frames = %u",g_vis_ctrl[user_dp].drop_frames);
                g_vis_ctrl[dp].drop_frames--;
        } else {
                if (g_vis_ctrl[user_dp].is_vip_enable[ch_id]) {
                        /*
                         * need judge use garbage count , garbage count must equ even
                         */
                        if (g_vis_ctrl[user_dp].use_garbae_cnt[ch_id] > 0) {
                                if (0 == g_vis_ctrl[user_dp].use_garbae_cnt[ch_id] % 2) {
                                        g_vis_ctrl[user_dp].use_garbae_cnt[ch_id] = 0;
                                        pbuf = list_pop(&g_vis_ctrl[user_dp].vis_output_inter[ch_id].vis_list_buf);
                                } else {
                                        pbuf = &g_vis_vip_garbage_buffer;
                                }
                        } else {
                                pbuf = list_pop(&g_vis_ctrl[user_dp].vis_output_inter[ch_id].vis_list_buf);
                        }
                        if (BUF_STATE_INVALID == pbuf->buf.state) {
                                g_vis_ctrl[user_dp].use_garbae_cnt[ch_id]++;
                        }
                } else {
                       //获取流式DMA的 dma_buf addr
                        pbuf = list_pop(&g_vis_ctrl[user_dp].vis_output_hal[ch_id].vis_list_buf[port_id]);

#ifdef DEBUG_PRINT_BUF
                        if (0 != pbuf->buf.y_addr_virt && CHANNEL_ID_0 == ch_id ) {
                                memcpy(buf_src,(char *) pbuf->buf.y_addr_virt,50);
                                vss_printk(VSS_LOG_INFO,"dp(%d) ch_id(%d) port(%d) buf_idx(%d)",
                                dp, ch_id, port_id, pbuf->buf.buf_idx);
                                for(i=0; i < 50; i++) {
                                        pr_info( "0x%02X " , buf_src[i]);
                                        if(24 == i) {
                                                pr_info( "\n");
                                        }
                                }
                                pr_info( "\n");
                                memset((void *) pbuf->buf.y_addr_virt, pbuf->buf.buf_idx,0x32);
                        }
#endif
                }
        }
        vss_printk(VSS_LOG_DEBUG, "dp = %d ch_id = %d mop_id = %d pbuf->buf=0x%lx idx =%d ",dp,ch_id,id,pbuf->buf.y_addr,pbuf->buf.buf_idx);
        return &pbuf->buf;
}

创建dma_buf

enqueueBuffer

STREAM_BUF_CNT_MAX = bufIdx

mpV4L2->setUserBufFd(buffer->fd, bufIdx)

应用层set 4个buffer

autochips/proprietary/hardware/videoin/libs/camerasource/src/cameraSourceImp.cpp

int CameraStream::enqueueBuffer(bufferInfo *buffer)
{
    int ret = 0;
    int bufIdx = 0;

    bufIdx = getBufferIndex(buffer);
    if (INVALID_INDEX == bufIdx) {
        // this buffer is queue first time.
        bufIdx = ApplyBufferIndex(buffer);
        if (INVALID_INDEX == bufIdx) {
            VI_LOGE("stream id(%d) queueBuffer failed.No buffer index free.", mStreamId);
            return -1;
        }
        VI_LOGI("stream id(%d) set fd (%d) idx(%d)", mStreamId, buffer->fd, bufIdx);
        mBufferMap[bufIdx].stream = mStreamId;
        mBufferMap[bufIdx].bufIdx = bufIdx;
        queueV4L2Buffer(1);
        if (false == bSetStride) {
            ret = mpV4L2->setStride(buffer->yStride, buffer->cStride);
            if (0 != ret) {
                VI_LOGE("stream id(%d) set stride(ystride:%d, cstride:%d) idx (%d) failed",
                    mStreamId, buffer->yStride, buffer->cStride, bufIdx);
                return -1;
            }
            bSetStride = true;
        }

        ret = mpV4L2->setUserBufFd(buffer->fd, bufIdx);
        if (0 != ret) {
            VI_LOGE("stream id(%d) set fd (%d) idx (%d) failed",
                    mStreamId, buffer->fd, bufIdx);
            return -1;
        }
    } else {
        queueV4L2Buffer(1, bufIdx); //调用vb2_core_qbuf
    }
    if(nullptr != mpLastData && (mpLastData->fd == buffer->fd)) {
        mpLastData = nullptr;
    }

    if (0 == mTotalQueue) {
        gDt.UpdateAndPrint("First buffer(to camera)");
    }

    return ret;
}

 videoin_set_fd_buffer_ext

static int videoin_set_fd_buffer_ext(struct video_device *vdev,
                                                struct logical_device *lg_dev,
                                                                int stream_id,
                                                                int buf_idx,
                                                                int buf_fd)
{
        int ret = -1;
        struct videoin_stream_info *pstream_info = NULL;
        struct videoin_dma_buf_info *p_dma_buf = NULL;
        struct capture_priv priv_data;
        struct device *dev = vdev->v4l2_dev->dev;
        struct hw_channel *hw_ch = lg_dev->hw_path->hw_ch[lg_dev->ch_id];
        u16 width = hw_ch->output_info[stream_id].out_width;
        u16 height = hw_ch->output_info[stream_id].out_height;
        u16 stride_y = 0;
        u16 stride_c = 0;
        u32 offset_line = 0;
        u8 data_fmt = hw_ch->output_info[stream_id].data_fmt;
        struct videoin_buf_addr *buf_addr = &hw_ch->output_buf_addr[stream_id][buf_idx];

        vss_printk(VSS_LOG_DEBUG, "buf_fd is %d, buf_idx is %d, stream_id is %d",
                                                buf_fd, buf_idx, stream_id);
        if (STREAM_CNT_MAX <= stream_id) {
                vss_printk(VSS_LOG_ERR, "error with buf_idx(%d) stream_id(%d)",
                                                        buf_idx, stream_id);
                return -EINVAL;
        }
        pstream_info = &lg_dev->video_port_info->stream_info[stream_id];
        p_dma_buf = &pstream_info->dma_output_buf[buf_idx];
        stride_y = pstream_info->line_stride[0];
        stride_c = pstream_info->line_stride[1];
        offset_line = pstream_info->port_info.offset_line;
        if (hw_ch->is_vip_enable) {
                dev = videoin_get_vip_dev();
        }
        //分配流式DMA空间地址 p_dma_buf->dma_address p_dma_buf->y_virt
        ret = videoin_buf_fd_to_dma_buf(p_dma_buf,dev,buf_fd);
        if(ret < 0) {
                vss_printk(VSS_LOG_ERR, " analyse fd -> buf_address fail");
                return -EINVAL;
        }
        //将分配的dma的虚拟地址保存到hw_ch->output_buf_addr中
        buf_addr->y_virt = p_dma_buf->y_virt;
        //初始化buf_addr
        data_path_dma_addr_to_YUV_addr(width, height, data_fmt, offset_line, stride_y, stride_c,
                p_dma_buf->dma_address, lg_dev->hw_path->is_avm_mode[stream_id], buf_addr);
        vss_printk(VSS_LOG_DEBUG, "stream_id(%d) dma addr(0x%lx) virt addr(0x%lx) offset_line(%d)",
                stream_id, (unsigned long int)buf_addr->y, buf_addr->y_virt, offset_line);
        //2. que this buffer to vis accorrding to hw path
        memset(&priv_data, 0, sizeof(struct capture_priv));
        priv_data.stream_id = stream_id;
        priv_data.buf_idx = buf_idx;
        //priv_data hw_channel
        if (data_path_que_buffer(priv_data, &lg_dev->hw_path->hw_ch[lg_dev->ch_id]) < 0) {
                vss_printk(VSS_LOG_ERR, "mipi2_path_que_buffer error");
                goto fail;
        }
        pstream_info->is_buf_ready = true;
        return 0;

fail:
        videoin_free_dma_buffer(p_dma_buf);
        return -EFAULT;
}

分配流式DMA空间地址

int videoin_buf_fd_to_dma_buf(struct videoin_dma_buf_info *dma_buf_info,
                                                struct device *dev, int buf_fd)
{
#ifndef CONFIG_AVIN_USE_RESERVED_MEM
        int i;
        int count = 0;
        struct scatterlist *sg;
#endif
        struct videoin_dma_buf_info *p_dma_buf_info = dma_buf_info;
        struct sg_table *sg_table_temp = NULL;
        p_dma_buf_info->dma_buffer = dma_buf_get(buf_fd);
        if (IS_ERR_OR_NULL(p_dma_buf_info->dma_buffer)) {
                vss_printk(VSS_LOG_ERR, "dma_buf_get error with buf_fd(%d) ",
                                                                        buf_fd);
                dma_buf_info->dma_buffer = NULL;
                return -EFAULT;
        }
        p_dma_buf_info->dma_attachment = dma_buf_attach(dma_buf_info->dma_buffer,
                                                                        dev);
        if (NULL == p_dma_buf_info->dma_attachment) {
                vss_printk(VSS_LOG_ERR, "dma_buf_attach error with buf_fd(%d)",
                                                                        buf_fd);
                goto fail;
        }
        sg_table_temp = dma_buf_map_attachment(p_dma_buf_info->dma_attachment,
                                                        DMA_BIDIRECTIONAL);
        if (NULL == sg_table_temp) {
                vss_printk(VSS_LOG_ERR, "dma_buf_map_attachment error  ");
                goto fail;
        }
        p_dma_buf_info->dma_sg_table = clone_sgt(sg_table_temp);
        count = dma_map_sg(dev, p_dma_buf_info->dma_sg_table->sgl,
                                p_dma_buf_info->dma_sg_table->nents,
                                DMA_BIDIRECTIONAL);
        vss_printk(VSS_LOG_DEBUG, "dev(%p) count=%d  nents=%d ",dev, count,
                                        p_dma_buf_info->dma_sg_table->nents);
        for_each_sg(p_dma_buf_info->dma_sg_table->sgl, sg,
                                p_dma_buf_info->dma_sg_table->nents, i) {
                vss_printk(VSS_LOG_DEBUG, " length=%u dma_addr=0x%lx phy=0x%lx",
                                        sg->length,
                                        (long unsigned int)sg_dma_address(sg),
                                        (long unsigned int)sg_phys(sg));
        }

        p_dma_buf_info->dma_address = sg_dma_address(
                                        p_dma_buf_info->dma_sg_table->sgl);
        p_dma_buf_info->y_virt = (uintptr_t)sg_virt(p_dma_buf_info->dma_sg_table->sgl);
        return 0;
fail:
        videoin_free_dma_buffer(p_dma_buf_info);
        return -1;
}

初始化YUV 的 buf_addr

#define ALIGN_ADDR 64
int data_path_dma_addr_to_YUV_addr(u16 width, u16 height, u8 data_fmt, u32 offset_line, u16 stride_y, u16 stride_c,
                                        dma_addr_t dma_address, bool is_avm_mode, struct videoin_buf_addr *buf_addr)
{       //output_buf_addrb->y 保存dma的物理地址
        buf_addr->y = (uintptr_t)dma_address; 
        switch (data_fmt) {
        case VIS_YUYV:
        case VIS_UYVY:
               //初始化YUV的地址和plane_cnt
                buf_addr->plane_cnt = 1;
                buf_addr->cb = 0;
                buf_addr->cr = 0;
                break;
        case VIS_YV12:
        case VIS_YU12:
                buf_addr->plane_cnt = 3;
                if (is_avm_mode) {
                        if (stride_y == 0 || stride_c == 0) {
                                buf_addr->y  = buf_addr->y + width * 2 * offset_line;
                                buf_addr->cb = buf_addr->y
                                        + (_ALIGN_UP(width * 2, ALIGN_ADDR)) * (height * 2)
                                        + width * offset_line / 4;;
                                buf_addr->cr = buf_addr->cb
                                        + (_ALIGN_UP(width * 2, ALIGN_ADDR) / 2) * (height * 2) / 2;
                        } else {
                                buf_addr->y  = buf_addr->y + stride_y * offset_line;
                                buf_addr->cb = buf_addr->y
                                                + stride_y * (height * 2)
                                                + stride_c * offset_line / 2;
                                buf_addr->cr = buf_addr->cb
                                                + stride_c * height
                                                + stride_c * offset_line / 2;
                        }
                } else {
                        if (stride_y == 0 || stride_c == 0) {
                                buf_addr->cb = buf_addr->y
                                        + _ALIGN_UP(width, ALIGN_ADDR) * height;
                                buf_addr->cr = buf_addr->cb
                                        + (_ALIGN_UP(width, ALIGN_ADDR) / 2) * height / 2;
                        } else {
                                buf_addr->cb = buf_addr->y
                                                + stride_y * height;
                                buf_addr->cr = buf_addr->cb
                                                + stride_c * height / 2;
                        }
                }
                break;
        case VIS_NV12:
        case VIS_NV21:
                buf_addr->plane_cnt = 2;
                if (is_avm_mode) {
                        if (stride_y == 0) {
                                buf_addr->cb = buf_addr->y
                                        + (_ALIGN_UP(width * 2, ALIGN_ADDR)) * (height * 2);
                                buf_addr->cr = 0;
                        } else {
                                buf_addr->y  = buf_addr->y + stride_y * offset_line;
                                buf_addr->cb = buf_addr->y
                                                + stride_y * (height * 2)
                                                + stride_c * offset_line / 2;
                                buf_addr->cr = 0;
                        }
                } else {
                        if (stride_y == 0) {
                                buf_addr->cb = buf_addr->y
                                        + _ALIGN_UP(width, ALIGN_ADDR) * height;
                                buf_addr->cr = 0;
                        } else {
                                buf_addr->cb = buf_addr->y
                                                + stride_y * height;
                                buf_addr->cr = 0;
                        }
                }
                break;
        default:
                vss_printk(VSS_LOG_ERR, "format is not support(%d)", data_fmt);
                return -EFAULT;
        }
        return 0;
}

Enqueue buffer

int data_path_que_buffer(struct capture_priv priv_data, struct hw_channel **p_hw_ch)
{
        u8 stream_id = priv_data.stream_id;
        u8 buf_idx = priv_data.buf_idx;
        struct hw_channel *hw_ch = *p_hw_ch;
        enum channel_id ch_id = hw_ch->ch_id;
        struct hw_data_path *hw_path = container_of(p_hw_ch ,struct hw_data_path, hw_ch[ch_id]);
        enum user_data_path dp = hw_path->dp;
        struct port_buffer vis_buf;
        struct videoin_buf_addr *output_buf_addr = NULL;

        mutex_lock(&hw_path->lock);
        if ((STREAM_BUF_CNT_MAX == buf_idx) && (STREAM_CNT_MAX == stream_id)) {
                mutex_unlock(&hw_path->lock);
                vss_printk(VSS_LOG_DEBUG, "buf_idx=CNT_MAX & stream_id=CNT_MAX, means this queue just for v4l2 buffer list\n");
                return 0;
        }
        switch(hw_path->hw_status)
        {
                case VIDEOIN_STATUS_READY:
                case VIDEOIN_STATUS_STARTED:
                        break;
                default:
                        vss_printk(VSS_LOG_WARNING, "dp(%d) queue buffer in wrong status(%d)", dp, hw_path->hw_status);
                        mutex_unlock(&hw_path->lock);
                        return -1;
        }
        if ((STREAM_BUF_CNT_MAX == buf_idx) || (STREAM_CNT_MAX == stream_id)) {
                mutex_unlock(&hw_path->lock);
                vss_printk(VSS_LOG_DEBUG, " buf_idx(%d) stream_id(%d)\n", buf_idx, stream_id);
                return 0;
        }
        //获取通过分buf_fd 获取的dma_buf
        output_buf_addr = &(hw_ch->output_buf_addr[stream_id][0]);
        if (0 == output_buf_addr[buf_idx].y) {
                mutex_unlock(&hw_path->lock);
                vss_printk(VSS_LOG_ERR, "stream(%d), idx(%d) addr is null, may not set_fd yet", stream_id, buf_idx);
                return -1;
        }
        memset(&vis_buf, 0, sizeof(struct port_buffer));
        vis_buf.plane_cnt   = output_buf_addr[buf_idx].plane_cnt;
        vis_buf.vir_addr[0] = output_buf_addr[buf_idx].y;
        vis_buf.vir_addr[1] = output_buf_addr[buf_idx].cb;
        vis_buf.vir_addr[2] = output_buf_addr[buf_idx].cr;
        vis_buf.y_virt      = output_buf_addr[buf_idx].y_virt;
        vis_buf.buf_idx     = buf_idx;
        vss_printk(VSS_LOG_DEBUG, "vis_buf.buf_idx=%d  pstream_info->port_id=%d  ch_id=%d y=0x%lx cb=0x%lx cr=0x%lx\n",
                                        vis_buf.buf_idx,stream_id,ch_id,vis_buf.vir_addr[0],vis_buf.vir_addr[1],vis_buf.vir_addr[2]);
        if (vis_buffer_queue(dp, stream_id, &vis_buf,ch_id)) {
                vss_printk(VSS_LOG_WARNING, "vis_buffer_queue error\n");
        }
        mutex_unlock(&hw_path->lock);
        return 0;
}

将dma buf 添加到vis_list_buf  中断时获取buf 

int vis_buffer_queue(enum user_data_path dp, enum dma_port port, struct port_buffer *user_buf,enum channel_id ch_id)
{
        int ch_id_idx = 0;
        struct buf_info buf;
        vss_printk(VSS_LOG_DEBUG, "Enter\n");
        if ( 0== (debug_queue[dp][ch_id] % QUEUE_DEBUG_INTERVAL))
        {
                vss_printk(VSS_LOG_INFO, "dp(%d) ch_id(%d) queue count(%d)!\n", dp, ch_id, debug_queue[dp][ch_id]);
        }
        debug_queue[dp][ch_id] ++;

        switch (g_vis_ctrl[dp].vis_output_type[port])
        {
                case OUTPUT_TYPE_IND:
                        memset(&buf,0,sizeof(struct buf_info));
                        buf.y_addr    = user_buf->vir_addr[0];
                        buf.cb_addr   = user_buf->vir_addr[1];
                        buf.cr_addr   = user_buf->vir_addr[2];
                        buf.y_addr_virt = user_buf->y_virt;
                        buf.plane_cnt = user_buf->plane_cnt;
                        buf.buf_idx   = user_buf->buf_idx;
                        buf.ch_id     = ch_id;
                        buf.auser = 0;
                        buf.dp = dp;
                        buf.stream_id = port;
                        buf.state = BUF_STATE_FREE;
                        list_push(&g_vis_ctrl[dp].vis_output_hal[ch_id].vis_list_buf[port], &buf);
                        vis_debug_fps(dp, ch_id, port);
                        if (g_vis_ctrl[dp].is_vip_enable[ch_id]) {
                                vis_debug_vip_fps(dp, ch_id);
                        }
                        break;
                case OUTPUT_TYPE_FULL:
                        {
#ifdef DO_TEST
                                struct test_sequence *temp = NULL;
                                struct test_sequence *temp1 = NULL;
#endif
                                struct test_sequence *list_entry = NULL;
                                unsigned long flags;

                                spin_lock_irqsave(&avm_list_irq_lock, flags);
                                memset(&g_test_seq[dp][g_list_conunt[dp][port] + VIS_BUF_CNT * port],0,sizeof(struct test_sequence));
                                list_entry = &g_test_seq[dp][g_list_conunt[dp][port] + VIS_BUF_CNT * port];
                                list_entry->index = user_buf->buf_idx;
                                list_add_tail(&list_entry->list,&g_sequence_list[dp][port]);
                                for (ch_id_idx = CHANNEL_ID_0; ch_id_idx < CHANNEL_ID_MAX; ch_id_idx++)
                                {
                                        vss_printk(VSS_LOG_DEBUG, "ch_id_idx=%d ",ch_id_idx);
                                        memset(&buf,0,sizeof(struct buf_info));
                                        buf.y_addr = user_buf->vir_addr[0]
                + g_vis_ctrl[dp].vis_output_hal[ch_id_idx].ch_buf_off[port][ch_id_idx*3+0];
                                        buf.cb_addr = user_buf->vir_addr[1]
                + g_vis_ctrl[dp].vis_output_hal[ch_id_idx].ch_buf_off[port][ch_id_idx*3+1];
                                        buf.cr_addr = user_buf->vir_addr[2]
                + g_vis_ctrl[dp].vis_output_hal[ch_id_idx].ch_buf_off[port][ch_id_idx*3+2];
                                        buf.plane_cnt = user_buf->plane_cnt;
                                        buf.buf_idx   = user_buf->buf_idx;
                                        buf.ch_id = ch_id_idx;
                                        buf.auser = 0;
                                        buf.dp = dp;
                                        buf.stream_id = port;
                                        buf.state = BUF_STATE_FREE;
                                        list_push(&g_vis_ctrl[dp].vis_output_hal[ch_id_idx].vis_list_buf[port], &buf);
                                        vis_debug_fps(dp, ch_id_idx, port);
                                        if (g_vis_ctrl[dp].is_vip_enable[ch_id_idx]) {
                                                vis_debug_vip_fps(dp, ch_id_idx);
                                        }
                                        vss_printk(VSS_LOG_DEBUG, "buf_idx=%d stream_id=%d  ch_id=%d y=0x%lx cb=0x%lx cr=0x%lx",
                                        buf.buf_idx, buf.stream_id, buf.ch_id, buf.y_addr, buf.cb_addr, buf.cr_addr);
                                }
                                vss_printk(VSS_LOG_DEBUG, " push idx: %u   ",user_buf->buf_idx);
#ifdef DO_TEST
                                list_for_each_entry_safe(temp, temp1, &g_sequence_list[dp][port], list)
                                {
                                        vss_printk(VSS_LOG_INFO, " idx: %u       ",temp->index);
                                }
#endif
                                g_list_conunt[dp][port]++;
                                g_list_conunt[dp][port] %= VIS_BUF_CNT;
                                spin_unlock_irqrestore(&avm_list_irq_lock, flags);
                                g_buf_max_cnt[dp][port] = max(g_buf_max_cnt[dp][port],buf.buf_idx);
                        }
                        break;
                default:
                        return -1;
        }
        vss_printk(VSS_LOG_DEBUG, "Leave \n");
        return VSS_NO_ERROR;
}
EXPORT_SYMBOL(vis_buffer_queue);

vb2_core_qbuf &vb2_dqbuf

通常用户空间的操作顺序如下:

VIDIOC_S_PARAM::参数为 struct v4l2_streamparm,主要设置帧率,type 与 capturemode/outputmode 等。内核的 ioctl 需要保存 capturemode(mode需自定义)帧率等数据,可以保存到自定义的结构体里面。也可以在内核里面通过 v4l2_subdev_call 来调用
VIDIOC_S_FMT::参数为 struct v4l2_format,主要设置长宽,数据格式等。内核的 ioctl 需要保存长宽,像素格式,所在的 field 等。必要时需要调用相关的子设备的结构体进行子设备的格式设置。
VIDIOC_REQBUFS::参数为 struct v4l2_requestbuffers,设置 count,type 与 memory 请求数据,在 queue_setup 回调函数里面需要设置一些东西。参见上面关于 queue_setup 的描述。
VIDIOC_QUERYBUF::参数为 struct v4l2_buffer,需设置 index,type,memory,length(plane length) 等参数
VIDIOC_QBUF,该操作与上一个不断循环,直到获取所有的 buf 并将 buf 入队到内核驱动的的 buf 管理列表中
VIDIOC_STREAMON::参数为 enum v4l2_buf_type,开启指定类型的 stream。
select 等待 buf 可读。在内核驱动里面需要获取 plane 的地址,vb2_plane_vaddr,在数据填充完毕之后需要调用 vb2_buffer_done 来标注该 buffer 已经成功填充完毕,以便 v4l2 把数据放入 done_list,以待用户空间进行读取。
VIDIOC_DQBUF,该操作与上一个操作不断循环,直到停止数据采集
VIDIOC_STREAMOFF,结束数据采集工作。

Enqueue buffer

[16:51:05][   71.101366]<3>WHardware name: ATC Inc. AC8x (DT)
[16:51:05][   71.101369]<3>WCall trace:
[16:51:05][   71.101383]<3>W[<ffffff800808bb7c>] dump_backtrace+0x0/0x2c0
[16:51:05][   71.101389]<3>W[<ffffff800808c36c>] show_stack+0x14/0x1c
[16:51:05][   71.101396]<3>W[<ffffff80083de478>] dump_stack+0x94/0xb4
[16:51:05][   71.101405]<3>W[<ffffff80084c432c>] videoin_buffer_queue+0x24/0x160
[16:51:05][   71.101415]<3>W[<ffffff80086e4ad0>] __enqueue_in_driver+0xd8/0x21c
[16:51:05][   71.101419]<3>W[<ffffff80086e5dc4>] vb2_core_qbuf+0x224/0x28c
[16:51:05][   71.101423]<3>W[<ffffff80086e9478>] vb2_qbuf+0x94/0xe0
[16:51:05][   71.101427]<3>W[<ffffff80086e9510>] vb2_ioctl_qbuf+0x4c/0x58
[16:51:05][   71.101430]<3>W[<ffffff80086ca40c>] v4l_qbuf+0x54/0x60
[16:51:05][   71.101434]<3>W[<ffffff80086c9a8c>] __video_do_ioctl+0x1dc/0x2a4
[16:51:05][   71.101437]<3>W[<ffffff80086c954c>] video_usercopy+0x1b8/0x500
[16:51:05][   71.101440]<3>W[<ffffff80086c98a8>] video_ioctl2+0x14/0x1c
[16:51:05][   71.101446]<3>W[<ffffff80086c3d14>] v4l2_ioctl+0xf8/0x124
[16:51:05][   71.101452]<3>W[<ffffff800822d61c>] do_vfs_ioctl+0xb8/0x944
[16:51:05][   71.101456]<3>W[<ffffff800822df2c>] SyS_ioctl+0x84/0x98
[16:51:05][   71.101460]<3>W[<ffffff80080835c0>] el0_svc_naked+0x34/0x38
[16:51:05][   71.101468]<3>I[videoin][drv_if] [debug]data_path_que_buffer(line:1306):  buf_idx(32) stream_id(0)

int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
{
        struct vb2_buffer *vb;
        int ret;

        vb = q->bufs[index];

        switch (vb->state) {
        //VIDIOC_REQBUFS 设置state=VB2_BUF_STATE_DEQUEUED
        case VB2_BUF_STATE_DEQUEUED:
                ret = __buf_prepare(vb, pb);
                if (ret)
                        return ret;
                break;
        case VB2_BUF_STATE_PREPARED:
                break;
        case VB2_BUF_STATE_PREPARING:
                dprintk(1, "buffer still being prepared\n");
                return -EINVAL;
        default:
                dprintk(1, "invalid buffer state %d\n", vb->state);
                return -EINVAL;
        }

        /*
         * Add to the queued buffers list, a buffer will stay on it until
         * dequeued in dqbuf.
         */
        list_add_tail(&vb->queued_entry, &q->queued_list);
        q->queued_count++;
        q->waiting_for_buffers = false;
        vb->state = VB2_BUF_STATE_QUEUED;

        if (pb)
                call_void_bufop(q, copy_timestamp, vb, pb);

        trace_vb2_qbuf(q, vb);

        /*
         * If already streaming, give the buffer to driver for processing.
         * If not, the buffer will be given to driver on next streamon.
         */
        if (q->start_streaming_called)
                __enqueue_in_driver(vb);

        /* Fill buffer information for the userspace */
        if (pb)
                call_void_bufop(q, fill_user_buffer, vb, pb);

        /*
         * If streamon has been called, and we haven't yet called
         * start_streaming() since not enough buffers were queued, and
         * we now have reached the minimum number of queued buffers,
         * then we can finally call start_streaming().
         */
        if (q->streaming && !q->start_streaming_called &&
            q->queued_count >= q->min_buffers_needed) {
                ret = vb2_start_streaming(q);
                if (ret)
                        return ret;
        }

        dprintk(1, "qbuf of buffer %d succeeded\n", vb->index);
        return 0;
}
EXPORT_SYMBOL_GPL(vb2_core_qbuf);

static int __buf_prepare(struct vb2_buffer *vb, const void *pb)
{
        struct vb2_queue *q = vb->vb2_queue;
        int ret;

        if (q->error) {
                dprintk(1, "fatal error occurred on queue\n");
                return -EIO;
        }

        vb->state = VB2_BUF_STATE_PREPARING;

        switch (q->memory) {
        case VB2_MEMORY_MMAP:
                ret = __qbuf_mmap(vb, pb);
                break;
        case VB2_MEMORY_USERPTR:
                ret = __qbuf_userptr(vb, pb);
                break;
        case VB2_MEMORY_DMABUF:
                ret = __qbuf_dmabuf(vb, pb);
                break;
        default:
                WARN(1, "Invalid queue type\n");
                ret = -EINVAL;
        }

        if (ret)
                dprintk(1, "buffer preparation failed: %d\n", ret);
        vb->state = ret ? VB2_BUF_STATE_DEQUEUED : VB2_BUF_STATE_PREPARED;

        return ret;
}
填充vb2_buffer

/**
 * __fill_vb2_buffer() - fill a vb2_buffer with information provided in a
 * v4l2_buffer by the userspace. It also verifies that struct
 * v4l2_buffer has a valid number of planes.
 */
static int __fill_vb2_buffer(struct vb2_buffer *vb,
                const void *pb, struct vb2_plane *planes)
{
        struct vb2_queue *q = vb->vb2_queue;
        const struct v4l2_buffer *b = pb;
        struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
        unsigned int plane;
        int ret;

        ret = __verify_length(vb, b);
        if (ret < 0) {
                dprintk(1, "plane parameters verification failed: %d\n", ret);
                return ret;
        }
        if (b->field == V4L2_FIELD_ALTERNATE && q->is_output) {
                /*
                 * If the format's field is ALTERNATE, then the buffer's field
                 * should be either TOP or BOTTOM, not ALTERNATE since that
                 * makes no sense. The driver has to know whether the
                 * buffer represents a top or a bottom field in order to
                 * program any DMA correctly. Using ALTERNATE is wrong, since
                 * that just says that it is either a top or a bottom field,
                 * but not which of the two it is.
                 */
                dprintk(1, "the field is incorrectly set to ALTERNATE "
                                        "for an output buffer\n");
                return -EINVAL;
        }
        vb->timestamp = 0;
        vbuf->sequence = 0;
         
         //多平面帧
        if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
                if (b->memory == VB2_MEMORY_USERPTR) {
                        for (plane = 0; plane < vb->num_planes; ++plane) {
                                planes[plane].m.userptr =
                                        b->m.planes[plane].m.userptr;
                                planes[plane].length =
                                        b->m.planes[plane].length;
                        }
                }
                if (b->memory == VB2_MEMORY_DMABUF) {
                        for (plane = 0; plane < vb->num_planes; ++plane) {
                                planes[plane].m.fd =
                                        b->m.planes[plane].m.fd;
                                planes[plane].length =
                                        b->m.planes[plane].length;
                        }
                }

                /* Fill in driver-provided information for OUTPUT types */
                if (V4L2_TYPE_IS_OUTPUT(b->type)) {
                        /*
                         * Will have to go up to b->length when API starts
                         * accepting variable number of planes.
                         *
                         * If bytesused == 0 for the output buffer, then fall
                         * back to the full buffer size. In that case
                         * userspace clearly never bothered to set it and
                         * it's a safe assumption that they really meant to
                         * use the full plane sizes.
                         *
                         * Some drivers, e.g. old codec drivers, use bytesused == 0
                         * as a way to indicate that streaming is finished.
                         * In that case, the driver should use the
                         * allow_zero_bytesused flag to keep old userspace
                         * applications working.
                         */
                        for (plane = 0; plane < vb->num_planes; ++plane) {
                                struct vb2_plane *pdst = &planes[plane];
                                struct v4l2_plane *psrc = &b->m.planes[plane];

                                if (psrc->bytesused == 0)
                                        vb2_warn_zero_bytesused(vb);

                                if (vb->vb2_queue->allow_zero_bytesused)
                                        pdst->bytesused = psrc->bytesused;
                                else
                                        pdst->bytesused = psrc->bytesused ?
                                                psrc->bytesused : pdst->length;
                                pdst->data_offset = psrc->data_offset;
                        }
                }
        } else {
                //VIDIOC_REQBUFS时对memory属性进行配置
                //对用户空间的v4l2_requestbuffers进行判断
                /*
                 * Single-planar buffers do not use planes array,
                 * so fill in relevant v4l2_buffer struct fields instead.
                 * In videobuf we use our internal V4l2_planes struct for
                 * single-planar buffers as well, for simplicity.
                 *
                 * If bytesused == 0 for the output buffer, then fall back
                 * to the full buffer size as that's a sensible default.
                 *
                 * Some drivers, e.g. old codec drivers, use bytesused == 0 as
                 * a way to indicate that streaming is finished. In that case,
                 * the driver should use the allow_zero_bytesused flag to keep
                 * old userspace applications working.
                 */
                if (b->memory == VB2_MEMORY_USERPTR) {
                        planes[0].m.userptr = b->m.userptr;
                        planes[0].length = b->length;
                }
                //是否是DMAbuf
                if (b->memory == VB2_MEMORY_DMABUF) {
                        planes[0].m.fd = b->m.fd;
                        planes[0].length = b->length;
                }

                if (V4L2_TYPE_IS_OUTPUT(b->type)) {
                        if (b->bytesused == 0)
                                vb2_warn_zero_bytesused(vb);

                        if (vb->vb2_queue->allow_zero_bytesused)
                                planes[0].bytesused = b->bytesused;
                        else
                                planes[0].bytesused = b->bytesused ?
                                        b->bytesused : planes[0].length;
                } else
                        planes[0].bytesused = 0;

        }
        
         //将vb2_buffer的v4l2_buf赋值
        /* Zero flags that the vb2 core handles */
        vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS;
        if (!vb->vb2_queue->copy_timestamp || !V4L2_TYPE_IS_OUTPUT(b->type)) {
                /*
                 * Non-COPY timestamps and non-OUTPUT queues will get
                 * their timestamp and timestamp source flags from the
                 * queue.
                 */
                vbuf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
        }

        if (V4L2_TYPE_IS_OUTPUT(b->type)) {
                /*
                 * For output buffers mask out the timecode flag:
                 * this will be handled later in vb2_qbuf().
                 * The 'field' is valid metadata for this output buffer
                 * and so that needs to be copied here.
                 */
                vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE;
                vbuf->field = b->field;
        } else {
                /* Zero any output buffer flags as this is a capture buffer */
                vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS;
        }

        return 0;
}

/**
 * __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing
 */
static void __enqueue_in_driver(struct vb2_buffer *vb)
{
        struct vb2_queue *q = vb->vb2_queue;
        unsigned int plane;

        vb->state = VB2_BUF_STATE_ACTIVE;
        atomic_inc(&q->owned_by_drv_count);

        trace_vb2_buf_queue(q, vb);

        /* sync buffers */
        for (plane = 0; plane < vb->num_planes; ++plane)
                call_void_memop(vb, prepare, vb->planes[plane].mem_priv);

        call_void_vb_qop(vb, buf_queue, vb);
}

static struct vb2_ops video_qops = {
        .queue_setup = videoin_buffer_queue_setup,
        .start_streaming = videoin_start_streaming,
        .stop_streaming = videoin_stop_streaming,
        .buf_queue = videoin_buffer_queue,
        .wait_prepare = videoin_wait_prepare,
        .wait_finish = videoin_wait_finish,
};
/**
* videoin_buffer_queue : Callback function to add buffer to DMA queue
* @vb: ptr to vb2_buffer
*/
static void videoin_buffer_queue(struct vb2_buffer *vb)
{
        struct logical_device *lg_dev = vb2_get_drv_priv(vb->vb2_queue);
        struct vb2_queue_mgr *queue_mgr = &lg_dev->queue_mgr[VIDEOIN_VIDEO_INDEX];
        struct videoin_cap_buffer *buf = to_videoin_buffer(vb);
        unsigned long flags;
        struct capture_priv *cap_info = NULL;

        spin_lock_irqsave(&queue_mgr->irqlock, flags);
        //user 空间mmap后kernel中获取user buffer addr 
        cap_info = (struct capture_priv *)vb2_plane_vaddr(vb, 0U);
        if (NULL == cap_info) {
                vss_printk(VSS_LOG_ERR, "error with cap_info is NULL!");
                spin_unlock_irqrestore(&queue_mgr->irqlock, flags);
                return;
        }
        /* add the buffer to the DMA queue */
        //将videoin_cap_buffer添加到dma_queue上
        list_add_tail(&buf->list, &queue_mgr->dma_queue);
        spin_unlock_irqrestore(&queue_mgr->irqlock, flags);
        if (VIDEOIN_CHANNEL_STATE_FILE_OPENED == lg_dev->state){
                // HW is not power one. Power on first.
                videoin_open_device(lg_dev);
        }
        if ((VIDEOIN_CHANNEL_STATE_READY == lg_dev->state) || (VIDEOIN_CHANNEL_STATE_STARTED == lg_dev->state)) {
                if (data_path_que_buffer(*cap_info,&lg_dev->hw_path->hw_ch[lg_dev->ch_id])) {
                        vss_printk(VSS_LOG_ERR, "data_path_que_buffer error");
                        return;
                }
        }

}

int data_path_que_buffer(struct capture_priv priv_data, struct hw_channel **p_hw_ch)
{
        u8 stream_id = priv_data.stream_id;
        u8 buf_idx = priv_data.buf_idx;
        struct hw_channel *hw_ch = *p_hw_ch;
        enum channel_id ch_id = hw_ch->ch_id;
        struct hw_data_path *hw_path = container_of(p_hw_ch ,struct hw_data_path, hw_ch[ch_id]);
        enum user_data_path dp = hw_path->dp;
        struct port_buffer vis_buf;
        struct videoin_buf_addr *output_buf_addr = NULL;

        mutex_lock(&hw_path->lock);
        if ((STREAM_BUF_CNT_MAX == buf_idx) && (STREAM_CNT_MAX == stream_id)) {
                mutex_unlock(&hw_path->lock);
                vss_printk(VSS_LOG_DEBUG, "buf_idx=CNT_MAX & stream_id=CNT_MAX, means this queue just for v4l2 buffer list\n");
                return 0;
        }
        switch(hw_path->hw_status)
        {
                case VIDEOIN_STATUS_READY:
                case VIDEOIN_STATUS_STARTED:
                        break;
                default:
                        vss_printk(VSS_LOG_WARNING, "dp(%d) queue buffer in wrong status(%d)", dp, hw_path->hw_status);
                        mutex_unlock(&hw_path->lock);
                        return -1;
        }
        if ((STREAM_BUF_CNT_MAX == buf_idx) || (STREAM_CNT_MAX == stream_id)) {
                mutex_unlock(&hw_path->lock);
                vss_printk(VSS_LOG_DEBUG, " buf_idx(%d) stream_id(%d)\n", buf_idx, stream_id);
                return 0;
        }
        output_buf_addr = &(hw_ch->output_buf_addr[stream_id][0]);
        if (0 == output_buf_addr[buf_idx].y) {
                mutex_unlock(&hw_path->lock);
                vss_printk(VSS_LOG_ERR, "stream(%d), idx(%d) addr is null, may not set_fd yet", stream_id, buf_idx);
                return -1;
        }
        //获取output_buf_addr的数据
        memset(&vis_buf, 0, sizeof(struct port_buffer));
        vis_buf.plane_cnt   = output_buf_addr[buf_idx].plane_cnt;
        vis_buf.vir_addr[0] = output_buf_addr[buf_idx].y;
        vis_buf.vir_addr[1] = output_buf_addr[buf_idx].cb;
        vis_buf.vir_addr[2] = output_buf_addr[buf_idx].cr;
        vis_buf.y_virt      = output_buf_addr[buf_idx].y_virt;
        vis_buf.buf_idx     = buf_idx;
        vss_printk(VSS_LOG_DEBUG, "vis_buf.buf_idx=%d  pstream_info->port_id=%d  ch_id=%d y=0x%lx cb=0x%lx cr=0x%lx\n",
                                        vis_buf.buf_idx,stream_id,ch_id,vis_buf.vir_addr[0],vis_buf.vir_addr[1],vis_buf.vir_addr[2]);
        if (vis_buffer_queue(dp, stream_id, &vis_buf,ch_id)) {
                vss_printk(VSS_LOG_WARNING, "vis_buffer_queue error\n");
        }
        mutex_unlock(&hw_path->lock);
        return 0;
}

Videoin 中断处理

(3)[<ffffff800808bb7c>] dump_backtrace+0x0/0x2c0
[16:51:07][   72.319560]<2>[W](3)[<ffffff800808c36c>] show_stack+0x14/0x1c
[16:51:07][   72.319567]<2>[W](3)[<ffffff80083de478>] dump_stack+0x94/0xb4
[16:51:07][   72.319575]<2>[W](3)[<ffffff80084cc524>] callback_to_v4l2+0x94/0x108
[16:51:07][   72.319581]<2>[W](3)[<ffffff80084cdfd8>] buffer_done_handler_without_lock+0x1d0/0x2cc
[16:51:07][   72.319586]<2>[W](3)[<ffffff80084ce390>] mipi_buffer_done_handler+0x2bc/0x844
[16:51:07][   72.319590]<2>[W](3)[<ffffff80084dfb90>] mipi_irq_handler+0x1c4/0x398
[16:51:07][   72.319596]<2>[W](3)[<ffffff80081081e0>] __handle_irq_event_percpu+0x60/0x24c
[16:51:07][   72.319600]<2>[W](3)[<ffffff80081084a0>] handle_irq_event+0x58/0xc0
[16:51:07][   72.319605]<2>[W](3)[<ffffff800810bf14>] handle_fasteoi_irq+0xb4/0x1b0
[16:51:07][   72.319609]<2>[W](3)[<ffffff80081072a4>] generic_handle_irq+0x24/0x38
[16:51:07][   72.319612]<2>[W](3)[<ffffff80081079f4>] __handle_domain_irq+0x60/0xac
[16:51:07][   72.319616]<2>[W](3)[<ffffff8008080e4c>] gic_handle_irq+0xc0/0x120

Mipi 中断触发

mipi_buffer_done_handler
/#define DEBUG_PRINT_BUF
/**
 * Dma port: to be added.
 **/
static int mipi_buffer_done_handler(enum data_path dp,
                                        enum mop_id id,
                                        enum channel_id ch_id,
                                        struct buf_info *buf)
{
        unsigned long flags;
        enum dma_port port_id = (enum dma_port)id;
        enum user_data_path user_dp = USER_DP_MAX;
        bool is_top = false;
        enum videoin_device port_dev = VIDEOIN_DEVICE_MAX;
#ifdef DEBUG_PRINT_BUF
        int i = 0;
        char buf_src[100];
#endif
        user_dp = dp == DP_MIPI_1 ? MIPI_1 : MIPI_2;
        spin_lock_irqsave(&vis_irq_lock, flags);
        is_top = g_vis_ctrl[user_dp].filed_is_top[ch_id];
        g_vis_ctrl[user_dp].sequence[ch_id][port_id]++;
        spin_unlock_irqrestore(&vis_irq_lock, flags);
#ifdef DEBUG_PRINT_BUF
        if (0 != buf->y_addr_virt && CHANNEL_ID_0 == ch_id) {
                memcpy(buf_src,(char *)buf->y_addr_virt,50);
                vss_printk(VSS_LOG_INFO,"dp(%d) ch_id(%d) port(%d) buf_idx(%d)",
                                dp, ch_id, port_id, buf->buf_idx);
                for(i=0; i < 50; i++) {
                        pr_info( "0x%02X " , buf_src[i]);
                        if(24 == i) {
                                pr_info( "\n");
                        }
                }
                pr_info( "\n");
                memset((void *)buf->y_addr_virt,buf->buf_idx,0x32);
        }
#endif
        if (BUF_STATE_INVALID!= buf->state) {
                switch(g_vis_ctrl[dp].vis_output_type[port_id])
                {
                case OUTPUT_TYPE_IND: {
                        struct vip_info info;
                        memset(&info, 0, sizeof(struct vip_info));
                        dp_channel_to_device(dp, ch_id, port_id, false, &port_dev);
                        videoin_debug_set_frmae_cnt(port_dev, g_vis_ctrl[user_dp].sequence[ch_id][port_id]);
                        if (g_vis_ctrl[user_dp].is_interlace && g_vis_ctrl[user_dp].is_vip_enable[ch_id]) {
                                if (g_vis_ctrl[user_dp].is_used_four_channel) {
                                        if (g_vis_ctrl[user_dp].buffer_count[ch_id] < 16) {
                                                info.dp = user_dp;
                                                info.ch_id = ch_id;
                                                info.evt_type = VIDEOIN_DO_FILED_PROESS;
                                                info.is_avm_mode = false;
                                                info.buf_idx = buf->buf_idx;
                                                g_vis_ctrl[user_dp].make_v4l2_event(&info);
                                                g_vis_ctrl[user_dp].inter_buf[ch_id][buf->buf_idx] = buf;
                                                vss_printk(VSS_LOG_DEBUG, "dp=%d buf_idx=%d ch_id=%d ",user_dp,buf->buf_idx,buf->ch_id);
                                                g_vis_ctrl[user_dp].buffer_count[ch_id]++;
                                                goto exit;
                                        }
                                        if (!g_vis_ctrl[user_dp].waiting_detect_top_bottom[ch_id]) {
                                                if ((g_vis_ctrl[user_dp].buffer_count[ch_id]%2) != 0) {
                                                        list_push(&g_vis_ctrl[user_dp].vis_output_inter[ch_id].vis_list_buf, buf);
                                                        g_vis_ctrl[user_dp].buffer_count[ch_id]++;
                                                        goto exit;
                                                } else {
                                                        if (g_vis_ctrl[user_dp].is_first_time_set[ch_id]) {
                                                                g_vis_ctrl[user_dp].next_filed_property[ch_id] = is_top;
                                                                g_vis_ctrl[user_dp].is_first_time_set[ch_id] = false;
                                                        }
                                                        buf->is_bot_field = g_vis_ctrl[user_dp].next_filed_property[ch_id];
                                                        g_vis_ctrl[user_dp].next_filed_property[ch_id] = !buf->is_bot_field;
                                                }
                                        } else {
                                                list_push(&g_vis_ctrl[user_dp].vis_output_inter[ch_id].vis_list_buf, buf);
                                                g_vis_ctrl[user_dp].buffer_count[ch_id]++;
                                                goto exit;
                                        }
                                }
                                if ((buf->is_bot_field == g_vis_ctrl[dp].last_filed_property[ch_id])
                                        && g_vis_ctrl[dp].is_queued_vip ){
                                        list_push(&g_vis_ctrl[dp].vis_output_inter[ch_id].vis_list_buf, buf);
                                        return 0;
                                }
                        }
                        g_vis_ctrl[user_dp].valid_buffer_cnt[ch_id]++;
                        buffer_done_handler_without_lock(user_dp, ch_id, port_id, buf);
                break;
                }
                case OUTPUT_TYPE_FULL: {
                        struct vip_info info;
                        memset(&info, 0, sizeof(struct vip_info));
                        dp_channel_to_device(dp, ch_id, port_id, true, &port_dev);
                        videoin_debug_set_frmae_cnt(port_dev, g_vis_ctrl[user_dp].sequence[ch_id][port_id]);
                        if (g_vis_ctrl[user_dp].is_interlace && g_vis_ctrl[user_dp].is_vip_enable[ch_id]) {
                                if(g_vis_ctrl[user_dp].buffer_count[ch_id] < 16 ) {
                                        info.dp = user_dp;
                                        info.ch_id = ch_id;
                                        info.evt_type = VIDEOIN_DO_FILED_PROESS;
                                        info.is_avm_mode = false;
                                        info.buf_idx = buf->buf_idx;
                                        g_vis_ctrl[user_dp].make_v4l2_event(&info);
                                        g_vis_ctrl[user_dp].inter_buf[ch_id][buf->buf_idx] = buf;
                                        vss_printk(VSS_LOG_DEBUG, "dp=%d buf_idx=%d ch_id=%d ",user_dp,buf->buf_idx,buf->ch_id);
                                        g_vis_ctrl[user_dp].buffer_count[ch_id]++;
                                        goto exit;
                                }
                                if (!g_vis_ctrl[user_dp].waiting_detect_top_bottom[0]
                                && !g_vis_ctrl[user_dp].waiting_detect_top_bottom[1]
                                && !g_vis_ctrl[user_dp].waiting_detect_top_bottom[2]
                                && !g_vis_ctrl[user_dp].waiting_detect_top_bottom[3]) {
                                        if ((g_vis_ctrl[user_dp].buffer_count[ch_id]%2) != 0) {
                                                list_push(&g_vis_ctrl[user_dp].vis_output_inter[ch_id].vis_list_buf, buf);
                                                g_vis_ctrl[user_dp].buffer_count[ch_id]++;
                                                goto exit;
                                        } else {
                                                if (g_vis_ctrl[user_dp].is_first_time_set[ch_id]) {
                                                        g_vis_ctrl[user_dp].next_filed_property[ch_id] = is_top;
                                                        vss_printk(VSS_LOG_DEBUG, " monitor field :%d  check field:%d buf_cnt:%d ch_id:%d",
                                                                buf->is_bot_field,g_vis_ctrl[user_dp].next_filed_property[ch_id],g_vis_ctrl[user_dp].buffer_count[ch_id],ch_id);
                                                        g_vis_ctrl[user_dp].is_first_time_set[ch_id] = false;
                                                }
                                                if (0 == buf->ch_id) {
                                                        if (buf->is_bot_field != g_vis_ctrl[user_dp].next_filed_property[ch_id]) {
                                                                vss_printk(VSS_LOG_DEBUG, " monitor field :%d  check field:%d buf_cnt:%d,check failed!",
                                                                        buf->is_bot_field,g_vis_ctrl[user_dp].next_filed_property[ch_id],g_vis_ctrl[user_dp].buffer_count[ch_id]);
                                                        }
                                                }
                                                buf->is_bot_field = g_vis_ctrl[user_dp].next_filed_property[ch_id];
                                                g_vis_ctrl[user_dp].next_filed_property[ch_id] = !buf->is_bot_field;
                                        }
                                } else {
                                        g_vis_ctrl[user_dp].buffer_count[ch_id]++;
                                        list_push(&g_vis_ctrl[dp].vis_output_inter[ch_id].vis_list_buf,buf);
                                        goto exit;
                                }
                        }
                        g_vis_ctrl[user_dp].valid_buffer_cnt[ch_id]++;
                        buffer_done_handler_without_lock(user_dp, ch_id, port_id, buf);
                break;
                }
                case OUTPUT_TYPE_MAX:
                        return 0;
                }
        } else {
                g_vis_ctrl[user_dp].garbage_cnt[ch_id][port_id]++;
                switch (g_vis_ctrl[dp].vis_output_type[port_id]) {
                case OUTPUT_TYPE_IND: {
                        dp_channel_to_device(dp, ch_id, port_id, false, &port_dev);
                        videoin_debug_set_garbage_cnt(port_dev, g_vis_ctrl[user_dp].garbage_cnt[ch_id][port_id]);
                break;
                }
                case OUTPUT_TYPE_FULL: {
                        dp_channel_to_device(dp, ch_id, port_id, true, &port_dev);
                        videoin_debug_set_garbage_cnt(port_dev, g_vis_ctrl[user_dp].garbage_cnt[ch_id][port_id]);
                break;
                }
                case OUTPUT_TYPE_MAX:
                        return 0;
                }
                vss_printk(VSS_LOG_DEBUG, "use garbage frames ch_id = %d !!!!!!",ch_id);
        }
exit:
        return 0;
}
buffer_done_handler_without_lock
/*
 * struct buf_info *buf is vis_output_inter
 */
int buffer_done_handler_without_lock(enum user_data_path dp,enum channel_id ch_id,enum dma_port port_id, struct buf_info *buf)
{

        struct vis_buf *pbuf_vop1 = NULL;
        struct vis_buf *pbuf_vop2 = NULL;
        int ticketid = g_vis_ctrl[dp].vis_output_hal[ch_id].ticketid;
        if (BUF_STATE_INVALID!= buf->state) {
                g_vis_ctrl[dp].last_filed_property[ch_id] = buf->is_bot_field;
                g_vis_ctrl[dp].is_queued_vip = true;
        } else {
                vss_printk(VSS_LOG_DEBUG, "use garbage frames!!!!!!");
                return 0;
        }
        //is_vip_enable=false
        if (g_vis_ctrl[dp].is_vip_enable[ch_id])
        {
                if (g_vis_ctrl[dp].vip_drop[ch_id])
                {
                        pbuf_vop1 = &g_vis_vip_garbage_buffer;
                        g_vis_ctrl[dp].vip_drop[ch_id] = false;
                        vss_printk(VSS_LOG_DEBUG, " ch_id (%d) ticketid(%d) vip_drop first frame",
                                                                ch_id, ticketid);
                        vip_frame_process_async(buf, &pbuf_vop1->buf, NULL, ticketid);
                        goto push_vip_success;
                } else {
                        switch(g_vis_ctrl[dp].vis_output_type)
                        {
                                case OUTPUT_TYPE_IND:
                                case OUTPUT_TYPE_FULL:
                                        port_id = DMA_PORT_1;
                                        pbuf_vop1 = list_pop(&g_vis_ctrl[dp].vis_output_hal[ch_id].vis_list_buf[port_id]);
                                        if (g_vis_ctrl[dp].out_stream_num[ch_id] > 1) {
                                                port_id = DMA_PORT_2;
                                                pbuf_vop2 = list_pop(&g_vis_ctrl[dp].vis_output_hal[ch_id].vis_list_buf[port_id]);
                                                vip_frame_process_async(buf, &pbuf_vop1->buf, &pbuf_vop2->buf, ticketid);
                                        } else {
                                                vip_frame_process_async(buf, &pbuf_vop1->buf, NULL, ticketid);
                                        }
                                        goto push_vip_success;
                                case OUTPUT_TYPE_MAX:
                                        vss_printk(VSS_LOG_ERR, " invalid  vis_output_type (%d)\n",g_vis_ctrl[dp].vis_output_type);
                                        return -1;
                        }
                        list_push(&g_vis_ctrl[dp].vis_output_inter[ch_id].vis_list_buf, buf);
                }
        } else {
                switch(g_vis_ctrl[dp].vis_output_type)
                {
                        case OUTPUT_TYPE_IND:
                                callback_to_v4l2(dp,ch_id,port_id,buf->buf_idx,false);
                                break;
                        case OUTPUT_TYPE_FULL:
                                judge_avm_frame_ready(dp,ch_id,port_id,buf);
                                break;
                        case OUTPUT_TYPE_MAX:
                                vss_printk(VSS_LOG_ERR, " invalid  vis_output_type (%d)\n",g_vis_ctrl[dp].vis_output_type);
                                return -1;
                }
        }
push_vip_success:
        return 0;
}
callback_to_v4l2
void callback_to_v4l2(enum user_data_path dp,enum channel_id ch_id,
                        enum dma_port port_id,int buf_idx,bool is_need_handle)
{
        struct capture_priv priv_data;

        if (g_vis_ctrl[dp].pfn_buffer_done) {
                memset(&priv_data, 0x0, sizeof(struct capture_priv));
                priv_data.stream_id = port_id;
                priv_data.buf_idx = buf_idx;// HAL buffer index
                g_vis_ctrl[dp].pfn_buffer_done(dp, ch_id, port_id, &priv_data,is_need_handle);
        } else {
                vss_printk(VSS_LOG_ERR, " callback function pfn_buffer_done is NULL !\n");
        }
}
 videoin_buffer_complete
int videoin_buffer_complete(enum user_data_path dp,enum channel_id ch_id,
        enum dma_port port_id, struct capture_priv *data,bool is_need_handle)
{
        struct vb2_queue_mgr *queue_mgr = NULL;
        unsigned long flags = 0;
        enum videoin_device device = VIDEOIN_DEVICE_MAX;
        struct logical_device *lg_dev = NULL;
        dp_channel_to_device(dp, ch_id, port_id, false, &device);
        lg_dev = &g_videoin_dev_port_mgr.port_dev[device];
        if (lg_dev->hw_path->is_avm_mode[port_id]) {
                dp_channel_to_device(dp,ch_id, port_id, true, &device);
                lg_dev = &g_videoin_dev_port_mgr.port_dev[device];
        }
        queue_mgr = &(lg_dev->queue_mgr[0]);
        /* skip If streaming is not started in this channel */
        /* Check the field format */
        /* Progressive mode */

        spin_lock_irqsave(&queue_mgr->irqlock, flags);
        vss_printk(VSS_LOG_DEBUG, "buf_idx=%d  stream_id=%d state=%d device(%d)",data->buf_idx,data->stream_id,lg_dev->state,device);
        if (VIDEOIN_CHANNEL_STATE_STARTED != lg_dev->state) {
                vss_printk(VSS_LOG_WARNING, "device(%d) is not stream on, so ignore!",device);
                spin_unlock_irqrestore(&queue_mgr->irqlock, flags);
                return 0;
        }
        //判断dma queue 是否为空,不为空调用vb2_buffer_done
        if (list_empty(&queue_mgr->dma_queue)) {
                vss_printk(VSS_LOG_WARNING, "not have empty buffer");
                spin_unlock_irqrestore(&queue_mgr->irqlock, flags);
                return -EFAULT;
        }
        videoin_get_signal(lg_dev,&(data->signal_status[0]));
//      vss_printk(VSS_LOG_DEBUG, "_lyq device_type(%d) ch:%d  signal_status:%d %d %d %d", device, lg_dev->ch_id, data->signal_status[0], data->signal_status[1], data->signal_status[2], data->signal_status[3]);
        if (is_need_handle) {
                data->signal_status[lg_dev->ch_id] = VIDEOIN_SIGNAL_NONE;
        }

        vss_printk(VSS_LOG_DEBUG, "device_type(%d) ", device);
        if (!lg_dev->is_avm_mode) {
                data->signal_status[0] = data->signal_status[lg_dev->ch_id];
        }
//      vss_printk(VSS_LOG_DEBUG, "_lyq device_type(%d) signal_status:%d", device, data->signal_status[0]);
        videoin_schedule_next_buffer2(queue_mgr);
        videoin_process_buffer_complete(queue_mgr, data);
        spin_unlock_irqrestore(&queue_mgr->irqlock, flags);
        return 0;
}

static void videoin_schedule_next_buffer2(struct vb2_queue_mgr *queue_mgr)
{
       //videoin_cap_buffer 
        queue_mgr->cur_frm = list_entry(queue_mgr->dma_queue.next, struct videoin_cap_buffer, list);
        /* Remove that buffer from the buffer queue */
        list_del(&queue_mgr->cur_frm->list);
}
 videoin_process_buffer_complete 获取plane buffer的数据
/**
* videoin_process_buffer_complete: process a completed buffer
* @queue_mgr: ptr to queue_mgr channel object
*
* This function time stamp the buffer and mark it as DONE. It also
* wake up any process waiting on the QUEUE and set the next buffer
* as current
*/
static void videoin_process_buffer_complete(struct vb2_queue_mgr *queue_mgr,
                                                const struct capture_priv *data)
{
        struct capture_priv *cap_param = NULL;

        if (NULL == queue_mgr->cur_frm) {
                vss_printk(VSS_LOG_ERR, "cur v4l2 frame is null!\n");
                return;
        }
        //videoin_cap_buffer获取vb2_buffer 
        //将capture_priv数据拷贝到qbuf中设置的vb2_buffer的内存地址中
        cap_param = (struct capture_priv *)vb2_plane_vaddr(&queue_mgr->cur_frm->vb, 0U);
        memcpy(cap_param, data, sizeof(struct capture_priv));
        queue_mgr->cur_frm->vb.timestamp = ktime_get_ns();
        vb2_buffer_done(&queue_mgr->cur_frm->vb, VB2_BUF_STATE_DONE);
        queue_mgr->cur_frm = NULL;
}
 vb2_buffer_done 唤醒dequeue buffer
void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
{
        struct vb2_queue *q = vb->vb2_queue;
        unsigned long flags;
        unsigned int plane;

        if (WARN_ON(vb->state != VB2_BUF_STATE_ACTIVE))
                return;

        if (WARN_ON(state != VB2_BUF_STATE_DONE &&
                    state != VB2_BUF_STATE_ERROR &&
                    state != VB2_BUF_STATE_QUEUED &&
                    state != VB2_BUF_STATE_REQUEUEING))
                state = VB2_BUF_STATE_ERROR;

#ifdef CONFIG_VIDEO_ADV_DEBUG
        /*
         * Although this is not a callback, it still does have to balance
         * with the buf_queue op. So update this counter manually.
         */
        vb->cnt_buf_done++;
#endif
        dprintk(4, "done processing on buffer %d, state: %d\n",
                        vb->index, state);

        if (state != VB2_BUF_STATE_QUEUED &&
            state != VB2_BUF_STATE_REQUEUEING) {
                /* sync buffers */
                //vb2_vmalloc_memops finish未实现
                for (plane = 0; plane < vb->num_planes; ++plane)
                        call_void_memop(vb, finish, vb->planes[plane].mem_priv);
        }

        spin_lock_irqsave(&q->done_lock, flags);
        if (state == VB2_BUF_STATE_QUEUED ||
            state == VB2_BUF_STATE_REQUEUEING) {
                vb->state = VB2_BUF_STATE_QUEUED;
        } else {
                // 将buffer 挂到done_list上,切换状态VB2_BUF_STATE_DONE
                /* Add the buffer to the done buffers list */
                list_add_tail(&vb->done_entry, &q->done_list);
                vb->state = state;
        }
        atomic_dec(&q->owned_by_drv_count);
        spin_unlock_irqrestore(&q->done_lock, flags);

        trace_vb2_buf_done(q, vb);

        switch (state) {
        case VB2_BUF_STATE_QUEUED:
                return;
        case VB2_BUF_STATE_REQUEUEING:
                if (q->start_streaming_called)
                        __enqueue_in_driver(vb);
                return;
        default:
                //唤醒__vb2_wait_for_done_vb
                /* Inform any processes that may be waiting for buffers */
                wake_up(&q->done_wq);
                break;
        }
}
EXPORT_SYMBOL_GPL(vb2_buffer_done);

Dequeue buffer 

<ffffff800808bb7c>] dump_backtrace+0x0/0x2c0
[16:51:07][   72.368176]<2>W[<ffffff800808c36c>] show_stack+0x14/0x1c
[16:51:07][   72.368180]<2>W[<ffffff80083de478>] dump_stack+0x94/0xb4
[16:51:07][   72.368186]<2>W[<ffffff80086e6604>] vb2_core_dqbuf+0x48/0x614
[16:51:07][   72.368189]<2>W[<ffffff80086e9d84>] vb2_ioctl_dqbuf+0xd0/0xe4
[16:51:07][   72.368194]<2>W[<ffffff80086ca3ac>] v4l_dqbuf+0x54/0x60
[16:51:07][   72.368197]<2>W[<ffffff80086c9a8c>] __video_do_ioctl+0x1dc/0x2a4
[16:51:07][   72.368200]<2>W[<ffffff80086c954c>] video_usercopy+0x1b8/0x500
[16:51:07][   72.368203]<2>W[<ffffff80086c98a8>] video_ioctl2+0x14/0x1c
[16:51:07][   72.368209]<2>W[<ffffff80086c3d14>] v4l2_ioctl+0xf8/0x124
[16:51:07][   72.368214]<2>W[<ffffff800822d61c>] do_vfs_ioctl+0xb8/0x944
[16:51:07][   72.368218]<2>W[<ffffff800822df2c>] SyS_ioctl+0x84/0x98
[16:51:07][   72.368221]<2>W[<ffffff800808361c>] __sys_trace_return+0x0/0x4
 vb2_core_dqbuf
int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
                   bool nonblocking)
{
        struct vb2_buffer *vb = NULL;
        int ret;

        ret = __vb2_get_done_vb(q, &vb, pb, nonblocking);
        if (ret < 0)
                return ret;

        switch (vb->state) {
        case VB2_BUF_STATE_DONE:
                dprintk(3, "returning done buffer\n");
                break;
        case VB2_BUF_STATE_ERROR:
                dprintk(3, "returning done buffer with errors\n");
                break;
        default:
                dprintk(1, "invalid buffer state\n");
                return -EINVAL;
        }

        call_void_vb_qop(vb, buf_finish, vb);

        if (pindex)
                *pindex = vb->index;
        
        填充buffer数据到用户空间,应用程序可以通过这个buffer的mmap地址进行读取数据了
        /* Fill buffer information for the userspace */
        if (pb)
                call_void_bufop(q, fill_user_buffer, vb, pb);

        /* Remove from videobuf queue */
        list_del(&vb->queued_entry);
        q->queued_count--;

        trace_vb2_dqbuf(q, vb);

        /* go back to dequeued state */
        __vb2_dqbuf(vb);

        dprintk(1, "dqbuf of buffer %d, with state %d\n",
                        vb->index, vb->state);

        return 0;

}
EXPORT_SYMBOL_GPL(vb2_core_dqbuf);
 __vb2_get_done_vb
/**
 * __vb2_get_done_vb() - get a buffer ready for dequeuing
 *
 * Will sleep if required for nonblocking == false.
 */
static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
                             void *pb, int nonblocking)
{
        unsigned long flags;
        int ret = 0;
        

        /*
         * Wait for at least one buffer to become available on the done_list.
         */
        ret = __vb2_wait_for_done_vb(q, nonblocking);
        if (ret)
                return ret;

        /*
         * Driver's lock has been held since we last verified that done_list
         * is not empty, so no need for another list_empty(done_list) check.
         */
        spin_lock_irqsave(&q->done_lock, flags);
        //获取内核驱动填充过数据的 vb2_buffer 返回给应用层
        *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
        /*
         * Only remove the buffer from done_list if all planes can be
         * handled. Some cases such as V4L2 file I/O and DVB have pb
         * == NULL; skip the check then as there's nothing to verify.
         */
        if (pb)
                ret = call_bufop(q, verify_planes_array, *vb, pb);
        if (!ret)
                list_del(&(*vb)->done_entry);
        spin_unlock_irqrestore(&q->done_lock, flags);

        return ret;
}
 __vb2_wait_for_done_vb 等待vb2_buffer_done唤醒
/**
 * __vb2_wait_for_done_vb() - wait for a buffer to become available
 * for dequeuing
 *
 * Will sleep if required for nonblocking == false.
 */
static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
{
        /*
         * All operations on vb_done_list are performed under done_lock
         * spinlock protection. However, buffers may be removed from
         * it and returned to userspace only while holding both driver's
         * lock and the done_lock spinlock. Thus we can be sure that as
         * long as we hold the driver's lock, the list will remain not
         * empty if list_empty() check succeeds.
         */

        for (;;) {
                int ret;

                if (!q->streaming) {
                        dprintk(1, "streaming off, will not wait for buffers\n");
                        return -EINVAL;
                }

                if (q->error) {
                        dprintk(1, "Queue in error state, will not wait for buffers\n");
                        return -EIO;
                }

                if (q->last_buffer_dequeued) {
                        dprintk(3, "last buffer dequeued already, will not wait for buffers\n");
                        return -EPIPE;
                }
                /*
                * done_list上有buffer则跳出这个循环,继续往下走
                * 对于使用了select的方式,这里应该就返回了
                */

                if (!list_empty(&q->done_list)) {
                        /*
                         * Found a buffer that we were waiting for.
                         */
                        break;
                }
                   /*
                    * 如果应用传递的是不堵塞
                    * 那么这里直接返回
                    */
                if (nonblocking) {
                        dprintk(1, "nonblocking and no buffers to dequeue, "
                                                                "will not wait\n");
                        return -EAGAIN;
                }

                /*
                 * We are streaming and blocking, wait for another buffer to
                 * become ready or for streamoff. Driver's lock is released to
                 * allow streamoff or qbuf to be called while waiting.
                 */
                call_void_qop(q, wait_prepare, q);

                /*
                 * All locks have been released, it is safe to sleep now.
                 */
                dprintk(3, "will sleep waiting for buffers\n");
                //等待vb2_buffer_done唤醒
                ret = wait_event_interruptible(q->done_wq,
                                !list_empty(&q->done_list) || !q->streaming ||
                                q->error);

                /*
                 * We need to reevaluate both conditions again after reacquiring
                 * the locks or return an error if one occurred.
                 */
                call_void_qop(q, wait_finish, q);
                if (ret) {
                        dprintk(1, "sleep was interrupted\n");
                        return ret;
                }
        }
        return 0;
}
fill_v4l2_buffer填充buffer数据到用户空间,

应用程序可以通过这个buffer的mmap地址进行读取数据了

**
 * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
 * returned to userspace
 */
static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
{
        struct v4l2_buffer *b = pb;//用户空间的buffer
        struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
        struct vb2_queue *q = vb->vb2_queue;
        unsigned int plane;
        
        //填充v4l2_buffer
        /* Copy back data such as timestamp, flags, etc. */
        b->index = vb->index; //用户空间根据index 找到mmap的内存地址获取数据
        b->type = vb->type;
        b->memory = vb->memory;
        b->bytesused = 0;

        b->flags = vbuf->flags;
        b->field = vbuf->field;
        b->timestamp = ns_to_timeval(vb->timestamp);
        b->timecode = vbuf->timecode;
        b->sequence = vbuf->sequence;
        b->reserved2 = 0;
        b->reserved = 0;

        //多平面帧
        if (q->is_multiplanar) {
                /*
                 * Fill in plane-related data if userspace provided an array
                 * for it. The caller has already verified memory and size.
                 */
                b->length = vb->num_planes;
                for (plane = 0; plane < vb->num_planes; ++plane) {
                        struct v4l2_plane *pdst = &b->m.planes[plane];
                        struct vb2_plane *psrc = &vb->planes[plane];

                        pdst->bytesused = psrc->bytesused;
                        pdst->length = psrc->length;
                        if (q->memory == VB2_MEMORY_MMAP)
                                pdst->m.mem_offset = psrc->m.offset;
                        else if (q->memory == VB2_MEMORY_USERPTR)
                                pdst->m.userptr = psrc->m.userptr;
                        else if (q->memory == VB2_MEMORY_DMABUF)
                                pdst->m.fd = psrc->m.fd;
                        pdst->data_offset = psrc->data_offset;
                        memset(pdst->reserved, 0, sizeof(pdst->reserved));
                }
        } else {
                /*length: 平面的大小,可能与真实的数据帧大小不一致,驱动为了page对齐会变大
                 * byteused: 帧数据的大小,这里为0,后面的分析中可以看到更新
                 * 应用代码中有时候可能会用length代表帧数据的大小,这个值有可能是不准确的
                 * 比如帧大小是4812Byte,那么驱动中有可能为了方便,将length设置为5000
                 * 所以最好使用byteused*/

                /*
                 * We use length and offset in v4l2_planes array even for
                 * single-planar buffers, but userspace does not.
                 */
                b->length = vb->planes[0].length;
                b->bytesused = vb->planes[0].bytesused;
                if (q->memory == VB2_MEMORY_MMAP)
                        b->m.offset = vb->planes[0].m.offset;
                else if (q->memory == VB2_MEMORY_USERPTR)
                        b->m.userptr = vb->planes[0].m.userptr;
                else if (q->memory == VB2_MEMORY_DMABUF)
                        b->m.fd = vb->planes[0].m.fd;
        }

        /*
         * Clear any buffer state related flags.
         */
        b->flags &= ~V4L2_BUFFER_MASK_FLAGS;
        b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK;
        if (!q->copy_timestamp) {
                /*
                 * For non-COPY timestamps, drop timestamp source bits
                 * and obtain the timestamp source from the queue.
                 */
                b->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
                b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
        }

        switch (vb->state) {
        case VB2_BUF_STATE_QUEUED:
        case VB2_BUF_STATE_ACTIVE:
                b->flags |= V4L2_BUF_FLAG_QUEUED;
                break;
        case VB2_BUF_STATE_ERROR:
                b->flags |= V4L2_BUF_FLAG_ERROR;
                /* fall through */
        case VB2_BUF_STATE_DONE:
                b->flags |= V4L2_BUF_FLAG_DONE;
                break;
        case VB2_BUF_STATE_PREPARED:
                b->flags |= V4L2_BUF_FLAG_PREPARED;
                break;
        case VB2_BUF_STATE_PREPARING:
        case VB2_BUF_STATE_DEQUEUED:
        case VB2_BUF_STATE_REQUEUEING:
                /* nothing */
                break;
        }

        if (vb2_buffer_in_use(q, vb))
                b->flags |= V4L2_BUF_FLAG_MAPPED;

        if (!q->is_output &&
                b->flags & V4L2_BUF_FLAG_DONE &&
                b->flags & V4L2_BUF_FLAG_LAST)
                q->last_buffer_dequeued = true;
}

  • 13
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值