diff options
Diffstat (limited to 'drivers/leds/leds-lm3530.c')
-rw-r--r-- | drivers/leds/leds-lm3530.c | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c new file mode 100644 index 000000000000..e7089a1f6cb6 --- /dev/null +++ b/drivers/leds/leds-lm3530.c | |||
@@ -0,0 +1,378 @@ | |||
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 | |||
353 | static struct i2c_driver lm3530_i2c_driver = { | ||
354 | .probe = lm3530_probe, | ||
355 | .remove = lm3530_remove, | ||
356 | .id_table = lm3530_id, | ||
357 | .driver = { | ||
358 | .name = LM3530_NAME, | ||
359 | .owner = THIS_MODULE, | ||
360 | }, | ||
361 | }; | ||
362 | |||
363 | static int __init lm3530_init(void) | ||
364 | { | ||
365 | return i2c_add_driver(&lm3530_i2c_driver); | ||
366 | } | ||
367 | |||
368 | static void __exit lm3530_exit(void) | ||
369 | { | ||
370 | i2c_del_driver(&lm3530_i2c_driver); | ||
371 | } | ||
372 | |||
373 | module_init(lm3530_init); | ||
374 | module_exit(lm3530_exit); | ||
375 | |||
376 | MODULE_DESCRIPTION("Back Light driver for LM3530"); | ||
377 | MODULE_LICENSE("GPL v2"); | ||
378 | MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>"); | ||