blob: 965c95566c9ee9af879d41f54b51b99fe4472e40 [file] [log] [blame]
Duy Truonge833aca2013-02-12 13:35:08 -08001/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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
Jay Chokshi12e49bf2011-07-22 16:24:39 -070013#define pr_fmt(fmt) "%s: " fmt, __func__
14
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070015#include <linux/kernel.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070016#include <linux/module.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070017#include <linux/init.h>
18#include <linux/slab.h>
19#include <linux/platform_device.h>
20#include <linux/leds.h>
21#include <linux/workqueue.h>
Jay Chokshi868312e2011-09-16 13:57:13 -070022#include <linux/err.h>
samin.ryue0041de2012-08-03 23:58:14 +090023#include <linux/ctype.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070024
25#include <linux/mfd/pm8xxx/core.h>
Jay Chokshi868312e2011-09-16 13:57:13 -070026#include <linux/mfd/pm8xxx/pwm.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070027#include <linux/leds-pm8xxx.h>
28
29#define SSBI_REG_ADDR_DRV_KEYPAD 0x48
30#define PM8XXX_DRV_KEYPAD_BL_MASK 0xf0
31#define PM8XXX_DRV_KEYPAD_BL_SHIFT 0x04
32
33#define SSBI_REG_ADDR_FLASH_DRV0 0x49
34#define PM8XXX_DRV_FLASH_MASK 0xf0
35#define PM8XXX_DRV_FLASH_SHIFT 0x04
36
37#define SSBI_REG_ADDR_FLASH_DRV1 0xFB
38
39#define SSBI_REG_ADDR_LED_CTRL_BASE 0x131
40#define SSBI_REG_ADDR_LED_CTRL(n) (SSBI_REG_ADDR_LED_CTRL_BASE + (n))
41#define PM8XXX_DRV_LED_CTRL_MASK 0xf8
42#define PM8XXX_DRV_LED_CTRL_SHIFT 0x03
43
Mohan Pallaka53fe1a12011-12-15 16:56:37 +053044#define SSBI_REG_ADDR_WLED_CTRL_BASE 0x25A
45#define SSBI_REG_ADDR_WLED_CTRL(n) (SSBI_REG_ADDR_WLED_CTRL_BASE + (n) - 1)
46
47/* wled control registers */
48#define WLED_MOD_CTRL_REG SSBI_REG_ADDR_WLED_CTRL(1)
49#define WLED_MAX_CURR_CFG_REG(n) SSBI_REG_ADDR_WLED_CTRL(n + 2)
Mohan Pallaka67710372012-11-19 16:58:59 +053050#define WLED_BRIGHTNESS_CNTL_REG1(n) SSBI_REG_ADDR_WLED_CTRL((2 * n) + 5)
51#define WLED_BRIGHTNESS_CNTL_REG2(n) SSBI_REG_ADDR_WLED_CTRL((2 * n) + 6)
Mohan Pallaka53fe1a12011-12-15 16:56:37 +053052#define WLED_SYNC_REG SSBI_REG_ADDR_WLED_CTRL(11)
53#define WLED_OVP_CFG_REG SSBI_REG_ADDR_WLED_CTRL(13)
54#define WLED_BOOST_CFG_REG SSBI_REG_ADDR_WLED_CTRL(14)
55#define WLED_HIGH_POLE_CAP_REG SSBI_REG_ADDR_WLED_CTRL(16)
56
57#define WLED_STRINGS 0x03
58#define WLED_OVP_VAL_MASK 0x30
59#define WLED_OVP_VAL_BIT_SHFT 0x04
60#define WLED_BOOST_LIMIT_MASK 0xE0
61#define WLED_BOOST_LIMIT_BIT_SHFT 0x05
Amy Maloche56913f52012-05-11 10:47:24 -070062#define WLED_BOOST_OFF 0x00
Mohan Pallaka53fe1a12011-12-15 16:56:37 +053063#define WLED_EN_MASK 0x01
64#define WLED_CP_SELECT_MAX 0x03
65#define WLED_CP_SELECT_MASK 0x03
66#define WLED_DIG_MOD_GEN_MASK 0x70
67#define WLED_CS_OUT_MASK 0x0E
68#define WLED_CTL_DLY_STEP 200
69#define WLED_CTL_DLY_MAX 1400
70#define WLED_CTL_DLY_MASK 0xE0
71#define WLED_CTL_DLY_BIT_SHFT 0x05
72#define WLED_MAX_CURR 25
73#define WLED_MAX_CURR_MASK 0x1F
74#define WLED_OP_FDBCK_MASK 0x1C
75#define WLED_OP_FDBCK_BIT_SHFT 0x02
76
Chandan Uddaraju6e73f0a2012-03-08 17:32:55 -080077#define WLED_MAX_LEVEL 255
Mohan Pallaka53fe1a12011-12-15 16:56:37 +053078#define WLED_8_BIT_MASK 0xFF
79#define WLED_8_BIT_SHFT 0x08
80#define WLED_MAX_DUTY_CYCLE 0xFFF
81
82#define WLED_SYNC_VAL 0x07
83#define WLED_SYNC_RESET_VAL 0x00
Amy Maloche1fe629e2012-08-28 11:06:40 -070084#define WLED_SYNC_MASK 0xF8
85
86#define ONE_WLED_STRING 1
87#define TWO_WLED_STRINGS 2
88#define THREE_WLED_STRINGS 3
89
90#define WLED_CABC_ONE_STRING 0x01
91#define WLED_CABC_TWO_STRING 0x03
92#define WLED_CABC_THREE_STRING 0x07
93
94#define WLED_CABC_SHIFT 3
Mohan Pallaka53fe1a12011-12-15 16:56:37 +053095
Amy Malochec17c3732012-02-27 18:34:07 -080096#define SSBI_REG_ADDR_RGB_CNTL1 0x12D
97#define SSBI_REG_ADDR_RGB_CNTL2 0x12E
98
99#define PM8XXX_DRV_RGB_RED_LED BIT(2)
100#define PM8XXX_DRV_RGB_GREEN_LED BIT(1)
101#define PM8XXX_DRV_RGB_BLUE_LED BIT(0)
102
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700103#define MAX_FLASH_LED_CURRENT 300
104#define MAX_LC_LED_CURRENT 40
105#define MAX_KP_BL_LED_CURRENT 300
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700106
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700107#define PM8XXX_ID_LED_CURRENT_FACTOR 2 /* Iout = x * 2mA */
108#define PM8XXX_ID_FLASH_CURRENT_FACTOR 20 /* Iout = x * 20mA */
109
110#define PM8XXX_FLASH_MODE_DBUS1 1
111#define PM8XXX_FLASH_MODE_DBUS2 2
112#define PM8XXX_FLASH_MODE_PWM 3
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700114#define MAX_LC_LED_BRIGHTNESS 20
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700115#define MAX_FLASH_BRIGHTNESS 15
116#define MAX_KB_LED_BRIGHTNESS 15
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700117
Jay Chokshide4cefb2011-08-04 18:10:44 -0700118#define PM8XXX_LED_OFFSET(id) ((id) - PM8XXX_ID_LED_0)
119
Devin Kima167a3f2012-09-04 16:30:19 -0700120#define PM8XXX_LED_PWM_FLAGS (PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP | \
samin.ryudfc10982012-08-30 02:01:09 +0900121 PM_PWM_LUT_PAUSE_LO_EN | PM_PWM_LUT_PAUSE_HI_EN)
122
123#define PM8XXX_LED_PWM_GRPFREQ_MAX 255
124#define PM8XXX_LED_PWM_GRPPWM_MAX 255
Jay Chokshi868312e2011-09-16 13:57:13 -0700125
Amy Maloche3d2c24c2012-03-22 19:02:25 -0700126#define LED_MAP(_version, _kb, _led0, _led1, _led2, _flash_led0, _flash_led1, \
Amy Malochec17c3732012-02-27 18:34:07 -0800127 _wled, _rgb_led_red, _rgb_led_green, _rgb_led_blue)\
Amy Maloche3d2c24c2012-03-22 19:02:25 -0700128 {\
129 .version = _version,\
130 .supported = _kb << PM8XXX_ID_LED_KB_LIGHT | \
131 _led0 << PM8XXX_ID_LED_0 | _led1 << PM8XXX_ID_LED_1 | \
132 _led2 << PM8XXX_ID_LED_2 | \
133 _flash_led0 << PM8XXX_ID_FLASH_LED_0 | \
134 _flash_led1 << PM8XXX_ID_FLASH_LED_1 | \
Amy Malochec17c3732012-02-27 18:34:07 -0800135 _wled << PM8XXX_ID_WLED | \
136 _rgb_led_red << PM8XXX_ID_RGB_LED_RED | \
137 _rgb_led_green << PM8XXX_ID_RGB_LED_GREEN | \
138 _rgb_led_blue << PM8XXX_ID_RGB_LED_BLUE, \
Amy Maloche3d2c24c2012-03-22 19:02:25 -0700139 }
140
141/**
142 * supported_leds - leds supported for each PMIC version
143 * @version - version of PMIC
144 * @supported - which leds are supported on version
145 */
146
147struct supported_leds {
148 enum pm8xxx_version version;
149 u32 supported;
150};
151
152static const struct supported_leds led_map[] = {
Amy Malochec17c3732012-02-27 18:34:07 -0800153 LED_MAP(PM8XXX_VERSION_8058, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
154 LED_MAP(PM8XXX_VERSION_8921, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
155 LED_MAP(PM8XXX_VERSION_8018, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0),
156 LED_MAP(PM8XXX_VERSION_8922, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1),
157 LED_MAP(PM8XXX_VERSION_8038, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1),
Amy Maloche3d2c24c2012-03-22 19:02:25 -0700158};
159
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160/**
161 * struct pm8xxx_led_data - internal led data structure
162 * @led_classdev - led class device
163 * @id - led index
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164 * @work - workqueue for led
165 * @lock - to protect the transactions
166 * @reg - cached value of led register
Jay Chokshi868312e2011-09-16 13:57:13 -0700167 * @pwm_dev - pointer to PWM device if LED is driven using PWM
168 * @pwm_channel - PWM channel ID
169 * @pwm_period_us - PWM period in micro seconds
170 * @pwm_duty_cycles - struct that describes PWM duty cycles info
samin.ryue0041de2012-08-03 23:58:14 +0900171 * @use_pwm - controlled by userspace
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172 */
173struct pm8xxx_led_data {
174 struct led_classdev cdev;
175 int id;
176 u8 reg;
Amy Maloche56913f52012-05-11 10:47:24 -0700177 u8 wled_mod_ctrl_val;
samin.ryue0041de2012-08-03 23:58:14 +0900178 u8 lock_update;
179 u8 blink;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180 struct device *dev;
181 struct work_struct work;
182 struct mutex lock;
Jay Chokshi868312e2011-09-16 13:57:13 -0700183 struct pwm_device *pwm_dev;
184 int pwm_channel;
185 u32 pwm_period_us;
186 struct pm8xxx_pwm_duty_cycles *pwm_duty_cycles;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530187 struct wled_config_data *wled_cfg;
188 int max_current;
samin.ryue0041de2012-08-03 23:58:14 +0900189 int use_pwm;
Devin Kim232d6652012-08-09 20:41:00 -0700190 int adjust_brightness;
samin.ryudfc10982012-08-30 02:01:09 +0900191 u16 pwm_grppwm;
192 u16 pwm_grpfreq;
193 u16 pwm_pause_hi;
194 u16 pwm_pause_lo;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195};
196
197static void led_kp_set(struct pm8xxx_led_data *led, enum led_brightness value)
198{
199 int rc;
200 u8 level;
201
202 level = (value << PM8XXX_DRV_KEYPAD_BL_SHIFT) &
203 PM8XXX_DRV_KEYPAD_BL_MASK;
204
205 led->reg &= ~PM8XXX_DRV_KEYPAD_BL_MASK;
206 led->reg |= level;
207
208 rc = pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_DRV_KEYPAD,
209 led->reg);
210 if (rc < 0)
211 dev_err(led->cdev.dev,
212 "can't set keypad backlight level rc=%d\n", rc);
213}
214
215static void led_lc_set(struct pm8xxx_led_data *led, enum led_brightness value)
216{
217 int rc, offset;
218 u8 level;
219
220 level = (value << PM8XXX_DRV_LED_CTRL_SHIFT) &
221 PM8XXX_DRV_LED_CTRL_MASK;
222
223 offset = PM8XXX_LED_OFFSET(led->id);
224
225 led->reg &= ~PM8XXX_DRV_LED_CTRL_MASK;
226 led->reg |= level;
227
228 rc = pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_LED_CTRL(offset),
229 led->reg);
230 if (rc)
231 dev_err(led->cdev.dev, "can't set (%d) led value rc=%d\n",
232 led->id, rc);
233}
234
235static void
236led_flash_set(struct pm8xxx_led_data *led, enum led_brightness value)
237{
238 int rc;
239 u8 level;
240 u16 reg_addr;
241
242 level = (value << PM8XXX_DRV_FLASH_SHIFT) &
243 PM8XXX_DRV_FLASH_MASK;
244
245 led->reg &= ~PM8XXX_DRV_FLASH_MASK;
246 led->reg |= level;
247
248 if (led->id == PM8XXX_ID_FLASH_LED_0)
249 reg_addr = SSBI_REG_ADDR_FLASH_DRV0;
250 else
251 reg_addr = SSBI_REG_ADDR_FLASH_DRV1;
252
253 rc = pm8xxx_writeb(led->dev->parent, reg_addr, led->reg);
254 if (rc < 0)
255 dev_err(led->cdev.dev, "can't set flash led%d level rc=%d\n",
256 led->id, rc);
257}
258
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530259static int
260led_wled_set(struct pm8xxx_led_data *led, enum led_brightness value)
261{
262 int rc, duty;
263 u8 val, i, num_wled_strings;
264
265 if (value > WLED_MAX_LEVEL)
266 value = WLED_MAX_LEVEL;
267
Amy Maloche56913f52012-05-11 10:47:24 -0700268 if (value == 0) {
269 rc = pm8xxx_writeb(led->dev->parent, WLED_MOD_CTRL_REG,
270 WLED_BOOST_OFF);
271 if (rc) {
272 dev_err(led->dev->parent, "can't write wled ctrl config"
273 " register rc=%d\n", rc);
274 return rc;
275 }
276 } else {
277 rc = pm8xxx_writeb(led->dev->parent, WLED_MOD_CTRL_REG,
278 led->wled_mod_ctrl_val);
279 if (rc) {
280 dev_err(led->dev->parent, "can't write wled ctrl config"
281 " register rc=%d\n", rc);
282 return rc;
283 }
284 }
285
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530286 duty = (WLED_MAX_DUTY_CYCLE * value) / WLED_MAX_LEVEL;
287
288 num_wled_strings = led->wled_cfg->num_strings;
289
290 /* program brightness control registers */
291 for (i = 0; i < num_wled_strings; i++) {
292 rc = pm8xxx_readb(led->dev->parent,
293 WLED_BRIGHTNESS_CNTL_REG1(i), &val);
294 if (rc) {
295 dev_err(led->dev->parent, "can't read wled brightnes ctrl"
296 " register1 rc=%d\n", rc);
297 return rc;
298 }
299
300 val = (val & ~WLED_MAX_CURR_MASK) | (duty >> WLED_8_BIT_SHFT);
301 rc = pm8xxx_writeb(led->dev->parent,
302 WLED_BRIGHTNESS_CNTL_REG1(i), val);
303 if (rc) {
304 dev_err(led->dev->parent, "can't write wled brightness ctrl"
305 " register1 rc=%d\n", rc);
306 return rc;
307 }
308
309 val = duty & WLED_8_BIT_MASK;
310 rc = pm8xxx_writeb(led->dev->parent,
311 WLED_BRIGHTNESS_CNTL_REG2(i), val);
312 if (rc) {
313 dev_err(led->dev->parent, "can't write wled brightness ctrl"
314 " register2 rc=%d\n", rc);
315 return rc;
316 }
317 }
Amy Maloche1fe629e2012-08-28 11:06:40 -0700318 rc = pm8xxx_readb(led->dev->parent, WLED_SYNC_REG, &val);
319 if (rc) {
320 dev_err(led->dev->parent,
321 "can't read wled sync register rc=%d\n", rc);
322 return rc;
323 }
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530324 /* sync */
Amy Maloche1fe629e2012-08-28 11:06:40 -0700325 val &= WLED_SYNC_MASK;
326 val |= WLED_SYNC_VAL;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530327 rc = pm8xxx_writeb(led->dev->parent, WLED_SYNC_REG, val);
328 if (rc) {
329 dev_err(led->dev->parent,
330 "can't read wled sync register rc=%d\n", rc);
331 return rc;
332 }
Amy Maloche1fe629e2012-08-28 11:06:40 -0700333 val &= WLED_SYNC_MASK;
334 val |= WLED_SYNC_RESET_VAL;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530335 rc = pm8xxx_writeb(led->dev->parent, WLED_SYNC_REG, val);
336 if (rc) {
337 dev_err(led->dev->parent,
338 "can't read wled sync register rc=%d\n", rc);
339 return rc;
340 }
341 return 0;
342}
343
344static void wled_dump_regs(struct pm8xxx_led_data *led)
345{
346 int i;
347 u8 val;
348
349 for (i = 1; i < 17; i++) {
350 pm8xxx_readb(led->dev->parent,
351 SSBI_REG_ADDR_WLED_CTRL(i), &val);
352 pr_debug("WLED_CTRL_%d = 0x%x\n", i, val);
353 }
354}
355
Amy Malochec17c3732012-02-27 18:34:07 -0800356static void
Amy Malochead3c7842012-07-20 14:51:27 -0700357led_rgb_write(struct pm8xxx_led_data *led, u16 addr, enum led_brightness value)
Amy Malochec17c3732012-02-27 18:34:07 -0800358{
359 int rc;
360 u8 val, mask;
361
Amy Malochead3c7842012-07-20 14:51:27 -0700362 if (led->id != PM8XXX_ID_RGB_LED_BLUE &&
363 led->id != PM8XXX_ID_RGB_LED_RED &&
364 led->id != PM8XXX_ID_RGB_LED_GREEN)
365 return;
366
367 rc = pm8xxx_readb(led->dev->parent, addr, &val);
Amy Malochec17c3732012-02-27 18:34:07 -0800368 if (rc) {
369 dev_err(led->cdev.dev, "can't read rgb ctrl register rc=%d\n",
370 rc);
371 return;
372 }
373
374 switch (led->id) {
375 case PM8XXX_ID_RGB_LED_RED:
376 mask = PM8XXX_DRV_RGB_RED_LED;
377 break;
378 case PM8XXX_ID_RGB_LED_GREEN:
379 mask = PM8XXX_DRV_RGB_GREEN_LED;
380 break;
381 case PM8XXX_ID_RGB_LED_BLUE:
382 mask = PM8XXX_DRV_RGB_BLUE_LED;
383 break;
384 default:
385 return;
386 }
387
388 if (value)
389 val |= mask;
390 else
391 val &= ~mask;
392
Amy Malochead3c7842012-07-20 14:51:27 -0700393 rc = pm8xxx_writeb(led->dev->parent, addr, val);
Amy Malochec17c3732012-02-27 18:34:07 -0800394 if (rc < 0)
395 dev_err(led->cdev.dev, "can't set rgb led %d level rc=%d\n",
396 led->id, rc);
397}
398
Amy Malochead3c7842012-07-20 14:51:27 -0700399static void
400led_rgb_set(struct pm8xxx_led_data *led, enum led_brightness value)
401{
402 if (value) {
403 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL1, value);
404 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL2, value);
405 } else {
406 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL2, value);
407 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL1, value);
408 }
409}
410
Devin Kim232d6652012-08-09 20:41:00 -0700411static int pm8xxx_adjust_brightness(struct led_classdev *led_cdev,
412 enum led_brightness value)
413{
414 int level = 0;
415 struct pm8xxx_led_data *led;
416 led = container_of(led_cdev, struct pm8xxx_led_data, cdev);
417
418 if (!led->adjust_brightness)
419 return value;
420
421 if (led->adjust_brightness == led->cdev.max_brightness)
422 return value;
423
424 level = (2 * value * led->adjust_brightness
425 + led->cdev.max_brightness)
426 / (2 * led->cdev.max_brightness);
427
428 if (!level && value)
429 level = 1;
430
431 return level;
432}
433
samin.ryue0041de2012-08-03 23:58:14 +0900434static int pm8xxx_led_pwm_pattern_update(struct pm8xxx_led_data * led)
435{
436 int start_idx, idx_len;
437 int *pcts = NULL;
438 int i, rc = 0;
439 int temp = 0;
Devin Kim232d6652012-08-09 20:41:00 -0700440 int pwm_max = 0;
samin.ryudfc10982012-08-30 02:01:09 +0900441 int total_ms, on_ms;
samin.ryue0041de2012-08-03 23:58:14 +0900442
443 if (!led->pwm_duty_cycles || !led->pwm_duty_cycles->duty_pcts) {
444 dev_err(led->cdev.dev, "duty_cycles and duty_pcts is not exist\n");
445 return -EINVAL;
446 }
447
samin.ryudfc10982012-08-30 02:01:09 +0900448 if (led->pwm_grppwm > 0 && led->pwm_grpfreq > 0) {
449 total_ms = led->pwm_grpfreq * 50;
450 on_ms = (led->pwm_grppwm * total_ms) >> 8;
451 if (PM8XXX_LED_PWM_FLAGS & PM_PWM_LUT_REVERSE) {
452 led->pwm_duty_cycles->duty_ms = on_ms /
453 (led->pwm_duty_cycles->num_duty_pcts << 1);
Devin Kima167a3f2012-09-04 16:30:19 -0700454 led->pwm_pause_lo = on_ms %
samin.ryudfc10982012-08-30 02:01:09 +0900455 (led->pwm_duty_cycles->num_duty_pcts << 1);
456 } else {
457 led->pwm_duty_cycles->duty_ms = on_ms /
458 (led->pwm_duty_cycles->num_duty_pcts);
Devin Kima167a3f2012-09-04 16:30:19 -0700459 led->pwm_pause_lo = on_ms %
samin.ryudfc10982012-08-30 02:01:09 +0900460 (led->pwm_duty_cycles->num_duty_pcts);
461 }
Devin Kima167a3f2012-09-04 16:30:19 -0700462 led->pwm_pause_hi = total_ms - on_ms;
samin.ryudfc10982012-08-30 02:01:09 +0900463 dev_dbg(led->cdev.dev, "duty_ms %d, pause_hi %d, pause_lo %d, total_ms %d, on_ms %d\n",
464 led->pwm_duty_cycles->duty_ms, led->pwm_pause_hi, led->pwm_pause_lo,
465 total_ms, on_ms);
466 }
467
Devin Kim232d6652012-08-09 20:41:00 -0700468 pwm_max = pm8xxx_adjust_brightness(&led->cdev, led->cdev.brightness);
samin.ryue0041de2012-08-03 23:58:14 +0900469 start_idx = led->pwm_duty_cycles->start_idx;
470 idx_len = led->pwm_duty_cycles->num_duty_pcts;
471 pcts = led->pwm_duty_cycles->duty_pcts;
472
Devin Kima167a3f2012-09-04 16:30:19 -0700473 if (led->blink) {
474 int mid = (idx_len - 1) >> 1;
475 for (i = 0; i <= mid; i++) {
476 temp = ((pwm_max * i) << 1) / mid + 1;
477 pcts[i] = temp >> 1;
478 pcts[idx_len - 1 - i] = temp >> 1;
479 }
480 } else {
481 for (i = 0; i < idx_len; i++) {
482 pcts[i] = pwm_max;
samin.ryue0041de2012-08-03 23:58:14 +0900483 }
484 }
485
486 if (idx_len >= PM_PWM_LUT_SIZE && start_idx) {
487 pr_err("Wrong LUT size or index\n");
488 return -EINVAL;
489 }
490 if ((start_idx + idx_len) > PM_PWM_LUT_SIZE) {
491 pr_err("Exceed LUT limit\n");
492 return -EINVAL;
493 }
494
495 rc = pm8xxx_pwm_lut_config(led->pwm_dev, led->pwm_period_us,
496 led->pwm_duty_cycles->duty_pcts,
497 led->pwm_duty_cycles->duty_ms,
samin.ryudfc10982012-08-30 02:01:09 +0900498 start_idx, idx_len, led->pwm_pause_lo, led->pwm_pause_hi,
samin.ryue0041de2012-08-03 23:58:14 +0900499 PM8XXX_LED_PWM_FLAGS);
500
501 return rc;
502}
503
Jay Chokshi868312e2011-09-16 13:57:13 -0700504static int pm8xxx_led_pwm_work(struct pm8xxx_led_data *led)
505{
506 int duty_us;
507 int rc = 0;
Devin Kim232d6652012-08-09 20:41:00 -0700508 int level = 0;
509
510 level = pm8xxx_adjust_brightness(&led->cdev, led->cdev.brightness);
Jay Chokshi868312e2011-09-16 13:57:13 -0700511
512 if (led->pwm_duty_cycles == NULL) {
Devin Kim232d6652012-08-09 20:41:00 -0700513 duty_us = (led->pwm_period_us * level) / LED_FULL;
Jay Chokshi868312e2011-09-16 13:57:13 -0700514 rc = pwm_config(led->pwm_dev, duty_us, led->pwm_period_us);
Amy Malochead3c7842012-07-20 14:51:27 -0700515 if (led->cdev.brightness) {
516 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL1,
517 led->cdev.brightness);
Jay Chokshi868312e2011-09-16 13:57:13 -0700518 rc = pwm_enable(led->pwm_dev);
Amy Malochead3c7842012-07-20 14:51:27 -0700519 } else {
Jay Chokshi868312e2011-09-16 13:57:13 -0700520 pwm_disable(led->pwm_dev);
Amy Malochead3c7842012-07-20 14:51:27 -0700521 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL1,
522 led->cdev.brightness);
523 }
Jay Chokshi868312e2011-09-16 13:57:13 -0700524 } else {
Devin Kim232d6652012-08-09 20:41:00 -0700525 if (level) {
samin.ryue0041de2012-08-03 23:58:14 +0900526 pm8xxx_led_pwm_pattern_update(led);
Devin Kim232d6652012-08-09 20:41:00 -0700527 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL1, level);
samin.ryue0041de2012-08-03 23:58:14 +0900528 }
529
Devin Kim232d6652012-08-09 20:41:00 -0700530 rc = pm8xxx_pwm_lut_enable(led->pwm_dev, level);
531 if (!level)
532 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL1, level);
Jay Chokshi868312e2011-09-16 13:57:13 -0700533 }
534
535 return rc;
536}
537
Jay Chokshide4cefb2011-08-04 18:10:44 -0700538static void __pm8xxx_led_work(struct pm8xxx_led_data *led,
Devin Kim232d6652012-08-09 20:41:00 -0700539 enum led_brightness value)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700540{
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530541 int rc;
Devin Kim232d6652012-08-09 20:41:00 -0700542 int level = 0;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530543
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700544 mutex_lock(&led->lock);
545
Devin Kim232d6652012-08-09 20:41:00 -0700546 level = pm8xxx_adjust_brightness(&led->cdev, value);
547
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700548 switch (led->id) {
549 case PM8XXX_ID_LED_KB_LIGHT:
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700550 led_kp_set(led, level);
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530551 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700552 case PM8XXX_ID_LED_0:
553 case PM8XXX_ID_LED_1:
554 case PM8XXX_ID_LED_2:
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700555 led_lc_set(led, level);
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530556 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700557 case PM8XXX_ID_FLASH_LED_0:
558 case PM8XXX_ID_FLASH_LED_1:
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700559 led_flash_set(led, level);
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530560 break;
561 case PM8XXX_ID_WLED:
562 rc = led_wled_set(led, level);
563 if (rc < 0)
564 pr_err("wled brightness set failed %d\n", rc);
565 break;
Amy Malochec17c3732012-02-27 18:34:07 -0800566 case PM8XXX_ID_RGB_LED_RED:
567 case PM8XXX_ID_RGB_LED_GREEN:
568 case PM8XXX_ID_RGB_LED_BLUE:
569 led_rgb_set(led, level);
570 break;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530571 default:
572 dev_err(led->cdev.dev, "unknown led id %d", led->id);
573 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700574 }
575
576 mutex_unlock(&led->lock);
577}
578
Jay Chokshide4cefb2011-08-04 18:10:44 -0700579static void pm8xxx_led_work(struct work_struct *work)
580{
Jay Chokshi868312e2011-09-16 13:57:13 -0700581 int rc;
582
Jay Chokshide4cefb2011-08-04 18:10:44 -0700583 struct pm8xxx_led_data *led = container_of(work,
584 struct pm8xxx_led_data, work);
Jay Chokshide4cefb2011-08-04 18:10:44 -0700585
samin.ryue0041de2012-08-03 23:58:14 +0900586 dev_dbg(led->cdev.dev, "led %s set %d (%s mode)\n",
587 led->cdev.name, led->cdev.brightness,
588 (led->pwm_dev ? "pwm" : "manual"));
589
Jay Chokshi868312e2011-09-16 13:57:13 -0700590 if (led->pwm_dev == NULL) {
591 __pm8xxx_led_work(led, led->cdev.brightness);
592 } else {
593 rc = pm8xxx_led_pwm_work(led);
594 if (rc)
595 pr_err("could not configure PWM mode for LED:%d\n",
596 led->id);
597 }
Jay Chokshide4cefb2011-08-04 18:10:44 -0700598}
599
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700600static void pm8xxx_led_set(struct led_classdev *led_cdev,
601 enum led_brightness value)
602{
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700603 struct pm8xxx_led_data *led;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700604
605 led = container_of(led_cdev, struct pm8xxx_led_data, cdev);
606
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700607 if (value < LED_OFF || value > led->cdev.max_brightness) {
608 dev_err(led->cdev.dev, "Invalid brightness value exceeds");
609 return;
610 }
611
samin.ryue0041de2012-08-03 23:58:14 +0900612 if (!led->lock_update) {
613 schedule_work(&led->work);
614 } else {
615 dev_dbg(led->cdev.dev, "set %d pending\n",
616 value);
617 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700618}
619
Devin Kim232d6652012-08-09 20:41:00 -0700620static int pm8xxx_set_led_mode_and_adjust_brightness(struct pm8xxx_led_data *led,
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700621 enum pm8xxx_led_modes led_mode, int max_current)
622{
Jay Chokshide4cefb2011-08-04 18:10:44 -0700623 switch (led->id) {
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700624 case PM8XXX_ID_LED_0:
625 case PM8XXX_ID_LED_1:
626 case PM8XXX_ID_LED_2:
samin.ryuc650af32012-08-29 11:34:34 +0900627 led->adjust_brightness = max_current /
628 PM8XXX_ID_LED_CURRENT_FACTOR;
629 if (led->adjust_brightness > MAX_LC_LED_BRIGHTNESS)
630 led->adjust_brightness = MAX_LC_LED_BRIGHTNESS;
Willie Ruana39d1d42011-08-19 13:54:34 -0700631 led->reg = led_mode;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700632 break;
633 case PM8XXX_ID_LED_KB_LIGHT:
634 case PM8XXX_ID_FLASH_LED_0:
635 case PM8XXX_ID_FLASH_LED_1:
Devin Kim232d6652012-08-09 20:41:00 -0700636 led->adjust_brightness = max_current /
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700637 PM8XXX_ID_FLASH_CURRENT_FACTOR;
Devin Kim232d6652012-08-09 20:41:00 -0700638 if (led->adjust_brightness > MAX_FLASH_BRIGHTNESS)
639 led->adjust_brightness = MAX_FLASH_BRIGHTNESS;
Jay Chokshide4cefb2011-08-04 18:10:44 -0700640
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700641 switch (led_mode) {
642 case PM8XXX_LED_MODE_PWM1:
643 case PM8XXX_LED_MODE_PWM2:
644 case PM8XXX_LED_MODE_PWM3:
Willie Ruana39d1d42011-08-19 13:54:34 -0700645 led->reg = PM8XXX_FLASH_MODE_PWM;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700646 break;
647 case PM8XXX_LED_MODE_DTEST1:
Willie Ruana39d1d42011-08-19 13:54:34 -0700648 led->reg = PM8XXX_FLASH_MODE_DBUS1;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700649 break;
650 case PM8XXX_LED_MODE_DTEST2:
Willie Ruana39d1d42011-08-19 13:54:34 -0700651 led->reg = PM8XXX_FLASH_MODE_DBUS2;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700652 break;
653 default:
Willie Ruana39d1d42011-08-19 13:54:34 -0700654 led->reg = PM8XXX_LED_MODE_MANUAL;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700655 break;
656 }
657 break;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530658 case PM8XXX_ID_WLED:
Devin Kim232d6652012-08-09 20:41:00 -0700659 led->adjust_brightness = WLED_MAX_LEVEL;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530660 break;
Amy Malochec17c3732012-02-27 18:34:07 -0800661 case PM8XXX_ID_RGB_LED_RED:
662 case PM8XXX_ID_RGB_LED_GREEN:
663 case PM8XXX_ID_RGB_LED_BLUE:
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700664 break;
Amy Malochec17c3732012-02-27 18:34:07 -0800665 default:
666 dev_err(led->cdev.dev, "LED Id is invalid");
667 return -EINVAL;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700668 }
669
Amy Malochec17c3732012-02-27 18:34:07 -0800670 return 0;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700671}
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700672
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700673static enum led_brightness pm8xxx_led_get(struct led_classdev *led_cdev)
674{
675 struct pm8xxx_led_data *led;
676
677 led = container_of(led_cdev, struct pm8xxx_led_data, cdev);
678
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700679 return led->cdev.brightness;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700680}
681
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530682static int __devinit init_wled(struct pm8xxx_led_data *led)
683{
684 int rc, i;
685 u8 val, num_wled_strings;
686
687 num_wled_strings = led->wled_cfg->num_strings;
688
689 /* program over voltage protection threshold */
690 if (led->wled_cfg->ovp_val > WLED_OVP_37V) {
691 dev_err(led->dev->parent, "Invalid ovp value");
692 return -EINVAL;
693 }
694
695 rc = pm8xxx_readb(led->dev->parent, WLED_OVP_CFG_REG, &val);
696 if (rc) {
697 dev_err(led->dev->parent, "can't read wled ovp config"
698 " register rc=%d\n", rc);
699 return rc;
700 }
701
702 val = (val & ~WLED_OVP_VAL_MASK) |
703 (led->wled_cfg->ovp_val << WLED_OVP_VAL_BIT_SHFT);
704
705 rc = pm8xxx_writeb(led->dev->parent, WLED_OVP_CFG_REG, val);
706 if (rc) {
707 dev_err(led->dev->parent, "can't write wled ovp config"
708 " register rc=%d\n", rc);
709 return rc;
710 }
711
712 /* program current boost limit and output feedback*/
713 if (led->wled_cfg->boost_curr_lim > WLED_CURR_LIMIT_1680mA) {
714 dev_err(led->dev->parent, "Invalid boost current limit");
715 return -EINVAL;
716 }
717
718 rc = pm8xxx_readb(led->dev->parent, WLED_BOOST_CFG_REG, &val);
719 if (rc) {
720 dev_err(led->dev->parent, "can't read wled boost config"
721 " register rc=%d\n", rc);
722 return rc;
723 }
724
725 val = (val & ~WLED_BOOST_LIMIT_MASK) |
726 (led->wled_cfg->boost_curr_lim << WLED_BOOST_LIMIT_BIT_SHFT);
727
728 val = (val & ~WLED_OP_FDBCK_MASK) |
729 (led->wled_cfg->op_fdbck << WLED_OP_FDBCK_BIT_SHFT);
730
731 rc = pm8xxx_writeb(led->dev->parent, WLED_BOOST_CFG_REG, val);
732 if (rc) {
733 dev_err(led->dev->parent, "can't write wled boost config"
734 " register rc=%d\n", rc);
735 return rc;
736 }
737
738 /* program high pole capacitance */
739 if (led->wled_cfg->cp_select > WLED_CP_SELECT_MAX) {
740 dev_err(led->dev->parent, "Invalid pole capacitance");
741 return -EINVAL;
742 }
743
744 rc = pm8xxx_readb(led->dev->parent, WLED_HIGH_POLE_CAP_REG, &val);
745 if (rc) {
746 dev_err(led->dev->parent, "can't read wled high pole"
747 " capacitance register rc=%d\n", rc);
748 return rc;
749 }
750
751 val = (val & ~WLED_CP_SELECT_MASK) | led->wled_cfg->cp_select;
752
753 rc = pm8xxx_writeb(led->dev->parent, WLED_HIGH_POLE_CAP_REG, val);
754 if (rc) {
755 dev_err(led->dev->parent, "can't write wled high pole"
756 " capacitance register rc=%d\n", rc);
757 return rc;
758 }
759
760 /* program activation delay and maximum current */
761 for (i = 0; i < num_wled_strings; i++) {
762 rc = pm8xxx_readb(led->dev->parent,
Mohan Pallaka67710372012-11-19 16:58:59 +0530763 WLED_MAX_CURR_CFG_REG(i), &val);
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530764 if (rc) {
765 dev_err(led->dev->parent, "can't read wled max current"
766 " config register rc=%d\n", rc);
767 return rc;
768 }
769
770 if ((led->wled_cfg->ctrl_delay_us % WLED_CTL_DLY_STEP) ||
771 (led->wled_cfg->ctrl_delay_us > WLED_CTL_DLY_MAX)) {
772 dev_err(led->dev->parent, "Invalid control delay\n");
773 return rc;
774 }
775
776 val = val / WLED_CTL_DLY_STEP;
777 val = (val & ~WLED_CTL_DLY_MASK) |
778 (led->wled_cfg->ctrl_delay_us << WLED_CTL_DLY_BIT_SHFT);
779
780 if ((led->max_current > WLED_MAX_CURR)) {
781 dev_err(led->dev->parent, "Invalid max current\n");
782 return -EINVAL;
783 }
784
785 val = (val & ~WLED_MAX_CURR_MASK) | led->max_current;
786
787 rc = pm8xxx_writeb(led->dev->parent,
Mohan Pallaka67710372012-11-19 16:58:59 +0530788 WLED_MAX_CURR_CFG_REG(i), val);
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530789 if (rc) {
790 dev_err(led->dev->parent, "can't write wled max current"
791 " config register rc=%d\n", rc);
792 return rc;
793 }
794 }
795
Amy Maloche1fe629e2012-08-28 11:06:40 -0700796 if (led->wled_cfg->cabc_en) {
797 rc = pm8xxx_readb(led->dev->parent, WLED_SYNC_REG, &val);
798 if (rc) {
799 dev_err(led->dev->parent,
800 "can't read cabc register rc=%d\n", rc);
801 return rc;
802 }
803
804 switch (num_wled_strings) {
805 case ONE_WLED_STRING:
806 val |= (WLED_CABC_ONE_STRING << WLED_CABC_SHIFT);
807 break;
808 case TWO_WLED_STRINGS:
809 val |= (WLED_CABC_TWO_STRING << WLED_CABC_SHIFT);
810 break;
811 case THREE_WLED_STRINGS:
812 val |= (WLED_CABC_THREE_STRING << WLED_CABC_SHIFT);
813 break;
814 default:
815 break;
816 }
817
818 rc = pm8xxx_writeb(led->dev->parent, WLED_SYNC_REG, val);
819 if (rc) {
820 dev_err(led->dev->parent,
821 "can't write to enable cabc rc=%d\n", rc);
822 return rc;
823 }
824 }
825
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530826 /* program digital module generator, cs out and enable the module */
827 rc = pm8xxx_readb(led->dev->parent, WLED_MOD_CTRL_REG, &val);
828 if (rc) {
829 dev_err(led->dev->parent, "can't read wled module ctrl"
830 " register rc=%d\n", rc);
831 return rc;
832 }
833
834 if (led->wled_cfg->dig_mod_gen_en)
835 val |= WLED_DIG_MOD_GEN_MASK;
836
837 if (led->wled_cfg->cs_out_en)
838 val |= WLED_CS_OUT_MASK;
839
840 val |= WLED_EN_MASK;
841
842 rc = pm8xxx_writeb(led->dev->parent, WLED_MOD_CTRL_REG, val);
843 if (rc) {
844 dev_err(led->dev->parent, "can't write wled module ctrl"
845 " register rc=%d\n", rc);
846 return rc;
847 }
Amy Maloche56913f52012-05-11 10:47:24 -0700848 led->wled_mod_ctrl_val = val;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530849
850 /* dump wled registers */
851 wled_dump_regs(led);
852
853 return 0;
854}
855
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700856static int __devinit get_init_value(struct pm8xxx_led_data *led, u8 *val)
857{
858 int rc, offset;
859 u16 addr;
860
861 switch (led->id) {
862 case PM8XXX_ID_LED_KB_LIGHT:
863 addr = SSBI_REG_ADDR_DRV_KEYPAD;
864 break;
865 case PM8XXX_ID_LED_0:
866 case PM8XXX_ID_LED_1:
867 case PM8XXX_ID_LED_2:
868 offset = PM8XXX_LED_OFFSET(led->id);
869 addr = SSBI_REG_ADDR_LED_CTRL(offset);
870 break;
871 case PM8XXX_ID_FLASH_LED_0:
872 addr = SSBI_REG_ADDR_FLASH_DRV0;
873 break;
874 case PM8XXX_ID_FLASH_LED_1:
875 addr = SSBI_REG_ADDR_FLASH_DRV1;
876 break;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530877 case PM8XXX_ID_WLED:
878 rc = init_wled(led);
879 if (rc)
880 dev_err(led->cdev.dev, "can't initialize wled rc=%d\n",
881 rc);
882 return rc;
Amy Malochec17c3732012-02-27 18:34:07 -0800883 case PM8XXX_ID_RGB_LED_RED:
884 case PM8XXX_ID_RGB_LED_GREEN:
885 case PM8XXX_ID_RGB_LED_BLUE:
Amy Malochec17c3732012-02-27 18:34:07 -0800886 addr = SSBI_REG_ADDR_RGB_CNTL1;
887 break;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530888 default:
889 dev_err(led->cdev.dev, "unknown led id %d", led->id);
890 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700891 }
892
893 rc = pm8xxx_readb(led->dev->parent, addr, val);
894 if (rc)
895 dev_err(led->cdev.dev, "can't get led(%d) level rc=%d\n",
896 led->id, rc);
897
898 return rc;
899}
900
Jay Chokshi868312e2011-09-16 13:57:13 -0700901static int pm8xxx_led_pwm_configure(struct pm8xxx_led_data *led)
902{
samin.ryue0041de2012-08-03 23:58:14 +0900903 int duty_us, rc;
Jay Chokshi868312e2011-09-16 13:57:13 -0700904
905 led->pwm_dev = pwm_request(led->pwm_channel,
906 led->cdev.name);
907
908 if (IS_ERR_OR_NULL(led->pwm_dev)) {
909 pr_err("could not acquire PWM Channel %d, "
910 "error %ld\n", led->pwm_channel,
911 PTR_ERR(led->pwm_dev));
912 led->pwm_dev = NULL;
913 return -ENODEV;
914 }
915
916 if (led->pwm_duty_cycles != NULL) {
samin.ryue0041de2012-08-03 23:58:14 +0900917 rc = pm8xxx_led_pwm_pattern_update(led);
Jay Chokshi868312e2011-09-16 13:57:13 -0700918 } else {
919 duty_us = led->pwm_period_us;
920 rc = pwm_config(led->pwm_dev, duty_us, led->pwm_period_us);
921 }
922
923 return rc;
924}
925
samin.ryue0041de2012-08-03 23:58:14 +0900926static ssize_t pm8xxx_led_lock_update_show(struct device *dev,
927 struct device_attribute *attr, char *buf)
928{
929 const struct pm8xxx_led_platform_data *pdata = dev->platform_data;
930 struct pm8xxx_led_data *leds = (struct pm8xxx_led_data *)dev_get_drvdata(dev);
931 int i, n = 0;
932
933 for (i = 0; i < pdata->num_configs; i++)
934 {
935 n += sprintf(&buf[n], "%s is %s\n",
936 leds[i].cdev.name,
937 (leds[i].lock_update ? "non-updatable" : "updatable"));
938 }
939
940 return n;
941}
942
943static ssize_t pm8xxx_led_lock_update_store(struct device *dev,
944 struct device_attribute *attr, const char *buf, size_t size)
945{
946 struct pm8xxx_led_platform_data *pdata = dev->platform_data;
947 struct pm8xxx_led_data *leds = (struct pm8xxx_led_data *)dev_get_drvdata(dev);
948 ssize_t rc = -EINVAL;
949 char *after;
950 unsigned long state = simple_strtoul(buf, &after, 10);
951 size_t count = after - buf;
952 int i;
953
954 if (isspace(*after))
955 count++;
956
957 if (count == size) {
958 rc = count;
959 for (i = 0; i < pdata->num_configs; i++)
960 {
961 leds[i].lock_update = state;
962 if (!state) {
963 dev_info(dev, "resume %s set %d\n",
964 leds[i].cdev.name, leds[i].cdev.brightness);
965 schedule_work(&leds[i].work);
966 }
967 }
968 }
969 return rc;
970}
971
972static DEVICE_ATTR(lock, 0644, pm8xxx_led_lock_update_show, pm8xxx_led_lock_update_store);
973
974static ssize_t pm8xxx_led_grppwm_show(struct device *dev,
975 struct device_attribute *attr, char *buf)
976{
977 const struct pm8xxx_led_platform_data *pdata = dev->platform_data;
978 struct pm8xxx_led_data *leds = (struct pm8xxx_led_data *)dev_get_drvdata(dev);
979 int i, n = 0;
980
981 for (i = 0; i < pdata->num_configs; i++)
982 {
samin.ryudfc10982012-08-30 02:01:09 +0900983 n += sprintf(&buf[n], "%s period_us is %d\n", leds[i].cdev.name, leds[i].pwm_grppwm);
samin.ryue0041de2012-08-03 23:58:14 +0900984 }
985 return n;
986}
987
988static ssize_t pm8xxx_led_grppwm_store(struct device *dev,
989 struct device_attribute *attr, const char *buf, size_t size)
990{
991 const struct pm8xxx_led_platform_data *pdata = dev->platform_data;
992 struct pm8xxx_led_data *leds = (struct pm8xxx_led_data *)dev_get_drvdata(dev);
993 ssize_t rc = -EINVAL;
994 char *after;
995 unsigned long state = simple_strtoul(buf, &after, 10);
996 size_t count = after - buf;
997 int i;
998
999 if (isspace(*after))
1000 count++;
1001
1002 if (count == size) {
1003 rc = count;
samin.ryudfc10982012-08-30 02:01:09 +09001004 if (state < 0)
1005 state = 0;
1006 if (state > PM8XXX_LED_PWM_GRPPWM_MAX)
1007 state = PM8XXX_LED_PWM_GRPPWM_MAX;
samin.ryue0041de2012-08-03 23:58:14 +09001008
1009 for (i = 0; i < pdata->num_configs; i++)
1010 {
samin.ryudfc10982012-08-30 02:01:09 +09001011 leds[i].pwm_grppwm = state;
1012 dev_dbg(leds[i].cdev.dev, "set grppwm %lu\n", state);
samin.ryue0041de2012-08-03 23:58:14 +09001013 }
1014 }
1015 return rc;
1016}
1017
1018static DEVICE_ATTR(grppwm, 0644, pm8xxx_led_grppwm_show, pm8xxx_led_grppwm_store);
1019
1020static ssize_t pm8xxx_led_grpfreq_show(struct device *dev,
1021 struct device_attribute *attr, char *buf)
1022{
1023 const struct pm8xxx_led_platform_data *pdata = dev->platform_data;
1024 struct pm8xxx_led_data *leds = (struct pm8xxx_led_data *)dev_get_drvdata(dev);
1025 int i, n = 0;
1026
1027 for (i = 0; i < pdata->num_configs; i++)
1028 {
samin.ryudfc10982012-08-30 02:01:09 +09001029 n += sprintf(&buf[n], "%s freq %d\n", leds[i].cdev.name, leds[i].pwm_grpfreq);
samin.ryue0041de2012-08-03 23:58:14 +09001030 }
1031 return n;
1032}
1033
1034static ssize_t pm8xxx_led_grpfreq_store(struct device *dev,
1035 struct device_attribute *attr, const char *buf, size_t size)
1036{
1037 const struct pm8xxx_led_platform_data *pdata = dev->platform_data;
1038 struct pm8xxx_led_data *leds = (struct pm8xxx_led_data *)dev_get_drvdata(dev);
1039 ssize_t rc = -EINVAL;
1040 char *after;
1041 unsigned long state = simple_strtoul(buf, &after, 10);
1042 size_t count = after - buf;
1043 int i;
1044
1045 if (isspace(*after))
1046 count++;
1047
1048 if (count == size) {
1049 rc = count;
1050
samin.ryudfc10982012-08-30 02:01:09 +09001051 if(state < 0)
1052 state = 0;
1053 if(state > PM8XXX_LED_PWM_GRPFREQ_MAX)
1054 state = PM8XXX_LED_PWM_GRPFREQ_MAX;
samin.ryue0041de2012-08-03 23:58:14 +09001055
1056 for (i = 0; i < pdata->num_configs; i++)
1057 {
samin.ryudfc10982012-08-30 02:01:09 +09001058 leds[i].pwm_grpfreq = state;
1059 dev_dbg(leds[i].cdev.dev, "set grpfreq %lu\n", state);
samin.ryue0041de2012-08-03 23:58:14 +09001060 }
1061 }
1062 return rc;
1063}
1064
1065static DEVICE_ATTR(grpfreq, 0644, pm8xxx_led_grpfreq_show, pm8xxx_led_grpfreq_store);
1066
1067static ssize_t pm8xxx_led_blink_show(struct device *dev,
1068 struct device_attribute *attr, char *buf)
1069{
1070 const struct pm8xxx_led_platform_data *pdata = dev->platform_data;
1071 struct pm8xxx_led_data *leds = (struct pm8xxx_led_data *)dev_get_drvdata(dev);
Devin Kima167a3f2012-09-04 16:30:19 -07001072 int i, blink = 0;
samin.ryue0041de2012-08-03 23:58:14 +09001073
1074 for (i = 0; i < pdata->num_configs; i++)
1075 {
Devin Kima167a3f2012-09-04 16:30:19 -07001076 if (leds[i].blink)
1077 blink |= 1 << i;
samin.ryue0041de2012-08-03 23:58:14 +09001078 }
Devin Kima167a3f2012-09-04 16:30:19 -07001079 return sprintf(buf, "%d", blink);
samin.ryue0041de2012-08-03 23:58:14 +09001080}
1081
1082static ssize_t pm8xxx_led_blink_store(struct device *dev,
1083 struct device_attribute *attr, const char *buf, size_t size)
1084{
1085 const struct pm8xxx_led_platform_data *pdata = dev->platform_data;
1086 struct pm8xxx_led_data *leds = (struct pm8xxx_led_data *)dev_get_drvdata(dev);
1087 ssize_t rc = -EINVAL;
1088 char *after;
1089 unsigned long state = simple_strtoul(buf, &after, 10);
1090 size_t count = after - buf;
1091 int i;
1092
1093 if (isspace(*after))
1094 count++;
1095
1096 if (count == size) {
1097 rc = count;
1098
1099 for (i = 0; i < pdata->num_configs; i++)
1100 {
1101 if (leds[i].blink != state) {
1102 leds[i].blink = state;
Devin Kima167a3f2012-09-04 16:30:19 -07001103 if(!leds[i].lock_update)
1104 rc = pm8xxx_led_pwm_pattern_update(&leds[i]);
samin.ryue0041de2012-08-03 23:58:14 +09001105 }
1106 }
1107 }
1108 return rc;
1109}
1110
1111static DEVICE_ATTR(blink, 0644, pm8xxx_led_blink_show, pm8xxx_led_blink_store);
1112
Mohan Pallaka53fe1a12011-12-15 16:56:37 +05301113
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001114static int __devinit pm8xxx_led_probe(struct platform_device *pdev)
1115{
Jay Chokshi8994e392011-09-14 18:20:39 -07001116 const struct pm8xxx_led_platform_data *pdata = pdev->dev.platform_data;
1117 const struct led_platform_data *pcore_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001118 struct led_info *curr_led;
1119 struct pm8xxx_led_data *led, *led_dat;
Jay Chokshi8994e392011-09-14 18:20:39 -07001120 struct pm8xxx_led_config *led_cfg;
Amy Maloche3d2c24c2012-03-22 19:02:25 -07001121 enum pm8xxx_version version;
1122 bool found = false;
1123 int rc, i, j;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001124
1125 if (pdata == NULL) {
1126 dev_err(&pdev->dev, "platform data not supplied\n");
1127 return -EINVAL;
1128 }
1129
Jay Chokshi8994e392011-09-14 18:20:39 -07001130 pcore_data = pdata->led_core;
1131
1132 if (pcore_data->num_leds != pdata->num_configs) {
1133 dev_err(&pdev->dev, "#no. of led configs and #no. of led"
1134 "entries are not equal\n");
1135 return -EINVAL;
1136 }
1137
1138 led = kcalloc(pcore_data->num_leds, sizeof(*led), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001139 if (led == NULL) {
1140 dev_err(&pdev->dev, "failed to alloc memory\n");
1141 return -ENOMEM;
1142 }
1143
Jay Chokshi8994e392011-09-14 18:20:39 -07001144 for (i = 0; i < pcore_data->num_leds; i++) {
1145 curr_led = &pcore_data->leds[i];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001146 led_dat = &led[i];
Jay Chokshi8994e392011-09-14 18:20:39 -07001147 led_cfg = &pdata->configs[i];
1148
1149 led_dat->id = led_cfg->id;
Jay Chokshi868312e2011-09-16 13:57:13 -07001150 led_dat->pwm_channel = led_cfg->pwm_channel;
1151 led_dat->pwm_period_us = led_cfg->pwm_period_us;
1152 led_dat->pwm_duty_cycles = led_cfg->pwm_duty_cycles;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +05301153 led_dat->wled_cfg = led_cfg->wled_cfg;
1154 led_dat->max_current = led_cfg->max_current;
samin.ryue0041de2012-08-03 23:58:14 +09001155 led_dat->lock_update = 0;
1156 led_dat->blink = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001157
1158 if (!((led_dat->id >= PM8XXX_ID_LED_KB_LIGHT) &&
Mohan Pallaka53fe1a12011-12-15 16:56:37 +05301159 (led_dat->id < PM8XXX_ID_MAX))) {
Amy Maloche3d2c24c2012-03-22 19:02:25 -07001160 dev_err(&pdev->dev, "invalid LED ID(%d) specified\n",
1161 led_dat->id);
1162 rc = -EINVAL;
1163 goto fail_id_check;
1164
1165 }
1166
1167 found = false;
1168 version = pm8xxx_get_version(pdev->dev.parent);
1169 for (j = 0; j < ARRAY_SIZE(led_map); j++) {
1170 if (version == led_map[j].version
1171 && (led_map[j].supported & (1 << led_dat->id))) {
1172 found = true;
1173 break;
1174 }
1175 }
1176
1177 if (!found) {
1178 dev_err(&pdev->dev, "invalid LED ID(%d) specified\n",
1179 led_dat->id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001180 rc = -EINVAL;
1181 goto fail_id_check;
1182 }
1183
1184 led_dat->cdev.name = curr_led->name;
1185 led_dat->cdev.default_trigger = curr_led->default_trigger;
1186 led_dat->cdev.brightness_set = pm8xxx_led_set;
1187 led_dat->cdev.brightness_get = pm8xxx_led_get;
Devin Kim232d6652012-08-09 20:41:00 -07001188 led_dat->cdev.max_brightness = LED_FULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001189 led_dat->cdev.brightness = LED_OFF;
Jay Chokshi8994e392011-09-14 18:20:39 -07001190 led_dat->cdev.flags = curr_led->flags;
Jay Chokshi12e49bf2011-07-22 16:24:39 -07001191 led_dat->dev = &pdev->dev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001192
1193 rc = get_init_value(led_dat, &led_dat->reg);
1194 if (rc < 0)
1195 goto fail_id_check;
1196
Devin Kim232d6652012-08-09 20:41:00 -07001197 rc = pm8xxx_set_led_mode_and_adjust_brightness(led_dat,
Jay Chokshi8994e392011-09-14 18:20:39 -07001198 led_cfg->mode, led_cfg->max_current);
Jay Chokshide4cefb2011-08-04 18:10:44 -07001199 if (rc < 0)
1200 goto fail_id_check;
1201
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001202 mutex_init(&led_dat->lock);
1203 INIT_WORK(&led_dat->work, pm8xxx_led_work);
1204
1205 rc = led_classdev_register(&pdev->dev, &led_dat->cdev);
1206 if (rc) {
1207 dev_err(&pdev->dev, "unable to register led %d,rc=%d\n",
1208 led_dat->id, rc);
1209 goto fail_id_check;
1210 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001211
Mohan Pallaka53fe1a12011-12-15 16:56:37 +05301212 /* configure default state */
1213 if (led_cfg->default_state)
samin.ryue0041de2012-08-03 23:58:14 +09001214 led_dat->cdev.brightness = led_dat->cdev.max_brightness;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +05301215 else
samin.ryue0041de2012-08-03 23:58:14 +09001216 led_dat->cdev.brightness = LED_OFF;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +05301217
Jay Chokshi868312e2011-09-16 13:57:13 -07001218 if (led_cfg->mode != PM8XXX_LED_MODE_MANUAL) {
Amy Malochec17c3732012-02-27 18:34:07 -08001219 if (led_dat->id == PM8XXX_ID_RGB_LED_RED ||
1220 led_dat->id == PM8XXX_ID_RGB_LED_GREEN ||
1221 led_dat->id == PM8XXX_ID_RGB_LED_BLUE)
1222 __pm8xxx_led_work(led_dat, 0);
1223 else
1224 __pm8xxx_led_work(led_dat,
Jay Chokshide4cefb2011-08-04 18:10:44 -07001225 led_dat->cdev.max_brightness);
Jay Chokshi868312e2011-09-16 13:57:13 -07001226
1227 if (led_dat->pwm_channel != -1) {
samin.ryuc650af32012-08-29 11:34:34 +09001228 if (led_cfg->pwm_adjust_brightness) {
1229 led_dat->adjust_brightness = led_cfg->pwm_adjust_brightness;
1230 } else {
1231 led_dat->adjust_brightness = 100;
1232 }
1233
Jay Chokshi868312e2011-09-16 13:57:13 -07001234 rc = pm8xxx_led_pwm_configure(led_dat);
1235 if (rc) {
1236 dev_err(&pdev->dev, "failed to "
1237 "configure LED, error: %d\n", rc);
1238 goto fail_id_check;
1239 }
samin.ryue0041de2012-08-03 23:58:14 +09001240 schedule_work(&led->work);
Jay Chokshi868312e2011-09-16 13:57:13 -07001241 }
1242 } else {
samin.ryue0041de2012-08-03 23:58:14 +09001243 __pm8xxx_led_work(led_dat, led_dat->cdev.brightness);
Jay Chokshi868312e2011-09-16 13:57:13 -07001244 }
Jay Chokshide4cefb2011-08-04 18:10:44 -07001245 }
Jay Chokshi12e49bf2011-07-22 16:24:39 -07001246
samin.ryue0041de2012-08-03 23:58:14 +09001247 led->use_pwm = pdata->use_pwm;
1248
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001249 platform_set_drvdata(pdev, led);
1250
samin.ryue0041de2012-08-03 23:58:14 +09001251 rc = device_create_file(&pdev->dev, &dev_attr_lock);
1252 if (rc) {
1253 device_remove_file(&pdev->dev, &dev_attr_lock);
1254 dev_err(&pdev->dev, "failed device_create_file(lock)\n");
1255 }
1256
1257 if (led->use_pwm) {
1258 rc = device_create_file(&pdev->dev, &dev_attr_blink);
1259 if (rc) {
1260 device_remove_file(&pdev->dev, &dev_attr_blink);
1261 dev_err(&pdev->dev, "failed device_create_file(blink)\n");
1262 }
1263
1264 rc = device_create_file(&pdev->dev, &dev_attr_grppwm);
1265 if (rc) {
1266 device_remove_file(&pdev->dev, &dev_attr_grppwm);
1267 dev_err(&pdev->dev, "failed device_create_fiaild(grppwm)\n");
1268 }
1269
1270 rc = device_create_file(&pdev->dev, &dev_attr_grpfreq);
1271 if (rc) {
1272 device_remove_file(&pdev->dev, &dev_attr_grpfreq);
1273 dev_err(&pdev->dev, "failed device_create_file(grpfreq)\n");
1274 }
1275 }
1276
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001277 return 0;
1278
1279fail_id_check:
1280 if (i > 0) {
1281 for (i = i - 1; i >= 0; i--) {
1282 mutex_destroy(&led[i].lock);
1283 led_classdev_unregister(&led[i].cdev);
Jay Chokshi868312e2011-09-16 13:57:13 -07001284 if (led[i].pwm_dev != NULL)
1285 pwm_free(led[i].pwm_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001286 }
1287 }
1288 kfree(led);
1289 return rc;
1290}
1291
1292static int __devexit pm8xxx_led_remove(struct platform_device *pdev)
1293{
1294 int i;
1295 const struct led_platform_data *pdata =
1296 pdev->dev.platform_data;
1297 struct pm8xxx_led_data *led = platform_get_drvdata(pdev);
1298
1299 for (i = 0; i < pdata->num_leds; i++) {
1300 cancel_work_sync(&led[i].work);
1301 mutex_destroy(&led[i].lock);
1302 led_classdev_unregister(&led[i].cdev);
samin.ryue0041de2012-08-03 23:58:14 +09001303 if (led[i].pwm_dev != NULL) {
Jay Chokshi868312e2011-09-16 13:57:13 -07001304 pwm_free(led[i].pwm_dev);
samin.ryue0041de2012-08-03 23:58:14 +09001305 }
1306 }
1307
1308 device_remove_file(&pdev->dev, &dev_attr_lock);
1309
1310 if (led->use_pwm) {
1311 device_remove_file(&pdev->dev, &dev_attr_blink);
1312 device_remove_file(&pdev->dev, &dev_attr_grppwm);
1313 device_remove_file(&pdev->dev, &dev_attr_grpfreq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001314 }
1315
1316 kfree(led);
1317
1318 return 0;
1319}
1320
1321static struct platform_driver pm8xxx_led_driver = {
1322 .probe = pm8xxx_led_probe,
1323 .remove = __devexit_p(pm8xxx_led_remove),
1324 .driver = {
1325 .name = PM8XXX_LEDS_DEV_NAME,
1326 .owner = THIS_MODULE,
1327 },
1328};
1329
1330static int __init pm8xxx_led_init(void)
1331{
1332 return platform_driver_register(&pm8xxx_led_driver);
1333}
Jay Chokshi12e49bf2011-07-22 16:24:39 -07001334subsys_initcall(pm8xxx_led_init);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001335
1336static void __exit pm8xxx_led_exit(void)
1337{
1338 platform_driver_unregister(&pm8xxx_led_driver);
1339}
1340module_exit(pm8xxx_led_exit);
1341
1342MODULE_DESCRIPTION("PM8XXX LEDs driver");
1343MODULE_LICENSE("GPL v2");
1344MODULE_VERSION("1.0");
1345MODULE_ALIAS("platform:pm8xxx-led");