这个文件中讲解的是IPUCSI_PRP_ENC_MEMMEM_ROT_ENC_MEM这两个channel的执行过程,首先,

CSI_PRP_ENC_MEM 这个channel表示从CSI中获取到数据,然后将数据保存到内存中这个流程。而MEM_ROT_ENC_MEM 这个channel是需要在CSI_PRP_ENC_MEM channel基础之上,将数据从内存中取出来,进行翻转运算以后,再放到内存中去这个流程。


首先来看看在mxc_v4l2_capture.c中的mxc_v4l_open函数,里面有这样一个选择语句:

if (strcmp(mxc_capture_inputs[cam->current_input].name, 
			   "CSI MEM") == 0) { 
#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE) 
			err = csi_enc_select(cam); 
#endif 
		} else if (strcmp(mxc_capture_inputs[cam->current_input].name, 
				  "CSI IC MEM") == 0) { 
#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) 
			err = prp_enc_select(cam); 
#endif 
		}

cam_data结构体中有一个current_input的成员,同时,在mxc_v4l2_capture.c中有一个mxc_capture_inputs[]数组,驱动程序会以cam_data->current_input作为下标来从这个mxc_capture_inputs[]数组中选取一个input。数组如下所示:

static struct v4l2_input mxc_capture_inputs[MXC_V4L2_CAPTURE_NUM_INPUTS] = { 
	{ 
	 .index = 0, 
	 .name = "CSI IC MEM", 
	 .type = V4L2_INPUT_TYPE_CAMERA, 
	 .audioset = 0, 
	 .tuner = 0, 
	 .std = V4L2_STD_UNKNOWN, 
	 .status = 0, 
	 }, 
	{ 
	 .index = 1, 
	 .name = "CSI MEM", 
	 .type = V4L2_INPUT_TYPE_CAMERA, 
	 .audioset = 0, 
	 .tuner = 0, 
	 .std = V4L2_STD_UNKNOWN, 
	 .status = V4L2_IN_ST_NO_POWER, 
	 }, 
}; 

驱动程序最终会根据这个数组的信息选择执行csi_enc_select函数或者prp_enc_select函数中的一个。


这个“CSI MEM”一般是用作overlay或者viewfinder,从CSI中获取到的数据,不经过IC,直接到达内存中。而“CSI IC MEM”一般会将数据进行一些处理,比如翻转图像等操作。我们现在分析的这个文件就是“CSI IC MEM”这种情况,对于“CSI MEM”的情况,会在ipu_csi_enc.c文件中分析。


mxc_v4l_open函数中,会执行这个prp_enc_select函数,那么就先从这个prp_enc_select函数函数开始分析。


1. prp_enc_select函数

int prp_enc_select(void *private) 
{ 
	cam_data *cam = (cam_data *) private; 
	int err = 0; 

	if (cam) { 
		cam->enc_update_eba = prp_enc_eba_update; 
		cam->enc_enable = prp_enc_enabling_tasks; 
		cam->enc_disable = prp_enc_disabling_tasks; 
		cam->enc_enable_csi = prp_enc_enable_csi; 
		cam->enc_disable_csi = prp_enc_disable_csi; 
	} else { 
		err = -EIO; 
	} 

	return err; 
} 
EXPORT_SYMBOL(prp_enc_select);

这个函数只是为cam_data结构体中的几个函数指针赋值,将它们指向这些函数。而这些函数会在mxc_v4l_capture.c中以cam->enc_update_eba cam->enc_enable等形式来调用,最终就会调用到ipu_prp_enc.c中的prp_enc_eba_update或者prp_enc_enabling_tasks等函数。


2. prp_enc_enabling_tasks函数

