8.2 子模块分析之CSI

来源:互联网 发布:颜表情软件 编辑:程序博客网 时间:2024/05/22 08:14

1)概述

每个IPU有两个同样的CSI接口,下图是两个IPUCSI模块示意图:

每个CSI包括同步单元,逻辑接口,数据处理单元和sensor接口控制单元组成,如下所示:

CSI被外围的通用寄存器控制,同时是双buffer模式,CSI的主要作用是从sensor中获取数据,根据IPU时钟同步数据和控制信号然后将处理过的数据发送到DATA_DEST寄存器里面的目的地,目的地可以为SMFCICVDI等等,这个过程可以从下图理解:

2CSI接口

从第一个图里面可以看出来,CSI支持两种类型的接口:并行接口和MIPI接口。所使用的接口类型需要在IPUx_CONF寄存器中的DATA_SOURCE位配置。

2.1 并行接口

在并行接口模式下,每个时钟信号下传输一个值(除了BT.1120模式,每个时钟信号下传输两个值),每个值可以为816位。具体是多少位的,需要在对应的IPUx_CSIx_SENS_CONF寄存器中的DATA_WIDTH位配置,如下所示:

CSI可以支持几种不同数据格式,具体使用哪个数据格式,需要在IPUx_CSIx_SENS_CONF寄存器中的CSI0_SENS_DATA_FORMAT中设置,解释如下:

有时候在芯片手册中的时序图与CSI中的时序图中的极性是相反的,这时候就需要设置输入的极性,在IPUx_CSIx_SENS_CONF寄存器中SENS_PIX_CLK_POL,DATA_POL, HSYNC_POL and VSYNC_POL中配置。


2.2 MIPI接口

MIPI接口模式下每个时钟信号下传输两个值,每个值是8bit

当工作在这个模式下时,CSI能够处理4路输入信号,每一路信号都包含virtualchannel号和这一路的数据类型,通过DI(dataidentifier)来区分。只有MIPI_DI0这一路里面的数据能够发送到所有的目的地,其他路MIPI_DI1-3的数据只能发送到SMFC中。

在这种模式下,SENS_DATA_FORMATDATA_WIDTH位都被忽略,因为CSI的数据是从MCT_DI中传入的。具体有关MCT_DI的解释,需要去查看手册的《Chapter19 MIPI CSI to IPU Gasket (CSI2IPU)》那一章,暂时先不说。下面是IPUx_CSI0_DI的图示:

3)测试模式

IPUx_CSI0_TST_CTRL寄存器中的TEST_GEN_MODE位置1的话,就开启了测试模式,在这个模式下CSI会产生虚假的数据发送到目的地。

主要是设置IPUx_CSI0_TST_CTRL寄存器,如下所示:


4sensorCSI的帧关系

sensor产生图像大小与CSI接收的大小是不同的,具体看下图就基本清楚了。


AVSYNC信号确定的图像框。

BHSYNC信号确定的图像框。

Csensor传过来的图像框,这个框在SENS_FRM_WIDTHand SENS_FRM_HEIGHT里面配置。

DCSI选择的图像框。其中HSC,VSC, ACT_FRM_HEIGHT ACT_FRM_WIDTH都是可以配置的。


5Timing/Datamode protocols

CSI可以工作在几个不同的Timing/Datamodeprotocols下,根据IPUx_CSI0_SENS_CONF寄存器中的CSI0_SENS_PRTCL位来选择使用哪种模式/协议。

下面分析几种模式/协议

5.1 Gated Mode

在这个模式下,VSYNC信号在每一帧开始的时候触发,HSYNC信号在每一行的开始和结束的时候触发,同时时钟信号一直在走。时序图如下:

5.2 Non-Gated Mode

在这个模式下,VSYNC用来确认一帧的开始,只有当有传输数据的时候,时钟信号才走,HSYNC信号没有使用。当在MIPI模式下的时候,应该使用这种模式。


5.3 BT.656 mode

在这种模式下,CSI工作在ITU-RBT.656协议下,时序信号(framestart, frame end, line start, lineend)嵌入在数据流中。有两个定时基准信号,一个在每个视频数据块的开始(Startof ActiveVideoSAV),另一个在每个视频数据块的结束(Endof Active VideoEAV);每个定时基准信号由4个字的序列组成,格式如下:FF00 00 XY 16进制)头三个是固定前缀,第4个字包含定义第二场标识、场消隐状态和行消隐状态的信息。其中这4个字中的前三个字在CCIR_PRECOMCCIR_CODE_3)中配置,第四个字在CCIR_CODE_1CCIR_CODE_2中配置。为什么这里的寄存器名称为CCIR这么奇怪的名字???因为CCIR656ITU-RBT656的旧称。同理,在程序中有关6561120模式的名称都使用的CCIR


