高通(QCOM)camera kernel新架構通過ION拿到sensor參數

前言:

ION 的前任是 PMEM,ION,最顯著的特點是它可以被用戶空間的進程之間或者內核空間的模塊之間進行內存共享,
而且這種共享可以是零拷貝的。在實際使用中,ION 和 VIDEOBUF2、DMA-BUF、V4L2 等結合的很緊密。
ION在內核中被當做一個misc設備來註冊,通常user通過打開/dev/ion來對內存進行操作,在高通710平臺的camera流
程中,拿取camera參數與之前的660平臺不太一樣,660平臺通過調用v4l-subdev的ioctl傳遞camera sensor的參數,在
新平臺上,通過ion拿取camera sensor參數.
	
下面來分析過程:

怎麼用的?

cam_sensor_subdev_ioctl過程(在vendor層通過調用media->ioctl獲取與camera相關的節點後,打開v4l2-subdev部分)

我們從 cam_sensor_subdev_ioctl()函數開始:
	/*1*/cam_sensor_subdev_ioctl(){
		.....
		struct cam_sensor_ctrl_t *s_ctrl =
			v4l2_get_subdevdata(sd);	/* 通過之前的分析,sd是在之前流程中拿到video_device下的參數,其中是沒有與sensor相關參數的*/
		switch (cmd) {
		case VIDIOC_CAM_CONTROL:
			rc = cam_sensor_driver_cmd(s_ctrl, arg);/* 主要看這個函數 */
			break;
		}
		return rc;
		.....
	}
		/*2*/cam_sensor_driver_cmd(s_ctrl, arg);{
			.....
			/* pu & pd 是關於power的變量,這裏還是空的,下面省略部分會分配內存*/
			struct cam_sensor_power_setting *pu = NULL; 
			struct cam_sensor_power_setting *pd = NULL;
			struct cam_sensor_power_ctrl_t *power_info = /* 這裏把power_info指向 sctrl中的power_info(現在是空的) */
				&s_ctrl->sensordata->power_info;
			.....
			/* 這裏把power_info中的參數指向 pu,pd; 後面ion中拿出來的power數據會填寫到power_info中,由於power_info已經指向
				pu,pd所以s_ctrl->sensordata->power_info = pu,pd = power_info,所以在後面對s_ctrl操作,就是對power_info操作 */
			power_info->power_setting = pu;
			power_info->power_down_setting = pd;
			
			if (cmd->handle_type ==
			CAM_HANDLE_MEM_HANDLE) {
			rc = cam_handle_mem_ptr(cmd->handle, s_ctrl);  /* 重點,這裏開始從ion拿數據了 */
				if (rc < 0) {
					CAM_ERR(CAM_SENSOR, "Get Buffer Handle Failed");
					kfree(pu);
					kfree(pd);
					goto release_mutex;
				}
			} else {
				CAM_ERR(CAM_SENSOR, "Invalid Command Type: %d",
					 cmd->handle_type);
				return -EINVAL;
			}
			...../* 後面probe 之前已經分析過了 */
		}
				/*3*/cam_handle_mem_ptr(cmd->handle, s_ctrl);{
					.....
					rc = cam_mem_get_cpu_buf(handle, /* 通過handle,拿到所需數據在ion中的地址packet,以及len */
					(uint64_t *)&packet, &len);
					.....
					/* 這裏高通自己的計算方法,計算後應該是得到 command descriptor(控制標識之類的東西), 在後面拿數據時會用 */
					/* 具體操作看 cam_mem_get_cpu_buf詳解 */
					pkt = (struct cam_packet *)packet;
					cmd_desc = (struct cam_cmd_buf_desc *)
						((uint32_t *)&pkt->payload + pkt->cmd_buf_offset/4);
					.....
					for (i = 0; i < pkt->num_cmd_buf; i++) { /* 循環拿buf數據,比如power數據,salve_info,iic */
						if (!(cmd_desc[i].length))
							continue;
						/* 通過handle(可以理解爲標識)拿我們需要的數據放到 cmd_buf1中 */
						rc = cam_mem_get_cpu_buf(cmd_desc[i].mem_handle, 
							(uint64_t *)&cmd_buf1, &len);
						if (rc < 0) {
							CAM_ERR(CAM_SENSOR,
								"Failed to parse the command Buffer Header");
							return -EINVAL;
						}
						/* 一系列計算(地址偏移)後,把ptr指向 cmd_buf 既指向cmd_buf1 */
						cmd_buf = (uint32_t *)cmd_buf1;
						cmd_buf += cmd_desc[i].offset/4;
						ptr = (void *) cmd_buf;
						/* 從ptr中拿取數據放到 s_ctrl,比如power_setting salve_info等*/
						rc = cam_handle_cmd_buffers_for_probe(ptr, s_ctrl,  
							i, cmd_desc[i].length);
						if (rc < 0) {
							CAM_ERR(CAM_SENSOR,
								"Failed to parse the command Buffer Header");
							return -EINVAL;
						}
					}

				}
						/*4*/cam_handle_cmd_buffers_for_probe(ptr, s_ctrl, i, cmd_desc[i].length);
							 {
								int32_t rc = 0;

								switch (cmd_buf_num) { /* 在上一個函數中拿的是什麼數據,就會case到幾 */
								case 0: { /* 關於isalve_info,以及i2c的信息,放到sctrl中,之前已經說過爲什麼放到s_ctrl中 */
									struct cam_cmd_i2c_info *i2c_info = NULL;
									struct cam_cmd_probe *probe_info;

									i2c_info = (struct cam_cmd_i2c_info *)cmd_buf;
									rc = cam_sensor_update_i2c_info(i2c_info, s_ctrl);
									if (rc < 0) {
										CAM_ERR(CAM_SENSOR, "Failed in Updating the i2c Info");
										return rc;
									}
									probe_info = (struct cam_cmd_probe *)
										(cmd_buf + sizeof(struct cam_cmd_i2c_info));
									rc = cam_sensor_update_slave_info(probe_info, s_ctrl);
									if (rc < 0) {
										CAM_ERR(CAM_SENSOR, "Updating the slave Info");
										return rc;
									}
									cmd_buf = probe_info;
								}
									break;
								case 1: {/* 關於power_setting的數據 */
									rc = cam_sensor_update_power_settings(cmd_buf,
										cmd_buf_length, &s_ctrl->sensordata->power_info);
									if (rc < 0) {
										CAM_ERR(CAM_SENSOR,
											"Failed in updating power settings");
										return rc;
									}
								}
									break;
								default:
									CAM_ERR(CAM_SENSOR, "Invalid command buffer");
									break;
								}
								return rc;
							}

