blob: 79542966725e20567450edc55d35c8efd03756d8 [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 Maloche0150b5e2013-08-15 18:18:32 -070026#include <linux/delay.h>
Chun Zhangc3b505b2013-06-03 19:01:49 -070027#include <linux/regulator/consumer.h>
Chun Zhang7ce3a9f2013-10-22 10:48:12 -070028#include <linux/delay.h>
Amy Malochef3d5a062012-08-16 19:14:11 -070029
30#define WLED_MOD_EN_REG(base, n) (base + 0x60 + n*0x10)
31#define WLED_IDAC_DLY_REG(base, n) (WLED_MOD_EN_REG(base, n) + 0x01)
32#define WLED_FULL_SCALE_REG(base, n) (WLED_IDAC_DLY_REG(base, n) + 0x01)
Amy Maloched44516e2013-02-14 17:36:34 -080033#define WLED_MOD_SRC_SEL_REG(base, n) (WLED_FULL_SCALE_REG(base, n) + 0x01)
Amy Malochef3d5a062012-08-16 19:14:11 -070034
35/* wled control registers */
36#define WLED_BRIGHTNESS_CNTL_LSB(base, n) (base + 0x40 + 2*n)
37#define WLED_BRIGHTNESS_CNTL_MSB(base, n) (base + 0x41 + 2*n)
38#define WLED_MOD_CTRL_REG(base) (base + 0x46)
39#define WLED_SYNC_REG(base) (base + 0x47)
40#define WLED_FDBCK_CTRL_REG(base) (base + 0x48)
41#define WLED_SWITCHING_FREQ_REG(base) (base + 0x4C)
42#define WLED_OVP_CFG_REG(base) (base + 0x4D)
43#define WLED_BOOST_LIMIT_REG(base) (base + 0x4E)
44#define WLED_CURR_SINK_REG(base) (base + 0x4F)
45#define WLED_HIGH_POLE_CAP_REG(base) (base + 0x58)
46#define WLED_CURR_SINK_MASK 0xE0
47#define WLED_CURR_SINK_SHFT 0x05
Amy Maloche0150b5e2013-08-15 18:18:32 -070048#define WLED_DISABLE_ALL_SINKS 0x00
Amy Maloche9eccb4c2013-07-12 14:31:56 -070049#define WLED_SWITCH_FREQ_MASK 0x0F
Amy Malochef3d5a062012-08-16 19:14:11 -070050#define WLED_OVP_VAL_MASK 0x03
51#define WLED_OVP_VAL_BIT_SHFT 0x00
52#define WLED_BOOST_LIMIT_MASK 0x07
53#define WLED_BOOST_LIMIT_BIT_SHFT 0x00
54#define WLED_BOOST_ON 0x80
55#define WLED_BOOST_OFF 0x00
56#define WLED_EN_MASK 0x80
57#define WLED_NO_MASK 0x00
58#define WLED_CP_SELECT_MAX 0x03
59#define WLED_CP_SELECT_MASK 0x02
60#define WLED_USE_EXT_GEN_MOD_SRC 0x01
61#define WLED_CTL_DLY_STEP 200
62#define WLED_CTL_DLY_MAX 1400
63#define WLED_MAX_CURR 25
Amy Maloche0150b5e2013-08-15 18:18:32 -070064#define WLED_NO_CURRENT 0x00
65#define WLED_OVP_DELAY 1000
Amy Malochef3d5a062012-08-16 19:14:11 -070066#define WLED_MSB_MASK 0x0F
Himanshu Aggarwale51de272013-09-11 10:02:24 +053067#define WLED_MAX_CURR_MASK 0x1F
Amy Malochef3d5a062012-08-16 19:14:11 -070068#define WLED_OP_FDBCK_MASK 0x07
69#define WLED_OP_FDBCK_BIT_SHFT 0x00
Amy Malochebd687672013-03-18 11:23:45 -070070#define WLED_OP_FDBCK_DEFAULT 0x00
Amy Malochef3d5a062012-08-16 19:14:11 -070071
Amy Maloched55fdb82013-02-26 18:11:57 -080072#define WLED_MAX_LEVEL 4095
Amy Malochef3d5a062012-08-16 19:14:11 -070073#define WLED_8_BIT_MASK 0xFF
74#define WLED_4_BIT_MASK 0x0F
75#define WLED_8_BIT_SHFT 0x08
76#define WLED_MAX_DUTY_CYCLE 0xFFF
77
78#define WLED_SYNC_VAL 0x07
79#define WLED_SYNC_RESET_VAL 0x00
80
Amy Maloche0150b5e2013-08-15 18:18:32 -070081#define PMIC_VER_8026 0x04
82#define PMIC_VERSION_REG 0x0105
83
Amy Malochef3d5a062012-08-16 19:14:11 -070084#define WLED_DEFAULT_STRINGS 0x01
85#define WLED_DEFAULT_OVP_VAL 0x02
86#define WLED_BOOST_LIM_DEFAULT 0x03
87#define WLED_CP_SEL_DEFAULT 0x00
88#define WLED_CTRL_DLY_DEFAULT 0x00
Amy Maloche9eccb4c2013-07-12 14:31:56 -070089#define WLED_SWITCH_FREQ_DEFAULT 0x0B
Amy Malochef3d5a062012-08-16 19:14:11 -070090
Amy Maloche864a6d52012-10-03 15:58:12 -070091#define FLASH_SAFETY_TIMER(base) (base + 0x40)
92#define FLASH_MAX_CURR(base) (base + 0x41)
93#define FLASH_LED_0_CURR(base) (base + 0x42)
94#define FLASH_LED_1_CURR(base) (base + 0x43)
95#define FLASH_CLAMP_CURR(base) (base + 0x44)
96#define FLASH_LED_TMR_CTRL(base) (base + 0x48)
Amy Maloched44516e2013-02-14 17:36:34 -080097#define FLASH_HEADROOM(base) (base + 0x4A)
Amy Maloche864a6d52012-10-03 15:58:12 -070098#define FLASH_STARTUP_DELAY(base) (base + 0x4B)
99#define FLASH_MASK_ENABLE(base) (base + 0x4C)
100#define FLASH_VREG_OK_FORCE(base) (base + 0x4F)
101#define FLASH_ENABLE_CONTROL(base) (base + 0x46)
102#define FLASH_LED_STROBE_CTRL(base) (base + 0x47)
Amy Malochebc97c0d22013-03-24 22:06:16 -0700103#define FLASH_LED_UNLOCK_SECURE(base) (base + 0xD0)
104#define FLASH_LED_TORCH(base) (base + 0xE4)
Chun Zhange8954cf2013-05-02 11:14:34 -0700105#define FLASH_FAULT_DETECT(base) (base + 0x51)
Chun Zhang0d6ca072013-07-30 21:08:39 -0700106#define FLASH_PERIPHERAL_SUBTYPE(base) (base + 0x05)
Chun Zhang7ce3a9f2013-10-22 10:48:12 -0700107#define FLASH_CURRENT_RAMP(base) (base + 0x54)
Amy Maloche864a6d52012-10-03 15:58:12 -0700108
109#define FLASH_MAX_LEVEL 0x4F
Chun Zhang9c1fbaa2013-06-17 15:31:18 -0700110#define TORCH_MAX_LEVEL 0x0F
Amy Maloche864a6d52012-10-03 15:58:12 -0700111#define FLASH_NO_MASK 0x00
112
113#define FLASH_MASK_1 0x20
114#define FLASH_MASK_REG_MASK 0xE0
115#define FLASH_HEADROOM_MASK 0x03
116#define FLASH_SAFETY_TIMER_MASK 0x7F
117#define FLASH_CURRENT_MASK 0xFF
Amy Malochebc97c0d22013-03-24 22:06:16 -0700118#define FLASH_MAX_CURRENT_MASK 0x7F
Amy Maloche864a6d52012-10-03 15:58:12 -0700119#define FLASH_TMR_MASK 0x03
120#define FLASH_TMR_WATCHDOG 0x03
121#define FLASH_TMR_SAFETY 0x00
Chun Zhange8954cf2013-05-02 11:14:34 -0700122#define FLASH_FAULT_DETECT_MASK 0X80
Chun Zhangbcdcf232013-06-06 18:11:41 -0700123#define FLASH_HW_VREG_OK 0x40
Amy Maloche864a6d52012-10-03 15:58:12 -0700124#define FLASH_VREG_MASK 0xC0
Amy Maloche864a6d52012-10-03 15:58:12 -0700125#define FLASH_STARTUP_DLY_MASK 0x02
Chun Zhang7ce3a9f2013-10-22 10:48:12 -0700126#define FLASH_CURRENT_RAMP_MASK 0xBF
Amy Maloche864a6d52012-10-03 15:58:12 -0700127
128#define FLASH_ENABLE_ALL 0xE0
129#define FLASH_ENABLE_MODULE 0x80
130#define FLASH_ENABLE_MODULE_MASK 0x80
131#define FLASH_DISABLE_ALL 0x00
Amy Maloche38b9aae2013-02-07 12:15:34 -0800132#define FLASH_ENABLE_MASK 0xE0
Chun Zhang9d5ff672013-08-01 18:18:26 -0700133#define FLASH_ENABLE_LED_0 0xC0
134#define FLASH_ENABLE_LED_1 0xA0
Amy Maloche864a6d52012-10-03 15:58:12 -0700135#define FLASH_INIT_MASK 0xE0
Chun Zhange8954cf2013-05-02 11:14:34 -0700136#define FLASH_SELFCHECK_ENABLE 0x80
Chun Zhang7ce3a9f2013-10-22 10:48:12 -0700137#define FLASH_RAMP_STEP_27US 0xBF
Amy Maloche864a6d52012-10-03 15:58:12 -0700138
Amy Malochebc97c0d22013-03-24 22:06:16 -0700139#define FLASH_STROBE_SW 0xC0
Chun Zhang9d5ff672013-08-01 18:18:26 -0700140#define FLASH_STROBE_HW 0x04
Amy Malochebc97c0d22013-03-24 22:06:16 -0700141#define FLASH_STROBE_MASK 0xC7
Amy Maloche864a6d52012-10-03 15:58:12 -0700142#define FLASH_LED_0_OUTPUT 0x80
143#define FLASH_LED_1_OUTPUT 0x40
144
145#define FLASH_CURRENT_PRGM_MIN 1
146#define FLASH_CURRENT_PRGM_SHIFT 1
Amy Malochebc97c0d22013-03-24 22:06:16 -0700147#define FLASH_CURRENT_MAX 0x4F
Chun Zhange8954cf2013-05-02 11:14:34 -0700148#define FLASH_CURRENT_TORCH 0x07
Amy Maloche864a6d52012-10-03 15:58:12 -0700149
150#define FLASH_DURATION_200ms 0x13
151#define FLASH_CLAMP_200mA 0x0F
152
Amy Malochebc97c0d22013-03-24 22:06:16 -0700153#define FLASH_TORCH_MASK 0x03
154#define FLASH_LED_TORCH_ENABLE 0x00
155#define FLASH_LED_TORCH_DISABLE 0x03
156#define FLASH_UNLOCK_SECURE 0xA5
157#define FLASH_SECURE_MASK 0xFF
158
Chun Zhang0d6ca072013-07-30 21:08:39 -0700159#define FLASH_SUBTYPE_DUAL 0x01
160#define FLASH_SUBTYPE_SINGLE 0x02
161
Chun Zhang7ce3a9f2013-10-22 10:48:12 -0700162#define FLASH_RAMP_UP_DELAY_US 1000
163#define FLASH_RAMP_DN_DELAY_US 2160
164
Amy Malochea5ca5552012-10-23 13:34:46 -0700165#define LED_TRIGGER_DEFAULT "none"
166
Amy Malocheeea7b592012-10-03 15:59:36 -0700167#define RGB_LED_SRC_SEL(base) (base + 0x45)
168#define RGB_LED_EN_CTL(base) (base + 0x46)
169#define RGB_LED_ATC_CTL(base) (base + 0x47)
170
171#define RGB_MAX_LEVEL LED_FULL
172#define RGB_LED_ENABLE_RED 0x80
173#define RGB_LED_ENABLE_GREEN 0x40
174#define RGB_LED_ENABLE_BLUE 0x20
175#define RGB_LED_SOURCE_VPH_PWR 0x01
176#define RGB_LED_ENABLE_MASK 0xE0
177#define RGB_LED_SRC_MASK 0x03
178#define QPNP_LED_PWM_FLAGS (PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP)
Amy Maloche013b35b2013-03-19 12:29:11 -0700179#define QPNP_LUT_RAMP_STEP_DEFAULT 255
Amy Malocheeea7b592012-10-03 15:59:36 -0700180#define PWM_LUT_MAX_SIZE 63
Amy Maloche4cf9f322013-05-29 15:53:46 -0700181#define PWM_GPLED_LUT_MAX_SIZE 31
Amy Malocheeea7b592012-10-03 15:59:36 -0700182#define RGB_LED_DISABLE 0x00
183
Amy Malochef3813742013-04-11 19:33:47 -0700184#define MPP_MAX_LEVEL LED_FULL
185#define LED_MPP_MODE_CTRL(base) (base + 0x40)
186#define LED_MPP_VIN_CTRL(base) (base + 0x41)
187#define LED_MPP_EN_CTRL(base) (base + 0x46)
188#define LED_MPP_SINK_CTRL(base) (base + 0x4C)
189
Mohan Pallaka38ec7f32013-09-11 13:01:48 +0530190#define LED_MPP_CURRENT_MIN 5
191#define LED_MPP_CURRENT_MAX 40
Chun Zhang874c9ab2013-07-08 17:18:34 -0700192#define LED_MPP_VIN_CTRL_DEFAULT 0
Amy Malochea2726f02013-05-10 10:19:03 -0700193#define LED_MPP_CURRENT_PER_SETTING 5
Amy Malochef3813742013-04-11 19:33:47 -0700194#define LED_MPP_SOURCE_SEL_DEFAULT LED_MPP_MODE_ENABLE
195
196#define LED_MPP_SINK_MASK 0x07
197#define LED_MPP_MODE_MASK 0x7F
Chun Zhang874c9ab2013-07-08 17:18:34 -0700198#define LED_MPP_VIN_MASK 0x03
Amy Malochef3813742013-04-11 19:33:47 -0700199#define LED_MPP_EN_MASK 0x80
Amy Malochece59f662013-05-02 10:59:53 -0700200#define LED_MPP_SRC_MASK 0x0F
201#define LED_MPP_MODE_CTRL_MASK 0x70
Amy Malochef3813742013-04-11 19:33:47 -0700202
203#define LED_MPP_MODE_SINK (0x06 << 4)
204#define LED_MPP_MODE_ENABLE 0x01
205#define LED_MPP_MODE_OUTPUT 0x10
206#define LED_MPP_MODE_DISABLE 0x00
207#define LED_MPP_EN_ENABLE 0x80
208#define LED_MPP_EN_DISABLE 0x00
209
210#define MPP_SOURCE_DTEST1 0x08
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530211
212#define KPDBL_MAX_LEVEL LED_FULL
213#define KPDBL_ROW_SRC_SEL(base) (base + 0x40)
214#define KPDBL_ENABLE(base) (base + 0x46)
215#define KPDBL_ROW_SRC(base) (base + 0xE5)
216
217#define KPDBL_ROW_SRC_SEL_VAL_MASK 0x0F
218#define KPDBL_ROW_SCAN_EN_MASK 0x80
219#define KPDBL_ROW_SCAN_VAL_MASK 0x0F
220#define KPDBL_ROW_SCAN_EN_SHIFT 7
221#define KPDBL_MODULE_EN 0x80
222#define KPDBL_MODULE_DIS 0x00
223#define KPDBL_MODULE_EN_MASK 0x80
224
Amy Malochef3d5a062012-08-16 19:14:11 -0700225/**
226 * enum qpnp_leds - QPNP supported led ids
227 * @QPNP_ID_WLED - White led backlight
228 */
229enum qpnp_leds {
Amy Maloche864a6d52012-10-03 15:58:12 -0700230 QPNP_ID_WLED = 0,
231 QPNP_ID_FLASH1_LED0,
232 QPNP_ID_FLASH1_LED1,
Amy Malocheeea7b592012-10-03 15:59:36 -0700233 QPNP_ID_RGB_RED,
234 QPNP_ID_RGB_GREEN,
235 QPNP_ID_RGB_BLUE,
Amy Malochef3813742013-04-11 19:33:47 -0700236 QPNP_ID_LED_MPP,
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530237 QPNP_ID_KPDBL,
Amy Maloche864a6d52012-10-03 15:58:12 -0700238 QPNP_ID_MAX,
Amy Malochef3d5a062012-08-16 19:14:11 -0700239};
240
241/* current boost limit */
242enum wled_current_boost_limit {
243 WLED_CURR_LIMIT_105mA,
244 WLED_CURR_LIMIT_385mA,
245 WLED_CURR_LIMIT_525mA,
246 WLED_CURR_LIMIT_805mA,
247 WLED_CURR_LIMIT_980mA,
248 WLED_CURR_LIMIT_1260mA,
249 WLED_CURR_LIMIT_1400mA,
250 WLED_CURR_LIMIT_1680mA,
251};
252
253/* over voltage protection threshold */
254enum wled_ovp_threshold {
255 WLED_OVP_35V,
256 WLED_OVP_32V,
257 WLED_OVP_29V,
258 WLED_OVP_37V,
259};
260
Amy Maloche864a6d52012-10-03 15:58:12 -0700261enum flash_headroom {
262 HEADROOM_250mV = 0,
263 HEADROOM_300mV,
264 HEADROOM_400mV,
265 HEADROOM_500mV,
266};
267
268enum flash_startup_dly {
269 DELAY_10us = 0,
270 DELAY_32us,
271 DELAY_64us,
272 DELAY_128us,
273};
274
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530275enum led_mode {
276 PWM_MODE = 0,
277 LPG_MODE,
Amy Malochea2726f02013-05-10 10:19:03 -0700278 MANUAL_MODE,
Amy Malocheeea7b592012-10-03 15:59:36 -0700279};
280
Amy Malochef3d5a062012-08-16 19:14:11 -0700281static u8 wled_debug_regs[] = {
282 /* common registers */
283 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4f,
284 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
285 /* LED1 */
286 0x60, 0x61, 0x62, 0x63, 0x66,
Amy Malochea5ca5552012-10-23 13:34:46 -0700287 /* LED2 */
Amy Malochef3d5a062012-08-16 19:14:11 -0700288 0x70, 0x71, 0x72, 0x73, 0x76,
Amy Malochea5ca5552012-10-23 13:34:46 -0700289 /* LED3 */
Amy Malochef3d5a062012-08-16 19:14:11 -0700290 0x80, 0x81, 0x82, 0x83, 0x86,
291};
292
Amy Maloche864a6d52012-10-03 15:58:12 -0700293static u8 flash_debug_regs[] = {
294 0x40, 0x41, 0x42, 0x43, 0x44, 0x48, 0x49, 0x4b, 0x4c,
295 0x4f, 0x46, 0x47,
296};
297
Amy Malocheeea7b592012-10-03 15:59:36 -0700298static u8 rgb_pwm_debug_regs[] = {
299 0x45, 0x46, 0x47,
300};
Amy Malochef3813742013-04-11 19:33:47 -0700301
302static u8 mpp_debug_regs[] = {
303 0x40, 0x41, 0x42, 0x45, 0x46, 0x4c,
304};
305
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530306static u8 kpdbl_debug_regs[] = {
307 0x40, 0x46, 0xb1, 0xb3, 0xb4, 0xe5,
308};
309
Amy Malochef3d5a062012-08-16 19:14:11 -0700310/**
Amy Malochea2726f02013-05-10 10:19:03 -0700311 * pwm_config_data - pwm configuration data
312 * @lut_params - lut parameters to be used by pwm driver
313 * @pwm_device - pwm device
314 * @pwm_channel - pwm channel to be configured for led
315 * @pwm_period_us - period for pwm, in us
316 * @mode - mode the led operates in
Amy Maloche4cf9f322013-05-29 15:53:46 -0700317 * @old_duty_pcts - storage for duty pcts that may need to be reused
318 * @default_mode - default mode of LED as set in device tree
319 * @use_blink - use blink sysfs entry
320 * @blinking - device is currently blinking w/LPG mode
Amy Malochea2726f02013-05-10 10:19:03 -0700321 */
322struct pwm_config_data {
323 struct lut_params lut_params;
324 struct pwm_device *pwm_dev;
325 int pwm_channel;
326 u32 pwm_period_us;
327 struct pwm_duty_cycles *duty_cycles;
Amy Maloche4cf9f322013-05-29 15:53:46 -0700328 int *old_duty_pcts;
Amy Malochea2726f02013-05-10 10:19:03 -0700329 u8 mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -0700330 u8 default_mode;
Amy Malochea5c4ed82013-06-05 11:05:28 -0700331 bool use_blink;
Amy Maloche4cf9f322013-05-29 15:53:46 -0700332 bool blinking;
Amy Malochea2726f02013-05-10 10:19:03 -0700333};
334
335/**
Amy Malochef3d5a062012-08-16 19:14:11 -0700336 * wled_config_data - wled configuration data
337 * @num_strings - number of wled strings supported
338 * @ovp_val - over voltage protection threshold
339 * @boost_curr_lim - boot current limit
340 * @cp_select - high pole capacitance
341 * @ctrl_delay_us - delay in activation of led
342 * @dig_mod_gen_en - digital module generator
343 * @cs_out_en - current sink output enable
344 * @op_fdbck - selection of output as feedback for the boost
345 */
346struct wled_config_data {
347 u8 num_strings;
348 u8 ovp_val;
349 u8 boost_curr_lim;
350 u8 cp_select;
351 u8 ctrl_delay_us;
352 u8 switch_freq;
Amy Malochebd687672013-03-18 11:23:45 -0700353 u8 op_fdbck;
Amy Maloche0150b5e2013-08-15 18:18:32 -0700354 u8 pmic_version;
Amy Malochef3d5a062012-08-16 19:14:11 -0700355 bool dig_mod_gen_en;
356 bool cs_out_en;
Amy Malochef3d5a062012-08-16 19:14:11 -0700357};
358
359/**
Amy Malochef3813742013-04-11 19:33:47 -0700360 * mpp_config_data - mpp configuration data
Amy Malochea2726f02013-05-10 10:19:03 -0700361 * @pwm_cfg - device pwm configuration
Amy Malochef3813742013-04-11 19:33:47 -0700362 * @current_setting - current setting, 5ma-40ma in 5ma increments
Amy Malochea2726f02013-05-10 10:19:03 -0700363 * @source_sel - source selection
364 * @mode_ctrl - mode control
Chun Zhang874c9ab2013-07-08 17:18:34 -0700365 * @vin_ctrl - input control
366 * @min_brightness - minimum brightness supported
Amy Malochea2726f02013-05-10 10:19:03 -0700367 * @pwm_mode - pwm mode in use
Amy Malochef3813742013-04-11 19:33:47 -0700368 */
369struct mpp_config_data {
Amy Malochea2726f02013-05-10 10:19:03 -0700370 struct pwm_config_data *pwm_cfg;
Amy Malochef3813742013-04-11 19:33:47 -0700371 u8 current_setting;
372 u8 source_sel;
373 u8 mode_ctrl;
Chun Zhang874c9ab2013-07-08 17:18:34 -0700374 u8 vin_ctrl;
375 u8 min_brightness;
Amy Malochea2726f02013-05-10 10:19:03 -0700376 u8 pwm_mode;
Amy Malochef3813742013-04-11 19:33:47 -0700377};
378
379/**
Amy Maloche864a6d52012-10-03 15:58:12 -0700380 * flash_config_data - flash configuration data
381 * @current_prgm - current to be programmed, scaled by max level
382 * @clamp_curr - clamp current to use
383 * @headroom - headroom value to use
384 * @duration - duration of the flash
385 * @enable_module - enable address for particular flash
386 * @trigger_flash - trigger flash
387 * @startup_dly - startup delay for flash
Amy Malochebc97c0d22013-03-24 22:06:16 -0700388 * @strobe_type - select between sw and hw strobe
Chun Zhang0d6ca072013-07-30 21:08:39 -0700389 * @peripheral_subtype - module peripheral subtype
Amy Maloche864a6d52012-10-03 15:58:12 -0700390 * @current_addr - address to write for current
391 * @second_addr - address of secondary flash to be written
392 * @safety_timer - enable safety timer or watchdog timer
Amy Malochebc97c0d22013-03-24 22:06:16 -0700393 * @torch_enable - enable flash LED torch mode
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700394 * @flash_reg_get - flash regulator attached or not
Chun Zhangc3b505b2013-06-03 19:01:49 -0700395 * @flash_on - flash status, on or off
Chun Zhangdf2d3062013-06-25 20:14:46 -0700396 * @torch_on - torch status, on or off
Chun Zhangc3b505b2013-06-03 19:01:49 -0700397 * @flash_boost_reg - boost regulator for flash
Chun Zhangdf2d3062013-06-25 20:14:46 -0700398 * @torch_boost_reg - boost regulator for torch
Amy Maloche864a6d52012-10-03 15:58:12 -0700399 */
400struct flash_config_data {
401 u8 current_prgm;
402 u8 clamp_curr;
403 u8 headroom;
404 u8 duration;
405 u8 enable_module;
406 u8 trigger_flash;
407 u8 startup_dly;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700408 u8 strobe_type;
Chun Zhang0d6ca072013-07-30 21:08:39 -0700409 u8 peripheral_subtype;
Amy Maloche864a6d52012-10-03 15:58:12 -0700410 u16 current_addr;
411 u16 second_addr;
412 bool safety_timer;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700413 bool torch_enable;
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700414 bool flash_reg_get;
Chun Zhangc3b505b2013-06-03 19:01:49 -0700415 bool flash_on;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700416 bool torch_on;
Chun Zhangc3b505b2013-06-03 19:01:49 -0700417 struct regulator *flash_boost_reg;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700418 struct regulator *torch_boost_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -0700419};
420
421/**
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530422 * kpdbl_config_data - kpdbl configuration data
Amy Malochea2726f02013-05-10 10:19:03 -0700423 * @pwm_cfg - device pwm configuration
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530424 * @mode - running mode: pwm or lut
425 * @row_id - row id of the led
426 * @row_src_vbst - 0 for vph_pwr and 1 for vbst
427 * @row_src_en - enable row source
428 * @always_on - always on row
429 * @lut_params - lut parameters to be used by pwm driver
430 * @duty_cycles - duty cycles for lut
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530431 */
432struct kpdbl_config_data {
Amy Malochea2726f02013-05-10 10:19:03 -0700433 struct pwm_config_data *pwm_cfg;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530434 u32 row_id;
435 bool row_src_vbst;
436 bool row_src_en;
437 bool always_on;
438 struct pwm_duty_cycles *duty_cycles;
439 struct lut_params lut_params;
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530440};
441
442/**
Amy Malocheeea7b592012-10-03 15:59:36 -0700443 * rgb_config_data - rgb configuration data
Amy Malochea2726f02013-05-10 10:19:03 -0700444 * @pwm_cfg - device pwm configuration
445 * @enable - bits to enable led
Amy Malocheeea7b592012-10-03 15:59:36 -0700446 */
447struct rgb_config_data {
Amy Malochea2726f02013-05-10 10:19:03 -0700448 struct pwm_config_data *pwm_cfg;
Amy Malocheeea7b592012-10-03 15:59:36 -0700449 u8 enable;
450};
451
452/**
Amy Malochef3d5a062012-08-16 19:14:11 -0700453 * struct qpnp_led_data - internal led data structure
454 * @led_classdev - led class device
Asaf Penso55ac8472013-01-21 21:17:37 +0200455 * @delayed_work - delayed work for turning off the LED
Chun Zhang815a1832013-06-20 13:47:13 -0700456 * @work - workqueue for led
Amy Malochef3d5a062012-08-16 19:14:11 -0700457 * @id - led index
458 * @base_reg - base register given in device tree
459 * @lock - to protect the transactions
460 * @reg - cached value of led register
Amy Malochea5ca5552012-10-23 13:34:46 -0700461 * @num_leds - number of leds in the module
Amy Malochef3d5a062012-08-16 19:14:11 -0700462 * @max_current - maximum current supported by LED
463 * @default_on - true: default state max, false, default state 0
Asaf Penso55ac8472013-01-21 21:17:37 +0200464 * @turn_off_delay_ms - number of msec before turning off the LED
Amy Malochef3d5a062012-08-16 19:14:11 -0700465 */
466struct qpnp_led_data {
467 struct led_classdev cdev;
468 struct spmi_device *spmi_dev;
Asaf Penso55ac8472013-01-21 21:17:37 +0200469 struct delayed_work dwork;
Chun Zhang815a1832013-06-20 13:47:13 -0700470 struct work_struct work;
Amy Malochef3d5a062012-08-16 19:14:11 -0700471 int id;
472 u16 base;
473 u8 reg;
Amy Malochea5ca5552012-10-23 13:34:46 -0700474 u8 num_leds;
Chun Zhang815a1832013-06-20 13:47:13 -0700475 struct mutex lock;
Amy Malochef3d5a062012-08-16 19:14:11 -0700476 struct wled_config_data *wled_cfg;
Amy Maloche864a6d52012-10-03 15:58:12 -0700477 struct flash_config_data *flash_cfg;
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530478 struct kpdbl_config_data *kpdbl_cfg;
Amy Malocheeea7b592012-10-03 15:59:36 -0700479 struct rgb_config_data *rgb_cfg;
Amy Malochef3813742013-04-11 19:33:47 -0700480 struct mpp_config_data *mpp_cfg;
Amy Malochef3d5a062012-08-16 19:14:11 -0700481 int max_current;
482 bool default_on;
Asaf Penso55ac8472013-01-21 21:17:37 +0200483 int turn_off_delay_ms;
Amy Malochef3d5a062012-08-16 19:14:11 -0700484};
485
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530486static int num_kpbl_leds_on;
487
Amy Malochef3d5a062012-08-16 19:14:11 -0700488static int
489qpnp_led_masked_write(struct qpnp_led_data *led, u16 addr, u8 mask, u8 val)
490{
491 int rc;
492 u8 reg;
493
494 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
495 addr, &reg, 1);
496 if (rc) {
497 dev_err(&led->spmi_dev->dev,
498 "Unable to read from addr=%x, rc(%d)\n", addr, rc);
499 }
500
501 reg &= ~mask;
502 reg |= val;
503
504 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
505 addr, &reg, 1);
506 if (rc)
507 dev_err(&led->spmi_dev->dev,
508 "Unable to write to addr=%x, rc(%d)\n", addr, rc);
509 return rc;
510}
511
Amy Malochea5ca5552012-10-23 13:34:46 -0700512static void qpnp_dump_regs(struct qpnp_led_data *led, u8 regs[], u8 array_size)
513{
514 int i;
515 u8 val;
516
517 pr_debug("===== %s LED register dump start =====\n", led->cdev.name);
518 for (i = 0; i < array_size; i++) {
519 spmi_ext_register_readl(led->spmi_dev->ctrl,
520 led->spmi_dev->sid,
521 led->base + regs[i],
522 &val, sizeof(val));
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530523 pr_debug("%s: 0x%x = 0x%x\n", led->cdev.name,
524 led->base + regs[i], val);
Amy Malochea5ca5552012-10-23 13:34:46 -0700525 }
526 pr_debug("===== %s LED register dump end =====\n", led->cdev.name);
527}
528
Amy Maloche0150b5e2013-08-15 18:18:32 -0700529static int qpnp_wled_sync(struct qpnp_led_data *led)
530{
531 int rc;
532 u8 val;
533
534 /* sync */
535 val = WLED_SYNC_VAL;
536 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
537 WLED_SYNC_REG(led->base), &val, 1);
538 if (rc) {
539 dev_err(&led->spmi_dev->dev,
540 "WLED set sync reg failed(%d)\n", rc);
541 return rc;
542 }
543
544 val = WLED_SYNC_RESET_VAL;
545 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
546 WLED_SYNC_REG(led->base), &val, 1);
547 if (rc) {
548 dev_err(&led->spmi_dev->dev,
549 "WLED reset sync reg failed(%d)\n", rc);
550 return rc;
551 }
552 return 0;
553}
554
Amy Malochef3d5a062012-08-16 19:14:11 -0700555static int qpnp_wled_set(struct qpnp_led_data *led)
556{
Amy Maloched55fdb82013-02-26 18:11:57 -0800557 int rc, duty, level;
Amy Maloche0150b5e2013-08-15 18:18:32 -0700558 u8 val, i, num_wled_strings, sink_val;
559
560 num_wled_strings = led->wled_cfg->num_strings;
Amy Malochef3d5a062012-08-16 19:14:11 -0700561
562 level = led->cdev.brightness;
563
564 if (level > WLED_MAX_LEVEL)
565 level = WLED_MAX_LEVEL;
566 if (level == 0) {
Amy Maloche0150b5e2013-08-15 18:18:32 -0700567 for (i = 0; i < num_wled_strings; i++) {
568 rc = qpnp_led_masked_write(led,
569 WLED_FULL_SCALE_REG(led->base, i),
570 WLED_MAX_CURR_MASK, WLED_NO_CURRENT);
571 if (rc) {
572 dev_err(&led->spmi_dev->dev,
573 "Write max current failure (%d)\n",
574 rc);
575 return rc;
576 }
577 }
578
579 rc = qpnp_wled_sync(led);
580 if (rc) {
581 dev_err(&led->spmi_dev->dev,
582 "WLED sync failed(%d)\n", rc);
583 return rc;
584 }
585
586 rc = spmi_ext_register_readl(led->spmi_dev->ctrl,
587 led->spmi_dev->sid, WLED_CURR_SINK_REG(led->base),
588 &sink_val, 1);
589 if (rc) {
590 dev_err(&led->spmi_dev->dev,
591 "WLED read sink reg failed(%d)\n", rc);
592 return rc;
593 }
594
595 if (led->wled_cfg->pmic_version == PMIC_VER_8026) {
596 val = WLED_DISABLE_ALL_SINKS;
597 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
598 led->spmi_dev->sid,
599 WLED_CURR_SINK_REG(led->base), &val, 1);
600 if (rc) {
601 dev_err(&led->spmi_dev->dev,
602 "WLED write sink reg failed(%d)\n", rc);
603 return rc;
604 }
605
606 usleep(WLED_OVP_DELAY);
607 }
608
Amy Malochef3d5a062012-08-16 19:14:11 -0700609 val = WLED_BOOST_OFF;
610 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
611 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
612 &val, 1);
613 if (rc) {
614 dev_err(&led->spmi_dev->dev,
615 "WLED write ctrl reg failed(%d)\n", rc);
616 return rc;
617 }
Amy Maloche0150b5e2013-08-15 18:18:32 -0700618
619 for (i = 0; i < num_wled_strings; i++) {
620 rc = qpnp_led_masked_write(led,
621 WLED_FULL_SCALE_REG(led->base, i),
622 WLED_MAX_CURR_MASK, led->max_current);
623 if (rc) {
624 dev_err(&led->spmi_dev->dev,
625 "Write max current failure (%d)\n",
626 rc);
627 return rc;
628 }
629 }
630
631 rc = qpnp_wled_sync(led);
632 if (rc) {
633 dev_err(&led->spmi_dev->dev,
634 "WLED sync failed(%d)\n", rc);
635 return rc;
636 }
637
638 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
639 led->spmi_dev->sid, WLED_CURR_SINK_REG(led->base),
640 &sink_val, 1);
641 if (rc) {
642 dev_err(&led->spmi_dev->dev,
643 "WLED write sink reg failed(%d)\n", rc);
644 return rc;
645 }
646
Amy Malochef3d5a062012-08-16 19:14:11 -0700647 } else {
648 val = WLED_BOOST_ON;
649 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
650 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
651 &val, 1);
652 if (rc) {
653 dev_err(&led->spmi_dev->dev,
654 "WLED write ctrl reg failed(%d)\n", rc);
655 return rc;
656 }
657 }
658
659 duty = (WLED_MAX_DUTY_CYCLE * level) / WLED_MAX_LEVEL;
660
Amy Malochef3d5a062012-08-16 19:14:11 -0700661 /* program brightness control registers */
662 for (i = 0; i < num_wled_strings; i++) {
663 rc = qpnp_led_masked_write(led,
664 WLED_BRIGHTNESS_CNTL_MSB(led->base, i), WLED_MSB_MASK,
665 (duty >> WLED_8_BIT_SHFT) & WLED_4_BIT_MASK);
666 if (rc) {
667 dev_err(&led->spmi_dev->dev,
668 "WLED set brightness MSB failed(%d)\n", rc);
669 return rc;
670 }
671 val = duty & WLED_8_BIT_MASK;
672 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
673 led->spmi_dev->sid,
674 WLED_BRIGHTNESS_CNTL_LSB(led->base, i), &val, 1);
675 if (rc) {
676 dev_err(&led->spmi_dev->dev,
677 "WLED set brightness LSB failed(%d)\n", rc);
678 return rc;
679 }
680 }
681
Amy Maloche0150b5e2013-08-15 18:18:32 -0700682 rc = qpnp_wled_sync(led);
Amy Malochef3d5a062012-08-16 19:14:11 -0700683 if (rc) {
Amy Maloche0150b5e2013-08-15 18:18:32 -0700684 dev_err(&led->spmi_dev->dev, "WLED sync failed(%d)\n", rc);
Amy Malochef3d5a062012-08-16 19:14:11 -0700685 return rc;
686 }
687 return 0;
688}
689
Amy Malochef3813742013-04-11 19:33:47 -0700690static int qpnp_mpp_set(struct qpnp_led_data *led)
691{
692 int rc, val;
Amy Malochea2726f02013-05-10 10:19:03 -0700693 int duty_us;
Amy Malochef3813742013-04-11 19:33:47 -0700694
695 if (led->cdev.brightness) {
Chun Zhang874c9ab2013-07-08 17:18:34 -0700696 if (led->cdev.brightness < led->mpp_cfg->min_brightness) {
697 dev_warn(&led->spmi_dev->dev,
698 "brightness is less than supported..." \
699 "set to minimum supported\n");
700 led->cdev.brightness = led->mpp_cfg->min_brightness;
701 }
702
Amy Maloche4cf9f322013-05-29 15:53:46 -0700703 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
704 if (!led->mpp_cfg->pwm_cfg->blinking) {
705 led->mpp_cfg->pwm_cfg->mode =
706 led->mpp_cfg->pwm_cfg->default_mode;
707 led->mpp_cfg->pwm_mode =
708 led->mpp_cfg->pwm_cfg->default_mode;
709 }
710 }
Amy Malochea2726f02013-05-10 10:19:03 -0700711 if (led->mpp_cfg->pwm_mode == PWM_MODE) {
712 pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
713 duty_us = (led->mpp_cfg->pwm_cfg->pwm_period_us *
714 led->cdev.brightness) / LED_FULL;
715 /*config pwm for brightness scaling*/
716 rc = pwm_config(led->mpp_cfg->pwm_cfg->pwm_dev,
717 duty_us,
718 led->mpp_cfg->pwm_cfg->pwm_period_us);
719 if (rc < 0) {
720 dev_err(&led->spmi_dev->dev, "Failed to " \
721 "configure pwm for new values\n");
722 return rc;
723 }
Amy Malochef3813742013-04-11 19:33:47 -0700724 }
725
Amy Malochea2726f02013-05-10 10:19:03 -0700726 if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
727 pwm_enable(led->mpp_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka38ec7f32013-09-11 13:01:48 +0530728 else {
729 if (led->cdev.brightness < LED_MPP_CURRENT_MIN)
730 led->cdev.brightness = LED_MPP_CURRENT_MIN;
731
732 val = (led->cdev.brightness / LED_MPP_CURRENT_MIN) - 1;
733
734 rc = qpnp_led_masked_write(led,
735 LED_MPP_SINK_CTRL(led->base),
736 LED_MPP_SINK_MASK, val);
737 if (rc) {
738 dev_err(&led->spmi_dev->dev,
739 "Failed to write sink control reg\n");
740 return rc;
741 }
742 }
Amy Malochea2726f02013-05-10 10:19:03 -0700743
Amy Malochece59f662013-05-02 10:59:53 -0700744 val = (led->mpp_cfg->source_sel & LED_MPP_SRC_MASK) |
745 (led->mpp_cfg->mode_ctrl & LED_MPP_MODE_CTRL_MASK);
Amy Malochef3813742013-04-11 19:33:47 -0700746
747 rc = qpnp_led_masked_write(led,
Amy Malochea2726f02013-05-10 10:19:03 -0700748 LED_MPP_MODE_CTRL(led->base), LED_MPP_MODE_MASK,
749 val);
Amy Malochef3813742013-04-11 19:33:47 -0700750 if (rc) {
751 dev_err(&led->spmi_dev->dev,
752 "Failed to write led mode reg\n");
753 return rc;
754 }
755
756 rc = qpnp_led_masked_write(led,
757 LED_MPP_EN_CTRL(led->base), LED_MPP_EN_MASK,
758 LED_MPP_EN_ENABLE);
Amy Malochea2726f02013-05-10 10:19:03 -0700759 if (rc) {
760 dev_err(&led->spmi_dev->dev,
761 "Failed to write led enable " \
762 "reg\n");
763 return rc;
764 }
Amy Malochef3813742013-04-11 19:33:47 -0700765 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -0700766 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
767 led->mpp_cfg->pwm_cfg->mode =
768 led->mpp_cfg->pwm_cfg->default_mode;
769 led->mpp_cfg->pwm_mode =
770 led->mpp_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -0700771 pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
Amy Maloche4cf9f322013-05-29 15:53:46 -0700772 }
Amy Malochef3813742013-04-11 19:33:47 -0700773 rc = qpnp_led_masked_write(led,
774 LED_MPP_MODE_CTRL(led->base),
775 LED_MPP_MODE_MASK,
776 LED_MPP_MODE_DISABLE);
777 if (rc) {
778 dev_err(&led->spmi_dev->dev,
779 "Failed to write led mode reg\n");
780 return rc;
781 }
782
783 rc = qpnp_led_masked_write(led,
784 LED_MPP_EN_CTRL(led->base),
785 LED_MPP_EN_MASK,
786 LED_MPP_EN_DISABLE);
787 if (rc) {
788 dev_err(&led->spmi_dev->dev,
789 "Failed to write led enable reg\n");
790 return rc;
791 }
792 }
793
Amy Maloche4cf9f322013-05-29 15:53:46 -0700794 if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
795 led->mpp_cfg->pwm_cfg->blinking = false;
Amy Malochef3813742013-04-11 19:33:47 -0700796 qpnp_dump_regs(led, mpp_debug_regs, ARRAY_SIZE(mpp_debug_regs));
797
798 return 0;
799}
800
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700801static int qpnp_flash_regulator_operate(struct qpnp_led_data *led, bool on)
802{
803 int rc, i;
804 struct qpnp_led_data *led_array;
805 bool regulator_on = false;
806
807 led_array = dev_get_drvdata(&led->spmi_dev->dev);
808 if (!led_array) {
809 dev_err(&led->spmi_dev->dev,
810 "Unable to get LED array\n");
811 return -EINVAL;
812 }
813
814 for (i = 0; i < led->num_leds; i++)
815 regulator_on |= led_array[i].flash_cfg->flash_on;
816
817 if (!on)
818 goto regulator_turn_off;
819
820 if (!regulator_on && !led->flash_cfg->flash_on) {
821 for (i = 0; i < led->num_leds; i++) {
822 if (led_array[i].flash_cfg->flash_reg_get) {
823 rc = regulator_enable(
824 led_array[i].flash_cfg->\
825 flash_boost_reg);
826 if (rc) {
827 dev_err(&led->spmi_dev->dev,
828 "Regulator enable failed(%d)\n",
829 rc);
830 return rc;
831 }
832 led->flash_cfg->flash_on = true;
833 }
834 break;
835 }
836 }
837
838 return 0;
839
840regulator_turn_off:
841 if (regulator_on && led->flash_cfg->flash_on) {
842 for (i = 0; i < led->num_leds; i++) {
843 if (led_array[i].flash_cfg->flash_reg_get) {
Chun Zhang9d5ff672013-08-01 18:18:26 -0700844 rc = qpnp_led_masked_write(led,
845 FLASH_ENABLE_CONTROL(led->base),
846 FLASH_ENABLE_MASK,
847 FLASH_DISABLE_ALL);
848 if (rc) {
849 dev_err(&led->spmi_dev->dev,
850 "Enable reg write failed(%d)\n",
851 rc);
852 }
853
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700854 rc = regulator_disable(led_array[i].flash_cfg->\
855 flash_boost_reg);
856 if (rc) {
857 dev_err(&led->spmi_dev->dev,
858 "Regulator disable failed(%d)\n",
859 rc);
860 return rc;
861 }
862 led->flash_cfg->flash_on = false;
863 }
864 break;
865 }
866 }
867
868 return 0;
869}
870
Chun Zhangdf2d3062013-06-25 20:14:46 -0700871static int qpnp_torch_regulator_operate(struct qpnp_led_data *led, bool on)
Amy Maloche864a6d52012-10-03 15:58:12 -0700872{
873 int rc;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700874
875 if (!on)
876 goto regulator_turn_off;
877
878 if (!led->flash_cfg->torch_on) {
879 rc = regulator_enable(led->flash_cfg->torch_boost_reg);
880 if (rc) {
881 dev_err(&led->spmi_dev->dev,
882 "Regulator enable failed(%d)\n", rc);
883 return rc;
884 }
885 led->flash_cfg->torch_on = true;
886 }
887 return 0;
888
889regulator_turn_off:
890 if (led->flash_cfg->torch_on) {
Chun Zhang9d5ff672013-08-01 18:18:26 -0700891 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
892 FLASH_ENABLE_MODULE_MASK, FLASH_DISABLE_ALL);
893 if (rc) {
894 dev_err(&led->spmi_dev->dev,
895 "Enable reg write failed(%d)\n", rc);
896 }
897
Chun Zhangdf2d3062013-06-25 20:14:46 -0700898 rc = regulator_disable(led->flash_cfg->torch_boost_reg);
899 if (rc) {
900 dev_err(&led->spmi_dev->dev,
901 "Regulator disable failed(%d)\n", rc);
902 return rc;
903 }
904 led->flash_cfg->torch_on = false;
905 }
906 return 0;
907}
908
909static int qpnp_flash_set(struct qpnp_led_data *led)
910{
911 int rc, error;
Amy Maloche864a6d52012-10-03 15:58:12 -0700912 int val = led->cdev.brightness;
913
Chun Zhang9c1fbaa2013-06-17 15:31:18 -0700914 if (led->flash_cfg->torch_enable)
915 led->flash_cfg->current_prgm =
916 (val * TORCH_MAX_LEVEL / led->max_current);
917 else
918 led->flash_cfg->current_prgm =
919 (val * FLASH_MAX_LEVEL / led->max_current);
Amy Maloche864a6d52012-10-03 15:58:12 -0700920
Amy Maloche864a6d52012-10-03 15:58:12 -0700921 /* Set led current */
922 if (val > 0) {
Amy Malochebc97c0d22013-03-24 22:06:16 -0700923 if (led->flash_cfg->torch_enable) {
Chun Zhang0d6ca072013-07-30 21:08:39 -0700924 if (led->flash_cfg->peripheral_subtype ==
925 FLASH_SUBTYPE_DUAL) {
926 rc = qpnp_torch_regulator_operate(led, true);
927 if (rc) {
928 dev_err(&led->spmi_dev->dev,
Chun Zhangdf2d3062013-06-25 20:14:46 -0700929 "Torch regulator operate failed(%d)\n",
930 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700931 return rc;
932 }
933 } else if (led->flash_cfg->peripheral_subtype ==
934 FLASH_SUBTYPE_SINGLE) {
935 rc = qpnp_flash_regulator_operate(led, true);
936 if (rc) {
937 dev_err(&led->spmi_dev->dev,
938 "Flash regulator operate failed(%d)\n",
939 rc);
940 goto error_flash_set;
941 }
Chun Zhangdf2d3062013-06-25 20:14:46 -0700942 }
943
Amy Malochebc97c0d22013-03-24 22:06:16 -0700944 rc = qpnp_led_masked_write(led,
945 FLASH_LED_UNLOCK_SECURE(led->base),
946 FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
947 if (rc) {
948 dev_err(&led->spmi_dev->dev,
949 "Secure reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700950 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700951 }
952
953 rc = qpnp_led_masked_write(led,
954 FLASH_LED_TORCH(led->base),
955 FLASH_TORCH_MASK, FLASH_LED_TORCH_ENABLE);
956 if (rc) {
957 dev_err(&led->spmi_dev->dev,
958 "Torch reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700959 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700960 }
961
Amy Malochebc97c0d22013-03-24 22:06:16 -0700962 rc = qpnp_led_masked_write(led,
963 led->flash_cfg->current_addr,
Chun Zhange8954cf2013-05-02 11:14:34 -0700964 FLASH_CURRENT_MASK,
965 led->flash_cfg->current_prgm);
Amy Malochebc97c0d22013-03-24 22:06:16 -0700966 if (rc) {
967 dev_err(&led->spmi_dev->dev,
968 "Current reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700969 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700970 }
971
972 rc = qpnp_led_masked_write(led,
973 led->flash_cfg->second_addr,
Chun Zhange8954cf2013-05-02 11:14:34 -0700974 FLASH_CURRENT_MASK,
975 led->flash_cfg->current_prgm);
Amy Malochebc97c0d22013-03-24 22:06:16 -0700976 if (rc) {
977 dev_err(&led->spmi_dev->dev,
978 "2nd Current reg write failed(%d)\n",
979 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700980 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700981 }
982
Chun Zhange8954cf2013-05-02 11:14:34 -0700983 qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
984 FLASH_CURRENT_MASK,
Chun Zhang9c1fbaa2013-06-17 15:31:18 -0700985 TORCH_MAX_LEVEL);
Chun Zhange8954cf2013-05-02 11:14:34 -0700986 if (rc) {
987 dev_err(&led->spmi_dev->dev,
988 "Max current reg write failed(%d)\n",
989 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700990 goto error_reg_write;
Chun Zhange8954cf2013-05-02 11:14:34 -0700991 }
992
Chun Zhang9d5ff672013-08-01 18:18:26 -0700993 rc = qpnp_led_masked_write(led,
994 FLASH_ENABLE_CONTROL(led->base),
995 FLASH_ENABLE_MASK,
996 led->flash_cfg->enable_module);
997 if (rc) {
998 dev_err(&led->spmi_dev->dev,
999 "Enable reg write failed(%d)\n",
1000 rc);
1001 goto error_reg_write;
1002 }
1003
1004 rc = qpnp_led_masked_write(led,
1005 FLASH_LED_STROBE_CTRL(led->base),
1006 led->flash_cfg->trigger_flash,
1007 led->flash_cfg->trigger_flash);
1008 if (rc) {
1009 dev_err(&led->spmi_dev->dev,
1010 "LED %d strobe reg write failed(%d)\n",
1011 led->id, rc);
1012 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001013 }
1014 } else {
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001015 rc = qpnp_flash_regulator_operate(led, true);
1016 if (rc) {
1017 dev_err(&led->spmi_dev->dev,
1018 "Flash regulator operate failed(%d)\n",
1019 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001020 goto error_flash_set;
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001021 }
1022
Chun Zhange8954cf2013-05-02 11:14:34 -07001023 /* Set flash safety timer */
Amy Malochebc97c0d22013-03-24 22:06:16 -07001024 rc = qpnp_led_masked_write(led,
Chun Zhange8954cf2013-05-02 11:14:34 -07001025 FLASH_SAFETY_TIMER(led->base),
1026 FLASH_SAFETY_TIMER_MASK,
1027 led->flash_cfg->duration);
1028 if (rc) {
1029 dev_err(&led->spmi_dev->dev,
1030 "Safety timer reg write failed(%d)\n",
1031 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001032 goto error_flash_set;
Chun Zhange8954cf2013-05-02 11:14:34 -07001033 }
1034
1035 /* Set max current */
1036 rc = qpnp_led_masked_write(led,
1037 FLASH_MAX_CURR(led->base), FLASH_CURRENT_MASK,
1038 FLASH_MAX_LEVEL);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001039 if (rc) {
1040 dev_err(&led->spmi_dev->dev,
1041 "Max current reg write failed(%d)\n",
1042 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001043 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001044 }
1045
Chun Zhange8954cf2013-05-02 11:14:34 -07001046 /* Set clamp current */
1047 rc = qpnp_led_masked_write(led,
1048 FLASH_CLAMP_CURR(led->base),
1049 FLASH_CURRENT_MASK,
1050 led->flash_cfg->clamp_curr);
1051 if (rc) {
1052 dev_err(&led->spmi_dev->dev,
1053 "Clamp current reg write failed(%d)\n",
1054 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001055 goto error_flash_set;
Chun Zhange8954cf2013-05-02 11:14:34 -07001056 }
1057
Amy Malochebc97c0d22013-03-24 22:06:16 -07001058 rc = qpnp_led_masked_write(led,
1059 led->flash_cfg->current_addr,
1060 FLASH_CURRENT_MASK,
1061 led->flash_cfg->current_prgm);
1062 if (rc) {
1063 dev_err(&led->spmi_dev->dev,
1064 "Current reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001065 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001066 }
1067
1068 rc = qpnp_led_masked_write(led,
Amy Malochebc97c0d22013-03-24 22:06:16 -07001069 FLASH_ENABLE_CONTROL(led->base),
Chun Zhang9d5ff672013-08-01 18:18:26 -07001070 led->flash_cfg->enable_module,
1071 led->flash_cfg->enable_module);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001072 if (rc) {
1073 dev_err(&led->spmi_dev->dev,
1074 "Enable reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001075 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001076 }
Amy Maloche38b9aae2013-02-07 12:15:34 -08001077
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07001078 /*
1079 * Add 1ms delay for bharger enter stable state
1080 */
1081 usleep(FLASH_RAMP_UP_DELAY_US);
1082
Chun Zhang9d5ff672013-08-01 18:18:26 -07001083 if (!led->flash_cfg->strobe_type) {
1084 rc = qpnp_led_masked_write(led,
1085 FLASH_LED_STROBE_CTRL(led->base),
1086 led->flash_cfg->trigger_flash,
1087 led->flash_cfg->trigger_flash);
1088 if (rc) {
1089 dev_err(&led->spmi_dev->dev,
Amy Malochebc97c0d22013-03-24 22:06:16 -07001090 "LED %d strobe reg write failed(%d)\n",
1091 led->id, rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001092 goto error_flash_set;
Chun Zhang9d5ff672013-08-01 18:18:26 -07001093 }
1094 } else {
1095 rc = qpnp_led_masked_write(led,
1096 FLASH_LED_STROBE_CTRL(led->base),
1097 (led->flash_cfg->trigger_flash |
1098 FLASH_STROBE_HW),
1099 (led->flash_cfg->trigger_flash |
1100 FLASH_STROBE_HW));
1101 if (rc) {
1102 dev_err(&led->spmi_dev->dev,
Amy Malochebc97c0d22013-03-24 22:06:16 -07001103 "LED %d strobe reg write failed(%d)\n",
1104 led->id, rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001105 goto error_flash_set;
Chun Zhang9d5ff672013-08-01 18:18:26 -07001106 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07001107 }
Amy Maloche38b9aae2013-02-07 12:15:34 -08001108 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001109 } else {
Chun Zhangdf2d3062013-06-25 20:14:46 -07001110 rc = qpnp_led_masked_write(led,
1111 FLASH_LED_STROBE_CTRL(led->base),
Chun Zhang9d5ff672013-08-01 18:18:26 -07001112 led->flash_cfg->trigger_flash,
Chun Zhangdf2d3062013-06-25 20:14:46 -07001113 FLASH_DISABLE_ALL);
1114 if (rc) {
1115 dev_err(&led->spmi_dev->dev,
1116 "LED %d flash write failed(%d)\n", led->id, rc);
1117 if (led->flash_cfg->torch_enable)
1118 goto error_torch_set;
1119 else
1120 goto error_flash_set;
1121 }
1122
Amy Malochebc97c0d22013-03-24 22:06:16 -07001123 if (led->flash_cfg->torch_enable) {
1124 rc = qpnp_led_masked_write(led,
1125 FLASH_LED_UNLOCK_SECURE(led->base),
1126 FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
1127 if (rc) {
1128 dev_err(&led->spmi_dev->dev,
1129 "Secure reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001130 goto error_torch_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001131 }
1132
1133 rc = qpnp_led_masked_write(led,
Chun Zhange8954cf2013-05-02 11:14:34 -07001134 FLASH_LED_TORCH(led->base),
1135 FLASH_TORCH_MASK,
1136 FLASH_LED_TORCH_DISABLE);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001137 if (rc) {
1138 dev_err(&led->spmi_dev->dev,
Chun Zhange8954cf2013-05-02 11:14:34 -07001139 "Torch reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001140 goto error_torch_set;
1141 }
1142
Chun Zhang0d6ca072013-07-30 21:08:39 -07001143 if (led->flash_cfg->peripheral_subtype ==
1144 FLASH_SUBTYPE_DUAL) {
1145 rc = qpnp_torch_regulator_operate(led, false);
1146 if (rc) {
1147 dev_err(&led->spmi_dev->dev,
1148 "Torch regulator operate failed(%d)\n",
1149 rc);
1150 return rc;
1151 }
1152 } else if (led->flash_cfg->peripheral_subtype ==
1153 FLASH_SUBTYPE_SINGLE) {
1154 rc = qpnp_flash_regulator_operate(led, false);
1155 if (rc) {
1156 dev_err(&led->spmi_dev->dev,
1157 "Flash regulator operate failed(%d)\n",
1158 rc);
1159 return rc;
1160 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07001161 }
Chun Zhangdf2d3062013-06-25 20:14:46 -07001162 } else {
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07001163 /*
1164 * Disable module after ramp down complete for stable
1165 * behavior
1166 */
1167 usleep(FLASH_RAMP_DN_DELAY_US);
1168
Chun Zhang9d5ff672013-08-01 18:18:26 -07001169 rc = qpnp_led_masked_write(led,
1170 FLASH_ENABLE_CONTROL(led->base),
1171 led->flash_cfg->enable_module &
1172 ~FLASH_ENABLE_MODULE_MASK,
1173 FLASH_DISABLE_ALL);
1174 if (rc) {
1175 dev_err(&led->spmi_dev->dev,
1176 "Enable reg write failed(%d)\n", rc);
1177 if (led->flash_cfg->torch_enable)
1178 goto error_torch_set;
1179 else
1180 goto error_flash_set;
1181 }
1182
Chun Zhangdf2d3062013-06-25 20:14:46 -07001183 rc = qpnp_flash_regulator_operate(led, false);
1184 if (rc) {
1185 dev_err(&led->spmi_dev->dev,
1186 "Flash regulator operate failed(%d)\n",
1187 rc);
1188 return rc;
1189 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001190 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001191 }
1192
Amy Malocheeea7b592012-10-03 15:59:36 -07001193 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
1194
1195 return 0;
Chun Zhangdf2d3062013-06-25 20:14:46 -07001196
Chun Zhang0d6ca072013-07-30 21:08:39 -07001197error_reg_write:
1198 if (led->flash_cfg->peripheral_subtype == FLASH_SUBTYPE_SINGLE)
1199 goto error_flash_set;
1200
Chun Zhangdf2d3062013-06-25 20:14:46 -07001201error_torch_set:
1202 error = qpnp_torch_regulator_operate(led, false);
1203 if (error) {
1204 dev_err(&led->spmi_dev->dev,
1205 "Torch regulator operate failed(%d)\n", rc);
1206 return error;
1207 }
1208 return rc;
1209
1210error_flash_set:
1211 error = qpnp_flash_regulator_operate(led, false);
1212 if (error) {
1213 dev_err(&led->spmi_dev->dev,
1214 "Flash regulator operate failed(%d)\n", rc);
1215 return error;
1216 }
1217 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07001218}
1219
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301220static int qpnp_kpdbl_set(struct qpnp_led_data *led)
1221{
1222 int duty_us;
1223 int rc;
1224
1225 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001226 if (!led->kpdbl_cfg->pwm_cfg->blinking)
1227 led->kpdbl_cfg->pwm_cfg->mode =
1228 led->kpdbl_cfg->pwm_cfg->default_mode;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301229 if (!num_kpbl_leds_on) {
1230 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
1231 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
1232 if (rc) {
1233 dev_err(&led->spmi_dev->dev,
1234 "Enable reg write failed(%d)\n", rc);
1235 return rc;
1236 }
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301237 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301238
1239 if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
1240 duty_us = (led->kpdbl_cfg->pwm_cfg->pwm_period_us *
1241 led->cdev.brightness) / KPDBL_MAX_LEVEL;
1242 rc = pwm_config(led->kpdbl_cfg->pwm_cfg->pwm_dev,
1243 duty_us,
1244 led->kpdbl_cfg->pwm_cfg->pwm_period_us);
1245 if (rc < 0) {
1246 dev_err(&led->spmi_dev->dev, "pwm config failed\n");
1247 return rc;
1248 }
1249 }
1250
Amy Malochea2726f02013-05-10 10:19:03 -07001251 rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301252 if (rc < 0) {
1253 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1254 return rc;
1255 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301256
1257 num_kpbl_leds_on++;
1258
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301259 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001260 led->kpdbl_cfg->pwm_cfg->mode =
1261 led->kpdbl_cfg->pwm_cfg->default_mode;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301262
1263 if (led->kpdbl_cfg->always_on) {
1264 rc = pwm_config(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0,
1265 led->kpdbl_cfg->pwm_cfg->pwm_period_us);
1266 if (rc < 0) {
1267 dev_err(&led->spmi_dev->dev,
1268 "pwm config failed\n");
1269 return rc;
1270 }
1271
1272 rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
1273 if (rc < 0) {
1274 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1275 return rc;
1276 }
1277 } else
1278 pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
1279
1280 if (num_kpbl_leds_on > 0)
1281 num_kpbl_leds_on--;
1282
1283 if (!num_kpbl_leds_on) {
1284 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
1285 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_DIS);
1286 if (rc) {
1287 dev_err(&led->spmi_dev->dev,
1288 "Failed to write led enable reg\n");
1289 return rc;
1290 }
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301291 }
1292 }
1293
Amy Maloche4cf9f322013-05-29 15:53:46 -07001294 led->kpdbl_cfg->pwm_cfg->blinking = false;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301295
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301296 qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
1297
1298 return 0;
1299}
1300
Amy Malocheeea7b592012-10-03 15:59:36 -07001301static int qpnp_rgb_set(struct qpnp_led_data *led)
1302{
1303 int duty_us;
1304 int rc;
1305
1306 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001307 if (!led->rgb_cfg->pwm_cfg->blinking)
1308 led->rgb_cfg->pwm_cfg->mode =
1309 led->rgb_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07001310 if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
1311 duty_us = (led->rgb_cfg->pwm_cfg->pwm_period_us *
Amy Malocheeea7b592012-10-03 15:59:36 -07001312 led->cdev.brightness) / LED_FULL;
Amy Malochea2726f02013-05-10 10:19:03 -07001313 rc = pwm_config(led->rgb_cfg->pwm_cfg->pwm_dev, duty_us,
1314 led->rgb_cfg->pwm_cfg->pwm_period_us);
Amy Malocheeea7b592012-10-03 15:59:36 -07001315 if (rc < 0) {
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301316 dev_err(&led->spmi_dev->dev,
1317 "pwm config failed\n");
Amy Malocheeea7b592012-10-03 15:59:36 -07001318 return rc;
1319 }
1320 }
1321 rc = qpnp_led_masked_write(led,
1322 RGB_LED_EN_CTL(led->base),
1323 led->rgb_cfg->enable, led->rgb_cfg->enable);
1324 if (rc) {
1325 dev_err(&led->spmi_dev->dev,
1326 "Failed to write led enable reg\n");
1327 return rc;
1328 }
Amy Malochea2726f02013-05-10 10:19:03 -07001329
1330 rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301331 if (rc < 0) {
1332 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1333 return rc;
1334 }
Amy Malocheeea7b592012-10-03 15:59:36 -07001335 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001336 led->rgb_cfg->pwm_cfg->mode =
1337 led->rgb_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07001338 pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
Amy Malocheeea7b592012-10-03 15:59:36 -07001339 rc = qpnp_led_masked_write(led,
1340 RGB_LED_EN_CTL(led->base),
1341 led->rgb_cfg->enable, RGB_LED_DISABLE);
1342 if (rc) {
1343 dev_err(&led->spmi_dev->dev,
1344 "Failed to write led enable reg\n");
1345 return rc;
1346 }
1347 }
1348
Amy Maloche4cf9f322013-05-29 15:53:46 -07001349 led->rgb_cfg->pwm_cfg->blinking = false;
Amy Malocheeea7b592012-10-03 15:59:36 -07001350 qpnp_dump_regs(led, rgb_pwm_debug_regs, ARRAY_SIZE(rgb_pwm_debug_regs));
1351
Amy Maloche864a6d52012-10-03 15:58:12 -07001352 return 0;
1353}
1354
Amy Malochef3d5a062012-08-16 19:14:11 -07001355static void qpnp_led_set(struct led_classdev *led_cdev,
1356 enum led_brightness value)
1357{
Amy Malochef3d5a062012-08-16 19:14:11 -07001358 struct qpnp_led_data *led;
1359
1360 led = container_of(led_cdev, struct qpnp_led_data, cdev);
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301361 if (value < LED_OFF) {
Amy Malochea5ca5552012-10-23 13:34:46 -07001362 dev_err(&led->spmi_dev->dev, "Invalid brightness value\n");
Amy Malochef3d5a062012-08-16 19:14:11 -07001363 return;
1364 }
1365
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301366 if (value > led->cdev.max_brightness)
1367 value = led->cdev.max_brightness;
1368
Chun Zhang815a1832013-06-20 13:47:13 -07001369 led->cdev.brightness = value;
1370 schedule_work(&led->work);
1371}
1372
1373static void __qpnp_led_work(struct qpnp_led_data *led,
1374 enum led_brightness value)
1375{
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001376 int rc;
Chun Zhangc3b505b2013-06-03 19:01:49 -07001377
Chun Zhang815a1832013-06-20 13:47:13 -07001378 mutex_lock(&led->lock);
Amy Malochef3d5a062012-08-16 19:14:11 -07001379
1380 switch (led->id) {
1381 case QPNP_ID_WLED:
1382 rc = qpnp_wled_set(led);
1383 if (rc < 0)
Amy Malochea5ca5552012-10-23 13:34:46 -07001384 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -07001385 "WLED set brightness failed (%d)\n", rc);
1386 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07001387 case QPNP_ID_FLASH1_LED0:
1388 case QPNP_ID_FLASH1_LED1:
1389 rc = qpnp_flash_set(led);
1390 if (rc < 0)
1391 dev_err(&led->spmi_dev->dev,
1392 "FLASH set brightness failed (%d)\n", rc);
1393 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07001394 case QPNP_ID_RGB_RED:
1395 case QPNP_ID_RGB_GREEN:
1396 case QPNP_ID_RGB_BLUE:
1397 rc = qpnp_rgb_set(led);
1398 if (rc < 0)
1399 dev_err(&led->spmi_dev->dev,
1400 "RGB set brightness failed (%d)\n", rc);
1401 break;
Amy Malochef3813742013-04-11 19:33:47 -07001402 case QPNP_ID_LED_MPP:
1403 rc = qpnp_mpp_set(led);
1404 if (rc < 0)
1405 dev_err(&led->spmi_dev->dev,
1406 "MPP set brightness failed (%d)\n", rc);
Amy Maloche14c9eb32013-05-06 13:37:58 -07001407 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301408 case QPNP_ID_KPDBL:
1409 rc = qpnp_kpdbl_set(led);
1410 if (rc < 0)
1411 dev_err(&led->spmi_dev->dev,
1412 "KPDBL set brightness failed (%d)\n", rc);
Amy Malochef3813742013-04-11 19:33:47 -07001413 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07001414 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07001415 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -07001416 break;
1417 }
Chun Zhang815a1832013-06-20 13:47:13 -07001418 mutex_unlock(&led->lock);
Chun Zhangc3b505b2013-06-03 19:01:49 -07001419
Amy Malochef3d5a062012-08-16 19:14:11 -07001420}
1421
Chun Zhang815a1832013-06-20 13:47:13 -07001422static void qpnp_led_work(struct work_struct *work)
1423{
1424 struct qpnp_led_data *led = container_of(work,
1425 struct qpnp_led_data, work);
1426
1427 __qpnp_led_work(led, led->cdev.brightness);
1428
1429 return;
1430}
1431
Amy Malochef3d5a062012-08-16 19:14:11 -07001432static int __devinit qpnp_led_set_max_brightness(struct qpnp_led_data *led)
1433{
1434 switch (led->id) {
1435 case QPNP_ID_WLED:
1436 led->cdev.max_brightness = WLED_MAX_LEVEL;
1437 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07001438 case QPNP_ID_FLASH1_LED0:
1439 case QPNP_ID_FLASH1_LED1:
1440 led->cdev.max_brightness = led->max_current;
1441 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07001442 case QPNP_ID_RGB_RED:
1443 case QPNP_ID_RGB_GREEN:
1444 case QPNP_ID_RGB_BLUE:
1445 led->cdev.max_brightness = RGB_MAX_LEVEL;
1446 break;
Amy Malochef3813742013-04-11 19:33:47 -07001447 case QPNP_ID_LED_MPP:
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05301448 if (led->mpp_cfg->pwm_mode == MANUAL_MODE)
1449 led->cdev.max_brightness = led->max_current;
1450 else
1451 led->cdev.max_brightness = MPP_MAX_LEVEL;
Amy Malochef3813742013-04-11 19:33:47 -07001452 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301453 case QPNP_ID_KPDBL:
1454 led->cdev.max_brightness = KPDBL_MAX_LEVEL;
1455 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07001456 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07001457 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -07001458 return -EINVAL;
1459 }
1460
1461 return 0;
1462}
1463
1464static enum led_brightness qpnp_led_get(struct led_classdev *led_cdev)
1465{
1466 struct qpnp_led_data *led;
1467
1468 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1469
1470 return led->cdev.brightness;
1471}
1472
Asaf Penso55ac8472013-01-21 21:17:37 +02001473static void qpnp_led_turn_off_delayed(struct work_struct *work)
1474{
1475 struct delayed_work *dwork = to_delayed_work(work);
1476 struct qpnp_led_data *led
1477 = container_of(dwork, struct qpnp_led_data, dwork);
1478
1479 led->cdev.brightness = LED_OFF;
1480 qpnp_led_set(&led->cdev, led->cdev.brightness);
1481}
1482
1483static void qpnp_led_turn_off(struct qpnp_led_data *led)
1484{
1485 INIT_DELAYED_WORK(&led->dwork, qpnp_led_turn_off_delayed);
1486 schedule_delayed_work(&led->dwork,
1487 msecs_to_jiffies(led->turn_off_delay_ms));
1488}
1489
Amy Malochef3d5a062012-08-16 19:14:11 -07001490static int __devinit qpnp_wled_init(struct qpnp_led_data *led)
1491{
1492 int rc, i;
1493 u8 num_wled_strings;
1494
1495 num_wled_strings = led->wled_cfg->num_strings;
1496
1497 /* verify ranges */
1498 if (led->wled_cfg->ovp_val > WLED_OVP_37V) {
1499 dev_err(&led->spmi_dev->dev, "Invalid ovp value\n");
1500 return -EINVAL;
1501 }
1502
1503 if (led->wled_cfg->boost_curr_lim > WLED_CURR_LIMIT_1680mA) {
1504 dev_err(&led->spmi_dev->dev, "Invalid boost current limit\n");
1505 return -EINVAL;
1506 }
1507
1508 if (led->wled_cfg->cp_select > WLED_CP_SELECT_MAX) {
1509 dev_err(&led->spmi_dev->dev, "Invalid pole capacitance\n");
1510 return -EINVAL;
1511 }
1512
1513 if ((led->max_current > WLED_MAX_CURR)) {
1514 dev_err(&led->spmi_dev->dev, "Invalid max current\n");
1515 return -EINVAL;
1516 }
1517
1518 if ((led->wled_cfg->ctrl_delay_us % WLED_CTL_DLY_STEP) ||
1519 (led->wled_cfg->ctrl_delay_us > WLED_CTL_DLY_MAX)) {
1520 dev_err(&led->spmi_dev->dev, "Invalid control delay\n");
1521 return -EINVAL;
1522 }
1523
1524 /* program over voltage protection threshold */
1525 rc = qpnp_led_masked_write(led, WLED_OVP_CFG_REG(led->base),
1526 WLED_OVP_VAL_MASK,
1527 (led->wled_cfg->ovp_val << WLED_OVP_VAL_BIT_SHFT));
1528 if (rc) {
1529 dev_err(&led->spmi_dev->dev,
1530 "WLED OVP reg write failed(%d)\n", rc);
1531 return rc;
1532 }
1533
1534 /* program current boost limit */
1535 rc = qpnp_led_masked_write(led, WLED_BOOST_LIMIT_REG(led->base),
1536 WLED_BOOST_LIMIT_MASK, led->wled_cfg->boost_curr_lim);
1537 if (rc) {
1538 dev_err(&led->spmi_dev->dev,
1539 "WLED boost limit reg write failed(%d)\n", rc);
1540 return rc;
1541 }
1542
1543 /* program output feedback */
1544 rc = qpnp_led_masked_write(led, WLED_FDBCK_CTRL_REG(led->base),
1545 WLED_OP_FDBCK_MASK,
1546 (led->wled_cfg->op_fdbck << WLED_OP_FDBCK_BIT_SHFT));
1547 if (rc) {
1548 dev_err(&led->spmi_dev->dev,
1549 "WLED fdbck ctrl reg write failed(%d)\n", rc);
1550 return rc;
1551 }
1552
1553 /* program switch frequency */
Amy Maloche9eccb4c2013-07-12 14:31:56 -07001554 rc = qpnp_led_masked_write(led,
1555 WLED_SWITCHING_FREQ_REG(led->base),
Amy Malochef3d5a062012-08-16 19:14:11 -07001556 WLED_SWITCH_FREQ_MASK, led->wled_cfg->switch_freq);
1557 if (rc) {
1558 dev_err(&led->spmi_dev->dev,
Amy Maloche9eccb4c2013-07-12 14:31:56 -07001559 "WLED switch freq reg write failed(%d)\n", rc);
Amy Malochef3d5a062012-08-16 19:14:11 -07001560 return rc;
1561 }
1562
1563 /* program current sink */
1564 if (led->wled_cfg->cs_out_en) {
1565 rc = qpnp_led_masked_write(led, WLED_CURR_SINK_REG(led->base),
1566 WLED_CURR_SINK_MASK,
Amy Maloche832d90a2013-01-07 10:15:29 -08001567 (((1 << led->wled_cfg->num_strings) - 1)
1568 << WLED_CURR_SINK_SHFT));
Amy Malochef3d5a062012-08-16 19:14:11 -07001569 if (rc) {
1570 dev_err(&led->spmi_dev->dev,
1571 "WLED curr sink reg write failed(%d)\n", rc);
1572 return rc;
1573 }
1574 }
1575
1576 /* program high pole capacitance */
1577 rc = qpnp_led_masked_write(led, WLED_HIGH_POLE_CAP_REG(led->base),
1578 WLED_CP_SELECT_MASK, led->wled_cfg->cp_select);
1579 if (rc) {
1580 dev_err(&led->spmi_dev->dev,
1581 "WLED pole cap reg write failed(%d)\n", rc);
1582 return rc;
1583 }
1584
1585 /* program modulator, current mod src and cabc */
1586 for (i = 0; i < num_wled_strings; i++) {
1587 rc = qpnp_led_masked_write(led, WLED_MOD_EN_REG(led->base, i),
1588 WLED_NO_MASK, WLED_EN_MASK);
1589 if (rc) {
1590 dev_err(&led->spmi_dev->dev,
1591 "WLED mod enable reg write failed(%d)\n", rc);
1592 return rc;
1593 }
1594
1595 if (led->wled_cfg->dig_mod_gen_en) {
1596 rc = qpnp_led_masked_write(led,
Amy Maloched44516e2013-02-14 17:36:34 -08001597 WLED_MOD_SRC_SEL_REG(led->base, i),
Amy Malochef3d5a062012-08-16 19:14:11 -07001598 WLED_NO_MASK, WLED_USE_EXT_GEN_MOD_SRC);
1599 if (rc) {
1600 dev_err(&led->spmi_dev->dev,
1601 "WLED dig mod en reg write failed(%d)\n", rc);
1602 }
1603 }
1604
1605 rc = qpnp_led_masked_write(led,
1606 WLED_FULL_SCALE_REG(led->base, i), WLED_MAX_CURR_MASK,
1607 led->max_current);
1608 if (rc) {
1609 dev_err(&led->spmi_dev->dev,
1610 "WLED max current reg write failed(%d)\n", rc);
1611 return rc;
1612 }
1613
1614 }
1615
1616 /* dump wled registers */
Amy Malochea5ca5552012-10-23 13:34:46 -07001617 qpnp_dump_regs(led, wled_debug_regs, ARRAY_SIZE(wled_debug_regs));
Amy Malochef3d5a062012-08-16 19:14:11 -07001618
1619 return 0;
1620}
1621
Amy Malochebc97c0d22013-03-24 22:06:16 -07001622static ssize_t led_mode_store(struct device *dev,
1623 struct device_attribute *attr,
1624 const char *buf, size_t count)
1625{
1626 struct qpnp_led_data *led;
1627 unsigned long state;
1628 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1629 ssize_t ret = -EINVAL;
1630
1631 ret = kstrtoul(buf, 10, &state);
1632 if (ret)
1633 return ret;
1634
1635 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1636
1637 /* '1' to enable torch mode; '0' to switch to flash mode */
1638 if (state == 1)
1639 led->flash_cfg->torch_enable = true;
1640 else
1641 led->flash_cfg->torch_enable = false;
1642
1643 return count;
1644}
1645
1646static ssize_t led_strobe_type_store(struct device *dev,
1647 struct device_attribute *attr,
1648 const char *buf, size_t count)
1649{
1650 struct qpnp_led_data *led;
1651 unsigned long state;
1652 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1653 ssize_t ret = -EINVAL;
1654
1655 ret = kstrtoul(buf, 10, &state);
1656 if (ret)
1657 return ret;
1658
1659 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1660
1661 /* '0' for sw strobe; '1' for hw strobe */
1662 if (state == 1)
1663 led->flash_cfg->strobe_type = 1;
1664 else
1665 led->flash_cfg->strobe_type = 0;
1666
1667 return count;
1668}
1669
Amy Malochea5c4ed82013-06-05 11:05:28 -07001670static int qpnp_pwm_init(struct pwm_config_data *pwm_cfg,
1671 struct spmi_device *spmi_dev,
1672 const char *name)
1673{
1674 int rc, start_idx, idx_len;
1675
1676 if (pwm_cfg->pwm_channel != -1) {
1677 pwm_cfg->pwm_dev =
1678 pwm_request(pwm_cfg->pwm_channel, name);
1679
1680 if (IS_ERR_OR_NULL(pwm_cfg->pwm_dev)) {
1681 dev_err(&spmi_dev->dev,
1682 "could not acquire PWM Channel %d, " \
1683 "error %ld\n",
1684 pwm_cfg->pwm_channel,
1685 PTR_ERR(pwm_cfg->pwm_dev));
1686 pwm_cfg->pwm_dev = NULL;
1687 return -ENODEV;
1688 }
1689
1690 if (pwm_cfg->mode == LPG_MODE) {
1691 start_idx =
1692 pwm_cfg->duty_cycles->start_idx;
1693 idx_len =
1694 pwm_cfg->duty_cycles->num_duty_pcts;
1695
1696 if (idx_len >= PWM_LUT_MAX_SIZE &&
1697 start_idx) {
1698 dev_err(&spmi_dev->dev,
1699 "Wrong LUT size or index\n");
1700 return -EINVAL;
1701 }
1702 if ((start_idx + idx_len) >
1703 PWM_LUT_MAX_SIZE) {
1704 dev_err(&spmi_dev->dev,
1705 "Exceed LUT limit\n");
1706 return -EINVAL;
1707 }
1708 rc = pwm_lut_config(pwm_cfg->pwm_dev,
1709 PM_PWM_PERIOD_MIN, /* ignored by hardware */
1710 pwm_cfg->duty_cycles->duty_pcts,
1711 pwm_cfg->lut_params);
1712 if (rc < 0) {
1713 dev_err(&spmi_dev->dev, "Failed to " \
1714 "configure pwm LUT\n");
1715 return rc;
1716 }
1717 }
1718 } else {
1719 dev_err(&spmi_dev->dev,
1720 "Invalid PWM channel\n");
1721 return -EINVAL;
1722 }
1723
1724 return 0;
1725}
1726
Amy Maloche4cf9f322013-05-29 15:53:46 -07001727static ssize_t pwm_us_store(struct device *dev,
1728 struct device_attribute *attr,
1729 const char *buf, size_t count)
1730{
1731 struct qpnp_led_data *led;
1732 u32 pwm_us;
1733 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1734 ssize_t ret;
1735 u32 previous_pwm_us;
1736 struct pwm_config_data *pwm_cfg;
1737
1738 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1739
1740 ret = kstrtou32(buf, 10, &pwm_us);
1741 if (ret)
1742 return ret;
1743
1744 switch (led->id) {
1745 case QPNP_ID_LED_MPP:
1746 pwm_cfg = led->mpp_cfg->pwm_cfg;
1747 break;
1748 case QPNP_ID_RGB_RED:
1749 case QPNP_ID_RGB_GREEN:
1750 case QPNP_ID_RGB_BLUE:
1751 pwm_cfg = led->rgb_cfg->pwm_cfg;
1752 break;
1753 default:
1754 dev_err(&led->spmi_dev->dev,
1755 "Invalid LED id type for pwm_us\n");
1756 return -EINVAL;
1757 }
1758
1759 if (pwm_cfg->mode == LPG_MODE)
1760 pwm_cfg->blinking = true;
1761
1762 previous_pwm_us = pwm_cfg->pwm_period_us;
1763
1764 pwm_cfg->pwm_period_us = pwm_us;
1765 pwm_free(pwm_cfg->pwm_dev);
1766 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1767 if (ret) {
1768 pwm_cfg->pwm_period_us = previous_pwm_us;
1769 pwm_free(pwm_cfg->pwm_dev);
1770 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1771 qpnp_led_set(&led->cdev, led->cdev.brightness);
1772 dev_err(&led->spmi_dev->dev,
1773 "Failed to initialize pwm with new pwm_us value\n");
1774 return ret;
1775 }
1776 qpnp_led_set(&led->cdev, led->cdev.brightness);
1777 return count;
1778}
1779
1780static ssize_t pause_lo_store(struct device *dev,
1781 struct device_attribute *attr,
1782 const char *buf, size_t count)
1783{
1784 struct qpnp_led_data *led;
1785 u32 pause_lo;
1786 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1787 ssize_t ret;
1788 u32 previous_pause_lo;
1789 struct pwm_config_data *pwm_cfg;
1790
1791 ret = kstrtou32(buf, 10, &pause_lo);
1792 if (ret)
1793 return ret;
1794 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1795
1796 switch (led->id) {
1797 case QPNP_ID_LED_MPP:
1798 pwm_cfg = led->mpp_cfg->pwm_cfg;
1799 break;
1800 case QPNP_ID_RGB_RED:
1801 case QPNP_ID_RGB_GREEN:
1802 case QPNP_ID_RGB_BLUE:
1803 pwm_cfg = led->rgb_cfg->pwm_cfg;
1804 break;
1805 default:
1806 dev_err(&led->spmi_dev->dev,
1807 "Invalid LED id type for pause lo\n");
1808 return -EINVAL;
1809 }
1810
1811 if (pwm_cfg->mode == LPG_MODE)
1812 pwm_cfg->blinking = true;
1813
1814 previous_pause_lo = pwm_cfg->lut_params.lut_pause_lo;
1815
1816 pwm_free(pwm_cfg->pwm_dev);
1817 pwm_cfg->lut_params.lut_pause_lo = pause_lo;
1818 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1819 if (ret) {
1820 pwm_cfg->lut_params.lut_pause_lo = previous_pause_lo;
1821 pwm_free(pwm_cfg->pwm_dev);
1822 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1823 qpnp_led_set(&led->cdev, led->cdev.brightness);
1824 dev_err(&led->spmi_dev->dev,
1825 "Failed to initialize pwm with new pause lo value\n");
1826 return ret;
1827 }
1828 qpnp_led_set(&led->cdev, led->cdev.brightness);
1829 return count;
1830}
1831
1832static ssize_t pause_hi_store(struct device *dev,
1833 struct device_attribute *attr,
1834 const char *buf, size_t count)
1835{
1836 struct qpnp_led_data *led;
1837 u32 pause_hi;
1838 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1839 ssize_t ret;
1840 u32 previous_pause_hi;
1841 struct pwm_config_data *pwm_cfg;
1842
1843 ret = kstrtou32(buf, 10, &pause_hi);
1844 if (ret)
1845 return ret;
1846 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1847
1848 switch (led->id) {
1849 case QPNP_ID_LED_MPP:
1850 pwm_cfg = led->mpp_cfg->pwm_cfg;
1851 break;
1852 case QPNP_ID_RGB_RED:
1853 case QPNP_ID_RGB_GREEN:
1854 case QPNP_ID_RGB_BLUE:
1855 pwm_cfg = led->rgb_cfg->pwm_cfg;
1856 break;
1857 default:
1858 dev_err(&led->spmi_dev->dev,
1859 "Invalid LED id type for pause hi\n");
1860 return -EINVAL;
1861 }
1862
1863 if (pwm_cfg->mode == LPG_MODE)
1864 pwm_cfg->blinking = true;
1865
1866 previous_pause_hi = pwm_cfg->lut_params.lut_pause_hi;
1867
1868 pwm_free(pwm_cfg->pwm_dev);
1869 pwm_cfg->lut_params.lut_pause_hi = pause_hi;
1870 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1871 if (ret) {
1872 pwm_cfg->lut_params.lut_pause_hi = previous_pause_hi;
1873 pwm_free(pwm_cfg->pwm_dev);
1874 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1875 qpnp_led_set(&led->cdev, led->cdev.brightness);
1876 dev_err(&led->spmi_dev->dev,
1877 "Failed to initialize pwm with new pause hi value\n");
1878 return ret;
1879 }
1880 qpnp_led_set(&led->cdev, led->cdev.brightness);
1881 return count;
1882}
1883
1884static ssize_t start_idx_store(struct device *dev,
1885 struct device_attribute *attr,
1886 const char *buf, size_t count)
1887{
1888 struct qpnp_led_data *led;
1889 u32 start_idx;
1890 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1891 ssize_t ret;
1892 u32 previous_start_idx;
1893 struct pwm_config_data *pwm_cfg;
1894
1895 ret = kstrtou32(buf, 10, &start_idx);
1896 if (ret)
1897 return ret;
1898 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1899
1900 switch (led->id) {
1901 case QPNP_ID_LED_MPP:
1902 pwm_cfg = led->mpp_cfg->pwm_cfg;
1903 break;
1904 case QPNP_ID_RGB_RED:
1905 case QPNP_ID_RGB_GREEN:
1906 case QPNP_ID_RGB_BLUE:
1907 pwm_cfg = led->rgb_cfg->pwm_cfg;
1908 break;
1909 default:
1910 dev_err(&led->spmi_dev->dev,
1911 "Invalid LED id type for start idx\n");
1912 return -EINVAL;
1913 }
1914
1915 if (pwm_cfg->mode == LPG_MODE)
1916 pwm_cfg->blinking = true;
1917
1918 previous_start_idx = pwm_cfg->duty_cycles->start_idx;
1919 pwm_cfg->duty_cycles->start_idx = start_idx;
1920 pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
1921 pwm_free(pwm_cfg->pwm_dev);
1922 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1923 if (ret) {
1924 pwm_cfg->duty_cycles->start_idx = previous_start_idx;
1925 pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
1926 pwm_free(pwm_cfg->pwm_dev);
1927 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1928 qpnp_led_set(&led->cdev, led->cdev.brightness);
1929 dev_err(&led->spmi_dev->dev,
1930 "Failed to initialize pwm with new start idx value\n");
1931 return ret;
1932 }
1933 qpnp_led_set(&led->cdev, led->cdev.brightness);
1934 return count;
1935}
1936
1937static ssize_t ramp_step_ms_store(struct device *dev,
1938 struct device_attribute *attr,
1939 const char *buf, size_t count)
1940{
1941 struct qpnp_led_data *led;
1942 u32 ramp_step_ms;
1943 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1944 ssize_t ret;
1945 u32 previous_ramp_step_ms;
1946 struct pwm_config_data *pwm_cfg;
1947
1948 ret = kstrtou32(buf, 10, &ramp_step_ms);
1949 if (ret)
1950 return ret;
1951 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1952
1953 switch (led->id) {
1954 case QPNP_ID_LED_MPP:
1955 pwm_cfg = led->mpp_cfg->pwm_cfg;
1956 break;
1957 case QPNP_ID_RGB_RED:
1958 case QPNP_ID_RGB_GREEN:
1959 case QPNP_ID_RGB_BLUE:
1960 pwm_cfg = led->rgb_cfg->pwm_cfg;
1961 break;
1962 default:
1963 dev_err(&led->spmi_dev->dev,
1964 "Invalid LED id type for ramp step\n");
1965 return -EINVAL;
1966 }
1967
1968 if (pwm_cfg->mode == LPG_MODE)
1969 pwm_cfg->blinking = true;
1970
1971 previous_ramp_step_ms = pwm_cfg->lut_params.ramp_step_ms;
1972
1973 pwm_free(pwm_cfg->pwm_dev);
1974 pwm_cfg->lut_params.ramp_step_ms = ramp_step_ms;
1975 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1976 if (ret) {
1977 pwm_cfg->lut_params.ramp_step_ms = previous_ramp_step_ms;
1978 pwm_free(pwm_cfg->pwm_dev);
1979 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1980 qpnp_led_set(&led->cdev, led->cdev.brightness);
1981 dev_err(&led->spmi_dev->dev,
1982 "Failed to initialize pwm with new ramp step value\n");
1983 return ret;
1984 }
1985 qpnp_led_set(&led->cdev, led->cdev.brightness);
1986 return count;
1987}
1988
1989static ssize_t lut_flags_store(struct device *dev,
1990 struct device_attribute *attr,
1991 const char *buf, size_t count)
1992{
1993 struct qpnp_led_data *led;
1994 u32 lut_flags;
1995 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1996 ssize_t ret;
1997 u32 previous_lut_flags;
1998 struct pwm_config_data *pwm_cfg;
1999
2000 ret = kstrtou32(buf, 10, &lut_flags);
2001 if (ret)
2002 return ret;
2003 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2004
2005 switch (led->id) {
2006 case QPNP_ID_LED_MPP:
2007 pwm_cfg = led->mpp_cfg->pwm_cfg;
2008 break;
2009 case QPNP_ID_RGB_RED:
2010 case QPNP_ID_RGB_GREEN:
2011 case QPNP_ID_RGB_BLUE:
2012 pwm_cfg = led->rgb_cfg->pwm_cfg;
2013 break;
2014 default:
2015 dev_err(&led->spmi_dev->dev,
2016 "Invalid LED id type for lut flags\n");
2017 return -EINVAL;
2018 }
2019
2020 if (pwm_cfg->mode == LPG_MODE)
2021 pwm_cfg->blinking = true;
2022
2023 previous_lut_flags = pwm_cfg->lut_params.flags;
2024
2025 pwm_free(pwm_cfg->pwm_dev);
2026 pwm_cfg->lut_params.flags = lut_flags;
2027 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2028 if (ret) {
2029 pwm_cfg->lut_params.flags = previous_lut_flags;
2030 pwm_free(pwm_cfg->pwm_dev);
2031 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2032 qpnp_led_set(&led->cdev, led->cdev.brightness);
2033 dev_err(&led->spmi_dev->dev,
2034 "Failed to initialize pwm with new lut flags value\n");
2035 return ret;
2036 }
2037 qpnp_led_set(&led->cdev, led->cdev.brightness);
2038 return count;
2039}
2040
2041static ssize_t duty_pcts_store(struct device *dev,
2042 struct device_attribute *attr,
2043 const char *buf, size_t count)
2044{
2045 struct qpnp_led_data *led;
2046 int num_duty_pcts = 0;
2047 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2048 char *buffer;
2049 ssize_t ret;
2050 int i = 0;
2051 int max_duty_pcts;
2052 struct pwm_config_data *pwm_cfg;
2053 u32 previous_num_duty_pcts;
2054 int value;
2055 int *previous_duty_pcts;
2056
2057 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2058
2059 switch (led->id) {
2060 case QPNP_ID_LED_MPP:
2061 pwm_cfg = led->mpp_cfg->pwm_cfg;
2062 max_duty_pcts = PWM_LUT_MAX_SIZE;
2063 break;
2064 case QPNP_ID_RGB_RED:
2065 case QPNP_ID_RGB_GREEN:
2066 case QPNP_ID_RGB_BLUE:
2067 pwm_cfg = led->rgb_cfg->pwm_cfg;
2068 max_duty_pcts = PWM_LUT_MAX_SIZE;
2069 break;
2070 default:
2071 dev_err(&led->spmi_dev->dev,
2072 "Invalid LED id type for duty pcts\n");
2073 return -EINVAL;
2074 }
2075
2076 if (pwm_cfg->mode == LPG_MODE)
2077 pwm_cfg->blinking = true;
2078
2079 buffer = (char *)buf;
2080
2081 for (i = 0; i < max_duty_pcts; i++) {
2082 if (buffer == NULL)
2083 break;
2084 ret = sscanf((const char *)buffer, "%u,%s", &value, buffer);
2085 pwm_cfg->old_duty_pcts[i] = value;
2086 num_duty_pcts++;
2087 if (ret <= 1)
2088 break;
2089 }
2090
2091 if (num_duty_pcts >= max_duty_pcts) {
2092 dev_err(&led->spmi_dev->dev,
2093 "Number of duty pcts given exceeds max (%d)\n",
2094 max_duty_pcts);
2095 return -EINVAL;
2096 }
2097
2098 previous_num_duty_pcts = pwm_cfg->duty_cycles->num_duty_pcts;
2099 previous_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
2100
2101 pwm_cfg->duty_cycles->num_duty_pcts = num_duty_pcts;
2102 pwm_cfg->duty_cycles->duty_pcts = pwm_cfg->old_duty_pcts;
2103 pwm_cfg->old_duty_pcts = previous_duty_pcts;
2104 pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
2105
2106 pwm_free(pwm_cfg->pwm_dev);
2107 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2108 if (ret)
2109 goto restore;
2110
2111 qpnp_led_set(&led->cdev, led->cdev.brightness);
2112 return count;
2113
2114restore:
2115 dev_err(&led->spmi_dev->dev,
2116 "Failed to initialize pwm with new duty pcts value\n");
2117 pwm_cfg->duty_cycles->num_duty_pcts = previous_num_duty_pcts;
2118 pwm_cfg->old_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
2119 pwm_cfg->duty_cycles->duty_pcts = previous_duty_pcts;
2120 pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
2121 pwm_free(pwm_cfg->pwm_dev);
2122 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2123 qpnp_led_set(&led->cdev, led->cdev.brightness);
2124 return ret;
2125}
2126
Amy Malochea5c4ed82013-06-05 11:05:28 -07002127static void led_blink(struct qpnp_led_data *led,
2128 struct pwm_config_data *pwm_cfg)
2129{
Amy Malochea5c4ed82013-06-05 11:05:28 -07002130 if (pwm_cfg->use_blink) {
2131 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07002132 pwm_cfg->blinking = true;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002133 if (led->id == QPNP_ID_LED_MPP)
2134 led->mpp_cfg->pwm_mode = LPG_MODE;
2135 pwm_cfg->mode = LPG_MODE;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002136 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07002137 pwm_cfg->blinking = false;
2138 pwm_cfg->mode = pwm_cfg->default_mode;
2139 if (led->id == QPNP_ID_LED_MPP)
2140 led->mpp_cfg->pwm_mode = pwm_cfg->default_mode;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002141 }
Amy Maloche4cf9f322013-05-29 15:53:46 -07002142 pwm_free(pwm_cfg->pwm_dev);
2143 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2144 qpnp_led_set(&led->cdev, led->cdev.brightness);
Amy Malochea5c4ed82013-06-05 11:05:28 -07002145 }
2146}
2147
2148static ssize_t blink_store(struct device *dev,
2149 struct device_attribute *attr,
2150 const char *buf, size_t count)
2151{
2152 struct qpnp_led_data *led;
2153 unsigned long blinking;
2154 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2155 ssize_t ret = -EINVAL;
2156
2157 ret = kstrtoul(buf, 10, &blinking);
2158 if (ret)
2159 return ret;
2160 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2161 led->cdev.brightness = blinking ? led->cdev.max_brightness : 0;
2162
2163 switch (led->id) {
2164 case QPNP_ID_LED_MPP:
2165 led_blink(led, led->mpp_cfg->pwm_cfg);
2166 break;
2167 case QPNP_ID_RGB_RED:
2168 case QPNP_ID_RGB_GREEN:
2169 case QPNP_ID_RGB_BLUE:
2170 led_blink(led, led->rgb_cfg->pwm_cfg);
2171 break;
2172 default:
2173 dev_err(&led->spmi_dev->dev, "Invalid LED id type for blink\n");
2174 return -EINVAL;
2175 }
2176 return count;
2177}
2178
Amy Malochebc97c0d22013-03-24 22:06:16 -07002179static DEVICE_ATTR(led_mode, 0664, NULL, led_mode_store);
2180static DEVICE_ATTR(strobe, 0664, NULL, led_strobe_type_store);
Amy Maloche4cf9f322013-05-29 15:53:46 -07002181static DEVICE_ATTR(pwm_us, 0664, NULL, pwm_us_store);
2182static DEVICE_ATTR(pause_lo, 0664, NULL, pause_lo_store);
2183static DEVICE_ATTR(pause_hi, 0664, NULL, pause_hi_store);
2184static DEVICE_ATTR(start_idx, 0664, NULL, start_idx_store);
2185static DEVICE_ATTR(ramp_step_ms, 0664, NULL, ramp_step_ms_store);
2186static DEVICE_ATTR(lut_flags, 0664, NULL, lut_flags_store);
2187static DEVICE_ATTR(duty_pcts, 0664, NULL, duty_pcts_store);
Amy Malochea5c4ed82013-06-05 11:05:28 -07002188static DEVICE_ATTR(blink, 0664, NULL, blink_store);
Amy Malochebc97c0d22013-03-24 22:06:16 -07002189
2190static struct attribute *led_attrs[] = {
2191 &dev_attr_led_mode.attr,
2192 &dev_attr_strobe.attr,
Amy Maloche12ad6f32013-04-02 14:39:24 -07002193 NULL
Amy Malochebc97c0d22013-03-24 22:06:16 -07002194};
2195
2196static const struct attribute_group led_attr_group = {
2197 .attrs = led_attrs,
2198};
2199
Amy Maloche4cf9f322013-05-29 15:53:46 -07002200static struct attribute *pwm_attrs[] = {
2201 &dev_attr_pwm_us.attr,
2202 NULL
2203};
2204
2205static struct attribute *lpg_attrs[] = {
2206 &dev_attr_pause_lo.attr,
2207 &dev_attr_pause_hi.attr,
2208 &dev_attr_start_idx.attr,
2209 &dev_attr_ramp_step_ms.attr,
2210 &dev_attr_lut_flags.attr,
2211 &dev_attr_duty_pcts.attr,
2212 NULL
2213};
2214
Amy Malochea5c4ed82013-06-05 11:05:28 -07002215static struct attribute *blink_attrs[] = {
2216 &dev_attr_blink.attr,
2217 NULL
2218};
2219
Amy Maloche4cf9f322013-05-29 15:53:46 -07002220static const struct attribute_group pwm_attr_group = {
2221 .attrs = pwm_attrs,
2222};
2223
2224static const struct attribute_group lpg_attr_group = {
2225 .attrs = lpg_attrs,
2226};
2227
Amy Malochea5c4ed82013-06-05 11:05:28 -07002228static const struct attribute_group blink_attr_group = {
2229 .attrs = blink_attrs,
2230};
2231
Amy Maloche864a6d52012-10-03 15:58:12 -07002232static int __devinit qpnp_flash_init(struct qpnp_led_data *led)
2233{
2234 int rc;
2235
Chun Zhangc3b505b2013-06-03 19:01:49 -07002236 led->flash_cfg->flash_on = false;
2237
Amy Maloche864a6d52012-10-03 15:58:12 -07002238 rc = qpnp_led_masked_write(led,
2239 FLASH_LED_STROBE_CTRL(led->base),
2240 FLASH_STROBE_MASK, FLASH_DISABLE_ALL);
2241 if (rc) {
2242 dev_err(&led->spmi_dev->dev,
2243 "LED %d flash write failed(%d)\n", led->id, rc);
2244 return rc;
2245 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002246
Chun Zhangdf2d3062013-06-25 20:14:46 -07002247 /* Disable flash LED module */
2248 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07002249 FLASH_ENABLE_MASK, FLASH_DISABLE_ALL);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002250 if (rc) {
2251 dev_err(&led->spmi_dev->dev,
2252 "Enable reg write failed(%d)\n", rc);
2253 return rc;
2254 }
2255
2256 if (led->flash_cfg->torch_enable)
2257 return 0;
2258
Amy Maloche864a6d52012-10-03 15:58:12 -07002259 /* Set headroom */
2260 rc = qpnp_led_masked_write(led, FLASH_HEADROOM(led->base),
2261 FLASH_HEADROOM_MASK, led->flash_cfg->headroom);
2262 if (rc) {
2263 dev_err(&led->spmi_dev->dev,
2264 "Headroom reg write failed(%d)\n", rc);
2265 return rc;
2266 }
2267
Chun Zhange8954cf2013-05-02 11:14:34 -07002268 /* Set startup delay */
2269 rc = qpnp_led_masked_write(led,
2270 FLASH_STARTUP_DELAY(led->base), FLASH_STARTUP_DLY_MASK,
2271 led->flash_cfg->startup_dly);
2272 if (rc) {
2273 dev_err(&led->spmi_dev->dev,
2274 "Startup delay reg write failed(%d)\n", rc);
2275 return rc;
2276 }
2277
2278 /* Set timer control - safety or watchdog */
2279 if (led->flash_cfg->safety_timer) {
2280 rc = qpnp_led_masked_write(led,
2281 FLASH_LED_TMR_CTRL(led->base),
2282 FLASH_TMR_MASK, FLASH_TMR_SAFETY);
2283 if (rc) {
2284 dev_err(&led->spmi_dev->dev,
2285 "LED timer ctrl reg write failed(%d)\n",
2286 rc);
2287 return rc;
2288 }
2289 }
2290
2291 /* Set Vreg force */
2292 rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
2293 FLASH_VREG_MASK, FLASH_HW_VREG_OK);
2294 if (rc) {
2295 dev_err(&led->spmi_dev->dev,
2296 "Vreg OK reg write failed(%d)\n", rc);
2297 return rc;
2298 }
2299
2300 /* Set self fault check */
2301 rc = qpnp_led_masked_write(led, FLASH_FAULT_DETECT(led->base),
2302 FLASH_FAULT_DETECT_MASK, FLASH_SELFCHECK_ENABLE);
2303 if (rc) {
2304 dev_err(&led->spmi_dev->dev,
2305 "Fault detect reg write failed(%d)\n", rc);
2306 return rc;
2307 }
2308
Amy Maloche864a6d52012-10-03 15:58:12 -07002309 /* Set mask enable */
2310 rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
2311 FLASH_MASK_REG_MASK, FLASH_MASK_1);
2312 if (rc) {
2313 dev_err(&led->spmi_dev->dev,
2314 "Mask enable reg write failed(%d)\n", rc);
2315 return rc;
2316 }
2317
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07002318 /* Set current ramp */
2319 rc = qpnp_led_masked_write(led, FLASH_CURRENT_RAMP(led->base),
2320 FLASH_CURRENT_RAMP_MASK, FLASH_RAMP_STEP_27US);
2321 if (rc) {
2322 dev_err(&led->spmi_dev->dev,
2323 "Current ramp reg write failed(%d)\n", rc);
2324 return rc;
2325 }
2326
Amy Malochebc97c0d22013-03-24 22:06:16 -07002327 led->flash_cfg->strobe_type = 0;
2328
Amy Maloche864a6d52012-10-03 15:58:12 -07002329 /* dump flash registers */
2330 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
2331
2332 return 0;
2333}
2334
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302335static int __devinit qpnp_kpdbl_init(struct qpnp_led_data *led)
2336{
2337 int rc;
2338 u8 val;
2339
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302340 /* select row source - vbst or vph */
2341 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2342 KPDBL_ROW_SRC_SEL(led->base), &val, 1);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302343 if (rc) {
2344 dev_err(&led->spmi_dev->dev,
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302345 "Unable to read from addr=%x, rc(%d)\n",
2346 KPDBL_ROW_SRC_SEL(led->base), rc);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302347 return rc;
2348 }
2349
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302350 if (led->kpdbl_cfg->row_src_vbst)
2351 val |= 1 << led->kpdbl_cfg->row_id;
2352 else
2353 val &= ~(1 << led->kpdbl_cfg->row_id);
2354
2355 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
2356 KPDBL_ROW_SRC_SEL(led->base), &val, 1);
2357 if (rc) {
2358 dev_err(&led->spmi_dev->dev,
2359 "Unable to read from addr=%x, rc(%d)\n",
2360 KPDBL_ROW_SRC_SEL(led->base), rc);
2361 return rc;
2362 }
2363
2364 /* row source enable */
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302365 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2366 KPDBL_ROW_SRC(led->base), &val, 1);
2367 if (rc) {
2368 dev_err(&led->spmi_dev->dev,
2369 "Unable to read from addr=%x, rc(%d)\n",
2370 KPDBL_ROW_SRC(led->base), rc);
2371 return rc;
2372 }
2373
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302374 if (led->kpdbl_cfg->row_src_en)
2375 val |= KPDBL_ROW_SCAN_EN_MASK | (1 << led->kpdbl_cfg->row_id);
2376 else
2377 val &= ~(1 << led->kpdbl_cfg->row_id);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302378
2379 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
2380 KPDBL_ROW_SRC(led->base), &val, 1);
2381 if (rc) {
2382 dev_err(&led->spmi_dev->dev,
2383 "Unable to write to addr=%x, rc(%d)\n",
2384 KPDBL_ROW_SRC(led->base), rc);
2385 return rc;
2386 }
2387
2388 /* enable module */
2389 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
2390 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
2391 if (rc) {
2392 dev_err(&led->spmi_dev->dev,
2393 "Enable module write failed(%d)\n", rc);
2394 return rc;
2395 }
2396
Amy Malochea2726f02013-05-10 10:19:03 -07002397 rc = qpnp_pwm_init(led->kpdbl_cfg->pwm_cfg, led->spmi_dev,
2398 led->cdev.name);
2399 if (rc) {
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302400 dev_err(&led->spmi_dev->dev,
Amy Malochea2726f02013-05-10 10:19:03 -07002401 "Failed to initialize pwm\n");
2402 return rc;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302403 }
2404
2405 /* dump kpdbl registers */
2406 qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
2407
2408 return 0;
2409}
2410
Amy Malocheeea7b592012-10-03 15:59:36 -07002411static int __devinit qpnp_rgb_init(struct qpnp_led_data *led)
2412{
Amy Malochea2726f02013-05-10 10:19:03 -07002413 int rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07002414
2415 rc = qpnp_led_masked_write(led, RGB_LED_SRC_SEL(led->base),
2416 RGB_LED_SRC_MASK, RGB_LED_SOURCE_VPH_PWR);
2417 if (rc) {
2418 dev_err(&led->spmi_dev->dev,
2419 "Failed to write led source select register\n");
2420 return rc;
2421 }
2422
Amy Malochea2726f02013-05-10 10:19:03 -07002423 rc = qpnp_pwm_init(led->rgb_cfg->pwm_cfg, led->spmi_dev,
2424 led->cdev.name);
2425 if (rc) {
Amy Malocheeea7b592012-10-03 15:59:36 -07002426 dev_err(&led->spmi_dev->dev,
Amy Malochea2726f02013-05-10 10:19:03 -07002427 "Failed to initialize pwm\n");
2428 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07002429 }
Amy Malocheeea7b592012-10-03 15:59:36 -07002430 /* Initialize led for use in auto trickle charging mode */
2431 rc = qpnp_led_masked_write(led, RGB_LED_ATC_CTL(led->base),
2432 led->rgb_cfg->enable, led->rgb_cfg->enable);
2433
2434 return 0;
2435}
2436
Amy Malochea2726f02013-05-10 10:19:03 -07002437static int __devinit qpnp_mpp_init(struct qpnp_led_data *led)
2438{
2439 int rc, val;
2440
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05302441
2442 if (led->max_current < LED_MPP_CURRENT_MIN ||
2443 led->max_current > LED_MPP_CURRENT_MAX) {
2444 dev_err(&led->spmi_dev->dev,
2445 "max current for mpp is not valid\n");
2446 return -EINVAL;
2447 }
2448
Amy Malochea2726f02013-05-10 10:19:03 -07002449 val = (led->mpp_cfg->current_setting / LED_MPP_CURRENT_PER_SETTING) - 1;
2450
2451 if (val < 0)
2452 val = 0;
2453
Chun Zhang874c9ab2013-07-08 17:18:34 -07002454 rc = qpnp_led_masked_write(led, LED_MPP_VIN_CTRL(led->base),
2455 LED_MPP_VIN_MASK, led->mpp_cfg->vin_ctrl);
2456 if (rc) {
2457 dev_err(&led->spmi_dev->dev,
2458 "Failed to write led vin control reg\n");
2459 return rc;
2460 }
2461
Amy Malochea2726f02013-05-10 10:19:03 -07002462 rc = qpnp_led_masked_write(led, LED_MPP_SINK_CTRL(led->base),
2463 LED_MPP_SINK_MASK, val);
2464 if (rc) {
2465 dev_err(&led->spmi_dev->dev,
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05302466 "Failed to write sink control reg\n");
Amy Malochea2726f02013-05-10 10:19:03 -07002467 return rc;
2468 }
2469
2470 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
2471 rc = qpnp_pwm_init(led->mpp_cfg->pwm_cfg, led->spmi_dev,
2472 led->cdev.name);
2473 if (rc) {
2474 dev_err(&led->spmi_dev->dev,
2475 "Failed to initialize pwm\n");
2476 return rc;
2477 }
2478 }
2479
2480 return 0;
2481}
2482
Amy Malochef3d5a062012-08-16 19:14:11 -07002483static int __devinit qpnp_led_initialize(struct qpnp_led_data *led)
2484{
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302485 int rc = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07002486
2487 switch (led->id) {
2488 case QPNP_ID_WLED:
2489 rc = qpnp_wled_init(led);
2490 if (rc)
Amy Malochea5ca5552012-10-23 13:34:46 -07002491 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -07002492 "WLED initialize failed(%d)\n", rc);
2493 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07002494 case QPNP_ID_FLASH1_LED0:
2495 case QPNP_ID_FLASH1_LED1:
2496 rc = qpnp_flash_init(led);
2497 if (rc)
2498 dev_err(&led->spmi_dev->dev,
2499 "FLASH initialize failed(%d)\n", rc);
2500 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07002501 case QPNP_ID_RGB_RED:
2502 case QPNP_ID_RGB_GREEN:
2503 case QPNP_ID_RGB_BLUE:
2504 rc = qpnp_rgb_init(led);
2505 if (rc)
2506 dev_err(&led->spmi_dev->dev,
2507 "RGB initialize failed(%d)\n", rc);
2508 break;
Amy Malochef3813742013-04-11 19:33:47 -07002509 case QPNP_ID_LED_MPP:
Amy Malochea2726f02013-05-10 10:19:03 -07002510 rc = qpnp_mpp_init(led);
2511 if (rc)
2512 dev_err(&led->spmi_dev->dev,
2513 "MPP initialize failed(%d)\n", rc);
Amy Malochef3813742013-04-11 19:33:47 -07002514 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302515 case QPNP_ID_KPDBL:
2516 rc = qpnp_kpdbl_init(led);
2517 if (rc)
2518 dev_err(&led->spmi_dev->dev,
2519 "KPDBL initialize failed(%d)\n", rc);
2520 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07002521 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07002522 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malocheeea7b592012-10-03 15:59:36 -07002523 return -EINVAL;
Amy Malochef3d5a062012-08-16 19:14:11 -07002524 }
2525
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302526 return rc;
Amy Malochef3d5a062012-08-16 19:14:11 -07002527}
2528
Amy Malochea5ca5552012-10-23 13:34:46 -07002529static int __devinit qpnp_get_common_configs(struct qpnp_led_data *led,
2530 struct device_node *node)
2531{
2532 int rc;
Asaf Penso55ac8472013-01-21 21:17:37 +02002533 u32 val;
Amy Malochea5ca5552012-10-23 13:34:46 -07002534 const char *temp_string;
2535
2536 led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
2537 rc = of_property_read_string(node, "linux,default-trigger",
2538 &temp_string);
2539 if (!rc)
2540 led->cdev.default_trigger = temp_string;
2541 else if (rc != -EINVAL)
2542 return rc;
2543
2544 led->default_on = false;
2545 rc = of_property_read_string(node, "qcom,default-state",
2546 &temp_string);
2547 if (!rc) {
2548 if (strncmp(temp_string, "on", sizeof("on")) == 0)
2549 led->default_on = true;
2550 } else if (rc != -EINVAL)
2551 return rc;
2552
Asaf Penso55ac8472013-01-21 21:17:37 +02002553 led->turn_off_delay_ms = 0;
2554 rc = of_property_read_u32(node, "qcom,turn-off-delay-ms", &val);
2555 if (!rc)
2556 led->turn_off_delay_ms = val;
2557 else if (rc != -EINVAL)
2558 return rc;
2559
Amy Malochea5ca5552012-10-23 13:34:46 -07002560 return 0;
2561}
2562
Amy Malochef3d5a062012-08-16 19:14:11 -07002563/*
2564 * Handlers for alternative sources of platform_data
2565 */
2566static int __devinit qpnp_get_config_wled(struct qpnp_led_data *led,
2567 struct device_node *node)
2568{
2569 u32 val;
2570 int rc;
Amy Malochef3d5a062012-08-16 19:14:11 -07002571
2572 led->wled_cfg = devm_kzalloc(&led->spmi_dev->dev,
2573 sizeof(struct wled_config_data), GFP_KERNEL);
2574 if (!led->wled_cfg) {
2575 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2576 return -ENOMEM;
2577 }
2578
Amy Maloche0150b5e2013-08-15 18:18:32 -07002579 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2580 PMIC_VERSION_REG, &led->wled_cfg->pmic_version, 1);
2581 if (rc) {
2582 dev_err(&led->spmi_dev->dev,
2583 "Unable to read pmic ver, rc(%d)\n", rc);
2584 }
2585
Amy Malochef3d5a062012-08-16 19:14:11 -07002586 led->wled_cfg->num_strings = WLED_DEFAULT_STRINGS;
2587 rc = of_property_read_u32(node, "qcom,num-strings", &val);
2588 if (!rc)
2589 led->wled_cfg->num_strings = (u8) val;
2590 else if (rc != -EINVAL)
2591 return rc;
2592
2593 led->wled_cfg->ovp_val = WLED_DEFAULT_OVP_VAL;
2594 rc = of_property_read_u32(node, "qcom,ovp-val", &val);
2595 if (!rc)
2596 led->wled_cfg->ovp_val = (u8) val;
2597 else if (rc != -EINVAL)
2598 return rc;
2599
2600 led->wled_cfg->boost_curr_lim = WLED_BOOST_LIM_DEFAULT;
2601 rc = of_property_read_u32(node, "qcom,boost-curr-lim", &val);
2602 if (!rc)
2603 led->wled_cfg->boost_curr_lim = (u8) val;
2604 else if (rc != -EINVAL)
2605 return rc;
2606
2607 led->wled_cfg->cp_select = WLED_CP_SEL_DEFAULT;
2608 rc = of_property_read_u32(node, "qcom,cp-sel", &val);
2609 if (!rc)
2610 led->wled_cfg->cp_select = (u8) val;
2611 else if (rc != -EINVAL)
2612 return rc;
2613
2614 led->wled_cfg->ctrl_delay_us = WLED_CTRL_DLY_DEFAULT;
2615 rc = of_property_read_u32(node, "qcom,ctrl-delay-us", &val);
2616 if (!rc)
2617 led->wled_cfg->ctrl_delay_us = (u8) val;
2618 else if (rc != -EINVAL)
2619 return rc;
2620
Amy Malochebd687672013-03-18 11:23:45 -07002621 led->wled_cfg->op_fdbck = WLED_OP_FDBCK_DEFAULT;
2622 rc = of_property_read_u32(node, "qcom,op-fdbck", &val);
2623 if (!rc)
2624 led->wled_cfg->op_fdbck = (u8) val;
2625 else if (rc != -EINVAL)
2626 return rc;
2627
Amy Malochef3d5a062012-08-16 19:14:11 -07002628 led->wled_cfg->switch_freq = WLED_SWITCH_FREQ_DEFAULT;
2629 rc = of_property_read_u32(node, "qcom,switch-freq", &val);
2630 if (!rc)
2631 led->wled_cfg->switch_freq = (u8) val;
2632 else if (rc != -EINVAL)
2633 return rc;
2634
2635 led->wled_cfg->dig_mod_gen_en =
2636 of_property_read_bool(node, "qcom,dig-mod-gen-en");
2637
2638 led->wled_cfg->cs_out_en =
2639 of_property_read_bool(node, "qcom,cs-out-en");
2640
Amy Malochef3d5a062012-08-16 19:14:11 -07002641 return 0;
2642}
2643
Amy Maloche864a6d52012-10-03 15:58:12 -07002644static int __devinit qpnp_get_config_flash(struct qpnp_led_data *led,
Chun Zhangc3b505b2013-06-03 19:01:49 -07002645 struct device_node *node, bool *reg_set)
Amy Maloche864a6d52012-10-03 15:58:12 -07002646{
2647 int rc;
2648 u32 val;
2649
2650 led->flash_cfg = devm_kzalloc(&led->spmi_dev->dev,
2651 sizeof(struct flash_config_data), GFP_KERNEL);
2652 if (!led->flash_cfg) {
2653 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2654 return -ENOMEM;
2655 }
2656
Chun Zhang0d6ca072013-07-30 21:08:39 -07002657 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2658 FLASH_PERIPHERAL_SUBTYPE(led->base),
2659 &led->flash_cfg->peripheral_subtype, 1);
2660 if (rc) {
2661 dev_err(&led->spmi_dev->dev,
2662 "Unable to read from addr=%x, rc(%d)\n",
2663 FLASH_PERIPHERAL_SUBTYPE(led->base), rc);
2664 }
2665
Chun Zhang9d5ff672013-08-01 18:18:26 -07002666 led->flash_cfg->torch_enable =
2667 of_property_read_bool(node, "qcom,torch-enable");
2668
Amy Maloche864a6d52012-10-03 15:58:12 -07002669 if (led->id == QPNP_ID_FLASH1_LED0) {
Chun Zhang9d5ff672013-08-01 18:18:26 -07002670 led->flash_cfg->enable_module = FLASH_ENABLE_LED_0;
Amy Maloche864a6d52012-10-03 15:58:12 -07002671 led->flash_cfg->current_addr = FLASH_LED_0_CURR(led->base);
Amy Maloche864a6d52012-10-03 15:58:12 -07002672 led->flash_cfg->trigger_flash = FLASH_LED_0_OUTPUT;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002673 if (!*reg_set) {
2674 led->flash_cfg->flash_boost_reg =
2675 regulator_get(&led->spmi_dev->dev,
Chun Zhang0d6ca072013-07-30 21:08:39 -07002676 "flash-boost");
Chun Zhangc3b505b2013-06-03 19:01:49 -07002677 if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
2678 rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
2679 dev_err(&led->spmi_dev->dev,
2680 "Regulator get failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002681 goto error_get_flash_reg;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002682 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002683 led->flash_cfg->flash_reg_get = true;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002684 *reg_set = true;
2685 } else
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002686 led->flash_cfg->flash_reg_get = false;
Chun Zhang9d5ff672013-08-01 18:18:26 -07002687
2688 if (led->flash_cfg->torch_enable) {
2689 led->flash_cfg->second_addr =
2690 FLASH_LED_1_CURR(led->base);
2691 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002692 } else if (led->id == QPNP_ID_FLASH1_LED1) {
Chun Zhang9d5ff672013-08-01 18:18:26 -07002693 led->flash_cfg->enable_module = FLASH_ENABLE_LED_1;
Amy Maloche864a6d52012-10-03 15:58:12 -07002694 led->flash_cfg->current_addr = FLASH_LED_1_CURR(led->base);
Amy Maloche864a6d52012-10-03 15:58:12 -07002695 led->flash_cfg->trigger_flash = FLASH_LED_1_OUTPUT;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002696 if (!*reg_set) {
2697 led->flash_cfg->flash_boost_reg =
2698 regulator_get(&led->spmi_dev->dev,
Chun Zhang0d6ca072013-07-30 21:08:39 -07002699 "flash-boost");
Chun Zhangc3b505b2013-06-03 19:01:49 -07002700 if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
2701 rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
2702 dev_err(&led->spmi_dev->dev,
2703 "Regulator get failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002704 goto error_get_flash_reg;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002705 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002706 led->flash_cfg->flash_reg_get = true;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002707 *reg_set = true;
2708 } else
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002709 led->flash_cfg->flash_reg_get = false;
Chun Zhang9d5ff672013-08-01 18:18:26 -07002710
2711 if (led->flash_cfg->torch_enable) {
2712 led->flash_cfg->second_addr =
2713 FLASH_LED_0_CURR(led->base);
2714 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002715 } else {
2716 dev_err(&led->spmi_dev->dev, "Unknown flash LED name given\n");
2717 return -EINVAL;
2718 }
2719
Chun Zhangdf2d3062013-06-25 20:14:46 -07002720 if (led->flash_cfg->torch_enable) {
Chun Zhang0d6ca072013-07-30 21:08:39 -07002721 if (of_find_property(of_get_parent(node), "torch-boost-supply",
2722 NULL)) {
2723 led->flash_cfg->torch_boost_reg =
2724 regulator_get(&led->spmi_dev->dev,
2725 "torch-boost");
2726 if (IS_ERR(led->flash_cfg->torch_boost_reg)) {
2727 rc = PTR_ERR(led->flash_cfg->torch_boost_reg);
2728 dev_err(&led->spmi_dev->dev,
2729 "Torch regulator get failed(%d)\n", rc);
2730 goto error_get_torch_reg;
2731 }
Chun Zhang9d5ff672013-08-01 18:18:26 -07002732 led->flash_cfg->enable_module = FLASH_ENABLE_MODULE;
2733 } else
2734 led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
2735 led->flash_cfg->trigger_flash = FLASH_STROBE_SW;
Chun Zhangdf2d3062013-06-25 20:14:46 -07002736 }
2737
Amy Maloche864a6d52012-10-03 15:58:12 -07002738 rc = of_property_read_u32(node, "qcom,current", &val);
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002739 if (!rc) {
Chun Zhangdf2d3062013-06-25 20:14:46 -07002740 if (led->flash_cfg->torch_enable) {
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002741 led->flash_cfg->current_prgm = (val *
2742 TORCH_MAX_LEVEL / led->max_current);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002743 return 0;
2744 }
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002745 else
2746 led->flash_cfg->current_prgm = (val *
Amy Maloche864a6d52012-10-03 15:58:12 -07002747 FLASH_MAX_LEVEL / led->max_current);
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002748 } else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002749 if (led->flash_cfg->torch_enable)
2750 goto error_get_torch_reg;
2751 else
2752 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002753
2754 rc = of_property_read_u32(node, "qcom,headroom", &val);
2755 if (!rc)
2756 led->flash_cfg->headroom = (u8) val;
2757 else if (rc == -EINVAL)
Amy Maloche62571b22013-04-17 17:23:10 -07002758 led->flash_cfg->headroom = HEADROOM_500mV;
Amy Maloche864a6d52012-10-03 15:58:12 -07002759 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002760 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002761
2762 rc = of_property_read_u32(node, "qcom,duration", &val);
2763 if (!rc)
Abinaya P41bb8bc2013-12-06 22:55:37 +05302764 led->flash_cfg->duration = (u8)((val - 10) / 10);
Amy Maloche864a6d52012-10-03 15:58:12 -07002765 else if (rc == -EINVAL)
2766 led->flash_cfg->duration = FLASH_DURATION_200ms;
2767 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002768 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002769
2770 rc = of_property_read_u32(node, "qcom,clamp-curr", &val);
2771 if (!rc)
2772 led->flash_cfg->clamp_curr = (val *
2773 FLASH_MAX_LEVEL / led->max_current);
2774 else if (rc == -EINVAL)
2775 led->flash_cfg->clamp_curr = FLASH_CLAMP_200mA;
2776 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002777 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002778
2779 rc = of_property_read_u32(node, "qcom,startup-dly", &val);
2780 if (!rc)
2781 led->flash_cfg->startup_dly = (u8) val;
2782 else if (rc == -EINVAL)
Amy Maloche62571b22013-04-17 17:23:10 -07002783 led->flash_cfg->startup_dly = DELAY_128us;
Amy Maloche864a6d52012-10-03 15:58:12 -07002784 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002785 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002786
2787 led->flash_cfg->safety_timer =
2788 of_property_read_bool(node, "qcom,safety-timer");
2789
2790 return 0;
Chun Zhangdf2d3062013-06-25 20:14:46 -07002791
2792error_get_torch_reg:
2793 regulator_put(led->flash_cfg->torch_boost_reg);
2794
2795error_get_flash_reg:
2796 regulator_put(led->flash_cfg->flash_boost_reg);
2797 return rc;
2798
Amy Maloche864a6d52012-10-03 15:58:12 -07002799}
2800
Amy Malochea2726f02013-05-10 10:19:03 -07002801static int __devinit qpnp_get_config_pwm(struct pwm_config_data *pwm_cfg,
2802 struct spmi_device *spmi_dev,
2803 struct device_node *node)
2804{
2805 struct property *prop;
2806 int rc, i;
2807 u32 val;
2808 u8 *temp_cfg;
2809
2810 rc = of_property_read_u32(node, "qcom,pwm-channel", &val);
2811 if (!rc)
2812 pwm_cfg->pwm_channel = val;
2813 else
2814 return rc;
2815
2816 if (pwm_cfg->mode == PWM_MODE) {
2817 rc = of_property_read_u32(node, "qcom,pwm-us", &val);
2818 if (!rc)
2819 pwm_cfg->pwm_period_us = val;
2820 else
2821 return rc;
2822 }
2823
Amy Malochea5c4ed82013-06-05 11:05:28 -07002824 pwm_cfg->use_blink =
2825 of_property_read_bool(node, "qcom,use-blink");
2826
2827 if (pwm_cfg->mode == LPG_MODE || pwm_cfg->use_blink) {
Amy Malochea2726f02013-05-10 10:19:03 -07002828 pwm_cfg->duty_cycles =
2829 devm_kzalloc(&spmi_dev->dev,
2830 sizeof(struct pwm_duty_cycles), GFP_KERNEL);
2831 if (!pwm_cfg->duty_cycles) {
2832 dev_err(&spmi_dev->dev,
2833 "Unable to allocate memory\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002834 rc = -ENOMEM;
2835 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002836 }
2837
2838 prop = of_find_property(node, "qcom,duty-pcts",
2839 &pwm_cfg->duty_cycles->num_duty_pcts);
2840 if (!prop) {
2841 dev_err(&spmi_dev->dev, "Looking up property " \
2842 "node qcom,duty-pcts failed\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002843 rc = -ENODEV;
2844 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002845 } else if (!pwm_cfg->duty_cycles->num_duty_pcts) {
2846 dev_err(&spmi_dev->dev, "Invalid length of " \
2847 "duty pcts\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002848 rc = -EINVAL;
2849 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002850 }
2851
2852 pwm_cfg->duty_cycles->duty_pcts =
2853 devm_kzalloc(&spmi_dev->dev,
Amy Maloche4cf9f322013-05-29 15:53:46 -07002854 sizeof(int) * PWM_LUT_MAX_SIZE,
Amy Malochea2726f02013-05-10 10:19:03 -07002855 GFP_KERNEL);
2856 if (!pwm_cfg->duty_cycles->duty_pcts) {
2857 dev_err(&spmi_dev->dev,
2858 "Unable to allocate memory\n");
Amy Maloche4cf9f322013-05-29 15:53:46 -07002859 rc = -ENOMEM;
2860 goto bad_lpg_params;
2861 }
2862
2863 pwm_cfg->old_duty_pcts =
2864 devm_kzalloc(&spmi_dev->dev,
2865 sizeof(int) * PWM_LUT_MAX_SIZE,
2866 GFP_KERNEL);
2867 if (!pwm_cfg->old_duty_pcts) {
2868 dev_err(&spmi_dev->dev,
2869 "Unable to allocate memory\n");
2870 rc = -ENOMEM;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002871 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002872 }
2873
2874 temp_cfg = devm_kzalloc(&spmi_dev->dev,
2875 pwm_cfg->duty_cycles->num_duty_pcts *
2876 sizeof(u8), GFP_KERNEL);
2877 if (!temp_cfg) {
2878 dev_err(&spmi_dev->dev, "Failed to allocate " \
2879 "memory for duty pcts\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002880 rc = -ENOMEM;
2881 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002882 }
2883
2884 memcpy(temp_cfg, prop->value,
2885 pwm_cfg->duty_cycles->num_duty_pcts);
2886
2887 for (i = 0; i < pwm_cfg->duty_cycles->num_duty_pcts; i++)
2888 pwm_cfg->duty_cycles->duty_pcts[i] =
2889 (int) temp_cfg[i];
2890
2891 rc = of_property_read_u32(node, "qcom,start-idx", &val);
2892 if (!rc) {
2893 pwm_cfg->lut_params.start_idx = val;
2894 pwm_cfg->duty_cycles->start_idx = val;
2895 } else
Amy Malochea5c4ed82013-06-05 11:05:28 -07002896 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002897
2898 pwm_cfg->lut_params.lut_pause_hi = 0;
2899 rc = of_property_read_u32(node, "qcom,pause-hi", &val);
2900 if (!rc)
2901 pwm_cfg->lut_params.lut_pause_hi = val;
2902 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002903 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002904
2905 pwm_cfg->lut_params.lut_pause_lo = 0;
2906 rc = of_property_read_u32(node, "qcom,pause-lo", &val);
2907 if (!rc)
2908 pwm_cfg->lut_params.lut_pause_lo = val;
2909 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002910 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002911
2912 pwm_cfg->lut_params.ramp_step_ms =
2913 QPNP_LUT_RAMP_STEP_DEFAULT;
2914 rc = of_property_read_u32(node, "qcom,ramp-step-ms", &val);
2915 if (!rc)
2916 pwm_cfg->lut_params.ramp_step_ms = val;
2917 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002918 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002919
2920 pwm_cfg->lut_params.flags = QPNP_LED_PWM_FLAGS;
2921 rc = of_property_read_u32(node, "qcom,lut-flags", &val);
2922 if (!rc)
2923 pwm_cfg->lut_params.flags = (u8) val;
2924 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002925 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002926
2927 pwm_cfg->lut_params.idx_len =
2928 pwm_cfg->duty_cycles->num_duty_pcts;
2929 }
2930 return 0;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002931
2932bad_lpg_params:
2933 pwm_cfg->use_blink = false;
2934 if (pwm_cfg->mode == PWM_MODE) {
2935 dev_err(&spmi_dev->dev, "LPG parameters not set for" \
2936 " blink mode, defaulting to PWM mode\n");
2937 return 0;
2938 }
2939 return rc;
Amy Malochea2726f02013-05-10 10:19:03 -07002940};
2941
2942static int qpnp_led_get_mode(const char *mode)
2943{
2944 if (strncmp(mode, "manual", strlen(mode)) == 0)
2945 return MANUAL_MODE;
2946 else if (strncmp(mode, "pwm", strlen(mode)) == 0)
2947 return PWM_MODE;
2948 else if (strncmp(mode, "lpg", strlen(mode)) == 0)
2949 return LPG_MODE;
2950 else
2951 return -EINVAL;
2952};
2953
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302954static int __devinit qpnp_get_config_kpdbl(struct qpnp_led_data *led,
2955 struct device_node *node)
2956{
2957 int rc;
2958 u32 val;
Amy Malochea2726f02013-05-10 10:19:03 -07002959 u8 led_mode;
2960 const char *mode;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302961
2962 led->kpdbl_cfg = devm_kzalloc(&led->spmi_dev->dev,
2963 sizeof(struct kpdbl_config_data), GFP_KERNEL);
2964 if (!led->kpdbl_cfg) {
2965 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2966 return -ENOMEM;
2967 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302968
Amy Malochea2726f02013-05-10 10:19:03 -07002969 rc = of_property_read_string(node, "qcom,mode", &mode);
2970 if (!rc) {
2971 led_mode = qpnp_led_get_mode(mode);
2972 if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
2973 dev_err(&led->spmi_dev->dev, "Selected mode not " \
2974 "supported for kpdbl.\n");
2975 return -EINVAL;
2976 }
2977 led->kpdbl_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
2978 sizeof(struct pwm_config_data),
2979 GFP_KERNEL);
2980 if (!led->kpdbl_cfg->pwm_cfg) {
2981 dev_err(&led->spmi_dev->dev,
2982 "Unable to allocate memory\n");
2983 return -ENOMEM;
2984 }
2985 led->kpdbl_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07002986 led->kpdbl_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07002987 } else
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302988 return rc;
2989
Amy Malochea2726f02013-05-10 10:19:03 -07002990 rc = qpnp_get_config_pwm(led->kpdbl_cfg->pwm_cfg, led->spmi_dev, node);
2991 if (rc < 0)
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302992 return rc;
2993
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302994 rc = of_property_read_u32(node, "qcom,row-id", &val);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302995 if (!rc)
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302996 led->kpdbl_cfg->row_id = val;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302997 else
2998 return rc;
2999
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303000 led->kpdbl_cfg->row_src_vbst =
3001 of_property_read_bool(node, "qcom,row-src-vbst");
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303002
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303003 led->kpdbl_cfg->row_src_en =
3004 of_property_read_bool(node, "qcom,row-src-en");
3005
3006 led->kpdbl_cfg->always_on =
3007 of_property_read_bool(node, "qcom,always-on");
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303008
3009 return 0;
3010}
3011
Amy Malocheeea7b592012-10-03 15:59:36 -07003012static int __devinit qpnp_get_config_rgb(struct qpnp_led_data *led,
3013 struct device_node *node)
3014{
Amy Malochea2726f02013-05-10 10:19:03 -07003015 int rc;
3016 u8 led_mode;
3017 const char *mode;
Amy Malocheeea7b592012-10-03 15:59:36 -07003018
3019 led->rgb_cfg = devm_kzalloc(&led->spmi_dev->dev,
3020 sizeof(struct rgb_config_data), GFP_KERNEL);
3021 if (!led->rgb_cfg) {
3022 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3023 return -ENOMEM;
3024 }
3025
3026 if (led->id == QPNP_ID_RGB_RED)
3027 led->rgb_cfg->enable = RGB_LED_ENABLE_RED;
3028 else if (led->id == QPNP_ID_RGB_GREEN)
3029 led->rgb_cfg->enable = RGB_LED_ENABLE_GREEN;
3030 else if (led->id == QPNP_ID_RGB_BLUE)
3031 led->rgb_cfg->enable = RGB_LED_ENABLE_BLUE;
3032 else
3033 return -EINVAL;
3034
Amy Malochea2726f02013-05-10 10:19:03 -07003035 rc = of_property_read_string(node, "qcom,mode", &mode);
3036 if (!rc) {
3037 led_mode = qpnp_led_get_mode(mode);
3038 if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
3039 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3040 "supported for rgb.\n");
Amy Malocheeea7b592012-10-03 15:59:36 -07003041 return -EINVAL;
3042 }
Amy Malochea2726f02013-05-10 10:19:03 -07003043 led->rgb_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3044 sizeof(struct pwm_config_data),
3045 GFP_KERNEL);
3046 if (!led->rgb_cfg->pwm_cfg) {
Amy Malocheeea7b592012-10-03 15:59:36 -07003047 dev_err(&led->spmi_dev->dev,
3048 "Unable to allocate memory\n");
3049 return -ENOMEM;
3050 }
Amy Malochea2726f02013-05-10 10:19:03 -07003051 led->rgb_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003052 led->rgb_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003053 } else
3054 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07003055
Amy Malochea2726f02013-05-10 10:19:03 -07003056 rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->spmi_dev, node);
3057 if (rc < 0)
3058 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07003059
3060 return 0;
3061}
3062
Amy Malochef3813742013-04-11 19:33:47 -07003063static int __devinit qpnp_get_config_mpp(struct qpnp_led_data *led,
3064 struct device_node *node)
3065{
3066 int rc;
3067 u32 val;
Amy Malochea2726f02013-05-10 10:19:03 -07003068 u8 led_mode;
3069 const char *mode;
Amy Malochef3813742013-04-11 19:33:47 -07003070
3071 led->mpp_cfg = devm_kzalloc(&led->spmi_dev->dev,
3072 sizeof(struct mpp_config_data), GFP_KERNEL);
3073 if (!led->mpp_cfg) {
3074 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3075 return -ENOMEM;
3076 }
3077
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05303078 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
Amy Malochef3813742013-04-11 19:33:47 -07003079 rc = of_property_read_u32(node, "qcom,current-setting", &val);
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05303080 if (!rc) {
3081 if (led->mpp_cfg->current_setting < LED_MPP_CURRENT_MIN)
3082 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
3083 else if (led->mpp_cfg->current_setting > LED_MPP_CURRENT_MAX)
3084 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MAX;
3085 else
3086 led->mpp_cfg->current_setting = (u8) val;
3087 } else if (rc != -EINVAL)
Amy Malochef3813742013-04-11 19:33:47 -07003088 return rc;
3089
3090 led->mpp_cfg->source_sel = LED_MPP_SOURCE_SEL_DEFAULT;
3091 rc = of_property_read_u32(node, "qcom,source-sel", &val);
3092 if (!rc)
3093 led->mpp_cfg->source_sel = (u8) val;
3094 else if (rc != -EINVAL)
3095 return rc;
3096
3097 led->mpp_cfg->mode_ctrl = LED_MPP_MODE_SINK;
3098 rc = of_property_read_u32(node, "qcom,mode-ctrl", &val);
3099 if (!rc)
3100 led->mpp_cfg->mode_ctrl = (u8) val;
3101 else if (rc != -EINVAL)
3102 return rc;
3103
Chun Zhang874c9ab2013-07-08 17:18:34 -07003104 led->mpp_cfg->vin_ctrl = LED_MPP_VIN_CTRL_DEFAULT;
3105 rc = of_property_read_u32(node, "qcom,vin-ctrl", &val);
3106 if (!rc)
3107 led->mpp_cfg->vin_ctrl = (u8) val;
3108 else if (rc != -EINVAL)
3109 return rc;
3110
3111 led->mpp_cfg->min_brightness = 0;
3112 rc = of_property_read_u32(node, "qcom,min-brightness", &val);
3113 if (!rc)
3114 led->mpp_cfg->min_brightness = (u8) val;
3115 else if (rc != -EINVAL)
3116 return rc;
3117
Amy Malochea2726f02013-05-10 10:19:03 -07003118 rc = of_property_read_string(node, "qcom,mode", &mode);
3119 if (!rc) {
3120 led_mode = qpnp_led_get_mode(mode);
3121 led->mpp_cfg->pwm_mode = led_mode;
3122 if (led_mode == MANUAL_MODE)
3123 return MANUAL_MODE;
3124 else if (led_mode == -EINVAL) {
3125 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3126 "supported for mpp.\n");
3127 return -EINVAL;
3128 }
3129 led->mpp_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3130 sizeof(struct pwm_config_data),
3131 GFP_KERNEL);
3132 if (!led->mpp_cfg->pwm_cfg) {
3133 dev_err(&led->spmi_dev->dev,
3134 "Unable to allocate memory\n");
3135 return -ENOMEM;
3136 }
3137 led->mpp_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003138 led->mpp_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003139 } else
3140 return rc;
3141
3142 rc = qpnp_get_config_pwm(led->mpp_cfg->pwm_cfg, led->spmi_dev, node);
3143 if (rc < 0)
3144 return rc;
3145
Amy Malochef3813742013-04-11 19:33:47 -07003146 return 0;
3147}
3148
Amy Malochef3d5a062012-08-16 19:14:11 -07003149static int __devinit qpnp_leds_probe(struct spmi_device *spmi)
3150{
Amy Malochef9490c62012-11-27 19:26:04 -08003151 struct qpnp_led_data *led, *led_array;
Amy Malochef3d5a062012-08-16 19:14:11 -07003152 struct resource *led_resource;
Amy Malochea5ca5552012-10-23 13:34:46 -07003153 struct device_node *node, *temp;
Amy Malochef9490c62012-11-27 19:26:04 -08003154 int rc, i, num_leds = 0, parsed_leds = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07003155 const char *led_label;
Chun Zhangc3b505b2013-06-03 19:01:49 -07003156 bool regulator_probe = false;
Amy Malochef3d5a062012-08-16 19:14:11 -07003157
Amy Malochea5ca5552012-10-23 13:34:46 -07003158 node = spmi->dev.of_node;
3159 if (node == NULL)
3160 return -ENODEV;
3161
3162 temp = NULL;
3163 while ((temp = of_get_next_child(node, temp)))
3164 num_leds++;
3165
Amy Malochef9490c62012-11-27 19:26:04 -08003166 if (!num_leds)
3167 return -ECHILD;
3168
3169 led_array = devm_kzalloc(&spmi->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07003170 (sizeof(struct qpnp_led_data) * num_leds), GFP_KERNEL);
Amy Malochef9490c62012-11-27 19:26:04 -08003171 if (!led_array) {
Amy Malochef3d5a062012-08-16 19:14:11 -07003172 dev_err(&spmi->dev, "Unable to allocate memory\n");
3173 return -ENOMEM;
3174 }
3175
Amy Malochea5ca5552012-10-23 13:34:46 -07003176 for_each_child_of_node(node, temp) {
Amy Malochef9490c62012-11-27 19:26:04 -08003177 led = &led_array[parsed_leds];
3178 led->num_leds = num_leds;
Amy Malochea5ca5552012-10-23 13:34:46 -07003179 led->spmi_dev = spmi;
Amy Malochef3d5a062012-08-16 19:14:11 -07003180
Amy Malochea5ca5552012-10-23 13:34:46 -07003181 led_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
3182 if (!led_resource) {
3183 dev_err(&spmi->dev, "Unable to get LED base address\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003184 rc = -ENXIO;
3185 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003186 }
3187 led->base = led_resource->start;
Amy Malochef3d5a062012-08-16 19:14:11 -07003188
Amy Malochea5ca5552012-10-23 13:34:46 -07003189 rc = of_property_read_string(temp, "label", &led_label);
Amy Malochef3d5a062012-08-16 19:14:11 -07003190 if (rc < 0) {
3191 dev_err(&led->spmi_dev->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07003192 "Failure reading label, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003193 goto fail_id_check;
Amy Malochef3d5a062012-08-16 19:14:11 -07003194 }
Amy Malochea5ca5552012-10-23 13:34:46 -07003195
3196 rc = of_property_read_string(temp, "linux,name",
3197 &led->cdev.name);
3198 if (rc < 0) {
3199 dev_err(&led->spmi_dev->dev,
3200 "Failure reading led name, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003201 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003202 }
3203
3204 rc = of_property_read_u32(temp, "qcom,max-current",
3205 &led->max_current);
3206 if (rc < 0) {
3207 dev_err(&led->spmi_dev->dev,
3208 "Failure reading max_current, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003209 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003210 }
3211
3212 rc = of_property_read_u32(temp, "qcom,id", &led->id);
3213 if (rc < 0) {
3214 dev_err(&led->spmi_dev->dev,
3215 "Failure reading led id, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003216 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003217 }
3218
3219 rc = qpnp_get_common_configs(led, temp);
3220 if (rc) {
3221 dev_err(&led->spmi_dev->dev,
3222 "Failure reading common led configuration," \
3223 " rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003224 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003225 }
3226
3227 led->cdev.brightness_set = qpnp_led_set;
3228 led->cdev.brightness_get = qpnp_led_get;
3229
3230 if (strncmp(led_label, "wled", sizeof("wled")) == 0) {
3231 rc = qpnp_get_config_wled(led, temp);
3232 if (rc < 0) {
3233 dev_err(&led->spmi_dev->dev,
3234 "Unable to read wled config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003235 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003236 }
Amy Maloche864a6d52012-10-03 15:58:12 -07003237 } else if (strncmp(led_label, "flash", sizeof("flash"))
3238 == 0) {
Chun Zhang0d6ca072013-07-30 21:08:39 -07003239 if (!of_find_property(node, "flash-boost-supply", NULL))
Chun Zhangc3b505b2013-06-03 19:01:49 -07003240 regulator_probe = true;
3241 rc = qpnp_get_config_flash(led, temp, &regulator_probe);
Amy Maloche864a6d52012-10-03 15:58:12 -07003242 if (rc < 0) {
3243 dev_err(&led->spmi_dev->dev,
3244 "Unable to read flash config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003245 goto fail_id_check;
Amy Maloche864a6d52012-10-03 15:58:12 -07003246 }
Amy Malocheeea7b592012-10-03 15:59:36 -07003247 } else if (strncmp(led_label, "rgb", sizeof("rgb")) == 0) {
3248 rc = qpnp_get_config_rgb(led, temp);
3249 if (rc < 0) {
3250 dev_err(&led->spmi_dev->dev,
3251 "Unable to read rgb config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003252 goto fail_id_check;
Amy Malocheeea7b592012-10-03 15:59:36 -07003253 }
Amy Malochef3813742013-04-11 19:33:47 -07003254 } else if (strncmp(led_label, "mpp", sizeof("mpp")) == 0) {
3255 rc = qpnp_get_config_mpp(led, temp);
3256 if (rc < 0) {
3257 dev_err(&led->spmi_dev->dev,
3258 "Unable to read mpp config data\n");
Amy Malochea2726f02013-05-10 10:19:03 -07003259 goto fail_id_check;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303260 }
3261 } else if (strncmp(led_label, "kpdbl", sizeof("kpdbl")) == 0) {
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303262 num_kpbl_leds_on = 0;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303263 rc = qpnp_get_config_kpdbl(led, temp);
3264 if (rc < 0) {
3265 dev_err(&led->spmi_dev->dev,
3266 "Unable to read kpdbl config data\n");
Amy Malochef3813742013-04-11 19:33:47 -07003267 goto fail_id_check;
3268 }
Amy Malochea5ca5552012-10-23 13:34:46 -07003269 } else {
3270 dev_err(&led->spmi_dev->dev, "No LED matching label\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003271 rc = -EINVAL;
3272 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003273 }
3274
Chun Zhang815a1832013-06-20 13:47:13 -07003275 mutex_init(&led->lock);
3276 INIT_WORK(&led->work, qpnp_led_work);
Amy Malochea5ca5552012-10-23 13:34:46 -07003277
3278 rc = qpnp_led_initialize(led);
3279 if (rc < 0)
3280 goto fail_id_check;
3281
3282 rc = qpnp_led_set_max_brightness(led);
3283 if (rc < 0)
3284 goto fail_id_check;
3285
3286 rc = led_classdev_register(&spmi->dev, &led->cdev);
3287 if (rc) {
3288 dev_err(&spmi->dev, "unable to register led %d,rc=%d\n",
3289 led->id, rc);
3290 goto fail_id_check;
3291 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07003292
3293 if (led->id == QPNP_ID_FLASH1_LED0 ||
3294 led->id == QPNP_ID_FLASH1_LED1) {
3295 rc = sysfs_create_group(&led->cdev.dev->kobj,
3296 &led_attr_group);
3297 if (rc)
3298 goto fail_id_check;
3299
3300 }
3301
Amy Malochea5c4ed82013-06-05 11:05:28 -07003302 if (led->id == QPNP_ID_LED_MPP) {
3303 if (!led->mpp_cfg->pwm_cfg)
3304 break;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003305 if (led->mpp_cfg->pwm_cfg->mode == PWM_MODE) {
3306 rc = sysfs_create_group(&led->cdev.dev->kobj,
3307 &pwm_attr_group);
3308 if (rc)
3309 goto fail_id_check;
3310 }
Amy Malochea5c4ed82013-06-05 11:05:28 -07003311 if (led->mpp_cfg->pwm_cfg->use_blink) {
3312 rc = sysfs_create_group(&led->cdev.dev->kobj,
3313 &blink_attr_group);
3314 if (rc)
3315 goto fail_id_check;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003316
3317 rc = sysfs_create_group(&led->cdev.dev->kobj,
3318 &lpg_attr_group);
3319 if (rc)
3320 goto fail_id_check;
3321 } else if (led->mpp_cfg->pwm_cfg->mode == LPG_MODE) {
3322 rc = sysfs_create_group(&led->cdev.dev->kobj,
3323 &lpg_attr_group);
3324 if (rc)
3325 goto fail_id_check;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003326 }
3327 } else if ((led->id == QPNP_ID_RGB_RED) ||
3328 (led->id == QPNP_ID_RGB_GREEN) ||
3329 (led->id == QPNP_ID_RGB_BLUE)) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07003330 if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
3331 rc = sysfs_create_group(&led->cdev.dev->kobj,
3332 &pwm_attr_group);
3333 if (rc)
3334 goto fail_id_check;
3335 }
Amy Malochea5c4ed82013-06-05 11:05:28 -07003336 if (led->rgb_cfg->pwm_cfg->use_blink) {
3337 rc = sysfs_create_group(&led->cdev.dev->kobj,
3338 &blink_attr_group);
3339 if (rc)
3340 goto fail_id_check;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003341
3342 rc = sysfs_create_group(&led->cdev.dev->kobj,
3343 &lpg_attr_group);
3344 if (rc)
3345 goto fail_id_check;
3346 } else if (led->rgb_cfg->pwm_cfg->mode == LPG_MODE) {
3347 rc = sysfs_create_group(&led->cdev.dev->kobj,
3348 &lpg_attr_group);
3349 if (rc)
3350 goto fail_id_check;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003351 }
3352 }
3353
Amy Malochea5ca5552012-10-23 13:34:46 -07003354 /* configure default state */
Asaf Penso55ac8472013-01-21 21:17:37 +02003355 if (led->default_on) {
Amy Malochea5ca5552012-10-23 13:34:46 -07003356 led->cdev.brightness = led->cdev.max_brightness;
Chun Zhang815a1832013-06-20 13:47:13 -07003357 __qpnp_led_work(led, led->cdev.brightness);
Asaf Penso55ac8472013-01-21 21:17:37 +02003358 if (led->turn_off_delay_ms > 0)
3359 qpnp_led_turn_off(led);
3360 } else
Amy Malochea5ca5552012-10-23 13:34:46 -07003361 led->cdev.brightness = LED_OFF;
3362
Amy Malochef9490c62012-11-27 19:26:04 -08003363 parsed_leds++;
Amy Malochef3d5a062012-08-16 19:14:11 -07003364 }
Amy Malochef9490c62012-11-27 19:26:04 -08003365 dev_set_drvdata(&spmi->dev, led_array);
Amy Malochef3d5a062012-08-16 19:14:11 -07003366 return 0;
3367
3368fail_id_check:
Chun Zhang815a1832013-06-20 13:47:13 -07003369 for (i = 0; i < parsed_leds; i++) {
3370 mutex_destroy(&led_array[i].lock);
Amy Malochef9490c62012-11-27 19:26:04 -08003371 led_classdev_unregister(&led_array[i].cdev);
Chun Zhang815a1832013-06-20 13:47:13 -07003372 }
3373
Amy Malochef3d5a062012-08-16 19:14:11 -07003374 return rc;
3375}
3376
3377static int __devexit qpnp_leds_remove(struct spmi_device *spmi)
3378{
Amy Malochef9490c62012-11-27 19:26:04 -08003379 struct qpnp_led_data *led_array = dev_get_drvdata(&spmi->dev);
3380 int i, parsed_leds = led_array->num_leds;
Amy Malochef3d5a062012-08-16 19:14:11 -07003381
Amy Malochebc97c0d22013-03-24 22:06:16 -07003382 for (i = 0; i < parsed_leds; i++) {
Chun Zhang815a1832013-06-20 13:47:13 -07003383 cancel_work_sync(&led_array[i].work);
3384 mutex_destroy(&led_array[i].lock);
Amy Malochef9490c62012-11-27 19:26:04 -08003385 led_classdev_unregister(&led_array[i].cdev);
Amy Malochebc97c0d22013-03-24 22:06:16 -07003386 switch (led_array[i].id) {
3387 case QPNP_ID_WLED:
3388 break;
3389 case QPNP_ID_FLASH1_LED0:
3390 case QPNP_ID_FLASH1_LED1:
Chun Zhangda8ad0f2013-07-17 14:46:47 -07003391 if (led_array[i].flash_cfg->flash_reg_get)
Chun Zhangc3b505b2013-06-03 19:01:49 -07003392 regulator_put(led_array[i].flash_cfg-> \
3393 flash_boost_reg);
Chun Zhangdf2d3062013-06-25 20:14:46 -07003394 if (led_array[i].flash_cfg->torch_enable)
3395 regulator_put(led_array[i].flash_cfg->\
3396 torch_boost_reg);
Amy Malochebc97c0d22013-03-24 22:06:16 -07003397 sysfs_remove_group(&led_array[i].cdev.dev->kobj,
3398 &led_attr_group);
3399 break;
3400 case QPNP_ID_RGB_RED:
3401 case QPNP_ID_RGB_GREEN:
3402 case QPNP_ID_RGB_BLUE:
Amy Maloche4cf9f322013-05-29 15:53:46 -07003403 if (led_array[i].rgb_cfg->pwm_cfg->mode == PWM_MODE)
3404 sysfs_remove_group(&led_array[i].cdev.dev->\
3405 kobj, &pwm_attr_group);
3406 if (led_array[i].rgb_cfg->pwm_cfg->use_blink) {
3407 sysfs_remove_group(&led_array[i].cdev.dev->\
3408 kobj, &blink_attr_group);
3409 sysfs_remove_group(&led_array[i].cdev.dev->\
3410 kobj, &lpg_attr_group);
3411 } else if (led_array[i].rgb_cfg->pwm_cfg->mode\
3412 == LPG_MODE)
3413 sysfs_remove_group(&led_array[i].cdev.dev->\
3414 kobj, &lpg_attr_group);
3415 break;
3416 case QPNP_ID_LED_MPP:
3417 if (!led_array[i].mpp_cfg->pwm_cfg)
3418 break;
3419 if (led_array[i].mpp_cfg->pwm_cfg->mode == PWM_MODE)
3420 sysfs_remove_group(&led_array[i].cdev.dev->\
3421 kobj, &pwm_attr_group);
3422 if (led_array[i].mpp_cfg->pwm_cfg->use_blink) {
3423 sysfs_remove_group(&led_array[i].cdev.dev->\
3424 kobj, &blink_attr_group);
3425 sysfs_remove_group(&led_array[i].cdev.dev->\
3426 kobj, &lpg_attr_group);
3427 } else if (led_array[i].mpp_cfg->pwm_cfg->mode\
3428 == LPG_MODE)
3429 sysfs_remove_group(&led_array[i].cdev.dev->\
3430 kobj, &lpg_attr_group);
3431 break;
Amy Malochebc97c0d22013-03-24 22:06:16 -07003432 default:
3433 dev_err(&led_array[i].spmi_dev->dev,
3434 "Invalid LED(%d)\n",
3435 led_array[i].id);
3436 return -EINVAL;
3437 }
3438 }
Amy Malochef3d5a062012-08-16 19:14:11 -07003439
3440 return 0;
3441}
3442static struct of_device_id spmi_match_table[] = {
3443 { .compatible = "qcom,leds-qpnp",
3444 }
3445};
3446
3447static struct spmi_driver qpnp_leds_driver = {
3448 .driver = {
3449 .name = "qcom,leds-qpnp",
3450 .of_match_table = spmi_match_table,
3451 },
3452 .probe = qpnp_leds_probe,
3453 .remove = __devexit_p(qpnp_leds_remove),
3454};
3455
3456static int __init qpnp_led_init(void)
3457{
3458 return spmi_driver_register(&qpnp_leds_driver);
3459}
3460module_init(qpnp_led_init);
3461
3462static void __exit qpnp_led_exit(void)
3463{
3464 spmi_driver_unregister(&qpnp_leds_driver);
3465}
3466module_exit(qpnp_led_exit);
3467
3468MODULE_DESCRIPTION("QPNP LEDs driver");
3469MODULE_LICENSE("GPL v2");
3470MODULE_ALIAS("leds:leds-qpnp");
Amy Maloche864a6d52012-10-03 15:58:12 -07003471