关于这个协议的详细介绍可以从网上查,《入门视频采集与处理(BT656简介)

http://www.cnblogs.com/s_agapo/archive/2012/04/08/2437775.html

ITU-RBT.656协议

http://www.cnblogs.com/crazybingo/archive/2011/03/27/1996974.html


5.4 BT.1120 mode

这个模式又分为SDRmodeDDRmode

上面这几种模式都涉及到IPUx_CSI0_CCIR_CODE_13这几个寄存器,如下所示:


上述的时钟模式设置在内核中是通过ipu_csi_init_interface函数来设置的。


6Packingto memory

CSI中传入SMFC的数据是128-bit的,所以CSI需要对数据进行处理后才能发送给SMFC,下图是处理过程:

7Skippingframes

许多帧数据不需要发送到SMFC,所以就可以在CSI中跳过这些数据,不发送到SMFC。需要操作IPUx_CSI0_SKIP寄存器中的CSI0_SKIP_SMFCCSI0_MAX_RATIO_SKIP_SMFC位,如下所示:

8CSI缺点

sensor的时钟不能比IPU时钟(HSP_CLK)大。

SENS_FRM_HEIGHT>= VSC + ACT_FRM_HEIGHT

SENS_FRM_WIDTH>= HSC + ACT_FRM_WIDTH


9)代码实现

上述有关CSI的一些设置,是需要在驱动中通过代码来实现的,下面来分析有关的代码:

9.1驱动中定义了一个有关CSI的结构体类型:

typedef struct { unsigned data_width:4; unsigned clk_mode:3; unsigned ext_vsync:1; unsigned Vsync_pol:1; unsigned Hsync_pol:1; unsigned pixclk_pol:1; unsigned data_pol:1; unsigned sens_clksrc:1; unsigned pack_tight:1; unsigned force_eof:1; unsigned data_en_pol:1; unsigned data_fmt; unsigned csi; unsigned mclk; } ipu_csi_signal_cfg_t

这个结构体中定义了CSI需要用到的很多参数,信号的极性和模式。后面我们就可以看到,在驱动中去设置这个结构体,并根据这个结构体里面的参数去设置IPU中有关CSI的寄存器的值。


9.2 开发板可以支持多种摄像头设备,而每个摄像头设备的具体参数也是不同的,所以,驱动程序中需要使用vidioc_int_g_ifparmvidioc_int_g_fmt_cap函数来询问当前的摄像头设备它的各个参数是什么,然后根据获得的参数来设置有关CSI的寄存器。

ov5640_mipi为例来说明,当驱动程序中调用vidioc_int_g_ifparm时,最终会调用到ov5640_mipi.c中的ioctl_g_ifparm函数:

static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) { if (s == NULL) { pr_err("   ERROR!! no slave device set!\n"); return -1; } memset(p, 0, sizeof(*p)); p->u.bt656.clock_curr = ov5640_data.mclk; pr_debug("   clock_curr=mclk=%d\n", ov5640_data.mclk); p->if_type = V4L2_IF_TYPE_BT656; p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; p->u.bt656.clock_min = OV5640_XCLK_MIN; p->u.bt656.clock_max = OV5640_XCLK_MAX; p->u.bt656.bt_sync_correct = 1;  /* Indicate external vsync */ return 0; }

之后驱动程序会根据上面设置的这些内容来对csi_param进行设置,这个csi_param9.1中所说的ipu_csi_signal_cfg_t类型的,如下所示:

csi_param.data_width = 0; csi_param.clk_mode = 0; csi_param.ext_vsync = 0; csi_param.Vsync_pol = 0; csi_param.Hsync_pol = 0; csi_param.pixclk_pol = 0; csi_param.data_pol = 0; csi_param.sens_clksrc = 0; csi_param.pack_tight = 0; csi_param.force_eof = 0; csi_param.data_en_pol = 0; csi_param.data_fmt = 0; csi_param.csi = cam->csi; csi_param.mclk = 0; pr_debug("   clock_curr=mclk=%d\n", ifparm.u.bt656.clock_curr); if (ifparm.u.bt656.clock_curr == 0) csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; else csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv; if (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) { csi_param.data_width = IPU_CSI_DATA_WIDTH_8; } else if (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) { csi_param.data_width = IPU_CSI_DATA_WIDTH_10; } else { csi_param.data_width = IPU_CSI_DATA_WIDTH_8; } csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv; csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv; csi_param.ext_vsync = ifparm.u.bt656.bt_sync_correct;

