原文地址:http://blog.chinaunix.net/uid-26009923-id-4050452.html
一. 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_gpio
-
-
srclk = clk_get(&pdev->dev, pdata->srclk_name); //获取srclk_name
-
ctrl->clock = clk_get(&pdev->dev, pdata->clk_name); //获取fimc_clk并enable
-
clk_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, enable
-
clk_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);
-
//使用完后iounmap
-
iounmap(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;
-
}
阅读(1044) | 评论(0) | 转发(0) |