500 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2011 ST-Ericsson SA.4 * Copyright (C) 2009 Motorola, Inc.5 *6 * Simple driver for National Semiconductor LM3530 Backlight driver chip7 *8 * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>9 * based on leds-lm3530.c by Dan Murphy <D.Murphy@motorola.com>10 */11 12#include <linux/i2c.h>13#include <linux/leds.h>14#include <linux/slab.h>15#include <linux/platform_device.h>16#include <linux/input.h>17#include <linux/led-lm3530.h>18#include <linux/types.h>19#include <linux/regulator/consumer.h>20#include <linux/module.h>21 22#define LM3530_LED_DEV "lcd-backlight"23#define LM3530_NAME "lm3530-led"24 25#define LM3530_GEN_CONFIG 0x1026#define LM3530_ALS_CONFIG 0x2027#define LM3530_BRT_RAMP_RATE 0x3028#define LM3530_ALS_IMP_SELECT 0x4129#define LM3530_BRT_CTRL_REG 0xA030#define LM3530_ALS_ZB0_REG 0x6031#define LM3530_ALS_ZB1_REG 0x6132#define LM3530_ALS_ZB2_REG 0x6233#define LM3530_ALS_ZB3_REG 0x6334#define LM3530_ALS_Z0T_REG 0x7035#define LM3530_ALS_Z1T_REG 0x7136#define LM3530_ALS_Z2T_REG 0x7237#define LM3530_ALS_Z3T_REG 0x7338#define LM3530_ALS_Z4T_REG 0x7439#define LM3530_REG_MAX 1440 41/* General Control Register */42#define LM3530_EN_I2C_SHIFT (0)43#define LM3530_RAMP_LAW_SHIFT (1)44#define LM3530_MAX_CURR_SHIFT (2)45#define LM3530_EN_PWM_SHIFT (5)46#define LM3530_PWM_POL_SHIFT (6)47#define LM3530_EN_PWM_SIMPLE_SHIFT (7)48 49#define LM3530_ENABLE_I2C (1 << LM3530_EN_I2C_SHIFT)50#define LM3530_ENABLE_PWM (1 << LM3530_EN_PWM_SHIFT)51#define LM3530_POL_LOW (1 << LM3530_PWM_POL_SHIFT)52#define LM3530_ENABLE_PWM_SIMPLE (1 << LM3530_EN_PWM_SIMPLE_SHIFT)53 54/* ALS Config Register Options */55#define LM3530_ALS_AVG_TIME_SHIFT (0)56#define LM3530_EN_ALS_SHIFT (3)57#define LM3530_ALS_SEL_SHIFT (5)58 59#define LM3530_ENABLE_ALS (3 << LM3530_EN_ALS_SHIFT)60 61/* Brightness Ramp Rate Register */62#define LM3530_BRT_RAMP_FALL_SHIFT (0)63#define LM3530_BRT_RAMP_RISE_SHIFT (3)64 65/* ALS Resistor Select */66#define LM3530_ALS1_IMP_SHIFT (0)67#define LM3530_ALS2_IMP_SHIFT (4)68 69/* Zone Boundary Register defaults */70#define LM3530_ALS_ZB_MAX (4)71#define LM3530_ALS_WINDOW_mV (1000)72#define LM3530_ALS_OFFSET_mV (4)73 74/* Zone Target Register defaults */75#define LM3530_DEF_ZT_0 (0x7F)76#define LM3530_DEF_ZT_1 (0x66)77#define LM3530_DEF_ZT_2 (0x4C)78#define LM3530_DEF_ZT_3 (0x33)79#define LM3530_DEF_ZT_4 (0x19)80 81/* 7 bits are used for the brightness : LM3530_BRT_CTRL_REG */82#define MAX_BRIGHTNESS (127)83 84struct lm3530_mode_map {85 const char *mode;86 enum lm3530_mode mode_val;87};88 89static struct lm3530_mode_map mode_map[] = {90 { "man", LM3530_BL_MODE_MANUAL },91 { "als", LM3530_BL_MODE_ALS },92 { "pwm", LM3530_BL_MODE_PWM },93};94 95/**96 * struct lm3530_data97 * @led_dev: led class device98 * @client: i2c client99 * @pdata: LM3530 platform data100 * @mode: mode of operation - manual, ALS, PWM101 * @regulator: regulator102 * @brightness: previous brightness value103 * @enable: regulator is enabled104 */105struct lm3530_data {106 struct led_classdev led_dev;107 struct i2c_client *client;108 struct lm3530_platform_data *pdata;109 enum lm3530_mode mode;110 struct regulator *regulator;111 enum led_brightness brightness;112 bool enable;113};114 115/*116 * struct lm3530_als_data117 * @config : value of ALS configuration register118 * @imp_sel : value of ALS resistor select register119 * @zone : values of ALS ZB(Zone Boundary) registers120 */121struct lm3530_als_data {122 u8 config;123 u8 imp_sel;124 u8 zones[LM3530_ALS_ZB_MAX];125};126 127static const u8 lm3530_reg[LM3530_REG_MAX] = {128 LM3530_GEN_CONFIG,129 LM3530_ALS_CONFIG,130 LM3530_BRT_RAMP_RATE,131 LM3530_ALS_IMP_SELECT,132 LM3530_BRT_CTRL_REG,133 LM3530_ALS_ZB0_REG,134 LM3530_ALS_ZB1_REG,135 LM3530_ALS_ZB2_REG,136 LM3530_ALS_ZB3_REG,137 LM3530_ALS_Z0T_REG,138 LM3530_ALS_Z1T_REG,139 LM3530_ALS_Z2T_REG,140 LM3530_ALS_Z3T_REG,141 LM3530_ALS_Z4T_REG,142};143 144static int lm3530_get_mode_from_str(const char *str)145{146 int i;147 148 for (i = 0; i < ARRAY_SIZE(mode_map); i++)149 if (sysfs_streq(str, mode_map[i].mode))150 return mode_map[i].mode_val;151 152 return -EINVAL;153}154 155static void lm3530_als_configure(struct lm3530_platform_data *pdata,156 struct lm3530_als_data *als)157{158 int i;159 u32 als_vmin, als_vmax, als_vstep;160 161 if (pdata->als_vmax == 0) {162 pdata->als_vmin = 0;163 pdata->als_vmax = LM3530_ALS_WINDOW_mV;164 }165 166 als_vmin = pdata->als_vmin;167 als_vmax = pdata->als_vmax;168 169 if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV)170 pdata->als_vmax = als_vmax = als_vmin + LM3530_ALS_WINDOW_mV;171 172 /* n zone boundary makes n+1 zones */173 als_vstep = (als_vmax - als_vmin) / (LM3530_ALS_ZB_MAX + 1);174 175 for (i = 0; i < LM3530_ALS_ZB_MAX; i++)176 als->zones[i] = (((als_vmin + LM3530_ALS_OFFSET_mV) +177 als_vstep + (i * als_vstep)) * LED_FULL) / 1000;178 179 als->config =180 (pdata->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |181 (LM3530_ENABLE_ALS) |182 (pdata->als_input_mode << LM3530_ALS_SEL_SHIFT);183 184 als->imp_sel =185 (pdata->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |186 (pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);187}188 189static int lm3530_led_enable(struct lm3530_data *drvdata)190{191 int ret;192 193 if (drvdata->enable)194 return 0;195 196 ret = regulator_enable(drvdata->regulator);197 if (ret) {198 dev_err(drvdata->led_dev.dev, "Failed to enable vin:%d\n", ret);199 return ret;200 }201 202 drvdata->enable = true;203 return 0;204}205 206static void lm3530_led_disable(struct lm3530_data *drvdata)207{208 int ret;209 210 if (!drvdata->enable)211 return;212 213 ret = regulator_disable(drvdata->regulator);214 if (ret) {215 dev_err(drvdata->led_dev.dev, "Failed to disable vin:%d\n",216 ret);217 return;218 }219 220 drvdata->enable = false;221}222 223static int lm3530_init_registers(struct lm3530_data *drvdata)224{225 int ret = 0;226 int i;227 u8 gen_config;228 u8 brt_ramp;229 u8 brightness;230 u8 reg_val[LM3530_REG_MAX];231 struct lm3530_platform_data *pdata = drvdata->pdata;232 struct i2c_client *client = drvdata->client;233 struct lm3530_pwm_data *pwm = &pdata->pwm_data;234 struct lm3530_als_data als;235 236 memset(&als, 0, sizeof(struct lm3530_als_data));237 238 gen_config = (pdata->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) |239 ((pdata->max_current & 7) << LM3530_MAX_CURR_SHIFT);240 241 switch (drvdata->mode) {242 case LM3530_BL_MODE_MANUAL:243 gen_config |= LM3530_ENABLE_I2C;244 break;245 case LM3530_BL_MODE_ALS:246 gen_config |= LM3530_ENABLE_I2C;247 lm3530_als_configure(pdata, &als);248 break;249 case LM3530_BL_MODE_PWM:250 gen_config |= LM3530_ENABLE_PWM | LM3530_ENABLE_PWM_SIMPLE |251 (pdata->pwm_pol_hi << LM3530_PWM_POL_SHIFT);252 break;253 }254 255 brt_ramp = (pdata->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) |256 (pdata->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT);257 258 if (drvdata->brightness)259 brightness = drvdata->brightness;260 else261 brightness = drvdata->brightness = pdata->brt_val;262 263 if (brightness > drvdata->led_dev.max_brightness)264 brightness = drvdata->led_dev.max_brightness;265 266 reg_val[0] = gen_config; /* LM3530_GEN_CONFIG */267 reg_val[1] = als.config; /* LM3530_ALS_CONFIG */268 reg_val[2] = brt_ramp; /* LM3530_BRT_RAMP_RATE */269 reg_val[3] = als.imp_sel; /* LM3530_ALS_IMP_SELECT */270 reg_val[4] = brightness; /* LM3530_BRT_CTRL_REG */271 reg_val[5] = als.zones[0]; /* LM3530_ALS_ZB0_REG */272 reg_val[6] = als.zones[1]; /* LM3530_ALS_ZB1_REG */273 reg_val[7] = als.zones[2]; /* LM3530_ALS_ZB2_REG */274 reg_val[8] = als.zones[3]; /* LM3530_ALS_ZB3_REG */275 reg_val[9] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */276 reg_val[10] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */277 reg_val[11] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */278 reg_val[12] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */279 reg_val[13] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */280 281 ret = lm3530_led_enable(drvdata);282 if (ret)283 return ret;284 285 for (i = 0; i < LM3530_REG_MAX; i++) {286 /* do not update brightness register when pwm mode */287 if (lm3530_reg[i] == LM3530_BRT_CTRL_REG &&288 drvdata->mode == LM3530_BL_MODE_PWM) {289 if (pwm->pwm_set_intensity)290 pwm->pwm_set_intensity(reg_val[i],291 drvdata->led_dev.max_brightness);292 continue;293 }294 295 ret = i2c_smbus_write_byte_data(client,296 lm3530_reg[i], reg_val[i]);297 if (ret)298 break;299 }300 301 return ret;302}303 304static void lm3530_brightness_set(struct led_classdev *led_cdev,305 enum led_brightness brt_val)306{307 int err;308 struct lm3530_data *drvdata =309 container_of(led_cdev, struct lm3530_data, led_dev);310 struct lm3530_platform_data *pdata = drvdata->pdata;311 struct lm3530_pwm_data *pwm = &pdata->pwm_data;312 u8 max_brightness = led_cdev->max_brightness;313 314 switch (drvdata->mode) {315 case LM3530_BL_MODE_MANUAL:316 317 if (!drvdata->enable) {318 err = lm3530_init_registers(drvdata);319 if (err) {320 dev_err(&drvdata->client->dev,321 "Register Init failed: %d\n", err);322 break;323 }324 }325 326 /* set the brightness in brightness control register*/327 err = i2c_smbus_write_byte_data(drvdata->client,328 LM3530_BRT_CTRL_REG, brt_val);329 if (err)330 dev_err(&drvdata->client->dev,331 "Unable to set brightness: %d\n", err);332 else333 drvdata->brightness = brt_val;334 335 if (brt_val == 0)336 lm3530_led_disable(drvdata);337 break;338 case LM3530_BL_MODE_ALS:339 break;340 case LM3530_BL_MODE_PWM:341 if (pwm->pwm_set_intensity)342 pwm->pwm_set_intensity(brt_val, max_brightness);343 break;344 default:345 break;346 }347}348 349static ssize_t mode_show(struct device *dev,350 struct device_attribute *attr, char *buf)351{352 struct led_classdev *led_cdev = dev_get_drvdata(dev);353 struct lm3530_data *drvdata;354 int i, len = 0;355 356 drvdata = container_of(led_cdev, struct lm3530_data, led_dev);357 for (i = 0; i < ARRAY_SIZE(mode_map); i++)358 if (drvdata->mode == mode_map[i].mode_val)359 len += sprintf(buf + len, "[%s] ", mode_map[i].mode);360 else361 len += sprintf(buf + len, "%s ", mode_map[i].mode);362 363 len += sprintf(buf + len, "\n");364 365 return len;366}367 368static ssize_t mode_store(struct device *dev, struct device_attribute369 *attr, const char *buf, size_t size)370{371 struct led_classdev *led_cdev = dev_get_drvdata(dev);372 struct lm3530_data *drvdata;373 struct lm3530_pwm_data *pwm;374 u8 max_brightness;375 int mode, err;376 377 drvdata = container_of(led_cdev, struct lm3530_data, led_dev);378 pwm = &drvdata->pdata->pwm_data;379 max_brightness = led_cdev->max_brightness;380 mode = lm3530_get_mode_from_str(buf);381 if (mode < 0) {382 dev_err(dev, "Invalid mode\n");383 return mode;384 }385 386 drvdata->mode = mode;387 388 /* set pwm to low if unnecessary */389 if (mode != LM3530_BL_MODE_PWM && pwm->pwm_set_intensity)390 pwm->pwm_set_intensity(0, max_brightness);391 392 err = lm3530_init_registers(drvdata);393 if (err) {394 dev_err(dev, "Setting %s Mode failed :%d\n", buf, err);395 return err;396 }397 398 return sizeof(drvdata->mode);399}400static DEVICE_ATTR_RW(mode);401 402static struct attribute *lm3530_attrs[] = {403 &dev_attr_mode.attr,404 NULL405};406ATTRIBUTE_GROUPS(lm3530);407 408static int lm3530_probe(struct i2c_client *client)409{410 struct lm3530_platform_data *pdata = dev_get_platdata(&client->dev);411 struct lm3530_data *drvdata;412 int err = 0;413 414 if (pdata == NULL) {415 dev_err(&client->dev, "platform data required\n");416 return -ENODEV;417 }418 419 /* BL mode */420 if (pdata->mode > LM3530_BL_MODE_PWM) {421 dev_err(&client->dev, "Illegal Mode request\n");422 return -EINVAL;423 }424 425 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {426 dev_err(&client->dev, "I2C_FUNC_I2C not supported\n");427 return -EIO;428 }429 430 drvdata = devm_kzalloc(&client->dev, sizeof(struct lm3530_data),431 GFP_KERNEL);432 if (drvdata == NULL)433 return -ENOMEM;434 435 drvdata->mode = pdata->mode;436 drvdata->client = client;437 drvdata->pdata = pdata;438 drvdata->brightness = LED_OFF;439 drvdata->enable = false;440 drvdata->led_dev.name = LM3530_LED_DEV;441 drvdata->led_dev.brightness_set = lm3530_brightness_set;442 drvdata->led_dev.max_brightness = MAX_BRIGHTNESS;443 drvdata->led_dev.groups = lm3530_groups;444 445 i2c_set_clientdata(client, drvdata);446 447 drvdata->regulator = devm_regulator_get(&client->dev, "vin");448 if (IS_ERR(drvdata->regulator)) {449 dev_err(&client->dev, "regulator get failed\n");450 err = PTR_ERR(drvdata->regulator);451 drvdata->regulator = NULL;452 return err;453 }454 455 if (drvdata->pdata->brt_val) {456 err = lm3530_init_registers(drvdata);457 if (err < 0) {458 dev_err(&client->dev,459 "Register Init failed: %d\n", err);460 return err;461 }462 }463 err = led_classdev_register(&client->dev, &drvdata->led_dev);464 if (err < 0) {465 dev_err(&client->dev, "Register led class failed: %d\n", err);466 return err;467 }468 469 return 0;470}471 472static void lm3530_remove(struct i2c_client *client)473{474 struct lm3530_data *drvdata = i2c_get_clientdata(client);475 476 lm3530_led_disable(drvdata);477 led_classdev_unregister(&drvdata->led_dev);478}479 480static const struct i2c_device_id lm3530_id[] = {481 { LM3530_NAME },482 {}483};484MODULE_DEVICE_TABLE(i2c, lm3530_id);485 486static struct i2c_driver lm3530_i2c_driver = {487 .probe = lm3530_probe,488 .remove = lm3530_remove,489 .id_table = lm3530_id,490 .driver = {491 .name = LM3530_NAME,492 },493};494 495module_i2c_driver(lm3530_i2c_driver);496 497MODULE_DESCRIPTION("Back Light driver for LM3530");498MODULE_LICENSE("GPL v2");499MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>");500