4.7 ipu_capture.c分析

ipu_common.c文件中,会调用到这个文件很多底层的函数,来设置视频捕获设备中底层的一些操作寄存器。这个文件就直接从头至尾开始分析:


1._ipu_csi_mclk_set函数

int _ipu_csi_mclk_set(struct ipu_soc *ipu, uint32_t pixel_clk, uint32_t csi) 
{ 
	uint32_t temp; 
	uint32_t div_ratio; 

	div_ratio = (clk_get_rate(ipu->ipu_clk) / pixel_clk) - 1; 

	if (div_ratio > 0xFF || div_ratio < 0) { 
		dev_dbg(ipu->dev, "value of pixel_clk extends normal range\n"); 
		return -EINVAL; 
	} 

	temp = ipu_csi_read(ipu, csi, CSI_SENS_CONF); 
	temp &= ~CSI_SENS_CONF_DIVRATIO_MASK; 
	ipu_csi_write(ipu, csi, temp | 
			(div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT), 
			CSI_SENS_CONF); 

	return 0; 
}

这个函数是设置csi设备的mclk时钟参数,首先根据想要设置的pixel_clk时钟参数计算出来需要设置的div_ratio值,然后通过ipu_csi_read函数读出原来的CSI_SENS_CONF寄存器的值,然后将div_ratio设置进去,再通过ipu_csi_write将值重新写到CSI_SENS_CONF寄存器中。


2.ipu_csi_init_interface函数,这个函数被mxc_v4l2_capture.cmxc_v4l_open函数和mxc_v4l2_s_param函数调用。

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; 
	} 

/*首先根据传进来的不同pixel_fmt参数的值,设置cfg_param.data_fmt的值。*/

	/* 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); 

/*根据传进来的widthheight参数,设置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); 
	} 

/*根据cfg_param.clk_mode参数的不同值来设置CSI_CCIR_CODE_1CSI_CCIR_CODE_2CSI_CCIR_CODE_3寄存器的值,同时在IPU_CSI_CLK_MODE_CCIR656_INTERLACED的情况下也会参考widthheight的值进行设置。其中会调用到_ipu_csi_ccir_err_detection_enable函数,这个函数在后面分析。在下面13中分析。*/

	dev_dbg(ipu->dev, "CSI_SENS_CONF = 0x%08X\n", 
		ipu_csi_read(ipu, csi, CSI_SENS_CONF)); 
	dev_dbg(ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", 
		ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE)); 

	mutex_unlock(&ipu->mutex_lock); 

	_ipu_put(ipu); 

	return 0; 
} 
EXPORT_SYMBOL(ipu_csi_init_interface);

3.ipu_csi_get_sensor_protocol函数,这个函数会在ipu_fg_overlay_sdc.c文件中的csi_enc_setup函数中调用:

int32_t ipu_csi_get_sensor_protocol(struct ipu_soc *ipu, uint32_t csi) 
{ 
	int ret; 
	_ipu_get(ipu); 
	ret = (ipu_csi_read(ipu, csi, CSI_SENS_CONF) & 
		CSI_SENS_CONF_SENS_PRTCL_MASK) >> 
		CSI_SENS_CONF_SENS_PRTCL_SHIFT; 
	_ipu_put(ipu); 
	return ret; 
} 
EXPORT_SYMBOL(ipu_csi_get_sensor_protocol);

这个函数的大致意思就是通过指定的ipu的某一个csi,返回这个csi设备的CSI_SENS_CONF寄存器里面已经配置好的值。


4.ipu_csi_enable_mclk函数,在mxc_v4l2_capture.c文件中调用了ipu_csi_enable_mclk_if函数,这个函数其实就是ipu_csi_enable_mclk函数。

int ipu_csi_enable_mclk(struct ipu_soc *ipu, int csi, bool flag, bool wait) 
{ 
	/* Return immediately if there is no csi_clk to manage */ 
	if (ipu->csi_clk[csi] == NULL) 
		return 0; 

	if (flag) { 
		clk_enable(ipu->csi_clk[csi]); 
		if (wait == true) 
			msleep(10); 
	} else { 
		clk_disable(ipu->csi_clk[csi]); 
	} 

	return 0; 
} 
EXPORT_SYMBOL(ipu_csi_enable_mclk);

