blob: 9e0a147fb43f8c15a33393940e125cb90da50e28 [file] [log] [blame]
Amy Malochef3d5a062012-08-16 19:14:11 -07001
Amy Maloche832d90a2013-01-07 10:15:29 -08002/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Amy Malochef3d5a062012-08-16 19:14:11 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/slab.h>
18#include <linux/leds.h>
19#include <linux/err.h>
Amy Malochedc3e5572012-09-25 16:39:06 -070020#include <linux/spinlock.h>
Amy Malochef3d5a062012-08-16 19:14:11 -070021#include <linux/of_platform.h>
22#include <linux/of_device.h>
23#include <linux/spmi.h>
Amy Malocheeea7b592012-10-03 15:59:36 -070024#include <linux/qpnp/pwm.h>
Asaf Penso55ac8472013-01-21 21:17:37 +020025#include <linux/workqueue.h>
Amy Malochef3d5a062012-08-16 19:14:11 -070026
27#define WLED_MOD_EN_REG(base, n) (base + 0x60 + n*0x10)
28#define WLED_IDAC_DLY_REG(base, n) (WLED_MOD_EN_REG(base, n) + 0x01)
29#define WLED_FULL_SCALE_REG(base, n) (WLED_IDAC_DLY_REG(base, n) + 0x01)
Amy Maloched44516e2013-02-14 17:36:34 -080030#define WLED_MOD_SRC_SEL_REG(base, n) (WLED_FULL_SCALE_REG(base, n) + 0x01)
Amy Malochef3d5a062012-08-16 19:14:11 -070031
32/* wled control registers */
33#define WLED_BRIGHTNESS_CNTL_LSB(base, n) (base + 0x40 + 2*n)
34#define WLED_BRIGHTNESS_CNTL_MSB(base, n) (base + 0x41 + 2*n)
35#define WLED_MOD_CTRL_REG(base) (base + 0x46)
36#define WLED_SYNC_REG(base) (base + 0x47)
37#define WLED_FDBCK_CTRL_REG(base) (base + 0x48)
38#define WLED_SWITCHING_FREQ_REG(base) (base + 0x4C)
39#define WLED_OVP_CFG_REG(base) (base + 0x4D)
40#define WLED_BOOST_LIMIT_REG(base) (base + 0x4E)
41#define WLED_CURR_SINK_REG(base) (base + 0x4F)
42#define WLED_HIGH_POLE_CAP_REG(base) (base + 0x58)
43#define WLED_CURR_SINK_MASK 0xE0
44#define WLED_CURR_SINK_SHFT 0x05
45#define WLED_SWITCH_FREQ_MASK 0x02
46#define WLED_OVP_VAL_MASK 0x03
47#define WLED_OVP_VAL_BIT_SHFT 0x00
48#define WLED_BOOST_LIMIT_MASK 0x07
49#define WLED_BOOST_LIMIT_BIT_SHFT 0x00
50#define WLED_BOOST_ON 0x80
51#define WLED_BOOST_OFF 0x00
52#define WLED_EN_MASK 0x80
53#define WLED_NO_MASK 0x00
54#define WLED_CP_SELECT_MAX 0x03
55#define WLED_CP_SELECT_MASK 0x02
56#define WLED_USE_EXT_GEN_MOD_SRC 0x01
57#define WLED_CTL_DLY_STEP 200
58#define WLED_CTL_DLY_MAX 1400
59#define WLED_MAX_CURR 25
60#define WLED_MSB_MASK 0x0F
61#define WLED_MAX_CURR_MASK 0x19
62#define WLED_OP_FDBCK_MASK 0x07
63#define WLED_OP_FDBCK_BIT_SHFT 0x00
64
Amy Maloched55fdb82013-02-26 18:11:57 -080065#define WLED_MAX_LEVEL 4095
Amy Malochef3d5a062012-08-16 19:14:11 -070066#define WLED_8_BIT_MASK 0xFF
67#define WLED_4_BIT_MASK 0x0F
68#define WLED_8_BIT_SHFT 0x08
69#define WLED_MAX_DUTY_CYCLE 0xFFF
70
71#define WLED_SYNC_VAL 0x07
72#define WLED_SYNC_RESET_VAL 0x00
73
Amy Malochef3d5a062012-08-16 19:14:11 -070074#define WLED_DEFAULT_STRINGS 0x01
75#define WLED_DEFAULT_OVP_VAL 0x02
76#define WLED_BOOST_LIM_DEFAULT 0x03
77#define WLED_CP_SEL_DEFAULT 0x00
78#define WLED_CTRL_DLY_DEFAULT 0x00
79#define WLED_SWITCH_FREQ_DEFAULT 0x02
80
Amy Maloche864a6d52012-10-03 15:58:12 -070081#define FLASH_SAFETY_TIMER(base) (base + 0x40)
82#define FLASH_MAX_CURR(base) (base + 0x41)
83#define FLASH_LED_0_CURR(base) (base + 0x42)
84#define FLASH_LED_1_CURR(base) (base + 0x43)
85#define FLASH_CLAMP_CURR(base) (base + 0x44)
86#define FLASH_LED_TMR_CTRL(base) (base + 0x48)
Amy Maloched44516e2013-02-14 17:36:34 -080087#define FLASH_HEADROOM(base) (base + 0x4A)
Amy Maloche864a6d52012-10-03 15:58:12 -070088#define FLASH_STARTUP_DELAY(base) (base + 0x4B)
89#define FLASH_MASK_ENABLE(base) (base + 0x4C)
90#define FLASH_VREG_OK_FORCE(base) (base + 0x4F)
91#define FLASH_ENABLE_CONTROL(base) (base + 0x46)
92#define FLASH_LED_STROBE_CTRL(base) (base + 0x47)
93
94#define FLASH_MAX_LEVEL 0x4F
95#define FLASH_NO_MASK 0x00
96
97#define FLASH_MASK_1 0x20
98#define FLASH_MASK_REG_MASK 0xE0
99#define FLASH_HEADROOM_MASK 0x03
100#define FLASH_SAFETY_TIMER_MASK 0x7F
101#define FLASH_CURRENT_MASK 0xFF
102#define FLASH_TMR_MASK 0x03
103#define FLASH_TMR_WATCHDOG 0x03
104#define FLASH_TMR_SAFETY 0x00
105
106#define FLASH_HW_VREG_OK 0x80
107#define FLASH_VREG_MASK 0xC0
108
109#define FLASH_STARTUP_DLY_MASK 0x02
110
111#define FLASH_ENABLE_ALL 0xE0
112#define FLASH_ENABLE_MODULE 0x80
113#define FLASH_ENABLE_MODULE_MASK 0x80
114#define FLASH_DISABLE_ALL 0x00
Amy Maloche38b9aae2013-02-07 12:15:34 -0800115#define FLASH_ENABLE_MASK 0xE0
Amy Maloche864a6d52012-10-03 15:58:12 -0700116#define FLASH_ENABLE_LED_0 0x40
117#define FLASH_ENABLE_LED_1 0x20
118#define FLASH_INIT_MASK 0xE0
119
120#define FLASH_STROBE_ALL 0xC0
121#define FLASH_STROBE_MASK 0xC0
122#define FLASH_LED_0_OUTPUT 0x80
123#define FLASH_LED_1_OUTPUT 0x40
124
125#define FLASH_CURRENT_PRGM_MIN 1
126#define FLASH_CURRENT_PRGM_SHIFT 1
127
128#define FLASH_DURATION_200ms 0x13
129#define FLASH_CLAMP_200mA 0x0F
130
Amy Malochea5ca5552012-10-23 13:34:46 -0700131#define LED_TRIGGER_DEFAULT "none"
132
Amy Malocheeea7b592012-10-03 15:59:36 -0700133#define RGB_LED_SRC_SEL(base) (base + 0x45)
134#define RGB_LED_EN_CTL(base) (base + 0x46)
135#define RGB_LED_ATC_CTL(base) (base + 0x47)
136
137#define RGB_MAX_LEVEL LED_FULL
138#define RGB_LED_ENABLE_RED 0x80
139#define RGB_LED_ENABLE_GREEN 0x40
140#define RGB_LED_ENABLE_BLUE 0x20
141#define RGB_LED_SOURCE_VPH_PWR 0x01
142#define RGB_LED_ENABLE_MASK 0xE0
143#define RGB_LED_SRC_MASK 0x03
144#define QPNP_LED_PWM_FLAGS (PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP)
145#define PWM_LUT_MAX_SIZE 63
146#define RGB_LED_DISABLE 0x00
147
Amy Malochef3d5a062012-08-16 19:14:11 -0700148/**
149 * enum qpnp_leds - QPNP supported led ids
150 * @QPNP_ID_WLED - White led backlight
151 */
152enum qpnp_leds {
Amy Maloche864a6d52012-10-03 15:58:12 -0700153 QPNP_ID_WLED = 0,
154 QPNP_ID_FLASH1_LED0,
155 QPNP_ID_FLASH1_LED1,
Amy Malocheeea7b592012-10-03 15:59:36 -0700156 QPNP_ID_RGB_RED,
157 QPNP_ID_RGB_GREEN,
158 QPNP_ID_RGB_BLUE,
Amy Maloche864a6d52012-10-03 15:58:12 -0700159 QPNP_ID_MAX,
Amy Malochef3d5a062012-08-16 19:14:11 -0700160};
161
162/* current boost limit */
163enum wled_current_boost_limit {
164 WLED_CURR_LIMIT_105mA,
165 WLED_CURR_LIMIT_385mA,
166 WLED_CURR_LIMIT_525mA,
167 WLED_CURR_LIMIT_805mA,
168 WLED_CURR_LIMIT_980mA,
169 WLED_CURR_LIMIT_1260mA,
170 WLED_CURR_LIMIT_1400mA,
171 WLED_CURR_LIMIT_1680mA,
172};
173
174/* over voltage protection threshold */
175enum wled_ovp_threshold {
176 WLED_OVP_35V,
177 WLED_OVP_32V,
178 WLED_OVP_29V,
179 WLED_OVP_37V,
180};
181
182/* switch frquency */
183enum wled_switch_freq {
184 WLED_800kHz = 0,
185 WLED_960kHz,
186 WLED_1600kHz,
187 WLED_3200kHz,
188};
189
Amy Maloche864a6d52012-10-03 15:58:12 -0700190enum flash_headroom {
191 HEADROOM_250mV = 0,
192 HEADROOM_300mV,
193 HEADROOM_400mV,
194 HEADROOM_500mV,
195};
196
197enum flash_startup_dly {
198 DELAY_10us = 0,
199 DELAY_32us,
200 DELAY_64us,
201 DELAY_128us,
202};
203
Amy Malocheeea7b592012-10-03 15:59:36 -0700204enum rgb_mode {
205 RGB_MODE_PWM = 0,
206 RGB_MODE_LPG,
207};
208
Amy Malochef3d5a062012-08-16 19:14:11 -0700209static u8 wled_debug_regs[] = {
210 /* common registers */
211 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4f,
212 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
213 /* LED1 */
214 0x60, 0x61, 0x62, 0x63, 0x66,
Amy Malochea5ca5552012-10-23 13:34:46 -0700215 /* LED2 */
Amy Malochef3d5a062012-08-16 19:14:11 -0700216 0x70, 0x71, 0x72, 0x73, 0x76,
Amy Malochea5ca5552012-10-23 13:34:46 -0700217 /* LED3 */
Amy Malochef3d5a062012-08-16 19:14:11 -0700218 0x80, 0x81, 0x82, 0x83, 0x86,
219};
220
Amy Maloche864a6d52012-10-03 15:58:12 -0700221static u8 flash_debug_regs[] = {
222 0x40, 0x41, 0x42, 0x43, 0x44, 0x48, 0x49, 0x4b, 0x4c,
223 0x4f, 0x46, 0x47,
224};
225
Amy Malocheeea7b592012-10-03 15:59:36 -0700226static u8 rgb_pwm_debug_regs[] = {
227 0x45, 0x46, 0x47,
228};
Amy Malochef3d5a062012-08-16 19:14:11 -0700229/**
230 * wled_config_data - wled configuration data
231 * @num_strings - number of wled strings supported
232 * @ovp_val - over voltage protection threshold
233 * @boost_curr_lim - boot current limit
234 * @cp_select - high pole capacitance
235 * @ctrl_delay_us - delay in activation of led
236 * @dig_mod_gen_en - digital module generator
237 * @cs_out_en - current sink output enable
238 * @op_fdbck - selection of output as feedback for the boost
239 */
240struct wled_config_data {
241 u8 num_strings;
242 u8 ovp_val;
243 u8 boost_curr_lim;
244 u8 cp_select;
245 u8 ctrl_delay_us;
246 u8 switch_freq;
247 bool dig_mod_gen_en;
248 bool cs_out_en;
249 bool op_fdbck;
250};
251
252/**
Amy Maloche864a6d52012-10-03 15:58:12 -0700253 * flash_config_data - flash configuration data
254 * @current_prgm - current to be programmed, scaled by max level
255 * @clamp_curr - clamp current to use
256 * @headroom - headroom value to use
257 * @duration - duration of the flash
258 * @enable_module - enable address for particular flash
259 * @trigger_flash - trigger flash
260 * @startup_dly - startup delay for flash
261 * @current_addr - address to write for current
262 * @second_addr - address of secondary flash to be written
263 * @safety_timer - enable safety timer or watchdog timer
264 */
265struct flash_config_data {
266 u8 current_prgm;
267 u8 clamp_curr;
268 u8 headroom;
269 u8 duration;
270 u8 enable_module;
271 u8 trigger_flash;
272 u8 startup_dly;
273 u16 current_addr;
274 u16 second_addr;
275 bool safety_timer;
276};
277
278/**
Amy Malocheeea7b592012-10-03 15:59:36 -0700279 * rgb_config_data - rgb configuration data
280 * @lut_params - lut parameters to be used by pwm driver
281 * @pwm_device - pwm device
282 * @pwm_channel - pwm channel to be configured for led
283 * @pwm_period_us - period for pwm, in us
284 * @mode - mode the led operates in
285 */
286struct rgb_config_data {
287 struct lut_params lut_params;
288 struct pwm_device *pwm_dev;
289 int pwm_channel;
290 u32 pwm_period_us;
291 struct pwm_duty_cycles *duty_cycles;
292 u8 mode;
293 u8 enable;
294};
295
296/**
Amy Malochef3d5a062012-08-16 19:14:11 -0700297 * struct qpnp_led_data - internal led data structure
298 * @led_classdev - led class device
Asaf Penso55ac8472013-01-21 21:17:37 +0200299 * @delayed_work - delayed work for turning off the LED
Amy Malochef3d5a062012-08-16 19:14:11 -0700300 * @id - led index
301 * @base_reg - base register given in device tree
302 * @lock - to protect the transactions
303 * @reg - cached value of led register
Amy Malochea5ca5552012-10-23 13:34:46 -0700304 * @num_leds - number of leds in the module
Amy Malochef3d5a062012-08-16 19:14:11 -0700305 * @max_current - maximum current supported by LED
306 * @default_on - true: default state max, false, default state 0
Asaf Penso55ac8472013-01-21 21:17:37 +0200307 * @turn_off_delay_ms - number of msec before turning off the LED
Amy Malochef3d5a062012-08-16 19:14:11 -0700308 */
309struct qpnp_led_data {
310 struct led_classdev cdev;
311 struct spmi_device *spmi_dev;
Asaf Penso55ac8472013-01-21 21:17:37 +0200312 struct delayed_work dwork;
Amy Malochef3d5a062012-08-16 19:14:11 -0700313 int id;
314 u16 base;
315 u8 reg;
Amy Malochea5ca5552012-10-23 13:34:46 -0700316 u8 num_leds;
Amy Malochedc3e5572012-09-25 16:39:06 -0700317 spinlock_t lock;
Amy Malochef3d5a062012-08-16 19:14:11 -0700318 struct wled_config_data *wled_cfg;
Amy Maloche864a6d52012-10-03 15:58:12 -0700319 struct flash_config_data *flash_cfg;
Amy Malocheeea7b592012-10-03 15:59:36 -0700320 struct rgb_config_data *rgb_cfg;
Amy Malochef3d5a062012-08-16 19:14:11 -0700321 int max_current;
322 bool default_on;
Asaf Penso55ac8472013-01-21 21:17:37 +0200323 int turn_off_delay_ms;
Amy Malochef3d5a062012-08-16 19:14:11 -0700324};
325
326static int
327qpnp_led_masked_write(struct qpnp_led_data *led, u16 addr, u8 mask, u8 val)
328{
329 int rc;
330 u8 reg;
331
332 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
333 addr, &reg, 1);
334 if (rc) {
335 dev_err(&led->spmi_dev->dev,
336 "Unable to read from addr=%x, rc(%d)\n", addr, rc);
337 }
338
339 reg &= ~mask;
340 reg |= val;
341
342 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
343 addr, &reg, 1);
344 if (rc)
345 dev_err(&led->spmi_dev->dev,
346 "Unable to write to addr=%x, rc(%d)\n", addr, rc);
347 return rc;
348}
349
Amy Malochea5ca5552012-10-23 13:34:46 -0700350static void qpnp_dump_regs(struct qpnp_led_data *led, u8 regs[], u8 array_size)
351{
352 int i;
353 u8 val;
354
355 pr_debug("===== %s LED register dump start =====\n", led->cdev.name);
356 for (i = 0; i < array_size; i++) {
357 spmi_ext_register_readl(led->spmi_dev->ctrl,
358 led->spmi_dev->sid,
359 led->base + regs[i],
360 &val, sizeof(val));
361 pr_debug("0x%x = 0x%x\n", led->base + regs[i], val);
362 }
363 pr_debug("===== %s LED register dump end =====\n", led->cdev.name);
364}
365
Amy Malochef3d5a062012-08-16 19:14:11 -0700366static int qpnp_wled_set(struct qpnp_led_data *led)
367{
Amy Maloched55fdb82013-02-26 18:11:57 -0800368 int rc, duty, level;
369 u8 val, i, num_wled_strings;
Amy Malochef3d5a062012-08-16 19:14:11 -0700370
371 level = led->cdev.brightness;
372
373 if (level > WLED_MAX_LEVEL)
374 level = WLED_MAX_LEVEL;
375 if (level == 0) {
376 val = WLED_BOOST_OFF;
377 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
378 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
379 &val, 1);
380 if (rc) {
381 dev_err(&led->spmi_dev->dev,
382 "WLED write ctrl reg failed(%d)\n", rc);
383 return rc;
384 }
385 } else {
386 val = WLED_BOOST_ON;
387 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
388 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
389 &val, 1);
390 if (rc) {
391 dev_err(&led->spmi_dev->dev,
392 "WLED write ctrl reg failed(%d)\n", rc);
393 return rc;
394 }
395 }
396
397 duty = (WLED_MAX_DUTY_CYCLE * level) / WLED_MAX_LEVEL;
398
399 num_wled_strings = led->wled_cfg->num_strings;
400
401 /* program brightness control registers */
402 for (i = 0; i < num_wled_strings; i++) {
403 rc = qpnp_led_masked_write(led,
404 WLED_BRIGHTNESS_CNTL_MSB(led->base, i), WLED_MSB_MASK,
405 (duty >> WLED_8_BIT_SHFT) & WLED_4_BIT_MASK);
406 if (rc) {
407 dev_err(&led->spmi_dev->dev,
408 "WLED set brightness MSB failed(%d)\n", rc);
409 return rc;
410 }
411 val = duty & WLED_8_BIT_MASK;
412 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
413 led->spmi_dev->sid,
414 WLED_BRIGHTNESS_CNTL_LSB(led->base, i), &val, 1);
415 if (rc) {
416 dev_err(&led->spmi_dev->dev,
417 "WLED set brightness LSB failed(%d)\n", rc);
418 return rc;
419 }
420 }
421
422 /* sync */
423 val = WLED_SYNC_VAL;
424 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
425 WLED_SYNC_REG(led->base), &val, 1);
426 if (rc) {
427 dev_err(&led->spmi_dev->dev,
428 "WLED set sync reg failed(%d)\n", rc);
429 return rc;
430 }
431
432 val = WLED_SYNC_RESET_VAL;
433 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
434 WLED_SYNC_REG(led->base), &val, 1);
435 if (rc) {
436 dev_err(&led->spmi_dev->dev,
437 "WLED reset sync reg failed(%d)\n", rc);
438 return rc;
439 }
440 return 0;
441}
442
Amy Maloche864a6d52012-10-03 15:58:12 -0700443static int qpnp_flash_set(struct qpnp_led_data *led)
444{
445 int rc;
446 int val = led->cdev.brightness;
447
448 led->flash_cfg->current_prgm = (val * FLASH_MAX_LEVEL /
449 led->max_current);
450
451 led->flash_cfg->current_prgm =
452 led->flash_cfg->current_prgm >> FLASH_CURRENT_PRGM_SHIFT;
453 if (!led->flash_cfg->current_prgm)
454 led->flash_cfg->current_prgm = FLASH_CURRENT_PRGM_MIN;
455
456 /* Set led current */
457 if (val > 0) {
Amy Maloche38b9aae2013-02-07 12:15:34 -0800458 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
459 FLASH_ENABLE_MODULE_MASK, FLASH_ENABLE_MODULE);
460 if (rc) {
461 dev_err(&led->spmi_dev->dev,
462 "Enable reg write failed(%d)\n", rc);
463 return rc;
464 }
465
Amy Maloche864a6d52012-10-03 15:58:12 -0700466 rc = qpnp_led_masked_write(led, led->flash_cfg->current_addr,
467 FLASH_CURRENT_MASK, led->flash_cfg->current_prgm);
468 if (rc) {
469 dev_err(&led->spmi_dev->dev,
470 "Current reg write failed(%d)\n", rc);
471 return rc;
472 }
473
474 rc = qpnp_led_masked_write(led, led->flash_cfg->second_addr,
475 FLASH_CURRENT_MASK, led->flash_cfg->current_prgm);
476 if (rc) {
477 dev_err(&led->spmi_dev->dev,
478 "Current reg write failed(%d)\n", rc);
479 return rc;
480 }
481
482 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
483 FLASH_ENABLE_MASK,
484 FLASH_ENABLE_ALL);
485 if (rc) {
486 dev_err(&led->spmi_dev->dev,
487 "Enable reg write failed(%d)\n", rc);
488 return rc;
489 }
490 rc = qpnp_led_masked_write(led,
491 FLASH_LED_STROBE_CTRL(led->base),
492 FLASH_STROBE_MASK, FLASH_STROBE_ALL);
493
494 if (rc) {
495 dev_err(&led->spmi_dev->dev,
496 "LED %d flash write failed(%d)\n", led->id, rc);
497 return rc;
498 }
Amy Maloche38b9aae2013-02-07 12:15:34 -0800499 rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
500 FLASH_VREG_MASK, FLASH_HW_VREG_OK);
501 if (rc) {
502 dev_err(&led->spmi_dev->dev,
503 "Vreg OK reg write failed(%d)\n", rc);
504 return rc;
505 }
Amy Maloche864a6d52012-10-03 15:58:12 -0700506 } else {
507 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
508 FLASH_ENABLE_MASK,
509 FLASH_DISABLE_ALL);
510 if (rc) {
511 dev_err(&led->spmi_dev->dev,
512 "Enable reg write failed(%d)\n", rc);
513 return rc;
514 }
515
516 rc = qpnp_led_masked_write(led,
517 FLASH_LED_STROBE_CTRL(led->base),
518 FLASH_STROBE_MASK,
519 FLASH_DISABLE_ALL);
520 if (rc) {
521 dev_err(&led->spmi_dev->dev,
522 "LED %d flash write failed(%d)\n", led->id, rc);
523 return rc;
524 }
525 }
526
Amy Malocheeea7b592012-10-03 15:59:36 -0700527 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
528
529 return 0;
530}
531
532static int qpnp_rgb_set(struct qpnp_led_data *led)
533{
534 int duty_us;
535 int rc;
536
537 if (led->cdev.brightness) {
538 if (led->rgb_cfg->mode == RGB_MODE_PWM) {
539 duty_us = (led->rgb_cfg->pwm_period_us *
540 led->cdev.brightness) / LED_FULL;
541 rc = pwm_config(led->rgb_cfg->pwm_dev, duty_us,
542 led->rgb_cfg->pwm_period_us);
543 if (rc < 0) {
544 dev_err(&led->spmi_dev->dev, "Failed to " \
545 "configure pwm for new values\n");
546 return rc;
547 }
548 }
549 rc = qpnp_led_masked_write(led,
550 RGB_LED_EN_CTL(led->base),
551 led->rgb_cfg->enable, led->rgb_cfg->enable);
552 if (rc) {
553 dev_err(&led->spmi_dev->dev,
554 "Failed to write led enable reg\n");
555 return rc;
556 }
557 rc = pwm_enable(led->rgb_cfg->pwm_dev);
558 } else {
559 pwm_disable(led->rgb_cfg->pwm_dev);
560 rc = qpnp_led_masked_write(led,
561 RGB_LED_EN_CTL(led->base),
562 led->rgb_cfg->enable, RGB_LED_DISABLE);
563 if (rc) {
564 dev_err(&led->spmi_dev->dev,
565 "Failed to write led enable reg\n");
566 return rc;
567 }
568 }
569
570 qpnp_dump_regs(led, rgb_pwm_debug_regs, ARRAY_SIZE(rgb_pwm_debug_regs));
571
Amy Maloche864a6d52012-10-03 15:58:12 -0700572 return 0;
573}
574
Amy Malochef3d5a062012-08-16 19:14:11 -0700575static void qpnp_led_set(struct led_classdev *led_cdev,
576 enum led_brightness value)
577{
578 int rc;
579 struct qpnp_led_data *led;
580
581 led = container_of(led_cdev, struct qpnp_led_data, cdev);
Amy Malochef3d5a062012-08-16 19:14:11 -0700582 if (value < LED_OFF || value > led->cdev.max_brightness) {
Amy Malochea5ca5552012-10-23 13:34:46 -0700583 dev_err(&led->spmi_dev->dev, "Invalid brightness value\n");
Amy Malochef3d5a062012-08-16 19:14:11 -0700584 return;
585 }
586
Amy Malochedc3e5572012-09-25 16:39:06 -0700587 spin_lock(&led->lock);
Amy Malochef3d5a062012-08-16 19:14:11 -0700588 led->cdev.brightness = value;
589
590 switch (led->id) {
591 case QPNP_ID_WLED:
592 rc = qpnp_wled_set(led);
593 if (rc < 0)
Amy Malochea5ca5552012-10-23 13:34:46 -0700594 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -0700595 "WLED set brightness failed (%d)\n", rc);
596 break;
Amy Maloche864a6d52012-10-03 15:58:12 -0700597 case QPNP_ID_FLASH1_LED0:
598 case QPNP_ID_FLASH1_LED1:
599 rc = qpnp_flash_set(led);
600 if (rc < 0)
601 dev_err(&led->spmi_dev->dev,
602 "FLASH set brightness failed (%d)\n", rc);
603 break;
Amy Malocheeea7b592012-10-03 15:59:36 -0700604 case QPNP_ID_RGB_RED:
605 case QPNP_ID_RGB_GREEN:
606 case QPNP_ID_RGB_BLUE:
607 rc = qpnp_rgb_set(led);
608 if (rc < 0)
609 dev_err(&led->spmi_dev->dev,
610 "RGB set brightness failed (%d)\n", rc);
611 break;
Amy Malochef3d5a062012-08-16 19:14:11 -0700612 default:
Amy Malochea5ca5552012-10-23 13:34:46 -0700613 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -0700614 break;
615 }
Amy Malochedc3e5572012-09-25 16:39:06 -0700616 spin_unlock(&led->lock);
Amy Malochef3d5a062012-08-16 19:14:11 -0700617}
618
619static int __devinit qpnp_led_set_max_brightness(struct qpnp_led_data *led)
620{
621 switch (led->id) {
622 case QPNP_ID_WLED:
623 led->cdev.max_brightness = WLED_MAX_LEVEL;
624 break;
Amy Maloche864a6d52012-10-03 15:58:12 -0700625 case QPNP_ID_FLASH1_LED0:
626 case QPNP_ID_FLASH1_LED1:
627 led->cdev.max_brightness = led->max_current;
628 break;
Amy Malocheeea7b592012-10-03 15:59:36 -0700629 case QPNP_ID_RGB_RED:
630 case QPNP_ID_RGB_GREEN:
631 case QPNP_ID_RGB_BLUE:
632 led->cdev.max_brightness = RGB_MAX_LEVEL;
633 break;
Amy Malochef3d5a062012-08-16 19:14:11 -0700634 default:
Amy Malochea5ca5552012-10-23 13:34:46 -0700635 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -0700636 return -EINVAL;
637 }
638
639 return 0;
640}
641
642static enum led_brightness qpnp_led_get(struct led_classdev *led_cdev)
643{
644 struct qpnp_led_data *led;
645
646 led = container_of(led_cdev, struct qpnp_led_data, cdev);
647
648 return led->cdev.brightness;
649}
650
Asaf Penso55ac8472013-01-21 21:17:37 +0200651static void qpnp_led_turn_off_delayed(struct work_struct *work)
652{
653 struct delayed_work *dwork = to_delayed_work(work);
654 struct qpnp_led_data *led
655 = container_of(dwork, struct qpnp_led_data, dwork);
656
657 led->cdev.brightness = LED_OFF;
658 qpnp_led_set(&led->cdev, led->cdev.brightness);
659}
660
661static void qpnp_led_turn_off(struct qpnp_led_data *led)
662{
663 INIT_DELAYED_WORK(&led->dwork, qpnp_led_turn_off_delayed);
664 schedule_delayed_work(&led->dwork,
665 msecs_to_jiffies(led->turn_off_delay_ms));
666}
667
Amy Malochef3d5a062012-08-16 19:14:11 -0700668static int __devinit qpnp_wled_init(struct qpnp_led_data *led)
669{
670 int rc, i;
671 u8 num_wled_strings;
672
673 num_wled_strings = led->wled_cfg->num_strings;
674
675 /* verify ranges */
676 if (led->wled_cfg->ovp_val > WLED_OVP_37V) {
677 dev_err(&led->spmi_dev->dev, "Invalid ovp value\n");
678 return -EINVAL;
679 }
680
681 if (led->wled_cfg->boost_curr_lim > WLED_CURR_LIMIT_1680mA) {
682 dev_err(&led->spmi_dev->dev, "Invalid boost current limit\n");
683 return -EINVAL;
684 }
685
686 if (led->wled_cfg->cp_select > WLED_CP_SELECT_MAX) {
687 dev_err(&led->spmi_dev->dev, "Invalid pole capacitance\n");
688 return -EINVAL;
689 }
690
691 if ((led->max_current > WLED_MAX_CURR)) {
692 dev_err(&led->spmi_dev->dev, "Invalid max current\n");
693 return -EINVAL;
694 }
695
696 if ((led->wled_cfg->ctrl_delay_us % WLED_CTL_DLY_STEP) ||
697 (led->wled_cfg->ctrl_delay_us > WLED_CTL_DLY_MAX)) {
698 dev_err(&led->spmi_dev->dev, "Invalid control delay\n");
699 return -EINVAL;
700 }
701
702 /* program over voltage protection threshold */
703 rc = qpnp_led_masked_write(led, WLED_OVP_CFG_REG(led->base),
704 WLED_OVP_VAL_MASK,
705 (led->wled_cfg->ovp_val << WLED_OVP_VAL_BIT_SHFT));
706 if (rc) {
707 dev_err(&led->spmi_dev->dev,
708 "WLED OVP reg write failed(%d)\n", rc);
709 return rc;
710 }
711
712 /* program current boost limit */
713 rc = qpnp_led_masked_write(led, WLED_BOOST_LIMIT_REG(led->base),
714 WLED_BOOST_LIMIT_MASK, led->wled_cfg->boost_curr_lim);
715 if (rc) {
716 dev_err(&led->spmi_dev->dev,
717 "WLED boost limit reg write failed(%d)\n", rc);
718 return rc;
719 }
720
721 /* program output feedback */
722 rc = qpnp_led_masked_write(led, WLED_FDBCK_CTRL_REG(led->base),
723 WLED_OP_FDBCK_MASK,
724 (led->wled_cfg->op_fdbck << WLED_OP_FDBCK_BIT_SHFT));
725 if (rc) {
726 dev_err(&led->spmi_dev->dev,
727 "WLED fdbck ctrl reg write failed(%d)\n", rc);
728 return rc;
729 }
730
731 /* program switch frequency */
732 rc = qpnp_led_masked_write(led, WLED_SWITCHING_FREQ_REG(led->base),
733 WLED_SWITCH_FREQ_MASK, led->wled_cfg->switch_freq);
734 if (rc) {
735 dev_err(&led->spmi_dev->dev,
736 "WLED switch freq reg write failed(%d)\n", rc);
737 return rc;
738 }
739
740 /* program current sink */
741 if (led->wled_cfg->cs_out_en) {
742 rc = qpnp_led_masked_write(led, WLED_CURR_SINK_REG(led->base),
743 WLED_CURR_SINK_MASK,
Amy Maloche832d90a2013-01-07 10:15:29 -0800744 (((1 << led->wled_cfg->num_strings) - 1)
745 << WLED_CURR_SINK_SHFT));
Amy Malochef3d5a062012-08-16 19:14:11 -0700746 if (rc) {
747 dev_err(&led->spmi_dev->dev,
748 "WLED curr sink reg write failed(%d)\n", rc);
749 return rc;
750 }
751 }
752
753 /* program high pole capacitance */
754 rc = qpnp_led_masked_write(led, WLED_HIGH_POLE_CAP_REG(led->base),
755 WLED_CP_SELECT_MASK, led->wled_cfg->cp_select);
756 if (rc) {
757 dev_err(&led->spmi_dev->dev,
758 "WLED pole cap reg write failed(%d)\n", rc);
759 return rc;
760 }
761
762 /* program modulator, current mod src and cabc */
763 for (i = 0; i < num_wled_strings; i++) {
764 rc = qpnp_led_masked_write(led, WLED_MOD_EN_REG(led->base, i),
765 WLED_NO_MASK, WLED_EN_MASK);
766 if (rc) {
767 dev_err(&led->spmi_dev->dev,
768 "WLED mod enable reg write failed(%d)\n", rc);
769 return rc;
770 }
771
772 if (led->wled_cfg->dig_mod_gen_en) {
773 rc = qpnp_led_masked_write(led,
Amy Maloched44516e2013-02-14 17:36:34 -0800774 WLED_MOD_SRC_SEL_REG(led->base, i),
Amy Malochef3d5a062012-08-16 19:14:11 -0700775 WLED_NO_MASK, WLED_USE_EXT_GEN_MOD_SRC);
776 if (rc) {
777 dev_err(&led->spmi_dev->dev,
778 "WLED dig mod en reg write failed(%d)\n", rc);
779 }
780 }
781
782 rc = qpnp_led_masked_write(led,
783 WLED_FULL_SCALE_REG(led->base, i), WLED_MAX_CURR_MASK,
784 led->max_current);
785 if (rc) {
786 dev_err(&led->spmi_dev->dev,
787 "WLED max current reg write failed(%d)\n", rc);
788 return rc;
789 }
790
791 }
792
793 /* dump wled registers */
Amy Malochea5ca5552012-10-23 13:34:46 -0700794 qpnp_dump_regs(led, wled_debug_regs, ARRAY_SIZE(wled_debug_regs));
Amy Malochef3d5a062012-08-16 19:14:11 -0700795
796 return 0;
797}
798
Amy Maloche864a6d52012-10-03 15:58:12 -0700799static int __devinit qpnp_flash_init(struct qpnp_led_data *led)
800{
801 int rc;
802
803 rc = qpnp_led_masked_write(led,
804 FLASH_LED_STROBE_CTRL(led->base),
805 FLASH_STROBE_MASK, FLASH_DISABLE_ALL);
806 if (rc) {
807 dev_err(&led->spmi_dev->dev,
808 "LED %d flash write failed(%d)\n", led->id, rc);
809 return rc;
810 }
811 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
812 FLASH_INIT_MASK, FLASH_ENABLE_MODULE);
813 if (rc) {
814 dev_err(&led->spmi_dev->dev,
815 "Enable reg write failed(%d)\n", rc);
816 return rc;
817 }
818
819 /* Set flash safety timer */
820 rc = qpnp_led_masked_write(led, FLASH_SAFETY_TIMER(led->base),
821 FLASH_SAFETY_TIMER_MASK, led->flash_cfg->duration);
822 if (rc) {
823 dev_err(&led->spmi_dev->dev,
824 "Safety timer reg write failed(%d)\n", rc);
825 return rc;
826 }
827
828 /* Set max current */
829 rc = qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
830 FLASH_CURRENT_MASK, FLASH_MAX_LEVEL);
831 if (rc) {
832 dev_err(&led->spmi_dev->dev,
833 "Max current reg write failed(%d)\n", rc);
834 return rc;
835 }
836 /* Set clamp current */
837 rc = qpnp_led_masked_write(led, FLASH_CLAMP_CURR(led->base),
838 FLASH_CURRENT_MASK, led->flash_cfg->clamp_curr);
839 if (rc) {
840 dev_err(&led->spmi_dev->dev,
841 "Clamp current reg write failed(%d)\n", rc);
842 return rc;
843 }
844
845 /* Set timer control - safety or watchdog */
846 if (led->flash_cfg->safety_timer)
847 rc = qpnp_led_masked_write(led, FLASH_LED_TMR_CTRL(led->base),
848 FLASH_TMR_MASK, FLASH_TMR_SAFETY);
849 else
850 rc = qpnp_led_masked_write(led, FLASH_LED_TMR_CTRL(led->base),
851 FLASH_TMR_MASK, FLASH_TMR_WATCHDOG);
852 if (rc) {
853 dev_err(&led->spmi_dev->dev,
854 "LED timer ctrl reg write failed(%d)\n", rc);
855 return rc;
856 }
857 /* Set headroom */
858 rc = qpnp_led_masked_write(led, FLASH_HEADROOM(led->base),
859 FLASH_HEADROOM_MASK, led->flash_cfg->headroom);
860 if (rc) {
861 dev_err(&led->spmi_dev->dev,
862 "Headroom reg write failed(%d)\n", rc);
863 return rc;
864 }
865
866 /* Set mask enable */
867 rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
868 FLASH_MASK_REG_MASK, FLASH_MASK_1);
869 if (rc) {
870 dev_err(&led->spmi_dev->dev,
871 "Mask enable reg write failed(%d)\n", rc);
872 return rc;
873 }
874
875 /* Set startup delay */
876 rc = qpnp_led_masked_write(led, FLASH_STARTUP_DELAY(led->base),
877 FLASH_STARTUP_DLY_MASK, led->flash_cfg->startup_dly);
878 if (rc) {
879 dev_err(&led->spmi_dev->dev,
880 "Startup delay reg write failed(%d)\n", rc);
881 return rc;
882 }
883
884 rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
885 FLASH_VREG_MASK, FLASH_HW_VREG_OK);
886 if (rc) {
887 dev_err(&led->spmi_dev->dev,
888 "Vreg OK reg write failed(%d)\n", rc);
889 return rc;
890 }
891
892 /* Set led current and enable module */
893 rc = qpnp_led_masked_write(led, led->flash_cfg->current_addr,
894 FLASH_CURRENT_MASK, led->flash_cfg->current_prgm);
895 if (rc) {
896 dev_err(&led->spmi_dev->dev,
897 "Current reg write failed(%d)\n", rc);
898 return rc;
899 }
900
901 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
Amy Maloche38b9aae2013-02-07 12:15:34 -0800902 FLASH_ENABLE_MODULE_MASK, FLASH_DISABLE_ALL);
Amy Maloche864a6d52012-10-03 15:58:12 -0700903 if (rc) {
904 dev_err(&led->spmi_dev->dev,
905 "Enable reg write failed(%d)\n", rc);
906 return rc;
907 }
908 /* dump flash registers */
909 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
910
911 return 0;
912}
913
Amy Malocheeea7b592012-10-03 15:59:36 -0700914static int __devinit qpnp_rgb_init(struct qpnp_led_data *led)
915{
916 int rc, start_idx, idx_len;
917
918 rc = qpnp_led_masked_write(led, RGB_LED_SRC_SEL(led->base),
919 RGB_LED_SRC_MASK, RGB_LED_SOURCE_VPH_PWR);
920 if (rc) {
921 dev_err(&led->spmi_dev->dev,
922 "Failed to write led source select register\n");
923 return rc;
924 }
925
926 if (led->rgb_cfg->pwm_channel != -1) {
927 led->rgb_cfg->pwm_dev =
928 pwm_request(led->rgb_cfg->pwm_channel,
929 led->cdev.name);
930
931 if (IS_ERR_OR_NULL(led->rgb_cfg->pwm_dev)) {
932 dev_err(&led->spmi_dev->dev,
933 "could not acquire PWM Channel %d, " \
934 "error %ld\n",
935 led->rgb_cfg->pwm_channel,
936 PTR_ERR(led->rgb_cfg->pwm_dev));
937 led->rgb_cfg->pwm_dev = NULL;
938 return -ENODEV;
939 }
940
941 if (led->rgb_cfg->mode == RGB_MODE_LPG) {
942 start_idx =
943 led->rgb_cfg->duty_cycles->start_idx;
944 idx_len =
945 led->rgb_cfg->duty_cycles->num_duty_pcts;
946
947 if (idx_len >= PWM_LUT_MAX_SIZE &&
948 start_idx) {
949 dev_err(&led->spmi_dev->dev,
950 "Wrong LUT size or index\n");
951 return -EINVAL;
952 }
953 if ((start_idx + idx_len) >
954 PWM_LUT_MAX_SIZE) {
955 dev_err(&led->spmi_dev->dev,
956 "Exceed LUT limit\n");
957 return -EINVAL;
958 }
959 rc = pwm_lut_config(led->rgb_cfg->pwm_dev,
960 led->rgb_cfg->pwm_period_us,
961 led->rgb_cfg->duty_cycles->duty_pcts,
962 led->rgb_cfg->lut_params);
963 if (rc < 0) {
964 dev_err(&led->spmi_dev->dev, "Failed to " \
965 "configure pwm LUT\n");
966 return rc;
967 }
968 }
969 } else {
970 dev_err(&led->spmi_dev->dev,
971 "Invalid PWM channel\n");
972 return -EINVAL;
973 }
974
975 /* Initialize led for use in auto trickle charging mode */
976 rc = qpnp_led_masked_write(led, RGB_LED_ATC_CTL(led->base),
977 led->rgb_cfg->enable, led->rgb_cfg->enable);
978
979 return 0;
980}
981
Amy Malochef3d5a062012-08-16 19:14:11 -0700982static int __devinit qpnp_led_initialize(struct qpnp_led_data *led)
983{
984 int rc;
985
986 switch (led->id) {
987 case QPNP_ID_WLED:
988 rc = qpnp_wled_init(led);
989 if (rc)
Amy Malochea5ca5552012-10-23 13:34:46 -0700990 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -0700991 "WLED initialize failed(%d)\n", rc);
992 break;
Amy Maloche864a6d52012-10-03 15:58:12 -0700993 case QPNP_ID_FLASH1_LED0:
994 case QPNP_ID_FLASH1_LED1:
995 rc = qpnp_flash_init(led);
996 if (rc)
997 dev_err(&led->spmi_dev->dev,
998 "FLASH initialize failed(%d)\n", rc);
999 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07001000 case QPNP_ID_RGB_RED:
1001 case QPNP_ID_RGB_GREEN:
1002 case QPNP_ID_RGB_BLUE:
1003 rc = qpnp_rgb_init(led);
1004 if (rc)
1005 dev_err(&led->spmi_dev->dev,
1006 "RGB initialize failed(%d)\n", rc);
1007 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07001008 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07001009 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malocheeea7b592012-10-03 15:59:36 -07001010 return -EINVAL;
Amy Malochef3d5a062012-08-16 19:14:11 -07001011 }
1012
Amy Malocheeea7b592012-10-03 15:59:36 -07001013 return 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07001014}
1015
Amy Malochea5ca5552012-10-23 13:34:46 -07001016static int __devinit qpnp_get_common_configs(struct qpnp_led_data *led,
1017 struct device_node *node)
1018{
1019 int rc;
Asaf Penso55ac8472013-01-21 21:17:37 +02001020 u32 val;
Amy Malochea5ca5552012-10-23 13:34:46 -07001021 const char *temp_string;
1022
1023 led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
1024 rc = of_property_read_string(node, "linux,default-trigger",
1025 &temp_string);
1026 if (!rc)
1027 led->cdev.default_trigger = temp_string;
1028 else if (rc != -EINVAL)
1029 return rc;
1030
1031 led->default_on = false;
1032 rc = of_property_read_string(node, "qcom,default-state",
1033 &temp_string);
1034 if (!rc) {
1035 if (strncmp(temp_string, "on", sizeof("on")) == 0)
1036 led->default_on = true;
1037 } else if (rc != -EINVAL)
1038 return rc;
1039
Asaf Penso55ac8472013-01-21 21:17:37 +02001040 led->turn_off_delay_ms = 0;
1041 rc = of_property_read_u32(node, "qcom,turn-off-delay-ms", &val);
1042 if (!rc)
1043 led->turn_off_delay_ms = val;
1044 else if (rc != -EINVAL)
1045 return rc;
1046
Amy Malochea5ca5552012-10-23 13:34:46 -07001047 return 0;
1048}
1049
Amy Malochef3d5a062012-08-16 19:14:11 -07001050/*
1051 * Handlers for alternative sources of platform_data
1052 */
1053static int __devinit qpnp_get_config_wled(struct qpnp_led_data *led,
1054 struct device_node *node)
1055{
1056 u32 val;
1057 int rc;
Amy Malochef3d5a062012-08-16 19:14:11 -07001058
1059 led->wled_cfg = devm_kzalloc(&led->spmi_dev->dev,
1060 sizeof(struct wled_config_data), GFP_KERNEL);
1061 if (!led->wled_cfg) {
1062 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
1063 return -ENOMEM;
1064 }
1065
Amy Malochef3d5a062012-08-16 19:14:11 -07001066 led->wled_cfg->num_strings = WLED_DEFAULT_STRINGS;
1067 rc = of_property_read_u32(node, "qcom,num-strings", &val);
1068 if (!rc)
1069 led->wled_cfg->num_strings = (u8) val;
1070 else if (rc != -EINVAL)
1071 return rc;
1072
1073 led->wled_cfg->ovp_val = WLED_DEFAULT_OVP_VAL;
1074 rc = of_property_read_u32(node, "qcom,ovp-val", &val);
1075 if (!rc)
1076 led->wled_cfg->ovp_val = (u8) val;
1077 else if (rc != -EINVAL)
1078 return rc;
1079
1080 led->wled_cfg->boost_curr_lim = WLED_BOOST_LIM_DEFAULT;
1081 rc = of_property_read_u32(node, "qcom,boost-curr-lim", &val);
1082 if (!rc)
1083 led->wled_cfg->boost_curr_lim = (u8) val;
1084 else if (rc != -EINVAL)
1085 return rc;
1086
1087 led->wled_cfg->cp_select = WLED_CP_SEL_DEFAULT;
1088 rc = of_property_read_u32(node, "qcom,cp-sel", &val);
1089 if (!rc)
1090 led->wled_cfg->cp_select = (u8) val;
1091 else if (rc != -EINVAL)
1092 return rc;
1093
1094 led->wled_cfg->ctrl_delay_us = WLED_CTRL_DLY_DEFAULT;
1095 rc = of_property_read_u32(node, "qcom,ctrl-delay-us", &val);
1096 if (!rc)
1097 led->wled_cfg->ctrl_delay_us = (u8) val;
1098 else if (rc != -EINVAL)
1099 return rc;
1100
1101 led->wled_cfg->switch_freq = WLED_SWITCH_FREQ_DEFAULT;
1102 rc = of_property_read_u32(node, "qcom,switch-freq", &val);
1103 if (!rc)
1104 led->wled_cfg->switch_freq = (u8) val;
1105 else if (rc != -EINVAL)
1106 return rc;
1107
1108 led->wled_cfg->dig_mod_gen_en =
1109 of_property_read_bool(node, "qcom,dig-mod-gen-en");
1110
1111 led->wled_cfg->cs_out_en =
1112 of_property_read_bool(node, "qcom,cs-out-en");
1113
1114 led->wled_cfg->op_fdbck =
1115 of_property_read_bool(node, "qcom,op-fdbck");
1116
1117 return 0;
1118}
1119
Amy Maloche864a6d52012-10-03 15:58:12 -07001120static int __devinit qpnp_get_config_flash(struct qpnp_led_data *led,
1121 struct device_node *node)
1122{
1123 int rc;
1124 u32 val;
1125
1126 led->flash_cfg = devm_kzalloc(&led->spmi_dev->dev,
1127 sizeof(struct flash_config_data), GFP_KERNEL);
1128 if (!led->flash_cfg) {
1129 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
1130 return -ENOMEM;
1131 }
1132
1133 if (led->id == QPNP_ID_FLASH1_LED0) {
1134 led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
1135 led->flash_cfg->current_addr = FLASH_LED_0_CURR(led->base);
1136 led->flash_cfg->second_addr = FLASH_LED_1_CURR(led->base);
1137 led->flash_cfg->trigger_flash = FLASH_LED_0_OUTPUT;
1138 } else if (led->id == QPNP_ID_FLASH1_LED1) {
1139 led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
1140 led->flash_cfg->current_addr = FLASH_LED_1_CURR(led->base);
1141 led->flash_cfg->second_addr = FLASH_LED_0_CURR(led->base);
1142 led->flash_cfg->trigger_flash = FLASH_LED_1_OUTPUT;
1143 } else {
1144 dev_err(&led->spmi_dev->dev, "Unknown flash LED name given\n");
1145 return -EINVAL;
1146 }
1147
1148 rc = of_property_read_u32(node, "qcom,current", &val);
1149 if (!rc)
1150 led->flash_cfg->current_prgm = (val *
1151 FLASH_MAX_LEVEL / led->max_current);
1152 else
1153 return -EINVAL;
1154
1155 rc = of_property_read_u32(node, "qcom,headroom", &val);
1156 if (!rc)
1157 led->flash_cfg->headroom = (u8) val;
1158 else if (rc == -EINVAL)
1159 led->flash_cfg->headroom = HEADROOM_300mV;
1160 else
1161 return rc;
1162
1163 rc = of_property_read_u32(node, "qcom,duration", &val);
1164 if (!rc)
1165 led->flash_cfg->duration = (((u8) val) - 10) / 10;
1166 else if (rc == -EINVAL)
1167 led->flash_cfg->duration = FLASH_DURATION_200ms;
1168 else
1169 return rc;
1170
1171 rc = of_property_read_u32(node, "qcom,clamp-curr", &val);
1172 if (!rc)
1173 led->flash_cfg->clamp_curr = (val *
1174 FLASH_MAX_LEVEL / led->max_current);
1175 else if (rc == -EINVAL)
1176 led->flash_cfg->clamp_curr = FLASH_CLAMP_200mA;
1177 else
1178 return rc;
1179
1180 rc = of_property_read_u32(node, "qcom,startup-dly", &val);
1181 if (!rc)
1182 led->flash_cfg->startup_dly = (u8) val;
1183 else if (rc == -EINVAL)
1184 led->flash_cfg->startup_dly = DELAY_32us;
1185 else
1186 return rc;
1187
1188 led->flash_cfg->safety_timer =
1189 of_property_read_bool(node, "qcom,safety-timer");
1190
1191 return 0;
1192}
1193
Amy Malocheeea7b592012-10-03 15:59:36 -07001194static int __devinit qpnp_get_config_rgb(struct qpnp_led_data *led,
1195 struct device_node *node)
1196{
1197 struct property *prop;
1198 int rc, i;
1199 u32 val;
1200 u8 *temp_cfg;
1201
1202 led->rgb_cfg = devm_kzalloc(&led->spmi_dev->dev,
1203 sizeof(struct rgb_config_data), GFP_KERNEL);
1204 if (!led->rgb_cfg) {
1205 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
1206 return -ENOMEM;
1207 }
1208
1209 if (led->id == QPNP_ID_RGB_RED)
1210 led->rgb_cfg->enable = RGB_LED_ENABLE_RED;
1211 else if (led->id == QPNP_ID_RGB_GREEN)
1212 led->rgb_cfg->enable = RGB_LED_ENABLE_GREEN;
1213 else if (led->id == QPNP_ID_RGB_BLUE)
1214 led->rgb_cfg->enable = RGB_LED_ENABLE_BLUE;
1215 else
1216 return -EINVAL;
1217
1218 rc = of_property_read_u32(node, "qcom,mode", &val);
1219 if (!rc)
1220 led->rgb_cfg->mode = (u8) val;
1221 else
1222 return rc;
1223
1224 rc = of_property_read_u32(node, "qcom,pwm-channel", &val);
1225 if (!rc)
1226 led->rgb_cfg->pwm_channel = (u8) val;
1227 else
1228 return rc;
1229
1230 rc = of_property_read_u32(node, "qcom,pwm-us", &val);
1231 if (!rc)
1232 led->rgb_cfg->pwm_period_us = val;
1233 else
1234 return rc;
1235
1236 if (led->rgb_cfg->mode == RGB_MODE_LPG) {
1237 led->rgb_cfg->duty_cycles =
1238 devm_kzalloc(&led->spmi_dev->dev,
1239 sizeof(struct pwm_duty_cycles), GFP_KERNEL);
1240 if (!led->rgb_cfg->duty_cycles) {
1241 dev_err(&led->spmi_dev->dev,
1242 "Unable to allocate memory\n");
1243 return -ENOMEM;
1244 }
1245
1246 rc = of_property_read_u32(node, "qcom,duty-ms", &val);
1247 if (!rc)
1248 led->rgb_cfg->duty_cycles->duty_ms = (u8) val;
1249 else
1250 return rc;
1251
1252 prop = of_find_property(node, "qcom,duty-pcts",
1253 &led->rgb_cfg->duty_cycles->num_duty_pcts);
1254 if (!prop) {
1255 dev_err(&led->spmi_dev->dev, "Looking up property " \
1256 "node qcom,duty-pcts failed\n");
1257 return -ENODEV;
1258 } else if (!led->rgb_cfg->duty_cycles->num_duty_pcts) {
1259 dev_err(&led->spmi_dev->dev, "Invalid length of " \
1260 "duty pcts\n");
1261 return -EINVAL;
1262 }
1263
1264 led->rgb_cfg->duty_cycles->duty_pcts =
1265 devm_kzalloc(&led->spmi_dev->dev,
1266 sizeof(int) * led->rgb_cfg->duty_cycles->num_duty_pcts,
1267 GFP_KERNEL);
1268 if (!led->rgb_cfg->duty_cycles->duty_pcts) {
1269 dev_err(&led->spmi_dev->dev,
1270 "Unable to allocate memory\n");
1271 return -ENOMEM;
1272 }
1273
1274 temp_cfg = devm_kzalloc(&led->spmi_dev->dev,
1275 led->rgb_cfg->duty_cycles->num_duty_pcts *
1276 sizeof(u8), GFP_KERNEL);
1277 if (!temp_cfg) {
1278 dev_err(&led->spmi_dev->dev, "Failed to allocate " \
1279 "memory for duty pcts\n");
1280 return -ENOMEM;
1281 }
1282
1283 memcpy(temp_cfg, prop->value,
1284 led->rgb_cfg->duty_cycles->num_duty_pcts);
1285
1286 for (i = 0; i < led->rgb_cfg->duty_cycles->num_duty_pcts; i++)
1287 led->rgb_cfg->duty_cycles->duty_pcts[i] =
1288 (int) temp_cfg[i];
1289
1290 rc = of_property_read_u32(node, "qcom,start-idx", &val);
1291 if (!rc) {
1292 led->rgb_cfg->lut_params.start_idx = (u8) val;
1293 led->rgb_cfg->duty_cycles->start_idx = (u8) val;
1294 } else
1295 return rc;
1296
1297 led->rgb_cfg->lut_params.idx_len =
1298 led->rgb_cfg->duty_cycles->num_duty_pcts;
1299 led->rgb_cfg->lut_params.lut_pause_hi = 0;
1300 led->rgb_cfg->lut_params.lut_pause_lo = 0;
1301 led->rgb_cfg->lut_params.ramp_step_ms = 255;
1302 led->rgb_cfg->lut_params.flags = QPNP_LED_PWM_FLAGS;
1303 }
1304
1305 return 0;
1306}
1307
Amy Malochef3d5a062012-08-16 19:14:11 -07001308static int __devinit qpnp_leds_probe(struct spmi_device *spmi)
1309{
Amy Malochef9490c62012-11-27 19:26:04 -08001310 struct qpnp_led_data *led, *led_array;
Amy Malochef3d5a062012-08-16 19:14:11 -07001311 struct resource *led_resource;
Amy Malochea5ca5552012-10-23 13:34:46 -07001312 struct device_node *node, *temp;
Amy Malochef9490c62012-11-27 19:26:04 -08001313 int rc, i, num_leds = 0, parsed_leds = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07001314 const char *led_label;
1315
Amy Malochea5ca5552012-10-23 13:34:46 -07001316 node = spmi->dev.of_node;
1317 if (node == NULL)
1318 return -ENODEV;
1319
1320 temp = NULL;
1321 while ((temp = of_get_next_child(node, temp)))
1322 num_leds++;
1323
Amy Malochef9490c62012-11-27 19:26:04 -08001324 if (!num_leds)
1325 return -ECHILD;
1326
1327 led_array = devm_kzalloc(&spmi->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07001328 (sizeof(struct qpnp_led_data) * num_leds), GFP_KERNEL);
Amy Malochef9490c62012-11-27 19:26:04 -08001329 if (!led_array) {
Amy Malochef3d5a062012-08-16 19:14:11 -07001330 dev_err(&spmi->dev, "Unable to allocate memory\n");
1331 return -ENOMEM;
1332 }
1333
Amy Malochea5ca5552012-10-23 13:34:46 -07001334 for_each_child_of_node(node, temp) {
Amy Malochef9490c62012-11-27 19:26:04 -08001335 led = &led_array[parsed_leds];
1336 led->num_leds = num_leds;
Amy Malochea5ca5552012-10-23 13:34:46 -07001337 led->spmi_dev = spmi;
Amy Malochef3d5a062012-08-16 19:14:11 -07001338
Amy Malochea5ca5552012-10-23 13:34:46 -07001339 led_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
1340 if (!led_resource) {
1341 dev_err(&spmi->dev, "Unable to get LED base address\n");
Amy Malochef9490c62012-11-27 19:26:04 -08001342 rc = -ENXIO;
1343 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07001344 }
1345 led->base = led_resource->start;
Amy Malochef3d5a062012-08-16 19:14:11 -07001346
Amy Malochea5ca5552012-10-23 13:34:46 -07001347 rc = of_property_read_string(temp, "label", &led_label);
Amy Malochef3d5a062012-08-16 19:14:11 -07001348 if (rc < 0) {
1349 dev_err(&led->spmi_dev->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07001350 "Failure reading label, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08001351 goto fail_id_check;
Amy Malochef3d5a062012-08-16 19:14:11 -07001352 }
Amy Malochea5ca5552012-10-23 13:34:46 -07001353
1354 rc = of_property_read_string(temp, "linux,name",
1355 &led->cdev.name);
1356 if (rc < 0) {
1357 dev_err(&led->spmi_dev->dev,
1358 "Failure reading led name, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08001359 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07001360 }
1361
1362 rc = of_property_read_u32(temp, "qcom,max-current",
1363 &led->max_current);
1364 if (rc < 0) {
1365 dev_err(&led->spmi_dev->dev,
1366 "Failure reading max_current, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08001367 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07001368 }
1369
1370 rc = of_property_read_u32(temp, "qcom,id", &led->id);
1371 if (rc < 0) {
1372 dev_err(&led->spmi_dev->dev,
1373 "Failure reading led id, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08001374 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07001375 }
1376
1377 rc = qpnp_get_common_configs(led, temp);
1378 if (rc) {
1379 dev_err(&led->spmi_dev->dev,
1380 "Failure reading common led configuration," \
1381 " rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08001382 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07001383 }
1384
1385 led->cdev.brightness_set = qpnp_led_set;
1386 led->cdev.brightness_get = qpnp_led_get;
1387
1388 if (strncmp(led_label, "wled", sizeof("wled")) == 0) {
1389 rc = qpnp_get_config_wled(led, temp);
1390 if (rc < 0) {
1391 dev_err(&led->spmi_dev->dev,
1392 "Unable to read wled config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08001393 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07001394 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001395 } else if (strncmp(led_label, "flash", sizeof("flash"))
1396 == 0) {
1397 rc = qpnp_get_config_flash(led, temp);
1398 if (rc < 0) {
1399 dev_err(&led->spmi_dev->dev,
1400 "Unable to read flash config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08001401 goto fail_id_check;
Amy Maloche864a6d52012-10-03 15:58:12 -07001402 }
Amy Malocheeea7b592012-10-03 15:59:36 -07001403 } else if (strncmp(led_label, "rgb", sizeof("rgb")) == 0) {
1404 rc = qpnp_get_config_rgb(led, temp);
1405 if (rc < 0) {
1406 dev_err(&led->spmi_dev->dev,
1407 "Unable to read rgb config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08001408 goto fail_id_check;
Amy Malocheeea7b592012-10-03 15:59:36 -07001409 }
Amy Malochea5ca5552012-10-23 13:34:46 -07001410 } else {
1411 dev_err(&led->spmi_dev->dev, "No LED matching label\n");
Amy Malochef9490c62012-11-27 19:26:04 -08001412 rc = -EINVAL;
1413 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07001414 }
1415
1416 spin_lock_init(&led->lock);
1417
1418 rc = qpnp_led_initialize(led);
1419 if (rc < 0)
1420 goto fail_id_check;
1421
1422 rc = qpnp_led_set_max_brightness(led);
1423 if (rc < 0)
1424 goto fail_id_check;
1425
1426 rc = led_classdev_register(&spmi->dev, &led->cdev);
1427 if (rc) {
1428 dev_err(&spmi->dev, "unable to register led %d,rc=%d\n",
1429 led->id, rc);
1430 goto fail_id_check;
1431 }
1432 /* configure default state */
Asaf Penso55ac8472013-01-21 21:17:37 +02001433 if (led->default_on) {
Amy Malochea5ca5552012-10-23 13:34:46 -07001434 led->cdev.brightness = led->cdev.max_brightness;
Asaf Penso55ac8472013-01-21 21:17:37 +02001435 if (led->turn_off_delay_ms > 0)
1436 qpnp_led_turn_off(led);
1437 } else
Amy Malochea5ca5552012-10-23 13:34:46 -07001438 led->cdev.brightness = LED_OFF;
1439
1440 qpnp_led_set(&led->cdev, led->cdev.brightness);
Amy Malochef9490c62012-11-27 19:26:04 -08001441
1442 parsed_leds++;
Amy Malochef3d5a062012-08-16 19:14:11 -07001443 }
Amy Malochef9490c62012-11-27 19:26:04 -08001444 dev_set_drvdata(&spmi->dev, led_array);
Amy Malochef3d5a062012-08-16 19:14:11 -07001445 return 0;
1446
1447fail_id_check:
Amy Malochef9490c62012-11-27 19:26:04 -08001448 for (i = 0; i < parsed_leds; i++)
1449 led_classdev_unregister(&led_array[i].cdev);
Amy Malochef3d5a062012-08-16 19:14:11 -07001450 return rc;
1451}
1452
1453static int __devexit qpnp_leds_remove(struct spmi_device *spmi)
1454{
Amy Malochef9490c62012-11-27 19:26:04 -08001455 struct qpnp_led_data *led_array = dev_get_drvdata(&spmi->dev);
1456 int i, parsed_leds = led_array->num_leds;
Amy Malochef3d5a062012-08-16 19:14:11 -07001457
Amy Malochef9490c62012-11-27 19:26:04 -08001458 for (i = 0; i < parsed_leds; i++)
1459 led_classdev_unregister(&led_array[i].cdev);
Amy Malochef3d5a062012-08-16 19:14:11 -07001460
1461 return 0;
1462}
1463static struct of_device_id spmi_match_table[] = {
1464 { .compatible = "qcom,leds-qpnp",
1465 }
1466};
1467
1468static struct spmi_driver qpnp_leds_driver = {
1469 .driver = {
1470 .name = "qcom,leds-qpnp",
1471 .of_match_table = spmi_match_table,
1472 },
1473 .probe = qpnp_leds_probe,
1474 .remove = __devexit_p(qpnp_leds_remove),
1475};
1476
1477static int __init qpnp_led_init(void)
1478{
1479 return spmi_driver_register(&qpnp_leds_driver);
1480}
1481module_init(qpnp_led_init);
1482
1483static void __exit qpnp_led_exit(void)
1484{
1485 spmi_driver_unregister(&qpnp_leds_driver);
1486}
1487module_exit(qpnp_led_exit);
1488
1489MODULE_DESCRIPTION("QPNP LEDs driver");
1490MODULE_LICENSE("GPL v2");
1491MODULE_ALIAS("leds:leds-qpnp");
Amy Maloche864a6d52012-10-03 15:58:12 -07001492