2018-06-19 09:01:47 +03:00
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2018 Intel Corporation
# include <linux/acpi.h>
# include <linux/delay.h>
# include <linux/i2c.h>
# include <linux/module.h>
# include <linux/pm_runtime.h>
2022-12-09 17:37:41 +03:00
# include <linux/regulator/consumer.h>
2018-06-19 09:01:47 +03:00
# include <media/v4l2-ctrls.h>
# include <media/v4l2-device.h>
# define AK7375_MAX_FOCUS_POS 4095
/*
* This sets the minimum granularity for the focus positions .
* A value of 1 gives maximum accuracy for a desired focus position
*/
# define AK7375_FOCUS_STEPS 1
/*
* This acts as the minimum granularity of lens movement .
* Keep this value power of 2 , so the control steps can be
* uniformly adjusted for gradual lens movement , with desired
* number of control steps .
*/
# define AK7375_CTRL_STEPS 64
# define AK7375_CTRL_DELAY_US 1000
2022-12-09 17:37:41 +03:00
/*
* The vcm may take up 10 ms ( tDELAY ) to power on and start taking
* I2C messages . Based on AK7371 datasheet .
*/
# define AK7375_POWER_DELAY_US 10000
2018-06-19 09:01:47 +03:00
# define AK7375_REG_POSITION 0x0
# define AK7375_REG_CONT 0x2
# define AK7375_MODE_ACTIVE 0x0
# define AK7375_MODE_STANDBY 0x40
2022-12-09 17:37:41 +03:00
static const char * const ak7375_supply_names [ ] = {
" vdd " ,
" vio " ,
} ;
2018-06-19 09:01:47 +03:00
/* ak7375 device structure */
struct ak7375_device {
struct v4l2_ctrl_handler ctrls_vcm ;
struct v4l2_subdev sd ;
struct v4l2_ctrl * focus ;
2022-12-09 17:37:41 +03:00
struct regulator_bulk_data supplies [ ARRAY_SIZE ( ak7375_supply_names ) ] ;
2018-06-19 09:01:47 +03:00
/* active or standby mode */
bool active ;
} ;
static inline struct ak7375_device * to_ak7375_vcm ( struct v4l2_ctrl * ctrl )
{
return container_of ( ctrl - > handler , struct ak7375_device , ctrls_vcm ) ;
}
static inline struct ak7375_device * sd_to_ak7375_vcm ( struct v4l2_subdev * subdev )
{
return container_of ( subdev , struct ak7375_device , sd ) ;
}
static int ak7375_i2c_write ( struct ak7375_device * ak7375 ,
u8 addr , u16 data , u8 size )
{
struct i2c_client * client = v4l2_get_subdevdata ( & ak7375 - > sd ) ;
u8 buf [ 3 ] ;
int ret ;
if ( size ! = 1 & & size ! = 2 )
return - EINVAL ;
buf [ 0 ] = addr ;
buf [ size ] = data & 0xff ;
if ( size = = 2 )
buf [ 1 ] = ( data > > 8 ) & 0xff ;
ret = i2c_master_send ( client , ( const char * ) buf , size + 1 ) ;
if ( ret < 0 )
return ret ;
if ( ret ! = size + 1 )
return - EIO ;
return 0 ;
}
static int ak7375_set_ctrl ( struct v4l2_ctrl * ctrl )
{
struct ak7375_device * dev_vcm = to_ak7375_vcm ( ctrl ) ;
if ( ctrl - > id = = V4L2_CID_FOCUS_ABSOLUTE )
return ak7375_i2c_write ( dev_vcm , AK7375_REG_POSITION ,
ctrl - > val < < 4 , 2 ) ;
return - EINVAL ;
}
static const struct v4l2_ctrl_ops ak7375_vcm_ctrl_ops = {
. s_ctrl = ak7375_set_ctrl ,
} ;
static int ak7375_open ( struct v4l2_subdev * sd , struct v4l2_subdev_fh * fh )
{
2021-04-23 18:19:11 +03:00
return pm_runtime_resume_and_get ( sd - > dev ) ;
2018-06-19 09:01:47 +03:00
}
static int ak7375_close ( struct v4l2_subdev * sd , struct v4l2_subdev_fh * fh )
{
pm_runtime_put ( sd - > dev ) ;
return 0 ;
}
static const struct v4l2_subdev_internal_ops ak7375_int_ops = {
. open = ak7375_open ,
. close = ak7375_close ,
} ;
static const struct v4l2_subdev_ops ak7375_ops = { } ;
static void ak7375_subdev_cleanup ( struct ak7375_device * ak7375_dev )
{
v4l2_async_unregister_subdev ( & ak7375_dev - > sd ) ;
v4l2_ctrl_handler_free ( & ak7375_dev - > ctrls_vcm ) ;
media_entity_cleanup ( & ak7375_dev - > sd . entity ) ;
}
static int ak7375_init_controls ( struct ak7375_device * dev_vcm )
{
struct v4l2_ctrl_handler * hdl = & dev_vcm - > ctrls_vcm ;
const struct v4l2_ctrl_ops * ops = & ak7375_vcm_ctrl_ops ;
v4l2_ctrl_handler_init ( hdl , 1 ) ;
dev_vcm - > focus = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_FOCUS_ABSOLUTE ,
0 , AK7375_MAX_FOCUS_POS , AK7375_FOCUS_STEPS , 0 ) ;
if ( hdl - > error )
dev_err ( dev_vcm - > sd . dev , " %s fail error: 0x%x \n " ,
__func__ , hdl - > error ) ;
dev_vcm - > sd . ctrl_handler = hdl ;
return hdl - > error ;
}
static int ak7375_probe ( struct i2c_client * client )
{
struct ak7375_device * ak7375_dev ;
int ret ;
2022-12-09 17:37:41 +03:00
unsigned int i ;
2018-06-19 09:01:47 +03:00
ak7375_dev = devm_kzalloc ( & client - > dev , sizeof ( * ak7375_dev ) ,
GFP_KERNEL ) ;
if ( ! ak7375_dev )
return - ENOMEM ;
2022-12-09 17:37:41 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( ak7375_supply_names ) ; i + + )
ak7375_dev - > supplies [ i ] . supply = ak7375_supply_names [ i ] ;
ret = devm_regulator_bulk_get ( & client - > dev ,
ARRAY_SIZE ( ak7375_supply_names ) ,
ak7375_dev - > supplies ) ;
if ( ret ) {
dev_err_probe ( & client - > dev , ret , " Failed to get regulators \n " ) ;
return ret ;
}
2018-06-19 09:01:47 +03:00
v4l2_i2c_subdev_init ( & ak7375_dev - > sd , client , & ak7375_ops ) ;
ak7375_dev - > sd . flags | = V4L2_SUBDEV_FL_HAS_DEVNODE ;
ak7375_dev - > sd . internal_ops = & ak7375_int_ops ;
ak7375_dev - > sd . entity . function = MEDIA_ENT_F_LENS ;
ret = ak7375_init_controls ( ak7375_dev ) ;
if ( ret )
goto err_cleanup ;
ret = media_entity_pads_init ( & ak7375_dev - > sd . entity , 0 , NULL ) ;
if ( ret < 0 )
goto err_cleanup ;
ret = v4l2_async_register_subdev ( & ak7375_dev - > sd ) ;
if ( ret < 0 )
goto err_cleanup ;
pm_runtime_set_active ( & client - > dev ) ;
pm_runtime_enable ( & client - > dev ) ;
pm_runtime_idle ( & client - > dev ) ;
return 0 ;
err_cleanup :
v4l2_ctrl_handler_free ( & ak7375_dev - > ctrls_vcm ) ;
media_entity_cleanup ( & ak7375_dev - > sd . entity ) ;
return ret ;
}
2022-08-15 11:02:30 +03:00
static void ak7375_remove ( struct i2c_client * client )
2018-06-19 09:01:47 +03:00
{
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
struct ak7375_device * ak7375_dev = sd_to_ak7375_vcm ( sd ) ;
ak7375_subdev_cleanup ( ak7375_dev ) ;
pm_runtime_disable ( & client - > dev ) ;
pm_runtime_set_suspended ( & client - > dev ) ;
}
/*
* This function sets the vcm position , so it consumes least current
* The lens position is gradually moved in units of AK7375_CTRL_STEPS ,
* to make the movements smoothly .
*/
static int __maybe_unused ak7375_vcm_suspend ( struct device * dev )
{
2020-09-21 19:23:28 +03:00
struct v4l2_subdev * sd = dev_get_drvdata ( dev ) ;
2018-06-19 09:01:47 +03:00
struct ak7375_device * ak7375_dev = sd_to_ak7375_vcm ( sd ) ;
int ret , val ;
if ( ! ak7375_dev - > active )
return 0 ;
for ( val = ak7375_dev - > focus - > val & ~ ( AK7375_CTRL_STEPS - 1 ) ;
val > = 0 ; val - = AK7375_CTRL_STEPS ) {
ret = ak7375_i2c_write ( ak7375_dev , AK7375_REG_POSITION ,
val < < 4 , 2 ) ;
if ( ret )
dev_err_once ( dev , " %s I2C failure: %d \n " ,
__func__ , ret ) ;
usleep_range ( AK7375_CTRL_DELAY_US , AK7375_CTRL_DELAY_US + 10 ) ;
}
ret = ak7375_i2c_write ( ak7375_dev , AK7375_REG_CONT ,
AK7375_MODE_STANDBY , 1 ) ;
if ( ret )
dev_err ( dev , " %s I2C failure: %d \n " , __func__ , ret ) ;
2022-12-09 17:37:41 +03:00
ret = regulator_bulk_disable ( ARRAY_SIZE ( ak7375_supply_names ) ,
ak7375_dev - > supplies ) ;
if ( ret )
return ret ;
2018-06-19 09:01:47 +03:00
ak7375_dev - > active = false ;
return 0 ;
}
/*
* This function sets the vcm position to the value set by the user
* through v4l2_ctrl_ops s_ctrl handler
* The lens position is gradually moved in units of AK7375_CTRL_STEPS ,
* to make the movements smoothly .
*/
static int __maybe_unused ak7375_vcm_resume ( struct device * dev )
{
2020-09-21 19:23:28 +03:00
struct v4l2_subdev * sd = dev_get_drvdata ( dev ) ;
2018-06-19 09:01:47 +03:00
struct ak7375_device * ak7375_dev = sd_to_ak7375_vcm ( sd ) ;
int ret , val ;
if ( ak7375_dev - > active )
return 0 ;
2022-12-09 17:37:41 +03:00
ret = regulator_bulk_enable ( ARRAY_SIZE ( ak7375_supply_names ) ,
ak7375_dev - > supplies ) ;
if ( ret )
return ret ;
/* Wait for vcm to become ready */
usleep_range ( AK7375_POWER_DELAY_US , AK7375_POWER_DELAY_US + 500 ) ;
2018-06-19 09:01:47 +03:00
ret = ak7375_i2c_write ( ak7375_dev , AK7375_REG_CONT ,
AK7375_MODE_ACTIVE , 1 ) ;
if ( ret ) {
dev_err ( dev , " %s I2C failure: %d \n " , __func__ , ret ) ;
return ret ;
}
for ( val = ak7375_dev - > focus - > val % AK7375_CTRL_STEPS ;
val < = ak7375_dev - > focus - > val ;
val + = AK7375_CTRL_STEPS ) {
ret = ak7375_i2c_write ( ak7375_dev , AK7375_REG_POSITION ,
val < < 4 , 2 ) ;
if ( ret )
dev_err_ratelimited ( dev , " %s I2C failure: %d \n " ,
__func__ , ret ) ;
usleep_range ( AK7375_CTRL_DELAY_US , AK7375_CTRL_DELAY_US + 10 ) ;
}
ak7375_dev - > active = true ;
return 0 ;
}
static const struct of_device_id ak7375_of_table [ ] = {
{ . compatible = " asahi-kasei,ak7375 " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , ak7375_of_table ) ;
static const struct dev_pm_ops ak7375_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( ak7375_vcm_suspend , ak7375_vcm_resume )
SET_RUNTIME_PM_OPS ( ak7375_vcm_suspend , ak7375_vcm_resume , NULL )
} ;
static struct i2c_driver ak7375_i2c_driver = {
. driver = {
. name = " ak7375 " ,
. pm = & ak7375_pm_ops ,
. of_match_table = ak7375_of_table ,
} ,
2023-05-14 15:04:07 +03:00
. probe = ak7375_probe ,
2018-06-19 09:01:47 +03:00
. remove = ak7375_remove ,
} ;
module_i2c_driver ( ak7375_i2c_driver ) ;
MODULE_AUTHOR ( " Tianshu Qiu <tian.shu.qiu@intel.com> " ) ;
MODULE_AUTHOR ( " Bingbu Cao <bingbu.cao@intel.com> " ) ;
MODULE_DESCRIPTION ( " AK7375 VCM driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;