这个函数根据传入的flag参数来决定使能还是关闭时钟,如果flagtrue的话,就调用clk_enable函数使能时钟,如果flag参数为false的话,就调用clk_disable函数来关闭时钟。


5.ipu_csi_get_window_size函数,这个函数在ipu_prp_vf_sdc_bg.cipu_prp_vf_sdc.cipu_prp_enc.c中都有调用。

void ipu_csi_get_window_size(struct ipu_soc *ipu, uint32_t *width, uint32_t 										*height, uint32_t csi) 
{ 
	uint32_t reg; 

	_ipu_get(ipu); 

	mutex_lock(&ipu->mutex_lock); 

	reg = ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE); 
	*width = (reg & 0xFFFF) + 1; 
	*height = (reg >> 16 & 0xFFFF) + 1; 

	mutex_unlock(&ipu->mutex_lock); 

	_ipu_put(ipu); 
} 
EXPORT_SYMBOL(ipu_csi_get_window_size);

这个函数就是从指定ipu的某个csi设备中,读取CSI_ACT_FRM_SIZE寄存器的值保存在*width*height中返回。


6.ipu_csi_set_window_size函数,这个函数在mxc_v4l2_capture.c中的mxc_v4l2_s_parammxc_v4l_openinit_camera_struct,和mxc_v4l_do_ioctlVIDIOC_S_CROP函数中都有调用。

void ipu_csi_set_window_size(struct ipu_soc *ipu, uint32_t width, uint32_t height, uint32_t csi) 
{ 
	_ipu_get(ipu); 

	mutex_lock(&ipu->mutex_lock); 

	ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16,  CSI_ACT_FRM_SIZE); 

	mutex_unlock(&ipu->mutex_lock); 

	_ipu_put(ipu); 
} 
EXPORT_SYMBOL(ipu_csi_set_window_size);

这个函数就是将传进来的widthheight参数设置到CSI_ACT_FRM_SIZE寄存器中。


7.ipu_csi_set_window_pos函数,这个函数和上一个ipu_csi_set_window_size函数是一起使用的。想要确定一个窗口的大小,只需要知道它的左上角坐标和窗口的长度/宽度就可以设置了。

void ipu_csi_set_window_pos(struct ipu_soc *ipu, uint32_t left, uint32_t top, uint32_t csi) 
{ 
	uint32_t temp; 

	_ipu_get(ipu); 

	mutex_lock(&ipu->mutex_lock); 

	temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL); 
	temp &= ~(CSI_HSC_MASK | CSI_VSC_MASK); 
	temp |= ((top << CSI_VSC_SHIFT) | (left << CSI_HSC_SHIFT)); 
	ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL); 

	mutex_unlock(&ipu->mutex_lock); 

	_ipu_put(ipu); 
} 
EXPORT_SYMBOL(ipu_csi_set_window_pos);

将传进来的lefttop参数对应设置到CSI_OUT_FRM_CTRL寄存器中即可。


8._ipu_csi_horizontal_downsize_enable函数,这个函数暂时没人调用。。。

void _ipu_csi_horizontal_downsize_enable(struct ipu_soc *ipu, uint32_t csi) 
{ 
	uint32_t temp; 

	temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL); 
	temp |= CSI_HORI_DOWNSIZE_EN; 
	ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL); 
}

函数的大致意思是设置csi设备允许水平方向上的小型化(缩放),这个概念不好理解,但是底层的操作很简单,就是将CSI_OUT_FRM_CTRL寄存器的CSI_HORI_DOWNSIZE_EN位置位即可。


9._ipu_csi_horizontal_downsize_disable函数,是上一个函数的反函数。

