blob: eba5ca8dfd59db6cdadbcd0354dc3962b679afe0 [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
Abinaya Pb6e3ba42014-01-07 15:32:31 +0530139#define FLASH_HW_SW_STROBE_SEL_MASK 0x04
Amy Malochebc97c0d22013-03-24 22:06:16 -0700140#define FLASH_STROBE_MASK 0xC7
Amy Maloche864a6d52012-10-03 15:58:12 -0700141#define FLASH_LED_0_OUTPUT 0x80
142#define FLASH_LED_1_OUTPUT 0x40
Abinaya Pb6e3ba42014-01-07 15:32:31 +0530143#define FLASH_TORCH_OUTPUT 0xC0
Amy Maloche864a6d52012-10-03 15:58:12 -0700144
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,
Abinaya Pe65d2cd2013-12-23 19:31:57 +0530258 WLED_OVP_27V,
Amy Malochef3d5a062012-08-16 19:14:11 -0700259};
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),
Abinaya P03cee1d2014-02-17 17:51:53 +0530622 WLED_MAX_CURR_MASK, (u8)led->max_current);
Amy Maloche0150b5e2013-08-15 18:18:32 -0700623 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{
Abinaya P03cee1d2014-02-17 17:51:53 +0530692 int rc;
693 u8 val;
Xu Kai9d395ce2014-02-28 13:11:09 +0800694 int duty_us, duty_ns, period_us;
Amy Malochef3813742013-04-11 19:33:47 -0700695
696 if (led->cdev.brightness) {
Chun Zhang874c9ab2013-07-08 17:18:34 -0700697 if (led->cdev.brightness < led->mpp_cfg->min_brightness) {
698 dev_warn(&led->spmi_dev->dev,
699 "brightness is less than supported..." \
700 "set to minimum supported\n");
701 led->cdev.brightness = led->mpp_cfg->min_brightness;
702 }
703
Amy Maloche4cf9f322013-05-29 15:53:46 -0700704 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
705 if (!led->mpp_cfg->pwm_cfg->blinking) {
706 led->mpp_cfg->pwm_cfg->mode =
707 led->mpp_cfg->pwm_cfg->default_mode;
708 led->mpp_cfg->pwm_mode =
709 led->mpp_cfg->pwm_cfg->default_mode;
710 }
711 }
Amy Malochea2726f02013-05-10 10:19:03 -0700712 if (led->mpp_cfg->pwm_mode == PWM_MODE) {
Amy Malochea2726f02013-05-10 10:19:03 -0700713 /*config pwm for brightness scaling*/
Xu Kai9d395ce2014-02-28 13:11:09 +0800714 period_us = led->mpp_cfg->pwm_cfg->pwm_period_us;
715 if (period_us > INT_MAX / NSEC_PER_USEC) {
716 duty_us = (period_us * led->cdev.brightness) /
717 LED_FULL;
718 rc = pwm_config_us(
719 led->mpp_cfg->pwm_cfg->pwm_dev,
Amy Malochea2726f02013-05-10 10:19:03 -0700720 duty_us,
Xu Kai9d395ce2014-02-28 13:11:09 +0800721 period_us);
722 } else {
723 duty_ns = ((period_us * NSEC_PER_USEC) /
724 LED_FULL) * led->cdev.brightness;
725 rc = pwm_config(
726 led->mpp_cfg->pwm_cfg->pwm_dev,
727 duty_ns,
728 period_us * NSEC_PER_USEC);
729 }
Amy Malochea2726f02013-05-10 10:19:03 -0700730 if (rc < 0) {
731 dev_err(&led->spmi_dev->dev, "Failed to " \
732 "configure pwm for new values\n");
733 return rc;
734 }
Amy Malochef3813742013-04-11 19:33:47 -0700735 }
736
Amy Malochea2726f02013-05-10 10:19:03 -0700737 if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
738 pwm_enable(led->mpp_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka38ec7f32013-09-11 13:01:48 +0530739 else {
740 if (led->cdev.brightness < LED_MPP_CURRENT_MIN)
741 led->cdev.brightness = LED_MPP_CURRENT_MIN;
742
743 val = (led->cdev.brightness / LED_MPP_CURRENT_MIN) - 1;
744
745 rc = qpnp_led_masked_write(led,
746 LED_MPP_SINK_CTRL(led->base),
747 LED_MPP_SINK_MASK, val);
748 if (rc) {
749 dev_err(&led->spmi_dev->dev,
750 "Failed to write sink control reg\n");
751 return rc;
752 }
753 }
Amy Malochea2726f02013-05-10 10:19:03 -0700754
Amy Malochece59f662013-05-02 10:59:53 -0700755 val = (led->mpp_cfg->source_sel & LED_MPP_SRC_MASK) |
756 (led->mpp_cfg->mode_ctrl & LED_MPP_MODE_CTRL_MASK);
Amy Malochef3813742013-04-11 19:33:47 -0700757
758 rc = qpnp_led_masked_write(led,
Amy Malochea2726f02013-05-10 10:19:03 -0700759 LED_MPP_MODE_CTRL(led->base), LED_MPP_MODE_MASK,
760 val);
Amy Malochef3813742013-04-11 19:33:47 -0700761 if (rc) {
762 dev_err(&led->spmi_dev->dev,
763 "Failed to write led mode reg\n");
764 return rc;
765 }
766
767 rc = qpnp_led_masked_write(led,
768 LED_MPP_EN_CTRL(led->base), LED_MPP_EN_MASK,
769 LED_MPP_EN_ENABLE);
Amy Malochea2726f02013-05-10 10:19:03 -0700770 if (rc) {
771 dev_err(&led->spmi_dev->dev,
772 "Failed to write led enable " \
773 "reg\n");
774 return rc;
775 }
Amy Malochef3813742013-04-11 19:33:47 -0700776 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -0700777 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
778 led->mpp_cfg->pwm_cfg->mode =
779 led->mpp_cfg->pwm_cfg->default_mode;
780 led->mpp_cfg->pwm_mode =
781 led->mpp_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -0700782 pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
Amy Maloche4cf9f322013-05-29 15:53:46 -0700783 }
Amy Malochef3813742013-04-11 19:33:47 -0700784 rc = qpnp_led_masked_write(led,
785 LED_MPP_MODE_CTRL(led->base),
786 LED_MPP_MODE_MASK,
787 LED_MPP_MODE_DISABLE);
788 if (rc) {
789 dev_err(&led->spmi_dev->dev,
790 "Failed to write led mode reg\n");
791 return rc;
792 }
793
794 rc = qpnp_led_masked_write(led,
795 LED_MPP_EN_CTRL(led->base),
796 LED_MPP_EN_MASK,
797 LED_MPP_EN_DISABLE);
798 if (rc) {
799 dev_err(&led->spmi_dev->dev,
800 "Failed to write led enable reg\n");
801 return rc;
802 }
803 }
804
Amy Maloche4cf9f322013-05-29 15:53:46 -0700805 if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
806 led->mpp_cfg->pwm_cfg->blinking = false;
Amy Malochef3813742013-04-11 19:33:47 -0700807 qpnp_dump_regs(led, mpp_debug_regs, ARRAY_SIZE(mpp_debug_regs));
808
809 return 0;
810}
811
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700812static int qpnp_flash_regulator_operate(struct qpnp_led_data *led, bool on)
813{
814 int rc, i;
815 struct qpnp_led_data *led_array;
816 bool regulator_on = false;
817
818 led_array = dev_get_drvdata(&led->spmi_dev->dev);
819 if (!led_array) {
820 dev_err(&led->spmi_dev->dev,
821 "Unable to get LED array\n");
822 return -EINVAL;
823 }
824
825 for (i = 0; i < led->num_leds; i++)
826 regulator_on |= led_array[i].flash_cfg->flash_on;
827
828 if (!on)
829 goto regulator_turn_off;
830
831 if (!regulator_on && !led->flash_cfg->flash_on) {
832 for (i = 0; i < led->num_leds; i++) {
833 if (led_array[i].flash_cfg->flash_reg_get) {
834 rc = regulator_enable(
835 led_array[i].flash_cfg->\
836 flash_boost_reg);
837 if (rc) {
838 dev_err(&led->spmi_dev->dev,
839 "Regulator enable failed(%d)\n",
840 rc);
841 return rc;
842 }
843 led->flash_cfg->flash_on = true;
844 }
845 break;
846 }
847 }
848
849 return 0;
850
851regulator_turn_off:
852 if (regulator_on && led->flash_cfg->flash_on) {
853 for (i = 0; i < led->num_leds; i++) {
854 if (led_array[i].flash_cfg->flash_reg_get) {
Chun Zhang9d5ff672013-08-01 18:18:26 -0700855 rc = qpnp_led_masked_write(led,
856 FLASH_ENABLE_CONTROL(led->base),
857 FLASH_ENABLE_MASK,
858 FLASH_DISABLE_ALL);
859 if (rc) {
860 dev_err(&led->spmi_dev->dev,
861 "Enable reg write failed(%d)\n",
862 rc);
863 }
864
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700865 rc = regulator_disable(led_array[i].flash_cfg->\
866 flash_boost_reg);
867 if (rc) {
868 dev_err(&led->spmi_dev->dev,
869 "Regulator disable failed(%d)\n",
870 rc);
871 return rc;
872 }
873 led->flash_cfg->flash_on = false;
874 }
875 break;
876 }
877 }
878
879 return 0;
880}
881
Chun Zhangdf2d3062013-06-25 20:14:46 -0700882static int qpnp_torch_regulator_operate(struct qpnp_led_data *led, bool on)
Amy Maloche864a6d52012-10-03 15:58:12 -0700883{
884 int rc;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700885
886 if (!on)
887 goto regulator_turn_off;
888
889 if (!led->flash_cfg->torch_on) {
890 rc = regulator_enable(led->flash_cfg->torch_boost_reg);
891 if (rc) {
892 dev_err(&led->spmi_dev->dev,
893 "Regulator enable failed(%d)\n", rc);
894 return rc;
895 }
896 led->flash_cfg->torch_on = true;
897 }
898 return 0;
899
900regulator_turn_off:
901 if (led->flash_cfg->torch_on) {
Chun Zhang9d5ff672013-08-01 18:18:26 -0700902 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
903 FLASH_ENABLE_MODULE_MASK, FLASH_DISABLE_ALL);
904 if (rc) {
905 dev_err(&led->spmi_dev->dev,
906 "Enable reg write failed(%d)\n", rc);
907 }
908
Chun Zhangdf2d3062013-06-25 20:14:46 -0700909 rc = regulator_disable(led->flash_cfg->torch_boost_reg);
910 if (rc) {
911 dev_err(&led->spmi_dev->dev,
912 "Regulator disable failed(%d)\n", rc);
913 return rc;
914 }
915 led->flash_cfg->torch_on = false;
916 }
917 return 0;
918}
919
920static int qpnp_flash_set(struct qpnp_led_data *led)
921{
922 int rc, error;
Amy Maloche864a6d52012-10-03 15:58:12 -0700923 int val = led->cdev.brightness;
924
Chun Zhang9c1fbaa2013-06-17 15:31:18 -0700925 if (led->flash_cfg->torch_enable)
926 led->flash_cfg->current_prgm =
927 (val * TORCH_MAX_LEVEL / led->max_current);
928 else
929 led->flash_cfg->current_prgm =
930 (val * FLASH_MAX_LEVEL / led->max_current);
Amy Maloche864a6d52012-10-03 15:58:12 -0700931
Amy Maloche864a6d52012-10-03 15:58:12 -0700932 /* Set led current */
933 if (val > 0) {
Amy Malochebc97c0d22013-03-24 22:06:16 -0700934 if (led->flash_cfg->torch_enable) {
Chun Zhang0d6ca072013-07-30 21:08:39 -0700935 if (led->flash_cfg->peripheral_subtype ==
936 FLASH_SUBTYPE_DUAL) {
937 rc = qpnp_torch_regulator_operate(led, true);
938 if (rc) {
939 dev_err(&led->spmi_dev->dev,
Chun Zhangdf2d3062013-06-25 20:14:46 -0700940 "Torch regulator operate failed(%d)\n",
941 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700942 return rc;
943 }
944 } else if (led->flash_cfg->peripheral_subtype ==
945 FLASH_SUBTYPE_SINGLE) {
946 rc = qpnp_flash_regulator_operate(led, true);
947 if (rc) {
948 dev_err(&led->spmi_dev->dev,
949 "Flash regulator operate failed(%d)\n",
950 rc);
951 goto error_flash_set;
952 }
Chun Zhangdf2d3062013-06-25 20:14:46 -0700953 }
954
Amy Malochebc97c0d22013-03-24 22:06:16 -0700955 rc = qpnp_led_masked_write(led,
956 FLASH_LED_UNLOCK_SECURE(led->base),
957 FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
958 if (rc) {
959 dev_err(&led->spmi_dev->dev,
960 "Secure reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700961 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700962 }
963
964 rc = qpnp_led_masked_write(led,
965 FLASH_LED_TORCH(led->base),
966 FLASH_TORCH_MASK, FLASH_LED_TORCH_ENABLE);
967 if (rc) {
968 dev_err(&led->spmi_dev->dev,
969 "Torch reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700970 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700971 }
972
Amy Malochebc97c0d22013-03-24 22:06:16 -0700973 rc = qpnp_led_masked_write(led,
974 led->flash_cfg->current_addr,
Chun Zhange8954cf2013-05-02 11:14:34 -0700975 FLASH_CURRENT_MASK,
976 led->flash_cfg->current_prgm);
Amy Malochebc97c0d22013-03-24 22:06:16 -0700977 if (rc) {
978 dev_err(&led->spmi_dev->dev,
979 "Current reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700980 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700981 }
982
983 rc = qpnp_led_masked_write(led,
984 led->flash_cfg->second_addr,
Chun Zhange8954cf2013-05-02 11:14:34 -0700985 FLASH_CURRENT_MASK,
986 led->flash_cfg->current_prgm);
Amy Malochebc97c0d22013-03-24 22:06:16 -0700987 if (rc) {
988 dev_err(&led->spmi_dev->dev,
989 "2nd Current reg write failed(%d)\n",
990 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700991 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700992 }
993
Chun Zhange8954cf2013-05-02 11:14:34 -0700994 qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
995 FLASH_CURRENT_MASK,
Chun Zhang9c1fbaa2013-06-17 15:31:18 -0700996 TORCH_MAX_LEVEL);
Chun Zhange8954cf2013-05-02 11:14:34 -0700997 if (rc) {
998 dev_err(&led->spmi_dev->dev,
999 "Max current reg write failed(%d)\n",
1000 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -07001001 goto error_reg_write;
Chun Zhange8954cf2013-05-02 11:14:34 -07001002 }
1003
Chun Zhang9d5ff672013-08-01 18:18:26 -07001004 rc = qpnp_led_masked_write(led,
1005 FLASH_ENABLE_CONTROL(led->base),
1006 FLASH_ENABLE_MASK,
1007 led->flash_cfg->enable_module);
1008 if (rc) {
1009 dev_err(&led->spmi_dev->dev,
1010 "Enable reg write failed(%d)\n",
1011 rc);
1012 goto error_reg_write;
1013 }
1014
Abinaya Pb6e3ba42014-01-07 15:32:31 +05301015 if (!led->flash_cfg->strobe_type)
1016 led->flash_cfg->trigger_flash &=
1017 ~FLASH_HW_SW_STROBE_SEL_MASK;
1018 else
1019 led->flash_cfg->trigger_flash |=
1020 FLASH_HW_SW_STROBE_SEL_MASK;
1021
Chun Zhang9d5ff672013-08-01 18:18:26 -07001022 rc = qpnp_led_masked_write(led,
1023 FLASH_LED_STROBE_CTRL(led->base),
1024 led->flash_cfg->trigger_flash,
1025 led->flash_cfg->trigger_flash);
1026 if (rc) {
1027 dev_err(&led->spmi_dev->dev,
1028 "LED %d strobe reg write failed(%d)\n",
1029 led->id, rc);
1030 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001031 }
1032 } else {
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001033 rc = qpnp_flash_regulator_operate(led, true);
1034 if (rc) {
1035 dev_err(&led->spmi_dev->dev,
1036 "Flash regulator operate failed(%d)\n",
1037 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001038 goto error_flash_set;
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001039 }
1040
Chun Zhange8954cf2013-05-02 11:14:34 -07001041 /* Set flash safety timer */
Amy Malochebc97c0d22013-03-24 22:06:16 -07001042 rc = qpnp_led_masked_write(led,
Chun Zhange8954cf2013-05-02 11:14:34 -07001043 FLASH_SAFETY_TIMER(led->base),
1044 FLASH_SAFETY_TIMER_MASK,
1045 led->flash_cfg->duration);
1046 if (rc) {
1047 dev_err(&led->spmi_dev->dev,
1048 "Safety timer reg write failed(%d)\n",
1049 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001050 goto error_flash_set;
Chun Zhange8954cf2013-05-02 11:14:34 -07001051 }
1052
1053 /* Set max current */
1054 rc = qpnp_led_masked_write(led,
1055 FLASH_MAX_CURR(led->base), FLASH_CURRENT_MASK,
1056 FLASH_MAX_LEVEL);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001057 if (rc) {
1058 dev_err(&led->spmi_dev->dev,
1059 "Max current reg write failed(%d)\n",
1060 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001061 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001062 }
1063
Chun Zhange8954cf2013-05-02 11:14:34 -07001064 /* Set clamp current */
1065 rc = qpnp_led_masked_write(led,
1066 FLASH_CLAMP_CURR(led->base),
1067 FLASH_CURRENT_MASK,
1068 led->flash_cfg->clamp_curr);
1069 if (rc) {
1070 dev_err(&led->spmi_dev->dev,
1071 "Clamp current reg write failed(%d)\n",
1072 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001073 goto error_flash_set;
Chun Zhange8954cf2013-05-02 11:14:34 -07001074 }
1075
Amy Malochebc97c0d22013-03-24 22:06:16 -07001076 rc = qpnp_led_masked_write(led,
1077 led->flash_cfg->current_addr,
1078 FLASH_CURRENT_MASK,
1079 led->flash_cfg->current_prgm);
1080 if (rc) {
1081 dev_err(&led->spmi_dev->dev,
1082 "Current reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001083 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001084 }
1085
1086 rc = qpnp_led_masked_write(led,
Amy Malochebc97c0d22013-03-24 22:06:16 -07001087 FLASH_ENABLE_CONTROL(led->base),
Chun Zhang9d5ff672013-08-01 18:18:26 -07001088 led->flash_cfg->enable_module,
1089 led->flash_cfg->enable_module);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001090 if (rc) {
1091 dev_err(&led->spmi_dev->dev,
1092 "Enable reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001093 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001094 }
Amy Maloche38b9aae2013-02-07 12:15:34 -08001095
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07001096 /*
1097 * Add 1ms delay for bharger enter stable state
1098 */
1099 usleep(FLASH_RAMP_UP_DELAY_US);
1100
Abinaya Pb6e3ba42014-01-07 15:32:31 +05301101 if (!led->flash_cfg->strobe_type)
1102 led->flash_cfg->trigger_flash &=
1103 ~FLASH_HW_SW_STROBE_SEL_MASK;
1104 else
1105 led->flash_cfg->trigger_flash |=
1106 FLASH_HW_SW_STROBE_SEL_MASK;
1107
1108 rc = qpnp_led_masked_write(led,
1109 FLASH_LED_STROBE_CTRL(led->base),
1110 led->flash_cfg->trigger_flash,
1111 led->flash_cfg->trigger_flash);
1112 if (rc) {
1113 dev_err(&led->spmi_dev->dev,
1114 "LED %d strobe reg write failed(%d)\n",
1115 led->id, rc);
1116 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001117 }
Amy Maloche38b9aae2013-02-07 12:15:34 -08001118 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001119 } else {
Chun Zhangdf2d3062013-06-25 20:14:46 -07001120 rc = qpnp_led_masked_write(led,
1121 FLASH_LED_STROBE_CTRL(led->base),
Chun Zhang9d5ff672013-08-01 18:18:26 -07001122 led->flash_cfg->trigger_flash,
Chun Zhangdf2d3062013-06-25 20:14:46 -07001123 FLASH_DISABLE_ALL);
1124 if (rc) {
1125 dev_err(&led->spmi_dev->dev,
1126 "LED %d flash write failed(%d)\n", led->id, rc);
1127 if (led->flash_cfg->torch_enable)
1128 goto error_torch_set;
1129 else
1130 goto error_flash_set;
1131 }
1132
Amy Malochebc97c0d22013-03-24 22:06:16 -07001133 if (led->flash_cfg->torch_enable) {
1134 rc = qpnp_led_masked_write(led,
1135 FLASH_LED_UNLOCK_SECURE(led->base),
1136 FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
1137 if (rc) {
1138 dev_err(&led->spmi_dev->dev,
1139 "Secure reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001140 goto error_torch_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001141 }
1142
1143 rc = qpnp_led_masked_write(led,
Chun Zhange8954cf2013-05-02 11:14:34 -07001144 FLASH_LED_TORCH(led->base),
1145 FLASH_TORCH_MASK,
1146 FLASH_LED_TORCH_DISABLE);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001147 if (rc) {
1148 dev_err(&led->spmi_dev->dev,
Chun Zhange8954cf2013-05-02 11:14:34 -07001149 "Torch reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001150 goto error_torch_set;
1151 }
1152
Chun Zhang0d6ca072013-07-30 21:08:39 -07001153 if (led->flash_cfg->peripheral_subtype ==
1154 FLASH_SUBTYPE_DUAL) {
1155 rc = qpnp_torch_regulator_operate(led, false);
1156 if (rc) {
1157 dev_err(&led->spmi_dev->dev,
1158 "Torch regulator operate failed(%d)\n",
1159 rc);
1160 return rc;
1161 }
1162 } else if (led->flash_cfg->peripheral_subtype ==
1163 FLASH_SUBTYPE_SINGLE) {
1164 rc = qpnp_flash_regulator_operate(led, false);
1165 if (rc) {
1166 dev_err(&led->spmi_dev->dev,
1167 "Flash regulator operate failed(%d)\n",
1168 rc);
1169 return rc;
1170 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07001171 }
Chun Zhangdf2d3062013-06-25 20:14:46 -07001172 } else {
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07001173 /*
1174 * Disable module after ramp down complete for stable
1175 * behavior
1176 */
1177 usleep(FLASH_RAMP_DN_DELAY_US);
1178
Chun Zhang9d5ff672013-08-01 18:18:26 -07001179 rc = qpnp_led_masked_write(led,
1180 FLASH_ENABLE_CONTROL(led->base),
1181 led->flash_cfg->enable_module &
1182 ~FLASH_ENABLE_MODULE_MASK,
1183 FLASH_DISABLE_ALL);
1184 if (rc) {
1185 dev_err(&led->spmi_dev->dev,
1186 "Enable reg write failed(%d)\n", rc);
1187 if (led->flash_cfg->torch_enable)
1188 goto error_torch_set;
1189 else
1190 goto error_flash_set;
1191 }
1192
Chun Zhangdf2d3062013-06-25 20:14:46 -07001193 rc = qpnp_flash_regulator_operate(led, false);
1194 if (rc) {
1195 dev_err(&led->spmi_dev->dev,
1196 "Flash regulator operate failed(%d)\n",
1197 rc);
1198 return rc;
1199 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001200 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001201 }
1202
Amy Malocheeea7b592012-10-03 15:59:36 -07001203 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
1204
1205 return 0;
Chun Zhangdf2d3062013-06-25 20:14:46 -07001206
Chun Zhang0d6ca072013-07-30 21:08:39 -07001207error_reg_write:
1208 if (led->flash_cfg->peripheral_subtype == FLASH_SUBTYPE_SINGLE)
1209 goto error_flash_set;
1210
Chun Zhangdf2d3062013-06-25 20:14:46 -07001211error_torch_set:
1212 error = qpnp_torch_regulator_operate(led, false);
1213 if (error) {
1214 dev_err(&led->spmi_dev->dev,
1215 "Torch regulator operate failed(%d)\n", rc);
1216 return error;
1217 }
1218 return rc;
1219
1220error_flash_set:
1221 error = qpnp_flash_regulator_operate(led, false);
1222 if (error) {
1223 dev_err(&led->spmi_dev->dev,
1224 "Flash regulator operate failed(%d)\n", rc);
1225 return error;
1226 }
1227 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07001228}
1229
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301230static int qpnp_kpdbl_set(struct qpnp_led_data *led)
1231{
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301232 int rc;
Xu Kai9d395ce2014-02-28 13:11:09 +08001233 int duty_us, duty_ns, period_us;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301234
1235 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001236 if (!led->kpdbl_cfg->pwm_cfg->blinking)
1237 led->kpdbl_cfg->pwm_cfg->mode =
1238 led->kpdbl_cfg->pwm_cfg->default_mode;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301239 if (!num_kpbl_leds_on) {
1240 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
1241 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
1242 if (rc) {
1243 dev_err(&led->spmi_dev->dev,
1244 "Enable reg write failed(%d)\n", rc);
1245 return rc;
1246 }
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301247 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301248
1249 if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
Xu Kai9d395ce2014-02-28 13:11:09 +08001250 period_us = led->kpdbl_cfg->pwm_cfg->pwm_period_us;
1251 if (period_us > INT_MAX / NSEC_PER_USEC) {
1252 duty_us = (period_us * led->cdev.brightness) /
1253 KPDBL_MAX_LEVEL;
1254 rc = pwm_config_us(
1255 led->kpdbl_cfg->pwm_cfg->pwm_dev,
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301256 duty_us,
Xu Kai9d395ce2014-02-28 13:11:09 +08001257 period_us);
1258 } else {
1259 duty_ns = ((period_us * NSEC_PER_USEC) /
1260 KPDBL_MAX_LEVEL) * led->cdev.brightness;
1261 rc = pwm_config(
1262 led->kpdbl_cfg->pwm_cfg->pwm_dev,
1263 duty_ns,
1264 period_us * NSEC_PER_USEC);
1265 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301266 if (rc < 0) {
1267 dev_err(&led->spmi_dev->dev, "pwm config failed\n");
1268 return rc;
1269 }
1270 }
1271
Amy Malochea2726f02013-05-10 10:19:03 -07001272 rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301273 if (rc < 0) {
1274 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1275 return rc;
1276 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301277
1278 num_kpbl_leds_on++;
1279
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301280 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001281 led->kpdbl_cfg->pwm_cfg->mode =
1282 led->kpdbl_cfg->pwm_cfg->default_mode;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301283
1284 if (led->kpdbl_cfg->always_on) {
Xu Kai0d9a3e22013-12-17 13:53:56 +08001285 rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0,
Xu Kai9d395ce2014-02-28 13:11:09 +08001286 led->kpdbl_cfg->pwm_cfg->pwm_period_us);
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301287 if (rc < 0) {
1288 dev_err(&led->spmi_dev->dev,
1289 "pwm config failed\n");
1290 return rc;
1291 }
1292
1293 rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
1294 if (rc < 0) {
1295 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1296 return rc;
1297 }
1298 } else
1299 pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
1300
1301 if (num_kpbl_leds_on > 0)
1302 num_kpbl_leds_on--;
1303
1304 if (!num_kpbl_leds_on) {
1305 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
1306 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_DIS);
1307 if (rc) {
1308 dev_err(&led->spmi_dev->dev,
1309 "Failed to write led enable reg\n");
1310 return rc;
1311 }
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301312 }
1313 }
1314
Amy Maloche4cf9f322013-05-29 15:53:46 -07001315 led->kpdbl_cfg->pwm_cfg->blinking = false;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301316
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301317 qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
1318
1319 return 0;
1320}
1321
Amy Malocheeea7b592012-10-03 15:59:36 -07001322static int qpnp_rgb_set(struct qpnp_led_data *led)
1323{
Amy Malocheeea7b592012-10-03 15:59:36 -07001324 int rc;
Xu Kai9d395ce2014-02-28 13:11:09 +08001325 int duty_us, duty_ns, period_us;
Amy Malocheeea7b592012-10-03 15:59:36 -07001326
1327 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001328 if (!led->rgb_cfg->pwm_cfg->blinking)
1329 led->rgb_cfg->pwm_cfg->mode =
1330 led->rgb_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07001331 if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
Xu Kai9d395ce2014-02-28 13:11:09 +08001332 period_us = led->rgb_cfg->pwm_cfg->pwm_period_us;
1333 if (period_us > INT_MAX / NSEC_PER_USEC) {
1334 duty_us = (period_us * led->cdev.brightness) /
1335 LED_FULL;
1336 rc = pwm_config_us(
1337 led->rgb_cfg->pwm_cfg->pwm_dev,
Xu Kai0d9a3e22013-12-17 13:53:56 +08001338 duty_us,
Xu Kai9d395ce2014-02-28 13:11:09 +08001339 period_us);
1340 } else {
1341 duty_ns = ((period_us * NSEC_PER_USEC) /
1342 LED_FULL) * led->cdev.brightness;
1343 rc = pwm_config(
1344 led->rgb_cfg->pwm_cfg->pwm_dev,
1345 duty_ns,
1346 period_us * NSEC_PER_USEC);
1347 }
Amy Malocheeea7b592012-10-03 15:59:36 -07001348 if (rc < 0) {
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301349 dev_err(&led->spmi_dev->dev,
1350 "pwm config failed\n");
Amy Malocheeea7b592012-10-03 15:59:36 -07001351 return rc;
1352 }
1353 }
1354 rc = qpnp_led_masked_write(led,
1355 RGB_LED_EN_CTL(led->base),
1356 led->rgb_cfg->enable, led->rgb_cfg->enable);
1357 if (rc) {
1358 dev_err(&led->spmi_dev->dev,
1359 "Failed to write led enable reg\n");
1360 return rc;
1361 }
Amy Malochea2726f02013-05-10 10:19:03 -07001362
1363 rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301364 if (rc < 0) {
1365 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1366 return rc;
1367 }
Amy Malocheeea7b592012-10-03 15:59:36 -07001368 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001369 led->rgb_cfg->pwm_cfg->mode =
1370 led->rgb_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07001371 pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
Amy Malocheeea7b592012-10-03 15:59:36 -07001372 rc = qpnp_led_masked_write(led,
1373 RGB_LED_EN_CTL(led->base),
1374 led->rgb_cfg->enable, RGB_LED_DISABLE);
1375 if (rc) {
1376 dev_err(&led->spmi_dev->dev,
1377 "Failed to write led enable reg\n");
1378 return rc;
1379 }
1380 }
1381
Amy Maloche4cf9f322013-05-29 15:53:46 -07001382 led->rgb_cfg->pwm_cfg->blinking = false;
Amy Malocheeea7b592012-10-03 15:59:36 -07001383 qpnp_dump_regs(led, rgb_pwm_debug_regs, ARRAY_SIZE(rgb_pwm_debug_regs));
1384
Amy Maloche864a6d52012-10-03 15:58:12 -07001385 return 0;
1386}
1387
Amy Malochef3d5a062012-08-16 19:14:11 -07001388static void qpnp_led_set(struct led_classdev *led_cdev,
1389 enum led_brightness value)
1390{
Amy Malochef3d5a062012-08-16 19:14:11 -07001391 struct qpnp_led_data *led;
1392
1393 led = container_of(led_cdev, struct qpnp_led_data, cdev);
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301394 if (value < LED_OFF) {
Amy Malochea5ca5552012-10-23 13:34:46 -07001395 dev_err(&led->spmi_dev->dev, "Invalid brightness value\n");
Amy Malochef3d5a062012-08-16 19:14:11 -07001396 return;
1397 }
1398
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301399 if (value > led->cdev.max_brightness)
1400 value = led->cdev.max_brightness;
1401
Chun Zhang815a1832013-06-20 13:47:13 -07001402 led->cdev.brightness = value;
1403 schedule_work(&led->work);
1404}
1405
1406static void __qpnp_led_work(struct qpnp_led_data *led,
1407 enum led_brightness value)
1408{
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001409 int rc;
Chun Zhangc3b505b2013-06-03 19:01:49 -07001410
Chun Zhang815a1832013-06-20 13:47:13 -07001411 mutex_lock(&led->lock);
Amy Malochef3d5a062012-08-16 19:14:11 -07001412
1413 switch (led->id) {
1414 case QPNP_ID_WLED:
1415 rc = qpnp_wled_set(led);
1416 if (rc < 0)
Amy Malochea5ca5552012-10-23 13:34:46 -07001417 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -07001418 "WLED set brightness failed (%d)\n", rc);
1419 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07001420 case QPNP_ID_FLASH1_LED0:
1421 case QPNP_ID_FLASH1_LED1:
1422 rc = qpnp_flash_set(led);
1423 if (rc < 0)
1424 dev_err(&led->spmi_dev->dev,
1425 "FLASH set brightness failed (%d)\n", rc);
1426 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07001427 case QPNP_ID_RGB_RED:
1428 case QPNP_ID_RGB_GREEN:
1429 case QPNP_ID_RGB_BLUE:
1430 rc = qpnp_rgb_set(led);
1431 if (rc < 0)
1432 dev_err(&led->spmi_dev->dev,
1433 "RGB set brightness failed (%d)\n", rc);
1434 break;
Amy Malochef3813742013-04-11 19:33:47 -07001435 case QPNP_ID_LED_MPP:
1436 rc = qpnp_mpp_set(led);
1437 if (rc < 0)
1438 dev_err(&led->spmi_dev->dev,
1439 "MPP set brightness failed (%d)\n", rc);
Amy Maloche14c9eb32013-05-06 13:37:58 -07001440 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301441 case QPNP_ID_KPDBL:
1442 rc = qpnp_kpdbl_set(led);
1443 if (rc < 0)
1444 dev_err(&led->spmi_dev->dev,
1445 "KPDBL set brightness failed (%d)\n", rc);
Amy Malochef3813742013-04-11 19:33:47 -07001446 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07001447 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07001448 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -07001449 break;
1450 }
Chun Zhang815a1832013-06-20 13:47:13 -07001451 mutex_unlock(&led->lock);
Chun Zhangc3b505b2013-06-03 19:01:49 -07001452
Amy Malochef3d5a062012-08-16 19:14:11 -07001453}
1454
Chun Zhang815a1832013-06-20 13:47:13 -07001455static void qpnp_led_work(struct work_struct *work)
1456{
1457 struct qpnp_led_data *led = container_of(work,
1458 struct qpnp_led_data, work);
1459
1460 __qpnp_led_work(led, led->cdev.brightness);
1461
1462 return;
1463}
1464
Amy Malochef3d5a062012-08-16 19:14:11 -07001465static int __devinit qpnp_led_set_max_brightness(struct qpnp_led_data *led)
1466{
1467 switch (led->id) {
1468 case QPNP_ID_WLED:
1469 led->cdev.max_brightness = WLED_MAX_LEVEL;
1470 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07001471 case QPNP_ID_FLASH1_LED0:
1472 case QPNP_ID_FLASH1_LED1:
1473 led->cdev.max_brightness = led->max_current;
1474 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07001475 case QPNP_ID_RGB_RED:
1476 case QPNP_ID_RGB_GREEN:
1477 case QPNP_ID_RGB_BLUE:
1478 led->cdev.max_brightness = RGB_MAX_LEVEL;
1479 break;
Amy Malochef3813742013-04-11 19:33:47 -07001480 case QPNP_ID_LED_MPP:
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05301481 if (led->mpp_cfg->pwm_mode == MANUAL_MODE)
1482 led->cdev.max_brightness = led->max_current;
1483 else
1484 led->cdev.max_brightness = MPP_MAX_LEVEL;
Amy Malochef3813742013-04-11 19:33:47 -07001485 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301486 case QPNP_ID_KPDBL:
1487 led->cdev.max_brightness = KPDBL_MAX_LEVEL;
1488 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07001489 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07001490 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -07001491 return -EINVAL;
1492 }
1493
1494 return 0;
1495}
1496
1497static enum led_brightness qpnp_led_get(struct led_classdev *led_cdev)
1498{
1499 struct qpnp_led_data *led;
1500
1501 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1502
1503 return led->cdev.brightness;
1504}
1505
Asaf Penso55ac8472013-01-21 21:17:37 +02001506static void qpnp_led_turn_off_delayed(struct work_struct *work)
1507{
1508 struct delayed_work *dwork = to_delayed_work(work);
1509 struct qpnp_led_data *led
1510 = container_of(dwork, struct qpnp_led_data, dwork);
1511
1512 led->cdev.brightness = LED_OFF;
1513 qpnp_led_set(&led->cdev, led->cdev.brightness);
1514}
1515
1516static void qpnp_led_turn_off(struct qpnp_led_data *led)
1517{
1518 INIT_DELAYED_WORK(&led->dwork, qpnp_led_turn_off_delayed);
1519 schedule_delayed_work(&led->dwork,
1520 msecs_to_jiffies(led->turn_off_delay_ms));
1521}
1522
Amy Malochef3d5a062012-08-16 19:14:11 -07001523static int __devinit qpnp_wled_init(struct qpnp_led_data *led)
1524{
1525 int rc, i;
1526 u8 num_wled_strings;
1527
1528 num_wled_strings = led->wled_cfg->num_strings;
1529
1530 /* verify ranges */
Abinaya Pe65d2cd2013-12-23 19:31:57 +05301531 if (led->wled_cfg->ovp_val > WLED_OVP_27V) {
Amy Malochef3d5a062012-08-16 19:14:11 -07001532 dev_err(&led->spmi_dev->dev, "Invalid ovp value\n");
1533 return -EINVAL;
1534 }
1535
1536 if (led->wled_cfg->boost_curr_lim > WLED_CURR_LIMIT_1680mA) {
1537 dev_err(&led->spmi_dev->dev, "Invalid boost current limit\n");
1538 return -EINVAL;
1539 }
1540
1541 if (led->wled_cfg->cp_select > WLED_CP_SELECT_MAX) {
1542 dev_err(&led->spmi_dev->dev, "Invalid pole capacitance\n");
1543 return -EINVAL;
1544 }
1545
1546 if ((led->max_current > WLED_MAX_CURR)) {
1547 dev_err(&led->spmi_dev->dev, "Invalid max current\n");
1548 return -EINVAL;
1549 }
1550
1551 if ((led->wled_cfg->ctrl_delay_us % WLED_CTL_DLY_STEP) ||
1552 (led->wled_cfg->ctrl_delay_us > WLED_CTL_DLY_MAX)) {
1553 dev_err(&led->spmi_dev->dev, "Invalid control delay\n");
1554 return -EINVAL;
1555 }
1556
1557 /* program over voltage protection threshold */
1558 rc = qpnp_led_masked_write(led, WLED_OVP_CFG_REG(led->base),
1559 WLED_OVP_VAL_MASK,
1560 (led->wled_cfg->ovp_val << WLED_OVP_VAL_BIT_SHFT));
1561 if (rc) {
1562 dev_err(&led->spmi_dev->dev,
1563 "WLED OVP reg write failed(%d)\n", rc);
1564 return rc;
1565 }
1566
1567 /* program current boost limit */
1568 rc = qpnp_led_masked_write(led, WLED_BOOST_LIMIT_REG(led->base),
1569 WLED_BOOST_LIMIT_MASK, led->wled_cfg->boost_curr_lim);
1570 if (rc) {
1571 dev_err(&led->spmi_dev->dev,
1572 "WLED boost limit reg write failed(%d)\n", rc);
1573 return rc;
1574 }
1575
1576 /* program output feedback */
1577 rc = qpnp_led_masked_write(led, WLED_FDBCK_CTRL_REG(led->base),
1578 WLED_OP_FDBCK_MASK,
1579 (led->wled_cfg->op_fdbck << WLED_OP_FDBCK_BIT_SHFT));
1580 if (rc) {
1581 dev_err(&led->spmi_dev->dev,
1582 "WLED fdbck ctrl reg write failed(%d)\n", rc);
1583 return rc;
1584 }
1585
1586 /* program switch frequency */
Amy Maloche9eccb4c2013-07-12 14:31:56 -07001587 rc = qpnp_led_masked_write(led,
1588 WLED_SWITCHING_FREQ_REG(led->base),
Amy Malochef3d5a062012-08-16 19:14:11 -07001589 WLED_SWITCH_FREQ_MASK, led->wled_cfg->switch_freq);
1590 if (rc) {
1591 dev_err(&led->spmi_dev->dev,
Amy Maloche9eccb4c2013-07-12 14:31:56 -07001592 "WLED switch freq reg write failed(%d)\n", rc);
Amy Malochef3d5a062012-08-16 19:14:11 -07001593 return rc;
1594 }
1595
1596 /* program current sink */
1597 if (led->wled_cfg->cs_out_en) {
1598 rc = qpnp_led_masked_write(led, WLED_CURR_SINK_REG(led->base),
1599 WLED_CURR_SINK_MASK,
Amy Maloche832d90a2013-01-07 10:15:29 -08001600 (((1 << led->wled_cfg->num_strings) - 1)
1601 << WLED_CURR_SINK_SHFT));
Amy Malochef3d5a062012-08-16 19:14:11 -07001602 if (rc) {
1603 dev_err(&led->spmi_dev->dev,
1604 "WLED curr sink reg write failed(%d)\n", rc);
1605 return rc;
1606 }
1607 }
1608
1609 /* program high pole capacitance */
1610 rc = qpnp_led_masked_write(led, WLED_HIGH_POLE_CAP_REG(led->base),
1611 WLED_CP_SELECT_MASK, led->wled_cfg->cp_select);
1612 if (rc) {
1613 dev_err(&led->spmi_dev->dev,
1614 "WLED pole cap reg write failed(%d)\n", rc);
1615 return rc;
1616 }
1617
1618 /* program modulator, current mod src and cabc */
1619 for (i = 0; i < num_wled_strings; i++) {
1620 rc = qpnp_led_masked_write(led, WLED_MOD_EN_REG(led->base, i),
1621 WLED_NO_MASK, WLED_EN_MASK);
1622 if (rc) {
1623 dev_err(&led->spmi_dev->dev,
1624 "WLED mod enable reg write failed(%d)\n", rc);
1625 return rc;
1626 }
1627
1628 if (led->wled_cfg->dig_mod_gen_en) {
1629 rc = qpnp_led_masked_write(led,
Amy Maloched44516e2013-02-14 17:36:34 -08001630 WLED_MOD_SRC_SEL_REG(led->base, i),
Amy Malochef3d5a062012-08-16 19:14:11 -07001631 WLED_NO_MASK, WLED_USE_EXT_GEN_MOD_SRC);
1632 if (rc) {
1633 dev_err(&led->spmi_dev->dev,
1634 "WLED dig mod en reg write failed(%d)\n", rc);
1635 }
1636 }
1637
1638 rc = qpnp_led_masked_write(led,
1639 WLED_FULL_SCALE_REG(led->base, i), WLED_MAX_CURR_MASK,
Abinaya P03cee1d2014-02-17 17:51:53 +05301640 (u8)led->max_current);
Amy Malochef3d5a062012-08-16 19:14:11 -07001641 if (rc) {
1642 dev_err(&led->spmi_dev->dev,
1643 "WLED max current reg write failed(%d)\n", rc);
1644 return rc;
1645 }
1646
1647 }
1648
Sarada Prasanna Garnayak40f65022013-11-01 20:17:32 +05301649 /* Reset WLED enable register */
1650 rc = qpnp_led_masked_write(led, WLED_MOD_CTRL_REG(led->base),
1651 WLED_8_BIT_MASK, WLED_BOOST_OFF);
1652 if (rc) {
1653 dev_err(&led->spmi_dev->dev,
1654 "WLED write ctrl reg failed(%d)\n", rc);
1655 return rc;
1656 }
1657
Amy Malochef3d5a062012-08-16 19:14:11 -07001658 /* dump wled registers */
Amy Malochea5ca5552012-10-23 13:34:46 -07001659 qpnp_dump_regs(led, wled_debug_regs, ARRAY_SIZE(wled_debug_regs));
Amy Malochef3d5a062012-08-16 19:14:11 -07001660
1661 return 0;
1662}
1663
Amy Malochebc97c0d22013-03-24 22:06:16 -07001664static ssize_t led_mode_store(struct device *dev,
1665 struct device_attribute *attr,
1666 const char *buf, size_t count)
1667{
1668 struct qpnp_led_data *led;
1669 unsigned long state;
1670 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1671 ssize_t ret = -EINVAL;
1672
1673 ret = kstrtoul(buf, 10, &state);
1674 if (ret)
1675 return ret;
1676
1677 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1678
1679 /* '1' to enable torch mode; '0' to switch to flash mode */
1680 if (state == 1)
1681 led->flash_cfg->torch_enable = true;
1682 else
1683 led->flash_cfg->torch_enable = false;
1684
1685 return count;
1686}
1687
1688static ssize_t led_strobe_type_store(struct device *dev,
1689 struct device_attribute *attr,
1690 const char *buf, size_t count)
1691{
1692 struct qpnp_led_data *led;
1693 unsigned long state;
1694 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1695 ssize_t ret = -EINVAL;
1696
1697 ret = kstrtoul(buf, 10, &state);
1698 if (ret)
1699 return ret;
1700
1701 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1702
1703 /* '0' for sw strobe; '1' for hw strobe */
1704 if (state == 1)
1705 led->flash_cfg->strobe_type = 1;
1706 else
1707 led->flash_cfg->strobe_type = 0;
1708
1709 return count;
1710}
1711
Amy Malochea5c4ed82013-06-05 11:05:28 -07001712static int qpnp_pwm_init(struct pwm_config_data *pwm_cfg,
1713 struct spmi_device *spmi_dev,
1714 const char *name)
1715{
1716 int rc, start_idx, idx_len;
1717
1718 if (pwm_cfg->pwm_channel != -1) {
1719 pwm_cfg->pwm_dev =
1720 pwm_request(pwm_cfg->pwm_channel, name);
1721
1722 if (IS_ERR_OR_NULL(pwm_cfg->pwm_dev)) {
1723 dev_err(&spmi_dev->dev,
1724 "could not acquire PWM Channel %d, " \
1725 "error %ld\n",
1726 pwm_cfg->pwm_channel,
1727 PTR_ERR(pwm_cfg->pwm_dev));
1728 pwm_cfg->pwm_dev = NULL;
1729 return -ENODEV;
1730 }
1731
1732 if (pwm_cfg->mode == LPG_MODE) {
1733 start_idx =
1734 pwm_cfg->duty_cycles->start_idx;
1735 idx_len =
1736 pwm_cfg->duty_cycles->num_duty_pcts;
1737
1738 if (idx_len >= PWM_LUT_MAX_SIZE &&
1739 start_idx) {
1740 dev_err(&spmi_dev->dev,
1741 "Wrong LUT size or index\n");
1742 return -EINVAL;
1743 }
1744 if ((start_idx + idx_len) >
1745 PWM_LUT_MAX_SIZE) {
1746 dev_err(&spmi_dev->dev,
1747 "Exceed LUT limit\n");
1748 return -EINVAL;
1749 }
1750 rc = pwm_lut_config(pwm_cfg->pwm_dev,
1751 PM_PWM_PERIOD_MIN, /* ignored by hardware */
1752 pwm_cfg->duty_cycles->duty_pcts,
1753 pwm_cfg->lut_params);
1754 if (rc < 0) {
1755 dev_err(&spmi_dev->dev, "Failed to " \
1756 "configure pwm LUT\n");
1757 return rc;
1758 }
1759 }
1760 } else {
1761 dev_err(&spmi_dev->dev,
1762 "Invalid PWM channel\n");
1763 return -EINVAL;
1764 }
1765
1766 return 0;
1767}
1768
Amy Maloche4cf9f322013-05-29 15:53:46 -07001769static ssize_t pwm_us_store(struct device *dev,
1770 struct device_attribute *attr,
1771 const char *buf, size_t count)
1772{
1773 struct qpnp_led_data *led;
1774 u32 pwm_us;
1775 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1776 ssize_t ret;
1777 u32 previous_pwm_us;
1778 struct pwm_config_data *pwm_cfg;
1779
1780 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1781
1782 ret = kstrtou32(buf, 10, &pwm_us);
1783 if (ret)
1784 return ret;
1785
1786 switch (led->id) {
1787 case QPNP_ID_LED_MPP:
1788 pwm_cfg = led->mpp_cfg->pwm_cfg;
1789 break;
1790 case QPNP_ID_RGB_RED:
1791 case QPNP_ID_RGB_GREEN:
1792 case QPNP_ID_RGB_BLUE:
1793 pwm_cfg = led->rgb_cfg->pwm_cfg;
1794 break;
1795 default:
1796 dev_err(&led->spmi_dev->dev,
1797 "Invalid LED id type for pwm_us\n");
1798 return -EINVAL;
1799 }
1800
1801 if (pwm_cfg->mode == LPG_MODE)
1802 pwm_cfg->blinking = true;
1803
1804 previous_pwm_us = pwm_cfg->pwm_period_us;
1805
1806 pwm_cfg->pwm_period_us = pwm_us;
1807 pwm_free(pwm_cfg->pwm_dev);
1808 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1809 if (ret) {
1810 pwm_cfg->pwm_period_us = previous_pwm_us;
1811 pwm_free(pwm_cfg->pwm_dev);
1812 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1813 qpnp_led_set(&led->cdev, led->cdev.brightness);
1814 dev_err(&led->spmi_dev->dev,
1815 "Failed to initialize pwm with new pwm_us value\n");
1816 return ret;
1817 }
1818 qpnp_led_set(&led->cdev, led->cdev.brightness);
1819 return count;
1820}
1821
1822static ssize_t pause_lo_store(struct device *dev,
1823 struct device_attribute *attr,
1824 const char *buf, size_t count)
1825{
1826 struct qpnp_led_data *led;
1827 u32 pause_lo;
1828 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1829 ssize_t ret;
1830 u32 previous_pause_lo;
1831 struct pwm_config_data *pwm_cfg;
1832
1833 ret = kstrtou32(buf, 10, &pause_lo);
1834 if (ret)
1835 return ret;
1836 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1837
1838 switch (led->id) {
1839 case QPNP_ID_LED_MPP:
1840 pwm_cfg = led->mpp_cfg->pwm_cfg;
1841 break;
1842 case QPNP_ID_RGB_RED:
1843 case QPNP_ID_RGB_GREEN:
1844 case QPNP_ID_RGB_BLUE:
1845 pwm_cfg = led->rgb_cfg->pwm_cfg;
1846 break;
1847 default:
1848 dev_err(&led->spmi_dev->dev,
1849 "Invalid LED id type for pause lo\n");
1850 return -EINVAL;
1851 }
1852
1853 if (pwm_cfg->mode == LPG_MODE)
1854 pwm_cfg->blinking = true;
1855
1856 previous_pause_lo = pwm_cfg->lut_params.lut_pause_lo;
1857
1858 pwm_free(pwm_cfg->pwm_dev);
1859 pwm_cfg->lut_params.lut_pause_lo = pause_lo;
1860 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1861 if (ret) {
1862 pwm_cfg->lut_params.lut_pause_lo = previous_pause_lo;
1863 pwm_free(pwm_cfg->pwm_dev);
1864 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1865 qpnp_led_set(&led->cdev, led->cdev.brightness);
1866 dev_err(&led->spmi_dev->dev,
1867 "Failed to initialize pwm with new pause lo value\n");
1868 return ret;
1869 }
1870 qpnp_led_set(&led->cdev, led->cdev.brightness);
1871 return count;
1872}
1873
1874static ssize_t pause_hi_store(struct device *dev,
1875 struct device_attribute *attr,
1876 const char *buf, size_t count)
1877{
1878 struct qpnp_led_data *led;
1879 u32 pause_hi;
1880 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1881 ssize_t ret;
1882 u32 previous_pause_hi;
1883 struct pwm_config_data *pwm_cfg;
1884
1885 ret = kstrtou32(buf, 10, &pause_hi);
1886 if (ret)
1887 return ret;
1888 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1889
1890 switch (led->id) {
1891 case QPNP_ID_LED_MPP:
1892 pwm_cfg = led->mpp_cfg->pwm_cfg;
1893 break;
1894 case QPNP_ID_RGB_RED:
1895 case QPNP_ID_RGB_GREEN:
1896 case QPNP_ID_RGB_BLUE:
1897 pwm_cfg = led->rgb_cfg->pwm_cfg;
1898 break;
1899 default:
1900 dev_err(&led->spmi_dev->dev,
1901 "Invalid LED id type for pause hi\n");
1902 return -EINVAL;
1903 }
1904
1905 if (pwm_cfg->mode == LPG_MODE)
1906 pwm_cfg->blinking = true;
1907
1908 previous_pause_hi = pwm_cfg->lut_params.lut_pause_hi;
1909
1910 pwm_free(pwm_cfg->pwm_dev);
1911 pwm_cfg->lut_params.lut_pause_hi = pause_hi;
1912 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1913 if (ret) {
1914 pwm_cfg->lut_params.lut_pause_hi = previous_pause_hi;
1915 pwm_free(pwm_cfg->pwm_dev);
1916 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1917 qpnp_led_set(&led->cdev, led->cdev.brightness);
1918 dev_err(&led->spmi_dev->dev,
1919 "Failed to initialize pwm with new pause hi value\n");
1920 return ret;
1921 }
1922 qpnp_led_set(&led->cdev, led->cdev.brightness);
1923 return count;
1924}
1925
1926static ssize_t start_idx_store(struct device *dev,
1927 struct device_attribute *attr,
1928 const char *buf, size_t count)
1929{
1930 struct qpnp_led_data *led;
1931 u32 start_idx;
1932 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1933 ssize_t ret;
1934 u32 previous_start_idx;
1935 struct pwm_config_data *pwm_cfg;
1936
1937 ret = kstrtou32(buf, 10, &start_idx);
1938 if (ret)
1939 return ret;
1940 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1941
1942 switch (led->id) {
1943 case QPNP_ID_LED_MPP:
1944 pwm_cfg = led->mpp_cfg->pwm_cfg;
1945 break;
1946 case QPNP_ID_RGB_RED:
1947 case QPNP_ID_RGB_GREEN:
1948 case QPNP_ID_RGB_BLUE:
1949 pwm_cfg = led->rgb_cfg->pwm_cfg;
1950 break;
1951 default:
1952 dev_err(&led->spmi_dev->dev,
1953 "Invalid LED id type for start idx\n");
1954 return -EINVAL;
1955 }
1956
1957 if (pwm_cfg->mode == LPG_MODE)
1958 pwm_cfg->blinking = true;
1959
1960 previous_start_idx = pwm_cfg->duty_cycles->start_idx;
1961 pwm_cfg->duty_cycles->start_idx = start_idx;
1962 pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
1963 pwm_free(pwm_cfg->pwm_dev);
1964 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1965 if (ret) {
1966 pwm_cfg->duty_cycles->start_idx = previous_start_idx;
1967 pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
1968 pwm_free(pwm_cfg->pwm_dev);
1969 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1970 qpnp_led_set(&led->cdev, led->cdev.brightness);
1971 dev_err(&led->spmi_dev->dev,
1972 "Failed to initialize pwm with new start idx value\n");
1973 return ret;
1974 }
1975 qpnp_led_set(&led->cdev, led->cdev.brightness);
1976 return count;
1977}
1978
1979static ssize_t ramp_step_ms_store(struct device *dev,
1980 struct device_attribute *attr,
1981 const char *buf, size_t count)
1982{
1983 struct qpnp_led_data *led;
1984 u32 ramp_step_ms;
1985 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1986 ssize_t ret;
1987 u32 previous_ramp_step_ms;
1988 struct pwm_config_data *pwm_cfg;
1989
1990 ret = kstrtou32(buf, 10, &ramp_step_ms);
1991 if (ret)
1992 return ret;
1993 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1994
1995 switch (led->id) {
1996 case QPNP_ID_LED_MPP:
1997 pwm_cfg = led->mpp_cfg->pwm_cfg;
1998 break;
1999 case QPNP_ID_RGB_RED:
2000 case QPNP_ID_RGB_GREEN:
2001 case QPNP_ID_RGB_BLUE:
2002 pwm_cfg = led->rgb_cfg->pwm_cfg;
2003 break;
2004 default:
2005 dev_err(&led->spmi_dev->dev,
2006 "Invalid LED id type for ramp step\n");
2007 return -EINVAL;
2008 }
2009
2010 if (pwm_cfg->mode == LPG_MODE)
2011 pwm_cfg->blinking = true;
2012
2013 previous_ramp_step_ms = pwm_cfg->lut_params.ramp_step_ms;
2014
2015 pwm_free(pwm_cfg->pwm_dev);
2016 pwm_cfg->lut_params.ramp_step_ms = ramp_step_ms;
2017 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2018 if (ret) {
2019 pwm_cfg->lut_params.ramp_step_ms = previous_ramp_step_ms;
2020 pwm_free(pwm_cfg->pwm_dev);
2021 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2022 qpnp_led_set(&led->cdev, led->cdev.brightness);
2023 dev_err(&led->spmi_dev->dev,
2024 "Failed to initialize pwm with new ramp step value\n");
2025 return ret;
2026 }
2027 qpnp_led_set(&led->cdev, led->cdev.brightness);
2028 return count;
2029}
2030
2031static ssize_t lut_flags_store(struct device *dev,
2032 struct device_attribute *attr,
2033 const char *buf, size_t count)
2034{
2035 struct qpnp_led_data *led;
2036 u32 lut_flags;
2037 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2038 ssize_t ret;
2039 u32 previous_lut_flags;
2040 struct pwm_config_data *pwm_cfg;
2041
2042 ret = kstrtou32(buf, 10, &lut_flags);
2043 if (ret)
2044 return ret;
2045 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2046
2047 switch (led->id) {
2048 case QPNP_ID_LED_MPP:
2049 pwm_cfg = led->mpp_cfg->pwm_cfg;
2050 break;
2051 case QPNP_ID_RGB_RED:
2052 case QPNP_ID_RGB_GREEN:
2053 case QPNP_ID_RGB_BLUE:
2054 pwm_cfg = led->rgb_cfg->pwm_cfg;
2055 break;
2056 default:
2057 dev_err(&led->spmi_dev->dev,
2058 "Invalid LED id type for lut flags\n");
2059 return -EINVAL;
2060 }
2061
2062 if (pwm_cfg->mode == LPG_MODE)
2063 pwm_cfg->blinking = true;
2064
2065 previous_lut_flags = pwm_cfg->lut_params.flags;
2066
2067 pwm_free(pwm_cfg->pwm_dev);
2068 pwm_cfg->lut_params.flags = lut_flags;
2069 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2070 if (ret) {
2071 pwm_cfg->lut_params.flags = previous_lut_flags;
2072 pwm_free(pwm_cfg->pwm_dev);
2073 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2074 qpnp_led_set(&led->cdev, led->cdev.brightness);
2075 dev_err(&led->spmi_dev->dev,
2076 "Failed to initialize pwm with new lut flags value\n");
2077 return ret;
2078 }
2079 qpnp_led_set(&led->cdev, led->cdev.brightness);
2080 return count;
2081}
2082
2083static ssize_t duty_pcts_store(struct device *dev,
2084 struct device_attribute *attr,
2085 const char *buf, size_t count)
2086{
2087 struct qpnp_led_data *led;
2088 int num_duty_pcts = 0;
2089 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2090 char *buffer;
2091 ssize_t ret;
2092 int i = 0;
2093 int max_duty_pcts;
2094 struct pwm_config_data *pwm_cfg;
2095 u32 previous_num_duty_pcts;
2096 int value;
2097 int *previous_duty_pcts;
2098
2099 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2100
2101 switch (led->id) {
2102 case QPNP_ID_LED_MPP:
2103 pwm_cfg = led->mpp_cfg->pwm_cfg;
2104 max_duty_pcts = PWM_LUT_MAX_SIZE;
2105 break;
2106 case QPNP_ID_RGB_RED:
2107 case QPNP_ID_RGB_GREEN:
2108 case QPNP_ID_RGB_BLUE:
2109 pwm_cfg = led->rgb_cfg->pwm_cfg;
2110 max_duty_pcts = PWM_LUT_MAX_SIZE;
2111 break;
2112 default:
2113 dev_err(&led->spmi_dev->dev,
2114 "Invalid LED id type for duty pcts\n");
2115 return -EINVAL;
2116 }
2117
2118 if (pwm_cfg->mode == LPG_MODE)
2119 pwm_cfg->blinking = true;
2120
2121 buffer = (char *)buf;
2122
2123 for (i = 0; i < max_duty_pcts; i++) {
2124 if (buffer == NULL)
2125 break;
2126 ret = sscanf((const char *)buffer, "%u,%s", &value, buffer);
2127 pwm_cfg->old_duty_pcts[i] = value;
2128 num_duty_pcts++;
2129 if (ret <= 1)
2130 break;
2131 }
2132
2133 if (num_duty_pcts >= max_duty_pcts) {
2134 dev_err(&led->spmi_dev->dev,
2135 "Number of duty pcts given exceeds max (%d)\n",
2136 max_duty_pcts);
2137 return -EINVAL;
2138 }
2139
2140 previous_num_duty_pcts = pwm_cfg->duty_cycles->num_duty_pcts;
2141 previous_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
2142
2143 pwm_cfg->duty_cycles->num_duty_pcts = num_duty_pcts;
2144 pwm_cfg->duty_cycles->duty_pcts = pwm_cfg->old_duty_pcts;
2145 pwm_cfg->old_duty_pcts = previous_duty_pcts;
2146 pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
2147
2148 pwm_free(pwm_cfg->pwm_dev);
2149 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2150 if (ret)
2151 goto restore;
2152
2153 qpnp_led_set(&led->cdev, led->cdev.brightness);
2154 return count;
2155
2156restore:
2157 dev_err(&led->spmi_dev->dev,
2158 "Failed to initialize pwm with new duty pcts value\n");
2159 pwm_cfg->duty_cycles->num_duty_pcts = previous_num_duty_pcts;
2160 pwm_cfg->old_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
2161 pwm_cfg->duty_cycles->duty_pcts = previous_duty_pcts;
2162 pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
2163 pwm_free(pwm_cfg->pwm_dev);
2164 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2165 qpnp_led_set(&led->cdev, led->cdev.brightness);
2166 return ret;
2167}
2168
Amy Malochea5c4ed82013-06-05 11:05:28 -07002169static void led_blink(struct qpnp_led_data *led,
2170 struct pwm_config_data *pwm_cfg)
2171{
Amy Malochea5c4ed82013-06-05 11:05:28 -07002172 if (pwm_cfg->use_blink) {
2173 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07002174 pwm_cfg->blinking = true;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002175 if (led->id == QPNP_ID_LED_MPP)
2176 led->mpp_cfg->pwm_mode = LPG_MODE;
2177 pwm_cfg->mode = LPG_MODE;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002178 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07002179 pwm_cfg->blinking = false;
2180 pwm_cfg->mode = pwm_cfg->default_mode;
2181 if (led->id == QPNP_ID_LED_MPP)
2182 led->mpp_cfg->pwm_mode = pwm_cfg->default_mode;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002183 }
Amy Maloche4cf9f322013-05-29 15:53:46 -07002184 pwm_free(pwm_cfg->pwm_dev);
2185 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2186 qpnp_led_set(&led->cdev, led->cdev.brightness);
Amy Malochea5c4ed82013-06-05 11:05:28 -07002187 }
2188}
2189
2190static ssize_t blink_store(struct device *dev,
2191 struct device_attribute *attr,
2192 const char *buf, size_t count)
2193{
2194 struct qpnp_led_data *led;
2195 unsigned long blinking;
2196 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2197 ssize_t ret = -EINVAL;
2198
2199 ret = kstrtoul(buf, 10, &blinking);
2200 if (ret)
2201 return ret;
2202 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2203 led->cdev.brightness = blinking ? led->cdev.max_brightness : 0;
2204
2205 switch (led->id) {
2206 case QPNP_ID_LED_MPP:
2207 led_blink(led, led->mpp_cfg->pwm_cfg);
2208 break;
2209 case QPNP_ID_RGB_RED:
2210 case QPNP_ID_RGB_GREEN:
2211 case QPNP_ID_RGB_BLUE:
2212 led_blink(led, led->rgb_cfg->pwm_cfg);
2213 break;
2214 default:
2215 dev_err(&led->spmi_dev->dev, "Invalid LED id type for blink\n");
2216 return -EINVAL;
2217 }
2218 return count;
2219}
2220
Amy Malochebc97c0d22013-03-24 22:06:16 -07002221static DEVICE_ATTR(led_mode, 0664, NULL, led_mode_store);
2222static DEVICE_ATTR(strobe, 0664, NULL, led_strobe_type_store);
Amy Maloche4cf9f322013-05-29 15:53:46 -07002223static DEVICE_ATTR(pwm_us, 0664, NULL, pwm_us_store);
2224static DEVICE_ATTR(pause_lo, 0664, NULL, pause_lo_store);
2225static DEVICE_ATTR(pause_hi, 0664, NULL, pause_hi_store);
2226static DEVICE_ATTR(start_idx, 0664, NULL, start_idx_store);
2227static DEVICE_ATTR(ramp_step_ms, 0664, NULL, ramp_step_ms_store);
2228static DEVICE_ATTR(lut_flags, 0664, NULL, lut_flags_store);
2229static DEVICE_ATTR(duty_pcts, 0664, NULL, duty_pcts_store);
Amy Malochea5c4ed82013-06-05 11:05:28 -07002230static DEVICE_ATTR(blink, 0664, NULL, blink_store);
Amy Malochebc97c0d22013-03-24 22:06:16 -07002231
2232static struct attribute *led_attrs[] = {
2233 &dev_attr_led_mode.attr,
2234 &dev_attr_strobe.attr,
Amy Maloche12ad6f32013-04-02 14:39:24 -07002235 NULL
Amy Malochebc97c0d22013-03-24 22:06:16 -07002236};
2237
2238static const struct attribute_group led_attr_group = {
2239 .attrs = led_attrs,
2240};
2241
Amy Maloche4cf9f322013-05-29 15:53:46 -07002242static struct attribute *pwm_attrs[] = {
2243 &dev_attr_pwm_us.attr,
2244 NULL
2245};
2246
2247static struct attribute *lpg_attrs[] = {
2248 &dev_attr_pause_lo.attr,
2249 &dev_attr_pause_hi.attr,
2250 &dev_attr_start_idx.attr,
2251 &dev_attr_ramp_step_ms.attr,
2252 &dev_attr_lut_flags.attr,
2253 &dev_attr_duty_pcts.attr,
2254 NULL
2255};
2256
Amy Malochea5c4ed82013-06-05 11:05:28 -07002257static struct attribute *blink_attrs[] = {
2258 &dev_attr_blink.attr,
2259 NULL
2260};
2261
Amy Maloche4cf9f322013-05-29 15:53:46 -07002262static const struct attribute_group pwm_attr_group = {
2263 .attrs = pwm_attrs,
2264};
2265
2266static const struct attribute_group lpg_attr_group = {
2267 .attrs = lpg_attrs,
2268};
2269
Amy Malochea5c4ed82013-06-05 11:05:28 -07002270static const struct attribute_group blink_attr_group = {
2271 .attrs = blink_attrs,
2272};
2273
Amy Maloche864a6d52012-10-03 15:58:12 -07002274static int __devinit qpnp_flash_init(struct qpnp_led_data *led)
2275{
2276 int rc;
2277
Chun Zhangc3b505b2013-06-03 19:01:49 -07002278 led->flash_cfg->flash_on = false;
2279
Amy Maloche864a6d52012-10-03 15:58:12 -07002280 rc = qpnp_led_masked_write(led,
2281 FLASH_LED_STROBE_CTRL(led->base),
2282 FLASH_STROBE_MASK, FLASH_DISABLE_ALL);
2283 if (rc) {
2284 dev_err(&led->spmi_dev->dev,
2285 "LED %d flash write failed(%d)\n", led->id, rc);
2286 return rc;
2287 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002288
Chun Zhangdf2d3062013-06-25 20:14:46 -07002289 /* Disable flash LED module */
2290 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07002291 FLASH_ENABLE_MASK, FLASH_DISABLE_ALL);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002292 if (rc) {
2293 dev_err(&led->spmi_dev->dev,
2294 "Enable reg write failed(%d)\n", rc);
2295 return rc;
2296 }
2297
2298 if (led->flash_cfg->torch_enable)
2299 return 0;
2300
Amy Maloche864a6d52012-10-03 15:58:12 -07002301 /* Set headroom */
2302 rc = qpnp_led_masked_write(led, FLASH_HEADROOM(led->base),
2303 FLASH_HEADROOM_MASK, led->flash_cfg->headroom);
2304 if (rc) {
2305 dev_err(&led->spmi_dev->dev,
2306 "Headroom reg write failed(%d)\n", rc);
2307 return rc;
2308 }
2309
Chun Zhange8954cf2013-05-02 11:14:34 -07002310 /* Set startup delay */
2311 rc = qpnp_led_masked_write(led,
2312 FLASH_STARTUP_DELAY(led->base), FLASH_STARTUP_DLY_MASK,
2313 led->flash_cfg->startup_dly);
2314 if (rc) {
2315 dev_err(&led->spmi_dev->dev,
2316 "Startup delay reg write failed(%d)\n", rc);
2317 return rc;
2318 }
2319
2320 /* Set timer control - safety or watchdog */
2321 if (led->flash_cfg->safety_timer) {
2322 rc = qpnp_led_masked_write(led,
2323 FLASH_LED_TMR_CTRL(led->base),
2324 FLASH_TMR_MASK, FLASH_TMR_SAFETY);
2325 if (rc) {
2326 dev_err(&led->spmi_dev->dev,
2327 "LED timer ctrl reg write failed(%d)\n",
2328 rc);
2329 return rc;
2330 }
2331 }
2332
2333 /* Set Vreg force */
2334 rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
2335 FLASH_VREG_MASK, FLASH_HW_VREG_OK);
2336 if (rc) {
2337 dev_err(&led->spmi_dev->dev,
2338 "Vreg OK reg write failed(%d)\n", rc);
2339 return rc;
2340 }
2341
2342 /* Set self fault check */
2343 rc = qpnp_led_masked_write(led, FLASH_FAULT_DETECT(led->base),
2344 FLASH_FAULT_DETECT_MASK, FLASH_SELFCHECK_ENABLE);
2345 if (rc) {
2346 dev_err(&led->spmi_dev->dev,
2347 "Fault detect reg write failed(%d)\n", rc);
2348 return rc;
2349 }
2350
Amy Maloche864a6d52012-10-03 15:58:12 -07002351 /* Set mask enable */
2352 rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
2353 FLASH_MASK_REG_MASK, FLASH_MASK_1);
2354 if (rc) {
2355 dev_err(&led->spmi_dev->dev,
2356 "Mask enable reg write failed(%d)\n", rc);
2357 return rc;
2358 }
2359
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07002360 /* Set current ramp */
2361 rc = qpnp_led_masked_write(led, FLASH_CURRENT_RAMP(led->base),
2362 FLASH_CURRENT_RAMP_MASK, FLASH_RAMP_STEP_27US);
2363 if (rc) {
2364 dev_err(&led->spmi_dev->dev,
2365 "Current ramp reg write failed(%d)\n", rc);
2366 return rc;
2367 }
2368
Amy Malochebc97c0d22013-03-24 22:06:16 -07002369 led->flash_cfg->strobe_type = 0;
2370
Amy Maloche864a6d52012-10-03 15:58:12 -07002371 /* dump flash registers */
2372 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
2373
2374 return 0;
2375}
2376
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302377static int __devinit qpnp_kpdbl_init(struct qpnp_led_data *led)
2378{
2379 int rc;
2380 u8 val;
2381
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302382 /* select row source - vbst or vph */
2383 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2384 KPDBL_ROW_SRC_SEL(led->base), &val, 1);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302385 if (rc) {
2386 dev_err(&led->spmi_dev->dev,
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302387 "Unable to read from addr=%x, rc(%d)\n",
2388 KPDBL_ROW_SRC_SEL(led->base), rc);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302389 return rc;
2390 }
2391
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302392 if (led->kpdbl_cfg->row_src_vbst)
2393 val |= 1 << led->kpdbl_cfg->row_id;
2394 else
2395 val &= ~(1 << led->kpdbl_cfg->row_id);
2396
2397 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
2398 KPDBL_ROW_SRC_SEL(led->base), &val, 1);
2399 if (rc) {
2400 dev_err(&led->spmi_dev->dev,
2401 "Unable to read from addr=%x, rc(%d)\n",
2402 KPDBL_ROW_SRC_SEL(led->base), rc);
2403 return rc;
2404 }
2405
2406 /* row source enable */
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302407 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2408 KPDBL_ROW_SRC(led->base), &val, 1);
2409 if (rc) {
2410 dev_err(&led->spmi_dev->dev,
2411 "Unable to read from addr=%x, rc(%d)\n",
2412 KPDBL_ROW_SRC(led->base), rc);
2413 return rc;
2414 }
2415
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302416 if (led->kpdbl_cfg->row_src_en)
2417 val |= KPDBL_ROW_SCAN_EN_MASK | (1 << led->kpdbl_cfg->row_id);
2418 else
2419 val &= ~(1 << led->kpdbl_cfg->row_id);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302420
2421 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
2422 KPDBL_ROW_SRC(led->base), &val, 1);
2423 if (rc) {
2424 dev_err(&led->spmi_dev->dev,
2425 "Unable to write to addr=%x, rc(%d)\n",
2426 KPDBL_ROW_SRC(led->base), rc);
2427 return rc;
2428 }
2429
2430 /* enable module */
2431 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
2432 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
2433 if (rc) {
2434 dev_err(&led->spmi_dev->dev,
2435 "Enable module write failed(%d)\n", rc);
2436 return rc;
2437 }
2438
Amy Malochea2726f02013-05-10 10:19:03 -07002439 rc = qpnp_pwm_init(led->kpdbl_cfg->pwm_cfg, led->spmi_dev,
2440 led->cdev.name);
2441 if (rc) {
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302442 dev_err(&led->spmi_dev->dev,
Amy Malochea2726f02013-05-10 10:19:03 -07002443 "Failed to initialize pwm\n");
2444 return rc;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302445 }
2446
2447 /* dump kpdbl registers */
2448 qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
2449
2450 return 0;
2451}
2452
Amy Malocheeea7b592012-10-03 15:59:36 -07002453static int __devinit qpnp_rgb_init(struct qpnp_led_data *led)
2454{
Amy Malochea2726f02013-05-10 10:19:03 -07002455 int rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07002456
2457 rc = qpnp_led_masked_write(led, RGB_LED_SRC_SEL(led->base),
2458 RGB_LED_SRC_MASK, RGB_LED_SOURCE_VPH_PWR);
2459 if (rc) {
2460 dev_err(&led->spmi_dev->dev,
2461 "Failed to write led source select register\n");
2462 return rc;
2463 }
2464
Amy Malochea2726f02013-05-10 10:19:03 -07002465 rc = qpnp_pwm_init(led->rgb_cfg->pwm_cfg, led->spmi_dev,
2466 led->cdev.name);
2467 if (rc) {
Amy Malocheeea7b592012-10-03 15:59:36 -07002468 dev_err(&led->spmi_dev->dev,
Amy Malochea2726f02013-05-10 10:19:03 -07002469 "Failed to initialize pwm\n");
2470 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07002471 }
Amy Malocheeea7b592012-10-03 15:59:36 -07002472 /* Initialize led for use in auto trickle charging mode */
2473 rc = qpnp_led_masked_write(led, RGB_LED_ATC_CTL(led->base),
2474 led->rgb_cfg->enable, led->rgb_cfg->enable);
2475
2476 return 0;
2477}
2478
Amy Malochea2726f02013-05-10 10:19:03 -07002479static int __devinit qpnp_mpp_init(struct qpnp_led_data *led)
2480{
Abinaya P03cee1d2014-02-17 17:51:53 +05302481 int rc;
2482 u8 val;
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05302483
2484 if (led->max_current < LED_MPP_CURRENT_MIN ||
2485 led->max_current > LED_MPP_CURRENT_MAX) {
2486 dev_err(&led->spmi_dev->dev,
2487 "max current for mpp is not valid\n");
2488 return -EINVAL;
2489 }
2490
Amy Malochea2726f02013-05-10 10:19:03 -07002491 val = (led->mpp_cfg->current_setting / LED_MPP_CURRENT_PER_SETTING) - 1;
2492
2493 if (val < 0)
2494 val = 0;
2495
Chun Zhang874c9ab2013-07-08 17:18:34 -07002496 rc = qpnp_led_masked_write(led, LED_MPP_VIN_CTRL(led->base),
2497 LED_MPP_VIN_MASK, led->mpp_cfg->vin_ctrl);
2498 if (rc) {
2499 dev_err(&led->spmi_dev->dev,
2500 "Failed to write led vin control reg\n");
2501 return rc;
2502 }
2503
Amy Malochea2726f02013-05-10 10:19:03 -07002504 rc = qpnp_led_masked_write(led, LED_MPP_SINK_CTRL(led->base),
2505 LED_MPP_SINK_MASK, val);
2506 if (rc) {
2507 dev_err(&led->spmi_dev->dev,
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05302508 "Failed to write sink control reg\n");
Amy Malochea2726f02013-05-10 10:19:03 -07002509 return rc;
2510 }
2511
2512 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
2513 rc = qpnp_pwm_init(led->mpp_cfg->pwm_cfg, led->spmi_dev,
2514 led->cdev.name);
2515 if (rc) {
2516 dev_err(&led->spmi_dev->dev,
2517 "Failed to initialize pwm\n");
2518 return rc;
2519 }
2520 }
2521
2522 return 0;
2523}
2524
Amy Malochef3d5a062012-08-16 19:14:11 -07002525static int __devinit qpnp_led_initialize(struct qpnp_led_data *led)
2526{
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302527 int rc = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07002528
2529 switch (led->id) {
2530 case QPNP_ID_WLED:
2531 rc = qpnp_wled_init(led);
2532 if (rc)
Amy Malochea5ca5552012-10-23 13:34:46 -07002533 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -07002534 "WLED initialize failed(%d)\n", rc);
2535 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07002536 case QPNP_ID_FLASH1_LED0:
2537 case QPNP_ID_FLASH1_LED1:
2538 rc = qpnp_flash_init(led);
2539 if (rc)
2540 dev_err(&led->spmi_dev->dev,
2541 "FLASH initialize failed(%d)\n", rc);
2542 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07002543 case QPNP_ID_RGB_RED:
2544 case QPNP_ID_RGB_GREEN:
2545 case QPNP_ID_RGB_BLUE:
2546 rc = qpnp_rgb_init(led);
2547 if (rc)
2548 dev_err(&led->spmi_dev->dev,
2549 "RGB initialize failed(%d)\n", rc);
2550 break;
Amy Malochef3813742013-04-11 19:33:47 -07002551 case QPNP_ID_LED_MPP:
Amy Malochea2726f02013-05-10 10:19:03 -07002552 rc = qpnp_mpp_init(led);
2553 if (rc)
2554 dev_err(&led->spmi_dev->dev,
2555 "MPP initialize failed(%d)\n", rc);
Amy Malochef3813742013-04-11 19:33:47 -07002556 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302557 case QPNP_ID_KPDBL:
2558 rc = qpnp_kpdbl_init(led);
2559 if (rc)
2560 dev_err(&led->spmi_dev->dev,
2561 "KPDBL initialize failed(%d)\n", rc);
2562 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07002563 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07002564 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malocheeea7b592012-10-03 15:59:36 -07002565 return -EINVAL;
Amy Malochef3d5a062012-08-16 19:14:11 -07002566 }
2567
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302568 return rc;
Amy Malochef3d5a062012-08-16 19:14:11 -07002569}
2570
Amy Malochea5ca5552012-10-23 13:34:46 -07002571static int __devinit qpnp_get_common_configs(struct qpnp_led_data *led,
2572 struct device_node *node)
2573{
2574 int rc;
Asaf Penso55ac8472013-01-21 21:17:37 +02002575 u32 val;
Amy Malochea5ca5552012-10-23 13:34:46 -07002576 const char *temp_string;
2577
2578 led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
2579 rc = of_property_read_string(node, "linux,default-trigger",
2580 &temp_string);
2581 if (!rc)
2582 led->cdev.default_trigger = temp_string;
2583 else if (rc != -EINVAL)
2584 return rc;
2585
2586 led->default_on = false;
2587 rc = of_property_read_string(node, "qcom,default-state",
2588 &temp_string);
2589 if (!rc) {
2590 if (strncmp(temp_string, "on", sizeof("on")) == 0)
2591 led->default_on = true;
2592 } else if (rc != -EINVAL)
2593 return rc;
2594
Asaf Penso55ac8472013-01-21 21:17:37 +02002595 led->turn_off_delay_ms = 0;
2596 rc = of_property_read_u32(node, "qcom,turn-off-delay-ms", &val);
2597 if (!rc)
2598 led->turn_off_delay_ms = val;
2599 else if (rc != -EINVAL)
2600 return rc;
2601
Amy Malochea5ca5552012-10-23 13:34:46 -07002602 return 0;
2603}
2604
Amy Malochef3d5a062012-08-16 19:14:11 -07002605/*
2606 * Handlers for alternative sources of platform_data
2607 */
2608static int __devinit qpnp_get_config_wled(struct qpnp_led_data *led,
2609 struct device_node *node)
2610{
2611 u32 val;
2612 int rc;
Amy Malochef3d5a062012-08-16 19:14:11 -07002613
2614 led->wled_cfg = devm_kzalloc(&led->spmi_dev->dev,
2615 sizeof(struct wled_config_data), GFP_KERNEL);
2616 if (!led->wled_cfg) {
2617 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2618 return -ENOMEM;
2619 }
2620
Amy Maloche0150b5e2013-08-15 18:18:32 -07002621 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2622 PMIC_VERSION_REG, &led->wled_cfg->pmic_version, 1);
2623 if (rc) {
2624 dev_err(&led->spmi_dev->dev,
2625 "Unable to read pmic ver, rc(%d)\n", rc);
2626 }
2627
Amy Malochef3d5a062012-08-16 19:14:11 -07002628 led->wled_cfg->num_strings = WLED_DEFAULT_STRINGS;
2629 rc = of_property_read_u32(node, "qcom,num-strings", &val);
2630 if (!rc)
2631 led->wled_cfg->num_strings = (u8) val;
2632 else if (rc != -EINVAL)
2633 return rc;
2634
2635 led->wled_cfg->ovp_val = WLED_DEFAULT_OVP_VAL;
2636 rc = of_property_read_u32(node, "qcom,ovp-val", &val);
2637 if (!rc)
2638 led->wled_cfg->ovp_val = (u8) val;
2639 else if (rc != -EINVAL)
2640 return rc;
2641
2642 led->wled_cfg->boost_curr_lim = WLED_BOOST_LIM_DEFAULT;
2643 rc = of_property_read_u32(node, "qcom,boost-curr-lim", &val);
2644 if (!rc)
2645 led->wled_cfg->boost_curr_lim = (u8) val;
2646 else if (rc != -EINVAL)
2647 return rc;
2648
2649 led->wled_cfg->cp_select = WLED_CP_SEL_DEFAULT;
2650 rc = of_property_read_u32(node, "qcom,cp-sel", &val);
2651 if (!rc)
2652 led->wled_cfg->cp_select = (u8) val;
2653 else if (rc != -EINVAL)
2654 return rc;
2655
2656 led->wled_cfg->ctrl_delay_us = WLED_CTRL_DLY_DEFAULT;
2657 rc = of_property_read_u32(node, "qcom,ctrl-delay-us", &val);
2658 if (!rc)
2659 led->wled_cfg->ctrl_delay_us = (u8) val;
2660 else if (rc != -EINVAL)
2661 return rc;
2662
Amy Malochebd687672013-03-18 11:23:45 -07002663 led->wled_cfg->op_fdbck = WLED_OP_FDBCK_DEFAULT;
2664 rc = of_property_read_u32(node, "qcom,op-fdbck", &val);
2665 if (!rc)
2666 led->wled_cfg->op_fdbck = (u8) val;
2667 else if (rc != -EINVAL)
2668 return rc;
2669
Amy Malochef3d5a062012-08-16 19:14:11 -07002670 led->wled_cfg->switch_freq = WLED_SWITCH_FREQ_DEFAULT;
2671 rc = of_property_read_u32(node, "qcom,switch-freq", &val);
2672 if (!rc)
2673 led->wled_cfg->switch_freq = (u8) val;
2674 else if (rc != -EINVAL)
2675 return rc;
2676
2677 led->wled_cfg->dig_mod_gen_en =
2678 of_property_read_bool(node, "qcom,dig-mod-gen-en");
2679
2680 led->wled_cfg->cs_out_en =
2681 of_property_read_bool(node, "qcom,cs-out-en");
2682
Amy Malochef3d5a062012-08-16 19:14:11 -07002683 return 0;
2684}
2685
Amy Maloche864a6d52012-10-03 15:58:12 -07002686static int __devinit qpnp_get_config_flash(struct qpnp_led_data *led,
Chun Zhangc3b505b2013-06-03 19:01:49 -07002687 struct device_node *node, bool *reg_set)
Amy Maloche864a6d52012-10-03 15:58:12 -07002688{
2689 int rc;
2690 u32 val;
2691
2692 led->flash_cfg = devm_kzalloc(&led->spmi_dev->dev,
2693 sizeof(struct flash_config_data), GFP_KERNEL);
2694 if (!led->flash_cfg) {
2695 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2696 return -ENOMEM;
2697 }
2698
Chun Zhang0d6ca072013-07-30 21:08:39 -07002699 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2700 FLASH_PERIPHERAL_SUBTYPE(led->base),
2701 &led->flash_cfg->peripheral_subtype, 1);
2702 if (rc) {
2703 dev_err(&led->spmi_dev->dev,
2704 "Unable to read from addr=%x, rc(%d)\n",
2705 FLASH_PERIPHERAL_SUBTYPE(led->base), rc);
2706 }
2707
Chun Zhang9d5ff672013-08-01 18:18:26 -07002708 led->flash_cfg->torch_enable =
2709 of_property_read_bool(node, "qcom,torch-enable");
2710
Amy Maloche864a6d52012-10-03 15:58:12 -07002711 if (led->id == QPNP_ID_FLASH1_LED0) {
Chun Zhang9d5ff672013-08-01 18:18:26 -07002712 led->flash_cfg->enable_module = FLASH_ENABLE_LED_0;
Amy Maloche864a6d52012-10-03 15:58:12 -07002713 led->flash_cfg->current_addr = FLASH_LED_0_CURR(led->base);
Amy Maloche864a6d52012-10-03 15:58:12 -07002714 led->flash_cfg->trigger_flash = FLASH_LED_0_OUTPUT;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002715 if (!*reg_set) {
2716 led->flash_cfg->flash_boost_reg =
2717 regulator_get(&led->spmi_dev->dev,
Chun Zhang0d6ca072013-07-30 21:08:39 -07002718 "flash-boost");
Chun Zhangc3b505b2013-06-03 19:01:49 -07002719 if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
2720 rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
2721 dev_err(&led->spmi_dev->dev,
2722 "Regulator get failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002723 goto error_get_flash_reg;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002724 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002725 led->flash_cfg->flash_reg_get = true;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002726 *reg_set = true;
2727 } else
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002728 led->flash_cfg->flash_reg_get = false;
Chun Zhang9d5ff672013-08-01 18:18:26 -07002729
2730 if (led->flash_cfg->torch_enable) {
2731 led->flash_cfg->second_addr =
2732 FLASH_LED_1_CURR(led->base);
2733 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002734 } else if (led->id == QPNP_ID_FLASH1_LED1) {
Chun Zhang9d5ff672013-08-01 18:18:26 -07002735 led->flash_cfg->enable_module = FLASH_ENABLE_LED_1;
Amy Maloche864a6d52012-10-03 15:58:12 -07002736 led->flash_cfg->current_addr = FLASH_LED_1_CURR(led->base);
Amy Maloche864a6d52012-10-03 15:58:12 -07002737 led->flash_cfg->trigger_flash = FLASH_LED_1_OUTPUT;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002738 if (!*reg_set) {
2739 led->flash_cfg->flash_boost_reg =
2740 regulator_get(&led->spmi_dev->dev,
Chun Zhang0d6ca072013-07-30 21:08:39 -07002741 "flash-boost");
Chun Zhangc3b505b2013-06-03 19:01:49 -07002742 if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
2743 rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
2744 dev_err(&led->spmi_dev->dev,
2745 "Regulator get failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002746 goto error_get_flash_reg;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002747 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002748 led->flash_cfg->flash_reg_get = true;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002749 *reg_set = true;
2750 } else
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002751 led->flash_cfg->flash_reg_get = false;
Chun Zhang9d5ff672013-08-01 18:18:26 -07002752
2753 if (led->flash_cfg->torch_enable) {
2754 led->flash_cfg->second_addr =
2755 FLASH_LED_0_CURR(led->base);
2756 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002757 } else {
2758 dev_err(&led->spmi_dev->dev, "Unknown flash LED name given\n");
2759 return -EINVAL;
2760 }
2761
Chun Zhangdf2d3062013-06-25 20:14:46 -07002762 if (led->flash_cfg->torch_enable) {
Chun Zhang0d6ca072013-07-30 21:08:39 -07002763 if (of_find_property(of_get_parent(node), "torch-boost-supply",
2764 NULL)) {
2765 led->flash_cfg->torch_boost_reg =
2766 regulator_get(&led->spmi_dev->dev,
2767 "torch-boost");
2768 if (IS_ERR(led->flash_cfg->torch_boost_reg)) {
2769 rc = PTR_ERR(led->flash_cfg->torch_boost_reg);
2770 dev_err(&led->spmi_dev->dev,
2771 "Torch regulator get failed(%d)\n", rc);
2772 goto error_get_torch_reg;
2773 }
Chun Zhang9d5ff672013-08-01 18:18:26 -07002774 led->flash_cfg->enable_module = FLASH_ENABLE_MODULE;
2775 } else
2776 led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
Abinaya Pb6e3ba42014-01-07 15:32:31 +05302777 led->flash_cfg->trigger_flash = FLASH_TORCH_OUTPUT;
Chun Zhangdf2d3062013-06-25 20:14:46 -07002778 }
2779
Amy Maloche864a6d52012-10-03 15:58:12 -07002780 rc = of_property_read_u32(node, "qcom,current", &val);
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002781 if (!rc) {
Chun Zhangdf2d3062013-06-25 20:14:46 -07002782 if (led->flash_cfg->torch_enable) {
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002783 led->flash_cfg->current_prgm = (val *
2784 TORCH_MAX_LEVEL / led->max_current);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002785 return 0;
2786 }
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002787 else
2788 led->flash_cfg->current_prgm = (val *
Amy Maloche864a6d52012-10-03 15:58:12 -07002789 FLASH_MAX_LEVEL / led->max_current);
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002790 } else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002791 if (led->flash_cfg->torch_enable)
2792 goto error_get_torch_reg;
2793 else
2794 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002795
2796 rc = of_property_read_u32(node, "qcom,headroom", &val);
2797 if (!rc)
2798 led->flash_cfg->headroom = (u8) val;
2799 else if (rc == -EINVAL)
Amy Maloche62571b22013-04-17 17:23:10 -07002800 led->flash_cfg->headroom = HEADROOM_500mV;
Amy Maloche864a6d52012-10-03 15:58:12 -07002801 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002802 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002803
2804 rc = of_property_read_u32(node, "qcom,duration", &val);
2805 if (!rc)
Abinaya P41bb8bc2013-12-06 22:55:37 +05302806 led->flash_cfg->duration = (u8)((val - 10) / 10);
Amy Maloche864a6d52012-10-03 15:58:12 -07002807 else if (rc == -EINVAL)
2808 led->flash_cfg->duration = FLASH_DURATION_200ms;
2809 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002810 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002811
2812 rc = of_property_read_u32(node, "qcom,clamp-curr", &val);
2813 if (!rc)
2814 led->flash_cfg->clamp_curr = (val *
2815 FLASH_MAX_LEVEL / led->max_current);
2816 else if (rc == -EINVAL)
2817 led->flash_cfg->clamp_curr = FLASH_CLAMP_200mA;
2818 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002819 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002820
2821 rc = of_property_read_u32(node, "qcom,startup-dly", &val);
2822 if (!rc)
2823 led->flash_cfg->startup_dly = (u8) val;
2824 else if (rc == -EINVAL)
Amy Maloche62571b22013-04-17 17:23:10 -07002825 led->flash_cfg->startup_dly = DELAY_128us;
Amy Maloche864a6d52012-10-03 15:58:12 -07002826 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002827 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002828
2829 led->flash_cfg->safety_timer =
2830 of_property_read_bool(node, "qcom,safety-timer");
2831
2832 return 0;
Chun Zhangdf2d3062013-06-25 20:14:46 -07002833
2834error_get_torch_reg:
2835 regulator_put(led->flash_cfg->torch_boost_reg);
2836
2837error_get_flash_reg:
2838 regulator_put(led->flash_cfg->flash_boost_reg);
2839 return rc;
2840
Amy Maloche864a6d52012-10-03 15:58:12 -07002841}
2842
Amy Malochea2726f02013-05-10 10:19:03 -07002843static int __devinit qpnp_get_config_pwm(struct pwm_config_data *pwm_cfg,
2844 struct spmi_device *spmi_dev,
2845 struct device_node *node)
2846{
2847 struct property *prop;
2848 int rc, i;
2849 u32 val;
2850 u8 *temp_cfg;
2851
2852 rc = of_property_read_u32(node, "qcom,pwm-channel", &val);
2853 if (!rc)
2854 pwm_cfg->pwm_channel = val;
2855 else
2856 return rc;
2857
2858 if (pwm_cfg->mode == PWM_MODE) {
2859 rc = of_property_read_u32(node, "qcom,pwm-us", &val);
2860 if (!rc)
2861 pwm_cfg->pwm_period_us = val;
2862 else
2863 return rc;
2864 }
2865
Amy Malochea5c4ed82013-06-05 11:05:28 -07002866 pwm_cfg->use_blink =
2867 of_property_read_bool(node, "qcom,use-blink");
2868
2869 if (pwm_cfg->mode == LPG_MODE || pwm_cfg->use_blink) {
Amy Malochea2726f02013-05-10 10:19:03 -07002870 pwm_cfg->duty_cycles =
2871 devm_kzalloc(&spmi_dev->dev,
2872 sizeof(struct pwm_duty_cycles), GFP_KERNEL);
2873 if (!pwm_cfg->duty_cycles) {
2874 dev_err(&spmi_dev->dev,
2875 "Unable to allocate memory\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002876 rc = -ENOMEM;
2877 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002878 }
2879
2880 prop = of_find_property(node, "qcom,duty-pcts",
2881 &pwm_cfg->duty_cycles->num_duty_pcts);
2882 if (!prop) {
2883 dev_err(&spmi_dev->dev, "Looking up property " \
2884 "node qcom,duty-pcts failed\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002885 rc = -ENODEV;
2886 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002887 } else if (!pwm_cfg->duty_cycles->num_duty_pcts) {
2888 dev_err(&spmi_dev->dev, "Invalid length of " \
2889 "duty pcts\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002890 rc = -EINVAL;
2891 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002892 }
2893
2894 pwm_cfg->duty_cycles->duty_pcts =
2895 devm_kzalloc(&spmi_dev->dev,
Amy Maloche4cf9f322013-05-29 15:53:46 -07002896 sizeof(int) * PWM_LUT_MAX_SIZE,
Amy Malochea2726f02013-05-10 10:19:03 -07002897 GFP_KERNEL);
2898 if (!pwm_cfg->duty_cycles->duty_pcts) {
2899 dev_err(&spmi_dev->dev,
2900 "Unable to allocate memory\n");
Amy Maloche4cf9f322013-05-29 15:53:46 -07002901 rc = -ENOMEM;
2902 goto bad_lpg_params;
2903 }
2904
2905 pwm_cfg->old_duty_pcts =
2906 devm_kzalloc(&spmi_dev->dev,
2907 sizeof(int) * PWM_LUT_MAX_SIZE,
2908 GFP_KERNEL);
2909 if (!pwm_cfg->old_duty_pcts) {
2910 dev_err(&spmi_dev->dev,
2911 "Unable to allocate memory\n");
2912 rc = -ENOMEM;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002913 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002914 }
2915
2916 temp_cfg = devm_kzalloc(&spmi_dev->dev,
2917 pwm_cfg->duty_cycles->num_duty_pcts *
2918 sizeof(u8), GFP_KERNEL);
2919 if (!temp_cfg) {
2920 dev_err(&spmi_dev->dev, "Failed to allocate " \
2921 "memory for duty pcts\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002922 rc = -ENOMEM;
2923 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002924 }
2925
2926 memcpy(temp_cfg, prop->value,
2927 pwm_cfg->duty_cycles->num_duty_pcts);
2928
2929 for (i = 0; i < pwm_cfg->duty_cycles->num_duty_pcts; i++)
2930 pwm_cfg->duty_cycles->duty_pcts[i] =
2931 (int) temp_cfg[i];
2932
2933 rc = of_property_read_u32(node, "qcom,start-idx", &val);
2934 if (!rc) {
2935 pwm_cfg->lut_params.start_idx = val;
2936 pwm_cfg->duty_cycles->start_idx = val;
2937 } else
Amy Malochea5c4ed82013-06-05 11:05:28 -07002938 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002939
2940 pwm_cfg->lut_params.lut_pause_hi = 0;
2941 rc = of_property_read_u32(node, "qcom,pause-hi", &val);
2942 if (!rc)
2943 pwm_cfg->lut_params.lut_pause_hi = val;
2944 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002945 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002946
2947 pwm_cfg->lut_params.lut_pause_lo = 0;
2948 rc = of_property_read_u32(node, "qcom,pause-lo", &val);
2949 if (!rc)
2950 pwm_cfg->lut_params.lut_pause_lo = val;
2951 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002952 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002953
2954 pwm_cfg->lut_params.ramp_step_ms =
2955 QPNP_LUT_RAMP_STEP_DEFAULT;
2956 rc = of_property_read_u32(node, "qcom,ramp-step-ms", &val);
2957 if (!rc)
2958 pwm_cfg->lut_params.ramp_step_ms = val;
2959 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002960 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002961
2962 pwm_cfg->lut_params.flags = QPNP_LED_PWM_FLAGS;
2963 rc = of_property_read_u32(node, "qcom,lut-flags", &val);
2964 if (!rc)
2965 pwm_cfg->lut_params.flags = (u8) val;
2966 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002967 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002968
2969 pwm_cfg->lut_params.idx_len =
2970 pwm_cfg->duty_cycles->num_duty_pcts;
2971 }
2972 return 0;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002973
2974bad_lpg_params:
2975 pwm_cfg->use_blink = false;
2976 if (pwm_cfg->mode == PWM_MODE) {
2977 dev_err(&spmi_dev->dev, "LPG parameters not set for" \
2978 " blink mode, defaulting to PWM mode\n");
2979 return 0;
2980 }
2981 return rc;
Amy Malochea2726f02013-05-10 10:19:03 -07002982};
2983
2984static int qpnp_led_get_mode(const char *mode)
2985{
2986 if (strncmp(mode, "manual", strlen(mode)) == 0)
2987 return MANUAL_MODE;
2988 else if (strncmp(mode, "pwm", strlen(mode)) == 0)
2989 return PWM_MODE;
2990 else if (strncmp(mode, "lpg", strlen(mode)) == 0)
2991 return LPG_MODE;
2992 else
2993 return -EINVAL;
2994};
2995
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302996static int __devinit qpnp_get_config_kpdbl(struct qpnp_led_data *led,
2997 struct device_node *node)
2998{
2999 int rc;
3000 u32 val;
Amy Malochea2726f02013-05-10 10:19:03 -07003001 u8 led_mode;
3002 const char *mode;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303003
3004 led->kpdbl_cfg = devm_kzalloc(&led->spmi_dev->dev,
3005 sizeof(struct kpdbl_config_data), GFP_KERNEL);
3006 if (!led->kpdbl_cfg) {
3007 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3008 return -ENOMEM;
3009 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303010
Amy Malochea2726f02013-05-10 10:19:03 -07003011 rc = of_property_read_string(node, "qcom,mode", &mode);
3012 if (!rc) {
3013 led_mode = qpnp_led_get_mode(mode);
3014 if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
3015 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3016 "supported for kpdbl.\n");
3017 return -EINVAL;
3018 }
3019 led->kpdbl_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3020 sizeof(struct pwm_config_data),
3021 GFP_KERNEL);
3022 if (!led->kpdbl_cfg->pwm_cfg) {
3023 dev_err(&led->spmi_dev->dev,
3024 "Unable to allocate memory\n");
3025 return -ENOMEM;
3026 }
3027 led->kpdbl_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003028 led->kpdbl_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003029 } else
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303030 return rc;
3031
Amy Malochea2726f02013-05-10 10:19:03 -07003032 rc = qpnp_get_config_pwm(led->kpdbl_cfg->pwm_cfg, led->spmi_dev, node);
3033 if (rc < 0)
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303034 return rc;
3035
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303036 rc = of_property_read_u32(node, "qcom,row-id", &val);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303037 if (!rc)
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303038 led->kpdbl_cfg->row_id = val;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303039 else
3040 return rc;
3041
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303042 led->kpdbl_cfg->row_src_vbst =
3043 of_property_read_bool(node, "qcom,row-src-vbst");
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303044
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303045 led->kpdbl_cfg->row_src_en =
3046 of_property_read_bool(node, "qcom,row-src-en");
3047
3048 led->kpdbl_cfg->always_on =
3049 of_property_read_bool(node, "qcom,always-on");
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303050
3051 return 0;
3052}
3053
Amy Malocheeea7b592012-10-03 15:59:36 -07003054static int __devinit qpnp_get_config_rgb(struct qpnp_led_data *led,
3055 struct device_node *node)
3056{
Amy Malochea2726f02013-05-10 10:19:03 -07003057 int rc;
3058 u8 led_mode;
3059 const char *mode;
Amy Malocheeea7b592012-10-03 15:59:36 -07003060
3061 led->rgb_cfg = devm_kzalloc(&led->spmi_dev->dev,
3062 sizeof(struct rgb_config_data), GFP_KERNEL);
3063 if (!led->rgb_cfg) {
3064 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3065 return -ENOMEM;
3066 }
3067
3068 if (led->id == QPNP_ID_RGB_RED)
3069 led->rgb_cfg->enable = RGB_LED_ENABLE_RED;
3070 else if (led->id == QPNP_ID_RGB_GREEN)
3071 led->rgb_cfg->enable = RGB_LED_ENABLE_GREEN;
3072 else if (led->id == QPNP_ID_RGB_BLUE)
3073 led->rgb_cfg->enable = RGB_LED_ENABLE_BLUE;
3074 else
3075 return -EINVAL;
3076
Amy Malochea2726f02013-05-10 10:19:03 -07003077 rc = of_property_read_string(node, "qcom,mode", &mode);
3078 if (!rc) {
3079 led_mode = qpnp_led_get_mode(mode);
3080 if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
3081 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3082 "supported for rgb.\n");
Amy Malocheeea7b592012-10-03 15:59:36 -07003083 return -EINVAL;
3084 }
Amy Malochea2726f02013-05-10 10:19:03 -07003085 led->rgb_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3086 sizeof(struct pwm_config_data),
3087 GFP_KERNEL);
3088 if (!led->rgb_cfg->pwm_cfg) {
Amy Malocheeea7b592012-10-03 15:59:36 -07003089 dev_err(&led->spmi_dev->dev,
3090 "Unable to allocate memory\n");
3091 return -ENOMEM;
3092 }
Amy Malochea2726f02013-05-10 10:19:03 -07003093 led->rgb_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003094 led->rgb_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003095 } else
3096 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07003097
Amy Malochea2726f02013-05-10 10:19:03 -07003098 rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->spmi_dev, node);
3099 if (rc < 0)
3100 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07003101
3102 return 0;
3103}
3104
Amy Malochef3813742013-04-11 19:33:47 -07003105static int __devinit qpnp_get_config_mpp(struct qpnp_led_data *led,
3106 struct device_node *node)
3107{
3108 int rc;
3109 u32 val;
Amy Malochea2726f02013-05-10 10:19:03 -07003110 u8 led_mode;
3111 const char *mode;
Amy Malochef3813742013-04-11 19:33:47 -07003112
3113 led->mpp_cfg = devm_kzalloc(&led->spmi_dev->dev,
3114 sizeof(struct mpp_config_data), GFP_KERNEL);
3115 if (!led->mpp_cfg) {
3116 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3117 return -ENOMEM;
3118 }
3119
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05303120 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
Amy Malochef3813742013-04-11 19:33:47 -07003121 rc = of_property_read_u32(node, "qcom,current-setting", &val);
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05303122 if (!rc) {
3123 if (led->mpp_cfg->current_setting < LED_MPP_CURRENT_MIN)
3124 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
3125 else if (led->mpp_cfg->current_setting > LED_MPP_CURRENT_MAX)
3126 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MAX;
3127 else
3128 led->mpp_cfg->current_setting = (u8) val;
3129 } else if (rc != -EINVAL)
Amy Malochef3813742013-04-11 19:33:47 -07003130 return rc;
3131
3132 led->mpp_cfg->source_sel = LED_MPP_SOURCE_SEL_DEFAULT;
3133 rc = of_property_read_u32(node, "qcom,source-sel", &val);
3134 if (!rc)
3135 led->mpp_cfg->source_sel = (u8) val;
3136 else if (rc != -EINVAL)
3137 return rc;
3138
3139 led->mpp_cfg->mode_ctrl = LED_MPP_MODE_SINK;
3140 rc = of_property_read_u32(node, "qcom,mode-ctrl", &val);
3141 if (!rc)
3142 led->mpp_cfg->mode_ctrl = (u8) val;
3143 else if (rc != -EINVAL)
3144 return rc;
3145
Chun Zhang874c9ab2013-07-08 17:18:34 -07003146 led->mpp_cfg->vin_ctrl = LED_MPP_VIN_CTRL_DEFAULT;
3147 rc = of_property_read_u32(node, "qcom,vin-ctrl", &val);
3148 if (!rc)
3149 led->mpp_cfg->vin_ctrl = (u8) val;
3150 else if (rc != -EINVAL)
3151 return rc;
3152
3153 led->mpp_cfg->min_brightness = 0;
3154 rc = of_property_read_u32(node, "qcom,min-brightness", &val);
3155 if (!rc)
3156 led->mpp_cfg->min_brightness = (u8) val;
3157 else if (rc != -EINVAL)
3158 return rc;
3159
Amy Malochea2726f02013-05-10 10:19:03 -07003160 rc = of_property_read_string(node, "qcom,mode", &mode);
3161 if (!rc) {
3162 led_mode = qpnp_led_get_mode(mode);
3163 led->mpp_cfg->pwm_mode = led_mode;
3164 if (led_mode == MANUAL_MODE)
3165 return MANUAL_MODE;
3166 else if (led_mode == -EINVAL) {
3167 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3168 "supported for mpp.\n");
3169 return -EINVAL;
3170 }
3171 led->mpp_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3172 sizeof(struct pwm_config_data),
3173 GFP_KERNEL);
3174 if (!led->mpp_cfg->pwm_cfg) {
3175 dev_err(&led->spmi_dev->dev,
3176 "Unable to allocate memory\n");
3177 return -ENOMEM;
3178 }
3179 led->mpp_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003180 led->mpp_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003181 } else
3182 return rc;
3183
3184 rc = qpnp_get_config_pwm(led->mpp_cfg->pwm_cfg, led->spmi_dev, node);
3185 if (rc < 0)
3186 return rc;
3187
Amy Malochef3813742013-04-11 19:33:47 -07003188 return 0;
3189}
3190
Amy Malochef3d5a062012-08-16 19:14:11 -07003191static int __devinit qpnp_leds_probe(struct spmi_device *spmi)
3192{
Amy Malochef9490c62012-11-27 19:26:04 -08003193 struct qpnp_led_data *led, *led_array;
Amy Malochef3d5a062012-08-16 19:14:11 -07003194 struct resource *led_resource;
Amy Malochea5ca5552012-10-23 13:34:46 -07003195 struct device_node *node, *temp;
Amy Malochef9490c62012-11-27 19:26:04 -08003196 int rc, i, num_leds = 0, parsed_leds = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07003197 const char *led_label;
Chun Zhangc3b505b2013-06-03 19:01:49 -07003198 bool regulator_probe = false;
Amy Malochef3d5a062012-08-16 19:14:11 -07003199
Amy Malochea5ca5552012-10-23 13:34:46 -07003200 node = spmi->dev.of_node;
3201 if (node == NULL)
3202 return -ENODEV;
3203
3204 temp = NULL;
3205 while ((temp = of_get_next_child(node, temp)))
3206 num_leds++;
3207
Amy Malochef9490c62012-11-27 19:26:04 -08003208 if (!num_leds)
3209 return -ECHILD;
3210
3211 led_array = devm_kzalloc(&spmi->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07003212 (sizeof(struct qpnp_led_data) * num_leds), GFP_KERNEL);
Amy Malochef9490c62012-11-27 19:26:04 -08003213 if (!led_array) {
Amy Malochef3d5a062012-08-16 19:14:11 -07003214 dev_err(&spmi->dev, "Unable to allocate memory\n");
3215 return -ENOMEM;
3216 }
3217
Amy Malochea5ca5552012-10-23 13:34:46 -07003218 for_each_child_of_node(node, temp) {
Amy Malochef9490c62012-11-27 19:26:04 -08003219 led = &led_array[parsed_leds];
3220 led->num_leds = num_leds;
Amy Malochea5ca5552012-10-23 13:34:46 -07003221 led->spmi_dev = spmi;
Amy Malochef3d5a062012-08-16 19:14:11 -07003222
Amy Malochea5ca5552012-10-23 13:34:46 -07003223 led_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
3224 if (!led_resource) {
3225 dev_err(&spmi->dev, "Unable to get LED base address\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003226 rc = -ENXIO;
3227 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003228 }
3229 led->base = led_resource->start;
Amy Malochef3d5a062012-08-16 19:14:11 -07003230
Amy Malochea5ca5552012-10-23 13:34:46 -07003231 rc = of_property_read_string(temp, "label", &led_label);
Amy Malochef3d5a062012-08-16 19:14:11 -07003232 if (rc < 0) {
3233 dev_err(&led->spmi_dev->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07003234 "Failure reading label, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003235 goto fail_id_check;
Amy Malochef3d5a062012-08-16 19:14:11 -07003236 }
Amy Malochea5ca5552012-10-23 13:34:46 -07003237
3238 rc = of_property_read_string(temp, "linux,name",
3239 &led->cdev.name);
3240 if (rc < 0) {
3241 dev_err(&led->spmi_dev->dev,
3242 "Failure reading led name, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003243 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003244 }
3245
3246 rc = of_property_read_u32(temp, "qcom,max-current",
3247 &led->max_current);
3248 if (rc < 0) {
3249 dev_err(&led->spmi_dev->dev,
3250 "Failure reading max_current, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003251 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003252 }
3253
3254 rc = of_property_read_u32(temp, "qcom,id", &led->id);
3255 if (rc < 0) {
3256 dev_err(&led->spmi_dev->dev,
3257 "Failure reading led id, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003258 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003259 }
3260
3261 rc = qpnp_get_common_configs(led, temp);
3262 if (rc) {
3263 dev_err(&led->spmi_dev->dev,
3264 "Failure reading common led configuration," \
3265 " rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003266 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003267 }
3268
3269 led->cdev.brightness_set = qpnp_led_set;
3270 led->cdev.brightness_get = qpnp_led_get;
3271
3272 if (strncmp(led_label, "wled", sizeof("wled")) == 0) {
3273 rc = qpnp_get_config_wled(led, temp);
3274 if (rc < 0) {
3275 dev_err(&led->spmi_dev->dev,
3276 "Unable to read wled config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003277 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003278 }
Amy Maloche864a6d52012-10-03 15:58:12 -07003279 } else if (strncmp(led_label, "flash", sizeof("flash"))
3280 == 0) {
Chun Zhang0d6ca072013-07-30 21:08:39 -07003281 if (!of_find_property(node, "flash-boost-supply", NULL))
Chun Zhangc3b505b2013-06-03 19:01:49 -07003282 regulator_probe = true;
3283 rc = qpnp_get_config_flash(led, temp, &regulator_probe);
Amy Maloche864a6d52012-10-03 15:58:12 -07003284 if (rc < 0) {
3285 dev_err(&led->spmi_dev->dev,
3286 "Unable to read flash config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003287 goto fail_id_check;
Amy Maloche864a6d52012-10-03 15:58:12 -07003288 }
Amy Malocheeea7b592012-10-03 15:59:36 -07003289 } else if (strncmp(led_label, "rgb", sizeof("rgb")) == 0) {
3290 rc = qpnp_get_config_rgb(led, temp);
3291 if (rc < 0) {
3292 dev_err(&led->spmi_dev->dev,
3293 "Unable to read rgb config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003294 goto fail_id_check;
Amy Malocheeea7b592012-10-03 15:59:36 -07003295 }
Amy Malochef3813742013-04-11 19:33:47 -07003296 } else if (strncmp(led_label, "mpp", sizeof("mpp")) == 0) {
3297 rc = qpnp_get_config_mpp(led, temp);
3298 if (rc < 0) {
3299 dev_err(&led->spmi_dev->dev,
3300 "Unable to read mpp config data\n");
Amy Malochea2726f02013-05-10 10:19:03 -07003301 goto fail_id_check;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303302 }
3303 } else if (strncmp(led_label, "kpdbl", sizeof("kpdbl")) == 0) {
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303304 num_kpbl_leds_on = 0;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303305 rc = qpnp_get_config_kpdbl(led, temp);
3306 if (rc < 0) {
3307 dev_err(&led->spmi_dev->dev,
3308 "Unable to read kpdbl config data\n");
Amy Malochef3813742013-04-11 19:33:47 -07003309 goto fail_id_check;
3310 }
Amy Malochea5ca5552012-10-23 13:34:46 -07003311 } else {
3312 dev_err(&led->spmi_dev->dev, "No LED matching label\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003313 rc = -EINVAL;
3314 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003315 }
3316
Chun Zhang815a1832013-06-20 13:47:13 -07003317 mutex_init(&led->lock);
3318 INIT_WORK(&led->work, qpnp_led_work);
Amy Malochea5ca5552012-10-23 13:34:46 -07003319
3320 rc = qpnp_led_initialize(led);
3321 if (rc < 0)
3322 goto fail_id_check;
3323
3324 rc = qpnp_led_set_max_brightness(led);
3325 if (rc < 0)
3326 goto fail_id_check;
3327
3328 rc = led_classdev_register(&spmi->dev, &led->cdev);
3329 if (rc) {
3330 dev_err(&spmi->dev, "unable to register led %d,rc=%d\n",
3331 led->id, rc);
3332 goto fail_id_check;
3333 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07003334
3335 if (led->id == QPNP_ID_FLASH1_LED0 ||
3336 led->id == QPNP_ID_FLASH1_LED1) {
3337 rc = sysfs_create_group(&led->cdev.dev->kobj,
3338 &led_attr_group);
3339 if (rc)
3340 goto fail_id_check;
3341
3342 }
3343
Amy Malochea5c4ed82013-06-05 11:05:28 -07003344 if (led->id == QPNP_ID_LED_MPP) {
3345 if (!led->mpp_cfg->pwm_cfg)
3346 break;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003347 if (led->mpp_cfg->pwm_cfg->mode == PWM_MODE) {
3348 rc = sysfs_create_group(&led->cdev.dev->kobj,
3349 &pwm_attr_group);
3350 if (rc)
3351 goto fail_id_check;
3352 }
Amy Malochea5c4ed82013-06-05 11:05:28 -07003353 if (led->mpp_cfg->pwm_cfg->use_blink) {
3354 rc = sysfs_create_group(&led->cdev.dev->kobj,
3355 &blink_attr_group);
3356 if (rc)
3357 goto fail_id_check;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003358
3359 rc = sysfs_create_group(&led->cdev.dev->kobj,
3360 &lpg_attr_group);
3361 if (rc)
3362 goto fail_id_check;
3363 } else if (led->mpp_cfg->pwm_cfg->mode == LPG_MODE) {
3364 rc = sysfs_create_group(&led->cdev.dev->kobj,
3365 &lpg_attr_group);
3366 if (rc)
3367 goto fail_id_check;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003368 }
3369 } else if ((led->id == QPNP_ID_RGB_RED) ||
3370 (led->id == QPNP_ID_RGB_GREEN) ||
3371 (led->id == QPNP_ID_RGB_BLUE)) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07003372 if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
3373 rc = sysfs_create_group(&led->cdev.dev->kobj,
3374 &pwm_attr_group);
3375 if (rc)
3376 goto fail_id_check;
3377 }
Amy Malochea5c4ed82013-06-05 11:05:28 -07003378 if (led->rgb_cfg->pwm_cfg->use_blink) {
3379 rc = sysfs_create_group(&led->cdev.dev->kobj,
3380 &blink_attr_group);
3381 if (rc)
3382 goto fail_id_check;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003383
3384 rc = sysfs_create_group(&led->cdev.dev->kobj,
3385 &lpg_attr_group);
3386 if (rc)
3387 goto fail_id_check;
3388 } else if (led->rgb_cfg->pwm_cfg->mode == LPG_MODE) {
3389 rc = sysfs_create_group(&led->cdev.dev->kobj,
3390 &lpg_attr_group);
3391 if (rc)
3392 goto fail_id_check;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003393 }
3394 }
3395
Amy Malochea5ca5552012-10-23 13:34:46 -07003396 /* configure default state */
Asaf Penso55ac8472013-01-21 21:17:37 +02003397 if (led->default_on) {
Amy Malochea5ca5552012-10-23 13:34:46 -07003398 led->cdev.brightness = led->cdev.max_brightness;
Chun Zhang815a1832013-06-20 13:47:13 -07003399 __qpnp_led_work(led, led->cdev.brightness);
Asaf Penso55ac8472013-01-21 21:17:37 +02003400 if (led->turn_off_delay_ms > 0)
3401 qpnp_led_turn_off(led);
3402 } else
Amy Malochea5ca5552012-10-23 13:34:46 -07003403 led->cdev.brightness = LED_OFF;
3404
Amy Malochef9490c62012-11-27 19:26:04 -08003405 parsed_leds++;
Amy Malochef3d5a062012-08-16 19:14:11 -07003406 }
Amy Malochef9490c62012-11-27 19:26:04 -08003407 dev_set_drvdata(&spmi->dev, led_array);
Amy Malochef3d5a062012-08-16 19:14:11 -07003408 return 0;
3409
3410fail_id_check:
Chun Zhang815a1832013-06-20 13:47:13 -07003411 for (i = 0; i < parsed_leds; i++) {
3412 mutex_destroy(&led_array[i].lock);
Amy Malochef9490c62012-11-27 19:26:04 -08003413 led_classdev_unregister(&led_array[i].cdev);
Chun Zhang815a1832013-06-20 13:47:13 -07003414 }
3415
Amy Malochef3d5a062012-08-16 19:14:11 -07003416 return rc;
3417}
3418
3419static int __devexit qpnp_leds_remove(struct spmi_device *spmi)
3420{
Amy Malochef9490c62012-11-27 19:26:04 -08003421 struct qpnp_led_data *led_array = dev_get_drvdata(&spmi->dev);
3422 int i, parsed_leds = led_array->num_leds;
Amy Malochef3d5a062012-08-16 19:14:11 -07003423
Amy Malochebc97c0d22013-03-24 22:06:16 -07003424 for (i = 0; i < parsed_leds; i++) {
Chun Zhang815a1832013-06-20 13:47:13 -07003425 cancel_work_sync(&led_array[i].work);
3426 mutex_destroy(&led_array[i].lock);
Amy Malochef9490c62012-11-27 19:26:04 -08003427 led_classdev_unregister(&led_array[i].cdev);
Amy Malochebc97c0d22013-03-24 22:06:16 -07003428 switch (led_array[i].id) {
3429 case QPNP_ID_WLED:
3430 break;
3431 case QPNP_ID_FLASH1_LED0:
3432 case QPNP_ID_FLASH1_LED1:
Chun Zhangda8ad0f2013-07-17 14:46:47 -07003433 if (led_array[i].flash_cfg->flash_reg_get)
Chun Zhangc3b505b2013-06-03 19:01:49 -07003434 regulator_put(led_array[i].flash_cfg-> \
3435 flash_boost_reg);
Chun Zhangdf2d3062013-06-25 20:14:46 -07003436 if (led_array[i].flash_cfg->torch_enable)
3437 regulator_put(led_array[i].flash_cfg->\
3438 torch_boost_reg);
Amy Malochebc97c0d22013-03-24 22:06:16 -07003439 sysfs_remove_group(&led_array[i].cdev.dev->kobj,
3440 &led_attr_group);
3441 break;
3442 case QPNP_ID_RGB_RED:
3443 case QPNP_ID_RGB_GREEN:
3444 case QPNP_ID_RGB_BLUE:
Amy Maloche4cf9f322013-05-29 15:53:46 -07003445 if (led_array[i].rgb_cfg->pwm_cfg->mode == PWM_MODE)
3446 sysfs_remove_group(&led_array[i].cdev.dev->\
3447 kobj, &pwm_attr_group);
3448 if (led_array[i].rgb_cfg->pwm_cfg->use_blink) {
3449 sysfs_remove_group(&led_array[i].cdev.dev->\
3450 kobj, &blink_attr_group);
3451 sysfs_remove_group(&led_array[i].cdev.dev->\
3452 kobj, &lpg_attr_group);
3453 } else if (led_array[i].rgb_cfg->pwm_cfg->mode\
3454 == LPG_MODE)
3455 sysfs_remove_group(&led_array[i].cdev.dev->\
3456 kobj, &lpg_attr_group);
3457 break;
3458 case QPNP_ID_LED_MPP:
3459 if (!led_array[i].mpp_cfg->pwm_cfg)
3460 break;
3461 if (led_array[i].mpp_cfg->pwm_cfg->mode == PWM_MODE)
3462 sysfs_remove_group(&led_array[i].cdev.dev->\
3463 kobj, &pwm_attr_group);
3464 if (led_array[i].mpp_cfg->pwm_cfg->use_blink) {
3465 sysfs_remove_group(&led_array[i].cdev.dev->\
3466 kobj, &blink_attr_group);
3467 sysfs_remove_group(&led_array[i].cdev.dev->\
3468 kobj, &lpg_attr_group);
3469 } else if (led_array[i].mpp_cfg->pwm_cfg->mode\
3470 == LPG_MODE)
3471 sysfs_remove_group(&led_array[i].cdev.dev->\
3472 kobj, &lpg_attr_group);
3473 break;
Amy Malochebc97c0d22013-03-24 22:06:16 -07003474 default:
3475 dev_err(&led_array[i].spmi_dev->dev,
3476 "Invalid LED(%d)\n",
3477 led_array[i].id);
3478 return -EINVAL;
3479 }
3480 }
Amy Malochef3d5a062012-08-16 19:14:11 -07003481
3482 return 0;
3483}
Himanshu Aggarwal4eac46c2013-11-27 13:46:53 +05303484
3485#ifdef CONFIG_OF
Amy Malochef3d5a062012-08-16 19:14:11 -07003486static struct of_device_id spmi_match_table[] = {
Himanshu Aggarwal4eac46c2013-11-27 13:46:53 +05303487 { .compatible = "qcom,leds-qpnp",},
3488 { },
Amy Malochef3d5a062012-08-16 19:14:11 -07003489};
Himanshu Aggarwal4eac46c2013-11-27 13:46:53 +05303490#else
3491#define spmi_match_table NULL
3492#endif
Amy Malochef3d5a062012-08-16 19:14:11 -07003493
3494static struct spmi_driver qpnp_leds_driver = {
3495 .driver = {
3496 .name = "qcom,leds-qpnp",
3497 .of_match_table = spmi_match_table,
3498 },
3499 .probe = qpnp_leds_probe,
3500 .remove = __devexit_p(qpnp_leds_remove),
3501};
3502
3503static int __init qpnp_led_init(void)
3504{
3505 return spmi_driver_register(&qpnp_leds_driver);
3506}
3507module_init(qpnp_led_init);
3508
3509static void __exit qpnp_led_exit(void)
3510{
3511 spmi_driver_unregister(&qpnp_leds_driver);
3512}
3513module_exit(qpnp_led_exit);
3514
3515MODULE_DESCRIPTION("QPNP LEDs driver");
3516MODULE_LICENSE("GPL v2");
3517MODULE_ALIAS("leds:leds-qpnp");
Amy Maloche864a6d52012-10-03 15:58:12 -07003518