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