linux 摄像头驱动 :
核心数据结构:
/**
* struct fimc_dev - abstraction for FIMC entity
* @slock: the spinlock protecting this data structure
* @lock: the mutex protecting this data structure
* @pdev: pointer to the FIMC platform device
* @pdata: pointer to the device platform data
* @variant: the IP variant information
* @id: FIMC device index (0..FIMC_MAX_DEVS)
* @num_clocks: the number of clocks managed by this device instance
* @clock: clocks required for FIMC operation
* @regs: the mapped hardware registers
* @regs_res: the resource claimed for IO registers
* @irq: FIMC interrupt number
* @irq_queue: interrupt handler waitqueue
* @m2m: memory-to-memory V4L2 device information
* @vid_cap: camera capture device information
* @state: flags used to synchronize m2m and capture mode operation
* @alloc_ctx: videobuf2 memory allocator context
*/
struct fimc_dev {
spinlock_t slock;
struct mutex lock;
struct platform_device *pdev;
struct s5p_platform_fimc *pdata;
struct samsung_fimc_variant *variant;
u16 id;
u16 num_clocks;
struct clk *clock[MAX_FIMC_CLOCKS];
void __iomem *regs;
struct resource *regs_res;
int irq;
wait_queue_head_t irq_queue;
struct fimc_m2m_device m2m;
struct fimc_vid_cap vid_cap;
unsigned long state;
struct vb2_alloc_ctx *alloc_ctx;
};
/**
* fimc_ctx - the device context data
* @slock: spinlock protecting this data structure
* @s_frame: source frame properties
* @d_frame: destination frame properties
* @out_order_1p: output 1-plane YCBCR order
* @out_order_2p: output 2-plane YCBCR order
* @in_order_1p input 1-plane YCBCR order
* @in_order_2p: input 2-plane YCBCR order
* @in_path: input mode (DMA or camera)
* @out_path: output mode (DMA or FIFO)
* @scaler: image scaler properties
* @effect: image effect
* @rotation: image clockwise rotation in degrees
* @flip: image flip mode
* @flags: additional flags for image conversion
* @state: flags to keep track of user configuration
* @fimc_dev: the FIMC device this context applies to
* @m2m_ctx: memory-to-memory device context
*/
struct fimc_ctx {
spinlock_t slock;
struct fimc_frame s_frame;
struct fimc_frame d_frame;
u32 out_order_1p;
u32 out_order_2p;
u32 in_order_1p;
u32 in_order_2p;
enum fimc_datapath in_path;
enum fimc_datapath out_path;
struct fimc_scaler scaler;
struct fimc_effect effect;
int rotation;
u32 flip;
u32 flags;
u32 state;
struct fimc_dev *fimc_dev;
struct v4l2_m2m_ctx *m2m_ctx;
};
/* fimc controller abstration */
struct fimc_control {
int id; /* controller id */
char name[16];
atomic_t in_use;
void __iomem *regs; /* register i/o */
struct clk *clk; /* interface clock */
struct regulator *regulator; /* pd regulator */
struct fimc_meminfo mem; /* for reserved mem */
/* kernel helpers */
struct mutex lock; /* controller lock */
struct mutex alloc_lock;
struct mutex v4l2_lock;
wait_queue_head_t wq;
struct device *dev;
int irq;
/* v4l2 related */
struct video_device *vd;
struct v4l2_device v4l2_dev;
/* fimc specific */
struct fimc_limit *limit; /* H/W limitation */
struct s3c_platform_camera *cam; /* activated camera */
struct fimc_capinfo *cap; /* capture dev info */
struct fimc_outinfo *out; /* output dev info */
struct fimc_fbinfo fb; /* fimd info */
struct fimc_scaler sc; /* scaler info */
struct fimc_effect fe; /* fimc effect info */
enum fimc_status status;
enum fimc_log log;
u32 ctx_busy[FIMC_MAX_CTXS];
};
/* global */
struct fimc_global {
struct fimc_control ctrl[FIMC_DEVICES];
struct s3c_platform_camera camera[FIMC_MAXCAMS];
int camera_isvalid[FIMC_MAXCAMS];
int active_camera;
int initialized;
};
设备对象结构:在platform 平台 probe时使用,传递给设备驱动
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
const struct platform_device_id *id_entry;
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
设备信息:
static struct i2c_board_info ov9650_i2c_info =
{
I2C_BOARD_INFO("OV9650", 0x60>>1),
.platform_data = &ov9650_plat,
};
static struct s3c_platform_camera ov9650 = {
.id = CAMERA_PAR_A,
.type = CAM_TYPE_ITU,
.fmt = ITU_601_YCBCR422_8BIT,
.order422 = CAM_ORDER422_8BIT_CBYCRY,
.i2c_busnum = IIC_NUM_CAM_USED,
.info = &ov9650_i2c_info,
.pixelformat = V4L2_PIX_FMT_VYUY,
.srclk_name = "mout_mpll",
.clk_name = "sclk_cam0",
.clk_rate = 24000000,
// .line_length = 640,
.line_length = 1920,
.width = 640,
.height = 480,
.window = {
.left = 0,
.top = 0,
.width = 640,
.height= 480,
},
/* Polarity */
.inv_pclk = 0,
.inv_vsync = 0,
.inv_href = 0,
.inv_hsync = 0,
.initialized = 0,
// .cam_power = smdkv210_OV9650_power,
.cam_power = tqcam_OV9650_power,
};
static struct s3c_platform_fimc fimc_plat_lsi = {
.srclk_name = "mout_mpll",
.clk_name = "sclk_fimc",
.lclk_name = "fimc",
.clk_rate = 166750000,
#if defined(CONFIG_VIDEO_S5K4EA)
.default_cam = CAMERA_CSI_C,
#else
#ifdef CAM_ITU_CH_A
.default_cam = CAMERA_PAR_A,
#else
.default_cam = CAMERA_PAR_B,
#endif
#endif
.camera = {
&ov9650,
},
.hw_ver = 0x43,
};
static struct s5p_media_device tq210_media_devs[] = {
[0] = {
.id = S5P_MDEV_MFC,
.name = "mfc",
.bank = 0,
.memsize = S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC0,
.paddr = 0,
},
[1] = {
.id = S5P_MDEV_MFC,
.name = "mfc",
.bank = 1,
.memsize = S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC1,
.paddr = 0,
},
[2] = {
.id = S5P_MDEV_FIMC0,
.name = "fimc0",
.bank = 1,
.memsize = S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC0,
.paddr = 0,
},
.............................
media_devs指向tq210_media_devs数组;
void __init s3c_fimc0_set_platdata(struct s3c_platform_fimc *pd)
{
struct s3c_platform_fimc *npd;
if (!pd)
pd = &default_fimc0_data;
npd = kmemdup(pd, sizeof(struct s3c_platform_fimc), GFP_KERNEL);
if (!npd)
printk(KERN_ERR "%s: no memory for platform data\n", __func__);
else {
if (!npd->cfg_gpio)
npd->cfg_gpio = s3c_fimc0_cfg_gpio;
if (!npd->clk_on)
npd->clk_on = s3c_fimc_clk_on;
if (!npd->clk_off)
npd->clk_off = s3c_fimc_clk_off;
npd->hw_ver = 0x45;
/* starting physical address of memory region */
npd->pmem_start = s5p_get_media_memory_bank(S5P_MDEV_FIMC0, 1);
/* size of memory region */
npd->pmem_size = s5p_get_media_memsize_bank(S5P_MDEV_FIMC0, 1);
s3c_device_fimc0.dev.platform_data = npd;
}
struct platform_device s3c_device_fimc0 = {
.name = "s3c-fimc",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_fimc0_resource),
.resource = s3c_fimc0_resource,
};
设备驱动注册:入口处;
static struct platform_driver fimc_driver = {
.probe = fimc_probe,
.remove = fimc_remove,
.suspend = fimc_suspend,
.resume = fimc_resume,
.driver = {
.name = FIMC_NAME,
.owner = THIS_MODULE,
},
};
static int fimc_register(void)
{
platform_driver_register(&fimc_driver);
return 0;
}
static void fimc_unregister(void)
{
platform_driver_unregister(&fimc_driver);
}
late_initcall(fimc_register);
在注册fimc_driver驱动后;根据bus device driver 在platform平台上匹配;会调用
fimc_driver->fimc_probe();函数;注册绑定设备对象;
static int __devinit fimc_probe(struct platform_device *pdev)
{
struct s3c_platform_fimc *pdata;
struct fimc_control *ctrl;
struct clk *srclk;
int ret;
if (!fimc_dev) { struct *fimc_global
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 (pdata->cfg_gpio)
pdata->cfg_gpio(pdev);
/* fimc source clock */
srclk = clk_get(&pdev->dev, pdata->srclk_name);
/* fimc clock */
ctrl->clk = clk_get(&pdev->dev, pdata->clk_name);
/* set parent for mclk */
clk_set_parent(ctrl->clk, srclk);
/* set rate for mclk */
clk_set_rate(ctrl->clk, pdata->clk_rate);
/* 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);
ret = device_create_file(&(pdev->dev), &dev_attr_log_level);
..............
return -EINVAL;
对于设备信息的传递:
根据platform_device的id
ctrl->vd = &fimc_video_device[id];
指向:
struct video_device fimc_video_device[FIMC_DEVICES] = {
[0] = {
.fops = &fimc_fops,
.ioctl_ops = &fimc_v4l2_ops,
.release = fimc_vdev_release,
},
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, mdev_id;
id = pdev->id;
mdev_id = S5P_MDEV_FIMC0 + 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;
/* alloc from bank1 as default */
ctrl->mem.base = pdata->pmem_start;
ctrl->mem.size = pdata->pmem_size;
ctrl->mem.curr = ctrl->mem.base;
ctrl->status = FIMC_STREAMOFF;
switch (pdata->hw_ver) {
case 0x40:
ctrl->limit = &fimc40_limits[id];
break;
..........................
}
ctrl->log = FIMC_LOG_DEFAULT;
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->alloc_lock);
mutex_init(&ctrl->v4l2_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 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 在将I/O内存资源的物理地址映射成核心虚地址后 */
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__);
fimc_hwset_reset(ctrl);
return ctrl;
v4l2_device和device关联起来
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
{
if (v4l2_dev == NULL)
return -EINVAL;
INIT_LIST_HEAD(&v4l2_dev->subdevs);
spin_lock_init(&v4l2_dev->lock);
mutex_init(&v4l2_dev->ioctl_lock);
v4l2_prio_init(&v4l2_dev->prio);
kref_init(&v4l2_dev->ref);
v4l2_dev->dev = dev;
if (dev == NULL) {
/* If dev == NULL, then name must be filled in by the caller */
WARN_ON(!v4l2_dev->name[0]);
return 0;
}
/* Set name to driver name + device name if it is empty. */
if (!v4l2_dev->name[0])
snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
dev->driver->name, dev_name(dev));
if (!dev_get_drvdata(dev))
dev_set_drvdata(dev, v4l2_dev);
return 0;
获取platform_device的相关信息
static int fimc_init_global(struct platform_device *pdev)
{
struct s3c_platform_fimc *pdata;
struct s3c_platform_camera *cam;
int i;
pdata = to_fimc_plat(&pdev->dev);
/* Registering external camera modules. re-arrange order to be sure */
for (i = 0; i < FIMC_MAXCAMS; i++) {
cam = pdata->camera[i];
if (!cam)
break;
cam->srclk = clk_get(&pdev->dev, cam->srclk_name);
if (IS_ERR(cam->srclk)) {
dev_err(&pdev->dev, "%s: failed to get mclk source\n",
__func__);
return -EINVAL;
}
/* mclk */
cam->clk = clk_get(&pdev->dev, cam->clk_name);
if (IS_ERR(cam->clk)) {
dev_err(&pdev->dev, "%s: failed to get mclk source\n",
__func__);
clk_put(cam->srclk);
return -EINVAL;
}
clk_put(cam->clk);
clk_put(cam->srclk);
/* Assign camera device to fimc */
memcpy(&fimc_dev->camera[i], cam, sizeof(*cam));
fimc_dev->camera_isvalid[i] = 1;
fimc_dev->camera[i].initialized = 0;
}
fimc_dev->active_camera = -1;
fimc_dev->initialized = 1;
return 0;
对于:video_register_device
主要是设置 其cdev的fops
vdev->cdev->ops = &v4l2_fops;
并注册
cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);;
设置设备号
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
注册device到总线中去;
ret = device_register(&vdev->dev);
vdev->dev.release = v4l2_device_release;
注册entry:
ret = media_device_register_entity(vdev->v4l2_dev->mdev,。。。
io口操作时要用:
video_device[vdev->minor] = vdev;
/**
* __video_register_device - register video4linux devices
* @vdev: video device structure we want to register
* @type: type of device to register
* @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ...
* -1 == first free)
* @warn_if_nr_in_use: warn if the desired device node number
* was already in use and another number was chosen instead.
* @owner: module that owns the video device node
*
* The registration code assigns minor numbers and device node numbers
* based on the requested type and registers the new device node with
* the kernel.
*
* This function assumes that struct video_device was zeroed when it
* was allocated and does not contain any stale date.
*
* An error is returned if no free minor or device node number could be
* found, or if the registration of the device node failed.
*
* Zero is returned on success.
*
* Valid types are
*
* %VFL_TYPE_GRABBER - A frame grabber
*
* %VFL_TYPE_VBI - Vertical blank data (undecoded)
*
* %VFL_TYPE_RADIO - A radio card
*
* %VFL_TYPE_SUBDEV - A subdevice
*/
int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
{
int i = 0;
int ret;
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
/* A minor value of -1 marks this video device as never
having been registered */
vdev->minor = -1;
/* the release callback MUST be present */
WARN_ON(!vdev->release);
if (!vdev->release)
return -EINVAL;
/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock);
INIT_LIST_HEAD(&vdev->fh_list);
/* Part 1: check device type */
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video";
break;
case VFL_TYPE_VBI:
name_base = "vbi";
break;
case VFL_TYPE_RADIO:
name_base = "radio";
break;
case VFL_TYPE_SUBDEV:
name_base = "v4l-subdev";
break;
default:
printk(KERN_ERR "%s called with unknown type: %d\n",
__func__, type);
return -EINVAL;
}
vdev->vfl_type = type;
vdev->cdev = NULL;
if (vdev->v4l2_dev) {
if (vdev->v4l2_dev->dev)
vdev->parent = vdev->v4l2_dev->dev;
if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
/* If the prio state pointer is NULL, then use the v4l2_device
prio state. */
if (vdev->prio == NULL)
vdev->prio = &vdev->v4l2_dev->prio;
}
/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* Keep the ranges for the first four types for historical
* reasons.
* Newer devices (not yet in place) should use the range
* of 128-191 and just pick the first free minor there
* (new style). */
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
break;
case VFL_TYPE_RADIO:
minor_offset = 64;
minor_cnt = 64;
break;
case VFL_TYPE_VBI:
minor_offset = 224;
minor_cnt = 32;
break;
default:
minor_offset = 128;
minor_cnt = 64;
break;
}
#endif
/* Pick a device node number */
mutex_lock(&videodev_lock);
nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
if (nr == minor_cnt)
nr = devnode_find(vdev, 0, minor_cnt);
if (nr == minor_cnt) {
printk(KERN_ERR "could not get a free device node number\n");
mutex_unlock(&videodev_lock);
return -ENFILE;
}
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* 1-on-1 mapping of device node number to minor number */
i = nr;
#else
/* The device node number and minor numbers are independent, so
we just find the first free minor number. */
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
if (video_device[i] == NULL)
break;
if (i == VIDEO_NUM_DEVICES) {
mutex_unlock(&videodev_lock);
printk(KERN_ERR "could not get a free minor\n");
return -ENFILE;
}
#endif
vdev->minor = i + minor_offset;
vdev->num = nr;
devnode_set(vdev);
/* Should not happen since we thought this minor was free */
WARN_ON(video_device[vdev->minor] != NULL);
vdev->index = get_index(vdev);
mutex_unlock(&videodev_lock);
/* Part 3: Initialize the character device */
vdev->cdev = cdev_alloc();
if (vdev->cdev == NULL) {
ret = -ENOMEM;
goto cleanup;
}
vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
printk(KERN_ERR "%s: cdev_add failed\n", __func__);
kfree(vdev->cdev);
vdev->cdev = NULL;
goto cleanup;
}
/* Part 4: register the device with sysfs */
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
if (vdev->parent)
vdev->dev.parent = vdev->parent;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
ret = device_register(&vdev->dev);
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
goto cleanup;
}
/* Register the release callback that will be called when the last
reference to the device goes away. */
vdev->dev.release = v4l2_device_release;
if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
name_base, nr, video_device_node_name(vdev));
/* Increase v4l2_device refcount */
if (vdev->v4l2_dev)
v4l2_device_get(vdev->v4l2_dev);
#if defined(CONFIG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */
if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
vdev->vfl_type != VFL_TYPE_SUBDEV) {
vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
vdev->entity.name = vdev->name;
vdev->entity.v4l.major = VIDEO_MAJOR;
vdev->entity.v4l.minor = vdev->minor;
ret = media_device_register_entity(vdev->v4l2_dev->mdev,
&vdev->entity);
if (ret < 0)
printk(KERN_WARNING
"%s: media_device_register_entity failed\n",
__func__);
}
#endif
/* Part 6: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
mutex_lock(&videodev_lock);
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
return 0;
cleanup:
mutex_lock(&videodev_lock);
if (vdev->cdev)
cdev_del(vdev->cdev);
devnode_clear(vdev);
mutex_unlock(&videodev_lock);
/* Mark this video device as never having been registered. */
vdev->minor = -1;
return ret;
}
EXPORT_SYMBOL(__video_register_device);
/**
* video_unregister_device - unregister a video4linux device
* @vdev: the device to unregister
*
* This unregisters the passed device. Future open calls will
* be met with errors.
*/
void video_unregister_device(struct video_device *vdev)
{
/* Check if vdev was ever registered at all */
if (!vdev || !video_is_registered(vdev))
return;
mutex_lock(&videodev_lock);
/* This must be in a critical section to prevent a race with v4l2_open.
* Once this bit has been cleared video_get may never be called again.
*/
clear_bit(V4L2_FL_REGISTERED, &vdev->flags);
mutex_unlock(&videodev_lock);
device_unregister(&vdev->dev);
}
EXPORT_SYMBOL(video_unregister_device);
/*
* Initialise video for linux
*/
static int __init videodev_init(void)
{
dev_t dev = MKDEV(VIDEO_MAJOR, 0);
int ret;
printk(KERN_INFO "Linux video capture interface: v2.00\n");
ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
if (ret < 0) {
printk(KERN_WARNING "videodev: unable to get major %d\n",
VIDEO_MAJOR);
return ret;
}
ret = class_register(&video_class);
if (ret < 0) {
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
printk(KERN_WARNING "video_dev: class_register failed\n");
return -EIO;
}
return 0;
}
static void __exit videodev_exit(void)
{
dev_t dev = MKDEV(VIDEO_MAJOR, 0);
class_unregister(&video_class);
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
}
module_init(videodev_init)
module_exit(videodev_exit)
MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>");
MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR);
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
设置drv_privedata;
video_set_drvdata(ctrl->vd, ctrl);
if(ctrl->vd->dev->p->driver_data ==null)
即ctrl->vd->dev->p->driver_data = ctrl;
对于v4l2_subdev的注册即iic设备的注册; 以及和v4l2设备的绑定如何??
static const struct i2c_device_id ov3640_id[] = {
{ OV3640_DRIVER_NAME, 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, ov3640_id);
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = OV3640_DRIVER_NAME,
.probe = ov3640_probe,
.remove = __devexit_p(ov3640_remove),
.id_table = ov3640_id,
};
/* Bus-based I2C implementation for kernels >= 2.6.26 */
static int __init v4l2_i2c_drv_init(void)
{
v4l2_i2c_driver.driver.name = v4l2_i2c_data.name;
v4l2_i2c_driver.command = v4l2_i2c_data.command;
v4l2_i2c_driver.probe = v4l2_i2c_data.probe;
v4l2_i2c_driver.remove = v4l2_i2c_data.remove;
v4l2_i2c_driver.suspend = v4l2_i2c_data.suspend;
v4l2_i2c_driver.resume = v4l2_i2c_data.resume;
v4l2_i2c_driver.id_table = v4l2_i2c_data.id_table;
return i2c_add_driver(&v4l2_i2c_driver);
}
static void __exit v4l2_i2c_drv_cleanup(void)
{
i2c_del_driver(&v4l2_i2c_driver);
}
module_init(v4l2_i2c_drv_init);
module_exit(v4l2_i2c_drv_cleanup);
关于iic的注册分析iic设备去分析iic设备驱动;
如果匹配到
static const struct v4l2_subdev_core_ops ov3640_core_ops = {
.init = ov3640_init, /* initializing API */
.s_config = ov3640_s_config, /* Fetch platform data */
.queryctrl = ov3640_queryctrl,
.querymenu = ov3640_querymenu,
.g_ctrl = ov3640_g_ctrl,
.s_ctrl = ov3640_s_ctrl,
};
static const struct v4l2_subdev_video_ops ov3640_video_ops = {
.g_fmt = ov3640_g_fmt,
.s_fmt = ov3640_s_fmt,
.enum_framesizes = ov3640_enum_framesizes,
.enum_frameintervals = ov3640_enum_frameintervals,
.enum_fmt = ov3640_enum_fmt,
.try_fmt = ov3640_try_fmt,
.g_parm = ov3640_g_parm,
.s_parm = ov3640_s_parm,
};
static const struct v4l2_subdev_ops ov3640_ops = {
.core = &ov3640_core_ops,
.video = &ov3640_video_ops,
};
static int ov3640_remove(struct i2c_client *client);
/*
* ov3640_probe
* Fetching platform data is being done with s_config subdev call.
* In probe routine, we just register subdev device
*/
static int ov3640_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ov3640_state *state;
struct v4l2_subdev *sd;
int err = 0;
state = kzalloc(sizeof(struct ov3640_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
strcpy(sd->name, OV3640_DRIVER_NAME);
/* Registering subdev */
v4l2_i2c_subdev_init(sd, client, &ov3640_ops);
err = checkIfOV3640(sd);
dev_info(&client->dev, "ov3640 has been probed,err(%d)\n",err);
if(err < 0)
ov3640_remove(client);
return err;
}
设置client和sd;
void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
const struct v4l2_subdev_ops *ops)
{
v4l2_subdev_init(sd, ops);
sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
/* the owner is the same as the i2c_client's driver owner */
sd->owner = client->driver->driver.owner;
/* i2c_client and v4l2_subdev point to one another */
v4l2_set_subdevdata(sd, client);
i2c_set_clientdata(client, sd);
/* initialize name */
snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
client->driver->driver.name, i2c_adapter_id(client->adapter),
client->addr);
}
其中v4l2_subdev和i2c_client相互关联并设置其ov3640_ops;
而在 v4l2_device中设置input camera id
此处分析问题
int fimc_s_input(struct file *file, void *fh, unsigned int i)
{
struct fimc_global *fimc = get_fimc_dev();
struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
int ret = 0;
fimc_dbg("%s: index %d\n", __func__, i);
if (i < 0 || i >= FIMC_MAXCAMS) {
fimc_err("%s: invalid input index\n", __func__);
return -EINVAL;
}
if (!fimc->camera_isvalid[i])
return -EINVAL;
if (fimc->camera[i].sd && ctrl->id != 2) {
fimc_err("%s: Camera already in use.\n", __func__);
return -EBUSY;
}
mutex_lock(&ctrl->v4l2_lock);
/* If ctrl->cam is not NULL, there is one subdev already registered.
* We need to unregister that subdev first.
*/
if (i != fimc->active_camera) {
fimc_release_subdev(ctrl);
ctrl->cam = &fimc->camera[i];
ret = fimc_configure_subdev(ctrl);
if (ret < 0) {
mutex_unlock(&ctrl->v4l2_lock);
fimc_err("%s: Could not register camera sensor "
"with V4L2.\n", __func__);
return -ENODEV;
}
fimc->active_camera = i;
}
if (ctrl->id == 2) {
if (i == fimc->active_camera) {
ctrl->cam = &fimc->camera[i];
} else {
mutex_unlock(&ctrl->v4l2_lock);
return -EINVAL;
}
}
mutex_unlock(&ctrl->v4l2_lock);
return 0;
其中配置fimc_configure_subdev();为核心
int fimc_configure_subdev(struct fimc_control *ctrl)
{
struct i2c_adapter *i2c_adap;
struct i2c_board_info *i2c_info;
struct v4l2_subdev *sd;
unsigned short addr;
int err;
char *name;
err = 0;
/* set parent for mclk */
if (clk_get_parent(ctrl->cam->clk->parent))
clk_set_parent(ctrl->cam->clk->parent, ctrl->cam->srclk);
/* set rate for mclk */
if (clk_get_rate(ctrl->cam->clk))
clk_set_rate(ctrl->cam->clk, ctrl->cam->clk_rate);
i2c_adap = i2c_get_adapter(ctrl->cam->i2c_busnum);
if (!i2c_adap)
fimc_err("subdev i2c_adapter missing-skip registration\n");
i2c_info = ctrl->cam->info;
if (!i2c_info) {
fimc_err("%s: subdev i2c board info missing\n", __func__);
return -ENODEV;
}
name = i2c_info->type;
if (!name) {
fimc_err("subdev i2c driver name missing-skip registration\n");
return -ENODEV;
}
addr = i2c_info->addr;
if (!addr) {
fimc_err("subdev i2c address missing-skip registration\n");
return -ENODEV;
}
/*
* NOTE: first time subdev being registered,
* s_config is called and try to initialize subdev device
* but in this point, we are not giving MCLK and power to subdev
* so nothing happens but pass platform data through
*/
sd = v4l2_i2c_new_subdev_board(&ctrl->v4l2_dev, i2c_adap,
i2c_info, &addr);
if (!sd) {
fimc_err("%s: v4l2 subdev board registering failed\n",
__func__);
err = -1;
}
/* Assign subdev to proper camera device pointer */
ctrl->cam->sd = sd;
return err;
// return 0;
}
找到adapter;
struct i2c_adapter *i2c_get_adapter(int nr)
{
struct i2c_adapter *adapter;
mutex_lock(&core_lock);
adapter = idr_find(&i2c_adapter_idr, nr);
if (adapter && !try_module_get(adapter->owner))
adapter = NULL;
mutex_unlock(&core_lock);
return adapter;
}
i2c_info = ctrl->cam->info;addr = i2c_info->addr; camera的iic信息;
/* Load an i2c sub-device. */
struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
struct i2c_adapter *adapter, struct i2c_board_info *info,
const unsigned short *probe_addrs)
{
struct v4l2_subdev *sd = NULL;
struct i2c_client *client;
BUG_ON(!v4l2_dev);
request_module(I2C_MODULE_PREFIX "%s", info->type);
/* Create the i2c client */
if (info->addr == 0 && probe_addrs)
client = i2c_new_probed_device(adapter, info, probe_addrs,
NULL);
else
client = i2c_new_device(adapter, info);
/* Note: by loading the module first we are certain that c->driver
will be set if the driver was found. If the module was not loaded
first, then the i2c core tries to delay-load the module for us,
and then c->driver is still NULL until the module is finally
loaded. This delay-load mechanism doesn't work if other drivers
want to use the i2c device, so explicitly loading the module
is the best alternative. */
if (client == NULL || client->driver == NULL)
goto error;
/* Lock the module so we can safely get the v4l2_subdev pointer */
if (!try_module_get(client->driver->driver.owner))
goto error;
sd = i2c_get_clientdata(client);
/* Register with the v4l2_device which increases the module's
use count as well. */
if (v4l2_device_register_subdev(v4l2_dev, sd))
sd = NULL;
/* Decrease the module use count to match the first try_module_get. */
module_put(client->driver->driver.owner);
error:
/* If we have a client but no subdev, then something went wrong and
we must unregister the client. */
if (client && sd == NULL)
i2c_unregister_device(client);
return sd;
}
E
struct i2c_client * client = i2c_new_device(adapter, info);
分析:就是分析问题
int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
struct v4l2_subdev *sd)
{
#if defined(CONFIG_MEDIA_CONTROLLER)
struct media_entity *entity = &sd->entity;很重要
#endif
int err;
/* Check for valid input */
if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
return -EINVAL;
/* Warn if we apparently re-register a subdev */
WARN_ON(sd->v4l2_dev != NULL);
if (!try_module_get(sd->owner))
return -ENODEV;
sd->v4l2_dev = v4l2_dev;
if (sd->internal_ops && sd->internal_ops->registered) {
err = sd->internal_ops->registered(sd);
if (err) {
module_put(sd->owner);
return err;
}
}
/* This just returns 0 if either of the two args is NULL */
err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
if (err) {
if (sd->internal_ops && sd->internal_ops->unregistered)
sd->internal_ops->unregistered(sd);
module_put(sd->owner);
return err;
}
#if defined(CONFIG_MEDIA_CONTROLLER)
/* Register the entity. */
if (v4l2_dev->mdev) {
err = media_device_register_entity(v4l2_dev->mdev, entity);
if (err < 0) {
if (sd->internal_ops && sd->internal_ops->unregistered)
sd->internal_ops->unregistered(sd);
module_put(sd->owner);
return err;
}
}
#endif
spin_lock(&v4l2_dev->lock);
list_add_tail(&sd->list, &v4l2_dev->subdevs);
spin_unlock(&v4l2_dev->lock);
return 0;
struct media_entity *entity = &sd->entity; 入口点
sd->v4l2_dev = v4l2_dev;
media_device_register_entity(v4l2_dev->mdev, entity);
-----.>加入链表
list_add_tail(&sd->list, &v4l2_dev->subdevs);
ctrl->cam->sd = sd;
static inline int fimc_mmap_cap(struct file *filp, struct vm_area_struct *vma)
{
struct fimc_prv_data *prv_data =
(struct fimc_prv_data *)filp->private_data;
struct fimc_control *ctrl = prv_data->ctrl;
u32 size = vma->vm_end - vma->vm_start;
u32 pfn, idx = vma->vm_pgoff;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_flags |= VM_RESERVED;
/*
* page frame number of the address for a source frame
* to be stored at.
*/
pfn = __phys_to_pfn(ctrl->cap->bufs[idx].base[0]);
if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) {
fimc_err("%s: writable mapping must be shared\n", __func__);
return -EINVAL;
}
if (remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot)) {
fimc_err("%s: mmap fail\n", __func__);
return -EINVAL;
}
return 0;
}
对于:v4l2_device和v4l2_sud关联;
这是另一种 v4l2_file_operations fimc_capture_fops;
于一开始的v4l2_file_operations fimc_ops实现不同但是 原理差不多;
static const struct v4l2_file_operations fimc_capture_fops = {
.owner = THIS_MODULE,
.open = fimc_capture_open,
.release = fimc_capture_close,
.poll = fimc_capture_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = fimc_capture_mmap,
};
static int fimc_capture_open(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
int ret = 0;
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
/* Return if the corresponding video mem2mem node is already opened. */
if (fimc_m2m_active(fimc))
return -EBUSY;
if (++fimc->vid_cap.refcnt == 1) {
ret = fimc_isp_subdev_init(fimc, 0);
if (ret) {
fimc->vid_cap.refcnt--;
return -EIO;
}
}
file->private_data = fimc->vid_cap.ctx;
return 0;
static int fimc_isp_subdev_init(struct fimc_dev *fimc, unsigned int index)
{
struct s5p_fimc_isp_info *isp_info;
struct s5p_platform_fimc *pdata = fimc->pdata;
int ret;
if (index >= pdata->num_clients)
return -EINVAL;
isp_info = &pdata->isp_info[index];
if (isp_info->clk_frequency)
clk_set_rate(fimc->clock[CLK_CAM], isp_info->clk_frequency);
ret = clk_enable(fimc->clock[CLK_CAM]);
if (ret)
return ret;
ret = fimc_subdev_attach(fimc, index);
if (ret)
return ret;
ret = fimc_hw_set_camera_polarity(fimc, isp_info);
if (ret)
return ret;
ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 1);
if (!ret)
return ret;
/* enabling power failed so unregister subdev */
fimc_subdev_unregister(fimc);
v4l2_err(&fimc->vid_cap.v4l2_dev, "ISP initialization failed: %d\n",
ret);
return ret;
}
/**
* fimc_subdev_attach - attach v4l2_subdev to camera host interface
*
* @fimc: FIMC device information
* @index: index to the array of available subdevices,
* -1 for full array search or non negative value
* to select specific subdevice
*/
static int fimc_subdev_attach(struct fimc_dev *fimc, int index)
{
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
struct s5p_platform_fimc *pdata = fimc->pdata;
struct s5p_fimc_isp_info *isp_info;
struct v4l2_subdev *sd;
int i;
for (i = 0; i < pdata->num_clients; ++i) {
isp_info = &pdata->isp_info[i];
if (index >= 0 && i != index)
continue;
sd = fimc_subdev_register(fimc, isp_info);
if (!IS_ERR_OR_NULL(sd)) {
vid_cap->sd = sd;
vid_cap->input_index = i;
return 0;
}
}
vid_cap->input_index = -1;
vid_cap->sd = NULL;
v4l2_err(&vid_cap->v4l2_dev, "fimc%d: sensor attach failed\n",
fimc->id);
return -ENODEV;
static struct v4l2_subdev *fimc_subdev_register(struct fimc_dev *fimc,
struct s5p_fimc_isp_info *isp_info)
{
struct i2c_adapter *i2c_adap;
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
struct v4l2_subdev *sd = NULL;
i2c_adap = i2c_get_adapter(isp_info->i2c_bus_num);
if (!i2c_adap)
return ERR_PTR(-ENOMEM);
sd = v4l2_i2c_new_subdev_board(&vid_cap->v4l2_dev, i2c_adap,
isp_info->board_info, NULL);
if (!sd) {
v4l2_err(&vid_cap->v4l2_dev, "failed to acquire subdev\n");
return NULL;
}
v4l2_info(&vid_cap->v4l2_dev, "subdevice %s registered successfuly\n",
isp_info->board_info->type);
return sd;
}
/* Load an i2c sub-device. */
struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
struct i2c_adapter *adapter, struct i2c_board_info *info,
const unsigned short *probe_addrs)
{
struct v4l2_subdev *sd = NULL;
struct i2c_client *client;
BUG_ON(!v4l2_dev);
request_module(I2C_MODULE_PREFIX "%s", info->type);
/* Create the i2c client */
if (info->addr == 0 && probe_addrs)
client = i2c_new_probed_device(adapter, info, probe_addrs,
NULL);
else
client = i2c_new_device(adapter, info);
/* Note: by loading the module first we are certain that c->driver
will be set if the driver was found. If the module was not loaded
first, then the i2c core tries to delay-load the module for us,
and then c->driver is still NULL until the module is finally
loaded. This delay-load mechanism doesn't work if other drivers
want to use the i2c device, so explicitly loading the module
is the best alternative. */
if (client == NULL || client->driver == NULL)
goto error;
/* Lock the module so we can safely get the v4l2_subdev pointer */
if (!try_module_get(client->driver->driver.owner))
goto error;
sd = i2c_get_clientdata(client);
/* Register with the v4l2_device which increases the module's
use count as well. */
if (v4l2_device_register_subdev(v4l2_dev, sd))
sd = NULL;
/* Decrease the module use count to match the first try_module_get. */
module_put(client->driver->driver.owner);
error:
/* If we have a client but no subdev, then something went wrong and
we must unregister the client. */
if (client && sd == NULL)
i2c_unregister_device(client);
return sd;
EXPORT_SYMBOL_GPL(v4l2_device_unregister);
int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
struct v4l2_subdev *sd)
{
#if defined(CONFIG_MEDIA_CONTROLLER)
struct media_entity *entity = &sd->entity;
#endif
int err;
/* Check for valid input */
if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
return -EINVAL;
/* Warn if we apparently re-register a subdev */
WARN_ON(sd->v4l2_dev != NULL);
if (!try_module_get(sd->owner))
return -ENODEV;
sd->v4l2_dev = v4l2_dev;
if (sd->internal_ops && sd->internal_ops->registered) {
err = sd->internal_ops->registered(sd);
if (err) {
module_put(sd->owner);
return err;
}
}
/* This just returns 0 if either of the two args is NULL */
err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
if (err) {
if (sd->internal_ops && sd->internal_ops->unregistered)
sd->internal_ops->unregistered(sd);
module_put(sd->owner);
return err;
}
#if defined(CONFIG_MEDIA_CONTROLLER)
/* Register the entity. */
if (v4l2_dev->mdev) {
err = media_device_register_entity(v4l2_dev->mdev, entity);
if (err < 0) {
if (sd->internal_ops && sd->internal_ops->unregistered)
sd->internal_ops->unregistered(sd);
module_put(sd->owner);
return err;
}
}
#endif
spin_lock(&v4l2_dev->lock);
list_add_tail(&sd->list, &v4l2_dev->subdevs);
spin_unlock(&v4l2_dev->lock);
return 0;