cam_mem_get_cpu_buf詳解

ion的alloc和map可以參考下面文章
https://blog.csdn.net/a185531353/article/details/89479076

int cam_mem_get_cpu_buf(int32_t buf_handle, uint64_t *vaddr_ptr, size_t *len)
{
	int rc = 0;
	int idx;
	struct ion_handle *ion_hdl = NULL;
	uint64_t kvaddr = 0;
	size_t klen = 0;

	if (!buf_handle || !vaddr_ptr || !len)
		return -EINVAL;
	/* 通過之前拿到的buf_handle(在之前的文章中已經說過了),
	拿到idx(追代碼時發現其實際上是一個句柄),通過idx可以找到ion_hdl */
	idx = CAM_MEM_MGR_GET_HDL_IDX(buf_handle);
	if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0)
		return -EINVAL;

	if (!tbl.bufq[idx].active)
		return -EPERM;

	mutex_lock(&tbl.bufq[idx].q_lock);
	if (buf_handle != tbl.bufq[idx].buf_handle) {
		rc = -EINVAL;
		goto exit_func;
	}
	/* 通過idx拿到ION_hdl */
	ion_hdl = tbl.bufq[idx].i_hdl;
	if (!ion_hdl) {
		CAM_ERR(CAM_CRM, "Invalid ION handle");
		rc = -EINVAL;
		goto exit_func;
	}

	if (tbl.bufq[idx].flags & CAM_MEM_FLAG_KMD_ACCESS) {
		if (!tbl.bufq[idx].kmdvaddr) {
			/* 這裏拿到的kvaddr纔是真正的sensor參數存放在ion中的地址 */
			rc = cam_mem_util_map_cpu_va(ion_hdl, 
				&kvaddr, &klen);
			if (rc)
				goto exit_func;
			tbl.bufq[idx].kmdvaddr = kvaddr; /* 賦值 */
		}
	} else {
		rc = -EINVAL;
		goto exit_func;
	}
	/* 賦值,這裏的vaddr_ptr就是之前傳進來的cmd_buf1 */
	*vaddr_ptr = tbl.bufq[idx].kmdvaddr;
	*len = tbl.bufq[idx].len;

exit_func:
	mutex_unlock(&tbl.bufq[idx].q_lock);
	return rc;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章