android2.3.4----5.ov9650驱动分析--附clk设置

来源:互联网 发布:秦美人盾牌进阶数据 编辑:程序博客网 时间:2024/05/29 03:16

一. V4L2的模块的初始化过程
module_init(videodev_init)
–> videodev_init
在drivers/media/video/v4l2-dev.c中
static int __init videodev_init(void)
{
dev_t dev = MKDEV(VIDEO_MAJOR, 0); //请求设备号VIDEO_MAJOR=81
register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
class_register(&video_class); //注册一个class
}
v4l2这个模块好像没有干什么事.
二. s3c_fimc_driver的初始化过程
1.
module_init(s3c_fimc_register);
–>s3c_fimc_register
在drivers/media/video/samsung/fimc/s3c_fimc_core.c中
static int s3c_fimc_register(void)
{
platform_driver_register(&s3c_fimc_driver);
}
platform_driver的结构如下:
static struct platform_driver s3c_fimc_driver = {
.probe = s3c_fimc_probe,
.remove = s3c_fimc_remove,
.suspend = s3c_fimc_suspend,
.resume = s3c_fimc_resume,
.driver = {
.name = “s3c-fimc”,
.owner = THIS_MODULE,
},
};
先看一下s3cfimc的设备
在arch/arm/plat-samsun/dev-fimc0.c中
static struct s3c_platform_fimc default_fimc0_data __initdata = {
.srclk_name = “hclk”,
.clk_name = “fimc”,
.clockrate = 133000000,
.line_length = 720,
.nr_frames = 4,
.shared_io = 0,
};
下一步进入probe过程
static int s3c_fimc_probe(struct platform_device *pdev)
{
struct s3c_platform_fimc *pdata;
struct s3c_fimc_control *ctrl;
struct clk *srclk;
int ret;

ctrl = s3c_fimc_register_controller(pdev);     //pdata = to_fimc_plat(&pdev->dev);if (pdata->cfg_gpio)    pdata->cfg_gpio(pdev);                     //gpio的初始化调用s3c_fimc0_cfg_gpiosrclk = clk_get(&pdev->dev, pdata->srclk_name);      //获取srclk_namectrl->clock = clk_get(&pdev->dev, pdata->clk_name);  //获取fimc_clk并enableclk_enable(ctrl->clock);  //向v4l2注册TYPE_GRABBER即图像采集设备  ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);    //VFL_TYPE_GRABBER 图像采集设备return 0;

}
简要说明一下
module_init(s3c_fimc_register);
–>s3c_fimc_register
–> s3c_fimc_probe
–> s3c_fimc_register_controller
struct s3c_fimc_control *s3c_fimc_register_controller(struct platform_device *pdev)
{
struct s3c_platform_fimc *pdata;
struct s3c_fimc_control *ctrl;
int id = pdev->id;
pdata = to_fimc_plat(&pdev->dev);
ctrl = &s3c_fimc.ctrl[id];
ctrl->id = id;
ctrl->dev = &pdev->dev;
ctrl->vd = &s3c_fimc_video_device[id]; //这句代码很关键

return ctrl;

}
看一下s3c_fimc_video_deivce的定义就知道了:
struct video_device s3c_fimc_video_device[S3C_FIMC_MAX_CTRLS] = {
[0] = {
.vfl_type = VID_TYPE_OVERLAY | VID_TYPE_CAPTURE | VID_TYPE_CLIPPING | VID_TYPE_SCALES,
.fops = &s3c_fimc_fops,
.ioctl_ops = &s3c_fimc_v4l2_ops,
.release = s3c_fimc_vdev_release,
.name = “sc3_video0”,
},
};
这实际上是注册了一两套函数指针,s3c_fimc_fops与s3c_fimc_v4l2_ops
以后就可以通过vl42_fops来操作fimc设备了
static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.get_unmapped_area = v4l2_get_unmapped_area,
.mmap = v4l2_mmap,
.unlocked_ioctl = v4l2_ioctl,

ifdef CONFIG_COMPAT

.compat_ioctl = v4l2_compat_ioctl32,

endif

.release = v4l2_release,.poll = v4l2_poll,.llseek = no_llseek,

};

