2011-05-05 22:39:25 +04:00
/*
2012-08-14 23:23:43 +04:00
* drivers / media / i2c / adp1653 . c
2011-05-05 22:39:25 +04:00
*
* Copyright ( C ) 2008 - - 2011 Nokia Corporation
*
2012-10-28 13:44:17 +04:00
* Contact : Sakari Ailus < sakari . ailus @ iki . fi >
2011-05-05 22:39:25 +04:00
*
* Contributors :
2012-10-28 13:44:17 +04:00
* Sakari Ailus < sakari . ailus @ iki . fi >
2011-05-05 22:39:25 +04:00
* Tuukka Toivonen < tuukkat76 @ gmail . com >
2015-04-09 10:42:38 +03:00
* Pavel Machek < pavel @ ucw . cz >
2011-05-05 22:39:25 +04:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*
* 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 . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*
* TODO :
* - fault interrupt handling
* - hardware strobe
* - power doesn ' t need to be ON if all lights are off
*
*/
# include <linux/delay.h>
2011-07-03 22:03:12 +04:00
# include <linux/module.h>
2011-05-05 22:39:25 +04:00
# include <linux/i2c.h>
# include <linux/slab.h>
2015-04-09 10:42:38 +03:00
# include <linux/of.h>
# include <linux/gpio/consumer.h>
2015-11-10 17:01:44 +03:00
# include <media/i2c/adp1653.h>
2011-05-05 22:39:25 +04:00
# include <media/v4l2-device.h>
# define TIMEOUT_MAX 820000
# define TIMEOUT_STEP 54600
# define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \
* TIMEOUT_STEP )
# define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \
/ TIMEOUT_STEP )
# define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP)
/* Write values into ADP1653 registers. */
static int adp1653_update_hw ( struct adp1653_flash * flash )
{
struct i2c_client * client = v4l2_get_subdevdata ( & flash - > subdev ) ;
u8 out_sel ;
u8 config = 0 ;
int rval ;
out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG (
flash - > indicator_intensity - > val )
< < ADP1653_REG_OUT_SEL_ILED_SHIFT ;
switch ( flash - > led_mode - > val ) {
case V4L2_FLASH_LED_MODE_NONE :
break ;
case V4L2_FLASH_LED_MODE_FLASH :
/* Flash mode, light on with strobe, duration from timer */
config = ADP1653_REG_CONFIG_TMR_CFG ;
config | = TIMEOUT_US_TO_CODE ( flash - > flash_timeout - > val )
< < ADP1653_REG_CONFIG_TMR_SET_SHIFT ;
break ;
case V4L2_FLASH_LED_MODE_TORCH :
/* Torch mode, light immediately on, duration indefinite */
out_sel | = ADP1653_FLASH_INTENSITY_mA_TO_REG (
flash - > torch_intensity - > val )
< < ADP1653_REG_OUT_SEL_HPLED_SHIFT ;
break ;
}
rval = i2c_smbus_write_byte_data ( client , ADP1653_REG_OUT_SEL , out_sel ) ;
if ( rval < 0 )
return rval ;
rval = i2c_smbus_write_byte_data ( client , ADP1653_REG_CONFIG , config ) ;
if ( rval < 0 )
return rval ;
return 0 ;
}
static int adp1653_get_fault ( struct adp1653_flash * flash )
{
struct i2c_client * client = v4l2_get_subdevdata ( & flash - > subdev ) ;
int fault ;
int rval ;
fault = i2c_smbus_read_byte_data ( client , ADP1653_REG_FAULT ) ;
if ( IS_ERR_VALUE ( fault ) )
return fault ;
flash - > fault | = fault ;
if ( ! flash - > fault )
return 0 ;
/* Clear faults. */
rval = i2c_smbus_write_byte_data ( client , ADP1653_REG_OUT_SEL , 0 ) ;
if ( IS_ERR_VALUE ( rval ) )
return rval ;
flash - > led_mode - > val = V4L2_FLASH_LED_MODE_NONE ;
rval = adp1653_update_hw ( flash ) ;
if ( IS_ERR_VALUE ( rval ) )
return rval ;
return flash - > fault ;
}
static int adp1653_strobe ( struct adp1653_flash * flash , int enable )
{
struct i2c_client * client = v4l2_get_subdevdata ( & flash - > subdev ) ;
u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG (
flash - > indicator_intensity - > val )
< < ADP1653_REG_OUT_SEL_ILED_SHIFT ;
int rval ;
if ( flash - > led_mode - > val ! = V4L2_FLASH_LED_MODE_FLASH )
return - EBUSY ;
if ( ! enable )
return i2c_smbus_write_byte_data ( client , ADP1653_REG_OUT_SEL ,
out_sel ) ;
out_sel | = ADP1653_FLASH_INTENSITY_mA_TO_REG (
flash - > flash_intensity - > val )
< < ADP1653_REG_OUT_SEL_HPLED_SHIFT ;
rval = i2c_smbus_write_byte_data ( client , ADP1653_REG_OUT_SEL , out_sel ) ;
if ( rval )
return rval ;
/* Software strobe using i2c */
rval = i2c_smbus_write_byte_data ( client , ADP1653_REG_SW_STROBE ,
ADP1653_REG_SW_STROBE_SW_STROBE ) ;
if ( rval )
return rval ;
return i2c_smbus_write_byte_data ( client , ADP1653_REG_SW_STROBE , 0 ) ;
}
/* --------------------------------------------------------------------------
* V4L2 controls
*/
static int adp1653_get_ctrl ( struct v4l2_ctrl * ctrl )
{
struct adp1653_flash * flash =
container_of ( ctrl - > handler , struct adp1653_flash , ctrls ) ;
int rval ;
rval = adp1653_get_fault ( flash ) ;
if ( IS_ERR_VALUE ( rval ) )
return rval ;
ctrl - > cur . val = 0 ;
if ( flash - > fault & ADP1653_REG_FAULT_FLT_SCP )
ctrl - > cur . val | = V4L2_FLASH_FAULT_SHORT_CIRCUIT ;
if ( flash - > fault & ADP1653_REG_FAULT_FLT_OT )
ctrl - > cur . val | = V4L2_FLASH_FAULT_OVER_TEMPERATURE ;
if ( flash - > fault & ADP1653_REG_FAULT_FLT_TMR )
ctrl - > cur . val | = V4L2_FLASH_FAULT_TIMEOUT ;
if ( flash - > fault & ADP1653_REG_FAULT_FLT_OV )
ctrl - > cur . val | = V4L2_FLASH_FAULT_OVER_VOLTAGE ;
flash - > fault = 0 ;
return 0 ;
}
static int adp1653_set_ctrl ( struct v4l2_ctrl * ctrl )
{
struct adp1653_flash * flash =
container_of ( ctrl - > handler , struct adp1653_flash , ctrls ) ;
int rval ;
rval = adp1653_get_fault ( flash ) ;
if ( IS_ERR_VALUE ( rval ) )
return rval ;
if ( ( rval & ( ADP1653_REG_FAULT_FLT_SCP |
ADP1653_REG_FAULT_FLT_OT |
ADP1653_REG_FAULT_FLT_OV ) ) & &
( ctrl - > id = = V4L2_CID_FLASH_STROBE | |
ctrl - > id = = V4L2_CID_FLASH_TORCH_INTENSITY | |
ctrl - > id = = V4L2_CID_FLASH_LED_MODE ) )
return - EBUSY ;
switch ( ctrl - > id ) {
case V4L2_CID_FLASH_STROBE :
return adp1653_strobe ( flash , 1 ) ;
case V4L2_CID_FLASH_STROBE_STOP :
return adp1653_strobe ( flash , 0 ) ;
}
return adp1653_update_hw ( flash ) ;
}
static const struct v4l2_ctrl_ops adp1653_ctrl_ops = {
. g_volatile_ctrl = adp1653_get_ctrl ,
. s_ctrl = adp1653_set_ctrl ,
} ;
static int adp1653_init_controls ( struct adp1653_flash * flash )
{
struct v4l2_ctrl * fault ;
v4l2_ctrl_handler_init ( & flash - > ctrls , 9 ) ;
flash - > led_mode =
v4l2_ctrl_new_std_menu ( & flash - > ctrls , & adp1653_ctrl_ops ,
V4L2_CID_FLASH_LED_MODE ,
V4L2_FLASH_LED_MODE_TORCH , ~ 0x7 , 0 ) ;
v4l2_ctrl_new_std_menu ( & flash - > ctrls , & adp1653_ctrl_ops ,
V4L2_CID_FLASH_STROBE_SOURCE ,
V4L2_FLASH_STROBE_SOURCE_SOFTWARE , ~ 0x1 , 0 ) ;
v4l2_ctrl_new_std ( & flash - > ctrls , & adp1653_ctrl_ops ,
V4L2_CID_FLASH_STROBE , 0 , 0 , 0 , 0 ) ;
v4l2_ctrl_new_std ( & flash - > ctrls , & adp1653_ctrl_ops ,
V4L2_CID_FLASH_STROBE_STOP , 0 , 0 , 0 , 0 ) ;
flash - > flash_timeout =
v4l2_ctrl_new_std ( & flash - > ctrls , & adp1653_ctrl_ops ,
V4L2_CID_FLASH_TIMEOUT , TIMEOUT_MIN ,
flash - > platform_data - > max_flash_timeout ,
TIMEOUT_STEP ,
flash - > platform_data - > max_flash_timeout ) ;
flash - > flash_intensity =
v4l2_ctrl_new_std ( & flash - > ctrls , & adp1653_ctrl_ops ,
V4L2_CID_FLASH_INTENSITY ,
ADP1653_FLASH_INTENSITY_MIN ,
flash - > platform_data - > max_flash_intensity ,
1 , flash - > platform_data - > max_flash_intensity ) ;
flash - > torch_intensity =
v4l2_ctrl_new_std ( & flash - > ctrls , & adp1653_ctrl_ops ,
V4L2_CID_FLASH_TORCH_INTENSITY ,
ADP1653_TORCH_INTENSITY_MIN ,
flash - > platform_data - > max_torch_intensity ,
ADP1653_FLASH_INTENSITY_STEP ,
flash - > platform_data - > max_torch_intensity ) ;
flash - > indicator_intensity =
v4l2_ctrl_new_std ( & flash - > ctrls , & adp1653_ctrl_ops ,
V4L2_CID_FLASH_INDICATOR_INTENSITY ,
ADP1653_INDICATOR_INTENSITY_MIN ,
flash - > platform_data - > max_indicator_intensity ,
ADP1653_INDICATOR_INTENSITY_STEP ,
ADP1653_INDICATOR_INTENSITY_MIN ) ;
fault = v4l2_ctrl_new_std ( & flash - > ctrls , & adp1653_ctrl_ops ,
V4L2_CID_FLASH_FAULT , 0 ,
V4L2_FLASH_FAULT_OVER_VOLTAGE
| V4L2_FLASH_FAULT_OVER_TEMPERATURE
| V4L2_FLASH_FAULT_SHORT_CIRCUIT , 0 , 0 ) ;
if ( flash - > ctrls . error )
return flash - > ctrls . error ;
2011-08-26 14:35:14 +04:00
fault - > flags | = V4L2_CTRL_FLAG_VOLATILE ;
2011-05-05 22:39:25 +04:00
flash - > subdev . ctrl_handler = & flash - > ctrls ;
return 0 ;
}
/* --------------------------------------------------------------------------
* V4L2 subdev operations
*/
static int
adp1653_init_device ( struct adp1653_flash * flash )
{
struct i2c_client * client = v4l2_get_subdevdata ( & flash - > subdev ) ;
int rval ;
/* Clear FAULT register by writing zero to OUT_SEL */
rval = i2c_smbus_write_byte_data ( client , ADP1653_REG_OUT_SEL , 0 ) ;
if ( rval < 0 ) {
dev_err ( & client - > dev , " failed writing fault register \n " ) ;
return - EIO ;
}
2012-01-25 04:05:34 +04:00
mutex_lock ( flash - > ctrls . lock ) ;
2011-05-05 22:39:25 +04:00
/* Reset faults before reading new ones. */
flash - > fault = 0 ;
rval = adp1653_get_fault ( flash ) ;
2012-01-25 04:05:34 +04:00
mutex_unlock ( flash - > ctrls . lock ) ;
2011-05-05 22:39:25 +04:00
if ( rval > 0 ) {
dev_err ( & client - > dev , " faults detected: 0x%1.1x \n " , rval ) ;
return - EIO ;
}
2012-01-25 04:05:34 +04:00
mutex_lock ( flash - > ctrls . lock ) ;
2011-05-05 22:39:25 +04:00
rval = adp1653_update_hw ( flash ) ;
2012-01-25 04:05:34 +04:00
mutex_unlock ( flash - > ctrls . lock ) ;
2011-05-05 22:39:25 +04:00
if ( rval ) {
dev_err ( & client - > dev ,
" adp1653_update_hw failed at %s \n " , __func__ ) ;
return - EIO ;
}
return 0 ;
}
static int
__adp1653_set_power ( struct adp1653_flash * flash , int on )
{
2015-04-09 13:33:45 +03:00
int ret ;
2011-05-05 22:39:25 +04:00
2015-04-09 10:42:38 +03:00
if ( flash - > platform_data - > power ) {
ret = flash - > platform_data - > power ( & flash - > subdev , on ) ;
if ( ret < 0 )
return ret ;
} else {
gpiod_set_value ( flash - > platform_data - > enable_gpio , on ) ;
if ( on )
/* Some delay is apparently required. */
udelay ( 20 ) ;
}
2011-05-05 22:39:25 +04:00
if ( ! on )
return 0 ;
ret = adp1653_init_device ( flash ) ;
2015-04-09 10:42:38 +03:00
if ( ret > = 0 )
return ret ;
if ( flash - > platform_data - > power )
2011-05-05 22:39:25 +04:00
flash - > platform_data - > power ( & flash - > subdev , 0 ) ;
2015-04-09 10:42:38 +03:00
else
gpiod_set_value ( flash - > platform_data - > enable_gpio , 0 ) ;
2011-05-05 22:39:25 +04:00
return ret ;
}
static int
adp1653_set_power ( struct v4l2_subdev * subdev , int on )
{
struct adp1653_flash * flash = to_adp1653_flash ( subdev ) ;
int ret = 0 ;
mutex_lock ( & flash - > power_lock ) ;
/* If the power count is modified from 0 to != 0 or from != 0 to 0,
* update the power state .
*/
if ( flash - > power_count = = ! on ) {
ret = __adp1653_set_power ( flash , ! ! on ) ;
if ( ret < 0 )
goto done ;
}
/* Update the power count. */
flash - > power_count + = on ? 1 : - 1 ;
WARN_ON ( flash - > power_count < 0 ) ;
done :
mutex_unlock ( & flash - > power_lock ) ;
return ret ;
}
static int adp1653_open ( struct v4l2_subdev * sd , struct v4l2_subdev_fh * fh )
{
return adp1653_set_power ( sd , 1 ) ;
}
static int adp1653_close ( struct v4l2_subdev * sd , struct v4l2_subdev_fh * fh )
{
return adp1653_set_power ( sd , 0 ) ;
}
static const struct v4l2_subdev_core_ops adp1653_core_ops = {
. s_power = adp1653_set_power ,
} ;
static const struct v4l2_subdev_ops adp1653_ops = {
. core = & adp1653_core_ops ,
} ;
static const struct v4l2_subdev_internal_ops adp1653_internal_ops = {
. open = adp1653_open ,
. close = adp1653_close ,
} ;
/* --------------------------------------------------------------------------
* I2C driver
*/
# ifdef CONFIG_PM
static int adp1653_suspend ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct v4l2_subdev * subdev = i2c_get_clientdata ( client ) ;
struct adp1653_flash * flash = to_adp1653_flash ( subdev ) ;
if ( ! flash - > power_count )
return 0 ;
return __adp1653_set_power ( flash , 0 ) ;
}
static int adp1653_resume ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct v4l2_subdev * subdev = i2c_get_clientdata ( client ) ;
struct adp1653_flash * flash = to_adp1653_flash ( subdev ) ;
if ( ! flash - > power_count )
return 0 ;
return __adp1653_set_power ( flash , 1 ) ;
}
# else
# define adp1653_suspend NULL
# define adp1653_resume NULL
# endif /* CONFIG_PM */
2015-04-09 10:42:38 +03:00
static int adp1653_of_init ( struct i2c_client * client ,
struct adp1653_flash * flash ,
struct device_node * node )
{
struct adp1653_platform_data * pd ;
struct device_node * child ;
pd = devm_kzalloc ( & client - > dev , sizeof ( * pd ) , GFP_KERNEL ) ;
if ( ! pd )
return - ENOMEM ;
flash - > platform_data = pd ;
child = of_get_child_by_name ( node , " flash " ) ;
if ( ! child )
return - EINVAL ;
if ( of_property_read_u32 ( child , " flash-timeout-us " ,
& pd - > max_flash_timeout ) )
goto err ;
if ( of_property_read_u32 ( child , " flash-max-microamp " ,
& pd - > max_flash_intensity ) )
goto err ;
pd - > max_flash_intensity / = 1000 ;
if ( of_property_read_u32 ( child , " led-max-microamp " ,
& pd - > max_torch_intensity ) )
goto err ;
pd - > max_torch_intensity / = 1000 ;
of_node_put ( child ) ;
child = of_get_child_by_name ( node , " indicator " ) ;
if ( ! child )
return - EINVAL ;
if ( of_property_read_u32 ( child , " led-max-microamp " ,
& pd - > max_indicator_intensity ) )
goto err ;
of_node_put ( child ) ;
2015-06-12 10:04:55 +03:00
pd - > enable_gpio = devm_gpiod_get ( & client - > dev , " enable " , GPIOD_OUT_LOW ) ;
2016-03-07 21:39:32 +03:00
if ( IS_ERR ( pd - > enable_gpio ) ) {
2015-04-09 10:42:38 +03:00
dev_err ( & client - > dev , " Error getting GPIO \n " ) ;
2016-03-07 21:39:32 +03:00
return PTR_ERR ( pd - > enable_gpio ) ;
2015-04-09 10:42:38 +03:00
}
return 0 ;
err :
dev_err ( & client - > dev , " Required property not found \n " ) ;
of_node_put ( child ) ;
return - EINVAL ;
}
2011-05-05 22:39:25 +04:00
static int adp1653_probe ( struct i2c_client * client ,
const struct i2c_device_id * devid )
{
struct adp1653_flash * flash ;
int ret ;
2013-05-02 15:29:43 +04:00
flash = devm_kzalloc ( & client - > dev , sizeof ( * flash ) , GFP_KERNEL ) ;
2011-05-05 22:39:25 +04:00
if ( flash = = NULL )
return - ENOMEM ;
2015-04-09 10:42:38 +03:00
if ( client - > dev . of_node ) {
ret = adp1653_of_init ( client , flash , client - > dev . of_node ) ;
if ( ret )
return ret ;
} else {
if ( ! client - > dev . platform_data ) {
dev_err ( & client - > dev ,
" Neither DT not platform data provided \n " ) ;
2016-02-07 07:27:32 +03:00
return - EINVAL ;
2015-04-09 10:42:38 +03:00
}
flash - > platform_data = client - > dev . platform_data ;
}
2011-05-05 22:39:25 +04:00
mutex_init ( & flash - > power_lock ) ;
v4l2_i2c_subdev_init ( & flash - > subdev , client , & adp1653_ops ) ;
flash - > subdev . internal_ops = & adp1653_internal_ops ;
flash - > subdev . flags | = V4L2_SUBDEV_FL_HAS_DEVNODE ;
2011-07-28 11:59:38 +04:00
ret = adp1653_init_controls ( flash ) ;
if ( ret )
goto free_and_quit ;
2011-05-05 22:39:25 +04:00
2015-12-11 12:44:40 +03:00
ret = media_entity_pads_init ( & flash - > subdev . entity , 0 , NULL ) ;
2011-05-05 22:39:25 +04:00
if ( ret < 0 )
2011-07-28 11:59:38 +04:00
goto free_and_quit ;
2011-05-05 22:39:25 +04:00
2015-12-10 22:25:41 +03:00
flash - > subdev . entity . function = MEDIA_ENT_F_FLASH ;
2015-04-09 13:33:45 +03:00
2011-07-28 11:59:38 +04:00
return 0 ;
free_and_quit :
2015-04-09 10:42:38 +03:00
dev_err ( & client - > dev , " adp1653: failed to register device \n " ) ;
2011-07-28 11:59:38 +04:00
v4l2_ctrl_handler_free ( & flash - > ctrls ) ;
2011-05-05 22:39:25 +04:00
return ret ;
}
2013-02-26 10:17:27 +04:00
static int adp1653_remove ( struct i2c_client * client )
2011-05-05 22:39:25 +04:00
{
struct v4l2_subdev * subdev = i2c_get_clientdata ( client ) ;
struct adp1653_flash * flash = to_adp1653_flash ( subdev ) ;
v4l2_device_unregister_subdev ( & flash - > subdev ) ;
v4l2_ctrl_handler_free ( & flash - > ctrls ) ;
media_entity_cleanup ( & flash - > subdev . entity ) ;
2013-05-02 15:29:43 +04:00
2011-05-05 22:39:25 +04:00
return 0 ;
}
static const struct i2c_device_id adp1653_id_table [ ] = {
{ ADP1653_NAME , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , adp1653_id_table ) ;
2015-04-09 10:42:38 +03:00
static const struct dev_pm_ops adp1653_pm_ops = {
2011-05-05 22:39:25 +04:00
. suspend = adp1653_suspend ,
. resume = adp1653_resume ,
} ;
static struct i2c_driver adp1653_i2c_driver = {
. driver = {
. name = ADP1653_NAME ,
. pm = & adp1653_pm_ops ,
} ,
. probe = adp1653_probe ,
2013-02-26 10:17:27 +04:00
. remove = adp1653_remove ,
2011-05-05 22:39:25 +04:00
. id_table = adp1653_id_table ,
} ;
2012-02-12 13:56:32 +04:00
module_i2c_driver ( adp1653_i2c_driver ) ;
2011-05-05 22:39:25 +04:00
MODULE_AUTHOR ( " Sakari Ailus <sakari.ailus@nokia.com> " ) ;
MODULE_DESCRIPTION ( " Analog Devices ADP1653 LED flash driver " ) ;
MODULE_LICENSE ( " GPL " ) ;