void _ipu_csi_horizontal_downsize_disable(struct ipu_soc *ipu, uint32_t csi) 
{ 
	uint32_t temp; 

	temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL); 
	temp &= ~CSI_HORI_DOWNSIZE_EN; 
	ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL); 
}

同样的,想要关闭这个功能,只需要将SI_OUT_FRM_CTRL寄存器的CSI_HORI_DOWNSIZE_EN位清零即可。


10._ipu_csi_vertical_downsize_enable函数

void _ipu_csi_vertical_downsize_enable(struct ipu_soc *ipu, uint32_t csi) 
{ 
	uint32_t temp; 

	temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL); 
	temp |= CSI_VERT_DOWNSIZE_EN; 
	ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL); 
}

这个函数就是允许csi设备垂直方向上的小型化(缩放),同样,只需要将CSI_OUT_FRM_CTRL寄存器的CSI_VERT_DOWNSIZE_EN位置位即可。


11._ipu_csi_vertical_downsize_disable函数

void _ipu_csi_vertical_downsize_disable(struct ipu_soc *ipu, uint32_t csi) 
{ 
	uint32_t temp; 

	temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL); 
	temp &= ~CSI_VERT_DOWNSIZE_EN; 
	ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL); 
}

这个函数是第10个函数的反函数,想要关闭垂直方向上的小型化这个功能,只需要将CSI_OUT_FRM_CTRL寄存器的CSI_VERT_DOWNSIZE_EN位清零即可。


12._ipu_csi_set_test_generator函数

void _ipu_csi_set_test_generator(struct ipu_soc *ipu, bool active, uint32_t r_value, 
	uint32_t g_value, uint32_t b_value, uint32_t pix_clk, uint32_t csi) 
{ 
	uint32_t temp; 

	temp = ipu_csi_read(ipu, csi, CSI_TST_CTRL); 

	if (active == false) { 
		temp &= ~CSI_TEST_GEN_MODE_EN; 
		ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL); 
	} else { 
		/* Set sensb_mclk div_ratio*/ 
		_ipu_csi_mclk_set(ipu, pix_clk, csi); 

		temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK | 
			CSI_TEST_GEN_B_MASK); 
		temp |= CSI_TEST_GEN_MODE_EN; 
		temp |= (r_value << CSI_TEST_GEN_R_SHIFT) | 
			(g_value << CSI_TEST_GEN_G_SHIFT) | 
			(b_value << CSI_TEST_GEN_B_SHIFT); 
		ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL); 
	} 
}

这个函数根据active参数的值决定是否启动这个测试发生器的功能。如果activefalse的话,就将CSI_TST_CTRL寄存器中的CSI_TEST_GEN_MODE_EN位清零,如果activetrue的话,就根据传入的pix_clk参数设置时钟,然后将传入的RGB色彩写到CSI_TST_CTRL寄存器中。


这个函数不知道具体想要实现什么功能,也没有其他的函数调用它,所以先分析这些。


13._ipu_csi_ccir_err_detection_enable函数

void _ipu_csi_ccir_err_detection_enable(struct ipu_soc *ipu, uint32_t csi) 
{ 
	uint32_t temp; 

	temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1); 
	temp |= CSI_CCIR_ERR_DET_EN; 
	ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1); 

}

这个函数在ipu_csi_init_interface函数中使用了它,我们在这分析:

它只是将CSI_CCIR_CODE_1寄存器的CSI_CCIR_ERR_DET_EN位置位了。启用CCIR交错模式的错误校验和修正。


14._ipu_csi_ccir_err_detection_disable函数

void _ipu_csi_ccir_err_detection_disable(struct ipu_soc *ipu, uint32_t csi) 
{ 
	uint32_t temp; 

	temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1); 
	temp &= ~CSI_CCIR_ERR_DET_EN; 
	ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1); 

}

这个函数是_ipu_csi_ccir_err_detection_enable函数的反函数。


15._ipu_csi_set_mipi_di函数,这个函数在ipu_common.c中的ipu_init_channel函数里调用.

