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

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串起来了。写的比较潦草,仅作记录,如果有幸能为后来的人提供个参考。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值