全志paralle csi 调试

与 cvbs以及 mipi csi不同.T7自带的parallel csis示例程序 一打开,就会产生i2c报错,导致kernel crush.

开始调试吧.

一,熟悉硬件原理图,确认输入流程,需要使用哪些硬件.

 1,2个独立的csi接口,16位宽.

    2,支持 8,10,12,16数字camera输入.

 3,支持ddr sample mode 

    4,支持CCIR656/bt1120输入协议,mode PAL和NTSC.

    5,最大拍照分辨率5M, 录像1080p,时钟148.5Mhz

T7平台上,N-CSI的输入,经过input Parser,video input post process and DMA.不经过ISP.

二,目前使用平台的car_reverse代码测试。

log中,总是有 chip 0x31/0x30 addr 0xff, value 0x0, i2c transfer err的错误.怀疑在是 csic的Camera,调用了subdev sensor,使用了i2c进行power,init等操作导致的.

目前准备重新从找找有哪些i2c操作.

先从 sensor开始.

首先,认真看vin_probe函数.

1,parse_modules_from_device_tree

   好吧,不想看了,,,主要是从dts解析sensor信息,然后注册sd的操作.

  关键是 注释掉 这一行之后,就没有 i2c报错了。。。

  看了一下sensor的slave-id,是0x60与0x62。右移一位,就变成0x30与0x31了。注释掉sensor写寄存器的操作,继续往后。

2,car_reverse函数

    首先开启一个 car_reverse->display_update_task =
        kthread_create(display_update_thread, NULL, "sunxi-preview");   

          ret = spin_is_locked(&car_reverse->display_lock);
           if (ret) {
            goto disp_loop;
           }     

           disp_loop:
                   set_current_state(TASK_INTERRUPTIBLE);
                   if (kthread_should_stop())
                       set_current_state(TASK_RUNNING);

                   car_reverse->thread_mask &= (~THREAD_RUN);
                   schedule();//当前进程如果不是running ,那么进行切换。

    wake_up_process(car_reverse->display_update_task);
    car_reverse->thread_mask |= THREAD_RUN;
    spin_unlock(&car_reverse->thread_lock);等待唤醒.

 

那么,谁调用他的呢?

car_reverse_preview_start  调用 video_source_streamon_vin 注册了 buffer_done_callback

然后在 static irqreturn_t vin_isr(int irq, void *priv) 中,调用  vin_buffer_process ,也就是 buffer_done_callback

 

Pass::INIT_WORK(&car_reverse->status_detect, status_detect_func);这个函数的含义是?

好吧,反正今天最终没调通,目前看到的是没有触发DMA中断,明天继续

昨天失败了,今天准备自己写一个demo程序,只是参考全志的代码,不再完全搬运了.

一,分析源码.首先从car_reverse_probe函数开始.

