blob: d1aed3f7a0ce0db08042b603ec04143a41d03845 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#include <linux/kernel.h>
13#include <linux/init.h>
14#include <linux/platform_device.h>
15#include <linux/leds.h>
16#include <linux/workqueue.h>
17#include <linux/spinlock.h>
18#include <linux/mfd/pmic8058.h>
19#include <linux/leds-pmic8058.h>
20
21#define SSBI_REG_ADDR_DRV_KEYPAD 0x48
22#define PM8058_DRV_KEYPAD_BL_MASK 0xf0
23#define PM8058_DRV_KEYPAD_BL_SHIFT 0x04
24
25#define SSBI_REG_ADDR_FLASH_DRV0 0x49
26#define PM8058_DRV_FLASH_MASK 0xf0
27#define PM8058_DRV_FLASH_SHIFT 0x04
28
29#define SSBI_REG_ADDR_FLASH_DRV1 0xFB
30
31#define SSBI_REG_ADDR_LED_CTRL_BASE 0x131
32#define SSBI_REG_ADDR_LED_CTRL(n) (SSBI_REG_ADDR_LED_CTRL_BASE + (n))
33#define PM8058_DRV_LED_CTRL_MASK 0xf8
34#define PM8058_DRV_LED_CTRL_SHIFT 0x03
35
36#define MAX_FLASH_CURRENT 300
37#define MAX_KEYPAD_CURRENT 300
38#define MAX_KEYPAD_BL_LEVEL (1 << 4)
39#define MAX_LED_DRV_LEVEL 20 /* 2 * 20 mA */
40
41#define PMIC8058_LED_OFFSET(id) ((id) - PMIC8058_ID_LED_0)
42
43struct pmic8058_led_data {
44 struct led_classdev cdev;
45 int id;
46 enum led_brightness brightness;
47 u8 flags;
48 struct pm8058_chip *pm_chip;
49 struct work_struct work;
50 struct mutex lock;
51 spinlock_t value_lock;
52 u8 reg_kp;
53 u8 reg_led_ctrl[3];
54 u8 reg_flash_led0;
55 u8 reg_flash_led1;
56};
57
58#define PM8058_MAX_LEDS 7
59static struct pmic8058_led_data led_data[PM8058_MAX_LEDS];
60
61static void kp_bl_set(struct pmic8058_led_data *led, enum led_brightness value)
62{
63 int rc;
64 u8 level;
65 unsigned long flags;
66
67 spin_lock_irqsave(&led->value_lock, flags);
68 level = (value << PM8058_DRV_KEYPAD_BL_SHIFT) &
69 PM8058_DRV_KEYPAD_BL_MASK;
70
71 led->reg_kp &= ~PM8058_DRV_KEYPAD_BL_MASK;
72 led->reg_kp |= level;
73 spin_unlock_irqrestore(&led->value_lock, flags);
74
75 rc = pm8058_write(led->pm_chip, SSBI_REG_ADDR_DRV_KEYPAD,
76 &led->reg_kp, 1);
77 if (rc)
78 pr_err("%s: can't set keypad backlight level\n", __func__);
79}
80
81static enum led_brightness kp_bl_get(struct pmic8058_led_data *led)
82{
83 if ((led->reg_kp & PM8058_DRV_KEYPAD_BL_MASK) >>
84 PM8058_DRV_KEYPAD_BL_SHIFT)
85 return LED_FULL;
86 else
87 return LED_OFF;
88}
89
90static void led_lc_set(struct pmic8058_led_data *led, enum led_brightness value)
91{
92 unsigned long flags;
93 int rc, offset;
94 u8 level, tmp;
95
96 spin_lock_irqsave(&led->value_lock, flags);
97
98 level = (led->brightness << PM8058_DRV_LED_CTRL_SHIFT) &
99 PM8058_DRV_LED_CTRL_MASK;
100
101 offset = PMIC8058_LED_OFFSET(led->id);
102 tmp = led->reg_led_ctrl[offset];
103
104 tmp &= ~PM8058_DRV_LED_CTRL_MASK;
105 tmp |= level;
106 spin_unlock_irqrestore(&led->value_lock, flags);
107
108 rc = pm8058_write(led->pm_chip, SSBI_REG_ADDR_LED_CTRL(offset),
109 &tmp, 1);
110 if (rc) {
111 dev_err(led->cdev.dev, "can't set (%d) led value\n",
112 led->id);
113 return;
114 }
115
116 spin_lock_irqsave(&led->value_lock, flags);
117 led->reg_led_ctrl[offset] = tmp;
118 spin_unlock_irqrestore(&led->value_lock, flags);
119}
120
121static enum led_brightness led_lc_get(struct pmic8058_led_data *led)
122{
123 int offset;
124 u8 value;
125
126 offset = PMIC8058_LED_OFFSET(led->id);
127 value = led->reg_led_ctrl[offset];
128
129 if ((value & PM8058_DRV_LED_CTRL_MASK) >>
130 PM8058_DRV_LED_CTRL_SHIFT)
131 return LED_FULL;
132 else
133 return LED_OFF;
134}
135
136static void
137led_flash_set(struct pmic8058_led_data *led, enum led_brightness value)
138{
139 int rc;
140 u8 level;
141 unsigned long flags;
142 u8 reg_flash_led;
143 u16 reg_addr;
144
145 spin_lock_irqsave(&led->value_lock, flags);
146 level = (value << PM8058_DRV_FLASH_SHIFT) &
147 PM8058_DRV_FLASH_MASK;
148
149 if (led->id == PMIC8058_ID_FLASH_LED_0) {
150 led->reg_flash_led0 &= ~PM8058_DRV_FLASH_MASK;
151 led->reg_flash_led0 |= level;
152 reg_flash_led = led->reg_flash_led0;
153 reg_addr = SSBI_REG_ADDR_FLASH_DRV0;
154 } else {
155 led->reg_flash_led1 &= ~PM8058_DRV_FLASH_MASK;
156 led->reg_flash_led1 |= level;
157 reg_flash_led = led->reg_flash_led1;
158 reg_addr = SSBI_REG_ADDR_FLASH_DRV1;
159 }
160 spin_unlock_irqrestore(&led->value_lock, flags);
161
162 rc = pm8058_write(led->pm_chip, reg_addr, &reg_flash_led, 1);
163 if (rc)
164 pr_err("%s: can't set flash led%d level %d\n", __func__,
165 led->id, rc);
166}
167
168int pm8058_set_flash_led_current(enum pmic8058_leds id, unsigned mA)
169{
170 struct pmic8058_led_data *led;
171
172 if ((id < PMIC8058_ID_FLASH_LED_0) || (id > PMIC8058_ID_FLASH_LED_1)) {
173 pr_err("%s: invalid LED ID (%d) specified\n", __func__, id);
174 return -EINVAL;
175 }
176
177 led = &led_data[id];
178 if (!led) {
179 pr_err("%s: flash led not available\n", __func__);
180 return -EINVAL;
181 }
182
183 if (mA > MAX_FLASH_CURRENT)
184 return -EINVAL;
185
186 led_flash_set(led, mA / 20);
187
188 return 0;
189}
190EXPORT_SYMBOL(pm8058_set_flash_led_current);
191
192int pm8058_set_led_current(enum pmic8058_leds id, unsigned mA)
193{
194 struct pmic8058_led_data *led;
195 int brightness = 0;
196
197 if ((id < PMIC8058_ID_LED_KB_LIGHT) || (id > PMIC8058_ID_FLASH_LED_1)) {
198 pr_err("%s: invalid LED ID (%d) specified\n", __func__, id);
199 return -EINVAL;
200 }
201
202 led = &led_data[id];
203 if (!led) {
204 pr_err("%s: flash led not available\n", __func__);
205 return -EINVAL;
206 }
207
208 switch (id) {
209 case PMIC8058_ID_LED_0:
210 case PMIC8058_ID_LED_1:
211 case PMIC8058_ID_LED_2:
212 brightness = mA / 2;
213 if (brightness > led->cdev.max_brightness)
214 return -EINVAL;
215 led_lc_set(led, brightness);
216 break;
217
218 case PMIC8058_ID_LED_KB_LIGHT:
219 case PMIC8058_ID_FLASH_LED_0:
220 case PMIC8058_ID_FLASH_LED_1:
221 brightness = mA / 20;
222 if (brightness > led->cdev.max_brightness)
223 return -EINVAL;
224 if (id == PMIC8058_ID_LED_KB_LIGHT)
225 kp_bl_set(led, brightness);
226 else
227 led_flash_set(led, brightness);
228 break;
229 }
230
231 return 0;
232}
233EXPORT_SYMBOL(pm8058_set_led_current);
234
235static void pmic8058_led_set(struct led_classdev *led_cdev,
236 enum led_brightness value)
237{
238 struct pmic8058_led_data *led;
239 unsigned long flags;
240
241 led = container_of(led_cdev, struct pmic8058_led_data, cdev);
242
243 spin_lock_irqsave(&led->value_lock, flags);
244 led->brightness = value;
245 schedule_work(&led->work);
246 spin_unlock_irqrestore(&led->value_lock, flags);
247}
248
249static void pmic8058_led_work(struct work_struct *work)
250{
251 struct pmic8058_led_data *led = container_of(work,
252 struct pmic8058_led_data, work);
253
254 mutex_lock(&led->lock);
255
256 switch (led->id) {
257 case PMIC8058_ID_LED_KB_LIGHT:
258 kp_bl_set(led, led->brightness);
259 break;
260 case PMIC8058_ID_LED_0:
261 case PMIC8058_ID_LED_1:
262 case PMIC8058_ID_LED_2:
263 led_lc_set(led, led->brightness);
264 break;
265 case PMIC8058_ID_FLASH_LED_0:
266 case PMIC8058_ID_FLASH_LED_1:
267 led_flash_set(led, led->brightness);
268 break;
269 }
270
271 mutex_unlock(&led->lock);
272}
273
274static enum led_brightness pmic8058_led_get(struct led_classdev *led_cdev)
275{
276 struct pmic8058_led_data *led;
277
278 led = container_of(led_cdev, struct pmic8058_led_data, cdev);
279
280 switch (led->id) {
281 case PMIC8058_ID_LED_KB_LIGHT:
282 return kp_bl_get(led);
283 case PMIC8058_ID_LED_0:
284 case PMIC8058_ID_LED_1:
285 case PMIC8058_ID_LED_2:
286 return led_lc_get(led);
287 }
288 return LED_OFF;
289}
290
291static int pmic8058_led_probe(struct platform_device *pdev)
292{
293 struct pmic8058_leds_platform_data *pdata = pdev->dev.platform_data;
294 struct pmic8058_led_data *led_dat;
295 struct pmic8058_led *curr_led;
296 int rc, i = 0;
297 struct pm8058_chip *pm_chip;
298 u8 reg_kp;
299 u8 reg_led_ctrl[3];
300 u8 reg_flash_led0;
301 u8 reg_flash_led1;
302
303 pm_chip = dev_get_drvdata(pdev->dev.parent);
304 if (pm_chip == NULL) {
305 dev_err(&pdev->dev, "no parent data passed in\n");
306 return -EFAULT;
307 }
308
309 if (pdata == NULL) {
310 dev_err(&pdev->dev, "platform data not supplied\n");
311 return -EINVAL;
312 }
313
314 rc = pm8058_read(pm_chip, SSBI_REG_ADDR_DRV_KEYPAD, &reg_kp,
315 1);
316 if (rc) {
317 dev_err(&pdev->dev, "can't get keypad backlight level\n");
318 goto err_reg_read;
319 }
320
321 rc = pm8058_read(pm_chip, SSBI_REG_ADDR_LED_CTRL_BASE,
322 reg_led_ctrl, 3);
323 if (rc) {
324 dev_err(&pdev->dev, "can't get led levels\n");
325 goto err_reg_read;
326 }
327
328 rc = pm8058_read(pm_chip, SSBI_REG_ADDR_FLASH_DRV0,
329 &reg_flash_led0, 1);
330 if (rc) {
331 dev_err(&pdev->dev, "can't read flash led0\n");
332 goto err_reg_read;
333 }
334
335 rc = pm8058_read(pm_chip, SSBI_REG_ADDR_FLASH_DRV1,
336 &reg_flash_led1, 1);
337 if (rc) {
338 dev_err(&pdev->dev, "can't get flash led1\n");
339 goto err_reg_read;
340 }
341
342 for (i = 0; i < pdata->num_leds; i++) {
343 curr_led = &pdata->leds[i];
344 led_dat = &led_data[curr_led->id];
345
346 led_dat->cdev.name = curr_led->name;
347 led_dat->cdev.default_trigger = curr_led->default_trigger;
348 led_dat->cdev.brightness_set = pmic8058_led_set;
349 led_dat->cdev.brightness_get = pmic8058_led_get;
350 led_dat->cdev.brightness = LED_OFF;
351 led_dat->cdev.max_brightness = curr_led->max_brightness;
352 led_dat->cdev.flags = LED_CORE_SUSPENDRESUME;
353
354 led_dat->id = curr_led->id;
355 led_dat->reg_kp = reg_kp;
356 memcpy(led_data->reg_led_ctrl, reg_led_ctrl,
357 sizeof(reg_led_ctrl));
358 led_dat->reg_flash_led0 = reg_flash_led0;
359 led_dat->reg_flash_led1 = reg_flash_led1;
360
361 if (!((led_dat->id >= PMIC8058_ID_LED_KB_LIGHT) &&
362 (led_dat->id <= PMIC8058_ID_FLASH_LED_1))) {
363 dev_err(&pdev->dev, "invalid LED ID (%d) specified\n",
364 led_dat->id);
365 rc = -EINVAL;
366 goto fail_id_check;
367 }
368
369 led_dat->pm_chip = pm_chip;
370
371 mutex_init(&led_dat->lock);
372 spin_lock_init(&led_dat->value_lock);
373 INIT_WORK(&led_dat->work, pmic8058_led_work);
374
375 rc = led_classdev_register(&pdev->dev, &led_dat->cdev);
376 if (rc) {
377 dev_err(&pdev->dev, "unable to register led %d\n",
378 led_dat->id);
379 goto fail_id_check;
380 }
381 }
382
383 platform_set_drvdata(pdev, led_data);
384
385 return 0;
386
387err_reg_read:
388fail_id_check:
389 if (i > 0) {
390 for (i = i - 1; i >= 0; i--)
391 led_classdev_unregister(&led_data[i].cdev);
392 }
393 return rc;
394}
395
396static int __devexit pmic8058_led_remove(struct platform_device *pdev)
397{
398 int i;
399 struct pmic8058_leds_platform_data *pdata = pdev->dev.platform_data;
400 struct pmic8058_led_data *led = platform_get_drvdata(pdev);
401
402 for (i = 0; i < pdata->num_leds; i++) {
403 led_classdev_unregister(&led[led->id].cdev);
404 cancel_work_sync(&led[led->id].work);
405 }
406
407 return 0;
408}
409
410static struct platform_driver pmic8058_led_driver = {
411 .probe = pmic8058_led_probe,
412 .remove = __devexit_p(pmic8058_led_remove),
413 .driver = {
414 .name = "pm8058-led",
415 .owner = THIS_MODULE,
416 },
417};
418
419static int __init pmic8058_led_init(void)
420{
421 return platform_driver_register(&pmic8058_led_driver);
422}
423module_init(pmic8058_led_init);
424
425static void __exit pmic8058_led_exit(void)
426{
427 platform_driver_unregister(&pmic8058_led_driver);
428}
429module_exit(pmic8058_led_exit);
430
431MODULE_DESCRIPTION("PMIC8058 LEDs driver");
432MODULE_LICENSE("GPL v2");
433MODULE_VERSION("1.0");
434MODULE_ALIAS("platform:pmic8058-led");