int _ipu_csi_set_mipi_di(struct ipu_soc *ipu, uint32_t num, uint32_t di_val, uint32_t csi) 
{ 
	uint32_t temp; 
	int retval = 0; 

	if (di_val > 0xFFL) { 
		retval = -EINVAL; 
		goto err; 
	} 

	temp = ipu_csi_read(ipu, csi, CSI_MIPI_DI); 

	switch (num) { 
	case IPU_CSI_MIPI_DI0: 
		temp &= ~CSI_MIPI_DI0_MASK; 
		temp |= (di_val << CSI_MIPI_DI0_SHIFT); 
		ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI); 
		break; 
	case IPU_CSI_MIPI_DI1: 
		temp &= ~CSI_MIPI_DI1_MASK; 
		temp |= (di_val << CSI_MIPI_DI1_SHIFT); 
		ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI); 
		break; 
	case IPU_CSI_MIPI_DI2: 
		temp &= ~CSI_MIPI_DI2_MASK; 
		temp |= (di_val << CSI_MIPI_DI2_SHIFT); 
		ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI); 
		break; 
	case IPU_CSI_MIPI_DI3: 
		temp &= ~CSI_MIPI_DI3_MASK; 
		temp |= (di_val << CSI_MIPI_DI3_SHIFT); 
		ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI); 
		break; 
	default: 
		retval = -EINVAL; 
	} 

err: 
	return retval; 
}

它就是根据这个传入的num值来修改CSI_MIPI_DI寄存器中的值,我在用户手册中搜索到的寄存器的名字叫做CSI1Data Identifier Register IPUx_CSI1_DI),它应该就是为了区分数据来源是哪个流。


16._ipu_csi_set_skip_isp函数

int _ipu_csi_set_skip_isp(struct ipu_soc *ipu, uint32_t skip, uint32_t max_ratio, uint32_t csi) 
{ 
	uint32_t temp; 
	int retval = 0; 

	if (max_ratio > 5) { 
		retval = -EINVAL; 
		goto err; 
	} 

	temp = ipu_csi_read(ipu, csi, CSI_SKIP); 
	temp &= ~(CSI_MAX_RATIO_SKIP_ISP_MASK | CSI_SKIP_ISP_MASK); 
	temp |= (max_ratio << CSI_MAX_RATIO_SKIP_ISP_SHIFT) | 
		(skip << CSI_SKIP_ISP_SHIFT); 
	ipu_csi_write(ipu, csi, temp, CSI_SKIP); 

err: 
	return retval; 
} 

它主要是用来设置CSI_SKIP寄存器的,根据传入的skipmax_ratio两个值来修改。查看用户数据手册可以发现,这个寄存器可以设置的地方有3个,这个函数可以修改skipmax_ratio,下面那个函数可以修改另外一个smfc


17._ipu_csi_set_skip_smfc函数

int _ipu_csi_set_skip_smfc(struct ipu_soc *ipu, uint32_t skip, 
	uint32_t max_ratio, uint32_t id, uint32_t csi) 
{ 
	uint32_t temp; 
	int retval = 0; 

	if (max_ratio > 5 || id > 3) { 
		retval = -EINVAL; 
		goto err; 
	} 

	temp = ipu_csi_read(ipu, csi, CSI_SKIP); 
	temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK | 
			CSI_SKIP_SMFC_MASK); 
	temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) | 
			(id << CSI_ID_2_SKIP_SHIFT) | 
			(skip << CSI_SKIP_SMFC_SHIFT); 
	ipu_csi_write(ipu, csi, temp, CSI_SKIP); 

err: 
	return retval; 
}

这个函数与上面那个类似,只是需要通过3个参数来确定修改CSI_SKIP中的哪些位。以后再具体查看这些位的含义。


18._ipu_smfc_init函数,它在ipu_common.c文件中的ipu_init_channel函数中调用了。

