blob: 2d14cca72f390a5d52771eaae95e7bb281ad897c [file] [log] [blame]
Amy Malochee7d7ef32013-01-30 16:16:35 -08001/* Copyright (c) 2010-2013, 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>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070023
24#include <linux/mfd/pm8xxx/core.h>
Jay Chokshi868312e2011-09-16 13:57:13 -070025#include <linux/mfd/pm8xxx/pwm.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026#include <linux/leds-pm8xxx.h>
27
28#define SSBI_REG_ADDR_DRV_KEYPAD 0x48
29#define PM8XXX_DRV_KEYPAD_BL_MASK 0xf0
30#define PM8XXX_DRV_KEYPAD_BL_SHIFT 0x04
31
32#define SSBI_REG_ADDR_FLASH_DRV0 0x49
33#define PM8XXX_DRV_FLASH_MASK 0xf0
34#define PM8XXX_DRV_FLASH_SHIFT 0x04
35
36#define SSBI_REG_ADDR_FLASH_DRV1 0xFB
37
38#define SSBI_REG_ADDR_LED_CTRL_BASE 0x131
39#define SSBI_REG_ADDR_LED_CTRL(n) (SSBI_REG_ADDR_LED_CTRL_BASE + (n))
40#define PM8XXX_DRV_LED_CTRL_MASK 0xf8
41#define PM8XXX_DRV_LED_CTRL_SHIFT 0x03
42
Mohan Pallaka53fe1a12011-12-15 16:56:37 +053043#define SSBI_REG_ADDR_WLED_CTRL_BASE 0x25A
44#define SSBI_REG_ADDR_WLED_CTRL(n) (SSBI_REG_ADDR_WLED_CTRL_BASE + (n) - 1)
45
46/* wled control registers */
47#define WLED_MOD_CTRL_REG SSBI_REG_ADDR_WLED_CTRL(1)
48#define WLED_MAX_CURR_CFG_REG(n) SSBI_REG_ADDR_WLED_CTRL(n + 2)
Mohan Pallaka58ff4052012-11-19 16:58:59 +053049#define WLED_BRIGHTNESS_CNTL_REG1(n) SSBI_REG_ADDR_WLED_CTRL((2 * n) + 5)
50#define WLED_BRIGHTNESS_CNTL_REG2(n) SSBI_REG_ADDR_WLED_CTRL((2 * n) + 6)
Mohan Pallaka53fe1a12011-12-15 16:56:37 +053051#define WLED_SYNC_REG SSBI_REG_ADDR_WLED_CTRL(11)
52#define WLED_OVP_CFG_REG SSBI_REG_ADDR_WLED_CTRL(13)
53#define WLED_BOOST_CFG_REG SSBI_REG_ADDR_WLED_CTRL(14)
54#define WLED_HIGH_POLE_CAP_REG SSBI_REG_ADDR_WLED_CTRL(16)
55
56#define WLED_STRINGS 0x03
57#define WLED_OVP_VAL_MASK 0x30
58#define WLED_OVP_VAL_BIT_SHFT 0x04
59#define WLED_BOOST_LIMIT_MASK 0xE0
60#define WLED_BOOST_LIMIT_BIT_SHFT 0x05
Amy Maloche56913f52012-05-11 10:47:24 -070061#define WLED_BOOST_OFF 0x00
Mohan Pallaka53fe1a12011-12-15 16:56:37 +053062#define WLED_EN_MASK 0x01
63#define WLED_CP_SELECT_MAX 0x03
64#define WLED_CP_SELECT_MASK 0x03
65#define WLED_DIG_MOD_GEN_MASK 0x70
66#define WLED_CS_OUT_MASK 0x0E
67#define WLED_CTL_DLY_STEP 200
68#define WLED_CTL_DLY_MAX 1400
69#define WLED_CTL_DLY_MASK 0xE0
70#define WLED_CTL_DLY_BIT_SHFT 0x05
71#define WLED_MAX_CURR 25
72#define WLED_MAX_CURR_MASK 0x1F
Amy Malochebe73e192012-11-29 12:53:59 -080073#define WLED_BRIGHTNESS_MSB_MASK 0x0F
Mohan Pallaka53fe1a12011-12-15 16:56:37 +053074#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 Malochec366d5b2012-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
Amy Malochec366d5b2012-08-28 11:06:40 -070090#define WLED_CABC_SHIFT 3
Mohan Pallaka53fe1a12011-12-15 16:56:37 +053091
Amy Malochec17c3732012-02-27 18:34:07 -080092#define SSBI_REG_ADDR_RGB_CNTL1 0x12D
93#define SSBI_REG_ADDR_RGB_CNTL2 0x12E
94
95#define PM8XXX_DRV_RGB_RED_LED BIT(2)
96#define PM8XXX_DRV_RGB_GREEN_LED BIT(1)
97#define PM8XXX_DRV_RGB_BLUE_LED BIT(0)
98
Jay Chokshi12e49bf2011-07-22 16:24:39 -070099#define MAX_FLASH_LED_CURRENT 300
100#define MAX_LC_LED_CURRENT 40
101#define MAX_KP_BL_LED_CURRENT 300
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700103#define PM8XXX_ID_LED_CURRENT_FACTOR 2 /* Iout = x * 2mA */
104#define PM8XXX_ID_FLASH_CURRENT_FACTOR 20 /* Iout = x * 20mA */
105
106#define PM8XXX_FLASH_MODE_DBUS1 1
107#define PM8XXX_FLASH_MODE_DBUS2 2
108#define PM8XXX_FLASH_MODE_PWM 3
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700110#define MAX_LC_LED_BRIGHTNESS 20
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700111#define MAX_FLASH_BRIGHTNESS 15
112#define MAX_KB_LED_BRIGHTNESS 15
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113
Jay Chokshide4cefb2011-08-04 18:10:44 -0700114#define PM8XXX_LED_OFFSET(id) ((id) - PM8XXX_ID_LED_0)
115
Jay Chokshi868312e2011-09-16 13:57:13 -0700116#define PM8XXX_LED_PWM_FLAGS (PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP)
117
Amy Maloche3d2c24c2012-03-22 19:02:25 -0700118#define LED_MAP(_version, _kb, _led0, _led1, _led2, _flash_led0, _flash_led1, \
Amy Malochec17c3732012-02-27 18:34:07 -0800119 _wled, _rgb_led_red, _rgb_led_green, _rgb_led_blue)\
Amy Maloche3d2c24c2012-03-22 19:02:25 -0700120 {\
121 .version = _version,\
122 .supported = _kb << PM8XXX_ID_LED_KB_LIGHT | \
123 _led0 << PM8XXX_ID_LED_0 | _led1 << PM8XXX_ID_LED_1 | \
124 _led2 << PM8XXX_ID_LED_2 | \
125 _flash_led0 << PM8XXX_ID_FLASH_LED_0 | \
126 _flash_led1 << PM8XXX_ID_FLASH_LED_1 | \
Amy Malochec17c3732012-02-27 18:34:07 -0800127 _wled << PM8XXX_ID_WLED | \
128 _rgb_led_red << PM8XXX_ID_RGB_LED_RED | \
129 _rgb_led_green << PM8XXX_ID_RGB_LED_GREEN | \
130 _rgb_led_blue << PM8XXX_ID_RGB_LED_BLUE, \
Amy Maloche3d2c24c2012-03-22 19:02:25 -0700131 }
132
133/**
134 * supported_leds - leds supported for each PMIC version
135 * @version - version of PMIC
136 * @supported - which leds are supported on version
137 */
138
139struct supported_leds {
140 enum pm8xxx_version version;
141 u32 supported;
142};
143
144static const struct supported_leds led_map[] = {
Amy Malochec17c3732012-02-27 18:34:07 -0800145 LED_MAP(PM8XXX_VERSION_8058, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
146 LED_MAP(PM8XXX_VERSION_8921, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
147 LED_MAP(PM8XXX_VERSION_8018, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0),
148 LED_MAP(PM8XXX_VERSION_8922, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1),
149 LED_MAP(PM8XXX_VERSION_8038, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1),
Amy Maloche3d2c24c2012-03-22 19:02:25 -0700150};
151
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700152/**
153 * struct pm8xxx_led_data - internal led data structure
154 * @led_classdev - led class device
155 * @id - led index
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700156 * @work - workqueue for led
157 * @lock - to protect the transactions
158 * @reg - cached value of led register
Jay Chokshi868312e2011-09-16 13:57:13 -0700159 * @pwm_dev - pointer to PWM device if LED is driven using PWM
160 * @pwm_channel - PWM channel ID
161 * @pwm_period_us - PWM period in micro seconds
162 * @pwm_duty_cycles - struct that describes PWM duty cycles info
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700163 */
164struct pm8xxx_led_data {
165 struct led_classdev cdev;
166 int id;
167 u8 reg;
Amy Maloche56913f52012-05-11 10:47:24 -0700168 u8 wled_mod_ctrl_val;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700169 struct device *dev;
170 struct work_struct work;
171 struct mutex lock;
Jay Chokshi868312e2011-09-16 13:57:13 -0700172 struct pwm_device *pwm_dev;
173 int pwm_channel;
174 u32 pwm_period_us;
175 struct pm8xxx_pwm_duty_cycles *pwm_duty_cycles;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530176 struct wled_config_data *wled_cfg;
177 int max_current;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178};
179
180static void led_kp_set(struct pm8xxx_led_data *led, enum led_brightness value)
181{
182 int rc;
183 u8 level;
184
185 level = (value << PM8XXX_DRV_KEYPAD_BL_SHIFT) &
186 PM8XXX_DRV_KEYPAD_BL_MASK;
187
188 led->reg &= ~PM8XXX_DRV_KEYPAD_BL_MASK;
189 led->reg |= level;
190
191 rc = pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_DRV_KEYPAD,
192 led->reg);
193 if (rc < 0)
194 dev_err(led->cdev.dev,
195 "can't set keypad backlight level rc=%d\n", rc);
196}
197
198static void led_lc_set(struct pm8xxx_led_data *led, enum led_brightness value)
199{
200 int rc, offset;
201 u8 level;
202
203 level = (value << PM8XXX_DRV_LED_CTRL_SHIFT) &
204 PM8XXX_DRV_LED_CTRL_MASK;
205
206 offset = PM8XXX_LED_OFFSET(led->id);
207
208 led->reg &= ~PM8XXX_DRV_LED_CTRL_MASK;
209 led->reg |= level;
210
211 rc = pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_LED_CTRL(offset),
212 led->reg);
213 if (rc)
214 dev_err(led->cdev.dev, "can't set (%d) led value rc=%d\n",
215 led->id, rc);
216}
217
218static void
219led_flash_set(struct pm8xxx_led_data *led, enum led_brightness value)
220{
221 int rc;
222 u8 level;
223 u16 reg_addr;
224
225 level = (value << PM8XXX_DRV_FLASH_SHIFT) &
226 PM8XXX_DRV_FLASH_MASK;
227
228 led->reg &= ~PM8XXX_DRV_FLASH_MASK;
229 led->reg |= level;
230
231 if (led->id == PM8XXX_ID_FLASH_LED_0)
232 reg_addr = SSBI_REG_ADDR_FLASH_DRV0;
233 else
234 reg_addr = SSBI_REG_ADDR_FLASH_DRV1;
235
236 rc = pm8xxx_writeb(led->dev->parent, reg_addr, led->reg);
237 if (rc < 0)
238 dev_err(led->cdev.dev, "can't set flash led%d level rc=%d\n",
239 led->id, rc);
240}
241
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530242static int
243led_wled_set(struct pm8xxx_led_data *led, enum led_brightness value)
244{
245 int rc, duty;
Amy Malochee7d7ef32013-01-30 16:16:35 -0800246 u8 val, i;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530247
248 if (value > WLED_MAX_LEVEL)
249 value = WLED_MAX_LEVEL;
250
Amy Maloche56913f52012-05-11 10:47:24 -0700251 if (value == 0) {
252 rc = pm8xxx_writeb(led->dev->parent, WLED_MOD_CTRL_REG,
253 WLED_BOOST_OFF);
254 if (rc) {
255 dev_err(led->dev->parent, "can't write wled ctrl config"
256 " register rc=%d\n", rc);
257 return rc;
258 }
259 } else {
260 rc = pm8xxx_writeb(led->dev->parent, WLED_MOD_CTRL_REG,
261 led->wled_mod_ctrl_val);
262 if (rc) {
263 dev_err(led->dev->parent, "can't write wled ctrl config"
264 " register rc=%d\n", rc);
265 return rc;
266 }
267 }
268
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530269 duty = (WLED_MAX_DUTY_CYCLE * value) / WLED_MAX_LEVEL;
270
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530271 /* program brightness control registers */
Amy Malochee7d7ef32013-01-30 16:16:35 -0800272 for (i = 0; i < WLED_STRINGS; i++) {
273 if (led->wled_cfg->strings && (1 << i)) {
274 rc = pm8xxx_readb(led->dev->parent,
275 WLED_BRIGHTNESS_CNTL_REG1(i), &val);
276 if (rc) {
277 dev_err(led->dev->parent,
278 "can't read wled brightnes ctrl"
279 " register1 rc=%d\n", rc);
280 return rc;
281 }
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530282
Amy Malochee7d7ef32013-01-30 16:16:35 -0800283 val = (val & ~WLED_MAX_CURR_MASK) |
284 (duty >> WLED_8_BIT_SHFT);
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530285
Amy Malochee7d7ef32013-01-30 16:16:35 -0800286 rc = pm8xxx_writeb(led->dev->parent,
287 WLED_BRIGHTNESS_CNTL_REG1(i), val);
288 if (rc) {
289 dev_err(led->dev->parent,
290 "can't write wled brightness ctrl"
291 " register1 rc=%d\n", rc);
292 return rc;
293 }
294
295 val = duty & WLED_8_BIT_MASK;
296 rc = pm8xxx_writeb(led->dev->parent,
297 WLED_BRIGHTNESS_CNTL_REG2(i), val);
298 if (rc) {
299 dev_err(led->dev->parent,
300 "can't write wled brightness ctrl"
301 " register2 rc=%d\n", rc);
302 return rc;
303 }
304 } else
305 continue;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530306 }
Amy Malochec366d5b2012-08-28 11:06:40 -0700307 rc = pm8xxx_readb(led->dev->parent, WLED_SYNC_REG, &val);
308 if (rc) {
309 dev_err(led->dev->parent,
310 "can't read wled sync register rc=%d\n", rc);
311 return rc;
312 }
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530313 /* sync */
Amy Malochec366d5b2012-08-28 11:06:40 -0700314 val &= WLED_SYNC_MASK;
315 val |= WLED_SYNC_VAL;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530316 rc = pm8xxx_writeb(led->dev->parent, WLED_SYNC_REG, val);
317 if (rc) {
318 dev_err(led->dev->parent,
319 "can't read wled sync register rc=%d\n", rc);
320 return rc;
321 }
Amy Malochec366d5b2012-08-28 11:06:40 -0700322 val &= WLED_SYNC_MASK;
323 val |= WLED_SYNC_RESET_VAL;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530324 rc = pm8xxx_writeb(led->dev->parent, WLED_SYNC_REG, val);
325 if (rc) {
326 dev_err(led->dev->parent,
327 "can't read wled sync register rc=%d\n", rc);
328 return rc;
329 }
330 return 0;
331}
332
333static void wled_dump_regs(struct pm8xxx_led_data *led)
334{
335 int i;
336 u8 val;
337
338 for (i = 1; i < 17; i++) {
339 pm8xxx_readb(led->dev->parent,
340 SSBI_REG_ADDR_WLED_CTRL(i), &val);
341 pr_debug("WLED_CTRL_%d = 0x%x\n", i, val);
342 }
343}
344
Amy Malochec17c3732012-02-27 18:34:07 -0800345static void
Amy Malochead3c7842012-07-20 14:51:27 -0700346led_rgb_write(struct pm8xxx_led_data *led, u16 addr, enum led_brightness value)
Amy Malochec17c3732012-02-27 18:34:07 -0800347{
348 int rc;
349 u8 val, mask;
350
Amy Malochead3c7842012-07-20 14:51:27 -0700351 if (led->id != PM8XXX_ID_RGB_LED_BLUE &&
352 led->id != PM8XXX_ID_RGB_LED_RED &&
353 led->id != PM8XXX_ID_RGB_LED_GREEN)
354 return;
355
356 rc = pm8xxx_readb(led->dev->parent, addr, &val);
Amy Malochec17c3732012-02-27 18:34:07 -0800357 if (rc) {
358 dev_err(led->cdev.dev, "can't read rgb ctrl register rc=%d\n",
359 rc);
360 return;
361 }
362
363 switch (led->id) {
364 case PM8XXX_ID_RGB_LED_RED:
365 mask = PM8XXX_DRV_RGB_RED_LED;
366 break;
367 case PM8XXX_ID_RGB_LED_GREEN:
368 mask = PM8XXX_DRV_RGB_GREEN_LED;
369 break;
370 case PM8XXX_ID_RGB_LED_BLUE:
371 mask = PM8XXX_DRV_RGB_BLUE_LED;
372 break;
373 default:
374 return;
375 }
376
377 if (value)
378 val |= mask;
379 else
380 val &= ~mask;
381
Amy Malochead3c7842012-07-20 14:51:27 -0700382 rc = pm8xxx_writeb(led->dev->parent, addr, val);
Amy Malochec17c3732012-02-27 18:34:07 -0800383 if (rc < 0)
384 dev_err(led->cdev.dev, "can't set rgb led %d level rc=%d\n",
385 led->id, rc);
386}
387
Amy Malochead3c7842012-07-20 14:51:27 -0700388static void
389led_rgb_set(struct pm8xxx_led_data *led, enum led_brightness value)
390{
391 if (value) {
392 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL1, value);
393 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL2, value);
394 } else {
395 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL2, value);
396 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL1, value);
397 }
398}
399
Jay Chokshi868312e2011-09-16 13:57:13 -0700400static int pm8xxx_led_pwm_work(struct pm8xxx_led_data *led)
401{
402 int duty_us;
403 int rc = 0;
404
405 if (led->pwm_duty_cycles == NULL) {
406 duty_us = (led->pwm_period_us * led->cdev.brightness) /
407 LED_FULL;
408 rc = pwm_config(led->pwm_dev, duty_us, led->pwm_period_us);
Amy Malochead3c7842012-07-20 14:51:27 -0700409 if (led->cdev.brightness) {
410 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL1,
411 led->cdev.brightness);
Jay Chokshi868312e2011-09-16 13:57:13 -0700412 rc = pwm_enable(led->pwm_dev);
Amy Malochead3c7842012-07-20 14:51:27 -0700413 } else {
Jay Chokshi868312e2011-09-16 13:57:13 -0700414 pwm_disable(led->pwm_dev);
Amy Malochead3c7842012-07-20 14:51:27 -0700415 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL1,
416 led->cdev.brightness);
417 }
Jay Chokshi868312e2011-09-16 13:57:13 -0700418 } else {
Amy Malochead3c7842012-07-20 14:51:27 -0700419 if (led->cdev.brightness)
420 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL1,
421 led->cdev.brightness);
Jay Chokshi868312e2011-09-16 13:57:13 -0700422 rc = pm8xxx_pwm_lut_enable(led->pwm_dev, led->cdev.brightness);
Amy Malochead3c7842012-07-20 14:51:27 -0700423 if (!led->cdev.brightness)
424 led_rgb_write(led, SSBI_REG_ADDR_RGB_CNTL1,
425 led->cdev.brightness);
Jay Chokshi868312e2011-09-16 13:57:13 -0700426 }
427
428 return rc;
429}
430
Jay Chokshide4cefb2011-08-04 18:10:44 -0700431static void __pm8xxx_led_work(struct pm8xxx_led_data *led,
432 enum led_brightness level)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700433{
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530434 int rc;
435
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700436 mutex_lock(&led->lock);
437
438 switch (led->id) {
439 case PM8XXX_ID_LED_KB_LIGHT:
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700440 led_kp_set(led, level);
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530441 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700442 case PM8XXX_ID_LED_0:
443 case PM8XXX_ID_LED_1:
444 case PM8XXX_ID_LED_2:
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700445 led_lc_set(led, level);
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530446 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700447 case PM8XXX_ID_FLASH_LED_0:
448 case PM8XXX_ID_FLASH_LED_1:
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700449 led_flash_set(led, level);
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530450 break;
451 case PM8XXX_ID_WLED:
452 rc = led_wled_set(led, level);
453 if (rc < 0)
454 pr_err("wled brightness set failed %d\n", rc);
455 break;
Amy Malochec17c3732012-02-27 18:34:07 -0800456 case PM8XXX_ID_RGB_LED_RED:
457 case PM8XXX_ID_RGB_LED_GREEN:
458 case PM8XXX_ID_RGB_LED_BLUE:
459 led_rgb_set(led, level);
460 break;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530461 default:
462 dev_err(led->cdev.dev, "unknown led id %d", led->id);
463 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700464 }
465
466 mutex_unlock(&led->lock);
467}
468
Jay Chokshide4cefb2011-08-04 18:10:44 -0700469static void pm8xxx_led_work(struct work_struct *work)
470{
Jay Chokshi868312e2011-09-16 13:57:13 -0700471 int rc;
472
Jay Chokshide4cefb2011-08-04 18:10:44 -0700473 struct pm8xxx_led_data *led = container_of(work,
474 struct pm8xxx_led_data, work);
Jay Chokshide4cefb2011-08-04 18:10:44 -0700475
Jay Chokshi868312e2011-09-16 13:57:13 -0700476 if (led->pwm_dev == NULL) {
477 __pm8xxx_led_work(led, led->cdev.brightness);
478 } else {
479 rc = pm8xxx_led_pwm_work(led);
480 if (rc)
481 pr_err("could not configure PWM mode for LED:%d\n",
482 led->id);
483 }
Jay Chokshide4cefb2011-08-04 18:10:44 -0700484}
485
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700486static void pm8xxx_led_set(struct led_classdev *led_cdev,
487 enum led_brightness value)
488{
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700489 struct pm8xxx_led_data *led;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700490
491 led = container_of(led_cdev, struct pm8xxx_led_data, cdev);
492
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700493 if (value < LED_OFF || value > led->cdev.max_brightness) {
494 dev_err(led->cdev.dev, "Invalid brightness value exceeds");
495 return;
496 }
497
498 led->cdev.brightness = value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700499 schedule_work(&led->work);
500}
501
Jay Chokshide4cefb2011-08-04 18:10:44 -0700502static int pm8xxx_set_led_mode_and_max_brightness(struct pm8xxx_led_data *led,
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700503 enum pm8xxx_led_modes led_mode, int max_current)
504{
Jay Chokshide4cefb2011-08-04 18:10:44 -0700505 switch (led->id) {
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700506 case PM8XXX_ID_LED_0:
507 case PM8XXX_ID_LED_1:
508 case PM8XXX_ID_LED_2:
509 led->cdev.max_brightness = max_current /
510 PM8XXX_ID_LED_CURRENT_FACTOR;
Jay Chokshide4cefb2011-08-04 18:10:44 -0700511 if (led->cdev.max_brightness > MAX_LC_LED_BRIGHTNESS)
512 led->cdev.max_brightness = MAX_LC_LED_BRIGHTNESS;
Willie Ruana39d1d42011-08-19 13:54:34 -0700513 led->reg = led_mode;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700514 break;
515 case PM8XXX_ID_LED_KB_LIGHT:
516 case PM8XXX_ID_FLASH_LED_0:
517 case PM8XXX_ID_FLASH_LED_1:
518 led->cdev.max_brightness = max_current /
519 PM8XXX_ID_FLASH_CURRENT_FACTOR;
Jay Chokshide4cefb2011-08-04 18:10:44 -0700520 if (led->cdev.max_brightness > MAX_FLASH_BRIGHTNESS)
521 led->cdev.max_brightness = MAX_FLASH_BRIGHTNESS;
522
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700523 switch (led_mode) {
524 case PM8XXX_LED_MODE_PWM1:
525 case PM8XXX_LED_MODE_PWM2:
526 case PM8XXX_LED_MODE_PWM3:
Willie Ruana39d1d42011-08-19 13:54:34 -0700527 led->reg = PM8XXX_FLASH_MODE_PWM;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700528 break;
529 case PM8XXX_LED_MODE_DTEST1:
Willie Ruana39d1d42011-08-19 13:54:34 -0700530 led->reg = PM8XXX_FLASH_MODE_DBUS1;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700531 break;
532 case PM8XXX_LED_MODE_DTEST2:
Willie Ruana39d1d42011-08-19 13:54:34 -0700533 led->reg = PM8XXX_FLASH_MODE_DBUS2;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700534 break;
535 default:
Willie Ruana39d1d42011-08-19 13:54:34 -0700536 led->reg = PM8XXX_LED_MODE_MANUAL;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700537 break;
538 }
539 break;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530540 case PM8XXX_ID_WLED:
541 led->cdev.max_brightness = WLED_MAX_LEVEL;
542 break;
Amy Malochec17c3732012-02-27 18:34:07 -0800543 case PM8XXX_ID_RGB_LED_RED:
544 case PM8XXX_ID_RGB_LED_GREEN:
545 case PM8XXX_ID_RGB_LED_BLUE:
546 led->cdev.max_brightness = LED_FULL;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700547 break;
Amy Malochec17c3732012-02-27 18:34:07 -0800548 default:
549 dev_err(led->cdev.dev, "LED Id is invalid");
550 return -EINVAL;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700551 }
552
Amy Malochec17c3732012-02-27 18:34:07 -0800553 return 0;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700554}
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700555
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700556static enum led_brightness pm8xxx_led_get(struct led_classdev *led_cdev)
557{
558 struct pm8xxx_led_data *led;
559
560 led = container_of(led_cdev, struct pm8xxx_led_data, cdev);
561
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700562 return led->cdev.brightness;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563}
564
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530565static int __devinit init_wled(struct pm8xxx_led_data *led)
566{
567 int rc, i;
Amy Malochee7d7ef32013-01-30 16:16:35 -0800568 u8 val;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530569
570 /* program over voltage protection threshold */
571 if (led->wled_cfg->ovp_val > WLED_OVP_37V) {
572 dev_err(led->dev->parent, "Invalid ovp value");
573 return -EINVAL;
574 }
575
576 rc = pm8xxx_readb(led->dev->parent, WLED_OVP_CFG_REG, &val);
577 if (rc) {
578 dev_err(led->dev->parent, "can't read wled ovp config"
579 " register rc=%d\n", rc);
580 return rc;
581 }
582
583 val = (val & ~WLED_OVP_VAL_MASK) |
584 (led->wled_cfg->ovp_val << WLED_OVP_VAL_BIT_SHFT);
585
586 rc = pm8xxx_writeb(led->dev->parent, WLED_OVP_CFG_REG, val);
587 if (rc) {
588 dev_err(led->dev->parent, "can't write wled ovp config"
589 " register rc=%d\n", rc);
590 return rc;
591 }
592
593 /* program current boost limit and output feedback*/
594 if (led->wled_cfg->boost_curr_lim > WLED_CURR_LIMIT_1680mA) {
595 dev_err(led->dev->parent, "Invalid boost current limit");
596 return -EINVAL;
597 }
598
599 rc = pm8xxx_readb(led->dev->parent, WLED_BOOST_CFG_REG, &val);
600 if (rc) {
601 dev_err(led->dev->parent, "can't read wled boost config"
602 " register rc=%d\n", rc);
603 return rc;
604 }
605
606 val = (val & ~WLED_BOOST_LIMIT_MASK) |
607 (led->wled_cfg->boost_curr_lim << WLED_BOOST_LIMIT_BIT_SHFT);
608
609 val = (val & ~WLED_OP_FDBCK_MASK) |
610 (led->wled_cfg->op_fdbck << WLED_OP_FDBCK_BIT_SHFT);
611
612 rc = pm8xxx_writeb(led->dev->parent, WLED_BOOST_CFG_REG, val);
613 if (rc) {
614 dev_err(led->dev->parent, "can't write wled boost config"
615 " register rc=%d\n", rc);
616 return rc;
617 }
618
619 /* program high pole capacitance */
620 if (led->wled_cfg->cp_select > WLED_CP_SELECT_MAX) {
621 dev_err(led->dev->parent, "Invalid pole capacitance");
622 return -EINVAL;
623 }
624
625 rc = pm8xxx_readb(led->dev->parent, WLED_HIGH_POLE_CAP_REG, &val);
626 if (rc) {
627 dev_err(led->dev->parent, "can't read wled high pole"
628 " capacitance register rc=%d\n", rc);
629 return rc;
630 }
631
632 val = (val & ~WLED_CP_SELECT_MASK) | led->wled_cfg->cp_select;
633
634 rc = pm8xxx_writeb(led->dev->parent, WLED_HIGH_POLE_CAP_REG, val);
635 if (rc) {
636 dev_err(led->dev->parent, "can't write wled high pole"
637 " capacitance register rc=%d\n", rc);
638 return rc;
639 }
640
641 /* program activation delay and maximum current */
Amy Malochee7d7ef32013-01-30 16:16:35 -0800642 for (i = 0; i < WLED_STRINGS; i++) {
643 if (led->wled_cfg->strings && (1 << i)) {
644 rc = pm8xxx_readb(led->dev->parent,
645 WLED_MAX_CURR_CFG_REG(i + 2), &val);
646 if (rc) {
647 dev_err(led->dev->parent,
648 "can't read wled max current"
649 " config register rc=%d\n", rc);
650 return rc;
651 }
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530652
Amy Malochee7d7ef32013-01-30 16:16:35 -0800653 if ((led->wled_cfg->ctrl_delay_us % WLED_CTL_DLY_STEP)
654 || (led->wled_cfg->ctrl_delay_us >
655 WLED_CTL_DLY_MAX)) {
656 dev_err(led->dev->parent,
657 "Invalid control delay\n");
658 return rc;
659 }
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530660
Amy Malochee7d7ef32013-01-30 16:16:35 -0800661 val = val / WLED_CTL_DLY_STEP;
662 val = (val & ~WLED_CTL_DLY_MASK) |
663 (led->wled_cfg->ctrl_delay_us <<
664 WLED_CTL_DLY_BIT_SHFT);
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530665
Amy Malochee7d7ef32013-01-30 16:16:35 -0800666 if ((led->max_current > WLED_MAX_CURR)) {
667 dev_err(led->dev->parent,
668 "Invalid max current\n");
669 return -EINVAL;
670 }
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530671
Amy Malochee7d7ef32013-01-30 16:16:35 -0800672 val = (val & ~WLED_MAX_CURR_MASK) | led->max_current;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530673
Amy Malochee7d7ef32013-01-30 16:16:35 -0800674 rc = pm8xxx_writeb(led->dev->parent,
675 WLED_MAX_CURR_CFG_REG(i + 2), val);
676 if (rc) {
677 dev_err(led->dev->parent,
678 "can't write wled max current"
679 " config register rc=%d\n", rc);
680 return rc;
681 }
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530682 }
683 }
684
Amy Malochec366d5b2012-08-28 11:06:40 -0700685 if (led->wled_cfg->cabc_en) {
686 rc = pm8xxx_readb(led->dev->parent, WLED_SYNC_REG, &val);
687 if (rc) {
688 dev_err(led->dev->parent,
689 "can't read cabc register rc=%d\n", rc);
690 return rc;
691 }
692
Amy Malochee7d7ef32013-01-30 16:16:35 -0800693 val |= (led->wled_cfg->strings << WLED_CABC_SHIFT);
Amy Malochec366d5b2012-08-28 11:06:40 -0700694
695 rc = pm8xxx_writeb(led->dev->parent, WLED_SYNC_REG, val);
696 if (rc) {
697 dev_err(led->dev->parent,
698 "can't write to enable cabc rc=%d\n", rc);
699 return rc;
700 }
701 }
702
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530703 /* program digital module generator, cs out and enable the module */
704 rc = pm8xxx_readb(led->dev->parent, WLED_MOD_CTRL_REG, &val);
705 if (rc) {
706 dev_err(led->dev->parent, "can't read wled module ctrl"
707 " register rc=%d\n", rc);
708 return rc;
709 }
710
711 if (led->wled_cfg->dig_mod_gen_en)
712 val |= WLED_DIG_MOD_GEN_MASK;
713
714 if (led->wled_cfg->cs_out_en)
715 val |= WLED_CS_OUT_MASK;
716
717 val |= WLED_EN_MASK;
718
719 rc = pm8xxx_writeb(led->dev->parent, WLED_MOD_CTRL_REG, val);
720 if (rc) {
721 dev_err(led->dev->parent, "can't write wled module ctrl"
722 " register rc=%d\n", rc);
723 return rc;
724 }
Amy Maloche56913f52012-05-11 10:47:24 -0700725 led->wled_mod_ctrl_val = val;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530726
727 /* dump wled registers */
728 wled_dump_regs(led);
729
730 return 0;
731}
732
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700733static int __devinit get_init_value(struct pm8xxx_led_data *led, u8 *val)
734{
735 int rc, offset;
736 u16 addr;
737
738 switch (led->id) {
739 case PM8XXX_ID_LED_KB_LIGHT:
740 addr = SSBI_REG_ADDR_DRV_KEYPAD;
741 break;
742 case PM8XXX_ID_LED_0:
743 case PM8XXX_ID_LED_1:
744 case PM8XXX_ID_LED_2:
745 offset = PM8XXX_LED_OFFSET(led->id);
746 addr = SSBI_REG_ADDR_LED_CTRL(offset);
747 break;
748 case PM8XXX_ID_FLASH_LED_0:
749 addr = SSBI_REG_ADDR_FLASH_DRV0;
750 break;
751 case PM8XXX_ID_FLASH_LED_1:
752 addr = SSBI_REG_ADDR_FLASH_DRV1;
753 break;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530754 case PM8XXX_ID_WLED:
755 rc = init_wled(led);
756 if (rc)
757 dev_err(led->cdev.dev, "can't initialize wled rc=%d\n",
758 rc);
759 return rc;
Amy Malochec17c3732012-02-27 18:34:07 -0800760 case PM8XXX_ID_RGB_LED_RED:
761 case PM8XXX_ID_RGB_LED_GREEN:
762 case PM8XXX_ID_RGB_LED_BLUE:
Amy Malochec17c3732012-02-27 18:34:07 -0800763 addr = SSBI_REG_ADDR_RGB_CNTL1;
764 break;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530765 default:
766 dev_err(led->cdev.dev, "unknown led id %d", led->id);
767 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700768 }
769
770 rc = pm8xxx_readb(led->dev->parent, addr, val);
771 if (rc)
772 dev_err(led->cdev.dev, "can't get led(%d) level rc=%d\n",
773 led->id, rc);
774
775 return rc;
776}
777
Jay Chokshi868312e2011-09-16 13:57:13 -0700778static int pm8xxx_led_pwm_configure(struct pm8xxx_led_data *led)
779{
780 int start_idx, idx_len, duty_us, rc;
781
782 led->pwm_dev = pwm_request(led->pwm_channel,
783 led->cdev.name);
784
785 if (IS_ERR_OR_NULL(led->pwm_dev)) {
786 pr_err("could not acquire PWM Channel %d, "
787 "error %ld\n", led->pwm_channel,
788 PTR_ERR(led->pwm_dev));
789 led->pwm_dev = NULL;
790 return -ENODEV;
791 }
792
793 if (led->pwm_duty_cycles != NULL) {
794 start_idx = led->pwm_duty_cycles->start_idx;
795 idx_len = led->pwm_duty_cycles->num_duty_pcts;
796
797 if (idx_len >= PM_PWM_LUT_SIZE && start_idx) {
798 pr_err("Wrong LUT size or index\n");
799 return -EINVAL;
800 }
801 if ((start_idx + idx_len) > PM_PWM_LUT_SIZE) {
802 pr_err("Exceed LUT limit\n");
803 return -EINVAL;
804 }
805
806 rc = pm8xxx_pwm_lut_config(led->pwm_dev, led->pwm_period_us,
807 led->pwm_duty_cycles->duty_pcts,
808 led->pwm_duty_cycles->duty_ms,
809 start_idx, idx_len, 0, 0,
810 PM8XXX_LED_PWM_FLAGS);
811 } else {
812 duty_us = led->pwm_period_us;
813 rc = pwm_config(led->pwm_dev, duty_us, led->pwm_period_us);
814 }
815
816 return rc;
817}
818
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530819
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700820static int __devinit pm8xxx_led_probe(struct platform_device *pdev)
821{
Jay Chokshi8994e392011-09-14 18:20:39 -0700822 const struct pm8xxx_led_platform_data *pdata = pdev->dev.platform_data;
823 const struct led_platform_data *pcore_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700824 struct led_info *curr_led;
825 struct pm8xxx_led_data *led, *led_dat;
Jay Chokshi8994e392011-09-14 18:20:39 -0700826 struct pm8xxx_led_config *led_cfg;
Amy Maloche3d2c24c2012-03-22 19:02:25 -0700827 enum pm8xxx_version version;
828 bool found = false;
829 int rc, i, j;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700830
831 if (pdata == NULL) {
832 dev_err(&pdev->dev, "platform data not supplied\n");
833 return -EINVAL;
834 }
835
Jay Chokshi8994e392011-09-14 18:20:39 -0700836 pcore_data = pdata->led_core;
837
838 if (pcore_data->num_leds != pdata->num_configs) {
839 dev_err(&pdev->dev, "#no. of led configs and #no. of led"
840 "entries are not equal\n");
841 return -EINVAL;
842 }
843
844 led = kcalloc(pcore_data->num_leds, sizeof(*led), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700845 if (led == NULL) {
846 dev_err(&pdev->dev, "failed to alloc memory\n");
847 return -ENOMEM;
848 }
849
Jay Chokshi8994e392011-09-14 18:20:39 -0700850 for (i = 0; i < pcore_data->num_leds; i++) {
851 curr_led = &pcore_data->leds[i];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700852 led_dat = &led[i];
Jay Chokshi8994e392011-09-14 18:20:39 -0700853 led_cfg = &pdata->configs[i];
854
855 led_dat->id = led_cfg->id;
Jay Chokshi868312e2011-09-16 13:57:13 -0700856 led_dat->pwm_channel = led_cfg->pwm_channel;
857 led_dat->pwm_period_us = led_cfg->pwm_period_us;
858 led_dat->pwm_duty_cycles = led_cfg->pwm_duty_cycles;
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530859 led_dat->wled_cfg = led_cfg->wled_cfg;
860 led_dat->max_current = led_cfg->max_current;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700861
862 if (!((led_dat->id >= PM8XXX_ID_LED_KB_LIGHT) &&
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530863 (led_dat->id < PM8XXX_ID_MAX))) {
Amy Maloche3d2c24c2012-03-22 19:02:25 -0700864 dev_err(&pdev->dev, "invalid LED ID(%d) specified\n",
865 led_dat->id);
866 rc = -EINVAL;
867 goto fail_id_check;
868
869 }
870
871 found = false;
872 version = pm8xxx_get_version(pdev->dev.parent);
873 for (j = 0; j < ARRAY_SIZE(led_map); j++) {
874 if (version == led_map[j].version
875 && (led_map[j].supported & (1 << led_dat->id))) {
876 found = true;
877 break;
878 }
879 }
880
881 if (!found) {
882 dev_err(&pdev->dev, "invalid LED ID(%d) specified\n",
883 led_dat->id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700884 rc = -EINVAL;
885 goto fail_id_check;
886 }
887
888 led_dat->cdev.name = curr_led->name;
889 led_dat->cdev.default_trigger = curr_led->default_trigger;
890 led_dat->cdev.brightness_set = pm8xxx_led_set;
891 led_dat->cdev.brightness_get = pm8xxx_led_get;
892 led_dat->cdev.brightness = LED_OFF;
Jay Chokshi8994e392011-09-14 18:20:39 -0700893 led_dat->cdev.flags = curr_led->flags;
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700894 led_dat->dev = &pdev->dev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700895
896 rc = get_init_value(led_dat, &led_dat->reg);
897 if (rc < 0)
898 goto fail_id_check;
899
Jay Chokshi8994e392011-09-14 18:20:39 -0700900 rc = pm8xxx_set_led_mode_and_max_brightness(led_dat,
901 led_cfg->mode, led_cfg->max_current);
Jay Chokshide4cefb2011-08-04 18:10:44 -0700902 if (rc < 0)
903 goto fail_id_check;
904
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700905 mutex_init(&led_dat->lock);
906 INIT_WORK(&led_dat->work, pm8xxx_led_work);
907
908 rc = led_classdev_register(&pdev->dev, &led_dat->cdev);
909 if (rc) {
910 dev_err(&pdev->dev, "unable to register led %d,rc=%d\n",
911 led_dat->id, rc);
912 goto fail_id_check;
913 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700914
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530915 /* configure default state */
916 if (led_cfg->default_state)
917 led->cdev.brightness = led_dat->cdev.max_brightness;
918 else
919 led->cdev.brightness = LED_OFF;
920
Jay Chokshi868312e2011-09-16 13:57:13 -0700921 if (led_cfg->mode != PM8XXX_LED_MODE_MANUAL) {
Amy Malochec17c3732012-02-27 18:34:07 -0800922 if (led_dat->id == PM8XXX_ID_RGB_LED_RED ||
923 led_dat->id == PM8XXX_ID_RGB_LED_GREEN ||
924 led_dat->id == PM8XXX_ID_RGB_LED_BLUE)
925 __pm8xxx_led_work(led_dat, 0);
926 else
927 __pm8xxx_led_work(led_dat,
Jay Chokshide4cefb2011-08-04 18:10:44 -0700928 led_dat->cdev.max_brightness);
Jay Chokshi868312e2011-09-16 13:57:13 -0700929
930 if (led_dat->pwm_channel != -1) {
931 led_dat->cdev.max_brightness = LED_FULL;
932 rc = pm8xxx_led_pwm_configure(led_dat);
933 if (rc) {
934 dev_err(&pdev->dev, "failed to "
935 "configure LED, error: %d\n", rc);
936 goto fail_id_check;
937 }
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530938 schedule_work(&led->work);
Jay Chokshi868312e2011-09-16 13:57:13 -0700939 }
940 } else {
Mohan Pallaka53fe1a12011-12-15 16:56:37 +0530941 __pm8xxx_led_work(led_dat, led->cdev.brightness);
Jay Chokshi868312e2011-09-16 13:57:13 -0700942 }
Jay Chokshide4cefb2011-08-04 18:10:44 -0700943 }
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700944
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700945 platform_set_drvdata(pdev, led);
946
947 return 0;
948
949fail_id_check:
950 if (i > 0) {
951 for (i = i - 1; i >= 0; i--) {
952 mutex_destroy(&led[i].lock);
953 led_classdev_unregister(&led[i].cdev);
Jay Chokshi868312e2011-09-16 13:57:13 -0700954 if (led[i].pwm_dev != NULL)
955 pwm_free(led[i].pwm_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700956 }
957 }
958 kfree(led);
959 return rc;
960}
961
962static int __devexit pm8xxx_led_remove(struct platform_device *pdev)
963{
964 int i;
965 const struct led_platform_data *pdata =
966 pdev->dev.platform_data;
967 struct pm8xxx_led_data *led = platform_get_drvdata(pdev);
968
969 for (i = 0; i < pdata->num_leds; i++) {
970 cancel_work_sync(&led[i].work);
971 mutex_destroy(&led[i].lock);
972 led_classdev_unregister(&led[i].cdev);
Jay Chokshi868312e2011-09-16 13:57:13 -0700973 if (led[i].pwm_dev != NULL)
974 pwm_free(led[i].pwm_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700975 }
976
977 kfree(led);
978
979 return 0;
980}
981
982static struct platform_driver pm8xxx_led_driver = {
983 .probe = pm8xxx_led_probe,
984 .remove = __devexit_p(pm8xxx_led_remove),
985 .driver = {
986 .name = PM8XXX_LEDS_DEV_NAME,
987 .owner = THIS_MODULE,
988 },
989};
990
991static int __init pm8xxx_led_init(void)
992{
993 return platform_driver_register(&pm8xxx_led_driver);
994}
Jay Chokshi12e49bf2011-07-22 16:24:39 -0700995subsys_initcall(pm8xxx_led_init);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700996
997static void __exit pm8xxx_led_exit(void)
998{
999 platform_driver_unregister(&pm8xxx_led_driver);
1000}
1001module_exit(pm8xxx_led_exit);
1002
1003MODULE_DESCRIPTION("PM8XXX LEDs driver");
1004MODULE_LICENSE("GPL v2");
1005MODULE_VERSION("1.0");
1006MODULE_ALIAS("platform:pm8xxx-led");