QCOM_CAMERA内存管理之 kernel 篇(一)

一.相关变量

1.buffer flag

// 对应HAL的CSLMemFlagHw
#define CAM_MEM_FLAG_HW_READ_WRITE              (1<<0)
// 下面两个在hal中未使用
#define CAM_MEM_FLAG_HW_READ_ONLY               (1<<1)
#define CAM_MEM_FLAG_HW_WRITE_ONLY              (1<<2)
// 是否要做dma_buf_kmap, 用于支持cpu直接访问
// dma_buf_kmap可以map出一段连续的虚拟地址,我们只需要call _kmap
// 映射每个单独的page,然后只使用第一次调用_kmap返回的虚拟地址
#define CAM_MEM_FLAG_KMD_ACCESS                 (1<<3)
// UMD 有在用,未见在kernel中使用
// UMD 是否需要 mmap 出来 UMD VirtualAddr,保存在 CSLBufferInfo 里
#define CAM_MEM_FLAG_UMD_ACCESS                 (1<<4)
/* 
 *  protected,在HAL中只看到hal的stream buffer会用以及跑在DSP上的buffer会用
 *  protected buffer也不能被map到hw以及UMD
 *  HAL中有如下解释
 *  This is based on assumption that - if any CHI node exists in secure mode,
 *  it runs on DSP and so set the below flags while importing the HAL buffer.
 *  1. Set ProtectedUMD Access so that Node neither map the buffer to
 *     any camera HW nor to UMD.
 *  2. Set DSPSecureAccess to indicate this buffer will be accessed by DSP in secure
 *     mode. For sink buffers mapping, this flag is not used as of now.
*/
#define CAM_MEM_FLAG_PROTECTED_MODE             (1<<5)
// 对应HAL使用camxcmdbufferManager create的buffer,比如sensor的一些参数,IPE,BPS的一些ConfigureIO参数
#define CAM_MEM_FLAG_CMD_BUF_TYPE               (1<<6)
#define CAM_MEM_FLAG_PIXEL_BUF_TYPE             (1<<7)
#define CAM_MEM_FLAG_STATS_BUF_TYPE             (1<<8)
// 对应HAL使用camxcmdbufferManager create打包cmdBuffer的packet buffer
#define CAM_MEM_FLAG_PACKET_BUF_TYPE            (1<<9)
// cpu与DMA同时访问的buffer需要设置,用于Cache同步操作,保证一致性,相关可以百度Cache一致性
#define CAM_MEM_FLAG_CACHE                      (1<<10)
// 从HAL使用来看,IPE,BPS的ConfigureIObuffer会使用,还有IPE BPS的stats buffer会使用.
// 设置此flag后,会从通过gen_pool_alloc从shared_mem_pool中分配内存
#define CAM_MEM_FLAG_HW_SHARED_ACCESS           (1<<11)
// 跑在DSP上 buffer 使用
#define CAM_MEM_FLAG_CDSP_OUTPUT                (1<<12)
/* 
 * 官方解释:
 * 由ION使用,它将确保在取消映射时不会删除映射,而是在释放ION_缓冲区时删除映射。
 *
 * 个人解释:
 * 是否需要delay unmap,这里指的是iommu
 * Msm_dma_iommu_mapping.c (kernel\msm-4.19\drivers\iommu)	11574	2020-8-4
 * 追代码发现在函数__msm_dma_map_sg中,如果使能了 DMA_ATTR_NO_DELAYED_UNMAP,那么就会call kref_get(&iommu_map->ref);
 * 当我们call csl unmap的时候会call msm_dma_unmap_sg_attrs,调用kref_put(&iommu_map->ref, msm_iommu_map_release); 如果ref = 0,则call msm_iommu_map_release
 * csl alloc and map 阶段会调用上述函数,并且在csl map阶段也会调用,试想一下如果不设置DMA_ATTR_NO_DELAYED_UNMAP,那么我们在alloc时调用了iommu的map,
 * 然后在csl map也做一次iommu的map,然后call 一次 unmap,那么iommu的map将会被释放,在下一次使用时又需要重新iommu map
*/
#define CAM_MEM_FLAG_DISABLE_DELAYED_UNMAP      (1<<13)

二.alloc and map阶段

概述:
我们就看ion_alloc,dma相关的api去看dma系列文章吧

路径:
Ion.c (kernel\msm-4.19\drivers\staging\android\ion) 34556 2020-8-4

*1.struct dma_buf ion_alloc_dmabuf

static const struct dma_buf_ops dma_buf_ops = {
	.map_dma_buf = ion_map_dma_buf,
	.unmap_dma_buf = ion_unmap_dma_buf,
	.mmap = ion_mmap,
	.release = ion_dma_buf_release,
	.attach = ion_dma_buf_attach,
	.detach = ion_dma_buf_detatch,
	.begin_cpu_access = ion_dma_buf_begin_cpu_access,
	.end_cpu_access = ion_dma_buf_end_cpu_access,
	.begin_cpu_access_umapped = ion_dma_buf_begin_cpu_access_umapped,
	.end_cpu_access_umapped = ion_dma_buf_end_cpu_access_umapped,
	.begin_cpu_access_partial = ion_dma_buf_begin_cpu_access_partial,
	.end_cpu_access_partial = ion_dma_buf_end_cpu_access_partial,
	.map = ion_dma_buf_kmap,
	.unmap = ion_dma_buf_kunmap,
	.vmap = ion_dma_buf_vmap,
	.vunmap = ion_dma_buf_vunmap,
	.get_flags = ion_dma_buf_get_flags,
};

