读Linux源码分析ov5640在三星exynos4412平台上的使用

来源:互联网 发布:香港代购mac口红多少钱 编辑:程序博客网 时间:2024/04/30 22:23

ov5640设备的抽象结构体在mach-itop4412.c文件中

    static struct s3c_platform_camera ov5640 = {            .id     = CAMERA_PAR_A,            .clk_name   = "sclk_cam0",            .i2c_busnum = 7,            .cam_power  = smdk4x12_cam1_reset,            .type       = CAM_TYPE_ITU,            .fmt        = ITU_601_YCBCR422_8BIT,            .order422   = CAM_ORDER422_8BIT_CBYCRY,            .info       = &ov5640_i2c_info,            .pixelformat    = V4L2_PIX_FMT_UYVY, //modify by cym V4L2_PIX_FMT_UYVY,            .srclk_name = "xusbxti",            .clk_rate   = 24000000,            .line_length    = 1920,            .width      = 640,            .height     = 480,            .window     = {                .left   = 0,                .top    = 0,                .width  = 640,                .height = 480,        },        /* Polarity */        .inv_pclk   = 0,        .inv_vsync  = 1,        .inv_href   = 0,        .inv_hsync  = 0,        .reset_camera   = 1,        .initialized    = 0,              .layout_rotate = 0 //for shuping, //180,     };

以上结构体注册在fimc_plat结构体中

    static struct s3c_platform_fimc fimc_plat = {#ifdef CONFIG_ITU_A    .default_cam    = CAMERA_PAR_A,#endif#ifdef CONFIG_ITU_B    .default_cam    = CAMERA_PAR_B,#endif#ifdef CONFIG_CSI_C    .default_cam    = CAMERA_CSI_C,#endif#ifdef CONFIG_CSI_D    .default_cam    = CAMERA_CSI_D,#endif#ifdef WRITEBACK_ENABLED    .default_cam    = CAMERA_WB,#endif    .camera     = {#if 0 //added yqf, adjust for middleware request#ifdef CONFIG_VIDEO_S5K4ECGX                &s5k4ecgx,#endif#else//for S5K4EC back#ifdef CONFIG_VIDEO_S5K4ECGX        &s5k4ecgx,#endif#ifdef CONFIG_VIDEO_OV5640        &ov5640,#endif/* add by cym 20140915 */#ifdef CONFIG_VIDEO_TVP5150        &tvp5150,#endif/* end add *///front/* add by cym 20121114 */#ifdef CONFIG_VIDEO_SR200PC20        &sr200pc20,#endif/* end add */#endif#ifdef WRITEBACK_ENABLED        &writeback,#endif    },    .hw_ver     = 0x51,//硬件版本号};

可以看到ov5640是作为fimc_plat结构体的camera成员的一个成员。fimc_plat对应的结构体为s3c_platform_fimc如下:

struct s3c_platform_fimc {    enum fimc_cam_index     default_cam;        /* index of default cam */#ifdef CONFIG_ARCH_EXYNOS4    struct s3c_platform_camera  *camera[7];     /* FIXME */#else    struct s3c_platform_camera  *camera[5];     /* FIXME */#endif    int             hw_ver;//硬件版本号    bool                use_cam;//这个没看懂是干什么的,在s3c_fimc0_set_platdata函数里面初始化为true    void                (*cfg_gpio)(struct platform_device *pdev);//相机GPIO的配置函数,在s3c_fimc0_set_platdata函数里面被赋值为s3c_fimc0_cfg_gpio    int             (*clk_on)(struct platform_device *pdev, struct clk **clk);//时钟开启,在s3c_fimc0_set_platdata函数里面被初始化为s3c_fimc_clk_on    int             (*clk_off)(struct platform_device *pdev, struct clk **clk);//时钟开启,在s3c_fimc0_set_platdata函数里面被初始化为s3c_fimc_clk_off};

s3c_fimc0_set_platdata函数中有个s3c_device_fimc0这是个全局变量,初始化为:

struct platform_device s3c_device_fimc0 = {    .name       = "s3c-fimc",    .id     = 0,    .num_resources  = ARRAY_SIZE(s3c_fimc0_resource),    .resource   = s3c_fimc0_resource,};

看到platform_device这个结构体,应该就知道这是平台设备,是最后和设备驱动进行匹配用的。它的name字段被初始化为”s3c-fimc”,之后会和设备驱动的名字进行匹配。在这个结构体中还有个s3c_fimc0_resource结构:

static struct resource s3c_fimc0_resource[] = {    [0] = {        .start  = S5P_PA_FIMC0,        .end    = S5P_PA_FIMC0 + SZ_4K - 1,        .flags  = IORESOURCE_MEM,    },    [1] = {        .start  = IRQ_FIMC0,        .end    = IRQ_FIMC0,        .flags  = IORESOURCE_IRQ,    },};

这个结构体描述了fimc0这个控制器的物理起始地址,占用范围和对应的IRQ资源。

s3c_fimc0_set_platdata函数中最后把fimc_plat的副本npd放在s3c_device_fimc0的dev.platform_data中。npd对应的s3c_platform_fimc结构,中的函数指针所指向的原形函数在setup_fimc0.c文件中定义。

驱动程序是在fimc_dev.c文件中,直接看平台驱动结构的初始化:

static struct platform_driver fimc_driver = {    .probe      = fimc_probe,    .remove     = fimc_remove,#if (!defined(CONFIG_EXYNOS_DEV_PD) || !defined(CONFIG_PM_RUNTIME))    .suspend    = fimc_suspend,    .resume     = fimc_resume,#endif    .driver     = {        .name   = FIMC_NAME,        .owner  = THIS_MODULE,#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME))        .pm = &fimc_pm_ops,#else        .pm = NULL,#endif    },};

里面的name成员FIMC_NAME实在fimc.h中定义的:

#define FIMC_NAME       "s3c-fimc"

这样,驱动就可以和设备进行match了。
接下来分析驱动。

static int __devinit  fimc_probe(struct platform_device *pdev){    struct s3c_platform_fimc *pdata;    struct fimc_control *ctrl;    int ret;    if (!fimc_dev) {        fimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL);        if (!fimc_dev) {            dev_err(&pdev->dev, "%s: not enough memory\n", __func__);            return -ENOMEM;        }    }    ctrl = fimc_register_controller(pdev);    if (!ctrl) {        printk(KERN_ERR "%s: cannot register fimc\n", __func__);        goto err_alloc;    }    pdata = to_fimc_plat(&pdev->dev);    if ((ctrl->id == FIMC0) && (pdata->cfg_gpio))        pdata->cfg_gpio(pdev);    /* V4L2 device-subdev registration */    ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);    if (ret) {        fimc_err("%s: v4l2 device register failed\n", __func__);        goto err_fimc;    }    /* things to initialize once */    if (!fimc_dev->initialized) {        ret = fimc_init_global(pdev);        if (ret)            goto err_v4l2;    }    /* video device register */    ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);    if (ret) {        fimc_err("%s: cannot register video driver\n", __func__);        goto err_v4l2;    }    video_set_drvdata(ctrl->vd, ctrl);#ifdef CONFIG_VIDEO_FIMC_RANGE_WIDE    ctrl->range = FIMC_RANGE_WIDE;#else    ctrl->range = FIMC_RANGE_NARROW;#endif    ret = device_create_file(&(pdev->dev), &dev_attr_log_level);    if (ret < 0) {        fimc_err("failed to add sysfs entries for log level\n");        goto err_global;    }    ret = device_create_file(&(pdev->dev), &dev_attr_range_mode);    if (ret < 0) {        fimc_err("failed to add sysfs entries for range mode\n");        goto err_global;    }    printk(KERN_INFO "FIMC%d registered successfully\n", ctrl->id);#if (defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME))    sprintf(buf, "fimc%d_iqr_wq_name", ctrl->id);    ctrl->fimc_irq_wq = create_workqueue(buf);    if (ctrl->fimc_irq_wq == NULL) {        fimc_err("failed to create_workqueue\n");        goto err_global;    }    INIT_WORK(&ctrl->work_struct, s3c_fimc_irq_work);    ctrl->irq_cnt.counter = 0;    ctrl->power_status = FIMC_POWER_OFF;    pm_runtime_enable(&pdev->dev);#endif#ifdef CONFIG_BUSFREQ_OPP    /* To lock bus frequency in OPP mode */    ctrl->bus_dev = dev_get(EXYNOS_BUSFREQ_NAME);#endif    return 0;err_global:    video_unregister_device(ctrl->vd);err_v4l2:    v4l2_device_unregister(&ctrl->v4l2_dev);err_fimc:    fimc_unregister_controller(pdev);err_alloc:    kfree(fimc_dev);    return -EINVAL;}

在驱动程序的probe中,fimc_dev是全局fimc_global结构体变量,在probe函数开始时分配的内存。

static struct fimc_control *fimc_register_controller(struct platform_device *pdev){    struct s3c_platform_fimc *pdata;    struct fimc_control *ctrl;    struct resource *res;    int id, err;    struct cma_info mem_info;    struct clk *sclk_fimc_lclk = NULL;    struct clk *fimc_src_clk = NULL;    id = pdev->id;    pdata = to_fimc_plat(&pdev->dev);    ctrl = get_fimc_ctrl(id);    ctrl->id = id;    ctrl->dev = &pdev->dev;    ctrl->vd = &fimc_video_device[id];    ctrl->vd->minor = id;    ctrl->log = FIMC_LOG_DEFAULT;    ctrl->power_status = FIMC_POWER_OFF;    /* CMA */    sprintf(ctrl->cma_name, "%s%d", FIMC_CMA_NAME, ctrl->id);    err = cma_info(&mem_info, ctrl->dev, 0);    fimc_info1("%s : [cma_info] start_addr : 0x%x, end_addr : 0x%x, "            "total_size : 0x%x, free_size : 0x%x\n",            __func__, mem_info.lower_bound, mem_info.upper_bound,            mem_info.total_size, mem_info.free_size);    if (err) {        fimc_err("%s: get cma info failed\n", __func__);        ctrl->mem.size = 0;        ctrl->mem.base = 0;    } else {        ctrl->mem.size = mem_info.total_size;        ctrl->mem.base = (dma_addr_t)cma_alloc            (ctrl->dev, ctrl->cma_name, (size_t)ctrl->mem.size, 0);    }    printk(KERN_DEBUG "ctrl->mem.size = 0x%x\n", ctrl->mem.size);    printk(KERN_DEBUG "ctrl->mem.base = 0x%x\n", ctrl->mem.base);    ctrl->mem.curr = ctrl->mem.base;    ctrl->status = FIMC_STREAMOFF;    switch (pdata->hw_ver) {    case 0x40:        ctrl->limit = &fimc40_limits[id];        break;    case 0x43:    case 0x45:        ctrl->limit = &fimc43_limits[id];        break;    case 0x50:        ctrl->limit = &fimc50_limits[id];        break;    case 0x51:        ctrl->limit = &fimc51_limits[id];        break;    default:        ctrl->limit = &fimc51_limits[id];        fimc_err("%s: failed to get HW version\n", __func__);        break;    }    sprintf(ctrl->name, "%s%d", FIMC_NAME, id);    strcpy(ctrl->vd->name, ctrl->name);    atomic_set(&ctrl->in_use, 0);    mutex_init(&ctrl->lock);    mutex_init(&ctrl->v4l2_lock);    spin_lock_init(&ctrl->outq_lock);    init_waitqueue_head(&ctrl->wq);    /* get resource for io memory */    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);    if (!res) {        fimc_err("%s: failed to get io memory region\n", __func__);        return NULL;    }    /* request mem region */    res = request_mem_region(res->start, res->end - res->start + 1,            pdev->name);    if (!res) {        fimc_err("%s: failed to request io memory region\n", __func__);        return NULL;    }    /* ioremap for register block */    ctrl->regs = ioremap(res->start, res->end - res->start + 1);    if (!ctrl->regs) {        fimc_err("%s: failed to remap io region\n", __func__);        return NULL;    }    /* irq */    ctrl->irq = platform_get_irq(pdev, 0);    if (request_irq(ctrl->irq, fimc_irq, IRQF_DISABLED, ctrl->name, ctrl))        fimc_err("%s: request_irq failed\n", __func__);    if (soc_is_exynos4210())        fimc_src_clk = clk_get(&pdev->dev, "mout_mpll");    else        fimc_src_clk = clk_get(&pdev->dev, "mout_mpll_user");    if (IS_ERR(fimc_src_clk)) {        dev_err(&pdev->dev, "failed to get parent clock\n");        iounmap(ctrl->regs);        return NULL;    }    sclk_fimc_lclk = clk_get(&pdev->dev, FIMC_CORE_CLK);    if (IS_ERR(sclk_fimc_lclk)) {        dev_err(&pdev->dev, "failed to get sclk_fimc_lclk\n");        iounmap(ctrl->regs);        clk_put(fimc_src_clk);        return NULL;    }    if (clk_set_parent(sclk_fimc_lclk, fimc_src_clk)) {        dev_err(&pdev->dev, "unable to set parent %s of clock %s.\n",                fimc_src_clk->name, sclk_fimc_lclk->name);        iounmap(ctrl->regs);        clk_put(sclk_fimc_lclk);        clk_put(fimc_src_clk);        return NULL;    }    clk_set_rate(sclk_fimc_lclk, FIMC_CLK_RATE);    clk_put(sclk_fimc_lclk);    clk_put(fimc_src_clk);#if (!defined(CONFIG_EXYNOS_DEV_PD) || !defined(CONFIG_PM_RUNTIME))    fimc_hwset_reset(ctrl);#endif    return ctrl;}

fimc_register_controller函数用ctrl这个fimc_control结构体,指向全局变量fimc_dev的ctrl。在这个函数中,只对这个指针进行操作,但因为它是个指针,所以操作之后的效果, 和直接操作fimc_dev.ctrl一样,而且返回到probe中后,在fimc_init_global中,同样再直接对fimc_dev进行操作,也可以看作是对ctrl的操作,(在这,指针的强大之处就体现出来了,)在fimc_reister_controller中设置一部分,在fimc_init_global又设置了另外一部分。在fimc_register_controller中,对结构体ctrl的vd成员的操作,完成了V4L2设备操作,和Fime设备操作的关联,fimc_video_device[id]是全局变量,是个video_device结构体,对应于camera的fimc_init_camera的调用,或者说注册是在这里完成的,竖着video_device结构体的ioctl_ops成员继续查找下去,ioctl_ops成员本身是个V4L2_ioctl_ops的结构体指针,指向了fimc_v4l2_ops,而fimc_v4l2_ioctl_ops的定义在Fimc_v4l2.c文件中,是个全局变量,这个结构体很大,没仔细看,(v4l2是很重要的东西,网上介绍的资料很多,之后会成为主要的学习方向,结构体的成员实在太多太杂,看着有点头晕)不过有个vidioc_s_input被赋值为fimc_s_input,fimc_s_input这个函数调用了fimc_configure_subdev函数,而fimc_configure_subdev最终调用了fimc_init_camera
fimc_init_global函数建立起了platform_device和驱动程序的关系。此时将设备层面的配置,关联到了驱动当中。

在fimc_v4l2.c文件中的全局变量fimc_v4l2_ops中有个vidioc_streamon函数指针被赋值为fimc_streamon,在这个函数中调用了fimc_stream_capture,在fimc_stream_capture这个函数中有个s3c_platform_camera *cam结构体指针,通过查找可以发现,在mach-itop4412.c文件中定义的结构体也是s3c_platform_camera,继续读代码,在fimc_stream_capture函数中有个fimc_control结构体指针ctrl,cam的值就是ctrl->cam,而这个ctrl实际就是fimc_dev.c文件中的全局变量fimc_dev,前面分析过,在fimc_probe函数中,有对fimc_dev的初始化,在fimc_probe中,调用的finc_init_global函数中,把fimc_dev->camera[i]赋值为pdata->camera[i], 这个东西实际就是在mach-itop4412.c文件中的fimc_plat这个sc3_platform_fimc结构体,这个结构体中的camera[]数组成员中就有ov5640这个具体设备。所以到此就把这个设备到驱动到v4l2串起来了。写的比较潦草,仅作记录,如果有幸能为后来的人提供个参考。

1 0