linux摄像头驱动的拍照流程分析(针对展讯8810(ARM架构),android平台)

首先,我们根据 展讯 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);

本文转自:http://m.blog.csdn.net/blog/duanlove/8224386

posted @ 2013-05-09 11:32  zhgt  阅读(986)  评论(0编辑  收藏  举报