static int prp_enc_enabling_tasks(void *private) 
{ 
	cam_data *cam = (cam_data *) private; 
	int err = 0; 
	CAMERA_TRACE("IPU:In prp_enc_enabling_tasks\n"); 

	cam->dummy_frame.vaddress = dma_alloc_coherent(0, 
			       PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), 
			       &cam->dummy_frame.paddress, 
			       GFP_DMA | GFP_KERNEL); 
	if (cam->dummy_frame.vaddress == 0) { 
		pr_err("ERROR: v4l2 capture: Allocate dummy frame " 
		       "failed.\n"); 
		return -ENOBUFS; 
	} 
	cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE; 
	cam->dummy_frame.buffer.length = 
	    PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); 
	cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress; 

	if (cam->rotation >= IPU_ROTATE_90_RIGHT) { 
		err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_ENC_ROT_OUT_EOF, 
				      prp_enc_callback, 0, "Mxc Camera", cam); 
	} else { 
		err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_ENC_OUT_EOF, 
				      prp_enc_callback, 0, "Mxc Camera", cam); 
	} 
	if (err != 0) { 
		printk(KERN_ERR "Error registering rot irq\n"); 
		return err; 
	} 

	err = prp_enc_setup(cam); 
	if (err != 0) { 
		printk(KERN_ERR "prp_enc_setup %d\n", err); 
		return err; 
	} 

	return err; 
}

这个函数中,首先cam->dummy_frame分配dma内存,它的虚拟地址保存在cam->dummy_frame.vaddress中,物理地址保存在&cam->dummy_frame.paddress中,然后为cam->dummy_frame中其他成员赋值。

重要的函数就是ipu_request_irqprp_enc_setup函数了。


2.1 ipu_request_irq函数

这个函数在ipu_common.c中定义,具体的函数就不再分析,重点看一下在这的意义。

这个函数会根据cam->rotation的值来选择申请不同的中断,如果在应用程序中添加了翻转信息的话,就会申请IPU_IRQ_PRP_ENC_ROT_OUT_EOF这个中断,而没有翻转信息的话,就会申请IPU_IRQ_PRP_ENC_OUT_EOF这个中断。

它们的含义基本就是当申请的buffer填充满以后(outeof),触发中断,执行中断处理函数prp_enc_callback。这个函数如下所示:

static irqreturn_t prp_enc_callback(int irq, void *dev_id) 
{ 
	cam_data *cam = (cam_data *) dev_id; 

	if (cam->enc_callback == NULL) 
		return IRQ_HANDLED; 

	cam->enc_callback(irq, dev_id); 

	return IRQ_HANDLED; 
}

而这个函数最终还对调用到cam_data结构体中的enc_callback函数指针,这个函数指针是在mxc_v4l_capture.c中的init_camera_struct函数中指定的。


2.2 prp_enc_setup函数

这个函数中,完成了ipu_init_channelipu_init_channel_buffer的功能。

static int prp_enc_setup(cam_data *cam) 
{ 
	ipu_channel_params_t enc; 
	int err = 0; 
	dma_addr_t dummy = cam->dummy_frame.buffer.m.offset; 
#ifdef CONFIG_MXC_MIPI_CSI2 
	void *mipi_csi2_info; 
	int ipu_id; 
	int csi_id; 
#endif 

	CAMERA_TRACE("In prp_enc_setup\n"); 
	if (!cam) { 
		printk(KERN_ERR "cam private is NULL\n"); 
		return -ENXIO; 
	} 
	memset(&enc, 0, sizeof(ipu_channel_params_t)); 

//ipu_channel_params_t联合中的值清空

	ipu_csi_get_window_size(cam->ipu, &enc.csi_prp_enc_mem.in_width, &enc.csi_prp_enc_mem.in_height, cam->csi); 

/*通过这个函数来获取cam->csi设备中的in_width信息和in_height信息填充在

*enc.csi_prp_enc_mem.in_widthenc.csi_prp_enc_mem.in_heigh中。

*/

	enc.csi_prp_enc_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY; 
	enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.width; 
	enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.height; 
	enc.csi_prp_enc_mem.csi = cam->csi; 
	if (cam->rotation >= IPU_ROTATE_90_RIGHT) { 
		enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.height; 
		enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.width; 
	} 

/*填充enc.csi_prp_enc_mem.in_pixel_fmtenc.csi_prp_enc_mem.out_widthenc.csi_prp_enc_mem.out_heightenc.csi_prp_enc_mem.csi的值。*/

	if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) { 
		enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV420P; 
		pr_info("YUV420\n"); 
	} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) { 
		enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YVU420P; 
		pr_info("YVU420\n"); 
	} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) { 
		enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV422P; 
		pr_info("YUV422P\n"); 
	} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { 
		enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUYV; 
		pr_info("YUYV\n"); 
	} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) { 
		enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_UYVY; 
		pr_info("UYVY\n"); 
	} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) { 
		enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_NV12; 
		pr_info("NV12\n"); 
	} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) { 
		enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR24; 
		pr_info("BGR24\n"); 
	} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) { 
		enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB24; 
		pr_info("RGB24\n"); 
	} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) { 
		enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB565; 
		pr_info("RGB565\n"); 
	} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) { 
		enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR32; 
		pr_info("BGR32\n"); 
	} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) { 
		enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB32; 
		pr_info("RGB32\n"); 
	} else { 
		printk(KERN_ERR "format not supported\n"); 
		return -EINVAL; 
	} 

