主要分析来自下面一篇文章。
http://www.chinaaet.com/article/3000015768
/*
由mach-smdk6410.c文件可知,内核启动时将所有platform_device
包括 s3c_device_fimc0 s3c_device_fimc1
挂载到platform总线。
*/
/*
dev-fimc0.c
*/
struct platform_device s3c_device_fimc0 = {
.name = "s3c-fimc",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_fimc0_resource),
.resource = s3c_fimc0_resource,
};
/*
dev-fimc1.c
*/
struct platform_device s3c_device_fimc1 = {
.name = "s3c-fimc",
.id = 1,
.num_resources = ARRAY_SIZE(s3c_fimc1_resource),
.resource = s3c_fimc1_resource,
};
/*
mach-smdk6410.c
*/
static struct platform_device *smdk6410_devices[] __initdata =
{
&s3c_device_hsmmc0,
&s3c_device_hsmmc1,
&s3c_device_i2c0,
&s3c_device_fb,
&s3c_device_ohci,
&s3c_device_usb_hsotg,
&samsung_asoc_dma,
&samsung_device_keypad,
&gpio_button_device,
&s3c_device_nand,
#ifdef CONFIG_DM9000
&s3c_device_dm9000,
#endif
&s3c64xx_device_ac97,
&s3c_device_rtc,
&s3c_device_ts,
&s3c_device_fimc0,//FIMC 设备
&s3c_device_fimc1,
&s3c_device_wdt,
&s3c_device_vpp,
&s3c_device_mfc,
&s3c_device_tvenc,
&s3c_device_tvscaler,
&s3c_device_rotator,
&s3c_device_jpeg,
&s3c_device_g2d,
&s3c_device_g3d,
&s3c64xx_device_spi0,
&s3c64xx_device_spi1,
};
platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));
/*
platform总线的match函数将device和driver匹配之后,
会自动调用s3c_fimc_driver中指定的probe函数探测设备、
申请内存资源、申请中断等,
并将最终形成的platform_device类型数据保存到内核中,
供后续使用。
*/
/*
s3c_fimc_core.c
*/
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);
if (!ctrl) {
err("cannot register fimc controller\n");
goto err_fimc;
}
pdata = to_fimc_plat(&pdev->dev);
if (pdata->cfg_gpio)
pdata->cfg_gpio(pdev);
/* fimc source clock */
srclk = clk_get(&pdev->dev, pdata->srclk_name);
if (IS_ERR(srclk)) {
err("failed to get source clock of fimc\n");
goto err_clk_io;
}
/* fimc clock */
ctrl->clock = clk_get(&pdev->dev, pdata->clk_name);
if (IS_ERR(ctrl->clock)) {
err("failed to get fimc clock source\n");
goto err_clk_io;
}
/* set parent clock */
clk_enable(ctrl->clock);
/* things to initialize once */
if (ctrl->id == 0) {
ret = s3c_fimc_init_global(pdev);
if (ret)
goto err_global;
}
/*
注册 video4linux devices
*/
ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);
if (ret) {
err("cannot register video driver\n");
goto err_video;
}
info("controller %d registered successfully\n", ctrl->id);
return 0;
err_video:
clk_put(s3c_fimc.cam_clock);
err_global:
clk_disable(ctrl->clock);
clk_put(ctrl->clock);
err_clk_io:
s3c_fimc_unregister_controller(pdev);
err_fimc:
return -EINVAL;
}
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,
},
};
/*
video_device结构体包括fops、ioctl_ops、release、name、vf_type几个成员变量,
其中,最重要的是file_operations类型的[fops] 和 v4l2_ioctl_ops类型的[ioctl_ops],
分别实现文件操作接口和V4L2接口。
*/
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",
},
[1] = {
.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_video1",
},
[2] = {
.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_video2",
},
};
struct video_device
{
/* device ops */
const struct v4l2_file_operations *fops;
/* sysfs */
struct device dev; /* v4l device */
struct cdev *cdev; /* character device */
/* Set either parent or v4l2_dev if your driver uses v4l2_device */
struct device *parent; /* device parent */
struct v4l2_device *v4l2_dev; /* v4l2_device parent */
/* Control handler associated with this device node. May be NULL. */
struct v4l2_ctrl_handler *ctrl_handler;
/* device info */
char name[32];
int vfl_type;
/* 'minor' is set to -1 if the registration failed */
int minor;
u16 num;
/* added for TV */
int type2;
int users;
/* use bitops to set/clear/test flags */
unsigned long flags;
/* attribute to differentiate multiple indices on one physical device */
int index;
/* V4L2 file handles */
spinlock_t fh_lock; /* Lock for all v4l2_fhs */
struct list_head fh_list; /* List of struct v4l2_fh */
int debug; /* Activates debug level*/
/* Video standard vars */
v4l2_std_id tvnorms; /* Supported tv norms */
v4l2_std_id current_norm; /* Current tvnorm */
/* callbacks */
void (*release)(struct video_device *vdev);
/* ioctl callbacks */
const struct v4l2_ioctl_ops *ioctl_ops;
};
/*
s3c_fimc_core.c
FIMC驱动遵循V4L2接口标准,其file_operations接口定义如下:
当应用程序通过系统调用open()打开摄像头设备时,内核会最终找到s3c_fimc_open()
函数来打开摄像头。
应用程序通过read()获取图像数据,该函数通过copy_to_user()
将内核空间所申请缓冲区中图像数据拷贝到用户空间中开辟的图像数据区。
该函数没有充分利用FIMC接口提供的ping-pong缓冲区,按字进行拷贝(memcpy),
十分耗时。
mmap函数将内核空间中申请到的图像缓冲区映射到应用程序所在的用户空间,
这样,应用程序申请到的buffer将指向内核空间的图像缓冲区,应用程序可以
(不拷贝)直接对图像进行操作。该函数配合V4L2标准中的环形缓冲区队列,
节省了应用程序读取图像数据所消耗的时间。
*/
static const struct v4l2_file_operations s3c_fimc_fops = {
//static const struct file_operations s3c_fimc_fops = {
.owner = THIS_MODULE,
.open = s3c_fimc_open,
.release = s3c_fimc_release,
.unlocked_ioctl = video_ioctl2,
.read = s3c_fimc_read,
.write = s3c_fimc_write,
.mmap = s3c_fimc_mmap,
.poll = s3c_fimc_poll,
};
/*
s3c_fimc_v4l2.c
*/
/*************************
应用程序通过ioctl接口使用这些函数,比如ioctl(fd,VIDIOC_S_FMT,&fmt)
用来设置图像格式,此时V4L2会将VIDIOC_S_FMT命令映射为s3c_fimc_v4l2_s_fmt_vid_cap()函数,
并将fmt指定的格式告知FIMC接口,FIMC会将OV9650传递过来的原始图像数据经过类型转换传递回应用程序。
s3c_fimc_v4l2_reqbufs()用于申请图像缓冲区,
该函数为应用程序在内核空间开辟ping-pong缓冲区。s3c_fimc_v4l2_qbuf()
函数将缓冲区组成环形缓冲队列,当应用程序需要调用图像数据时,使用s3c_fimc_v4l2_dqbuf()
使指定的缓冲区出队,缓冲区在出队期间,不会被新来的图像数据覆盖,
新到的图像数据会被传送到环形队列中指定缓冲区的下一个缓冲区。
由于FIMC控制器为P通道和C通道分别开辟了4个缓冲区,在内核初始化时已经申请到,
因此FIMC驱动中并不需要再重新申请。
*************************/
const struct v4l2_ioctl_ops s3c_fimc_v4l2_ops = {
.vidioc_querycap = s3c_fimc_v4l2_querycap,
.vidioc_g_fbuf = s3c_fimc_v4l2_g_fbuf,
.vidioc_s_fbuf = s3c_fimc_v4l2_s_fbuf,
.vidioc_enum_fmt_vid_cap = s3c_fimc_v4l2_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = s3c_fimc_v4l2_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = s3c_fimc_v4l2_s_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = s3c_fimc_v4l2_try_fmt_vid_cap,
.vidioc_try_fmt_vid_overlay = s3c_fimc_v4l2_try_fmt_overlay,
.vidioc_overlay = s3c_fimc_v4l2_overlay,
.vidioc_g_ctrl = s3c_fimc_v4l2_g_ctrl,
.vidioc_s_ctrl = s3c_fimc_v4l2_s_ctrl,
.vidioc_streamon = s3c_fimc_v4l2_streamon,
.vidioc_streamoff = s3c_fimc_v4l2_streamoff,
.vidioc_g_input = s3c_fimc_v4l2_g_input,
.vidioc_s_input = s3c_fimc_v4l2_s_input,
.vidioc_g_output = s3c_fimc_v4l2_g_output,
.vidioc_s_output = s3c_fimc_v4l2_s_output,
.vidioc_enum_input = s3c_fimc_v4l2_enum_input,
.vidioc_enum_output = s3c_fimc_v4l2_enum_output,
.vidioc_reqbufs = s3c_fimc_v4l2_reqbufs,
.vidioc_querybuf = s3c_fimc_v4l2_querybuf,
.vidioc_qbuf = s3c_fimc_v4l2_qbuf,
.vidioc_dqbuf = s3c_fimc_v4l2_dqbuf,
.vidioc_cropcap = s3c_fimc_v4l2_cropcap,
.vidioc_g_crop = s3c_fimc_v4l2_g_crop,
.vidioc_s_crop = s3c_fimc_v4l2_s_crop,
.vidioc_s_parm = s3c_fimc_v4l2_s_parm,
};
在内核中加入打印。追踪ok6410 官方camera 测试程序运行过程。
--s3c_fimc_probe
---[CAM]s3c_fimc_register_controller.id=0
--[CAM]s3c_fimc_set_active_camera,id=0
s3c_fimc_init_global s3c_fimc.cam_clock=-1065304896
--video_register_device
device: 'video0': device_add
s3c-fimc: controller 0 registered successfully
driver: 's3c-fimc.0': driver_bound: bound to device 's3c-fimc'
bus: 'platform': really_probe: bound device s3c-fimc.0 to driver s3c-fimc
bus: 'platform': driver_probe_device: matched device s3c-fimc.1 with driver s3c-fimc
bus: 'platform': really_probe: probing driver s3c-fimc with device s3c-fimc.1
--s3c_fimc_probe
---[CAM]s3c_fimc_register_controller.id=1
--[CAM]s3c_fimc_set_active_camera,id=0
--video_register_device
device: 'video1': device_add
s3c-fimc: controller 1 registered successfully
driver: 's3c-fimc.1': driver_bound: bound to device 's3c-fimc'
bus: 'platform': really_probe: bound device s3c-fimc.1 to driver s3c-fimc
s3c-fimc:
--info ov965x_init
bus: 'i2c': add driver ov965x
bus: 'i2c': driver_probe_device: matched device 0-0030 with driver ov965x
bus: 'i2c': really_probe: probing driver ov965x with device 0-0030
s3c-fimc:
--ov965x_probe
[CAM]s3c_fimc_register_camera,cam->id=0
s3c_fimc.cam_clock = c080bcc0
parent clock for camera: 266.000 MHz, divisor: 11
[CAM]RESET CAM.[CAM]Reset and init reg!1cam->client=cc460c00
[CAM]Reset and init reg!1
[CAM]Reset and init reg!2
--[CAM]s3c_fimc_init_camera
[CAM]I2C_CAM_INIT.
--[CAM]s3c_fimc_init_camera
--[CAM]s3c_fimc_init_camera
[CAM]Reset and init reg!3
s3c-fimc: //OV9650 寄存器设置
--reg 12 80
--reg ff 0a
--reg 6a 3e
--reg 3b 09
--reg 13 8f
--reg 01 80
--reg 02 80
--reg 00 00
--reg 10 00
--reg 35 91
--reg 0e a0
--reg 1e 34
--reg a8 80
--reg 04 00
--reg 0c 04
--reg 0d 80
--reg 11 81
--reg 12 40
--reg 37 91
--reg 38 12
--reg 39 43
--reg 18 c6
--reg 17 26
--reg 32 ad
--reg 03 00
--reg 1a 3d
--reg 19 01
--reg 3f a6
--reg 14 2e
--reg 15 10
--reg 41 02
--reg 42 08
--reg 1b 00
--reg 16 06
--reg 33 e2
--reg 34 bf
--reg 96 04
-reg 3a 00
--reg 8e 00
--reg 3c 77
--reg 8b 06
--reg 94 88
--reg 95 88
--reg 40 c1
--reg 29 3f
--reg 0f 42
--reg 3d 92
--reg 69 40
--reg 5c b9
--reg 5d 96
--reg 5e 10
--reg 59 c0
--reg 5a af
--reg 5b 55
-reg 43 f0
--reg 44 10
--reg 45 68
--reg 46 96
--reg 47 60
--reg 48 80
--reg 5f e0
--reg 60 8c
--reg 61 20
--reg a5 d9
--reg a4 74
--reg 8d 02
--reg 4f 3a
--reg 50 3d
--reg 51 03
--reg 52 12
--reg 53 26
--reg 54 36
--reg 55 45
--reg 56 40
--reg 57 40
--reg 58 0d
--reg 8c 23
--reg 3e 02
--reg a9 b8
--reg aa 92
--reg ab 0a
--reg 8f df
--reg 90 00
--reg 91 00
--reg 9f 00
--reg a0 00
--reg 3a 01
--reg 24 70
--reg 25 64
--reg 26 c3
--reg 2a 00
--reg 2b 00
--reg 6c 40
--reg 6d 30
--reg 6e 4b
--reg 6f 60
--reg 70 70
--reg 71 70
--reg 72 70
--reg 73 70
--reg 74 60
--reg 75 60
--reg 76 50
--reg 77 48
--reg 78 3a
--reg 79 2e
--reg 7a 28
--reg 7b 22
--reg 7c 04
--reg 7d 07
-reg 7e 10
--reg 7f 28
-reg 80 36
--reg 81 44
-reg 82 52
--reg 83 60
--reg 84 6c
--reg 85 78
--reg 86 8c
--reg 87 9e
--reg 88 bb
--reg 89 d2
--reg 8a e6
driver: '0-0030': driver_bound: bound to device 'ov965x'
bus: 'i2c': really_probe: bound device 0-0030 to driver ov965x
Please press Enter to activate this console. device: 'vcs2': device_add
device: 'vcsa2': device_add
device: 'vcs4': device_add
device: 'vcsa4': device_add
device: 'vcs3': device_add
device: 'vcsa3': device_add
touch...
[root@FORLINX6410]#
[root@FORLINX6410]# ls
adctest keytest sbin
at24c08test ledtest sdcard
bin lib spitest
camera linuxrc sys
dev mnt tmp
duhuitest mpu6050 uarttest
etc nfs.txt udisk
fork opt udp
gpio ov9650 usr
iictest proc var
key_workqueue_test root w25q128
[root@FORLINX6410]# cd camera
[root@FORLINX6410]# ls
422jpeg.h Makefile testcamera v4l2.c videodev.h videodev2.h
[root@FORLINX6410]# ./testcamera
//运行testcamera,开始显示图片
Start Main ---v4l2_open id=0
--v4l2 get
--[CAM]s3c_fimc_open.id=0
--[CAM]ctrl->in_cam->width=640
s3c_fimc_v4l2_s_fmt_vid_cap filp=cbcd4f00,fh=c08bbed0,f=cbccf000
s3c_fimc_alloc_output_memory width 140 height f0 format 0 buf_size = 26000
s3c_fimc_v4l2_streamon is called
--[CAM]s3c_fimc_init_camera
--v4l2 read
---s3c_fimc_read
already open the devicise codec
Before openning FB
VIDIOC_S_FMT is c0cc5605
VIDIOC_STREAMON is 40045612
start to get pic 0
display 153600
--v4l2 read
---s3c_fimc_read
display 153600
--v4l2 read
---s3c_fimc_read
display 153600