isp_probe
isp_initialize_modules
ret = omap3isp_csiphy_init(isp);
ret = omap3isp_csi2_init(isp);
ret = csi2_init_entities(csi2a);
ret = omap3isp_video_init(&csi2->video_out, "CSI2a");
ret = omap3isp_ccp2_init(isp);
ret = ccp2_init_entities(ccp2);
ret = omap3isp_video_init(&ccp2->video_in, "CCP2");
ret = omap3isp_ccdc_init(isp);
ret = ccdc_init_entities(ccdc);
ret = omap3isp_video_init(&ccdc->video_out, "CCDC");
ret = omap3isp_preview_init(isp);
return preview_init_entities(prev);
ret = omap3isp_video_init(&prev->video_in, "preview");
ret = omap3isp_video_init(&prev->video_out, "preview");
ret = omap3isp_resizer_init(isp);
return resizer_init_entities(res);
ret = omap3isp_video_init(&res->video_in, "resizer");
ret
omap3isp_video_init(&res->video_out,
=
"resizer");
//csi2a,ccp2,ccdc,preview,resizer 子设备有向上层开放操作接口(video_device)
ret = omap3isp_hist_init(isp);
ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops);
ret = omap3isp_h3a_aewb_init(isp);
return omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops);
ret = omap3isp_h3a_af_init(isp);
return
omap3isp_stat_init(af,
//hist,aewb,af 子设备不需要向上层开放操作接口
"AF",
&h3a_af_subdev_ops);
omap3isp_video_init(struct isp_video *video, const char *name)// 初 始 化
video_device(ispvideo.c)
video->video.fops = &isp_video_fops;
video->video.vfl_type = VFL_TYPE_GRABBER;
video->video.release = video_device_release_empty;
video->video.ioctl_ops = &isp_video_ioctl_ops;
video->pipe.stream_state = ISP_PIPELINE_STREAM_STOPPED;
video_set_drvdata(&video->video, video);
omap3isp_video_register
csi2a,ccp2,ccdc,preview,resizer
ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
共
#
video_device.fops,video_device.ioctl_ops(
不
同 ),vb2_queue.ops,vb2_queue.mem_ops, 需 要 各 自 实 现 的 是 不 同 的 底 层 硬 件 操 作
v4l2_subdev_ops
# preview,resizer 的输入和输出都有上层应用参与处理,所以一个 v4l2_subdev 同时对应 2 个
video_device(2 个不同的设备节点).
子
备
备
只
是
节
点
设
设
用
# 一个 v4l2_subdev 可能没有对应的 video_device,也可能对应不止一个 video_device(挂接在
OMAP ISP 上的外部 sensor 没有实现对应的 video_device)
#
v4l2_subdev_ops 绑 定 给 v4l2_subdev 由 v4l2_subdev_init 负 责 完
成.v4l2_subdev_call(sd, o, f, args...)用于调用 v4l2_subdev_ops 某函数集中的指定函数
# 为 vb2_queue 绑定 ops 和 mem_ops 函数集,一般在 open 函数中进行.call_ptr_memop(vb, op,
args...)用于调用 vb2_queue.mem_ops 里的指定函数.
# 上 层 的 调 用 动 作 (fops,ioctl_ops) 最 终 会 落 实 到 3 处 : v4l2_subdev_ops( 针 对 硬 件 ),
vb2_queue.ops( 针 对 缓 冲 区 队 列 , 按 需 实 现 ), vb2_queue.mem_ops( 针 对 缓 冲 区 , 使 用 如
videobuf2-dma-contig.c 中的实现)
# ISP 各组件共享同一条中断线,中断处理函数通过读取中断状态寄存器来判断中断源.
# ISP 内部带有硬件缓冲区,数据在各组件处理期间不需要内存参与,驱动申请的缓冲区用于存储 ISP 处
理后的数据,对应下图中的 4(raw data to YUV4:2:2),3(resize)或者 C(raw data),当有数据存
入驱动维护的缓冲区后,应用层就可取出里面的数据做后续处理(软件转码等)
RAW data are processed through the CCDC module and are directly pipelined to the preview
engine.
Another way is to output directly from the CCDC to memory (C) In the preview block; the
format is
converted from RAW data to YUV4:2:2 (1). The data can be output to memory (4) or pipelined
to the
resizer (2). The rescaled YUV4:2:2 image is finally stored in memory (3).
In parallel, processed data in the CCDC are used by the H3A module (A), which writes tables
of statistics
in memory, and by the HIST module (B). The results of the HIST modules are stored in status
registers in
the HIST module.
static struct v4l2_file_operations isp_video_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = video_ioctl2,
.open = isp_video_open,
.release = isp_video_release,
.poll = isp_video_poll,
.mmap = isp_video_mmap,
};
isp_video_open
struct isp_video_fh *handle;
struct vb2_queue *queue;
handle = kzalloc(sizeof(*handle), GFP_KERNEL);
v4l2_fh_init(&handle->vfh, &video->video); // 初 始 化 v4l2_fh, 并 把
video_device 的 *ctrl_handler 绑 定 给 v4l2_fh 的 *ctrl_handler(fh->ctrl_handler =
vdev->ctrl_handler;)
v4l2_fh_add(&handle->vfh); //将 v4l2_fh 链接到 video_device 的 fh_list 链表
queue = &handle->queue;
queue->type = video->type;
queue->io_modes = VB2_MMAP | VB2_USERPTR;
queue->drv_priv = handle;
queue->ops = &isp_video_queue_ops; //管理缓冲区队列的函数集
queue->mem_ops = &vb2_dma_contig_memops;
// 操 作 缓 冲 区 的 函 数
集,vb2_dma_contig_memops 定义在 videobuf2-dma-contig.c
queue->buf_struct_size = sizeof(struct isp_buffer);
queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(&handle->queue); //初始化缓冲区队列
memset(&handle->format, 0, sizeof(handle->format));
handle->format.type = video->type;
handle->timeperframe.denominator = 1;
handle->video = video;
file->private_data = &handle->vfh; //v4l2_fh 作为 file->private_data
isp_video_reqbufs
ret = vb2_reqbufs(&vfh->queue, rb);
return ret ? ret : vb2_core_reqbufs(q, req->memory, &req->count);
/* Finally, allocate buffers and video memory */
allocated_buffers
__vb2_queue_alloc(q,
=
memory,
num_buffers,
num_planes, plane_sizes);
/* Allocate video buffer memory for the MMAP type */
if (memory == VB2_MEMORY_MMAP) {
ret = __vb2_buf_mem_alloc(vb);
mem_priv
=
call_ptr_memop(vb,
alloc,
q->alloc_ctx[plane], size, dma_dir, q->gfp_flags);
有 ”queue->mem_ops
vb2_dma_contig_memops 里的 vb2_dc_alloc
=
vb2_dc_alloc
中
&vb2_dma_contig_memops”, 所 以 这 里 调 用 的 是
//ispvideo.c:isp_video_open
__setup_offsets(vb);
/*
* Call the driver-provided buffer initialization
* callback, if given. An error in initialization
* results in queue setup failure.
*/
ret = call_vb_qop(vb, buf_init, vb);
// 调 用 由 驱 动 实 现 的
vb2_ops.buf_init(ispvideo.c:isp_video_queue_ops 没有实现 buf_init)
}
isp_video_qbuf
ret = vb2_qbuf(&vfh->queue, b);
return vb2_internal_qbuf(q, b);
return ret ? ret : vb2_core_qbuf(q, b->index, b);
ret = __buf_prepare(vb, pb);
et = __qbuf_mmap(vb, pb);
return ret ? ret : call_vb_qop(vb, buf_prepare, vb);
vb2_ops.buf_prepare(ispvideo.c:isp_video_buffer_prepare)
isp_video_buffer_prepare
// 调 用 由 驱 动 实 现 的
/*
* Add to the queued buffers list, a buffer will stay on it until
* dequeued in dqbuf.
*/
list_add_tail(&vb->queued_entry, &q->queued_list); //
把
vb2_buffer 放入 vb2_queue 尾部
/*
* If already streaming, give the buffer to driver for processing.
* If not, the buffer will be given to driver on next streamon.
*/
if (q->start_streaming_called)
__enqueue_in_driver(vb);
call_void_vb_qop(vb, buf_queue, vb);
//调用由驱动实现
的 vb2_ops.buf_queue(ispvideo.c)
isp_video_buffer_queue
/*
* If streamon has been called, and we haven't yet called
* start_streaming() since not enough buffers were queued, and
* we now have reached the minimum number of queued buffers,
* then we can finally call start_streaming().
*/
if
!q->start_streaming_called
(q->streaming
&&
&&
q->queued_count >= q->min_buffers_needed)
ret = vb2_start_streaming(q);
/*
* If any buffers were queued before streamon,
* we can now pass them to driver for processing.
*/
list_for_each_entry(vb, &q->queued_list, queued_entry)
__enqueue_in_driver(vb);
call_void_vb_qop(vb, buf_queue, vb);
// 调 用
由驱动实现的 vb2_ops.buf_queue
isp_video_buffer_queue
q,
atomic_read(&q->owned_by_drv_count)); // 调 用 由 驱 动 实 现 的
vb2_ops.start_streaming
start_streaming,
call_qop(q,
ret
=
isp_video_start_streaming