与 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格式后,会自动解析么?反正,最后结果是好的,后面继续总结一下本次调试中略过的细节吧.