2008-11-30 03:36:58 +03:00
/*
V4L2 device support .
Copyright ( C ) 2008 Hans Verkuil < hverkuil @ xs4all . nl >
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/types.h>
# include <linux/ioctl.h>
2011-07-03 22:03:12 +04:00
# include <linux/module.h>
2008-11-30 03:36:58 +03:00
# include <linux/i2c.h>
2011-09-13 15:07:55 +04:00
# include <linux/slab.h>
2010-03-23 17:23:29 +03:00
# if defined(CONFIG_SPI)
# include <linux/spi/spi.h>
# endif
2008-11-30 03:36:58 +03:00
# include <linux/videodev2.h>
# include <media/v4l2-device.h>
2010-05-16 16:24:06 +04:00
# include <media/v4l2-ctrls.h>
2008-11-30 03:36:58 +03:00
int v4l2_device_register ( struct device * dev , struct v4l2_device * v4l2_dev )
{
2009-02-14 17:54:23 +03:00
if ( v4l2_dev = = NULL )
2008-11-30 03:36:58 +03:00
return - EINVAL ;
2009-02-14 17:54:23 +03:00
2008-11-30 03:36:58 +03:00
INIT_LIST_HEAD ( & v4l2_dev - > subdevs ) ;
spin_lock_init ( & v4l2_dev - > lock ) ;
2010-11-26 12:47:28 +03:00
mutex_init ( & v4l2_dev - > ioctl_lock ) ;
2011-02-24 16:42:24 +03:00
v4l2_prio_init ( & v4l2_dev - > prio ) ;
2011-03-12 12:37:19 +03:00
kref_init ( & v4l2_dev - > ref ) ;
2011-09-06 16:08:08 +04:00
get_device ( dev ) ;
2008-11-30 03:36:58 +03:00
v4l2_dev - > dev = dev ;
2009-02-14 17:54:23 +03:00
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 " ,
2009-03-25 02:38:22 +03:00
dev - > driver - > name , dev_name ( dev ) ) ;
2009-12-09 14:40:05 +03:00
if ( ! dev_get_drvdata ( dev ) )
dev_set_drvdata ( dev , v4l2_dev ) ;
2008-11-30 03:36:58 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( v4l2_device_register ) ;
2011-03-12 12:37:19 +03:00
static void v4l2_device_release ( struct kref * ref )
{
struct v4l2_device * v4l2_dev =
container_of ( ref , struct v4l2_device , ref ) ;
if ( v4l2_dev - > release )
v4l2_dev - > release ( v4l2_dev ) ;
}
int v4l2_device_put ( struct v4l2_device * v4l2_dev )
{
return kref_put ( & v4l2_dev - > ref , v4l2_device_release ) ;
}
EXPORT_SYMBOL_GPL ( v4l2_device_put ) ;
2009-05-02 17:12:50 +04:00
int v4l2_device_set_name ( struct v4l2_device * v4l2_dev , const char * basename ,
atomic_t * instance )
{
int num = atomic_inc_return ( instance ) - 1 ;
int len = strlen ( basename ) ;
if ( basename [ len - 1 ] > = ' 0 ' & & basename [ len - 1 ] < = ' 9 ' )
snprintf ( v4l2_dev - > name , sizeof ( v4l2_dev - > name ) ,
" %s-%d " , basename , num ) ;
else
snprintf ( v4l2_dev - > name , sizeof ( v4l2_dev - > name ) ,
" %s%d " , basename , num ) ;
return num ;
}
EXPORT_SYMBOL_GPL ( v4l2_device_set_name ) ;
2009-03-14 14:28:45 +03:00
void v4l2_device_disconnect ( struct v4l2_device * v4l2_dev )
{
2009-12-09 14:40:05 +03:00
if ( v4l2_dev - > dev = = NULL )
return ;
if ( dev_get_drvdata ( v4l2_dev - > dev ) = = v4l2_dev )
2009-03-14 14:28:45 +03:00
dev_set_drvdata ( v4l2_dev - > dev , NULL ) ;
2011-09-06 16:08:08 +04:00
put_device ( v4l2_dev - > dev ) ;
2009-12-09 14:40:05 +03:00
v4l2_dev - > dev = NULL ;
2009-03-14 14:28:45 +03:00
}
EXPORT_SYMBOL_GPL ( v4l2_device_disconnect ) ;
2008-11-30 03:36:58 +03:00
void v4l2_device_unregister ( struct v4l2_device * v4l2_dev )
{
struct v4l2_subdev * sd , * next ;
2009-02-14 17:54:23 +03:00
if ( v4l2_dev = = NULL )
2008-11-30 03:36:58 +03:00
return ;
2009-03-14 14:28:45 +03:00
v4l2_device_disconnect ( v4l2_dev ) ;
2009-02-14 17:54:23 +03:00
/* Unregister subdevs */
2009-05-02 17:58:51 +04:00
list_for_each_entry_safe ( sd , next , & v4l2_dev - > subdevs , list ) {
2008-11-30 03:36:58 +03:00
v4l2_device_unregister_subdev ( sd ) ;
2009-06-10 18:58:22 +04:00
# if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
2009-05-02 17:58:51 +04:00
if ( sd - > flags & V4L2_SUBDEV_FL_IS_I2C ) {
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
/* We need to unregister the i2c client explicitly.
We cannot rely on i2c_del_adapter to always
unregister clients for us , since if the i2c bus
is a platform bus , then it is never deleted . */
if ( client )
i2c_unregister_device ( client ) ;
2011-01-11 23:48:21 +03:00
continue ;
2009-05-02 17:58:51 +04:00
}
2010-03-23 17:23:29 +03:00
# endif
# if defined(CONFIG_SPI)
if ( sd - > flags & V4L2_SUBDEV_FL_IS_SPI ) {
struct spi_device * spi = v4l2_get_subdevdata ( sd ) ;
if ( spi )
spi_unregister_device ( spi ) ;
2011-01-11 23:48:21 +03:00
continue ;
2010-03-23 17:23:29 +03:00
}
2009-05-11 20:37:41 +04:00
# endif
2009-05-02 17:58:51 +04:00
}
2008-11-30 03:36:58 +03:00
}
EXPORT_SYMBOL_GPL ( v4l2_device_unregister ) ;
2009-02-14 17:54:23 +03:00
int v4l2_device_register_subdev ( struct v4l2_device * v4l2_dev ,
2009-12-09 14:40:08 +03:00
struct v4l2_subdev * sd )
2008-11-30 03:36:58 +03:00
{
2009-12-09 14:40:08 +03:00
# if defined(CONFIG_MEDIA_CONTROLLER)
struct media_entity * entity = & sd - > entity ;
# endif
2010-05-16 16:24:06 +04:00
int err ;
2008-11-30 03:36:58 +03:00
/* Check for valid input */
2009-02-14 17:54:23 +03:00
if ( v4l2_dev = = NULL | | sd = = NULL | | ! sd - > name [ 0 ] )
2008-11-30 03:36:58 +03:00
return - EINVAL ;
2009-12-09 14:38:49 +03:00
2008-11-30 03:36:58 +03:00
/* Warn if we apparently re-register a subdev */
2009-02-14 18:00:53 +03:00
WARN_ON ( sd - > v4l2_dev ! = NULL ) ;
2009-12-09 14:38:49 +03:00
2008-11-30 03:36:58 +03:00
if ( ! try_module_get ( sd - > owner ) )
return - ENODEV ;
2009-12-09 14:38:49 +03:00
2011-01-08 13:15:53 +03:00
sd - > v4l2_dev = v4l2_dev ;
if ( sd - > internal_ops & & sd - > internal_ops - > registered ) {
err = sd - > internal_ops - > registered ( sd ) ;
2011-04-30 17:34:05 +04:00
if ( err ) {
module_put ( sd - > owner ) ;
2011-01-08 13:15:53 +03:00
return err ;
2011-04-30 17:34:05 +04:00
}
2011-01-08 13:15:53 +03:00
}
2009-12-09 14:38:49 +03:00
2010-05-16 16:24:06 +04:00
/* This just returns 0 if either of the two args is NULL */
err = v4l2_ctrl_add_handler ( v4l2_dev - > ctrl_handler , sd - > ctrl_handler ) ;
2011-01-08 13:15:53 +03:00
if ( err ) {
if ( sd - > internal_ops & & sd - > internal_ops - > unregistered )
sd - > internal_ops - > unregistered ( sd ) ;
2011-04-30 17:34:05 +04:00
module_put ( sd - > owner ) ;
2010-05-16 16:24:06 +04:00
return err ;
2011-01-08 13:15:53 +03:00
}
2009-12-09 14:38:49 +03:00
2009-12-09 14:40:08 +03:00
# 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
2009-02-14 17:54:23 +03:00
spin_lock ( & v4l2_dev - > lock ) ;
list_add_tail ( & sd - > list , & v4l2_dev - > subdevs ) ;
spin_unlock ( & v4l2_dev - > lock ) ;
2009-12-09 14:38:49 +03:00
2008-11-30 03:36:58 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( v4l2_device_register_subdev ) ;
2011-09-13 15:07:55 +04:00
static void v4l2_device_release_subdev_node ( struct video_device * vdev )
{
struct v4l2_subdev * sd = video_get_drvdata ( vdev ) ;
sd - > devnode = NULL ;
kfree ( vdev ) ;
}
2009-12-09 14:38:49 +03:00
int v4l2_device_register_subdev_nodes ( struct v4l2_device * v4l2_dev )
{
struct video_device * vdev ;
struct v4l2_subdev * sd ;
int err ;
/* Register a device node for every subdev marked with the
* V4L2_SUBDEV_FL_HAS_DEVNODE flag .
*/
list_for_each_entry ( sd , & v4l2_dev - > subdevs , list ) {
if ( ! ( sd - > flags & V4L2_SUBDEV_FL_HAS_DEVNODE ) )
continue ;
2011-09-13 15:07:55 +04:00
vdev = kzalloc ( sizeof ( * vdev ) , GFP_KERNEL ) ;
if ( ! vdev ) {
err = - ENOMEM ;
goto clean_up ;
}
video_set_drvdata ( vdev , sd ) ;
2009-12-09 14:38:49 +03:00
strlcpy ( vdev - > name , sd - > name , sizeof ( vdev - > name ) ) ;
vdev - > v4l2_dev = v4l2_dev ;
vdev - > fops = & v4l2_subdev_fops ;
2011-09-13 15:07:55 +04:00
vdev - > release = v4l2_device_release_subdev_node ;
2011-05-25 15:52:13 +04:00
vdev - > ctrl_handler = sd - > ctrl_handler ;
2009-12-09 14:38:49 +03:00
err = __video_register_device ( vdev , VFL_TYPE_SUBDEV , - 1 , 1 ,
sd - > owner ) ;
2011-09-13 15:07:55 +04:00
if ( err < 0 ) {
kfree ( vdev ) ;
goto clean_up ;
}
2009-12-09 14:40:08 +03:00
# if defined(CONFIG_MEDIA_CONTROLLER)
sd - > entity . v4l . major = VIDEO_MAJOR ;
sd - > entity . v4l . minor = vdev - > minor ;
# endif
2011-09-13 15:07:55 +04:00
sd - > devnode = vdev ;
2009-12-09 14:38:49 +03:00
}
return 0 ;
2011-09-13 15:07:55 +04:00
clean_up :
list_for_each_entry ( sd , & v4l2_dev - > subdevs , list ) {
if ( ! sd - > devnode )
break ;
video_unregister_device ( sd - > devnode ) ;
}
return err ;
2009-12-09 14:38:49 +03:00
}
EXPORT_SYMBOL_GPL ( v4l2_device_register_subdev_nodes ) ;
2008-11-30 03:36:58 +03:00
void v4l2_device_unregister_subdev ( struct v4l2_subdev * sd )
{
2009-12-09 14:40:08 +03:00
struct v4l2_device * v4l2_dev ;
2008-11-30 03:36:58 +03:00
/* return if it isn't registered */
2009-02-14 18:00:53 +03:00
if ( sd = = NULL | | sd - > v4l2_dev = = NULL )
2008-11-30 03:36:58 +03:00
return ;
2009-12-09 14:38:49 +03:00
2009-12-09 14:40:08 +03:00
v4l2_dev = sd - > v4l2_dev ;
spin_lock ( & v4l2_dev - > lock ) ;
2008-11-30 03:36:58 +03:00
list_del ( & sd - > list ) ;
2009-12-09 14:40:08 +03:00
spin_unlock ( & v4l2_dev - > lock ) ;
2011-01-08 13:15:53 +03:00
if ( sd - > internal_ops & & sd - > internal_ops - > unregistered )
sd - > internal_ops - > unregistered ( sd ) ;
2009-02-14 18:00:53 +03:00
sd - > v4l2_dev = NULL ;
2009-12-09 14:38:49 +03:00
2009-12-09 14:40:08 +03:00
# if defined(CONFIG_MEDIA_CONTROLLER)
if ( v4l2_dev - > mdev )
media_device_unregister_entity ( & sd - > entity ) ;
# endif
2011-09-13 15:07:55 +04:00
video_unregister_device ( sd - > devnode ) ;
2008-11-30 03:36:58 +03:00
module_put ( sd - > owner ) ;
}
EXPORT_SYMBOL_GPL ( v4l2_device_unregister_subdev ) ;