/*填充enc.csi_prp_enc_mem.out_pixel_fmt的值*/

#ifdef CONFIG_MXC_MIPI_CSI2 
	mipi_csi2_info = mipi_csi2_get_info(); 

	if (mipi_csi2_info) { 
		if (mipi_csi2_get_status(mipi_csi2_info)) { 
			ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); 
			csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); 

			if (cam->ipu == ipu_get_soc(ipu_id) 
				&& cam->csi == csi_id) { 
				enc.csi_prp_enc_mem.mipi_en = true; 
				enc.csi_prp_enc_mem.mipi_vc = 
				mipi_csi2_get_virtual_channel(mipi_csi2_info); 
				enc.csi_prp_enc_mem.mipi_id = 
				mipi_csi2_get_datatype(mipi_csi2_info); 

				mipi_csi2_pixelclk_enable(mipi_csi2_info); 
			} else { 
				enc.csi_prp_enc_mem.mipi_en = false; 
				enc.csi_prp_enc_mem.mipi_vc = 0; 
				enc.csi_prp_enc_mem.mipi_id = 0; 
			} 
		} else { 
			enc.csi_prp_enc_mem.mipi_en = false; 
			enc.csi_prp_enc_mem.mipi_vc = 0; 
			enc.csi_prp_enc_mem.mipi_id = 0; 
		} 
	} 
#endif 

	err = ipu_init_channel(cam->ipu, CSI_PRP_ENC_MEM, &enc); 
	if (err != 0) { 
		printk(KERN_ERR "ipu_init_channel %d\n", err); 
		return err; 
	} 

/*上面将enc.csi_prp_enc_mem里面的值都填充完毕了,目的就是为了在ipu_init_channel函数中使用它们。通过这个函数来初始化channel*/

	grotation = cam->rotation; 
	if (cam->rotation >= IPU_ROTATE_90_RIGHT) { 
		if (cam->rot_enc_bufs_vaddr[0]) { 
			dma_free_coherent(0, cam->rot_enc_buf_size[0], 
					  cam->rot_enc_bufs_vaddr[0], 
					  cam->rot_enc_bufs[0]); 
		} 
		if (cam->rot_enc_bufs_vaddr[1]) { 
			dma_free_coherent(0, cam->rot_enc_buf_size[1], 
					  cam->rot_enc_bufs_vaddr[1], 
					  cam->rot_enc_bufs[1]); 
		} 
		cam->rot_enc_buf_size[0] = 
		    PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); 
		cam->rot_enc_bufs_vaddr[0] = 
		    (void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[0], 
					       &cam->rot_enc_bufs[0], 
					       GFP_DMA | GFP_KERNEL); 
		if (!cam->rot_enc_bufs_vaddr[0]) { 
			printk(KERN_ERR "alloc enc_bufs0\n"); 
			return -ENOMEM; 
		} 
		cam->rot_enc_buf_size[1] = 
		    PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); 
		cam->rot_enc_bufs_vaddr[1] = 
		    (void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[1], 
					       &cam->rot_enc_bufs[1], 
					       GFP_DMA | GFP_KERNEL); 
		if (!cam->rot_enc_bufs_vaddr[1]) { 
			dma_free_coherent(0, cam->rot_enc_buf_size[0], 
					  cam->rot_enc_bufs_vaddr[0], 
					  cam->rot_enc_bufs[0]); 
			cam->rot_enc_bufs_vaddr[0] = NULL; 
			cam->rot_enc_bufs[0] = 0; 
			printk(KERN_ERR "alloc enc_bufs1\n"); 
			return -ENOMEM; 
		} 