驱动程序中继续调用vidioc_int_g_fmt_cap函数来获取摄像头的像素格式,以及宽度高度等信息,最终会调用到ov5640_mipi.c中的ioctl_g_fmt_cap函数:

static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) { struct sensor_data *sensor = s->priv; f->fmt.pix = sensor->pix; return 0; }

获取到这些参数后,根据它们来设置驱动中cam结构体里面的参数,如下所示:

cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); pr_debug("   g_fmt_cap returns widthxheight of input as %d x %d\n", cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height); csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat; cam->crop_bounds.top = cam->crop_bounds.left = 0; cam->crop_bounds.width = cam_fmt.fmt.pix.width; cam->crop_bounds.height = cam_fmt.fmt.pix.height;

最终通过上面这些函数调用,驱动程序已经知道了摄像头设备的信号极性,模式,像素信息等等,接下来就需要将这些信息设置到CSI相关的寄存器中,最主要的是ipu_csi_init_interface函数:

int32_t ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height, uint32_t pixel_fmt, ipu_csi_signal_cfg_t cfg_param) { uint32_t data = 0; uint32_t csi = cfg_param.csi; /* Set SENS_DATA_FORMAT bits (8, 9 and 10)    RGB or YUV444 is 0 which is current value in data so not set    explicitly    This is also the default value if attempts are made to set it to    something invalid. */ switch (pixel_fmt) { case IPU_PIX_FMT_YUYV: cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV; break; case IPU_PIX_FMT_UYVY: cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY; break; case IPU_PIX_FMT_RGB24: case IPU_PIX_FMT_BGR24: cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444; break; case IPU_PIX_FMT_GENERIC: case IPU_PIX_FMT_GENERIC_16: cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; break; case IPU_PIX_FMT_RGB565: cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565; break; case IPU_PIX_FMT_RGB555: cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555; break; default: return -EINVAL; } /* Set the CSI_SENS_CONF register remaining fields */ data |= cfg_param.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | cfg_param.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT | cfg_param.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT | cfg_param.Vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT | cfg_param.Hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT | cfg_param.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT | cfg_param.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT | cfg_param.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT | cfg_param.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT | cfg_param.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT | cfg_param.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT; _ipu_get(ipu); mutex_lock(&ipu->mutex_lock); ipu_csi_write(ipu, csi, data, CSI_SENS_CONF); /* 将摄像头的信号信息写到 CSI_SENS_CONF寄存器中,可以对照芯片手册来看看这些位都设置成了什么。 *//* Setup sensor frame size */ ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_SENS_FRM_SIZE); /* 将摄像头的frame宽度和高度信息写到 CSI_SENS_FRM_SIZE寄存器中。 *//* Set CCIR registers */ if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) { ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1); ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3); } else if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED) { if (width == 720 && height == 625) { /* PAL case */ /*  * Field0BlankEnd = 0x6, Field0BlankStart = 0x2,  * Field0ActiveEnd = 0x4, Field0ActiveStart = 0  */ ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_1); /*  * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,  * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1  */ ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_2); ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3); } else if (width == 720 && height == 525) { /* NTSC case */ /*  * Field0BlankEnd = 0x7, Field0BlankStart = 0x3,  * Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1  */ ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_1); /*  * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,  * Field1ActiveEnd = 0x4, Field1ActiveStart = 0  */ ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_2); ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3); } else { dev_err(ipu->dev, "Unsupported CCIR656 interlaced " "video mode\n"); mutex_unlock(&ipu->mutex_lock); _ipu_put(ipu); return -EINVAL; } _ipu_csi_ccir_err_detection_enable(ipu, csi); } else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR) || (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR) || (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR) || (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR)) { ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1); ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3); _ipu_csi_ccir_err_detection_enable(ipu, csi); } else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_GATED_CLK) ||    (cfg_param.clk_mode == IPU_CSI_CLK_MODE_NONGATED_CLK)) { _ipu_csi_ccir_err_detection_disable(ipu, csi); } /* 这一段代码是有关CCIR相关的设置,在上面介绍数据传输协议的时候说了,当用BT.656或者BT.1120模式传输的时候,需要设置 定时基准信号中4个字的序列组成,就是在这几个寄存器中设置的。 */mutex_unlock(&ipu->mutex_lock); _ipu_put(ipu); return 0; } EXPORT_SYMBOL(ipu_csi_init_interface);

另外,设置CSI的相关函数还有ipu_csi_set_window_sizeipu_csi_set_window_pos,这两个函数比较简单,就不分析了。


通过如上的代码,就可以设置好CSI相关的寄存器。


0 0
原创粉丝点击