struct dma_buf *ion_alloc_dmabuf(size_t len, unsigned int heap_id_mask,
				 unsigned int flags)
{
    // 遍历所有的heap,通过传入的heap,call 不同的heap->ops->allocate
    // heap->ops->allocate
    // 路径:Ion_system_heap.c (kernel\msm-4.19\drivers\staging\android\ion)	21342	2020-8-4
	plist_for_each_entry(heap, &dev->heaps, node) {
		/* if the caller didn't specify this heap id */
		if (!((1 << heap->id) & heap_id_mask))
			continue;
		buffer = ion_buffer_create(heap, dev, len, flags);
		if (!IS_ERR(buffer) || PTR_ERR(buffer) == -EINTR)
			break;
	}
 	exp_info.ops = &dma_buf_ops; // 这个ops是ion的,dma的ops会在dma_buf_export中获取
	exp_info.size = buffer->size;
	exp_info.flags = O_RDWR;
	exp_info.priv = buffer;
	exp_info.exp_name = kasprintf(GFP_KERNEL, "%s-%s-%d-%s", KBUILD_MODNAME,
				      heap->name, current->tgid, task_comm);
  // 下面看一下此函数
  // 此函数就是把上面的几个变量填到dmabuf中,并且把file和dmabuf联系起来
	dmabuf = dma_buf_export(&exp_info);
}

2.dma_buf_export
分配一个dam_buf,并且绑定ops(ion ops),创建file绑定dam_buf以及ops(dma ops),dam_buf ref + 1, 然后返回dam_buf

static const struct file_operations dma_buf_fops = {
	.release	= dma_buf_release,
	.mmap		= dma_buf_mmap_internal,
	.llseek		= dma_buf_llseek,
	.poll		= dma_buf_poll,
	.unlocked_ioctl	= dma_buf_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= dma_buf_ioctl,
#endif
};

struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info){
	dmabuf = kzalloc(alloc_size, GFP_KERNEL);
 
 	dmabuf->priv = exp_info->priv;
	dmabuf->ops = exp_info->ops;
	dmabuf->size = exp_info->size;
	dmabuf->exp_name = exp_info->exp_name;
	dmabuf->owner = exp_info->owner;
	init_waitqueue_head(&dmabuf->poll);
	dmabuf->cb_excl.poll = dmabuf->cb_shared.poll = &dmabuf->poll;
	dmabuf->cb_excl.active = dmabuf->cb_shared.active = 0;
	dmabuf->name = bufname;
	dmabuf->ktime = ktime_get();
  // file 和 dmabuf 绑定,并且file ops 是 dma_buf_fops,上面已经粘贴出来
  // 当我们在后面 call dma_buf_fd 的时候,fd 和 file 绑定,在hal层对fd的操作,实际上是链接到 dma_buf_fops
  // 比如我们在 UMD Call mmap 则call dma_buf_mmap_internal,实际上 dma_buf_mmap_internal 里会call dmabuf->ops->mmap,也就是ion的mmap
  // 并且 file->private_data = priv(dmabuf)
	file = anon_inode_getfile(bufname, &dma_buf_fops, dmabuf,
					exp_info->flags);
     
	file->f_mode |= FMODE_LSEEK;
	dmabuf->file = file;
 
 	dma_buf_ref_init(dmabuf);
  // 所以 dma_buf_export 也会将dmabuf ref + 1
	dma_buf_ref_mod(dmabuf, 1);
}

3.static int32_t cam_mem_get_slot
概述:
获取未使用的slot,用于维护内部的buffer,类似于fd

4.static int cam_mem_util_map_hw_va

