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