2019-08-15 13:48:03 -03:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* v4l2 - i2c - I2C helpers for Video4Linux2
*/
# include <linux/i2c.h>
# include <linux/module.h>
# include <media/v4l2-common.h>
# include <media/v4l2-device.h>
2019-08-15 13:48:05 -03:00
void v4l2_i2c_subdev_unregister ( struct v4l2_subdev * sd )
{
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 .
*
* Device tree or ACPI based devices must not
* be unregistered as they have not been
* registered by us , and would not be
* re - created by just probing the V4L2 driver .
*/
if ( client & & ! client - > dev . of_node & & ! client - > dev . fwnode )
i2c_unregister_device ( client ) ;
}
2019-08-15 13:48:03 -03:00
void v4l2_i2c_subdev_set_name ( struct v4l2_subdev * sd , struct i2c_client * client ,
const char * devname , const char * postfix )
{
if ( ! devname )
devname = client - > dev . driver - > name ;
if ( ! postfix )
postfix = " " ;
snprintf ( sd - > name , sizeof ( sd - > name ) , " %s%s %d-%04x " , devname , postfix ,
i2c_adapter_id ( client - > adapter ) , client - > addr ) ;
}
EXPORT_SYMBOL_GPL ( v4l2_i2c_subdev_set_name ) ;
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 - > dev . driver - > owner ;
sd - > dev = & client - > dev ;
/* i2c_client and v4l2_subdev point to one another */
v4l2_set_subdevdata ( sd , client ) ;
i2c_set_clientdata ( client , sd ) ;
v4l2_i2c_subdev_set_name ( sd , client , NULL , NULL ) ;
}
EXPORT_SYMBOL_GPL ( v4l2_i2c_subdev_init ) ;
/* 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 - > dev . driver = = NULL )
goto error ;
/* Lock the module so we can safely get the v4l2_subdev pointer */
if ( ! try_module_get ( client - > dev . 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 - > dev . 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_i2c_new_subdev_board ) ;
struct v4l2_subdev * v4l2_i2c_new_subdev ( struct v4l2_device * v4l2_dev ,
struct i2c_adapter * adapter , const char * client_type ,
u8 addr , const unsigned short * probe_addrs )
{
struct i2c_board_info info ;
/* Setup the i2c board info with the device type and
the device address . */
memset ( & info , 0 , sizeof ( info ) ) ;
strscpy ( info . type , client_type , sizeof ( info . type ) ) ;
info . addr = addr ;
return v4l2_i2c_new_subdev_board ( v4l2_dev , adapter , & info , probe_addrs ) ;
}
EXPORT_SYMBOL_GPL ( v4l2_i2c_new_subdev ) ;
/* Return i2c client address of v4l2_subdev. */
unsigned short v4l2_i2c_subdev_addr ( struct v4l2_subdev * sd )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
return client ? client - > addr : I2C_CLIENT_END ;
}
EXPORT_SYMBOL_GPL ( v4l2_i2c_subdev_addr ) ;
/* Return a list of I2C tuner addresses to probe. Use only if the tuner
addresses are unknown . */
const unsigned short * v4l2_i2c_tuner_addrs ( enum v4l2_i2c_tuner_type type )
{
static const unsigned short radio_addrs [ ] = {
# if IS_ENABLED(CONFIG_MEDIA_TUNER_TEA5761)
0x10 ,
# endif
0x60 ,
I2C_CLIENT_END
} ;
static const unsigned short demod_addrs [ ] = {
0x42 , 0x43 , 0x4a , 0x4b ,
I2C_CLIENT_END
} ;
static const unsigned short tv_addrs [ ] = {
0x42 , 0x43 , 0x4a , 0x4b , /* tda8290 */
0x60 , 0x61 , 0x62 , 0x63 , 0x64 ,
I2C_CLIENT_END
} ;
switch ( type ) {
case ADDRS_RADIO :
return radio_addrs ;
case ADDRS_DEMOD :
return demod_addrs ;
case ADDRS_TV :
return tv_addrs ;
case ADDRS_TV_WITH_DEMOD :
return tv_addrs + 4 ;
}
return NULL ;
}
EXPORT_SYMBOL_GPL ( v4l2_i2c_tuner_addrs ) ;