aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
authorShreshtha Kumar Sahu <shreshthakumar.sahu@stericsson.com>2011-03-22 19:30:16 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-03-22 20:43:59 -0400
commitb1e6b7068f026e88257c20522555c78122e5a14d (patch)
tree21cbe63961ce77a0077a638fcfbda84d4530a63e /drivers/leds
parent551ea73838c5eba18d673bd4e7fb0fec77cdcb8a (diff)
leds: add driver for LM3530 ALS
Simple backlight driver for National Semiconductor LM3530. Presently only manual mode is supported, PWM and ALS support to be added. Signed-off-by: Shreshtha Kumar Sahu <shreshthakumar.sahu@stericsson.com> Cc: Linus Walleij <linus.walleij@stericsson.com> Cc: Richard Purdie <rpurdie@rpsys.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig10
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-lm3530.c378
3 files changed, 389 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 6f190f4cdbc0..9bec8699b8a3 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -34,6 +34,16 @@ config LEDS_ATMEL_PWM
34 This option enables support for LEDs driven using outputs 34 This option enables support for LEDs driven using outputs
35 of the dedicated PWM controller found on newer Atmel SOCs. 35 of the dedicated PWM controller found on newer Atmel SOCs.
36 36
37config LEDS_LM3530
38 tristate "LCD Backlight driver for LM3530"
39 depends on LEDS_CLASS
40 depends on I2C
41 help
42 This option enables support for the LCD backlight using
43 LM3530 ambient light sensor chip. This ALS chip can be
44 controlled manually or using PWM input or using ambient
45 light automatically.
46
37config LEDS_LOCOMO 47config LEDS_LOCOMO
38 tristate "LED Support for Locomo device" 48 tristate "LED Support for Locomo device"
39 depends on LEDS_CLASS 49 depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index aae6989ff6b6..39c80fca84d2 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o
9obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o 9obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
10obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o 10obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
11obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o 11obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
12obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
12obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o 13obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
13obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o 14obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
14obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o 15obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o
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
82struct lm3530_mode_map {
83 const char *mode;
84 enum lm3530_mode mode_val;
85};
86
87static 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 */
100struct 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
107static 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
125static 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
136static 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
203static 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
230static 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
263static DEVICE_ATTR(mode, 0644, NULL, lm3530_mode_set);
264
265static 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
329err_create_file:
330 led_classdev_unregister(&drvdata->led_dev);
331err_class_register:
332err_reg_init:
333 kfree(drvdata);
334err_out:
335 return err;
336}
337
338static 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
348static const struct i2c_device_id lm3530_id[] = {
349 {LM3530_NAME, 0},
350 {}
351};
352
353static 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
363static int __init lm3530_init(void)
364{
365 return i2c_add_driver(&lm3530_i2c_driver);
366}
367
368static void __exit lm3530_exit(void)
369{
370 i2c_del_driver(&lm3530_i2c_driver);
371}
372
373module_init(lm3530_init);
374module_exit(lm3530_exit);
375
376MODULE_DESCRIPTION("Back Light driver for LM3530");
377MODULE_LICENSE("GPL v2");
378MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>");