三. ov965x_i2c_driver的初始化过程
1. 硬件的定义
ov9650的硬件定义在arch/arm/mach-s3c64xx/mach-smdk6410.c中
在arch/arm/mach-s3c64xx/mach-smdk6410.c中
static struct i2c_board_info i2c_devs0[] __initdata = {
{ I2C_BOARD_INFO(“ov965x”, 0x30), }, // gjl
};
//这个定义会被smdk6410_machine_init调用,添加到I2C的链表中
在arch/arm/mach-s3c64xx/mach-smdk6410.c中
static void __init smdk6410_machine_init(void)
{
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
}
2. ov9650驱动初始化
ov9650的驱动在drivers/media/video/samsung/fimc/ov965x.c中
module_init(ov965x_init)
–> ov965x_init >> i2c_add_driver
static __init int ov965x_init(void)
{
return i2c_add_driver(&ov965x_i2c_driver);
}
这个i2c_add_driver也是调用了driver_register,最终会调用ov965x的probe函数
在include/linux/i2c.h中
static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, driver);
}
//driver_register中会调用具体函数的probe
在drivers/i2c/i2c-core.c中
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
res = driver_register(&driver->driver);
if (res)
return res;
}
3. ov9650的probe函数
module_init(ov965x_init)
–> ov965x_init >> i2c_add_driver
–> ov965x_probe
drivers/media/video/samsung/fimc/ov965x.c
static int ov965x_probe(struct i2c_client *c, const struct i2c_device_id *id)
{
int i;
int ret;
ov965x_data.client = c;
s3c_fimc_register_camera(&ov965x_data); //3.1向s3c-fimc注册camera
for (i = 0; i < OV965X_INIT_REGS; i++) { //通过I2C向ov965x中的寄存器写入配置信息
ret = i2c_smbus_write_byte_data(c, OV965X_init_reg[i].subaddr, OV965X_init_reg[i].value);
} //在.config 中定义了CONFIG_OV965X_QVGA=y,所以这儿是QVGA(320*240)模式
return 0;
}

3.1 向s3c_fimc_driver注册camera
先看一下s3c_fimc_camera是个什么东东
static struct s3c_fimc_camera ov965x_data = {
.id = CONFIG_VIDEO_FIMC_CAM_CH,
.type = CAM_TYPE_ITU,
.mode = ITU_601_YCBCR422_8BIT,
.order422 = CAM_ORDER422_8BIT_YCBYCR,//YCRYCB,
.clockrate = 24000000,
.width = 640,//800,
.height = 480,//600,
.offset = {
.h1 = 0,
.h2 = 0,
.v1 = 0,
.v2 = 0,
},
.polarity = {
.pclk = 0,
.vsync = 1,
.href = 0,
.hsync = 0,
},
.initialized = 0,
};

module_init(ov965x_init)
–> ov965x_init >> i2c_add_driver
–> ov965x_probe
–> s3c_fimc_register_camera >>>s3c_fimc_register_camera(&ov965x_data);
在drivers/media/video/samsung/fimc/s3c_fimc_core.c中
void s3c_fimc_register_camera(struct s3c_fimc_camera *cam)
{
int i;
static int once=0;
//通过索引全局数组s3c_fimc_camer就可以找到ov9650的控制指针
s3c_fimc.camera[cam->id] = cam;

for (i = 0; i < S3C_FIMC_MAX_CTRLS; i++) {    s3c_fimc.ctrl[i].in_cam = s3c_fimc.camera[cam->id];}//设置摄像头的clock: disable, set, enableclk_disable(s3c_fimc.cam_clock);clk_set_rate(s3c_fimc.cam_clock, cam->clockrate);clk_enable(s3c_fimc.cam_clock);s3c_fimc_reset_camera();                     //3.1.1复位后,设置的clock就起作用了if((once==0)&&(cam->client!=0)){    for (i = 0; i < S3C_FIMC_MAX_CTRLS; i++)         s3c_fimc_init_camera(&s3c_fimc.ctrl[i]);  //3.1.2复位后,设置的clock就起作用了           once = 1;}

}
3.1.1 复位camera
module_init(ov965x_init)
–> ov965x_init >> i2c_add_driver
–> ov965x_probe
–> s3c_fimc_register_camera >>>s3c_fimc_register_camera(&ov965x_data);
–> s3c_fimc_reset_camera
void s3c_fimc_reset_camera(void)
{
//对寄存器一个ioremap然后直接使用
void __iomem *regs = ioremap(S3C64XX_PA_FIMC, SZ_4K);
u32 cfg = readl(regs + S3C_CIGCTRL);
cfg |= S3C_CIGCTRL_CAMRST;
writel(cfg, regs + S3C_CIGCTRL);
mdelay(200);

cfg = readl(regs + S3C_CIGCTRL);cfg &= ~S3C_CIGCTRL_CAMRST;writel(cfg, regs + S3C_CIGCTRL);udelay(2000);//使用完后iounmapiounmap(regs);

}