void _ipu_smfc_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t mipi_id, uint32_t csi) 
{ 
	uint32_t temp; 

	temp = ipu_smfc_read(ipu, SMFC_MAP); 

	switch (channel) { 
	case CSI_MEM0: 
		temp &= ~SMFC_MAP_CH0_MASK; 
		temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH0_SHIFT; 
		break; 
	case CSI_MEM1: 
		temp &= ~SMFC_MAP_CH1_MASK; 
		temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH1_SHIFT; 
		break; 
	case CSI_MEM2: 
		temp &= ~SMFC_MAP_CH2_MASK; 
		temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH2_SHIFT; 
		break; 
	case CSI_MEM3: 
		temp &= ~SMFC_MAP_CH3_MASK; 
		temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH3_SHIFT; 
		break; 
	default: 
		return; 
	} 

	ipu_smfc_write(ipu, temp, SMFC_MAP); 
}

看函数的注释,MapCSI frames to IDMACchannels.这个SMFC位于csi设备与IDMAC之间的位置,意思应该就是映射csiframeIDMAC中。操作很简单,根据channel参数的值来设置SMFC_MAP寄存器中的值。


19._ipu_smfc_set_wmc函数

void _ipu_smfc_set_wmc(struct ipu_soc *ipu, ipu_channel_t channel, bool set, uint32_t level) 
{ 
	uint32_t temp; 

	temp = ipu_smfc_read(ipu, SMFC_WMC); 

	switch (channel) { 
	case CSI_MEM0: 
		if (set == true) { 
			temp &= ~SMFC_WM0_SET_MASK; 
			temp |= level << SMFC_WM0_SET_SHIFT; 
		} else { 
			temp &= ~SMFC_WM0_CLR_MASK; 
			temp |= level << SMFC_WM0_CLR_SHIFT; 
		} 
		break; 
	case CSI_MEM1: 
		if (set == true) { 
			temp &= ~SMFC_WM1_SET_MASK; 
			temp |= level << SMFC_WM1_SET_SHIFT; 
		} else { 
			temp &= ~SMFC_WM1_CLR_MASK; 
			temp |= level << SMFC_WM1_CLR_SHIFT; 
		} 
		break; 
	case CSI_MEM2: 
		if (set == true) { 
			temp &= ~SMFC_WM2_SET_MASK; 
			temp |= level << SMFC_WM2_SET_SHIFT; 
		} else { 
			temp &= ~SMFC_WM2_CLR_MASK; 
			temp |= level << SMFC_WM2_CLR_SHIFT; 
		} 
		break; 
	case CSI_MEM3: 
		if (set == true) { 
			temp &= ~SMFC_WM3_SET_MASK; 
			temp |= level << SMFC_WM3_SET_SHIFT; 
		} else { 
			temp &= ~SMFC_WM3_CLR_MASK; 
			temp |= level << SMFC_WM3_CLR_SHIFT; 
		} 
		break; 
	default: 
		return; 
	} 

	ipu_smfc_write(ipu, temp, SMFC_WMC); 
}

根据channel的值来设置SMFC_WMC寄存器中的不同位。函数意思不懂。


20._ipu_smfc_set_burst_size函数,在ipu_common.c中的ipu_init_channel_buffer中调用了。

void _ipu_smfc_set_burst_size(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t bs) 
{ 
	uint32_t temp; 

	temp = ipu_smfc_read(ipu, SMFC_BS); 

	switch (channel) { 
	case CSI_MEM0: 
		temp &= ~SMFC_BS0_MASK; 
		temp |= bs << SMFC_BS0_SHIFT; 
		break; 
	case CSI_MEM1: 
		temp &= ~SMFC_BS1_MASK; 
		temp |= bs << SMFC_BS1_SHIFT; 
		break; 
	case CSI_MEM2: 
		temp &= ~SMFC_BS2_MASK; 
		temp |= bs << SMFC_BS2_SHIFT; 
		break; 
	case CSI_MEM3: 
		temp &= ~SMFC_BS3_MASK; 
		temp |= bs << SMFC_BS3_SHIFT; 
		break; 
	default: 
		return; 
	} 

	ipu_smfc_write(ipu, temp, SMFC_BS); 
}