1,分配了一个 静态变量,保存为 car_reverse pdev的drv_data

    car_reverse = devm_kzalloc(
        &pdev->dev, sizeof(struct car_reverse_private_data), GFP_KERNEL);

    然后,关注一下car_reverse pdev的 PM操作.

    car_reverse_pm_ops = {
        .suspend {car_reverse->standby = 1;flush_workqueue(car_reverse->preview_workqueue);}

        .resume{car_reverse->standby = 0;queue_work(car_reverse->preview_workqueue, &car_reverse->status_detect);}

2, 解析dts

    of_get_value_by_name(pdev, "tvd_id", &priv->config.tvd_id, 0);
    of_get_value_by_name(pdev, "screen_width", &priv->config.screen_width,
                 0);
    of_get_value_by_name(pdev, "screen_height", &priv->config.screen_height,
                 0);
    of_get_value_by_name(pdev, "rotation", &priv->config.rotation, 1);
    of_get_value_by_name(pdev, "source", &priv->config.input_src, 1);
    of_get_gpio_by_name(pdev, "reverse_pin", &priv->reverse_gpio);

3, 注册一个状态检测函数???

    car_reverse->status = CAR_REVERSE_STOP;

    INIT_WORK(&car_reverse->status_detect, status_detect_func);

     car_reverse->preview_workqueue =
            create_singlethread_workqueue("car-reverse-wq");

     queue_work(car_reverse->preview_workqueue, &car_reverse->status_detect);

    //通过status_detect_func 调用car_reverse_preview_start;car_reverse_preview_stop;

4, 创建 预览更新线程.

    car_reverse->display_update_task =
        kthread_create(display_update_thread, NULL, "sunxi-preview");

5, 注册中断

    request_irq(reverse_pin_irqnum, reverse_irq_handle,IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,"car-reverse", pdev)

    reverse_irq_handle {queue_work(car_reverse->preview_workqueue, &car_reverse->status_detect);}

也就是说,整个流程是通过 irq,调用 status_detect_func, 获取 gpio状态.根据gpio状态,选择调用car_reverse_preview_start/car_reverse_preview_stop, 这两个函数中,又会唤醒display_update_thread线程.

6 补充

static DECLARE_DELAYED_WORK(car_freework, car_do_freemem);//这个是干嘛的?

cancel_delayed_work(&car_freework);

USE_SUNXI_DI_MODULE//这个是 视屏后处理技术 deinterlace 去交错的意思.就是将隔行扫描,变成逐行扫描.

二,分析preview与v4l2函数.

1, buffer管理.(注册,分配,设置)

    car_reverse->buffer_pool = alloc_buffer_pool(car_reverse->config.dev,  SWAP_BUFFER_CNT_VIN, 1280 * 720 * 2);

    INIT_LIST_HEAD(&car_reverse->pending_frame);

2, video_source_connect(&car_reverse->config);

    这里面除了普通的注册之外,还调用了一个 vin_core_gbl 设备.
    也就是vin_core中注册的v4l2设备. 

    struct vin_core *vinc = vin_core_gbl[id];
    struct list_head *active = &vinc->vid_cap.vidq_active;
    struct list_head *done = &vinc->vid_cap.vidq_done;

    INIT_LIST_HEAD(active);
    INIT_LIST_HEAD(done);

    set_bit(VIN_BUSY, &vinc->vid_cap.state);

     dramfreq_master_access(MASTER_CSI, true); //?????

    tvd_fd = params->tvd_id;   //静态变量,用于在 vin_core_gbl中找到相应的vin_core设备.
 video_source_format_setting(params);
 {format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 video_source_format_setting(params);
 vin_s_input_special(tvd_fd, 0);
 vin_g_fmt_special(tvd_fd, &format);
 params->locked = 1;
 format.fmt.pix.width  = 1280;
 format.fmt.pix.height = 720;
 params->system = format.fmt.raw_data[105];
 params->interface = format.fmt.raw_data[104];
 format_prew.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 format_prew.fmt.pix.pixelformat = params->format;
 format_prew.fmt.pix.width  = format.fmt.pix.width;
 format_prew.fmt.pix.height = format.fmt.pix.height;
 vin_s_fmt_special(tvd_fd,  &format_prew)
 }

car_reverse->config.format = V4L2_PIX_FMT_YUYV;

__vin_sensor_setup_link  sensor与csi进行link.

vin_pipeline_call(vinc, open, &cap->pipe, &cap->vdev.entity, true);   //调用 vin_core的 open函数.

__vin_pipeline_open    vin_pipeline_s_power  //各sub_dev上电v4l2_subdev_call(sd, core, s_power, on);

妈蛋,函数太多了,除了sensor,以外,isp,csi,paraser,dma都在配置。

由于硬件输入的线都是硬件飞的,决定还是先从接口模块paraser开始排查吧。

csic_prs_pclk_en(csi->id, enable);

csic_prs_enable(csi->id);

csic_prs_mode(csi->id, PRS_NCSI);

csic_prs_ncsi_bt656_header_cfg(csi->id, &bt656_header);
csic_prs_ncsi_if_cfg(csi->id, &csi->ncsi_if);
csic_prs_ncsi_en(csi->id, 1);

csic_prs_input_fmt_cfg(csi->id, i, csi->csi_fmt->infmt);
csic_prs_output_size_cfg(csi->id, i, &csi->out_size);

csic_prs_capture_start(csi->id, csi->bus_info.ch_total_num, &mode);

发现这些函数,并没有返回状态,按理来说,这种硬件数据通路,应该会有状态位变化吧。。。

加一个show函数看看。

3. 添加debug节点。

    调用show函数时,将csi_parse的所有相关寄存器打印。store函数,使能中断状态寄存器。

结果,发现初始时,csi2_parse的4个通道控制寄存器全部初始化为0。调用CSI测试函数后,这段算是正确的赋值了。

但是,插上外接信号源,拔出外接信号源。通道状态寄存器,以及中断状态寄存器,都没有发现任何变化。果断怀疑是硬件问题,联系硬件同事测量信号,发现有个sync信号没连好。

最终结果,parse_状态寄存器明显接受到信号了,但是预览仍然没有数据输出。需要进一步调试了,不知道下一步,是scaler,isp,还是DMA啥的?

回顾硬件通路,

csi_parser.2->isp.0(not_used)->vipp(scaler).0->dma.0

这条通路的选择,是在vind选择vinc的时候,在vinc0中创建的.不同的vinc对应不同的硬件通路,但具体应该怎么选择,以后再看吧.当前的问题时,先看这条硬件通路,卡在哪里了.

首先已经知道的是,csi_parser.2能够检测到数据,只是不清楚这个数据有没有传给isp.

由于配置了isp_not_used,那么现在的排查点,就是vipp(scaler).0有没有检测到数据,如果检测到数据了,再看DMA.没有接收到数据的话,就需要尝试换一下isp了.修改成csi_parser.2->isp.2->vipp(scaler).4->dma.4.

 

下面这段代码有些疑惑.

    for (i = 0; i < vin_cap->vinc->total_rx_ch; i++)
        csic_isp_input_select(vind->id, vin_cap->vinc->isp_sel, i,
                vin_cap->vinc->csi_sel, i);

    csic_vipp_input_select(vind->id, vin_cap->vinc->vipp_sel,
            vin_cap->vinc->isp_sel, vin_cap->vinc->isp_tx_ch);

这个是啥?为啥只循环设置了isp的input,没有循环设置vipp的input???

dump出寄存器后,对照datasheet,发现配置的与选择的的相同,没看出问题所在.

最后的最后,自己重新写了一个CSI demo程序,打开/dev/video0,,然后居然就好了....

对比了一下正常和异常时候的寄存器,没啥差异的...

再次回忆一下,输入的是 不标准的 bt656,数据线8根,信号参数如下.

Horizontal Blank pixel:  120 pixel
  Horizontal Blank clock:  2*120 = 240 clocks.
  Total Horizontal Clcok :   2560+240 = 2800 clocks
  Vertical Blank lines :          30 lines
  Total Vertical lines             720+30 = 750 lines.
  Pixel clock :    2800*750*30 = 63M

在配置驱动的时候,应该选择 bt656 1channel,图像格式MEDIA_BUS_FMT_UYVY8_2X8,mbus_confi V4L2_MBUS_BT656,pol,channel 1.NCSI0, parser2,isp0,vipp0,dma0.

对于硬件配置,一直感觉很好奇,按照当前的配置,sov与eov之间有2560个数据,这部分是配置成MEDIA_BUS_FMT_UYVY8_2X8格式后,会自动解析么?反正,最后结果是好的,后面继续总结一下本次调试中略过的细节吧.

你可能感兴趣的:(car)