1)首先call "cam_mem_util_get_dma_dir"获取DMA的读写模式,对于camera只用到<CAM_MEM_FLAG_HW_READ_WRITE>模式
2)根据flag call 不同的map函数:
① CAM_MEM_FLAG_PROTECTED_MODE>: 会 call "int cam_smmu_map_stage2_iova", 
② 非<CAM_MEM_FLAG_PROTECTED_MODE>: 会 call ”int cam_smmu_map_user_iova“
3) 对于cam_smmu_map_user_iova 
cam_smmu_map_user_iova 
// 此函数中判断了region_id != CAM_SMMU_REGION_SHARED (即flag != CAM_MEM_FLAG_HW_SHARED_ACCESS) 时 len_ptr = (size_t)0;
// 后面讲这里设置的意义
cam_smmu_map_iova_validate_params
// 通过handle计算idx,这里的handle可以理解为devices的handle,对于每个devices是唯一的
// 实际上这个handle是devices ioctl CAM_QUERY_CAP到 KMD 时通过call cam_smmu_get_handle获得的
// 例如:fd 模块在cam_fd_hw_mgr_init中call cam_smmu_get_handle
idx = GET_SMMU_TABLE_IDX(handle);
// 检查 fd 是不是在每个handle(即devices)维护的list中
cam_smmu_check_fd_in_list
// new cam_dma_buff_info *mapping_info保存dma-buf的信息并且加入到链表中
cam_smmu_map_buffer_and_add_to_list
// file->f_count + 1,dam_buf->ref + 1
dma_buf_get
// 这里有对 region_id 做分开处理:
// CAM_SMMU_REGION_SHARED: 会从gen_share_pool分配内存,然后call iommu_map_sg;注意我们在dam_buf_map_attachment中是call iommu_dma_map_sg
// CAM_SMMU_REGION_IO: 正常操作dam_buf_map_attachment,如果没有disable delay map,则会设置DMA_ATTR_DELAYED_UNMAP flag
cam_smmu_map_buffer_validate
4)cam_smmu_map_stage2_iova 与 cam_smmu_map_user_iova大同小异,只是会设置 DMA_ATTR_SKIP_CPU_SYNC flag,应该是PROTECTED_MODE不会有cpu介入访问

5.buffer handle

概述:
1)区别于mmu hanlde,buffer handle是通过 idx(区别于通过mmu handle计算得到的idx) 与 ion_fd做一个计算后得到的
2)CRM MEM 维护了一个 static struct cam_mem_table tbl 全局变量,结构体中有cam_mem_buf_queue bufq[CAM_MEM_BUFQ_MAX]
用来存放buffer handle, ion_fd, flag, mmu handle等, idx通过cam_mem_get_slot得到。所以我们通过buffer handle做逆向运算可以求出idx以及ion_fd

三.map 阶段

概述:
map所要做的只是call dma_buf_get获取dam_buf,然后call cam_mem_util_map_hw_va,间接call dma_buf_attach,以及dam_buf_map_attachment,f_count + 1,dam_buf + 1,最后加入到链表中。

这个链表是干什么的?
变量:iommu_cb_set.cb_info[idx].smmu_buf_list
作用是:每个idx(通过handle得到,可以理解为devices)都会维护一个链表,存放这个devices所import的buffer,当然一个buffer可能会被多个devices import,QC支持在alloc时就map,也可以先alloc后面在map,通过传入的num handle来控制

四.release 阶段

cam_mem_mgr_release
// 通过逆向计算buffer handel获取idx
cam_mem_util_unmap
// 从tal->bufq中拿出ion_fd,mmu handle等
cam_mem_util_unmap_hw_va
// 通过mmu handle,拿到 idx (当前device在数组中的位置),然后把ion_fd所在的list remove掉
// call dma_buf_unmap_attachment dma_buf_detach dma_buf_put等函数
// 要注意的是:alloc阶段分配一个idx,ion_fd,mmu handle放入bufq,map阶段也会分配一个idx,放入fd,mmu handle
// 所以我们在umap哪些devices是根据buf handle来的,buffer handle逆向运算得到哪个idx,就会umap哪些mmu handle代表的devices
cam_smmu_unmap_user_iova

### 解决Android设备上IMX686摄像头溢出问题 对于IMX686摄像头在Android设备上的溢出问题,通常涉及硬件寄存器设置不当或软件配置错误。具体解决方案可以从以下几个方面入手: #### 1. 检查传感器初始化参数 确保`vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/chromatix/0310/chromatix_imx686_sunny/Android.mk`中的配置正确无误[^1]。特别是与曝光时间、增益控制和其他关键参数有关的部分。 #### 2. 验证I2C通信稳定性 确认I2C总线工作正常,不存在干扰或其他异常情况影响到相机模块的数据传输过程。可以尝试调整I2C时钟频率来改善连接质量。 #### 3. 调整帧率和分辨率设定 适当降低视频流的帧速率以及图像解析度有助于减少数据量从而缓解潜在的缓冲区满载状况。这可以通过修改Camera HAL层的相关属性实现。 ```java // 设置较低的预览尺寸和帧率 parameters.setPreviewSize(width, height); parameters.setPreviewFpsRange(min_fps * 1000, max_fps * 1000); camera.setParameters(parameters); ``` #### 4. 增加DMA通道带宽分配 如果系统资源允许的话,考虑增加用于处理来自ISP(Image Signal Processor)输出数据所占用DMA (Direct Memory Access) 的权重比例,以此加快内存访问速度并防止因等待而造成堆积现象发生。 #### 5. 审视内核日志记录 仔细查看kernel log里是否有任何提示性的报错信息,比如类似于“buffer overflow detected”的字样;这些线索往往能帮助定位根本原因所在之处。 通过上述措施应该能够有效应对大多数情况下由IMX686引起的overflow issues,在实际操作过程中还需要密切配合具体的开发环境来进行针对性优化。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值