函数作用是设置IDMACchannelburstsize(脉冲串?),根据传入的bs参数设置SMFC_BS寄存器即可。


21._ipu_csi_init函数,它在ipu_common.c中的ipu_init_channel函数中调用了。

int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi) 
{ 
	uint32_t csi_sens_conf, csi_dest; 
	int retval = 0; 

	switch (channel) { 
	case CSI_MEM0: 
	case CSI_MEM1: 
	case CSI_MEM2: 
	case CSI_MEM3: 
		csi_dest = CSI_DATA_DEST_IDMAC; 
		break; 
	case CSI_PRP_ENC_MEM: 
	case CSI_PRP_VF_MEM: 
		csi_dest = CSI_DATA_DEST_IC; 
		break; 
	default: 
		retval = -EINVAL; 
		goto err; 
	} 

	csi_sens_conf = ipu_csi_read(ipu, csi, CSI_SENS_CONF); 
	csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK; 
	ipu_csi_write(ipu, csi, csi_sens_conf | (csi_dest << 
		CSI_SENS_CONF_DATA_DEST_SHIFT), CSI_SENS_CONF); 
err: 
	return retval; 
}

这个函数初始化csi设备,它根据传入的channel的不同,设置不同的csi数据目的地,然后将这个目的地设置到CSI_SENS_CONF寄存器中。感觉这个channelipu内部的意思就是ipu内数据流通的过程,每个channle里面都含有目的地等信息。


22._ipu_csi_wait4eof函数,在ipu_common.c中的ipu_disable_csi函数中调用了。

void _ipu_csi_wait4eof(struct ipu_soc *ipu, ipu_channel_t channel) 
{ 
	int ret; 
	int irq = 0; 

	if (channel == CSI_MEM0) 
		irq = IPU_IRQ_CSI0_OUT_EOF; 
	else if (channel == CSI_MEM1) 
		irq = IPU_IRQ_CSI1_OUT_EOF; 
	else if (channel == CSI_MEM2) 
		irq = IPU_IRQ_CSI2_OUT_EOF; 
	else if (channel == CSI_MEM3) 
		irq = IPU_IRQ_CSI3_OUT_EOF; 
	else if (channel == CSI_PRP_ENC_MEM) 
		irq = IPU_IRQ_PRP_ENC_OUT_EOF; 
	else if (channel == CSI_PRP_VF_MEM) 
		irq = IPU_IRQ_PRP_VF_OUT_EOF; 
	else{ 
		dev_err(ipu->dev, "Not a CSI channel\n"); 
		return; 
	} 

	init_completion(&ipu->csi_comp); 
	ret = ipu_request_irq(ipu, irq, csi_irq_handler, 0, NULL, ipu); 
	if (ret < 0) { 
		dev_err(ipu->dev, "CSI irq %d in use\n", irq); 
		return; 
	} 
	ret = wait_for_completion_timeout(&ipu->csi_comp, msecs_to_jiffies(500)); 
	ipu_free_irq(ipu, irq, ipu); 
	dev_dbg(ipu->dev, "CSI stop timeout - %d * 10ms\n", 5 - ret); 
}

它的中断处理函数如下:

static irqreturn_t csi_irq_handler(int irq, void *dev_id) 
{ 
	struct ipu_soc *ipu = dev_id; 
	struct completion *comp = &ipu->csi_comp; 

	complete(comp); 
	return IRQ_HANDLED; 
}

这个函数中,首先根据channel的不同值设置不同的irq触发方式,然后初始化完成量,然后调用ipu_request_irq函数申请中断,这个函数在ipu_common.c中定义。如果发生中断后,就会调用到这个中断处理函数,在中断处理函数中通过complete()函数来唤醒阻塞在ipu->csi_comp上的首个线程。wait_for_completion_timeout函数表示,如果超过一定时间没有发生这个中断,就直接结束等待。


关于这个完成量的解释和操作函数,可以看《Linuxcompletion机制.pdf》和《线程的约会completion.pdf》两个文件。


至此,这个文件简单分析完毕。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值