3.1.2 将初始化标志设为1
module_init(ov965x_init)
–> ov965x_init >> i2c_add_driver
–> ov965x_probe
–> s3c_fimc_register_camera >>>s3c_fimc_register_camera(&ov965x_data);
–> s3c_fimc_init_camera
void s3c_fimc_init_camera(struct s3c_fimc_control *ctrl)
{
struct s3c_fimc_camera *cam = ctrl->in_cam;
if (cam && cam->id != S3C_FIMC_TPID && !cam->initialized) {
cam->initialized = 1;
}
}
4. 总结
初始化完成了,总结一下
ov9650 –> s3c_fimc_core注册
s3c_fimc_core –> v4l2注册
这样上层就可以调用v4l2–>s3c_fimc_core来间接的控制ov9650了.
附录一. camera 的clk设置
1. 获取clk的结构体
在drivers/media/video/samsung/fimc/s3c_fimc_core.c中
s3c_fimc_probe
–> s3c_fimc_init_global
static int s3c_fimc_init_global(struct platform_device *pdev)
{
s3c_fimc.cam_clock = clk_get(&pdev->dev, “sclk_cam”);
}
其中sclk_cam是定义在arch/arm/mach-s3c64xx/clock.c中
static struct clk init_clocks[] =
{
.name = “sclk_cam”,
.id = -1,
.parent = &clk_h2,
.enable = s3c64xx_sclk_ctrl,
.ctrlbit = S3C_CLKCON_SCLK_CAM,
.ops = &(struct clk_ops) {
.set_rate = s3c64xx_setrate_sclk_cam,
}
}
2.clk的初始化
module_init(ov965x_init)
–> ov965x_init >> i2c_add_driver
–> ov965x_probe
–> s3c_fimc_register_camera
在drivers/media/video/samsung/fimc/s3c_fimc_core.c中
void s3c_fimc_register_camera(struct s3c_fimc_camera *cam)
{
//设置摄像头的clock频率为24M
clk_disable(s3c_fimc.cam_clock);
//cam->clockrate是在ov965x_data中设定的数值
clk_set_rate(s3c_fimc.cam_clock, cam->clockrate); //cam->clockrate=24000000=24M
clk_enable(s3c_fimc.cam_clock);
}
2.1 clk_disable
module_init(ov965x_init)
–> ov965x_init >> i2c_add_driver
–> ov965x_probe
–> s3c_fimc_register_camera
–> clock_disable
在arch/arm/plat-samsung/clock.c中
void clk_disable(struct clk *clk)
{
spin_lock(&clocks_lock);
(clk->enable)(clk, 0); //这个enabel也就是调用”sclk_cam”的enable
spin_unlock(&clocks_lock);
clk_disable(clk->parent);
}
module_init(ov965x_init)
–> ov965x_init >> i2c_add_driver
–> ov965x_probe
–> s3c_fimc_register_camera
–> clock_disable
int s3c64xx_sclk_ctrl(struct clk *clk, int enable)
{
//enable=0或1控制打开或关闭
return s3c64xx_gate(S3C_SCLK_GATE, clk, enable);
}
2.2 clk_set_rate
module_init(ov965x_init)
–> ov965x_init >> i2c_add_driver
–> ov965x_probe
–> s3c_fimc_register_camera
–>clk_set_rate
在arch/arm/plat-samsung/clock.c中
int clk_set_rate(struct clk *clk, unsigned long rate)
{
spin_lock(&clocks_lock);
//这个clk_set_rate也就是调用”sclk_cam”的clk_set_rate
ret = (clk->ops->set_rate)(clk, rate);
spin_unlock(&clocks_lock);
}
2.2.1设置camra的clk是24M
module_init(ov965x_init)
–> ov965x_init >> i2c_add_driver
–> ov965x_probe
–> s3c_fimc_register_camera
–>clk_set_rate
–> s3c64xx_setrate_sclk_cam
在arch/arm/mach-s3c64xx/clock.c中
static int s3c64xx_setrate_sclk_cam(struct clk *clk, unsigned long rate)
{
u32 shift = 20;
u32 cam_div, cfg;
unsigned long src_clk = clk_get_rate(clk->parent); //parent=266M

cam_div = src_clk / rate;   //cam_div=266M/24M=11// 手册P147 CLK_DIV0  0x7E00_F020 // CAM_RATIO [23:20] --> CAM clock divider ratio // CLKCAM = HCLKX2 / (CAM_RATIO + 1)   cfg = __raw_readl(S3C_CLK_DIV0);cfg &= ~(0xf << shift);cfg |= ((cam_div - 1) << shift);__raw_writel(cfg, S3C_CLK_DIV0);return 0;

}

0 0
原创粉丝点击