diff options
Diffstat (limited to 'drivers/leds/leds-lm3530.c')
| -rw-r--r-- | drivers/leds/leds-lm3530.c | 343 |
1 files changed, 237 insertions, 106 deletions
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index e7089a1f6cb..652368c2ea9 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -17,6 +17,8 @@ #include <linux/input.h> #include <linux/led-lm3530.h> #include <linux/types.h> +#include <linux/regulator/consumer.h> +#include <linux/module.h> #define LM3530_LED_DEV "lcd-backlight" #define LM3530_NAME "lm3530-led" @@ -24,7 +26,6 @@ #define LM3530_GEN_CONFIG 0x10 #define LM3530_ALS_CONFIG 0x20 #define LM3530_BRT_RAMP_RATE 0x30 -#define LM3530_ALS_ZONE_REG 0x40 #define LM3530_ALS_IMP_SELECT 0x41 #define LM3530_BRT_CTRL_REG 0xA0 #define LM3530_ALS_ZB0_REG 0x60 @@ -36,7 +37,7 @@ #define LM3530_ALS_Z2T_REG 0x72 #define LM3530_ALS_Z3T_REG 0x73 #define LM3530_ALS_Z4T_REG 0x74 -#define LM3530_REG_MAX 15 +#define LM3530_REG_MAX 14 /* General Control Register */ #define LM3530_EN_I2C_SHIFT (0) @@ -67,17 +68,19 @@ #define LM3530_ALS2_IMP_SHIFT (4) /* Zone Boundary Register defaults */ -#define LM3530_DEF_ZB_0 (0x33) -#define LM3530_DEF_ZB_1 (0x66) -#define LM3530_DEF_ZB_2 (0x99) -#define LM3530_DEF_ZB_3 (0xCC) +#define LM3530_ALS_ZB_MAX (4) +#define LM3530_ALS_WINDOW_mV (1000) +#define LM3530_ALS_OFFSET_mV (4) /* Zone Target Register defaults */ -#define LM3530_DEF_ZT_0 (0x19) -#define LM3530_DEF_ZT_1 (0x33) +#define LM3530_DEF_ZT_0 (0x7F) +#define LM3530_DEF_ZT_1 (0x66) #define LM3530_DEF_ZT_2 (0x4C) -#define LM3530_DEF_ZT_3 (0x66) -#define LM3530_DEF_ZT_4 (0x7F) +#define LM3530_DEF_ZT_3 (0x33) +#define LM3530_DEF_ZT_4 (0x19) + +/* 7 bits are used for the brightness : LM3530_BRT_CTRL_REG */ +#define MAX_BRIGHTNESS (127) struct lm3530_mode_map { const char *mode; @@ -96,19 +99,36 @@ static struct lm3530_mode_map mode_map[] = { * @client: i2c client * @pdata: LM3530 platform data * @mode: mode of operation - manual, ALS, PWM + * @regulator: regulator + * @brighness: previous brightness value + * @enable: regulator is enabled */ struct lm3530_data { struct led_classdev led_dev; struct i2c_client *client; struct lm3530_platform_data *pdata; enum lm3530_mode mode; + struct regulator *regulator; + enum led_brightness brightness; + bool enable; +}; + +/* + * 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]; }; static const u8 lm3530_reg[LM3530_REG_MAX] = { LM3530_GEN_CONFIG, LM3530_ALS_CONFIG, LM3530_BRT_RAMP_RATE, - LM3530_ALS_ZONE_REG, LM3530_ALS_IMP_SELECT, LM3530_BRT_CTRL_REG, LM3530_ALS_ZB0_REG, @@ -130,7 +150,75 @@ static int lm3530_get_mode_from_str(const char *str) if (sysfs_streq(str, mode_map[i].mode)) return mode_map[i].mode_val; - return -1; + return -EINVAL; +} + +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); +} + +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; } static int lm3530_init_registers(struct lm3530_data *drvdata) @@ -138,59 +226,73 @@ static int lm3530_init_registers(struct lm3530_data *drvdata) int ret = 0; int i; u8 gen_config; - u8 als_config = 0; u8 brt_ramp; - u8 als_imp_sel = 0; u8 brightness; u8 reg_val[LM3530_REG_MAX]; - struct lm3530_platform_data *pltfm = drvdata->pdata; + struct lm3530_platform_data *pdata = drvdata->pdata; struct i2c_client *client = drvdata->client; + struct lm3530_pwm_data *pwm = &pdata->pwm_data; + struct lm3530_als_data als; - gen_config = (pltfm->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) | - ((pltfm->max_current & 7) << LM3530_MAX_CURR_SHIFT); + memset(&als, 0, sizeof(struct lm3530_als_data)); - if (drvdata->mode == LM3530_BL_MODE_MANUAL || - drvdata->mode == LM3530_BL_MODE_ALS) - gen_config |= (LM3530_ENABLE_I2C); + gen_config = (pdata->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) | + ((pdata->max_current & 7) << LM3530_MAX_CURR_SHIFT); - if (drvdata->mode == LM3530_BL_MODE_ALS) { - als_config = - (pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) | - (LM3530_ENABLE_ALS) | - (pltfm->als_input_mode << LM3530_ALS_SEL_SHIFT); - - als_imp_sel = - (pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) | - (pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT); + switch (drvdata->mode) { + case LM3530_BL_MODE_MANUAL: + gen_config |= LM3530_ENABLE_I2C; + break; + case LM3530_BL_MODE_ALS: + gen_config |= LM3530_ENABLE_I2C; + lm3530_als_configure(pdata, &als); + break; + case LM3530_BL_MODE_PWM: + gen_config |= LM3530_ENABLE_PWM | LM3530_ENABLE_PWM_SIMPLE | + (pdata->pwm_pol_hi << LM3530_PWM_POL_SHIFT); + break; } - if (drvdata->mode == LM3530_BL_MODE_PWM) - gen_config |= (LM3530_ENABLE_PWM) | - (pltfm->pwm_pol_hi << LM3530_PWM_POL_SHIFT) | - (LM3530_ENABLE_PWM_SIMPLE); + brt_ramp = (pdata->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) | + (pdata->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT); - brt_ramp = (pltfm->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) | - (pltfm->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT); + if (drvdata->brightness) + brightness = drvdata->brightness; + else + brightness = drvdata->brightness = pdata->brt_val; - brightness = pltfm->brt_val; + if (brightness > drvdata->led_dev.max_brightness) + brightness = drvdata->led_dev.max_brightness; reg_val[0] = gen_config; /* LM3530_GEN_CONFIG */ - reg_val[1] = als_config; /* LM3530_ALS_CONFIG */ + reg_val[1] = als.config; /* LM3530_ALS_CONFIG */ reg_val[2] = brt_ramp; /* LM3530_BRT_RAMP_RATE */ - reg_val[3] = 0x00; /* LM3530_ALS_ZONE_REG */ - reg_val[4] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */ - reg_val[5] = brightness; /* LM3530_BRT_CTRL_REG */ - reg_val[6] = LM3530_DEF_ZB_0; /* LM3530_ALS_ZB0_REG */ - reg_val[7] = LM3530_DEF_ZB_1; /* LM3530_ALS_ZB1_REG */ - reg_val[8] = LM3530_DEF_ZB_2; /* LM3530_ALS_ZB2_REG */ - reg_val[9] = LM3530_DEF_ZB_3; /* LM3530_ALS_ZB3_REG */ - reg_val[10] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */ - reg_val[11] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */ - reg_val[12] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */ - reg_val[13] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */ - reg_val[14] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */ + reg_val[3] = als.imp_sel; /* LM3530_ALS_IMP_SELECT */ + reg_val[4] = brightness; /* LM3530_BRT_CTRL_REG */ + 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 */ + 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 */ + + ret = lm3530_led_enable(drvdata); + if (ret) + return ret; for (i = 0; i < LM3530_REG_MAX; i++) { + /* 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; + } + ret = i2c_smbus_write_byte_data(client, lm3530_reg[i], reg_val[i]); if (ret) @@ -206,50 +308,87 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev, int err; struct lm3530_data *drvdata = container_of(led_cdev, struct lm3530_data, led_dev); + struct lm3530_platform_data *pdata = drvdata->pdata; + struct lm3530_pwm_data *pwm = &pdata->pwm_data; + u8 max_brightness = led_cdev->max_brightness; switch (drvdata->mode) { case LM3530_BL_MODE_MANUAL: + if (!drvdata->enable) { + err = lm3530_init_registers(drvdata); + if (err) { + dev_err(&drvdata->client->dev, + "Register Init failed: %d\n", err); + break; + } + } + /* set the brightness in brightness control register*/ err = i2c_smbus_write_byte_data(drvdata->client, - LM3530_BRT_CTRL_REG, brt_val / 2); + LM3530_BRT_CTRL_REG, brt_val); if (err) dev_err(&drvdata->client->dev, "Unable to set brightness: %d\n", err); + else + drvdata->brightness = brt_val; + + if (brt_val == 0) + lm3530_led_disable(drvdata); break; case LM3530_BL_MODE_ALS: break; case LM3530_BL_MODE_PWM: + if (pwm->pwm_set_intensity) + pwm->pwm_set_intensity(brt_val, max_brightness); break; default: break; } } +static ssize_t lm3530_mode_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lm3530_data *drvdata; + int i, len = 0; + + drvdata = container_of(led_cdev, struct lm3530_data, led_dev); + 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; +} static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - int err; - struct i2c_client *client = container_of( - dev->parent, struct i2c_client, dev); - struct lm3530_data *drvdata = i2c_get_clientdata(client); - int mode; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lm3530_data *drvdata; + struct lm3530_pwm_data *pwm; + u8 max_brightness; + int mode, err; + drvdata = container_of(led_cdev, struct lm3530_data, led_dev); + pwm = &drvdata->pdata->pwm_data; + max_brightness = led_cdev->max_brightness; mode = lm3530_get_mode_from_str(buf); if (mode < 0) { dev_err(dev, "Invalid mode\n"); - return -EINVAL; + return mode; } - if (mode == LM3530_BL_MODE_MANUAL) - drvdata->mode = LM3530_BL_MODE_MANUAL; - else if (mode == LM3530_BL_MODE_ALS) - drvdata->mode = LM3530_BL_MODE_ALS; - else if (mode == LM3530_BL_MODE_PWM) { - dev_err(dev, "PWM mode not supported\n"); - return -EINVAL; - } + 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); err = lm3530_init_registers(drvdata); if (err) { @@ -259,62 +398,67 @@ static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute return sizeof(drvdata->mode); } +static DEVICE_ATTR(mode, 0644, lm3530_mode_get, lm3530_mode_set); -static DEVICE_ATTR(mode, 0644, NULL, lm3530_mode_set); - -static int __devinit lm3530_probe(struct i2c_client *client, +static int lm3530_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lm3530_platform_data *pdata = client->dev.platform_data; + struct lm3530_platform_data *pdata = dev_get_platdata(&client->dev); struct lm3530_data *drvdata; int err = 0; if (pdata == NULL) { dev_err(&client->dev, "platform data required\n"); - err = -ENODEV; - goto err_out; + return -ENODEV; } /* BL mode */ if (pdata->mode > LM3530_BL_MODE_PWM) { dev_err(&client->dev, "Illegal Mode request\n"); - err = -EINVAL; - goto err_out; + return -EINVAL; } if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "I2C_FUNC_I2C not supported\n"); - err = -EIO; - goto err_out; + return -EIO; } - drvdata = kzalloc(sizeof(struct lm3530_data), GFP_KERNEL); - if (drvdata == NULL) { - err = -ENOMEM; - goto err_out; - } + drvdata = devm_kzalloc(&client->dev, sizeof(struct lm3530_data), + GFP_KERNEL); + if (drvdata == NULL) + return -ENOMEM; drvdata->mode = pdata->mode; drvdata->client = client; drvdata->pdata = pdata; + drvdata->brightness = LED_OFF; + drvdata->enable = false; drvdata->led_dev.name = LM3530_LED_DEV; drvdata->led_dev.brightness_set = lm3530_brightness_set; + drvdata->led_dev.max_brightness = MAX_BRIGHTNESS; i2c_set_clientdata(client, drvdata); - err = lm3530_init_registers(drvdata); - if (err < 0) { - dev_err(&client->dev, "Register Init failed: %d\n", err); - err = -ENODEV; - goto err_reg_init; + drvdata->regulator = devm_regulator_get(&client->dev, "vin"); + if (IS_ERR(drvdata->regulator)) { + dev_err(&client->dev, "regulator get failed\n"); + err = PTR_ERR(drvdata->regulator); + drvdata->regulator = NULL; + return err; } - err = led_classdev_register((struct device *) - &client->dev, &drvdata->led_dev); + if (drvdata->pdata->brt_val) { + err = lm3530_init_registers(drvdata); + if (err < 0) { + dev_err(&client->dev, + "Register Init failed: %d\n", err); + return err; + } + } + err = led_classdev_register(&client->dev, &drvdata->led_dev); if (err < 0) { dev_err(&client->dev, "Register led class failed: %d\n", err); - err = -ENODEV; - goto err_class_register; + return err; } err = device_create_file(drvdata->led_dev.dev, &dev_attr_mode); @@ -328,20 +472,17 @@ static int __devinit lm3530_probe(struct i2c_client *client, err_create_file: led_classdev_unregister(&drvdata->led_dev); -err_class_register: -err_reg_init: - kfree(drvdata); -err_out: return err; } -static int __devexit lm3530_remove(struct i2c_client *client) +static int lm3530_remove(struct i2c_client *client) { struct lm3530_data *drvdata = i2c_get_clientdata(client); device_remove_file(drvdata->led_dev.dev, &dev_attr_mode); + + lm3530_led_disable(drvdata); led_classdev_unregister(&drvdata->led_dev); - kfree(drvdata); return 0; } @@ -349,6 +490,7 @@ static const struct i2c_device_id lm3530_id[] = { {LM3530_NAME, 0}, {} }; +MODULE_DEVICE_TABLE(i2c, lm3530_id); static struct i2c_driver lm3530_i2c_driver = { .probe = lm3530_probe, @@ -360,18 +502,7 @@ static struct i2c_driver lm3530_i2c_driver = { }, }; -static int __init lm3530_init(void) -{ - return i2c_add_driver(&lm3530_i2c_driver); -} - -static void __exit lm3530_exit(void) -{ - i2c_del_driver(&lm3530_i2c_driver); -} - -module_init(lm3530_init); -module_exit(lm3530_exit); +module_i2c_driver(lm3530_i2c_driver); MODULE_DESCRIPTION("Back Light driver for LM3530"); MODULE_LICENSE("GPL v2"); |
