Merge branch 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jlbec/ocfs2
[pandora-kernel.git] / 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
21 #define LM3530_LED_DEV "lcd-backlight"
22 #define LM3530_NAME "lm3530-led"
23
24 #define LM3530_GEN_CONFIG               0x10
25 #define LM3530_ALS_CONFIG               0x20
26 #define LM3530_BRT_RAMP_RATE            0x30
27 #define LM3530_ALS_ZONE_REG             0x40
28 #define LM3530_ALS_IMP_SELECT           0x41
29 #define LM3530_BRT_CTRL_REG             0xA0
30 #define LM3530_ALS_ZB0_REG              0x60
31 #define LM3530_ALS_ZB1_REG              0x61
32 #define LM3530_ALS_ZB2_REG              0x62
33 #define LM3530_ALS_ZB3_REG              0x63
34 #define LM3530_ALS_Z0T_REG              0x70
35 #define LM3530_ALS_Z1T_REG              0x71
36 #define LM3530_ALS_Z2T_REG              0x72
37 #define LM3530_ALS_Z3T_REG              0x73
38 #define LM3530_ALS_Z4T_REG              0x74
39 #define LM3530_REG_MAX                  15
40
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_DEF_ZB_0                 (0x33)
71 #define LM3530_DEF_ZB_1                 (0x66)
72 #define LM3530_DEF_ZB_2                 (0x99)
73 #define LM3530_DEF_ZB_3                 (0xCC)
74
75 /* Zone Target Register defaults */
76 #define LM3530_DEF_ZT_0                 (0x19)
77 #define LM3530_DEF_ZT_1                 (0x33)
78 #define LM3530_DEF_ZT_2                 (0x4C)
79 #define LM3530_DEF_ZT_3                 (0x66)
80 #define LM3530_DEF_ZT_4                 (0x7F)
81
82 struct lm3530_mode_map {
83         const char *mode;
84         enum lm3530_mode mode_val;
85 };
86
87 static struct lm3530_mode_map mode_map[] = {
88         { "man", LM3530_BL_MODE_MANUAL },
89         { "als", LM3530_BL_MODE_ALS },
90         { "pwm", LM3530_BL_MODE_PWM },
91 };
92
93 /**
94  * struct lm3530_data
95  * @led_dev: led class device
96  * @client: i2c client
97  * @pdata: LM3530 platform data
98  * @mode: mode of operation - manual, ALS, PWM
99  */
100 struct lm3530_data {
101         struct led_classdev led_dev;
102         struct i2c_client *client;
103         struct lm3530_platform_data *pdata;
104         enum lm3530_mode mode;
105 };
106
107 static const u8 lm3530_reg[LM3530_REG_MAX] = {
108         LM3530_GEN_CONFIG,
109         LM3530_ALS_CONFIG,
110         LM3530_BRT_RAMP_RATE,
111         LM3530_ALS_ZONE_REG,
112         LM3530_ALS_IMP_SELECT,
113         LM3530_BRT_CTRL_REG,
114         LM3530_ALS_ZB0_REG,
115         LM3530_ALS_ZB1_REG,
116         LM3530_ALS_ZB2_REG,
117         LM3530_ALS_ZB3_REG,
118         LM3530_ALS_Z0T_REG,
119         LM3530_ALS_Z1T_REG,
120         LM3530_ALS_Z2T_REG,
121         LM3530_ALS_Z3T_REG,
122         LM3530_ALS_Z4T_REG,
123 };
124
125 static int lm3530_get_mode_from_str(const char *str)
126 {
127         int i;
128
129         for (i = 0; i < ARRAY_SIZE(mode_map); i++)
130                 if (sysfs_streq(str, mode_map[i].mode))
131                         return mode_map[i].mode_val;
132
133         return -1;
134 }
135
136 static int lm3530_init_registers(struct lm3530_data *drvdata)
137 {
138         int ret = 0;
139         int i;
140         u8 gen_config;
141         u8 als_config = 0;
142         u8 brt_ramp;
143         u8 als_imp_sel = 0;
144         u8 brightness;
145         u8 reg_val[LM3530_REG_MAX];
146         struct lm3530_platform_data *pltfm = drvdata->pdata;
147         struct i2c_client *client = drvdata->client;
148
149         gen_config = (pltfm->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) |
150                         ((pltfm->max_current & 7) << LM3530_MAX_CURR_SHIFT);
151
152         if (drvdata->mode == LM3530_BL_MODE_MANUAL ||
153             drvdata->mode == LM3530_BL_MODE_ALS)
154                 gen_config |= (LM3530_ENABLE_I2C);
155
156         if (drvdata->mode == LM3530_BL_MODE_ALS) {
157                 als_config =
158                         (pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
159                         (LM3530_ENABLE_ALS) |
160                         (pltfm->als_input_mode << LM3530_ALS_SEL_SHIFT);
161
162                 als_imp_sel =
163                         (pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
164                         (pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
165         }
166
167         if (drvdata->mode == LM3530_BL_MODE_PWM)
168                 gen_config |= (LM3530_ENABLE_PWM) |
169                                 (pltfm->pwm_pol_hi << LM3530_PWM_POL_SHIFT) |
170                                 (LM3530_ENABLE_PWM_SIMPLE);
171
172         brt_ramp = (pltfm->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) |
173                         (pltfm->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT);
174
175         brightness = pltfm->brt_val;
176
177         reg_val[0] = gen_config;        /* LM3530_GEN_CONFIG */
178         reg_val[1] = als_config;        /* LM3530_ALS_CONFIG */
179         reg_val[2] = brt_ramp;          /* LM3530_BRT_RAMP_RATE */
180         reg_val[3] = 0x00;              /* LM3530_ALS_ZONE_REG */
181         reg_val[4] = als_imp_sel;       /* LM3530_ALS_IMP_SELECT */
182         reg_val[5] = brightness;        /* LM3530_BRT_CTRL_REG */
183         reg_val[6] = LM3530_DEF_ZB_0;   /* LM3530_ALS_ZB0_REG */
184         reg_val[7] = LM3530_DEF_ZB_1;   /* LM3530_ALS_ZB1_REG */
185         reg_val[8] = LM3530_DEF_ZB_2;   /* LM3530_ALS_ZB2_REG */
186         reg_val[9] = LM3530_DEF_ZB_3;   /* LM3530_ALS_ZB3_REG */
187         reg_val[10] = LM3530_DEF_ZT_0;  /* LM3530_ALS_Z0T_REG */
188         reg_val[11] = LM3530_DEF_ZT_1;  /* LM3530_ALS_Z1T_REG */
189         reg_val[12] = LM3530_DEF_ZT_2;  /* LM3530_ALS_Z2T_REG */
190         reg_val[13] = LM3530_DEF_ZT_3;  /* LM3530_ALS_Z3T_REG */
191         reg_val[14] = LM3530_DEF_ZT_4;  /* LM3530_ALS_Z4T_REG */
192
193         for (i = 0; i < LM3530_REG_MAX; i++) {
194                 ret = i2c_smbus_write_byte_data(client,
195                                 lm3530_reg[i], reg_val[i]);
196                 if (ret)
197                         break;
198         }
199
200         return ret;
201 }
202
203 static void lm3530_brightness_set(struct led_classdev *led_cdev,
204                                      enum led_brightness brt_val)
205 {
206         int err;
207         struct lm3530_data *drvdata =
208             container_of(led_cdev, struct lm3530_data, led_dev);
209
210         switch (drvdata->mode) {
211         case LM3530_BL_MODE_MANUAL:
212
213                 /* set the brightness in brightness control register*/
214                 err = i2c_smbus_write_byte_data(drvdata->client,
215                                 LM3530_BRT_CTRL_REG, brt_val / 2);
216                 if (err)
217                         dev_err(&drvdata->client->dev,
218                                 "Unable to set brightness: %d\n", err);
219                 break;
220         case LM3530_BL_MODE_ALS:
221                 break;
222         case LM3530_BL_MODE_PWM:
223                 break;
224         default:
225                 break;
226         }
227 }
228
229
230 static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
231                                    *attr, const char *buf, size_t size)
232 {
233         int err;
234         struct i2c_client *client = container_of(
235                                         dev->parent, struct i2c_client, dev);
236         struct lm3530_data *drvdata = i2c_get_clientdata(client);
237         int mode;
238
239         mode = lm3530_get_mode_from_str(buf);
240         if (mode < 0) {
241                 dev_err(dev, "Invalid mode\n");
242                 return -EINVAL;
243         }
244
245         if (mode == LM3530_BL_MODE_MANUAL)
246                 drvdata->mode = LM3530_BL_MODE_MANUAL;
247         else if (mode == LM3530_BL_MODE_ALS)
248                 drvdata->mode = LM3530_BL_MODE_ALS;
249         else if (mode == LM3530_BL_MODE_PWM) {
250                 dev_err(dev, "PWM mode not supported\n");
251                 return -EINVAL;
252         }
253
254         err = lm3530_init_registers(drvdata);
255         if (err) {
256                 dev_err(dev, "Setting %s Mode failed :%d\n", buf, err);
257                 return err;
258         }
259
260         return sizeof(drvdata->mode);
261 }
262
263 static DEVICE_ATTR(mode, 0644, NULL, lm3530_mode_set);
264
265 static int __devinit lm3530_probe(struct i2c_client *client,
266                            const struct i2c_device_id *id)
267 {
268         struct lm3530_platform_data *pdata = client->dev.platform_data;
269         struct lm3530_data *drvdata;
270         int err = 0;
271
272         if (pdata == NULL) {
273                 dev_err(&client->dev, "platform data required\n");
274                 err = -ENODEV;
275                 goto err_out;
276         }
277
278         /* BL mode */
279         if (pdata->mode > LM3530_BL_MODE_PWM) {
280                 dev_err(&client->dev, "Illegal Mode request\n");
281                 err = -EINVAL;
282                 goto err_out;
283         }
284
285         if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
286                 dev_err(&client->dev, "I2C_FUNC_I2C not supported\n");
287                 err = -EIO;
288                 goto err_out;
289         }
290
291         drvdata = kzalloc(sizeof(struct lm3530_data), GFP_KERNEL);
292         if (drvdata == NULL) {
293                 err = -ENOMEM;
294                 goto err_out;
295         }
296
297         drvdata->mode = pdata->mode;
298         drvdata->client = client;
299         drvdata->pdata = pdata;
300         drvdata->led_dev.name = LM3530_LED_DEV;
301         drvdata->led_dev.brightness_set = lm3530_brightness_set;
302
303         i2c_set_clientdata(client, drvdata);
304
305         err = lm3530_init_registers(drvdata);
306         if (err < 0) {
307                 dev_err(&client->dev, "Register Init failed: %d\n", err);
308                 err = -ENODEV;
309                 goto err_reg_init;
310         }
311
312         err = led_classdev_register((struct device *)
313                                       &client->dev, &drvdata->led_dev);
314         if (err < 0) {
315                 dev_err(&client->dev, "Register led class failed: %d\n", err);
316                 err = -ENODEV;
317                 goto err_class_register;
318         }
319
320         err = device_create_file(drvdata->led_dev.dev, &dev_attr_mode);
321         if (err < 0) {
322                 dev_err(&client->dev, "File device creation failed: %d\n", err);
323                 err = -ENODEV;
324                 goto err_create_file;
325         }
326
327         return 0;
328
329 err_create_file:
330         led_classdev_unregister(&drvdata->led_dev);
331 err_class_register:
332 err_reg_init:
333         kfree(drvdata);
334 err_out:
335         return err;
336 }
337
338 static int __devexit lm3530_remove(struct i2c_client *client)
339 {
340         struct lm3530_data *drvdata = i2c_get_clientdata(client);
341
342         device_remove_file(drvdata->led_dev.dev, &dev_attr_mode);
343         led_classdev_unregister(&drvdata->led_dev);
344         kfree(drvdata);
345         return 0;
346 }
347
348 static const struct i2c_device_id lm3530_id[] = {
349         {LM3530_NAME, 0},
350         {}
351 };
352 MODULE_DEVICE_TABLE(i2c, lm3530_id);
353
354 static struct i2c_driver lm3530_i2c_driver = {
355         .probe = lm3530_probe,
356         .remove = lm3530_remove,
357         .id_table = lm3530_id,
358         .driver = {
359                 .name = LM3530_NAME,
360                 .owner = THIS_MODULE,
361         },
362 };
363
364 static int __init lm3530_init(void)
365 {
366         return i2c_add_driver(&lm3530_i2c_driver);
367 }
368
369 static void __exit lm3530_exit(void)
370 {
371         i2c_del_driver(&lm3530_i2c_driver);
372 }
373
374 module_init(lm3530_init);
375 module_exit(lm3530_exit);
376
377 MODULE_DESCRIPTION("Back Light driver for LM3530");
378 MODULE_LICENSE("GPL v2");
379 MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>");