/*上面这些信息就是在rot的过程中,需要使用到两个buffer,而这两个buffer都需要通过dma函数来申请,最终这两个buffer的物理地址分别存放在cam->rot_enc_bufs[]数组中,虚拟地址存放在cam->rot_enc_bufs_vaddr[]数组中,buffer的大小存放在cam->rot_enc_buf_size[]数组中。*/

		err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_ENC_MEM, 
					      IPU_OUTPUT_BUFFER, 
					      enc.csi_prp_enc_mem.out_pixel_fmt, 
					      enc.csi_prp_enc_mem.out_width, 
					      enc.csi_prp_enc_mem.out_height, 
					      enc.csi_prp_enc_mem.out_width, 
					      IPU_ROTATE_NONE, 
					      cam->rot_enc_bufs[0], 
					      cam->rot_enc_bufs[1], 0, 0, 0); 
		if (err != 0) { 
			printk(KERN_ERR "CSI_PRP_ENC_MEM err\n"); 
			return err; 
		} 

/*为之前申请的CSI_PRP_ENC_MEMchannel申请buffer*/

		err = ipu_init_channel(cam->ipu, MEM_ROT_ENC_MEM, NULL); 
		if (err != 0) { 
			printk(KERN_ERR "MEM_ROT_ENC_MEM channel err\n"); 
			return err; 
		} 

/*申请MEM_ROT_ENC_MEMchannel,在需要rot的情况下,需要多申请一个MEM_ROT_ENC_MEMchannel,同时需要将这两个channel连接起来,在后面有这个连接函数。*/

		err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_ENC_MEM, 
					      IPU_INPUT_BUFFER, 
					      enc.csi_prp_enc_mem.out_pixel_fmt, 
					      enc.csi_prp_enc_mem.out_width, 
					      enc.csi_prp_enc_mem.out_height, 
					      enc.csi_prp_enc_mem.out_width, 
					      cam->rotation, 
					      cam->rot_enc_bufs[0], 
					      cam->rot_enc_bufs[1], 0, 0, 0); 
		if (err != 0) { 
			printk(KERN_ERR "MEM_ROT_ENC_MEM input buffer\n"); 
			return err; 
		} 

/*为申请的MEM_ROT_ENC_MEMchannel申请buffer*/

		err = 
		    ipu_init_channel_buffer(cam->ipu, MEM_ROT_ENC_MEM, 
					    IPU_OUTPUT_BUFFER, 
					    enc.csi_prp_enc_mem.out_pixel_fmt, 
					    enc.csi_prp_enc_mem.out_height, 
					    enc.csi_prp_enc_mem.out_width, 
					    cam->v2f.fmt.pix.bytesperline / 
					    bytes_per_pixel(enc.csi_prp_enc_mem. 
							    out_pixel_fmt), 
					    IPU_ROTATE_NONE, 
					    dummy, dummy, 0, 
					    cam->offset.u_offset, 
					    cam->offset.v_offset); 
		if (err != 0) { 
			printk(KERN_ERR "MEM_ROT_ENC_MEM output buffer\n"); 
			return err; 
		} 


/*再次为申请的MEM_ROT_ENC_MEM channel申请buffer。不过这个buffer里面填充的是dummy(虚假)的地址,原因在《5.2 应用程序和驱动程序中buffer的传输流程》中解释了。*/

		err = ipu_link_channels(cam->ipu, 
					CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM); 
		if (err < 0) { 
			printk(KERN_ERR 
			       "link CSI_PRP_ENC_MEM-MEM_ROT_ENC_MEM\n"); 
			return err; 
		} 

/*将两个channel连接起来。*/

		err = ipu_enable_channel(cam->ipu, CSI_PRP_ENC_MEM); 
		if (err < 0) { 
			printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n"); 
			return err; 
		} 
		err = ipu_enable_channel(cam->ipu, MEM_ROT_ENC_MEM); 
		if (err < 0) { 
			printk(KERN_ERR "ipu_enable_channel MEM_ROT_ENC_MEM\n"); 
			return err; 
		} 

		ipu_select_buffer(cam->ipu, CSI_PRP_ENC_MEM, 
				  IPU_OUTPUT_BUFFER, 0); 
		ipu_select_buffer(cam->ipu, CSI_PRP_ENC_MEM, 
				  IPU_OUTPUT_BUFFER, 1); 

