首先,我们根据 展讯 SC8810的datasheet的摄像原理相关章节,找到了摄像相关寄存器的名字(CAP_FRM_SIZE、CAP_IMG_DECI),通过在驱动目录(kernel/drivers/media/),对这些名字的搜索,很幸运的可以找到相关源文件。
$ find kernel/drivers/media/ | xargs grep "CAP_FRM_SIZE"
kernel/drivers/media/video/sprd_dcam/sc8810/sc8810_reg_isp.h: union _CAP_FRM_SIZE_TAG
kernel/drivers/media/video/sprd_dcam/sc8810/sc8810_reg_isp.h: struct _CAP_FRM_SIZE_MAP
kernel/drivers/media/video/sprd_dcam/sc8810/sc8810_reg_isp.h: union _CAP_FRM_SIZE_TAG
kernel/drivers/media/video/sprd_dcam/sc8810/sc8810_reg_isp.h: struct _CAP_FRM_SIZE_MAP
$ find kernel/drivers/media/ | xargs grep "CAP_IMG_DECI" kernel/drivers/media/video/sprd_dcam/sc8810/sc8810_reg_isp.h: union _CAP_IMG_DECI_TAG
kernel/drivers/media/video/sprd_dcam/sc8810/sc8810_reg_isp.h: struct _CAP_IMG_DECI_MAP
kernel/drivers/media/video/sprd_dcam/sc8810/sc8810_reg_isp.h: union _CAP_IMG_DECI_TAG
kernel/drivers/media/video/sprd_dcam/sc8810/sc8810_reg_isp.h: struct _CAP_IMG_DECI_MAP
$ vim kernel/drivers/media/video/sprd_dcam/sc8810/sc8810_reg_isp.h (这个文件里定义了摄像头相关的寄存器)
$ find kernel/drivers/media/ | xargs grep "sc8810_reg_isp.h"
kernel/drivers/media/video/sprd_dcam/sc8810/sc8810_reg_isp.h:* drivers/media/video/sprd_dcam/sc8810_reg_isp.h (这个是源文件的头注释,实际上路径写错了。)
kernel//drivers/media/video/sprd_dcam/sc8810/dcam_drv_sc8810.h:#include "sc8810_reg_isp.h"
kernel/drivers/media/video/sprd_scale/scale_drv_sc8810.h:#include "../sprd_dcam/sc8810/sc8810_reg_isp.h"
我们看到 ,有 “dcam_drv_sc8810.h”和 “scale_drv_sc8810.h” 两个头文件又引用了 “sc8810_reg_isp.h”。
我们继续向下追踪:
$ find kernel/drivers/media/ | xargs grep "dcam_drv_sc8810.h"
kernel/drivers/media/video/sprd_dcam/sc8810/dcam_drv_sc8810.c:#include "dcam_drv_sc8810.h"
kernel/drivers/media/video/sprd_dcam/sc8810/dcam_service_sc8810.h:#include "dcam_drv_sc8810.h"
kernel/drivers/media/video/sprd_dcam/sc8810/dcam_drv_sc8810.h:* drivers/media/video/sprd_dcam/sc8810/dcam_drv_sc8810.h(这个是源文件的头注释。)
最后, “dcam_drv_sc8810.c”引用了 “dcam_drv_sc8810.h” ,所以,我们猜想 “dcam_drv_sc8810.c” 肯定是使用了这些寄存器进行编程的。
另外, “dcam_service_sc8810.h” 除了包含了 “dcam_drv_sc8810.h” 之外,自己还定义了一些枚举类型、结构体和宏,例如:
typedef struct dcam_init_param { DCAM_MODE_TYPE_E mode; DCAM_DATA_FORMAT_E format; DCAM_YUV_PATTERN_E yuv_pattern; RGB_TYPE_E display_rgb_type; DCAM_SIZE_T0 input_size; DCAM_POLARITY_T polarity; DCAM_RECT_T0 input_rect; DCAM_RECT_T0 display_rect; DCAM_RECT_T0 encoder_rect; DCAM_ROTATION_E rotation; int skip_frame; uint32_t first_buf_addr; uint32_t first_u_buf_addr; uint32_t zoom_level; uint32_t zoom_multiple; uint32_t skip_flag; uint32_t is_Y_UV; }DCAM_INIT_PARAM_T;
顺便追踪下,dcam_service的路径:
$ find kernel/drivers/media/ | xargs grep "dcam_service_sc8810.h"
kernel/drivers/media/video/sprd_dcam/sc8810/dcam_service_sc8810.c:#include "dcam_service_sc8810.h"
kernel/drivers/media/video/sprd_dcam/sc8810/dcam_service.h:#include "dcam_service_sc8810.h"
kernel/drivers/media/video/sprd_dcam/sc8810/dcam_service_sc8810.h:* drivers/media/video/sprd_dcam/dcam_service_sc8810.h
在 “dcam_service_sc8810.c” 中我们可以看到如下内容,主要是对摄像头的某些参数进行了初始化。 这里先列出部分源文件,为了使大家有一个印象。之后再详细分析流程。
typedef struct dcam_parameter { DCAM_MODE_TYPE_E mode; DCAM_DATA_FORMAT_E format; DCAM_YUV_PATTERN_E yuv_pattern; RGB_TYPE_E display_rgb_type; DCAM_SIZE_T0 input_size; DCAM_POLARITY_T polarity; DCAM_RECT_T0 input_rect; DCAM_RECT_T0 display_rect; DCAM_RECT_T0 encoder_rect; DCAM_ROTATION_E rotation; int skip_frame; uint32_t first_buf_addr; uint32_t first_buf_uv_addr; uint32_t zoom_level; uint32_t zoom_multiple; uint32_t no_skip_frame_flag; uint32_t is_Y_UV; }DCAM_PARAMETER_T; DCAM_PARAMETER_T g_dcam_param;
int dcam_parameter_init(DCAM_INIT_PARAM_T *init_param) { DCAM_TRACE("DCAM: dcam_parameter_init start. \n"); g_dcam_param.mode = init_param->mode; g_dcam_param.format = init_param->format; g_dcam_param.yuv_pattern = init_param->yuv_pattern; g_dcam_param.display_rgb_type = init_param->display_rgb_type; g_dcam_param.input_size.w = init_param->input_size.w; g_dcam_param.input_size.h = init_param->input_size.h; g_dcam_param.polarity.hsync = init_param->polarity.hsync; g_dcam_param.polarity.vsync = init_param->polarity.vsync; g_dcam_param.polarity.pclk = init_param->polarity.pclk; g_dcam_param.input_rect.x = init_param->input_rect.x; g_dcam_param.input_rect.y = init_param->input_rect.y; g_dcam_param.input_rect.w = init_param->input_rect.w; g_dcam_param.input_rect.h = init_param->input_rect.h; g_dcam_param.display_rect.x = init_param->display_rect.x; g_dcam_param.display_rect.y = init_param->display_rect.y; g_dcam_param.display_rect.w = init_param->display_rect.w; g_dcam_param.display_rect.h = init_param->display_rect.h; g_dcam_param.encoder_rect.x = init_param->encoder_rect.x; g_dcam_param.encoder_rect.y = init_param->encoder_rect.y; g_dcam_param.encoder_rect.w = init_param->encoder_rect.w; g_dcam_param.encoder_rect.h = init_param->encoder_rect.h; g_dcam_param.rotation = init_param->rotation; g_dcam_param.skip_frame = init_param->skip_frame; g_dcam_param.first_buf_addr = init_param->first_buf_addr; g_dcam_param.first_buf_uv_addr = init_param->first_u_buf_addr; g_dcam_param.zoom_level = init_param->zoom_level; g_dcam_param.zoom_multiple = init_param->zoom_multiple; g_dcam_param.is_Y_UV = init_param->is_Y_UV; if(0 == init_param->skip_flag) { g_dcam_param.no_skip_frame_flag = 1; } else { g_dcam_param.no_skip_frame_flag = 0; } DCAM_TRACE("DCAM: dcam_parameter_init mode: %d, format: %d, yuv_pattern: %d. \n", g_dcam_param.mode,g_dcam_param.format,g_dcam_param.yuv_pattern); DCAM_TRACE("DCAM: dcam_parameter_init disp w: %d, disp h: %d, input_rect:w: %d, h:%d\n", g_dcam_param.display_rect.w,g_dcam_param.display_rect.h,g_dcam_param.input_rect.w,g_dcam_param.input_rect.h); DCAM_TRACE("DCAM: dcam_parameter_init end. \n"); DCAM_TRACE("DCAM: dcam_parameter_init input rect:%d,%d,%d,%d\n", g_dcam_param.input_rect.x,g_dcam_param.input_rect.y,g_dcam_param.input_rect.w, g_dcam_param.input_rect.h); return 0; }
我们回过头来看 “dcam_drv_sc8810.c” 这个文件中做了什么吧。它主要定义了如下变量和 函数,当然,为了让篇幅不至于臃肿,我并没有列出所有的函数和变量的定义。
typedef struct _isp_path_desc_tag { ISP_SIZE_T input_size; ISP_RECT_T input_rect; ISP_SIZE_T sc_input_size; ISP_SIZE_T output_size; ISP_FRAME_T input_frame; uint32_t input_format; ISP_FRAME_T *p_output_frame_head; ISP_FRAME_T *p_output_frame_cur; uint32_t output_frame_count; uint32_t output_format; uint32_t output_frame_flag; ISP_FRAME_T swap_frame; ISP_FRAME_T line_frame; uint32_t scale_en; uint32_t sub_sample_en; uint32_t sub_sample_factor; uint32_t sub_sample_mode; uint32_t slice_en; uint32_t h_scale_coeff; uint32_t v_scale_coeff; }ISP_PATH_DESCRIPTION_T; typedef struct _isp_module_tagss { ISP_MODE_E isp_mode; uint32_t module_addr; ISP_CAP_DESCRIPTION_T isp_cap; ISP_PATH_DESCRIPTION_T isp_path1; ISP_PATH_DESCRIPTION_T isp_path2; ISP_ISR_FUNC_PTR user_func[ISP_IRQ_NUMBER]; }ISP_MODULE_T; static ISP_FRAME_T s_path1_frame[ISP_PATH1_FRAME_COUNT_MAX]; static ISP_FRAME_T s_path2_frame[ISP_PATH2_FRAME_COUNT_MAX]; static ISP_MODULE_T s_isp_mod; uint32_t g_is_stop = 0;
static void _ISP_DrvierModuleReset(uint32_t base_addr); static uint32_t _ISP_DriverReadIrqLine(uint32_t base_addr); static void _ISP_DriverIrqClear(uint32_t base_addr,uint32_t mask); static void _ISP_DriverIrqDisable(uint32_t base_addr,uint32_t mask); static void _ISP_DriverIrqEnable(uint32_t base_addr,uint32_t mask); static void _ISP_ISRSensorStartOfFrame(uint32_t base_addr); static void _ISP_ISRSensorEndOfFrame(uint32_t base_addr); static void _ISP_ISRCapStartOfFrame(uint32_t base_addr); static void _ISP_ISRCapEndOfFrame(uint32_t base_addr); static void _ISP_ISRPath1Done(uint32_t base_addr); static void _ISP_ISRCapFifoOverflow(uint32_t base_addr); static void _ISP_ISRSensorLineErr(uint32_t base_addr); static void _ISP_ISRSensorFrameErr(uint32_t base_addr); static void _ISP_ISRJpegBufOverflow(uint32_t base_addr); static ISR_EXE_T _ISP_ISRSystemRoot(uint32_t param); static void _ISP_DriverISRRoot(uint32_t base_addr); static void _ISP_DriverLinkFrames(void); static void _ISP_DriverAutoCopy(uint32_t base_addr); static int32_t _ISP_DriverCalcSC1Size(void); static int32_t _ISP_DriverSetSC1Coeff(uint32_t base_addr); static int32_t _ISP_DriverPath1TrimAndScaling(uint32_t base_addr); static int32_t _ISP_DriverCalcSC2Size(void); static int32_t _ISP_DriverGenScxCoeff(uint32_t base_addr, uint32_t idxScx); static void _ISP_ISRPath2Done(uint32_t base_addr);
这里先介绍一下 _pard 这个宏。因为摄像头驱动里对寄存器的操作都用到了它。一共有如下几个文件中定义了这个宏。
kernel/drivers/media/video/sprd_rotation/rotation_reg_sc8800g2.h:#define _pard(a) __raw_readl(a)
kernel/drivers/media/video/sprd_dcam/sc8810/dcam_common.h:#define _pard(a) __raw_readl(a)
kernel/drivers/media/video/sprd_dcam/dcam_common.h:#define _pard(a) __raw_readl(a)
kernel/drivers/media/video/sprd_scale/scale_drv_sc8810.h:#define _pard(a) __raw_readl(a)
kernel/drivers/media/video/sprd_scale/scale_reg_sc8800g2.h:#define _pard(a) __raw_readl(a)
#define _pard(a) __raw_readl(a) ,而 __raw_readl(a) 的意思就是 把a为地址的存储单元的数据读出来。 具体过程可以参考“嵌入式linux和uboot中关于读写寄存器的函数(__raw_writel, writel等) ”。
#define readl(a) __arch_getl(a)
#define __arch_getl(a) (*(volatile unsigned int *)(a)) //就是把值通过指针读出来
“dcam_drv_sc8810.c” 中的函数流程如下:
int32_t ISP_DriverStart(uint32_t base_addr, uint32_t is_Y_UV)
ISP_CHECK_PARAM_ZERO_POINTER(base_addr);
_ISP_DriverPath1TrimAndScaling(base_addr);
_ISP_DriverIrqClear(base_addr,ISP_IRQ_LINE_MASK);
_ISP_DriverIrqEnable(base_addr, ISP_IRQ_LINE_MASK);
#ifdef DCAM_DRV_DEBUG
_ISP_GetReg();
#endif
_ISP_DriverForeCopy(base_addr);
int32_t ISP_DriverStop(uint32_t base_addr)
ISP_CHECK_PARAM_ZERO_POINTER(base_addr);
_ISP_DriverIrqDisable(base_addr,ISP_IRQ_LINE_MASK);
switch(s_isp_mod.isp_mode)
{
case ISP_MODE_CAPTURE:
case ISP_MODE_MPEG:
case ISP_MODE_VT:
case ISP_MODE_PREVIEW_EX:
p_isp_reg->dcam_path_cfg_u.mBits.cap_eb = 0;
if(ISP_MODE_PREVIEW_EX == s_isp_mod.isp_mode)
{
isp_put_path2();
}
msleep(20);//wait the dcam stop
break;
case ISP_MODE_PREVIEW:
{
p_isp_reg->dcam_path_cfg_u.mBits.cap_eb = 0;
g_is_stop = 1;
}
break;
default:
rtn = ISP_DRV_RTN_MODE_ERR;
break;
}
_ISP_DriverIrqDisable(base_addr, ISP_IRQ_LINE_MASK);
_ISP_DriverIrqClear(base_addr,ISP_IRQ_LINE_MASK);
ISP_DriverStart() 在 dcam_service_sc8810.c 和 dcam_sc8800g2.c中被调用了:
$ find kernel/drivers/media/ | xargs grep "= ISP_DriverStart"
kernel/drivers/media/video/sprd_dcam/sc8810/dcam_service_sc8810.c: rtn_drv = ISP_DriverStart(s->module_addr, g_dcam_param.is_Y_UV);
kernel/drivers/media/video/sprd_dcam/sc8810/dcam_service_sc8810.c: rtn_drv = ISP_DriverStart(s->module_addr, 0);
kernel/drivers/media/video/sprd_dcam/dcam_sc8800g2.c: rtn_drv = ISP_DriverStart(s->module_addr);
kernel/drivers/media/video/sprd_dcam/dcam_sc8800g2.c: rtn_drv = ISP_DriverStart(s->module_addr);
int dcam_start(void) // kernel~video/sprd_dcam/sc8810/dcam_v4l2.c
ISP_ServiceStartPreview() // ~/sprd_dcam/sc8810/dcam_service_sc8810.c 前面的路径略掉
_ISP_ServiceStartPreview()
ISP_DriverStart(s->module_addr, g_dcam_param.is_Y_UV);// ~/sprd_dcam/sc8810/dcam_drv_sc8810.c
有两个地方调用了dcam_start()
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) //kernel/drivers/media/video/sprd_dcam/sc8810/dcam_v4l2.c
dcam_start()
static void dcam_start_handle(int param) // kernel/drivers/media/video/sprd_dcam/sc8810/dcam_v4l2.c
dcam_start();
static int vidioc_handle_ctrl(struct v4l2_control *ctrl) // kernel/drivers/media/video/sprd_dcam/sc8810/dcam_v4l2.c
switch(ctrl->id)
case: V4L2_CID_BRIGHTNESS||* 等其他好几个分支都由调用dcam_start_handle
dcam_start_handle(is_previewing)
// kernel/drivers/media/video/sprd_dcam/sc8810/dcam_v4l2.c 文件中dcam_ioctl_ops 结构体被V4L2子系统注册。
static const struct v4l2_ioctl_ops dcam_ioctl_ops = { .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, .vidioc_querycap = vidioc_querycap, .vidioc_cropcap = vidioc_cropcap, .vidioc_s_crop = vidioc_s_crop, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, .vidioc_s_std = vidioc_s_std, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_crop = vidioc_g_crop, .vidioc_g_output = vidioc_g_output, .vidioc_querymenu = vidioc_querymenu, #ifdef CONFIG_VIDEO_V4L1_COMPAT // .vidiocgmbuf = vidiocgmbuf, #endif }; static struct video_device dcam_template = { .name = "dcam", .fops = &dcam_fops, .ioctl_ops = &dcam_ioctl_ops, .minor = -1, .release = video_device_release, .tvnorms = V4L2_STD_525_60, .current_norm = V4L2_STD_NTSC_M, };
static int __init create_instance(int inst)
{
struct dcam_dev *dev;
struct video_device *vfd;
int ret, i;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
"%s-%03d", DCAM_MODULE_NAME, inst);
ret = v4l2_device_register(NULL, &dev->v4l2_dev);
if (ret)
goto free_dev;
/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
init_waitqueue_head(&dev->vidq.wq);
/* initialize locks */
spin_lock_init(&dev->slock);
mutex_init(&dev->mutex);
ret = -ENOMEM;
vfd = video_device_alloc();
if (!vfd)
goto unreg_dev;
*vfd = dcam_template;
vfd->debug = debug;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
if (ret < 0)
goto rel_vdev;
video_set_drvdata(vfd, dev);
/* Set all controls to their default value. */
for (i = 0; i < ARRAY_SIZE(dcam_qctrl); i++)
dev->qctl_regs[i] = dcam_qctrl[i].default_value;
/* Now that everything is fine, let's add it to device list */
list_add_tail(&dev->dcam_devlist, &dcam_devlist);
snprintf(vfd->name, sizeof(vfd->name), "%s (%i)",
dcam_template.name, vfd->num);
if (video_nr >= 0)
video_nr++;
dev->vfd = vfd;
v4l2_info(&dev->v4l2_dev, "V4L2 device registered as /dev/video%d\n", vfd->num);
return 0;
rel_vdev:
video_device_release(vfd);
unreg_dev:
v4l2_device_unregister(&dev->v4l2_dev);
free_dev:
kfree(dev);
return ret;
}
static struct platform_driver dcam_driver = {
.probe = dcam_probe,
.remove = dcam_remove,
.driver = {
.owner = THIS_MODULE,
.name = "sc8800g_dcam",
},
};
int __init dcam_v4l2_init(void)
{
int ret = 0, i;
if(platform_driver_register(&dcam_driver) != 0) {
printk("platform device register Failed \n");
return -1;
}
if (n_devs <= 0)
n_devs = 1;
for (i = 0; i < n_devs; i++) {
ret = create_instance(i) ;
if (ret) {
/* If some instantiations succeeded, keep driver */
if (i)
ret = 0;
break;
}
}
if (ret < 0) {
printk(KERN_INFO "Error %d while loading dcam driver\n", ret);
return ret;
}
printk(KERN_INFO "Video Technology Magazine Virtual Video "
"Capture Board ver %u.%u.%u successfully loaded.\n",
(DCAM_VERSION >> 16) & 0xFF, (DCAM_VERSION >> 8) & 0xFF,
DCAM_VERSION & 0xFF);
/* n_devs will reflect the actual number of allocated devices */
n_devs = i;
return ret;
}
module_init(dcam_v4l2_init);
module_exit(dcam_v4l2_exit);