2011-03-23 02:30:16 +03:00
/*
* Copyright ( C ) 2011 ST - Ericsson SA .
* Copyright ( C ) 2009 Motorola , Inc .
*
* License Terms : GNU General Public License v2
*
* Simple driver for National Semiconductor LM3530 Backlight driver chip
*
* Author : Shreshtha Kumar SAHU < shreshthakumar . sahu @ stericsson . com >
* based on leds - lm3530 . c by Dan Murphy < D . Murphy @ motorola . com >
*/
# include <linux/i2c.h>
# include <linux/leds.h>
# include <linux/slab.h>
# include <linux/platform_device.h>
# include <linux/input.h>
# include <linux/led-lm3530.h>
# include <linux/types.h>
2011-05-25 04:13:28 +04:00
# include <linux/regulator/consumer.h>
2011-07-03 21:56:03 +04:00
# include <linux/module.h>
2011-03-23 02:30:16 +03:00
# define LM3530_LED_DEV "lcd-backlight"
# define LM3530_NAME "lm3530-led"
# define LM3530_GEN_CONFIG 0x10
# define LM3530_ALS_CONFIG 0x20
# define LM3530_BRT_RAMP_RATE 0x30
# define LM3530_ALS_IMP_SELECT 0x41
# define LM3530_BRT_CTRL_REG 0xA0
# define LM3530_ALS_ZB0_REG 0x60
# define LM3530_ALS_ZB1_REG 0x61
# define LM3530_ALS_ZB2_REG 0x62
# define LM3530_ALS_ZB3_REG 0x63
# define LM3530_ALS_Z0T_REG 0x70
# define LM3530_ALS_Z1T_REG 0x71
# define LM3530_ALS_Z2T_REG 0x72
# define LM3530_ALS_Z3T_REG 0x73
# define LM3530_ALS_Z4T_REG 0x74
2012-03-24 02:02:12 +04:00
# define LM3530_REG_MAX 14
2011-03-23 02:30:16 +03:00
/* General Control Register */
# define LM3530_EN_I2C_SHIFT (0)
# define LM3530_RAMP_LAW_SHIFT (1)
# define LM3530_MAX_CURR_SHIFT (2)
# define LM3530_EN_PWM_SHIFT (5)
# define LM3530_PWM_POL_SHIFT (6)
# define LM3530_EN_PWM_SIMPLE_SHIFT (7)
# define LM3530_ENABLE_I2C (1 << LM3530_EN_I2C_SHIFT)
# define LM3530_ENABLE_PWM (1 << LM3530_EN_PWM_SHIFT)
# define LM3530_POL_LOW (1 << LM3530_PWM_POL_SHIFT)
# define LM3530_ENABLE_PWM_SIMPLE (1 << LM3530_EN_PWM_SIMPLE_SHIFT)
/* ALS Config Register Options */
# define LM3530_ALS_AVG_TIME_SHIFT (0)
# define LM3530_EN_ALS_SHIFT (3)
# define LM3530_ALS_SEL_SHIFT (5)
# define LM3530_ENABLE_ALS (3 << LM3530_EN_ALS_SHIFT)
/* Brightness Ramp Rate Register */
# define LM3530_BRT_RAMP_FALL_SHIFT (0)
# define LM3530_BRT_RAMP_RISE_SHIFT (3)
/* ALS Resistor Select */
# define LM3530_ALS1_IMP_SHIFT (0)
# define LM3530_ALS2_IMP_SHIFT (4)
/* Zone Boundary Register defaults */
2011-07-26 04:13:17 +04:00
# define LM3530_ALS_ZB_MAX (4)
# define LM3530_ALS_WINDOW_mV (1000)
# define LM3530_ALS_OFFSET_mV (4)
2011-03-23 02:30:16 +03:00
/* Zone Target Register defaults */
2011-07-26 04:13:17 +04:00
# define LM3530_DEF_ZT_0 (0x7F)
# define LM3530_DEF_ZT_1 (0x66)
2011-03-23 02:30:16 +03:00
# define LM3530_DEF_ZT_2 (0x4C)
2011-07-26 04:13:17 +04:00
# define LM3530_DEF_ZT_3 (0x33)
# define LM3530_DEF_ZT_4 (0x19)
2011-03-23 02:30:16 +03:00
2012-03-24 02:02:11 +04:00
/* 7 bits are used for the brightness : LM3530_BRT_CTRL_REG */
# define MAX_BRIGHTNESS (127)
2011-03-23 02:30:16 +03:00
struct lm3530_mode_map {
const char * mode ;
enum lm3530_mode mode_val ;
} ;
static struct lm3530_mode_map mode_map [ ] = {
{ " man " , LM3530_BL_MODE_MANUAL } ,
{ " als " , LM3530_BL_MODE_ALS } ,
{ " pwm " , LM3530_BL_MODE_PWM } ,
} ;
/**
* struct lm3530_data
* @ led_dev : led class device
* @ client : i2c client
* @ pdata : LM3530 platform data
* @ mode : mode of operation - manual , ALS , PWM
2011-05-25 04:13:28 +04:00
* @ regulator : regulator
* @ brighness : previous brightness value
* @ enable : regulator is enabled
2011-03-23 02:30:16 +03:00
*/
struct lm3530_data {
struct led_classdev led_dev ;
struct i2c_client * client ;
struct lm3530_platform_data * pdata ;
enum lm3530_mode mode ;
2011-05-25 04:13:28 +04:00
struct regulator * regulator ;
enum led_brightness brightness ;
bool enable ;
2011-03-23 02:30:16 +03:00
} ;
2012-05-30 02:07:29 +04:00
/*
* struct lm3530_als_data
* @ config : value of ALS configuration register
* @ imp_sel : value of ALS resistor select register
* @ zone : values of ALS ZB ( Zone Boundary ) registers
*/
struct lm3530_als_data {
u8 config ;
u8 imp_sel ;
u8 zones [ LM3530_ALS_ZB_MAX ] ;
} ;
2011-03-23 02:30:16 +03:00
static const u8 lm3530_reg [ LM3530_REG_MAX ] = {
LM3530_GEN_CONFIG ,
LM3530_ALS_CONFIG ,
LM3530_BRT_RAMP_RATE ,
LM3530_ALS_IMP_SELECT ,
LM3530_BRT_CTRL_REG ,
LM3530_ALS_ZB0_REG ,
LM3530_ALS_ZB1_REG ,
LM3530_ALS_ZB2_REG ,
LM3530_ALS_ZB3_REG ,
LM3530_ALS_Z0T_REG ,
LM3530_ALS_Z1T_REG ,
LM3530_ALS_Z2T_REG ,
LM3530_ALS_Z3T_REG ,
LM3530_ALS_Z4T_REG ,
} ;
static int lm3530_get_mode_from_str ( const char * str )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( mode_map ) ; i + + )
if ( sysfs_streq ( str , mode_map [ i ] . mode ) )
return mode_map [ i ] . mode_val ;
2012-09-12 10:34:09 +04:00
return - EINVAL ;
2011-03-23 02:30:16 +03:00
}
2012-05-30 02:07:29 +04:00
static void lm3530_als_configure ( struct lm3530_platform_data * pdata ,
struct lm3530_als_data * als )
{
int i ;
u32 als_vmin , als_vmax , als_vstep ;
if ( pdata - > als_vmax = = 0 ) {
pdata - > als_vmin = 0 ;
pdata - > als_vmax = LM3530_ALS_WINDOW_mV ;
}
als_vmin = pdata - > als_vmin ;
als_vmax = pdata - > als_vmax ;
if ( ( als_vmax - als_vmin ) > LM3530_ALS_WINDOW_mV )
pdata - > als_vmax = als_vmax = als_vmin + LM3530_ALS_WINDOW_mV ;
/* n zone boundary makes n+1 zones */
als_vstep = ( als_vmax - als_vmin ) / ( LM3530_ALS_ZB_MAX + 1 ) ;
for ( i = 0 ; i < LM3530_ALS_ZB_MAX ; i + + )
als - > zones [ i ] = ( ( ( als_vmin + LM3530_ALS_OFFSET_mV ) +
als_vstep + ( i * als_vstep ) ) * LED_FULL ) / 1000 ;
als - > config =
( pdata - > als_avrg_time < < LM3530_ALS_AVG_TIME_SHIFT ) |
( LM3530_ENABLE_ALS ) |
( pdata - > als_input_mode < < LM3530_ALS_SEL_SHIFT ) ;
als - > imp_sel =
( pdata - > als1_resistor_sel < < LM3530_ALS1_IMP_SHIFT ) |
( pdata - > als2_resistor_sel < < LM3530_ALS2_IMP_SHIFT ) ;
}
2012-12-20 04:59:43 +04:00
static int lm3530_led_enable ( struct lm3530_data * drvdata )
{
int ret ;
if ( drvdata - > enable )
return 0 ;
ret = regulator_enable ( drvdata - > regulator ) ;
if ( ret ) {
dev_err ( drvdata - > led_dev . dev , " Failed to enable vin:%d \n " , ret ) ;
return ret ;
}
drvdata - > enable = true ;
return 0 ;
}
static void lm3530_led_disable ( struct lm3530_data * drvdata )
{
int ret ;
if ( ! drvdata - > enable )
return ;
ret = regulator_disable ( drvdata - > regulator ) ;
if ( ret ) {
dev_err ( drvdata - > led_dev . dev , " Failed to disable vin:%d \n " ,
ret ) ;
return ;
}
drvdata - > enable = false ;
}
2011-03-23 02:30:16 +03:00
static int lm3530_init_registers ( struct lm3530_data * drvdata )
{
int ret = 0 ;
int i ;
u8 gen_config ;
u8 brt_ramp ;
u8 brightness ;
u8 reg_val [ LM3530_REG_MAX ] ;
2012-03-24 02:02:13 +04:00
struct lm3530_platform_data * pdata = drvdata - > pdata ;
2011-03-23 02:30:16 +03:00
struct i2c_client * client = drvdata - > client ;
2012-03-24 02:02:13 +04:00
struct lm3530_pwm_data * pwm = & pdata - > pwm_data ;
2012-05-30 02:07:29 +04:00
struct lm3530_als_data als ;
memset ( & als , 0 , sizeof ( struct lm3530_als_data ) ) ;
2011-03-23 02:30:16 +03:00
2012-03-24 02:02:13 +04:00
gen_config = ( pdata - > brt_ramp_law < < LM3530_RAMP_LAW_SHIFT ) |
( ( pdata - > max_current & 7 ) < < LM3530_MAX_CURR_SHIFT ) ;
2011-03-23 02:30:16 +03:00
2012-03-24 02:02:13 +04:00
switch ( drvdata - > mode ) {
case LM3530_BL_MODE_MANUAL :
2012-05-30 02:07:29 +04:00
gen_config | = LM3530_ENABLE_I2C ;
break ;
2012-03-24 02:02:13 +04:00
case LM3530_BL_MODE_ALS :
gen_config | = LM3530_ENABLE_I2C ;
2012-05-30 02:07:29 +04:00
lm3530_als_configure ( pdata , & als ) ;
2012-03-24 02:02:13 +04:00
break ;
case LM3530_BL_MODE_PWM :
gen_config | = LM3530_ENABLE_PWM | LM3530_ENABLE_PWM_SIMPLE |
( pdata - > pwm_pol_hi < < LM3530_PWM_POL_SHIFT ) ;
break ;
}
2011-03-23 02:30:16 +03:00
2012-03-24 02:02:13 +04:00
brt_ramp = ( pdata - > brt_ramp_fall < < LM3530_BRT_RAMP_FALL_SHIFT ) |
( pdata - > brt_ramp_rise < < LM3530_BRT_RAMP_RISE_SHIFT ) ;
2011-03-23 02:30:16 +03:00
2011-05-25 04:13:28 +04:00
if ( drvdata - > brightness )
brightness = drvdata - > brightness ;
else
2012-03-24 02:02:13 +04:00
brightness = drvdata - > brightness = pdata - > brt_val ;
2011-03-23 02:30:16 +03:00
2012-03-24 02:02:11 +04:00
if ( brightness > drvdata - > led_dev . max_brightness )
brightness = drvdata - > led_dev . max_brightness ;
2011-03-23 02:30:16 +03:00
reg_val [ 0 ] = gen_config ; /* LM3530_GEN_CONFIG */
2012-05-30 02:07:29 +04:00
reg_val [ 1 ] = als . config ; /* LM3530_ALS_CONFIG */
2011-03-23 02:30:16 +03:00
reg_val [ 2 ] = brt_ramp ; /* LM3530_BRT_RAMP_RATE */
2012-05-30 02:07:29 +04:00
reg_val [ 3 ] = als . imp_sel ; /* LM3530_ALS_IMP_SELECT */
2012-03-24 02:02:12 +04:00
reg_val [ 4 ] = brightness ; /* LM3530_BRT_CTRL_REG */
2012-05-30 02:07:29 +04:00
reg_val [ 5 ] = als . zones [ 0 ] ; /* LM3530_ALS_ZB0_REG */
reg_val [ 6 ] = als . zones [ 1 ] ; /* LM3530_ALS_ZB1_REG */
reg_val [ 7 ] = als . zones [ 2 ] ; /* LM3530_ALS_ZB2_REG */
reg_val [ 8 ] = als . zones [ 3 ] ; /* LM3530_ALS_ZB3_REG */
2012-03-24 02:02:12 +04:00
reg_val [ 9 ] = LM3530_DEF_ZT_0 ; /* LM3530_ALS_Z0T_REG */
reg_val [ 10 ] = LM3530_DEF_ZT_1 ; /* LM3530_ALS_Z1T_REG */
reg_val [ 11 ] = LM3530_DEF_ZT_2 ; /* LM3530_ALS_Z2T_REG */
reg_val [ 12 ] = LM3530_DEF_ZT_3 ; /* LM3530_ALS_Z3T_REG */
reg_val [ 13 ] = LM3530_DEF_ZT_4 ; /* LM3530_ALS_Z4T_REG */
2011-03-23 02:30:16 +03:00
2012-12-20 04:59:43 +04:00
ret = lm3530_led_enable ( drvdata ) ;
if ( ret )
return ret ;
2011-05-25 04:13:28 +04:00
2011-03-23 02:30:16 +03:00
for ( i = 0 ; i < LM3530_REG_MAX ; i + + ) {
2012-03-24 02:02:12 +04:00
/* do not update brightness register when pwm mode */
if ( lm3530_reg [ i ] = = LM3530_BRT_CTRL_REG & &
drvdata - > mode = = LM3530_BL_MODE_PWM ) {
if ( pwm - > pwm_set_intensity )
pwm - > pwm_set_intensity ( reg_val [ i ] ,
drvdata - > led_dev . max_brightness ) ;
continue ;
}
2011-03-23 02:30:16 +03:00
ret = i2c_smbus_write_byte_data ( client ,
lm3530_reg [ i ] , reg_val [ i ] ) ;
if ( ret )
break ;
}
return ret ;
}
static void lm3530_brightness_set ( struct led_classdev * led_cdev ,
enum led_brightness brt_val )
{
int err ;
struct lm3530_data * drvdata =
container_of ( led_cdev , struct lm3530_data , led_dev ) ;
2012-03-24 02:02:12 +04:00
struct lm3530_platform_data * pdata = drvdata - > pdata ;
struct lm3530_pwm_data * pwm = & pdata - > pwm_data ;
u8 max_brightness = led_cdev - > max_brightness ;
2011-03-23 02:30:16 +03:00
switch ( drvdata - > mode ) {
case LM3530_BL_MODE_MANUAL :
2011-05-25 04:13:28 +04:00
if ( ! drvdata - > enable ) {
err = lm3530_init_registers ( drvdata ) ;
if ( err ) {
dev_err ( & drvdata - > client - > dev ,
" Register Init failed: %d \n " , err ) ;
break ;
}
}
2011-03-23 02:30:16 +03:00
/* set the brightness in brightness control register*/
err = i2c_smbus_write_byte_data ( drvdata - > client ,
2012-03-24 02:02:11 +04:00
LM3530_BRT_CTRL_REG , brt_val ) ;
2011-03-23 02:30:16 +03:00
if ( err )
dev_err ( & drvdata - > client - > dev ,
" Unable to set brightness: %d \n " , err ) ;
2011-05-25 04:13:28 +04:00
else
2012-03-24 02:02:11 +04:00
drvdata - > brightness = brt_val ;
2011-05-25 04:13:28 +04:00
2012-12-20 04:59:43 +04:00
if ( brt_val = = 0 )
lm3530_led_disable ( drvdata ) ;
2011-03-23 02:30:16 +03:00
break ;
case LM3530_BL_MODE_ALS :
break ;
case LM3530_BL_MODE_PWM :
2012-03-24 02:02:12 +04:00
if ( pwm - > pwm_set_intensity )
pwm - > pwm_set_intensity ( brt_val , max_brightness ) ;
2011-03-23 02:30:16 +03:00
break ;
default :
break ;
}
}
2011-07-26 04:13:17 +04:00
static ssize_t lm3530_mode_get ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2012-03-24 02:02:11 +04:00
struct led_classdev * led_cdev = dev_get_drvdata ( dev ) ;
struct lm3530_data * drvdata ;
2011-07-26 04:13:17 +04:00
int i , len = 0 ;
2012-03-24 02:02:11 +04:00
drvdata = container_of ( led_cdev , struct lm3530_data , led_dev ) ;
2011-07-26 04:13:17 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( mode_map ) ; i + + )
if ( drvdata - > mode = = mode_map [ i ] . mode_val )
len + = sprintf ( buf + len , " [%s] " , mode_map [ i ] . mode ) ;
else
len + = sprintf ( buf + len , " %s " , mode_map [ i ] . mode ) ;
len + = sprintf ( buf + len , " \n " ) ;
return len ;
}
2011-03-23 02:30:16 +03:00
static ssize_t lm3530_mode_set ( struct device * dev , struct device_attribute
* attr , const char * buf , size_t size )
{
2012-03-24 02:02:11 +04:00
struct led_classdev * led_cdev = dev_get_drvdata ( dev ) ;
struct lm3530_data * drvdata ;
2012-03-24 02:02:12 +04:00
struct lm3530_pwm_data * pwm ;
u8 max_brightness ;
2012-03-24 02:02:11 +04:00
int mode , err ;
2011-03-23 02:30:16 +03:00
2012-03-24 02:02:11 +04:00
drvdata = container_of ( led_cdev , struct lm3530_data , led_dev ) ;
2012-03-24 02:02:12 +04:00
pwm = & drvdata - > pdata - > pwm_data ;
max_brightness = led_cdev - > max_brightness ;
2011-03-23 02:30:16 +03:00
mode = lm3530_get_mode_from_str ( buf ) ;
if ( mode < 0 ) {
dev_err ( dev , " Invalid mode \n " ) ;
2012-09-12 10:34:09 +04:00
return mode ;
2011-03-23 02:30:16 +03:00
}
2012-03-24 02:02:12 +04:00
drvdata - > mode = mode ;
/* set pwm to low if unnecessary */
if ( mode ! = LM3530_BL_MODE_PWM & & pwm - > pwm_set_intensity )
pwm - > pwm_set_intensity ( 0 , max_brightness ) ;
2011-03-23 02:30:16 +03:00
err = lm3530_init_registers ( drvdata ) ;
if ( err ) {
dev_err ( dev , " Setting %s Mode failed :%d \n " , buf , err ) ;
return err ;
}
return sizeof ( drvdata - > mode ) ;
}
2011-07-26 04:13:17 +04:00
static DEVICE_ATTR ( mode , 0644 , lm3530_mode_get , lm3530_mode_set ) ;
2011-03-23 02:30:16 +03:00
2014-06-25 21:08:45 +04:00
static struct attribute * lm3530_attrs [ ] = {
& dev_attr_mode . attr ,
NULL
} ;
ATTRIBUTE_GROUPS ( lm3530 ) ;
2012-11-19 22:23:02 +04:00
static int lm3530_probe ( struct i2c_client * client ,
2011-03-23 02:30:16 +03:00
const struct i2c_device_id * id )
{
2013-07-30 12:07:35 +04:00
struct lm3530_platform_data * pdata = dev_get_platdata ( & client - > dev ) ;
2011-03-23 02:30:16 +03:00
struct lm3530_data * drvdata ;
int err = 0 ;
if ( pdata = = NULL ) {
dev_err ( & client - > dev , " platform data required \n " ) ;
2012-07-04 08:24:40 +04:00
return - ENODEV ;
2011-03-23 02:30:16 +03:00
}
/* BL mode */
if ( pdata - > mode > LM3530_BL_MODE_PWM ) {
dev_err ( & client - > dev , " Illegal Mode request \n " ) ;
2012-07-04 08:24:40 +04:00
return - EINVAL ;
2011-03-23 02:30:16 +03:00
}
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
dev_err ( & client - > dev , " I2C_FUNC_I2C not supported \n " ) ;
2012-07-04 08:24:40 +04:00
return - EIO ;
2011-03-23 02:30:16 +03:00
}
2012-07-04 08:24:40 +04:00
drvdata = devm_kzalloc ( & client - > dev , sizeof ( struct lm3530_data ) ,
GFP_KERNEL ) ;
if ( drvdata = = NULL )
return - ENOMEM ;
2011-03-23 02:30:16 +03:00
drvdata - > mode = pdata - > mode ;
drvdata - > client = client ;
drvdata - > pdata = pdata ;
2011-05-25 04:13:28 +04:00
drvdata - > brightness = LED_OFF ;
drvdata - > enable = false ;
2011-03-23 02:30:16 +03:00
drvdata - > led_dev . name = LM3530_LED_DEV ;
drvdata - > led_dev . brightness_set = lm3530_brightness_set ;
2012-03-24 02:02:11 +04:00
drvdata - > led_dev . max_brightness = MAX_BRIGHTNESS ;
2014-06-25 21:08:45 +04:00
drvdata - > led_dev . groups = lm3530_groups ;
2011-03-23 02:30:16 +03:00
i2c_set_clientdata ( client , drvdata ) ;
2012-09-11 15:42:10 +04:00
drvdata - > regulator = devm_regulator_get ( & client - > dev , " vin " ) ;
2011-05-25 04:13:28 +04:00
if ( IS_ERR ( drvdata - > regulator ) ) {
dev_err ( & client - > dev , " regulator get failed \n " ) ;
err = PTR_ERR ( drvdata - > regulator ) ;
drvdata - > regulator = NULL ;
2012-07-04 08:24:40 +04:00
return err ;
2011-03-23 02:30:16 +03:00
}
2011-05-25 04:13:28 +04:00
if ( drvdata - > pdata - > brt_val ) {
err = lm3530_init_registers ( drvdata ) ;
if ( err < 0 ) {
dev_err ( & client - > dev ,
" Register Init failed: %d \n " , err ) ;
2012-09-12 10:34:09 +04:00
return err ;
2011-05-25 04:13:28 +04:00
}
}
err = led_classdev_register ( & client - > dev , & drvdata - > led_dev ) ;
2011-03-23 02:30:16 +03:00
if ( err < 0 ) {
dev_err ( & client - > dev , " Register led class failed: %d \n " , err ) ;
2012-09-12 10:34:09 +04:00
return err ;
2011-03-23 02:30:16 +03:00
}
return 0 ;
}
2012-11-19 22:26:00 +04:00
static int lm3530_remove ( struct i2c_client * client )
2011-03-23 02:30:16 +03:00
{
struct lm3530_data * drvdata = i2c_get_clientdata ( client ) ;
2012-12-20 04:59:43 +04:00
lm3530_led_disable ( drvdata ) ;
2011-03-23 02:30:16 +03:00
led_classdev_unregister ( & drvdata - > led_dev ) ;
return 0 ;
}
static const struct i2c_device_id lm3530_id [ ] = {
{ LM3530_NAME , 0 } ,
{ }
} ;
2011-05-18 02:44:09 +04:00
MODULE_DEVICE_TABLE ( i2c , lm3530_id ) ;
2011-03-23 02:30:16 +03:00
static struct i2c_driver lm3530_i2c_driver = {
. probe = lm3530_probe ,
2012-11-19 22:20:20 +04:00
. remove = lm3530_remove ,
2011-03-23 02:30:16 +03:00
. id_table = lm3530_id ,
. driver = {
. name = LM3530_NAME ,
} ,
} ;
2012-01-11 03:09:27 +04:00
module_i2c_driver ( lm3530_i2c_driver ) ;
2011-03-23 02:30:16 +03:00
MODULE_DESCRIPTION ( " Back Light driver for LM3530 " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com> " ) ;