/*使能channel*/

	}

/*上面这种情况是需要rot的情况下的操作,如果不需要rot,操作就简单多了,如下面的else语句*/

 else { 
		err = 
		    ipu_init_channel_buffer(cam->ipu, CSI_PRP_ENC_MEM, 
					    IPU_OUTPUT_BUFFER, 
					    enc.csi_prp_enc_mem.out_pixel_fmt, 
					    enc.csi_prp_enc_mem.out_width, 
					    enc.csi_prp_enc_mem.out_height, 
					    cam->v2f.fmt.pix.bytesperline / 
					    bytes_per_pixel(enc.csi_prp_enc_mem. 
							    out_pixel_fmt), 
					    cam->rotation, 
					    dummy, dummy, 0, 
					    cam->offset.u_offset, 
					    cam->offset.v_offset); 
		if (err != 0) { 
			printk(KERN_ERR "CSI_PRP_ENC_MEM output buffer\n"); 
			return err; 
		} 
		err = ipu_enable_channel(cam->ipu, CSI_PRP_ENC_MEM); 
		if (err < 0) { 
			printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n"); 
			return err; 
		} 
	} 

	return err; 
}

注意,这时候通过ipu_init_channel_buffer函数向cpmem中填充的是dummy虚假的buffer信息,在使用过程中,还需要更新cpmem里面的地址值。


3.prp_enc_eba_update函数

static int prp_enc_eba_update(void *private, dma_addr_t eba) 
{ 
	int err = 0; 
	cam_data *cam = (cam_data *) private; 
	struct ipu_soc *ipu = cam->ipu; 
	int *buffer_num = &cam->ping_pong_csi; 

	pr_debug("eba %x\n", eba); 
	if (grotation >= IPU_ROTATE_90_RIGHT) { 
		err = ipu_update_channel_buffer(ipu, MEM_ROT_ENC_MEM, 
						IPU_OUTPUT_BUFFER, *buffer_num, 
						eba); 
	} else { 
		err = ipu_update_channel_buffer(ipu, CSI_PRP_ENC_MEM, 
						IPU_OUTPUT_BUFFER, *buffer_num, 
						eba); 
	} 
	if (err != 0) { 
		if (grotation >= IPU_ROTATE_90_RIGHT) { 
			ipu_clear_buffer_ready(ipu, MEM_ROT_ENC_MEM, 
					       IPU_OUTPUT_BUFFER, 
					       *buffer_num); 
			err = ipu_update_channel_buffer(ipu, MEM_ROT_ENC_MEM, 
							IPU_OUTPUT_BUFFER, 
							*buffer_num, 
							eba); 
		} else { 
			ipu_clear_buffer_ready(ipu, CSI_PRP_ENC_MEM, 
					       IPU_OUTPUT_BUFFER, 
					       *buffer_num); 
			err = ipu_update_channel_buffer(ipu, CSI_PRP_ENC_MEM, 
							IPU_OUTPUT_BUFFER, 
							*buffer_num, 
							eba); 
		} 

		if (err != 0) { 
			pr_err("ERROR: v4l2 capture: fail to update " 
			       "buf%d\n", *buffer_num); 
			return err; 
		} 
	} 

	if (grotation >= IPU_ROTATE_90_RIGHT) { 
		ipu_select_buffer(ipu, MEM_ROT_ENC_MEM, IPU_OUTPUT_BUFFER, 
				  *buffer_num); 
	} else { 
		ipu_select_buffer(ipu, CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER, 
				  *buffer_num); 
	} 

	*buffer_num = (*buffer_num == 0) ? 1 : 0; 
	return 0; 
}

这个函数通过ipu_update_channel_buffer函数来更新对应channelcpmem中的buffer地址。


4.prp_enc_enable_csi函数

static int prp_enc_enable_csi(void *private) 
{ 
	cam_data *cam = (cam_data *) private; 

	return ipu_enable_csi(cam->ipu, cam->csi); 
} 

这个函数最终就会调用到ipu_enable_csi函数来使能csi设备。


更多推荐

6.1 ipu_prp_enc.c详细分析