blob: dd0299eb97edf760f7f4947c9ab2b6ab45e8be26 [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
Chunmei Caif094d072014-08-06 13:58:50 +0800399 * @flash_wa_reg - flash regulator for wa
Amy Maloche864a6d52012-10-03 15:58:12 -0700400 */
401struct flash_config_data {
402 u8 current_prgm;
403 u8 clamp_curr;
404 u8 headroom;
405 u8 duration;
406 u8 enable_module;
407 u8 trigger_flash;
408 u8 startup_dly;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700409 u8 strobe_type;
Chun Zhang0d6ca072013-07-30 21:08:39 -0700410 u8 peripheral_subtype;
Amy Maloche864a6d52012-10-03 15:58:12 -0700411 u16 current_addr;
412 u16 second_addr;
413 bool safety_timer;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700414 bool torch_enable;
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700415 bool flash_reg_get;
Chun Zhangc3b505b2013-06-03 19:01:49 -0700416 bool flash_on;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700417 bool torch_on;
Chun Zhangc3b505b2013-06-03 19:01:49 -0700418 struct regulator *flash_boost_reg;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700419 struct regulator *torch_boost_reg;
Chunmei Caif094d072014-08-06 13:58:50 +0800420 struct regulator *flash_wa_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -0700421};
422
423/**
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530424 * kpdbl_config_data - kpdbl configuration data
Amy Malochea2726f02013-05-10 10:19:03 -0700425 * @pwm_cfg - device pwm configuration
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530426 * @mode - running mode: pwm or lut
427 * @row_id - row id of the led
428 * @row_src_vbst - 0 for vph_pwr and 1 for vbst
429 * @row_src_en - enable row source
430 * @always_on - always on row
431 * @lut_params - lut parameters to be used by pwm driver
432 * @duty_cycles - duty cycles for lut
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530433 */
434struct kpdbl_config_data {
Amy Malochea2726f02013-05-10 10:19:03 -0700435 struct pwm_config_data *pwm_cfg;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530436 u32 row_id;
437 bool row_src_vbst;
438 bool row_src_en;
439 bool always_on;
440 struct pwm_duty_cycles *duty_cycles;
441 struct lut_params lut_params;
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530442};
443
444/**
Amy Malocheeea7b592012-10-03 15:59:36 -0700445 * rgb_config_data - rgb configuration data
Amy Malochea2726f02013-05-10 10:19:03 -0700446 * @pwm_cfg - device pwm configuration
447 * @enable - bits to enable led
Amy Malocheeea7b592012-10-03 15:59:36 -0700448 */
449struct rgb_config_data {
Amy Malochea2726f02013-05-10 10:19:03 -0700450 struct pwm_config_data *pwm_cfg;
Amy Malocheeea7b592012-10-03 15:59:36 -0700451 u8 enable;
452};
453
454/**
Amy Malochef3d5a062012-08-16 19:14:11 -0700455 * struct qpnp_led_data - internal led data structure
456 * @led_classdev - led class device
Asaf Penso55ac8472013-01-21 21:17:37 +0200457 * @delayed_work - delayed work for turning off the LED
Chun Zhang815a1832013-06-20 13:47:13 -0700458 * @work - workqueue for led
Amy Malochef3d5a062012-08-16 19:14:11 -0700459 * @id - led index
460 * @base_reg - base register given in device tree
461 * @lock - to protect the transactions
462 * @reg - cached value of led register
Amy Malochea5ca5552012-10-23 13:34:46 -0700463 * @num_leds - number of leds in the module
Amy Malochef3d5a062012-08-16 19:14:11 -0700464 * @max_current - maximum current supported by LED
465 * @default_on - true: default state max, false, default state 0
Asaf Penso55ac8472013-01-21 21:17:37 +0200466 * @turn_off_delay_ms - number of msec before turning off the LED
Amy Malochef3d5a062012-08-16 19:14:11 -0700467 */
468struct qpnp_led_data {
469 struct led_classdev cdev;
470 struct spmi_device *spmi_dev;
Asaf Penso55ac8472013-01-21 21:17:37 +0200471 struct delayed_work dwork;
Chun Zhang815a1832013-06-20 13:47:13 -0700472 struct work_struct work;
Amy Malochef3d5a062012-08-16 19:14:11 -0700473 int id;
474 u16 base;
475 u8 reg;
Amy Malochea5ca5552012-10-23 13:34:46 -0700476 u8 num_leds;
Chun Zhang815a1832013-06-20 13:47:13 -0700477 struct mutex lock;
Amy Malochef3d5a062012-08-16 19:14:11 -0700478 struct wled_config_data *wled_cfg;
Amy Maloche864a6d52012-10-03 15:58:12 -0700479 struct flash_config_data *flash_cfg;
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530480 struct kpdbl_config_data *kpdbl_cfg;
Amy Malocheeea7b592012-10-03 15:59:36 -0700481 struct rgb_config_data *rgb_cfg;
Amy Malochef3813742013-04-11 19:33:47 -0700482 struct mpp_config_data *mpp_cfg;
Amy Malochef3d5a062012-08-16 19:14:11 -0700483 int max_current;
484 bool default_on;
Asaf Penso55ac8472013-01-21 21:17:37 +0200485 int turn_off_delay_ms;
Amy Malochef3d5a062012-08-16 19:14:11 -0700486};
487
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530488static int num_kpbl_leds_on;
Abinaya P9e5dc3e2014-04-14 15:38:11 +0530489static DEFINE_MUTEX(flash_lock);
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530490
Amy Malochef3d5a062012-08-16 19:14:11 -0700491static int
492qpnp_led_masked_write(struct qpnp_led_data *led, u16 addr, u8 mask, u8 val)
493{
494 int rc;
495 u8 reg;
496
497 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
498 addr, &reg, 1);
499 if (rc) {
500 dev_err(&led->spmi_dev->dev,
501 "Unable to read from addr=%x, rc(%d)\n", addr, rc);
502 }
503
504 reg &= ~mask;
505 reg |= val;
506
507 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
508 addr, &reg, 1);
509 if (rc)
510 dev_err(&led->spmi_dev->dev,
511 "Unable to write to addr=%x, rc(%d)\n", addr, rc);
512 return rc;
513}
514
Amy Malochea5ca5552012-10-23 13:34:46 -0700515static void qpnp_dump_regs(struct qpnp_led_data *led, u8 regs[], u8 array_size)
516{
517 int i;
518 u8 val;
519
520 pr_debug("===== %s LED register dump start =====\n", led->cdev.name);
521 for (i = 0; i < array_size; i++) {
522 spmi_ext_register_readl(led->spmi_dev->ctrl,
523 led->spmi_dev->sid,
524 led->base + regs[i],
525 &val, sizeof(val));
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530526 pr_debug("%s: 0x%x = 0x%x\n", led->cdev.name,
527 led->base + regs[i], val);
Amy Malochea5ca5552012-10-23 13:34:46 -0700528 }
529 pr_debug("===== %s LED register dump end =====\n", led->cdev.name);
530}
531
Amy Maloche0150b5e2013-08-15 18:18:32 -0700532static int qpnp_wled_sync(struct qpnp_led_data *led)
533{
534 int rc;
535 u8 val;
536
537 /* sync */
538 val = WLED_SYNC_VAL;
539 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
540 WLED_SYNC_REG(led->base), &val, 1);
541 if (rc) {
542 dev_err(&led->spmi_dev->dev,
543 "WLED set sync reg failed(%d)\n", rc);
544 return rc;
545 }
546
547 val = WLED_SYNC_RESET_VAL;
548 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
549 WLED_SYNC_REG(led->base), &val, 1);
550 if (rc) {
551 dev_err(&led->spmi_dev->dev,
552 "WLED reset sync reg failed(%d)\n", rc);
553 return rc;
554 }
555 return 0;
556}
557
Amy Malochef3d5a062012-08-16 19:14:11 -0700558static int qpnp_wled_set(struct qpnp_led_data *led)
559{
Amy Maloched55fdb82013-02-26 18:11:57 -0800560 int rc, duty, level;
Amy Maloche0150b5e2013-08-15 18:18:32 -0700561 u8 val, i, num_wled_strings, sink_val;
562
563 num_wled_strings = led->wled_cfg->num_strings;
Amy Malochef3d5a062012-08-16 19:14:11 -0700564
565 level = led->cdev.brightness;
566
567 if (level > WLED_MAX_LEVEL)
568 level = WLED_MAX_LEVEL;
569 if (level == 0) {
Amy Maloche0150b5e2013-08-15 18:18:32 -0700570 for (i = 0; i < num_wled_strings; i++) {
571 rc = qpnp_led_masked_write(led,
572 WLED_FULL_SCALE_REG(led->base, i),
573 WLED_MAX_CURR_MASK, WLED_NO_CURRENT);
574 if (rc) {
575 dev_err(&led->spmi_dev->dev,
576 "Write max current failure (%d)\n",
577 rc);
578 return rc;
579 }
580 }
581
582 rc = qpnp_wled_sync(led);
583 if (rc) {
584 dev_err(&led->spmi_dev->dev,
585 "WLED sync failed(%d)\n", rc);
586 return rc;
587 }
588
589 rc = spmi_ext_register_readl(led->spmi_dev->ctrl,
590 led->spmi_dev->sid, WLED_CURR_SINK_REG(led->base),
591 &sink_val, 1);
592 if (rc) {
593 dev_err(&led->spmi_dev->dev,
594 "WLED read sink reg failed(%d)\n", rc);
595 return rc;
596 }
597
598 if (led->wled_cfg->pmic_version == PMIC_VER_8026) {
599 val = WLED_DISABLE_ALL_SINKS;
600 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
601 led->spmi_dev->sid,
602 WLED_CURR_SINK_REG(led->base), &val, 1);
603 if (rc) {
604 dev_err(&led->spmi_dev->dev,
605 "WLED write sink reg failed(%d)\n", rc);
606 return rc;
607 }
608
609 usleep(WLED_OVP_DELAY);
610 }
611
Amy Malochef3d5a062012-08-16 19:14:11 -0700612 val = WLED_BOOST_OFF;
613 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
614 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
615 &val, 1);
616 if (rc) {
617 dev_err(&led->spmi_dev->dev,
618 "WLED write ctrl reg failed(%d)\n", rc);
619 return rc;
620 }
Amy Maloche0150b5e2013-08-15 18:18:32 -0700621
622 for (i = 0; i < num_wled_strings; i++) {
623 rc = qpnp_led_masked_write(led,
624 WLED_FULL_SCALE_REG(led->base, i),
Abinaya P03cee1d2014-02-17 17:51:53 +0530625 WLED_MAX_CURR_MASK, (u8)led->max_current);
Amy Maloche0150b5e2013-08-15 18:18:32 -0700626 if (rc) {
627 dev_err(&led->spmi_dev->dev,
628 "Write max current failure (%d)\n",
629 rc);
630 return rc;
631 }
632 }
633
634 rc = qpnp_wled_sync(led);
635 if (rc) {
636 dev_err(&led->spmi_dev->dev,
637 "WLED sync failed(%d)\n", rc);
638 return rc;
639 }
640
641 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
642 led->spmi_dev->sid, WLED_CURR_SINK_REG(led->base),
643 &sink_val, 1);
644 if (rc) {
645 dev_err(&led->spmi_dev->dev,
646 "WLED write sink reg failed(%d)\n", rc);
647 return rc;
648 }
649
Amy Malochef3d5a062012-08-16 19:14:11 -0700650 } else {
651 val = WLED_BOOST_ON;
652 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
653 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
654 &val, 1);
655 if (rc) {
656 dev_err(&led->spmi_dev->dev,
657 "WLED write ctrl reg failed(%d)\n", rc);
658 return rc;
659 }
660 }
661
662 duty = (WLED_MAX_DUTY_CYCLE * level) / WLED_MAX_LEVEL;
663
Amy Malochef3d5a062012-08-16 19:14:11 -0700664 /* program brightness control registers */
665 for (i = 0; i < num_wled_strings; i++) {
666 rc = qpnp_led_masked_write(led,
667 WLED_BRIGHTNESS_CNTL_MSB(led->base, i), WLED_MSB_MASK,
668 (duty >> WLED_8_BIT_SHFT) & WLED_4_BIT_MASK);
669 if (rc) {
670 dev_err(&led->spmi_dev->dev,
671 "WLED set brightness MSB failed(%d)\n", rc);
672 return rc;
673 }
674 val = duty & WLED_8_BIT_MASK;
675 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
676 led->spmi_dev->sid,
677 WLED_BRIGHTNESS_CNTL_LSB(led->base, i), &val, 1);
678 if (rc) {
679 dev_err(&led->spmi_dev->dev,
680 "WLED set brightness LSB failed(%d)\n", rc);
681 return rc;
682 }
683 }
684
Amy Maloche0150b5e2013-08-15 18:18:32 -0700685 rc = qpnp_wled_sync(led);
Amy Malochef3d5a062012-08-16 19:14:11 -0700686 if (rc) {
Amy Maloche0150b5e2013-08-15 18:18:32 -0700687 dev_err(&led->spmi_dev->dev, "WLED sync failed(%d)\n", rc);
Amy Malochef3d5a062012-08-16 19:14:11 -0700688 return rc;
689 }
690 return 0;
691}
692
Amy Malochef3813742013-04-11 19:33:47 -0700693static int qpnp_mpp_set(struct qpnp_led_data *led)
694{
Abinaya P03cee1d2014-02-17 17:51:53 +0530695 int rc;
696 u8 val;
Xu Kai9d395ce2014-02-28 13:11:09 +0800697 int duty_us, duty_ns, period_us;
Amy Malochef3813742013-04-11 19:33:47 -0700698
699 if (led->cdev.brightness) {
Chun Zhang874c9ab2013-07-08 17:18:34 -0700700 if (led->cdev.brightness < led->mpp_cfg->min_brightness) {
701 dev_warn(&led->spmi_dev->dev,
702 "brightness is less than supported..." \
703 "set to minimum supported\n");
704 led->cdev.brightness = led->mpp_cfg->min_brightness;
705 }
706
Amy Maloche4cf9f322013-05-29 15:53:46 -0700707 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
708 if (!led->mpp_cfg->pwm_cfg->blinking) {
709 led->mpp_cfg->pwm_cfg->mode =
710 led->mpp_cfg->pwm_cfg->default_mode;
711 led->mpp_cfg->pwm_mode =
712 led->mpp_cfg->pwm_cfg->default_mode;
713 }
714 }
Amy Malochea2726f02013-05-10 10:19:03 -0700715 if (led->mpp_cfg->pwm_mode == PWM_MODE) {
Amy Malochea2726f02013-05-10 10:19:03 -0700716 /*config pwm for brightness scaling*/
Xu Kai9d395ce2014-02-28 13:11:09 +0800717 period_us = led->mpp_cfg->pwm_cfg->pwm_period_us;
718 if (period_us > INT_MAX / NSEC_PER_USEC) {
719 duty_us = (period_us * led->cdev.brightness) /
720 LED_FULL;
721 rc = pwm_config_us(
722 led->mpp_cfg->pwm_cfg->pwm_dev,
Amy Malochea2726f02013-05-10 10:19:03 -0700723 duty_us,
Xu Kai9d395ce2014-02-28 13:11:09 +0800724 period_us);
725 } else {
726 duty_ns = ((period_us * NSEC_PER_USEC) /
727 LED_FULL) * led->cdev.brightness;
728 rc = pwm_config(
729 led->mpp_cfg->pwm_cfg->pwm_dev,
730 duty_ns,
731 period_us * NSEC_PER_USEC);
732 }
Amy Malochea2726f02013-05-10 10:19:03 -0700733 if (rc < 0) {
734 dev_err(&led->spmi_dev->dev, "Failed to " \
735 "configure pwm for new values\n");
736 return rc;
737 }
Amy Malochef3813742013-04-11 19:33:47 -0700738 }
739
Amy Malochea2726f02013-05-10 10:19:03 -0700740 if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
741 pwm_enable(led->mpp_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka38ec7f32013-09-11 13:01:48 +0530742 else {
743 if (led->cdev.brightness < LED_MPP_CURRENT_MIN)
744 led->cdev.brightness = LED_MPP_CURRENT_MIN;
745
746 val = (led->cdev.brightness / LED_MPP_CURRENT_MIN) - 1;
747
748 rc = qpnp_led_masked_write(led,
749 LED_MPP_SINK_CTRL(led->base),
750 LED_MPP_SINK_MASK, val);
751 if (rc) {
752 dev_err(&led->spmi_dev->dev,
753 "Failed to write sink control reg\n");
754 return rc;
755 }
756 }
Amy Malochea2726f02013-05-10 10:19:03 -0700757
Amy Malochece59f662013-05-02 10:59:53 -0700758 val = (led->mpp_cfg->source_sel & LED_MPP_SRC_MASK) |
759 (led->mpp_cfg->mode_ctrl & LED_MPP_MODE_CTRL_MASK);
Amy Malochef3813742013-04-11 19:33:47 -0700760
761 rc = qpnp_led_masked_write(led,
Amy Malochea2726f02013-05-10 10:19:03 -0700762 LED_MPP_MODE_CTRL(led->base), LED_MPP_MODE_MASK,
763 val);
Amy Malochef3813742013-04-11 19:33:47 -0700764 if (rc) {
765 dev_err(&led->spmi_dev->dev,
766 "Failed to write led mode reg\n");
767 return rc;
768 }
769
770 rc = qpnp_led_masked_write(led,
771 LED_MPP_EN_CTRL(led->base), LED_MPP_EN_MASK,
772 LED_MPP_EN_ENABLE);
Amy Malochea2726f02013-05-10 10:19:03 -0700773 if (rc) {
774 dev_err(&led->spmi_dev->dev,
775 "Failed to write led enable " \
776 "reg\n");
777 return rc;
778 }
Amy Malochef3813742013-04-11 19:33:47 -0700779 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -0700780 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
781 led->mpp_cfg->pwm_cfg->mode =
782 led->mpp_cfg->pwm_cfg->default_mode;
783 led->mpp_cfg->pwm_mode =
784 led->mpp_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -0700785 pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
Amy Maloche4cf9f322013-05-29 15:53:46 -0700786 }
Amy Malochef3813742013-04-11 19:33:47 -0700787 rc = qpnp_led_masked_write(led,
788 LED_MPP_MODE_CTRL(led->base),
789 LED_MPP_MODE_MASK,
790 LED_MPP_MODE_DISABLE);
791 if (rc) {
792 dev_err(&led->spmi_dev->dev,
793 "Failed to write led mode reg\n");
794 return rc;
795 }
796
797 rc = qpnp_led_masked_write(led,
798 LED_MPP_EN_CTRL(led->base),
799 LED_MPP_EN_MASK,
800 LED_MPP_EN_DISABLE);
801 if (rc) {
802 dev_err(&led->spmi_dev->dev,
803 "Failed to write led enable reg\n");
804 return rc;
805 }
806 }
807
Amy Maloche4cf9f322013-05-29 15:53:46 -0700808 if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
809 led->mpp_cfg->pwm_cfg->blinking = false;
Amy Malochef3813742013-04-11 19:33:47 -0700810 qpnp_dump_regs(led, mpp_debug_regs, ARRAY_SIZE(mpp_debug_regs));
811
812 return 0;
813}
814
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700815static int qpnp_flash_regulator_operate(struct qpnp_led_data *led, bool on)
816{
817 int rc, i;
818 struct qpnp_led_data *led_array;
819 bool regulator_on = false;
820
821 led_array = dev_get_drvdata(&led->spmi_dev->dev);
822 if (!led_array) {
823 dev_err(&led->spmi_dev->dev,
824 "Unable to get LED array\n");
825 return -EINVAL;
826 }
827
828 for (i = 0; i < led->num_leds; i++)
829 regulator_on |= led_array[i].flash_cfg->flash_on;
830
831 if (!on)
832 goto regulator_turn_off;
833
834 if (!regulator_on && !led->flash_cfg->flash_on) {
835 for (i = 0; i < led->num_leds; i++) {
836 if (led_array[i].flash_cfg->flash_reg_get) {
837 rc = regulator_enable(
Chunmei Caif094d072014-08-06 13:58:50 +0800838 led_array[i].flash_cfg->flash_wa_reg);
839 if (rc) {
840 dev_err(&led->spmi_dev->dev,
841 "Flash_wa regulator enable failed(%d)\n",
842 rc);
843 return rc;
844 }
845
846 rc = regulator_enable(
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700847 led_array[i].flash_cfg->\
848 flash_boost_reg);
849 if (rc) {
850 dev_err(&led->spmi_dev->dev,
851 "Regulator enable failed(%d)\n",
852 rc);
853 return rc;
854 }
855 led->flash_cfg->flash_on = true;
856 }
857 break;
858 }
859 }
860
861 return 0;
862
863regulator_turn_off:
864 if (regulator_on && led->flash_cfg->flash_on) {
865 for (i = 0; i < led->num_leds; i++) {
866 if (led_array[i].flash_cfg->flash_reg_get) {
Chun Zhang9d5ff672013-08-01 18:18:26 -0700867 rc = qpnp_led_masked_write(led,
868 FLASH_ENABLE_CONTROL(led->base),
869 FLASH_ENABLE_MASK,
870 FLASH_DISABLE_ALL);
871 if (rc) {
872 dev_err(&led->spmi_dev->dev,
873 "Enable reg write failed(%d)\n",
874 rc);
875 }
876
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700877 rc = regulator_disable(led_array[i].flash_cfg->\
878 flash_boost_reg);
879 if (rc) {
880 dev_err(&led->spmi_dev->dev,
881 "Regulator disable failed(%d)\n",
882 rc);
883 return rc;
884 }
Chunmei Caif094d072014-08-06 13:58:50 +0800885 rc = regulator_disable(
886 led_array[i].flash_cfg->flash_wa_reg);
887 if (rc) {
888 dev_err(&led->spmi_dev->dev,
889 "Flash_wa regulator disable failed(%d)\n",
890 rc);
891 return rc;
892 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700893 led->flash_cfg->flash_on = false;
894 }
895 break;
896 }
897 }
898
899 return 0;
900}
901
Chun Zhangdf2d3062013-06-25 20:14:46 -0700902static int qpnp_torch_regulator_operate(struct qpnp_led_data *led, bool on)
Amy Maloche864a6d52012-10-03 15:58:12 -0700903{
904 int rc;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700905
906 if (!on)
907 goto regulator_turn_off;
908
909 if (!led->flash_cfg->torch_on) {
910 rc = regulator_enable(led->flash_cfg->torch_boost_reg);
911 if (rc) {
912 dev_err(&led->spmi_dev->dev,
913 "Regulator enable failed(%d)\n", rc);
914 return rc;
915 }
916 led->flash_cfg->torch_on = true;
917 }
918 return 0;
919
920regulator_turn_off:
921 if (led->flash_cfg->torch_on) {
Chun Zhang9d5ff672013-08-01 18:18:26 -0700922 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
923 FLASH_ENABLE_MODULE_MASK, FLASH_DISABLE_ALL);
924 if (rc) {
925 dev_err(&led->spmi_dev->dev,
926 "Enable reg write failed(%d)\n", rc);
927 }
928
Chun Zhangdf2d3062013-06-25 20:14:46 -0700929 rc = regulator_disable(led->flash_cfg->torch_boost_reg);
930 if (rc) {
931 dev_err(&led->spmi_dev->dev,
932 "Regulator disable failed(%d)\n", rc);
933 return rc;
934 }
935 led->flash_cfg->torch_on = false;
936 }
937 return 0;
938}
939
940static int qpnp_flash_set(struct qpnp_led_data *led)
941{
942 int rc, error;
Amy Maloche864a6d52012-10-03 15:58:12 -0700943 int val = led->cdev.brightness;
944
Chun Zhang9c1fbaa2013-06-17 15:31:18 -0700945 if (led->flash_cfg->torch_enable)
946 led->flash_cfg->current_prgm =
947 (val * TORCH_MAX_LEVEL / led->max_current);
948 else
949 led->flash_cfg->current_prgm =
950 (val * FLASH_MAX_LEVEL / led->max_current);
Amy Maloche864a6d52012-10-03 15:58:12 -0700951
Amy Maloche864a6d52012-10-03 15:58:12 -0700952 /* Set led current */
953 if (val > 0) {
Amy Malochebc97c0d22013-03-24 22:06:16 -0700954 if (led->flash_cfg->torch_enable) {
Chun Zhang0d6ca072013-07-30 21:08:39 -0700955 if (led->flash_cfg->peripheral_subtype ==
956 FLASH_SUBTYPE_DUAL) {
957 rc = qpnp_torch_regulator_operate(led, true);
958 if (rc) {
959 dev_err(&led->spmi_dev->dev,
Chun Zhangdf2d3062013-06-25 20:14:46 -0700960 "Torch regulator operate failed(%d)\n",
961 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700962 return rc;
963 }
964 } else if (led->flash_cfg->peripheral_subtype ==
965 FLASH_SUBTYPE_SINGLE) {
966 rc = qpnp_flash_regulator_operate(led, true);
967 if (rc) {
968 dev_err(&led->spmi_dev->dev,
969 "Flash regulator operate failed(%d)\n",
970 rc);
971 goto error_flash_set;
972 }
Chun Zhangdf2d3062013-06-25 20:14:46 -0700973 }
974
Amy Malochebc97c0d22013-03-24 22:06:16 -0700975 rc = qpnp_led_masked_write(led,
976 FLASH_LED_UNLOCK_SECURE(led->base),
977 FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
978 if (rc) {
979 dev_err(&led->spmi_dev->dev,
980 "Secure reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700981 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700982 }
983
984 rc = qpnp_led_masked_write(led,
985 FLASH_LED_TORCH(led->base),
986 FLASH_TORCH_MASK, FLASH_LED_TORCH_ENABLE);
987 if (rc) {
988 dev_err(&led->spmi_dev->dev,
989 "Torch reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700990 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700991 }
992
Amy Malochebc97c0d22013-03-24 22:06:16 -0700993 rc = qpnp_led_masked_write(led,
994 led->flash_cfg->current_addr,
Chun Zhange8954cf2013-05-02 11:14:34 -0700995 FLASH_CURRENT_MASK,
996 led->flash_cfg->current_prgm);
Amy Malochebc97c0d22013-03-24 22:06:16 -0700997 if (rc) {
998 dev_err(&led->spmi_dev->dev,
999 "Current reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -07001000 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001001 }
1002
1003 rc = qpnp_led_masked_write(led,
1004 led->flash_cfg->second_addr,
Chun Zhange8954cf2013-05-02 11:14:34 -07001005 FLASH_CURRENT_MASK,
1006 led->flash_cfg->current_prgm);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001007 if (rc) {
1008 dev_err(&led->spmi_dev->dev,
1009 "2nd Current reg write failed(%d)\n",
1010 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -07001011 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001012 }
1013
Chun Zhange8954cf2013-05-02 11:14:34 -07001014 qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
1015 FLASH_CURRENT_MASK,
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07001016 TORCH_MAX_LEVEL);
Chun Zhange8954cf2013-05-02 11:14:34 -07001017 if (rc) {
1018 dev_err(&led->spmi_dev->dev,
1019 "Max current reg write failed(%d)\n",
1020 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -07001021 goto error_reg_write;
Chun Zhange8954cf2013-05-02 11:14:34 -07001022 }
1023
Chun Zhang9d5ff672013-08-01 18:18:26 -07001024 rc = qpnp_led_masked_write(led,
1025 FLASH_ENABLE_CONTROL(led->base),
1026 FLASH_ENABLE_MASK,
1027 led->flash_cfg->enable_module);
1028 if (rc) {
1029 dev_err(&led->spmi_dev->dev,
1030 "Enable reg write failed(%d)\n",
1031 rc);
1032 goto error_reg_write;
1033 }
1034
Abinaya Pb6e3ba42014-01-07 15:32:31 +05301035 if (!led->flash_cfg->strobe_type)
1036 led->flash_cfg->trigger_flash &=
1037 ~FLASH_HW_SW_STROBE_SEL_MASK;
1038 else
1039 led->flash_cfg->trigger_flash |=
1040 FLASH_HW_SW_STROBE_SEL_MASK;
1041
Chun Zhang9d5ff672013-08-01 18:18:26 -07001042 rc = qpnp_led_masked_write(led,
1043 FLASH_LED_STROBE_CTRL(led->base),
1044 led->flash_cfg->trigger_flash,
1045 led->flash_cfg->trigger_flash);
1046 if (rc) {
1047 dev_err(&led->spmi_dev->dev,
1048 "LED %d strobe reg write failed(%d)\n",
1049 led->id, rc);
1050 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001051 }
1052 } else {
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001053 rc = qpnp_flash_regulator_operate(led, true);
1054 if (rc) {
1055 dev_err(&led->spmi_dev->dev,
1056 "Flash regulator operate failed(%d)\n",
1057 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001058 goto error_flash_set;
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001059 }
1060
Chun Zhange8954cf2013-05-02 11:14:34 -07001061 /* Set flash safety timer */
Amy Malochebc97c0d22013-03-24 22:06:16 -07001062 rc = qpnp_led_masked_write(led,
Chun Zhange8954cf2013-05-02 11:14:34 -07001063 FLASH_SAFETY_TIMER(led->base),
1064 FLASH_SAFETY_TIMER_MASK,
1065 led->flash_cfg->duration);
1066 if (rc) {
1067 dev_err(&led->spmi_dev->dev,
1068 "Safety timer reg write failed(%d)\n",
1069 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001070 goto error_flash_set;
Chun Zhange8954cf2013-05-02 11:14:34 -07001071 }
1072
1073 /* Set max current */
1074 rc = qpnp_led_masked_write(led,
1075 FLASH_MAX_CURR(led->base), FLASH_CURRENT_MASK,
1076 FLASH_MAX_LEVEL);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001077 if (rc) {
1078 dev_err(&led->spmi_dev->dev,
1079 "Max current reg write failed(%d)\n",
1080 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001081 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001082 }
1083
Chun Zhange8954cf2013-05-02 11:14:34 -07001084 /* Set clamp current */
1085 rc = qpnp_led_masked_write(led,
1086 FLASH_CLAMP_CURR(led->base),
1087 FLASH_CURRENT_MASK,
1088 led->flash_cfg->clamp_curr);
1089 if (rc) {
1090 dev_err(&led->spmi_dev->dev,
1091 "Clamp current reg write failed(%d)\n",
1092 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001093 goto error_flash_set;
Chun Zhange8954cf2013-05-02 11:14:34 -07001094 }
1095
Amy Malochebc97c0d22013-03-24 22:06:16 -07001096 rc = qpnp_led_masked_write(led,
1097 led->flash_cfg->current_addr,
1098 FLASH_CURRENT_MASK,
1099 led->flash_cfg->current_prgm);
1100 if (rc) {
1101 dev_err(&led->spmi_dev->dev,
1102 "Current reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001103 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001104 }
1105
1106 rc = qpnp_led_masked_write(led,
Amy Malochebc97c0d22013-03-24 22:06:16 -07001107 FLASH_ENABLE_CONTROL(led->base),
Chun Zhang9d5ff672013-08-01 18:18:26 -07001108 led->flash_cfg->enable_module,
1109 led->flash_cfg->enable_module);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001110 if (rc) {
1111 dev_err(&led->spmi_dev->dev,
1112 "Enable reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001113 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001114 }
Amy Maloche38b9aae2013-02-07 12:15:34 -08001115
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07001116 /*
1117 * Add 1ms delay for bharger enter stable state
1118 */
1119 usleep(FLASH_RAMP_UP_DELAY_US);
1120
Abinaya Pb6e3ba42014-01-07 15:32:31 +05301121 if (!led->flash_cfg->strobe_type)
1122 led->flash_cfg->trigger_flash &=
1123 ~FLASH_HW_SW_STROBE_SEL_MASK;
1124 else
1125 led->flash_cfg->trigger_flash |=
1126 FLASH_HW_SW_STROBE_SEL_MASK;
1127
1128 rc = qpnp_led_masked_write(led,
1129 FLASH_LED_STROBE_CTRL(led->base),
1130 led->flash_cfg->trigger_flash,
1131 led->flash_cfg->trigger_flash);
1132 if (rc) {
1133 dev_err(&led->spmi_dev->dev,
1134 "LED %d strobe reg write failed(%d)\n",
1135 led->id, rc);
1136 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001137 }
Amy Maloche38b9aae2013-02-07 12:15:34 -08001138 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001139 } else {
Chun Zhangdf2d3062013-06-25 20:14:46 -07001140 rc = qpnp_led_masked_write(led,
1141 FLASH_LED_STROBE_CTRL(led->base),
Chun Zhang9d5ff672013-08-01 18:18:26 -07001142 led->flash_cfg->trigger_flash,
Chun Zhangdf2d3062013-06-25 20:14:46 -07001143 FLASH_DISABLE_ALL);
1144 if (rc) {
1145 dev_err(&led->spmi_dev->dev,
1146 "LED %d flash write failed(%d)\n", led->id, rc);
1147 if (led->flash_cfg->torch_enable)
1148 goto error_torch_set;
1149 else
1150 goto error_flash_set;
1151 }
1152
Amy Malochebc97c0d22013-03-24 22:06:16 -07001153 if (led->flash_cfg->torch_enable) {
1154 rc = qpnp_led_masked_write(led,
1155 FLASH_LED_UNLOCK_SECURE(led->base),
1156 FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
1157 if (rc) {
1158 dev_err(&led->spmi_dev->dev,
1159 "Secure reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001160 goto error_torch_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001161 }
1162
1163 rc = qpnp_led_masked_write(led,
Chun Zhange8954cf2013-05-02 11:14:34 -07001164 FLASH_LED_TORCH(led->base),
1165 FLASH_TORCH_MASK,
1166 FLASH_LED_TORCH_DISABLE);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001167 if (rc) {
1168 dev_err(&led->spmi_dev->dev,
Chun Zhange8954cf2013-05-02 11:14:34 -07001169 "Torch reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001170 goto error_torch_set;
1171 }
1172
Chun Zhang0d6ca072013-07-30 21:08:39 -07001173 if (led->flash_cfg->peripheral_subtype ==
1174 FLASH_SUBTYPE_DUAL) {
1175 rc = qpnp_torch_regulator_operate(led, false);
1176 if (rc) {
1177 dev_err(&led->spmi_dev->dev,
1178 "Torch regulator operate failed(%d)\n",
1179 rc);
1180 return rc;
1181 }
1182 } else if (led->flash_cfg->peripheral_subtype ==
1183 FLASH_SUBTYPE_SINGLE) {
1184 rc = qpnp_flash_regulator_operate(led, false);
1185 if (rc) {
1186 dev_err(&led->spmi_dev->dev,
1187 "Flash regulator operate failed(%d)\n",
1188 rc);
1189 return rc;
1190 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07001191 }
Chun Zhangdf2d3062013-06-25 20:14:46 -07001192 } else {
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07001193 /*
1194 * Disable module after ramp down complete for stable
1195 * behavior
1196 */
1197 usleep(FLASH_RAMP_DN_DELAY_US);
1198
Chun Zhang9d5ff672013-08-01 18:18:26 -07001199 rc = qpnp_led_masked_write(led,
1200 FLASH_ENABLE_CONTROL(led->base),
1201 led->flash_cfg->enable_module &
1202 ~FLASH_ENABLE_MODULE_MASK,
1203 FLASH_DISABLE_ALL);
1204 if (rc) {
1205 dev_err(&led->spmi_dev->dev,
1206 "Enable reg write failed(%d)\n", rc);
1207 if (led->flash_cfg->torch_enable)
1208 goto error_torch_set;
1209 else
1210 goto error_flash_set;
1211 }
1212
Chun Zhangdf2d3062013-06-25 20:14:46 -07001213 rc = qpnp_flash_regulator_operate(led, false);
1214 if (rc) {
1215 dev_err(&led->spmi_dev->dev,
1216 "Flash regulator operate failed(%d)\n",
1217 rc);
1218 return rc;
1219 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001220 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001221 }
1222
Amy Malocheeea7b592012-10-03 15:59:36 -07001223 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
1224
1225 return 0;
Chun Zhangdf2d3062013-06-25 20:14:46 -07001226
Chun Zhang0d6ca072013-07-30 21:08:39 -07001227error_reg_write:
1228 if (led->flash_cfg->peripheral_subtype == FLASH_SUBTYPE_SINGLE)
1229 goto error_flash_set;
1230
Chun Zhangdf2d3062013-06-25 20:14:46 -07001231error_torch_set:
1232 error = qpnp_torch_regulator_operate(led, false);
1233 if (error) {
1234 dev_err(&led->spmi_dev->dev,
1235 "Torch regulator operate failed(%d)\n", rc);
1236 return error;
1237 }
1238 return rc;
1239
1240error_flash_set:
1241 error = qpnp_flash_regulator_operate(led, false);
1242 if (error) {
1243 dev_err(&led->spmi_dev->dev,
1244 "Flash regulator operate failed(%d)\n", rc);
1245 return error;
1246 }
1247 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07001248}
1249
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301250static int qpnp_kpdbl_set(struct qpnp_led_data *led)
1251{
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301252 int rc;
Xu Kai9d395ce2014-02-28 13:11:09 +08001253 int duty_us, duty_ns, period_us;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301254
1255 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001256 if (!led->kpdbl_cfg->pwm_cfg->blinking)
1257 led->kpdbl_cfg->pwm_cfg->mode =
1258 led->kpdbl_cfg->pwm_cfg->default_mode;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301259 if (!num_kpbl_leds_on) {
1260 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
1261 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
1262 if (rc) {
1263 dev_err(&led->spmi_dev->dev,
1264 "Enable reg write failed(%d)\n", rc);
1265 return rc;
1266 }
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301267 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301268
1269 if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
Xu Kai9d395ce2014-02-28 13:11:09 +08001270 period_us = led->kpdbl_cfg->pwm_cfg->pwm_period_us;
1271 if (period_us > INT_MAX / NSEC_PER_USEC) {
1272 duty_us = (period_us * led->cdev.brightness) /
1273 KPDBL_MAX_LEVEL;
1274 rc = pwm_config_us(
1275 led->kpdbl_cfg->pwm_cfg->pwm_dev,
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301276 duty_us,
Xu Kai9d395ce2014-02-28 13:11:09 +08001277 period_us);
1278 } else {
1279 duty_ns = ((period_us * NSEC_PER_USEC) /
1280 KPDBL_MAX_LEVEL) * led->cdev.brightness;
1281 rc = pwm_config(
1282 led->kpdbl_cfg->pwm_cfg->pwm_dev,
1283 duty_ns,
1284 period_us * NSEC_PER_USEC);
1285 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301286 if (rc < 0) {
1287 dev_err(&led->spmi_dev->dev, "pwm config failed\n");
1288 return rc;
1289 }
1290 }
1291
Amy Malochea2726f02013-05-10 10:19:03 -07001292 rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301293 if (rc < 0) {
1294 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1295 return rc;
1296 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301297
1298 num_kpbl_leds_on++;
1299
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301300 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001301 led->kpdbl_cfg->pwm_cfg->mode =
1302 led->kpdbl_cfg->pwm_cfg->default_mode;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301303
1304 if (led->kpdbl_cfg->always_on) {
Xu Kai0d9a3e22013-12-17 13:53:56 +08001305 rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0,
Xu Kai9d395ce2014-02-28 13:11:09 +08001306 led->kpdbl_cfg->pwm_cfg->pwm_period_us);
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301307 if (rc < 0) {
1308 dev_err(&led->spmi_dev->dev,
1309 "pwm config failed\n");
1310 return rc;
1311 }
1312
1313 rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
1314 if (rc < 0) {
1315 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1316 return rc;
1317 }
1318 } else
1319 pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
1320
1321 if (num_kpbl_leds_on > 0)
1322 num_kpbl_leds_on--;
1323
1324 if (!num_kpbl_leds_on) {
1325 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
1326 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_DIS);
1327 if (rc) {
1328 dev_err(&led->spmi_dev->dev,
1329 "Failed to write led enable reg\n");
1330 return rc;
1331 }
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301332 }
1333 }
1334
Amy Maloche4cf9f322013-05-29 15:53:46 -07001335 led->kpdbl_cfg->pwm_cfg->blinking = false;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301336
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301337 qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
1338
1339 return 0;
1340}
1341
Amy Malocheeea7b592012-10-03 15:59:36 -07001342static int qpnp_rgb_set(struct qpnp_led_data *led)
1343{
Amy Malocheeea7b592012-10-03 15:59:36 -07001344 int rc;
Xu Kai9d395ce2014-02-28 13:11:09 +08001345 int duty_us, duty_ns, period_us;
Amy Malocheeea7b592012-10-03 15:59:36 -07001346
1347 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001348 if (!led->rgb_cfg->pwm_cfg->blinking)
1349 led->rgb_cfg->pwm_cfg->mode =
1350 led->rgb_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07001351 if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
Xu Kai9d395ce2014-02-28 13:11:09 +08001352 period_us = led->rgb_cfg->pwm_cfg->pwm_period_us;
1353 if (period_us > INT_MAX / NSEC_PER_USEC) {
1354 duty_us = (period_us * led->cdev.brightness) /
1355 LED_FULL;
1356 rc = pwm_config_us(
1357 led->rgb_cfg->pwm_cfg->pwm_dev,
Xu Kai0d9a3e22013-12-17 13:53:56 +08001358 duty_us,
Xu Kai9d395ce2014-02-28 13:11:09 +08001359 period_us);
1360 } else {
1361 duty_ns = ((period_us * NSEC_PER_USEC) /
1362 LED_FULL) * led->cdev.brightness;
1363 rc = pwm_config(
1364 led->rgb_cfg->pwm_cfg->pwm_dev,
1365 duty_ns,
1366 period_us * NSEC_PER_USEC);
1367 }
Amy Malocheeea7b592012-10-03 15:59:36 -07001368 if (rc < 0) {
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301369 dev_err(&led->spmi_dev->dev,
1370 "pwm config failed\n");
Amy Malocheeea7b592012-10-03 15:59:36 -07001371 return rc;
1372 }
1373 }
1374 rc = qpnp_led_masked_write(led,
1375 RGB_LED_EN_CTL(led->base),
1376 led->rgb_cfg->enable, led->rgb_cfg->enable);
1377 if (rc) {
1378 dev_err(&led->spmi_dev->dev,
1379 "Failed to write led enable reg\n");
1380 return rc;
1381 }
Amy Malochea2726f02013-05-10 10:19:03 -07001382
1383 rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301384 if (rc < 0) {
1385 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1386 return rc;
1387 }
Amy Malocheeea7b592012-10-03 15:59:36 -07001388 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001389 led->rgb_cfg->pwm_cfg->mode =
1390 led->rgb_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07001391 pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
Amy Malocheeea7b592012-10-03 15:59:36 -07001392 rc = qpnp_led_masked_write(led,
1393 RGB_LED_EN_CTL(led->base),
1394 led->rgb_cfg->enable, RGB_LED_DISABLE);
1395 if (rc) {
1396 dev_err(&led->spmi_dev->dev,
1397 "Failed to write led enable reg\n");
1398 return rc;
1399 }
1400 }
1401
Amy Maloche4cf9f322013-05-29 15:53:46 -07001402 led->rgb_cfg->pwm_cfg->blinking = false;
Amy Malocheeea7b592012-10-03 15:59:36 -07001403 qpnp_dump_regs(led, rgb_pwm_debug_regs, ARRAY_SIZE(rgb_pwm_debug_regs));
1404
Amy Maloche864a6d52012-10-03 15:58:12 -07001405 return 0;
1406}
1407
Amy Malochef3d5a062012-08-16 19:14:11 -07001408static void qpnp_led_set(struct led_classdev *led_cdev,
1409 enum led_brightness value)
1410{
Amy Malochef3d5a062012-08-16 19:14:11 -07001411 struct qpnp_led_data *led;
1412
1413 led = container_of(led_cdev, struct qpnp_led_data, cdev);
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301414 if (value < LED_OFF) {
Amy Malochea5ca5552012-10-23 13:34:46 -07001415 dev_err(&led->spmi_dev->dev, "Invalid brightness value\n");
Amy Malochef3d5a062012-08-16 19:14:11 -07001416 return;
1417 }
1418
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301419 if (value > led->cdev.max_brightness)
1420 value = led->cdev.max_brightness;
1421
Chun Zhang815a1832013-06-20 13:47:13 -07001422 led->cdev.brightness = value;
1423 schedule_work(&led->work);
1424}
1425
1426static void __qpnp_led_work(struct qpnp_led_data *led,
1427 enum led_brightness value)
1428{
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001429 int rc;
Chun Zhangc3b505b2013-06-03 19:01:49 -07001430
Abinaya P9e5dc3e2014-04-14 15:38:11 +05301431 if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1)
1432 mutex_lock(&flash_lock);
1433 else
1434 mutex_lock(&led->lock);
Amy Malochef3d5a062012-08-16 19:14:11 -07001435
1436 switch (led->id) {
1437 case QPNP_ID_WLED:
1438 rc = qpnp_wled_set(led);
1439 if (rc < 0)
Amy Malochea5ca5552012-10-23 13:34:46 -07001440 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -07001441 "WLED set brightness failed (%d)\n", rc);
1442 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07001443 case QPNP_ID_FLASH1_LED0:
1444 case QPNP_ID_FLASH1_LED1:
1445 rc = qpnp_flash_set(led);
1446 if (rc < 0)
1447 dev_err(&led->spmi_dev->dev,
1448 "FLASH set brightness failed (%d)\n", rc);
1449 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07001450 case QPNP_ID_RGB_RED:
1451 case QPNP_ID_RGB_GREEN:
1452 case QPNP_ID_RGB_BLUE:
1453 rc = qpnp_rgb_set(led);
1454 if (rc < 0)
1455 dev_err(&led->spmi_dev->dev,
1456 "RGB set brightness failed (%d)\n", rc);
1457 break;
Amy Malochef3813742013-04-11 19:33:47 -07001458 case QPNP_ID_LED_MPP:
1459 rc = qpnp_mpp_set(led);
1460 if (rc < 0)
1461 dev_err(&led->spmi_dev->dev,
1462 "MPP set brightness failed (%d)\n", rc);
Amy Maloche14c9eb32013-05-06 13:37:58 -07001463 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301464 case QPNP_ID_KPDBL:
1465 rc = qpnp_kpdbl_set(led);
1466 if (rc < 0)
1467 dev_err(&led->spmi_dev->dev,
1468 "KPDBL set brightness failed (%d)\n", rc);
Amy Malochef3813742013-04-11 19:33:47 -07001469 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07001470 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07001471 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -07001472 break;
1473 }
Abinaya P9e5dc3e2014-04-14 15:38:11 +05301474 if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1)
1475 mutex_unlock(&flash_lock);
1476 else
1477 mutex_unlock(&led->lock);
Chun Zhangc3b505b2013-06-03 19:01:49 -07001478
Amy Malochef3d5a062012-08-16 19:14:11 -07001479}
1480
Chun Zhang815a1832013-06-20 13:47:13 -07001481static void qpnp_led_work(struct work_struct *work)
1482{
1483 struct qpnp_led_data *led = container_of(work,
1484 struct qpnp_led_data, work);
1485
1486 __qpnp_led_work(led, led->cdev.brightness);
1487
1488 return;
1489}
1490
Amy Malochef3d5a062012-08-16 19:14:11 -07001491static int __devinit qpnp_led_set_max_brightness(struct qpnp_led_data *led)
1492{
1493 switch (led->id) {
1494 case QPNP_ID_WLED:
1495 led->cdev.max_brightness = WLED_MAX_LEVEL;
1496 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07001497 case QPNP_ID_FLASH1_LED0:
1498 case QPNP_ID_FLASH1_LED1:
1499 led->cdev.max_brightness = led->max_current;
1500 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07001501 case QPNP_ID_RGB_RED:
1502 case QPNP_ID_RGB_GREEN:
1503 case QPNP_ID_RGB_BLUE:
1504 led->cdev.max_brightness = RGB_MAX_LEVEL;
1505 break;
Amy Malochef3813742013-04-11 19:33:47 -07001506 case QPNP_ID_LED_MPP:
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05301507 if (led->mpp_cfg->pwm_mode == MANUAL_MODE)
1508 led->cdev.max_brightness = led->max_current;
1509 else
1510 led->cdev.max_brightness = MPP_MAX_LEVEL;
Amy Malochef3813742013-04-11 19:33:47 -07001511 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301512 case QPNP_ID_KPDBL:
1513 led->cdev.max_brightness = KPDBL_MAX_LEVEL;
1514 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07001515 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07001516 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -07001517 return -EINVAL;
1518 }
1519
1520 return 0;
1521}
1522
1523static enum led_brightness qpnp_led_get(struct led_classdev *led_cdev)
1524{
1525 struct qpnp_led_data *led;
1526
1527 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1528
1529 return led->cdev.brightness;
1530}
1531
Asaf Penso55ac8472013-01-21 21:17:37 +02001532static void qpnp_led_turn_off_delayed(struct work_struct *work)
1533{
1534 struct delayed_work *dwork = to_delayed_work(work);
1535 struct qpnp_led_data *led
1536 = container_of(dwork, struct qpnp_led_data, dwork);
1537
1538 led->cdev.brightness = LED_OFF;
1539 qpnp_led_set(&led->cdev, led->cdev.brightness);
1540}
1541
1542static void qpnp_led_turn_off(struct qpnp_led_data *led)
1543{
1544 INIT_DELAYED_WORK(&led->dwork, qpnp_led_turn_off_delayed);
1545 schedule_delayed_work(&led->dwork,
1546 msecs_to_jiffies(led->turn_off_delay_ms));
1547}
1548
Amy Malochef3d5a062012-08-16 19:14:11 -07001549static int __devinit qpnp_wled_init(struct qpnp_led_data *led)
1550{
1551 int rc, i;
1552 u8 num_wled_strings;
1553
1554 num_wled_strings = led->wled_cfg->num_strings;
1555
1556 /* verify ranges */
Abinaya Pe65d2cd2013-12-23 19:31:57 +05301557 if (led->wled_cfg->ovp_val > WLED_OVP_27V) {
Amy Malochef3d5a062012-08-16 19:14:11 -07001558 dev_err(&led->spmi_dev->dev, "Invalid ovp value\n");
1559 return -EINVAL;
1560 }
1561
1562 if (led->wled_cfg->boost_curr_lim > WLED_CURR_LIMIT_1680mA) {
1563 dev_err(&led->spmi_dev->dev, "Invalid boost current limit\n");
1564 return -EINVAL;
1565 }
1566
1567 if (led->wled_cfg->cp_select > WLED_CP_SELECT_MAX) {
1568 dev_err(&led->spmi_dev->dev, "Invalid pole capacitance\n");
1569 return -EINVAL;
1570 }
1571
1572 if ((led->max_current > WLED_MAX_CURR)) {
1573 dev_err(&led->spmi_dev->dev, "Invalid max current\n");
1574 return -EINVAL;
1575 }
1576
1577 if ((led->wled_cfg->ctrl_delay_us % WLED_CTL_DLY_STEP) ||
1578 (led->wled_cfg->ctrl_delay_us > WLED_CTL_DLY_MAX)) {
1579 dev_err(&led->spmi_dev->dev, "Invalid control delay\n");
1580 return -EINVAL;
1581 }
1582
1583 /* program over voltage protection threshold */
1584 rc = qpnp_led_masked_write(led, WLED_OVP_CFG_REG(led->base),
1585 WLED_OVP_VAL_MASK,
1586 (led->wled_cfg->ovp_val << WLED_OVP_VAL_BIT_SHFT));
1587 if (rc) {
1588 dev_err(&led->spmi_dev->dev,
1589 "WLED OVP reg write failed(%d)\n", rc);
1590 return rc;
1591 }
1592
1593 /* program current boost limit */
1594 rc = qpnp_led_masked_write(led, WLED_BOOST_LIMIT_REG(led->base),
1595 WLED_BOOST_LIMIT_MASK, led->wled_cfg->boost_curr_lim);
1596 if (rc) {
1597 dev_err(&led->spmi_dev->dev,
1598 "WLED boost limit reg write failed(%d)\n", rc);
1599 return rc;
1600 }
1601
1602 /* program output feedback */
1603 rc = qpnp_led_masked_write(led, WLED_FDBCK_CTRL_REG(led->base),
1604 WLED_OP_FDBCK_MASK,
1605 (led->wled_cfg->op_fdbck << WLED_OP_FDBCK_BIT_SHFT));
1606 if (rc) {
1607 dev_err(&led->spmi_dev->dev,
1608 "WLED fdbck ctrl reg write failed(%d)\n", rc);
1609 return rc;
1610 }
1611
1612 /* program switch frequency */
Amy Maloche9eccb4c2013-07-12 14:31:56 -07001613 rc = qpnp_led_masked_write(led,
1614 WLED_SWITCHING_FREQ_REG(led->base),
Amy Malochef3d5a062012-08-16 19:14:11 -07001615 WLED_SWITCH_FREQ_MASK, led->wled_cfg->switch_freq);
1616 if (rc) {
1617 dev_err(&led->spmi_dev->dev,
Amy Maloche9eccb4c2013-07-12 14:31:56 -07001618 "WLED switch freq reg write failed(%d)\n", rc);
Amy Malochef3d5a062012-08-16 19:14:11 -07001619 return rc;
1620 }
1621
1622 /* program current sink */
1623 if (led->wled_cfg->cs_out_en) {
1624 rc = qpnp_led_masked_write(led, WLED_CURR_SINK_REG(led->base),
1625 WLED_CURR_SINK_MASK,
Amy Maloche832d90a2013-01-07 10:15:29 -08001626 (((1 << led->wled_cfg->num_strings) - 1)
1627 << WLED_CURR_SINK_SHFT));
Amy Malochef3d5a062012-08-16 19:14:11 -07001628 if (rc) {
1629 dev_err(&led->spmi_dev->dev,
1630 "WLED curr sink reg write failed(%d)\n", rc);
1631 return rc;
1632 }
1633 }
1634
1635 /* program high pole capacitance */
1636 rc = qpnp_led_masked_write(led, WLED_HIGH_POLE_CAP_REG(led->base),
1637 WLED_CP_SELECT_MASK, led->wled_cfg->cp_select);
1638 if (rc) {
1639 dev_err(&led->spmi_dev->dev,
1640 "WLED pole cap reg write failed(%d)\n", rc);
1641 return rc;
1642 }
1643
1644 /* program modulator, current mod src and cabc */
1645 for (i = 0; i < num_wled_strings; i++) {
1646 rc = qpnp_led_masked_write(led, WLED_MOD_EN_REG(led->base, i),
1647 WLED_NO_MASK, WLED_EN_MASK);
1648 if (rc) {
1649 dev_err(&led->spmi_dev->dev,
1650 "WLED mod enable reg write failed(%d)\n", rc);
1651 return rc;
1652 }
1653
1654 if (led->wled_cfg->dig_mod_gen_en) {
1655 rc = qpnp_led_masked_write(led,
Amy Maloched44516e2013-02-14 17:36:34 -08001656 WLED_MOD_SRC_SEL_REG(led->base, i),
Amy Malochef3d5a062012-08-16 19:14:11 -07001657 WLED_NO_MASK, WLED_USE_EXT_GEN_MOD_SRC);
1658 if (rc) {
1659 dev_err(&led->spmi_dev->dev,
1660 "WLED dig mod en reg write failed(%d)\n", rc);
1661 }
1662 }
1663
1664 rc = qpnp_led_masked_write(led,
1665 WLED_FULL_SCALE_REG(led->base, i), WLED_MAX_CURR_MASK,
Abinaya P03cee1d2014-02-17 17:51:53 +05301666 (u8)led->max_current);
Amy Malochef3d5a062012-08-16 19:14:11 -07001667 if (rc) {
1668 dev_err(&led->spmi_dev->dev,
1669 "WLED max current reg write failed(%d)\n", rc);
1670 return rc;
1671 }
1672
1673 }
1674
Sarada Prasanna Garnayak40f65022013-11-01 20:17:32 +05301675 /* Reset WLED enable register */
1676 rc = qpnp_led_masked_write(led, WLED_MOD_CTRL_REG(led->base),
1677 WLED_8_BIT_MASK, WLED_BOOST_OFF);
1678 if (rc) {
1679 dev_err(&led->spmi_dev->dev,
1680 "WLED write ctrl reg failed(%d)\n", rc);
1681 return rc;
1682 }
1683
Amy Malochef3d5a062012-08-16 19:14:11 -07001684 /* dump wled registers */
Amy Malochea5ca5552012-10-23 13:34:46 -07001685 qpnp_dump_regs(led, wled_debug_regs, ARRAY_SIZE(wled_debug_regs));
Amy Malochef3d5a062012-08-16 19:14:11 -07001686
1687 return 0;
1688}
1689
Amy Malochebc97c0d22013-03-24 22:06:16 -07001690static ssize_t led_mode_store(struct device *dev,
1691 struct device_attribute *attr,
1692 const char *buf, size_t count)
1693{
1694 struct qpnp_led_data *led;
1695 unsigned long state;
1696 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1697 ssize_t ret = -EINVAL;
1698
1699 ret = kstrtoul(buf, 10, &state);
1700 if (ret)
1701 return ret;
1702
1703 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1704
1705 /* '1' to enable torch mode; '0' to switch to flash mode */
1706 if (state == 1)
1707 led->flash_cfg->torch_enable = true;
1708 else
1709 led->flash_cfg->torch_enable = false;
1710
1711 return count;
1712}
1713
1714static ssize_t led_strobe_type_store(struct device *dev,
1715 struct device_attribute *attr,
1716 const char *buf, size_t count)
1717{
1718 struct qpnp_led_data *led;
1719 unsigned long state;
1720 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1721 ssize_t ret = -EINVAL;
1722
1723 ret = kstrtoul(buf, 10, &state);
1724 if (ret)
1725 return ret;
1726
1727 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1728
1729 /* '0' for sw strobe; '1' for hw strobe */
1730 if (state == 1)
1731 led->flash_cfg->strobe_type = 1;
1732 else
1733 led->flash_cfg->strobe_type = 0;
1734
1735 return count;
1736}
1737
Amy Malochea5c4ed82013-06-05 11:05:28 -07001738static int qpnp_pwm_init(struct pwm_config_data *pwm_cfg,
1739 struct spmi_device *spmi_dev,
1740 const char *name)
1741{
1742 int rc, start_idx, idx_len;
1743
1744 if (pwm_cfg->pwm_channel != -1) {
1745 pwm_cfg->pwm_dev =
1746 pwm_request(pwm_cfg->pwm_channel, name);
1747
1748 if (IS_ERR_OR_NULL(pwm_cfg->pwm_dev)) {
1749 dev_err(&spmi_dev->dev,
1750 "could not acquire PWM Channel %d, " \
1751 "error %ld\n",
1752 pwm_cfg->pwm_channel,
1753 PTR_ERR(pwm_cfg->pwm_dev));
1754 pwm_cfg->pwm_dev = NULL;
1755 return -ENODEV;
1756 }
1757
1758 if (pwm_cfg->mode == LPG_MODE) {
1759 start_idx =
1760 pwm_cfg->duty_cycles->start_idx;
1761 idx_len =
1762 pwm_cfg->duty_cycles->num_duty_pcts;
1763
1764 if (idx_len >= PWM_LUT_MAX_SIZE &&
1765 start_idx) {
1766 dev_err(&spmi_dev->dev,
1767 "Wrong LUT size or index\n");
1768 return -EINVAL;
1769 }
1770 if ((start_idx + idx_len) >
1771 PWM_LUT_MAX_SIZE) {
1772 dev_err(&spmi_dev->dev,
1773 "Exceed LUT limit\n");
1774 return -EINVAL;
1775 }
1776 rc = pwm_lut_config(pwm_cfg->pwm_dev,
Xu Kai757c2b22014-05-08 12:51:24 +08001777 pwm_cfg->pwm_period_us,
Amy Malochea5c4ed82013-06-05 11:05:28 -07001778 pwm_cfg->duty_cycles->duty_pcts,
1779 pwm_cfg->lut_params);
1780 if (rc < 0) {
1781 dev_err(&spmi_dev->dev, "Failed to " \
1782 "configure pwm LUT\n");
1783 return rc;
1784 }
1785 }
1786 } else {
1787 dev_err(&spmi_dev->dev,
1788 "Invalid PWM channel\n");
1789 return -EINVAL;
1790 }
1791
1792 return 0;
1793}
1794
Amy Maloche4cf9f322013-05-29 15:53:46 -07001795static ssize_t pwm_us_store(struct device *dev,
1796 struct device_attribute *attr,
1797 const char *buf, size_t count)
1798{
1799 struct qpnp_led_data *led;
1800 u32 pwm_us;
1801 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1802 ssize_t ret;
1803 u32 previous_pwm_us;
1804 struct pwm_config_data *pwm_cfg;
1805
1806 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1807
1808 ret = kstrtou32(buf, 10, &pwm_us);
1809 if (ret)
1810 return ret;
1811
1812 switch (led->id) {
1813 case QPNP_ID_LED_MPP:
1814 pwm_cfg = led->mpp_cfg->pwm_cfg;
1815 break;
1816 case QPNP_ID_RGB_RED:
1817 case QPNP_ID_RGB_GREEN:
1818 case QPNP_ID_RGB_BLUE:
1819 pwm_cfg = led->rgb_cfg->pwm_cfg;
1820 break;
1821 default:
1822 dev_err(&led->spmi_dev->dev,
1823 "Invalid LED id type for pwm_us\n");
1824 return -EINVAL;
1825 }
1826
1827 if (pwm_cfg->mode == LPG_MODE)
1828 pwm_cfg->blinking = true;
1829
1830 previous_pwm_us = pwm_cfg->pwm_period_us;
1831
1832 pwm_cfg->pwm_period_us = pwm_us;
1833 pwm_free(pwm_cfg->pwm_dev);
1834 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1835 if (ret) {
1836 pwm_cfg->pwm_period_us = previous_pwm_us;
1837 pwm_free(pwm_cfg->pwm_dev);
1838 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1839 qpnp_led_set(&led->cdev, led->cdev.brightness);
1840 dev_err(&led->spmi_dev->dev,
1841 "Failed to initialize pwm with new pwm_us value\n");
1842 return ret;
1843 }
1844 qpnp_led_set(&led->cdev, led->cdev.brightness);
1845 return count;
1846}
1847
1848static ssize_t pause_lo_store(struct device *dev,
1849 struct device_attribute *attr,
1850 const char *buf, size_t count)
1851{
1852 struct qpnp_led_data *led;
1853 u32 pause_lo;
1854 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1855 ssize_t ret;
1856 u32 previous_pause_lo;
1857 struct pwm_config_data *pwm_cfg;
1858
1859 ret = kstrtou32(buf, 10, &pause_lo);
1860 if (ret)
1861 return ret;
1862 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1863
1864 switch (led->id) {
1865 case QPNP_ID_LED_MPP:
1866 pwm_cfg = led->mpp_cfg->pwm_cfg;
1867 break;
1868 case QPNP_ID_RGB_RED:
1869 case QPNP_ID_RGB_GREEN:
1870 case QPNP_ID_RGB_BLUE:
1871 pwm_cfg = led->rgb_cfg->pwm_cfg;
1872 break;
1873 default:
1874 dev_err(&led->spmi_dev->dev,
1875 "Invalid LED id type for pause lo\n");
1876 return -EINVAL;
1877 }
1878
1879 if (pwm_cfg->mode == LPG_MODE)
1880 pwm_cfg->blinking = true;
1881
1882 previous_pause_lo = pwm_cfg->lut_params.lut_pause_lo;
1883
1884 pwm_free(pwm_cfg->pwm_dev);
1885 pwm_cfg->lut_params.lut_pause_lo = pause_lo;
1886 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1887 if (ret) {
1888 pwm_cfg->lut_params.lut_pause_lo = previous_pause_lo;
1889 pwm_free(pwm_cfg->pwm_dev);
1890 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1891 qpnp_led_set(&led->cdev, led->cdev.brightness);
1892 dev_err(&led->spmi_dev->dev,
1893 "Failed to initialize pwm with new pause lo value\n");
1894 return ret;
1895 }
1896 qpnp_led_set(&led->cdev, led->cdev.brightness);
1897 return count;
1898}
1899
1900static ssize_t pause_hi_store(struct device *dev,
1901 struct device_attribute *attr,
1902 const char *buf, size_t count)
1903{
1904 struct qpnp_led_data *led;
1905 u32 pause_hi;
1906 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1907 ssize_t ret;
1908 u32 previous_pause_hi;
1909 struct pwm_config_data *pwm_cfg;
1910
1911 ret = kstrtou32(buf, 10, &pause_hi);
1912 if (ret)
1913 return ret;
1914 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1915
1916 switch (led->id) {
1917 case QPNP_ID_LED_MPP:
1918 pwm_cfg = led->mpp_cfg->pwm_cfg;
1919 break;
1920 case QPNP_ID_RGB_RED:
1921 case QPNP_ID_RGB_GREEN:
1922 case QPNP_ID_RGB_BLUE:
1923 pwm_cfg = led->rgb_cfg->pwm_cfg;
1924 break;
1925 default:
1926 dev_err(&led->spmi_dev->dev,
1927 "Invalid LED id type for pause hi\n");
1928 return -EINVAL;
1929 }
1930
1931 if (pwm_cfg->mode == LPG_MODE)
1932 pwm_cfg->blinking = true;
1933
1934 previous_pause_hi = pwm_cfg->lut_params.lut_pause_hi;
1935
1936 pwm_free(pwm_cfg->pwm_dev);
1937 pwm_cfg->lut_params.lut_pause_hi = pause_hi;
1938 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1939 if (ret) {
1940 pwm_cfg->lut_params.lut_pause_hi = previous_pause_hi;
1941 pwm_free(pwm_cfg->pwm_dev);
1942 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1943 qpnp_led_set(&led->cdev, led->cdev.brightness);
1944 dev_err(&led->spmi_dev->dev,
1945 "Failed to initialize pwm with new pause hi value\n");
1946 return ret;
1947 }
1948 qpnp_led_set(&led->cdev, led->cdev.brightness);
1949 return count;
1950}
1951
1952static ssize_t start_idx_store(struct device *dev,
1953 struct device_attribute *attr,
1954 const char *buf, size_t count)
1955{
1956 struct qpnp_led_data *led;
1957 u32 start_idx;
1958 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1959 ssize_t ret;
1960 u32 previous_start_idx;
1961 struct pwm_config_data *pwm_cfg;
1962
1963 ret = kstrtou32(buf, 10, &start_idx);
1964 if (ret)
1965 return ret;
1966 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1967
1968 switch (led->id) {
1969 case QPNP_ID_LED_MPP:
1970 pwm_cfg = led->mpp_cfg->pwm_cfg;
1971 break;
1972 case QPNP_ID_RGB_RED:
1973 case QPNP_ID_RGB_GREEN:
1974 case QPNP_ID_RGB_BLUE:
1975 pwm_cfg = led->rgb_cfg->pwm_cfg;
1976 break;
1977 default:
1978 dev_err(&led->spmi_dev->dev,
1979 "Invalid LED id type for start idx\n");
1980 return -EINVAL;
1981 }
1982
1983 if (pwm_cfg->mode == LPG_MODE)
1984 pwm_cfg->blinking = true;
1985
1986 previous_start_idx = pwm_cfg->duty_cycles->start_idx;
1987 pwm_cfg->duty_cycles->start_idx = start_idx;
1988 pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
1989 pwm_free(pwm_cfg->pwm_dev);
1990 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1991 if (ret) {
1992 pwm_cfg->duty_cycles->start_idx = previous_start_idx;
1993 pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
1994 pwm_free(pwm_cfg->pwm_dev);
1995 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1996 qpnp_led_set(&led->cdev, led->cdev.brightness);
1997 dev_err(&led->spmi_dev->dev,
1998 "Failed to initialize pwm with new start idx value\n");
1999 return ret;
2000 }
2001 qpnp_led_set(&led->cdev, led->cdev.brightness);
2002 return count;
2003}
2004
2005static ssize_t ramp_step_ms_store(struct device *dev,
2006 struct device_attribute *attr,
2007 const char *buf, size_t count)
2008{
2009 struct qpnp_led_data *led;
2010 u32 ramp_step_ms;
2011 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2012 ssize_t ret;
2013 u32 previous_ramp_step_ms;
2014 struct pwm_config_data *pwm_cfg;
2015
2016 ret = kstrtou32(buf, 10, &ramp_step_ms);
2017 if (ret)
2018 return ret;
2019 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2020
2021 switch (led->id) {
2022 case QPNP_ID_LED_MPP:
2023 pwm_cfg = led->mpp_cfg->pwm_cfg;
2024 break;
2025 case QPNP_ID_RGB_RED:
2026 case QPNP_ID_RGB_GREEN:
2027 case QPNP_ID_RGB_BLUE:
2028 pwm_cfg = led->rgb_cfg->pwm_cfg;
2029 break;
2030 default:
2031 dev_err(&led->spmi_dev->dev,
2032 "Invalid LED id type for ramp step\n");
2033 return -EINVAL;
2034 }
2035
2036 if (pwm_cfg->mode == LPG_MODE)
2037 pwm_cfg->blinking = true;
2038
2039 previous_ramp_step_ms = pwm_cfg->lut_params.ramp_step_ms;
2040
2041 pwm_free(pwm_cfg->pwm_dev);
2042 pwm_cfg->lut_params.ramp_step_ms = ramp_step_ms;
2043 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2044 if (ret) {
2045 pwm_cfg->lut_params.ramp_step_ms = previous_ramp_step_ms;
2046 pwm_free(pwm_cfg->pwm_dev);
2047 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2048 qpnp_led_set(&led->cdev, led->cdev.brightness);
2049 dev_err(&led->spmi_dev->dev,
2050 "Failed to initialize pwm with new ramp step value\n");
2051 return ret;
2052 }
2053 qpnp_led_set(&led->cdev, led->cdev.brightness);
2054 return count;
2055}
2056
2057static ssize_t lut_flags_store(struct device *dev,
2058 struct device_attribute *attr,
2059 const char *buf, size_t count)
2060{
2061 struct qpnp_led_data *led;
2062 u32 lut_flags;
2063 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2064 ssize_t ret;
2065 u32 previous_lut_flags;
2066 struct pwm_config_data *pwm_cfg;
2067
2068 ret = kstrtou32(buf, 10, &lut_flags);
2069 if (ret)
2070 return ret;
2071 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2072
2073 switch (led->id) {
2074 case QPNP_ID_LED_MPP:
2075 pwm_cfg = led->mpp_cfg->pwm_cfg;
2076 break;
2077 case QPNP_ID_RGB_RED:
2078 case QPNP_ID_RGB_GREEN:
2079 case QPNP_ID_RGB_BLUE:
2080 pwm_cfg = led->rgb_cfg->pwm_cfg;
2081 break;
2082 default:
2083 dev_err(&led->spmi_dev->dev,
2084 "Invalid LED id type for lut flags\n");
2085 return -EINVAL;
2086 }
2087
2088 if (pwm_cfg->mode == LPG_MODE)
2089 pwm_cfg->blinking = true;
2090
2091 previous_lut_flags = pwm_cfg->lut_params.flags;
2092
2093 pwm_free(pwm_cfg->pwm_dev);
2094 pwm_cfg->lut_params.flags = lut_flags;
2095 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2096 if (ret) {
2097 pwm_cfg->lut_params.flags = previous_lut_flags;
2098 pwm_free(pwm_cfg->pwm_dev);
2099 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2100 qpnp_led_set(&led->cdev, led->cdev.brightness);
2101 dev_err(&led->spmi_dev->dev,
2102 "Failed to initialize pwm with new lut flags value\n");
2103 return ret;
2104 }
2105 qpnp_led_set(&led->cdev, led->cdev.brightness);
2106 return count;
2107}
2108
2109static ssize_t duty_pcts_store(struct device *dev,
2110 struct device_attribute *attr,
2111 const char *buf, size_t count)
2112{
2113 struct qpnp_led_data *led;
2114 int num_duty_pcts = 0;
2115 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2116 char *buffer;
2117 ssize_t ret;
2118 int i = 0;
2119 int max_duty_pcts;
2120 struct pwm_config_data *pwm_cfg;
2121 u32 previous_num_duty_pcts;
2122 int value;
2123 int *previous_duty_pcts;
2124
2125 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2126
2127 switch (led->id) {
2128 case QPNP_ID_LED_MPP:
2129 pwm_cfg = led->mpp_cfg->pwm_cfg;
2130 max_duty_pcts = PWM_LUT_MAX_SIZE;
2131 break;
2132 case QPNP_ID_RGB_RED:
2133 case QPNP_ID_RGB_GREEN:
2134 case QPNP_ID_RGB_BLUE:
2135 pwm_cfg = led->rgb_cfg->pwm_cfg;
2136 max_duty_pcts = PWM_LUT_MAX_SIZE;
2137 break;
2138 default:
2139 dev_err(&led->spmi_dev->dev,
2140 "Invalid LED id type for duty pcts\n");
2141 return -EINVAL;
2142 }
2143
2144 if (pwm_cfg->mode == LPG_MODE)
2145 pwm_cfg->blinking = true;
2146
2147 buffer = (char *)buf;
2148
2149 for (i = 0; i < max_duty_pcts; i++) {
2150 if (buffer == NULL)
2151 break;
2152 ret = sscanf((const char *)buffer, "%u,%s", &value, buffer);
2153 pwm_cfg->old_duty_pcts[i] = value;
2154 num_duty_pcts++;
2155 if (ret <= 1)
2156 break;
2157 }
2158
2159 if (num_duty_pcts >= max_duty_pcts) {
2160 dev_err(&led->spmi_dev->dev,
2161 "Number of duty pcts given exceeds max (%d)\n",
2162 max_duty_pcts);
2163 return -EINVAL;
2164 }
2165
2166 previous_num_duty_pcts = pwm_cfg->duty_cycles->num_duty_pcts;
2167 previous_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
2168
2169 pwm_cfg->duty_cycles->num_duty_pcts = num_duty_pcts;
2170 pwm_cfg->duty_cycles->duty_pcts = pwm_cfg->old_duty_pcts;
2171 pwm_cfg->old_duty_pcts = previous_duty_pcts;
2172 pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
2173
2174 pwm_free(pwm_cfg->pwm_dev);
2175 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2176 if (ret)
2177 goto restore;
2178
2179 qpnp_led_set(&led->cdev, led->cdev.brightness);
2180 return count;
2181
2182restore:
2183 dev_err(&led->spmi_dev->dev,
2184 "Failed to initialize pwm with new duty pcts value\n");
2185 pwm_cfg->duty_cycles->num_duty_pcts = previous_num_duty_pcts;
2186 pwm_cfg->old_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
2187 pwm_cfg->duty_cycles->duty_pcts = previous_duty_pcts;
2188 pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
2189 pwm_free(pwm_cfg->pwm_dev);
2190 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2191 qpnp_led_set(&led->cdev, led->cdev.brightness);
2192 return ret;
2193}
2194
Amy Malochea5c4ed82013-06-05 11:05:28 -07002195static void led_blink(struct qpnp_led_data *led,
2196 struct pwm_config_data *pwm_cfg)
2197{
Amy Malochea5c4ed82013-06-05 11:05:28 -07002198 if (pwm_cfg->use_blink) {
2199 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07002200 pwm_cfg->blinking = true;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002201 if (led->id == QPNP_ID_LED_MPP)
2202 led->mpp_cfg->pwm_mode = LPG_MODE;
2203 pwm_cfg->mode = LPG_MODE;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002204 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07002205 pwm_cfg->blinking = false;
2206 pwm_cfg->mode = pwm_cfg->default_mode;
2207 if (led->id == QPNP_ID_LED_MPP)
2208 led->mpp_cfg->pwm_mode = pwm_cfg->default_mode;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002209 }
Amy Maloche4cf9f322013-05-29 15:53:46 -07002210 pwm_free(pwm_cfg->pwm_dev);
2211 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2212 qpnp_led_set(&led->cdev, led->cdev.brightness);
Amy Malochea5c4ed82013-06-05 11:05:28 -07002213 }
2214}
2215
2216static ssize_t blink_store(struct device *dev,
2217 struct device_attribute *attr,
2218 const char *buf, size_t count)
2219{
2220 struct qpnp_led_data *led;
2221 unsigned long blinking;
2222 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2223 ssize_t ret = -EINVAL;
2224
2225 ret = kstrtoul(buf, 10, &blinking);
2226 if (ret)
2227 return ret;
2228 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2229 led->cdev.brightness = blinking ? led->cdev.max_brightness : 0;
2230
2231 switch (led->id) {
2232 case QPNP_ID_LED_MPP:
2233 led_blink(led, led->mpp_cfg->pwm_cfg);
2234 break;
2235 case QPNP_ID_RGB_RED:
2236 case QPNP_ID_RGB_GREEN:
2237 case QPNP_ID_RGB_BLUE:
2238 led_blink(led, led->rgb_cfg->pwm_cfg);
2239 break;
2240 default:
2241 dev_err(&led->spmi_dev->dev, "Invalid LED id type for blink\n");
2242 return -EINVAL;
2243 }
2244 return count;
2245}
2246
Amy Malochebc97c0d22013-03-24 22:06:16 -07002247static DEVICE_ATTR(led_mode, 0664, NULL, led_mode_store);
2248static DEVICE_ATTR(strobe, 0664, NULL, led_strobe_type_store);
Amy Maloche4cf9f322013-05-29 15:53:46 -07002249static DEVICE_ATTR(pwm_us, 0664, NULL, pwm_us_store);
2250static DEVICE_ATTR(pause_lo, 0664, NULL, pause_lo_store);
2251static DEVICE_ATTR(pause_hi, 0664, NULL, pause_hi_store);
2252static DEVICE_ATTR(start_idx, 0664, NULL, start_idx_store);
2253static DEVICE_ATTR(ramp_step_ms, 0664, NULL, ramp_step_ms_store);
2254static DEVICE_ATTR(lut_flags, 0664, NULL, lut_flags_store);
2255static DEVICE_ATTR(duty_pcts, 0664, NULL, duty_pcts_store);
Amy Malochea5c4ed82013-06-05 11:05:28 -07002256static DEVICE_ATTR(blink, 0664, NULL, blink_store);
Amy Malochebc97c0d22013-03-24 22:06:16 -07002257
2258static struct attribute *led_attrs[] = {
2259 &dev_attr_led_mode.attr,
2260 &dev_attr_strobe.attr,
Amy Maloche12ad6f32013-04-02 14:39:24 -07002261 NULL
Amy Malochebc97c0d22013-03-24 22:06:16 -07002262};
2263
2264static const struct attribute_group led_attr_group = {
2265 .attrs = led_attrs,
2266};
2267
Amy Maloche4cf9f322013-05-29 15:53:46 -07002268static struct attribute *pwm_attrs[] = {
2269 &dev_attr_pwm_us.attr,
2270 NULL
2271};
2272
2273static struct attribute *lpg_attrs[] = {
2274 &dev_attr_pause_lo.attr,
2275 &dev_attr_pause_hi.attr,
2276 &dev_attr_start_idx.attr,
2277 &dev_attr_ramp_step_ms.attr,
2278 &dev_attr_lut_flags.attr,
2279 &dev_attr_duty_pcts.attr,
2280 NULL
2281};
2282
Amy Malochea5c4ed82013-06-05 11:05:28 -07002283static struct attribute *blink_attrs[] = {
2284 &dev_attr_blink.attr,
2285 NULL
2286};
2287
Amy Maloche4cf9f322013-05-29 15:53:46 -07002288static const struct attribute_group pwm_attr_group = {
2289 .attrs = pwm_attrs,
2290};
2291
2292static const struct attribute_group lpg_attr_group = {
2293 .attrs = lpg_attrs,
2294};
2295
Amy Malochea5c4ed82013-06-05 11:05:28 -07002296static const struct attribute_group blink_attr_group = {
2297 .attrs = blink_attrs,
2298};
2299
Amy Maloche864a6d52012-10-03 15:58:12 -07002300static int __devinit qpnp_flash_init(struct qpnp_led_data *led)
2301{
2302 int rc;
2303
Chun Zhangc3b505b2013-06-03 19:01:49 -07002304 led->flash_cfg->flash_on = false;
2305
Amy Maloche864a6d52012-10-03 15:58:12 -07002306 rc = qpnp_led_masked_write(led,
2307 FLASH_LED_STROBE_CTRL(led->base),
2308 FLASH_STROBE_MASK, FLASH_DISABLE_ALL);
2309 if (rc) {
2310 dev_err(&led->spmi_dev->dev,
2311 "LED %d flash write failed(%d)\n", led->id, rc);
2312 return rc;
2313 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002314
Chun Zhangdf2d3062013-06-25 20:14:46 -07002315 /* Disable flash LED module */
2316 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07002317 FLASH_ENABLE_MASK, FLASH_DISABLE_ALL);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002318 if (rc) {
2319 dev_err(&led->spmi_dev->dev,
2320 "Enable reg write failed(%d)\n", rc);
2321 return rc;
2322 }
2323
2324 if (led->flash_cfg->torch_enable)
2325 return 0;
2326
Amy Maloche864a6d52012-10-03 15:58:12 -07002327 /* Set headroom */
2328 rc = qpnp_led_masked_write(led, FLASH_HEADROOM(led->base),
2329 FLASH_HEADROOM_MASK, led->flash_cfg->headroom);
2330 if (rc) {
2331 dev_err(&led->spmi_dev->dev,
2332 "Headroom reg write failed(%d)\n", rc);
2333 return rc;
2334 }
2335
Chun Zhange8954cf2013-05-02 11:14:34 -07002336 /* Set startup delay */
2337 rc = qpnp_led_masked_write(led,
2338 FLASH_STARTUP_DELAY(led->base), FLASH_STARTUP_DLY_MASK,
2339 led->flash_cfg->startup_dly);
2340 if (rc) {
2341 dev_err(&led->spmi_dev->dev,
2342 "Startup delay reg write failed(%d)\n", rc);
2343 return rc;
2344 }
2345
2346 /* Set timer control - safety or watchdog */
2347 if (led->flash_cfg->safety_timer) {
2348 rc = qpnp_led_masked_write(led,
2349 FLASH_LED_TMR_CTRL(led->base),
2350 FLASH_TMR_MASK, FLASH_TMR_SAFETY);
2351 if (rc) {
2352 dev_err(&led->spmi_dev->dev,
2353 "LED timer ctrl reg write failed(%d)\n",
2354 rc);
2355 return rc;
2356 }
2357 }
2358
2359 /* Set Vreg force */
2360 rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
2361 FLASH_VREG_MASK, FLASH_HW_VREG_OK);
2362 if (rc) {
2363 dev_err(&led->spmi_dev->dev,
2364 "Vreg OK reg write failed(%d)\n", rc);
2365 return rc;
2366 }
2367
2368 /* Set self fault check */
2369 rc = qpnp_led_masked_write(led, FLASH_FAULT_DETECT(led->base),
2370 FLASH_FAULT_DETECT_MASK, FLASH_SELFCHECK_ENABLE);
2371 if (rc) {
2372 dev_err(&led->spmi_dev->dev,
2373 "Fault detect reg write failed(%d)\n", rc);
2374 return rc;
2375 }
2376
Amy Maloche864a6d52012-10-03 15:58:12 -07002377 /* Set mask enable */
2378 rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
2379 FLASH_MASK_REG_MASK, FLASH_MASK_1);
2380 if (rc) {
2381 dev_err(&led->spmi_dev->dev,
2382 "Mask enable reg write failed(%d)\n", rc);
2383 return rc;
2384 }
2385
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07002386 /* Set current ramp */
2387 rc = qpnp_led_masked_write(led, FLASH_CURRENT_RAMP(led->base),
2388 FLASH_CURRENT_RAMP_MASK, FLASH_RAMP_STEP_27US);
2389 if (rc) {
2390 dev_err(&led->spmi_dev->dev,
2391 "Current ramp reg write failed(%d)\n", rc);
2392 return rc;
2393 }
2394
Amy Malochebc97c0d22013-03-24 22:06:16 -07002395 led->flash_cfg->strobe_type = 0;
2396
Amy Maloche864a6d52012-10-03 15:58:12 -07002397 /* dump flash registers */
2398 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
2399
2400 return 0;
2401}
2402
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302403static int __devinit qpnp_kpdbl_init(struct qpnp_led_data *led)
2404{
2405 int rc;
2406 u8 val;
2407
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302408 /* select row source - vbst or vph */
2409 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2410 KPDBL_ROW_SRC_SEL(led->base), &val, 1);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302411 if (rc) {
2412 dev_err(&led->spmi_dev->dev,
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302413 "Unable to read from addr=%x, rc(%d)\n",
2414 KPDBL_ROW_SRC_SEL(led->base), rc);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302415 return rc;
2416 }
2417
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302418 if (led->kpdbl_cfg->row_src_vbst)
2419 val |= 1 << led->kpdbl_cfg->row_id;
2420 else
2421 val &= ~(1 << led->kpdbl_cfg->row_id);
2422
2423 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
2424 KPDBL_ROW_SRC_SEL(led->base), &val, 1);
2425 if (rc) {
2426 dev_err(&led->spmi_dev->dev,
2427 "Unable to read from addr=%x, rc(%d)\n",
2428 KPDBL_ROW_SRC_SEL(led->base), rc);
2429 return rc;
2430 }
2431
2432 /* row source enable */
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302433 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2434 KPDBL_ROW_SRC(led->base), &val, 1);
2435 if (rc) {
2436 dev_err(&led->spmi_dev->dev,
2437 "Unable to read from addr=%x, rc(%d)\n",
2438 KPDBL_ROW_SRC(led->base), rc);
2439 return rc;
2440 }
2441
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302442 if (led->kpdbl_cfg->row_src_en)
2443 val |= KPDBL_ROW_SCAN_EN_MASK | (1 << led->kpdbl_cfg->row_id);
2444 else
2445 val &= ~(1 << led->kpdbl_cfg->row_id);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302446
2447 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
2448 KPDBL_ROW_SRC(led->base), &val, 1);
2449 if (rc) {
2450 dev_err(&led->spmi_dev->dev,
2451 "Unable to write to addr=%x, rc(%d)\n",
2452 KPDBL_ROW_SRC(led->base), rc);
2453 return rc;
2454 }
2455
2456 /* enable module */
2457 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
2458 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
2459 if (rc) {
2460 dev_err(&led->spmi_dev->dev,
2461 "Enable module write failed(%d)\n", rc);
2462 return rc;
2463 }
2464
Amy Malochea2726f02013-05-10 10:19:03 -07002465 rc = qpnp_pwm_init(led->kpdbl_cfg->pwm_cfg, led->spmi_dev,
2466 led->cdev.name);
2467 if (rc) {
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302468 dev_err(&led->spmi_dev->dev,
Amy Malochea2726f02013-05-10 10:19:03 -07002469 "Failed to initialize pwm\n");
2470 return rc;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302471 }
2472
2473 /* dump kpdbl registers */
2474 qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
2475
2476 return 0;
2477}
2478
Amy Malocheeea7b592012-10-03 15:59:36 -07002479static int __devinit qpnp_rgb_init(struct qpnp_led_data *led)
2480{
Amy Malochea2726f02013-05-10 10:19:03 -07002481 int rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07002482
2483 rc = qpnp_led_masked_write(led, RGB_LED_SRC_SEL(led->base),
2484 RGB_LED_SRC_MASK, RGB_LED_SOURCE_VPH_PWR);
2485 if (rc) {
2486 dev_err(&led->spmi_dev->dev,
2487 "Failed to write led source select register\n");
2488 return rc;
2489 }
2490
Amy Malochea2726f02013-05-10 10:19:03 -07002491 rc = qpnp_pwm_init(led->rgb_cfg->pwm_cfg, led->spmi_dev,
2492 led->cdev.name);
2493 if (rc) {
Amy Malocheeea7b592012-10-03 15:59:36 -07002494 dev_err(&led->spmi_dev->dev,
Amy Malochea2726f02013-05-10 10:19:03 -07002495 "Failed to initialize pwm\n");
2496 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07002497 }
Amy Malocheeea7b592012-10-03 15:59:36 -07002498 /* Initialize led for use in auto trickle charging mode */
2499 rc = qpnp_led_masked_write(led, RGB_LED_ATC_CTL(led->base),
2500 led->rgb_cfg->enable, led->rgb_cfg->enable);
2501
2502 return 0;
2503}
2504
Amy Malochea2726f02013-05-10 10:19:03 -07002505static int __devinit qpnp_mpp_init(struct qpnp_led_data *led)
2506{
Abinaya P03cee1d2014-02-17 17:51:53 +05302507 int rc;
2508 u8 val;
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05302509
2510 if (led->max_current < LED_MPP_CURRENT_MIN ||
2511 led->max_current > LED_MPP_CURRENT_MAX) {
2512 dev_err(&led->spmi_dev->dev,
2513 "max current for mpp is not valid\n");
2514 return -EINVAL;
2515 }
2516
Amy Malochea2726f02013-05-10 10:19:03 -07002517 val = (led->mpp_cfg->current_setting / LED_MPP_CURRENT_PER_SETTING) - 1;
2518
2519 if (val < 0)
2520 val = 0;
2521
Chun Zhang874c9ab2013-07-08 17:18:34 -07002522 rc = qpnp_led_masked_write(led, LED_MPP_VIN_CTRL(led->base),
2523 LED_MPP_VIN_MASK, led->mpp_cfg->vin_ctrl);
2524 if (rc) {
2525 dev_err(&led->spmi_dev->dev,
2526 "Failed to write led vin control reg\n");
2527 return rc;
2528 }
2529
Amy Malochea2726f02013-05-10 10:19:03 -07002530 rc = qpnp_led_masked_write(led, LED_MPP_SINK_CTRL(led->base),
2531 LED_MPP_SINK_MASK, val);
2532 if (rc) {
2533 dev_err(&led->spmi_dev->dev,
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05302534 "Failed to write sink control reg\n");
Amy Malochea2726f02013-05-10 10:19:03 -07002535 return rc;
2536 }
2537
2538 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
2539 rc = qpnp_pwm_init(led->mpp_cfg->pwm_cfg, led->spmi_dev,
2540 led->cdev.name);
2541 if (rc) {
2542 dev_err(&led->spmi_dev->dev,
2543 "Failed to initialize pwm\n");
2544 return rc;
2545 }
2546 }
2547
2548 return 0;
2549}
2550
Amy Malochef3d5a062012-08-16 19:14:11 -07002551static int __devinit qpnp_led_initialize(struct qpnp_led_data *led)
2552{
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302553 int rc = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07002554
2555 switch (led->id) {
2556 case QPNP_ID_WLED:
2557 rc = qpnp_wled_init(led);
2558 if (rc)
Amy Malochea5ca5552012-10-23 13:34:46 -07002559 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -07002560 "WLED initialize failed(%d)\n", rc);
2561 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07002562 case QPNP_ID_FLASH1_LED0:
2563 case QPNP_ID_FLASH1_LED1:
2564 rc = qpnp_flash_init(led);
2565 if (rc)
2566 dev_err(&led->spmi_dev->dev,
2567 "FLASH initialize failed(%d)\n", rc);
2568 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07002569 case QPNP_ID_RGB_RED:
2570 case QPNP_ID_RGB_GREEN:
2571 case QPNP_ID_RGB_BLUE:
2572 rc = qpnp_rgb_init(led);
2573 if (rc)
2574 dev_err(&led->spmi_dev->dev,
2575 "RGB initialize failed(%d)\n", rc);
2576 break;
Amy Malochef3813742013-04-11 19:33:47 -07002577 case QPNP_ID_LED_MPP:
Amy Malochea2726f02013-05-10 10:19:03 -07002578 rc = qpnp_mpp_init(led);
2579 if (rc)
2580 dev_err(&led->spmi_dev->dev,
2581 "MPP initialize failed(%d)\n", rc);
Amy Malochef3813742013-04-11 19:33:47 -07002582 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302583 case QPNP_ID_KPDBL:
2584 rc = qpnp_kpdbl_init(led);
2585 if (rc)
2586 dev_err(&led->spmi_dev->dev,
2587 "KPDBL initialize failed(%d)\n", rc);
2588 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07002589 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07002590 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malocheeea7b592012-10-03 15:59:36 -07002591 return -EINVAL;
Amy Malochef3d5a062012-08-16 19:14:11 -07002592 }
2593
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302594 return rc;
Amy Malochef3d5a062012-08-16 19:14:11 -07002595}
2596
Amy Malochea5ca5552012-10-23 13:34:46 -07002597static int __devinit qpnp_get_common_configs(struct qpnp_led_data *led,
2598 struct device_node *node)
2599{
2600 int rc;
Asaf Penso55ac8472013-01-21 21:17:37 +02002601 u32 val;
Amy Malochea5ca5552012-10-23 13:34:46 -07002602 const char *temp_string;
2603
2604 led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
2605 rc = of_property_read_string(node, "linux,default-trigger",
2606 &temp_string);
2607 if (!rc)
2608 led->cdev.default_trigger = temp_string;
2609 else if (rc != -EINVAL)
2610 return rc;
2611
2612 led->default_on = false;
2613 rc = of_property_read_string(node, "qcom,default-state",
2614 &temp_string);
2615 if (!rc) {
2616 if (strncmp(temp_string, "on", sizeof("on")) == 0)
2617 led->default_on = true;
2618 } else if (rc != -EINVAL)
2619 return rc;
2620
Asaf Penso55ac8472013-01-21 21:17:37 +02002621 led->turn_off_delay_ms = 0;
2622 rc = of_property_read_u32(node, "qcom,turn-off-delay-ms", &val);
2623 if (!rc)
2624 led->turn_off_delay_ms = val;
2625 else if (rc != -EINVAL)
2626 return rc;
2627
Amy Malochea5ca5552012-10-23 13:34:46 -07002628 return 0;
2629}
2630
Amy Malochef3d5a062012-08-16 19:14:11 -07002631/*
2632 * Handlers for alternative sources of platform_data
2633 */
2634static int __devinit qpnp_get_config_wled(struct qpnp_led_data *led,
2635 struct device_node *node)
2636{
2637 u32 val;
2638 int rc;
Amy Malochef3d5a062012-08-16 19:14:11 -07002639
2640 led->wled_cfg = devm_kzalloc(&led->spmi_dev->dev,
2641 sizeof(struct wled_config_data), GFP_KERNEL);
2642 if (!led->wled_cfg) {
2643 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2644 return -ENOMEM;
2645 }
2646
Amy Maloche0150b5e2013-08-15 18:18:32 -07002647 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2648 PMIC_VERSION_REG, &led->wled_cfg->pmic_version, 1);
2649 if (rc) {
2650 dev_err(&led->spmi_dev->dev,
2651 "Unable to read pmic ver, rc(%d)\n", rc);
2652 }
2653
Amy Malochef3d5a062012-08-16 19:14:11 -07002654 led->wled_cfg->num_strings = WLED_DEFAULT_STRINGS;
2655 rc = of_property_read_u32(node, "qcom,num-strings", &val);
2656 if (!rc)
2657 led->wled_cfg->num_strings = (u8) val;
2658 else if (rc != -EINVAL)
2659 return rc;
2660
2661 led->wled_cfg->ovp_val = WLED_DEFAULT_OVP_VAL;
2662 rc = of_property_read_u32(node, "qcom,ovp-val", &val);
2663 if (!rc)
2664 led->wled_cfg->ovp_val = (u8) val;
2665 else if (rc != -EINVAL)
2666 return rc;
2667
2668 led->wled_cfg->boost_curr_lim = WLED_BOOST_LIM_DEFAULT;
2669 rc = of_property_read_u32(node, "qcom,boost-curr-lim", &val);
2670 if (!rc)
2671 led->wled_cfg->boost_curr_lim = (u8) val;
2672 else if (rc != -EINVAL)
2673 return rc;
2674
2675 led->wled_cfg->cp_select = WLED_CP_SEL_DEFAULT;
2676 rc = of_property_read_u32(node, "qcom,cp-sel", &val);
2677 if (!rc)
2678 led->wled_cfg->cp_select = (u8) val;
2679 else if (rc != -EINVAL)
2680 return rc;
2681
2682 led->wled_cfg->ctrl_delay_us = WLED_CTRL_DLY_DEFAULT;
2683 rc = of_property_read_u32(node, "qcom,ctrl-delay-us", &val);
2684 if (!rc)
2685 led->wled_cfg->ctrl_delay_us = (u8) val;
2686 else if (rc != -EINVAL)
2687 return rc;
2688
Amy Malochebd687672013-03-18 11:23:45 -07002689 led->wled_cfg->op_fdbck = WLED_OP_FDBCK_DEFAULT;
2690 rc = of_property_read_u32(node, "qcom,op-fdbck", &val);
2691 if (!rc)
2692 led->wled_cfg->op_fdbck = (u8) val;
2693 else if (rc != -EINVAL)
2694 return rc;
2695
Amy Malochef3d5a062012-08-16 19:14:11 -07002696 led->wled_cfg->switch_freq = WLED_SWITCH_FREQ_DEFAULT;
2697 rc = of_property_read_u32(node, "qcom,switch-freq", &val);
2698 if (!rc)
2699 led->wled_cfg->switch_freq = (u8) val;
2700 else if (rc != -EINVAL)
2701 return rc;
2702
2703 led->wled_cfg->dig_mod_gen_en =
2704 of_property_read_bool(node, "qcom,dig-mod-gen-en");
2705
2706 led->wled_cfg->cs_out_en =
2707 of_property_read_bool(node, "qcom,cs-out-en");
2708
Amy Malochef3d5a062012-08-16 19:14:11 -07002709 return 0;
2710}
2711
Amy Maloche864a6d52012-10-03 15:58:12 -07002712static int __devinit qpnp_get_config_flash(struct qpnp_led_data *led,
Chun Zhangc3b505b2013-06-03 19:01:49 -07002713 struct device_node *node, bool *reg_set)
Amy Maloche864a6d52012-10-03 15:58:12 -07002714{
2715 int rc;
2716 u32 val;
2717
2718 led->flash_cfg = devm_kzalloc(&led->spmi_dev->dev,
2719 sizeof(struct flash_config_data), GFP_KERNEL);
2720 if (!led->flash_cfg) {
2721 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2722 return -ENOMEM;
2723 }
2724
Chun Zhang0d6ca072013-07-30 21:08:39 -07002725 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2726 FLASH_PERIPHERAL_SUBTYPE(led->base),
2727 &led->flash_cfg->peripheral_subtype, 1);
2728 if (rc) {
2729 dev_err(&led->spmi_dev->dev,
2730 "Unable to read from addr=%x, rc(%d)\n",
2731 FLASH_PERIPHERAL_SUBTYPE(led->base), rc);
2732 }
2733
Chun Zhang9d5ff672013-08-01 18:18:26 -07002734 led->flash_cfg->torch_enable =
2735 of_property_read_bool(node, "qcom,torch-enable");
2736
Chunmei Caif094d072014-08-06 13:58:50 +08002737 if (of_find_property(of_get_parent(node), "flash-wa-supply",
2738 NULL) && (!*reg_set)) {
2739 led->flash_cfg->flash_wa_reg =
2740 devm_regulator_get(&led->spmi_dev->dev,
2741 "flash-wa");
2742 if (IS_ERR_OR_NULL(led->flash_cfg->flash_wa_reg)) {
2743 rc = PTR_ERR(led->flash_cfg->flash_wa_reg);
2744 if (rc != EPROBE_DEFER) {
2745 dev_err(&led->spmi_dev->dev,
2746 "Falsh wa regulator get failed(%d)\n",
2747 rc);
2748 }
2749 }
2750 }
2751
Amy Maloche864a6d52012-10-03 15:58:12 -07002752 if (led->id == QPNP_ID_FLASH1_LED0) {
Chun Zhang9d5ff672013-08-01 18:18:26 -07002753 led->flash_cfg->enable_module = FLASH_ENABLE_LED_0;
Amy Maloche864a6d52012-10-03 15:58:12 -07002754 led->flash_cfg->current_addr = FLASH_LED_0_CURR(led->base);
Amy Maloche864a6d52012-10-03 15:58:12 -07002755 led->flash_cfg->trigger_flash = FLASH_LED_0_OUTPUT;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002756 if (!*reg_set) {
2757 led->flash_cfg->flash_boost_reg =
2758 regulator_get(&led->spmi_dev->dev,
Chun Zhang0d6ca072013-07-30 21:08:39 -07002759 "flash-boost");
Chun Zhangc3b505b2013-06-03 19:01:49 -07002760 if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
2761 rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
2762 dev_err(&led->spmi_dev->dev,
2763 "Regulator get failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002764 goto error_get_flash_reg;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002765 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002766 led->flash_cfg->flash_reg_get = true;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002767 *reg_set = true;
2768 } else
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002769 led->flash_cfg->flash_reg_get = false;
Chun Zhang9d5ff672013-08-01 18:18:26 -07002770
2771 if (led->flash_cfg->torch_enable) {
2772 led->flash_cfg->second_addr =
2773 FLASH_LED_1_CURR(led->base);
2774 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002775 } else if (led->id == QPNP_ID_FLASH1_LED1) {
Chun Zhang9d5ff672013-08-01 18:18:26 -07002776 led->flash_cfg->enable_module = FLASH_ENABLE_LED_1;
Amy Maloche864a6d52012-10-03 15:58:12 -07002777 led->flash_cfg->current_addr = FLASH_LED_1_CURR(led->base);
Amy Maloche864a6d52012-10-03 15:58:12 -07002778 led->flash_cfg->trigger_flash = FLASH_LED_1_OUTPUT;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002779 if (!*reg_set) {
2780 led->flash_cfg->flash_boost_reg =
2781 regulator_get(&led->spmi_dev->dev,
Chun Zhang0d6ca072013-07-30 21:08:39 -07002782 "flash-boost");
Chun Zhangc3b505b2013-06-03 19:01:49 -07002783 if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
2784 rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
2785 dev_err(&led->spmi_dev->dev,
2786 "Regulator get failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002787 goto error_get_flash_reg;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002788 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002789 led->flash_cfg->flash_reg_get = true;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002790 *reg_set = true;
2791 } else
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002792 led->flash_cfg->flash_reg_get = false;
Chun Zhang9d5ff672013-08-01 18:18:26 -07002793
2794 if (led->flash_cfg->torch_enable) {
2795 led->flash_cfg->second_addr =
2796 FLASH_LED_0_CURR(led->base);
2797 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002798 } else {
2799 dev_err(&led->spmi_dev->dev, "Unknown flash LED name given\n");
2800 return -EINVAL;
2801 }
2802
Chun Zhangdf2d3062013-06-25 20:14:46 -07002803 if (led->flash_cfg->torch_enable) {
Chun Zhang0d6ca072013-07-30 21:08:39 -07002804 if (of_find_property(of_get_parent(node), "torch-boost-supply",
2805 NULL)) {
2806 led->flash_cfg->torch_boost_reg =
2807 regulator_get(&led->spmi_dev->dev,
2808 "torch-boost");
2809 if (IS_ERR(led->flash_cfg->torch_boost_reg)) {
2810 rc = PTR_ERR(led->flash_cfg->torch_boost_reg);
2811 dev_err(&led->spmi_dev->dev,
2812 "Torch regulator get failed(%d)\n", rc);
2813 goto error_get_torch_reg;
2814 }
Chun Zhang9d5ff672013-08-01 18:18:26 -07002815 led->flash_cfg->enable_module = FLASH_ENABLE_MODULE;
2816 } else
2817 led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
Abinaya Pb6e3ba42014-01-07 15:32:31 +05302818 led->flash_cfg->trigger_flash = FLASH_TORCH_OUTPUT;
Chun Zhangdf2d3062013-06-25 20:14:46 -07002819 }
2820
Amy Maloche864a6d52012-10-03 15:58:12 -07002821 rc = of_property_read_u32(node, "qcom,current", &val);
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002822 if (!rc) {
Chun Zhangdf2d3062013-06-25 20:14:46 -07002823 if (led->flash_cfg->torch_enable) {
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002824 led->flash_cfg->current_prgm = (val *
2825 TORCH_MAX_LEVEL / led->max_current);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002826 return 0;
2827 }
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002828 else
2829 led->flash_cfg->current_prgm = (val *
Amy Maloche864a6d52012-10-03 15:58:12 -07002830 FLASH_MAX_LEVEL / led->max_current);
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002831 } else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002832 if (led->flash_cfg->torch_enable)
2833 goto error_get_torch_reg;
2834 else
2835 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002836
2837 rc = of_property_read_u32(node, "qcom,headroom", &val);
2838 if (!rc)
2839 led->flash_cfg->headroom = (u8) val;
2840 else if (rc == -EINVAL)
Amy Maloche62571b22013-04-17 17:23:10 -07002841 led->flash_cfg->headroom = HEADROOM_500mV;
Amy Maloche864a6d52012-10-03 15:58:12 -07002842 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002843 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002844
2845 rc = of_property_read_u32(node, "qcom,duration", &val);
2846 if (!rc)
Abinaya P41bb8bc2013-12-06 22:55:37 +05302847 led->flash_cfg->duration = (u8)((val - 10) / 10);
Amy Maloche864a6d52012-10-03 15:58:12 -07002848 else if (rc == -EINVAL)
2849 led->flash_cfg->duration = FLASH_DURATION_200ms;
2850 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002851 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002852
2853 rc = of_property_read_u32(node, "qcom,clamp-curr", &val);
2854 if (!rc)
2855 led->flash_cfg->clamp_curr = (val *
2856 FLASH_MAX_LEVEL / led->max_current);
2857 else if (rc == -EINVAL)
2858 led->flash_cfg->clamp_curr = FLASH_CLAMP_200mA;
2859 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002860 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002861
2862 rc = of_property_read_u32(node, "qcom,startup-dly", &val);
2863 if (!rc)
2864 led->flash_cfg->startup_dly = (u8) val;
2865 else if (rc == -EINVAL)
Amy Maloche62571b22013-04-17 17:23:10 -07002866 led->flash_cfg->startup_dly = DELAY_128us;
Amy Maloche864a6d52012-10-03 15:58:12 -07002867 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002868 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002869
2870 led->flash_cfg->safety_timer =
2871 of_property_read_bool(node, "qcom,safety-timer");
2872
2873 return 0;
Chun Zhangdf2d3062013-06-25 20:14:46 -07002874
2875error_get_torch_reg:
2876 regulator_put(led->flash_cfg->torch_boost_reg);
2877
2878error_get_flash_reg:
2879 regulator_put(led->flash_cfg->flash_boost_reg);
2880 return rc;
2881
Amy Maloche864a6d52012-10-03 15:58:12 -07002882}
2883
Amy Malochea2726f02013-05-10 10:19:03 -07002884static int __devinit qpnp_get_config_pwm(struct pwm_config_data *pwm_cfg,
2885 struct spmi_device *spmi_dev,
2886 struct device_node *node)
2887{
2888 struct property *prop;
2889 int rc, i;
2890 u32 val;
2891 u8 *temp_cfg;
2892
2893 rc = of_property_read_u32(node, "qcom,pwm-channel", &val);
2894 if (!rc)
2895 pwm_cfg->pwm_channel = val;
2896 else
2897 return rc;
2898
Abinaya P04df8962014-05-20 16:56:09 +05302899 if (pwm_cfg->mode != MANUAL_MODE) {
Amy Malochea2726f02013-05-10 10:19:03 -07002900 rc = of_property_read_u32(node, "qcom,pwm-us", &val);
2901 if (!rc)
2902 pwm_cfg->pwm_period_us = val;
2903 else
2904 return rc;
2905 }
2906
Amy Malochea5c4ed82013-06-05 11:05:28 -07002907 pwm_cfg->use_blink =
2908 of_property_read_bool(node, "qcom,use-blink");
2909
2910 if (pwm_cfg->mode == LPG_MODE || pwm_cfg->use_blink) {
Amy Malochea2726f02013-05-10 10:19:03 -07002911 pwm_cfg->duty_cycles =
2912 devm_kzalloc(&spmi_dev->dev,
2913 sizeof(struct pwm_duty_cycles), GFP_KERNEL);
2914 if (!pwm_cfg->duty_cycles) {
2915 dev_err(&spmi_dev->dev,
2916 "Unable to allocate memory\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002917 rc = -ENOMEM;
2918 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002919 }
2920
2921 prop = of_find_property(node, "qcom,duty-pcts",
2922 &pwm_cfg->duty_cycles->num_duty_pcts);
2923 if (!prop) {
2924 dev_err(&spmi_dev->dev, "Looking up property " \
2925 "node qcom,duty-pcts failed\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002926 rc = -ENODEV;
2927 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002928 } else if (!pwm_cfg->duty_cycles->num_duty_pcts) {
2929 dev_err(&spmi_dev->dev, "Invalid length of " \
2930 "duty pcts\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002931 rc = -EINVAL;
2932 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002933 }
2934
2935 pwm_cfg->duty_cycles->duty_pcts =
2936 devm_kzalloc(&spmi_dev->dev,
Amy Maloche4cf9f322013-05-29 15:53:46 -07002937 sizeof(int) * PWM_LUT_MAX_SIZE,
Amy Malochea2726f02013-05-10 10:19:03 -07002938 GFP_KERNEL);
2939 if (!pwm_cfg->duty_cycles->duty_pcts) {
2940 dev_err(&spmi_dev->dev,
2941 "Unable to allocate memory\n");
Amy Maloche4cf9f322013-05-29 15:53:46 -07002942 rc = -ENOMEM;
2943 goto bad_lpg_params;
2944 }
2945
2946 pwm_cfg->old_duty_pcts =
2947 devm_kzalloc(&spmi_dev->dev,
2948 sizeof(int) * PWM_LUT_MAX_SIZE,
2949 GFP_KERNEL);
2950 if (!pwm_cfg->old_duty_pcts) {
2951 dev_err(&spmi_dev->dev,
2952 "Unable to allocate memory\n");
2953 rc = -ENOMEM;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002954 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002955 }
2956
2957 temp_cfg = devm_kzalloc(&spmi_dev->dev,
2958 pwm_cfg->duty_cycles->num_duty_pcts *
2959 sizeof(u8), GFP_KERNEL);
2960 if (!temp_cfg) {
2961 dev_err(&spmi_dev->dev, "Failed to allocate " \
2962 "memory for duty pcts\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002963 rc = -ENOMEM;
2964 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002965 }
2966
2967 memcpy(temp_cfg, prop->value,
2968 pwm_cfg->duty_cycles->num_duty_pcts);
2969
2970 for (i = 0; i < pwm_cfg->duty_cycles->num_duty_pcts; i++)
2971 pwm_cfg->duty_cycles->duty_pcts[i] =
2972 (int) temp_cfg[i];
2973
2974 rc = of_property_read_u32(node, "qcom,start-idx", &val);
2975 if (!rc) {
2976 pwm_cfg->lut_params.start_idx = val;
2977 pwm_cfg->duty_cycles->start_idx = val;
2978 } else
Amy Malochea5c4ed82013-06-05 11:05:28 -07002979 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002980
2981 pwm_cfg->lut_params.lut_pause_hi = 0;
2982 rc = of_property_read_u32(node, "qcom,pause-hi", &val);
2983 if (!rc)
2984 pwm_cfg->lut_params.lut_pause_hi = val;
2985 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002986 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002987
2988 pwm_cfg->lut_params.lut_pause_lo = 0;
2989 rc = of_property_read_u32(node, "qcom,pause-lo", &val);
2990 if (!rc)
2991 pwm_cfg->lut_params.lut_pause_lo = val;
2992 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002993 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002994
2995 pwm_cfg->lut_params.ramp_step_ms =
2996 QPNP_LUT_RAMP_STEP_DEFAULT;
2997 rc = of_property_read_u32(node, "qcom,ramp-step-ms", &val);
2998 if (!rc)
2999 pwm_cfg->lut_params.ramp_step_ms = val;
3000 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07003001 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07003002
3003 pwm_cfg->lut_params.flags = QPNP_LED_PWM_FLAGS;
3004 rc = of_property_read_u32(node, "qcom,lut-flags", &val);
3005 if (!rc)
3006 pwm_cfg->lut_params.flags = (u8) val;
3007 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07003008 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07003009
3010 pwm_cfg->lut_params.idx_len =
3011 pwm_cfg->duty_cycles->num_duty_pcts;
3012 }
3013 return 0;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003014
3015bad_lpg_params:
3016 pwm_cfg->use_blink = false;
3017 if (pwm_cfg->mode == PWM_MODE) {
3018 dev_err(&spmi_dev->dev, "LPG parameters not set for" \
3019 " blink mode, defaulting to PWM mode\n");
3020 return 0;
3021 }
3022 return rc;
Amy Malochea2726f02013-05-10 10:19:03 -07003023};
3024
3025static int qpnp_led_get_mode(const char *mode)
3026{
3027 if (strncmp(mode, "manual", strlen(mode)) == 0)
3028 return MANUAL_MODE;
3029 else if (strncmp(mode, "pwm", strlen(mode)) == 0)
3030 return PWM_MODE;
3031 else if (strncmp(mode, "lpg", strlen(mode)) == 0)
3032 return LPG_MODE;
3033 else
3034 return -EINVAL;
3035};
3036
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303037static int __devinit qpnp_get_config_kpdbl(struct qpnp_led_data *led,
3038 struct device_node *node)
3039{
3040 int rc;
3041 u32 val;
Amy Malochea2726f02013-05-10 10:19:03 -07003042 u8 led_mode;
3043 const char *mode;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303044
3045 led->kpdbl_cfg = devm_kzalloc(&led->spmi_dev->dev,
3046 sizeof(struct kpdbl_config_data), GFP_KERNEL);
3047 if (!led->kpdbl_cfg) {
3048 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3049 return -ENOMEM;
3050 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303051
Amy Malochea2726f02013-05-10 10:19:03 -07003052 rc = of_property_read_string(node, "qcom,mode", &mode);
3053 if (!rc) {
3054 led_mode = qpnp_led_get_mode(mode);
3055 if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
3056 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3057 "supported for kpdbl.\n");
3058 return -EINVAL;
3059 }
3060 led->kpdbl_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3061 sizeof(struct pwm_config_data),
3062 GFP_KERNEL);
3063 if (!led->kpdbl_cfg->pwm_cfg) {
3064 dev_err(&led->spmi_dev->dev,
3065 "Unable to allocate memory\n");
3066 return -ENOMEM;
3067 }
3068 led->kpdbl_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003069 led->kpdbl_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003070 } else
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303071 return rc;
3072
Amy Malochea2726f02013-05-10 10:19:03 -07003073 rc = qpnp_get_config_pwm(led->kpdbl_cfg->pwm_cfg, led->spmi_dev, node);
3074 if (rc < 0)
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303075 return rc;
3076
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303077 rc = of_property_read_u32(node, "qcom,row-id", &val);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303078 if (!rc)
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303079 led->kpdbl_cfg->row_id = val;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303080 else
3081 return rc;
3082
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303083 led->kpdbl_cfg->row_src_vbst =
3084 of_property_read_bool(node, "qcom,row-src-vbst");
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303085
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303086 led->kpdbl_cfg->row_src_en =
3087 of_property_read_bool(node, "qcom,row-src-en");
3088
3089 led->kpdbl_cfg->always_on =
3090 of_property_read_bool(node, "qcom,always-on");
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303091
3092 return 0;
3093}
3094
Amy Malocheeea7b592012-10-03 15:59:36 -07003095static int __devinit qpnp_get_config_rgb(struct qpnp_led_data *led,
3096 struct device_node *node)
3097{
Amy Malochea2726f02013-05-10 10:19:03 -07003098 int rc;
3099 u8 led_mode;
3100 const char *mode;
Amy Malocheeea7b592012-10-03 15:59:36 -07003101
3102 led->rgb_cfg = devm_kzalloc(&led->spmi_dev->dev,
3103 sizeof(struct rgb_config_data), GFP_KERNEL);
3104 if (!led->rgb_cfg) {
3105 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3106 return -ENOMEM;
3107 }
3108
3109 if (led->id == QPNP_ID_RGB_RED)
3110 led->rgb_cfg->enable = RGB_LED_ENABLE_RED;
3111 else if (led->id == QPNP_ID_RGB_GREEN)
3112 led->rgb_cfg->enable = RGB_LED_ENABLE_GREEN;
3113 else if (led->id == QPNP_ID_RGB_BLUE)
3114 led->rgb_cfg->enable = RGB_LED_ENABLE_BLUE;
3115 else
3116 return -EINVAL;
3117
Amy Malochea2726f02013-05-10 10:19:03 -07003118 rc = of_property_read_string(node, "qcom,mode", &mode);
3119 if (!rc) {
3120 led_mode = qpnp_led_get_mode(mode);
3121 if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
3122 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3123 "supported for rgb.\n");
Amy Malocheeea7b592012-10-03 15:59:36 -07003124 return -EINVAL;
3125 }
Amy Malochea2726f02013-05-10 10:19:03 -07003126 led->rgb_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3127 sizeof(struct pwm_config_data),
3128 GFP_KERNEL);
3129 if (!led->rgb_cfg->pwm_cfg) {
Amy Malocheeea7b592012-10-03 15:59:36 -07003130 dev_err(&led->spmi_dev->dev,
3131 "Unable to allocate memory\n");
3132 return -ENOMEM;
3133 }
Amy Malochea2726f02013-05-10 10:19:03 -07003134 led->rgb_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003135 led->rgb_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003136 } else
3137 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07003138
Amy Malochea2726f02013-05-10 10:19:03 -07003139 rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->spmi_dev, node);
3140 if (rc < 0)
3141 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07003142
3143 return 0;
3144}
3145
Amy Malochef3813742013-04-11 19:33:47 -07003146static int __devinit qpnp_get_config_mpp(struct qpnp_led_data *led,
3147 struct device_node *node)
3148{
3149 int rc;
3150 u32 val;
Amy Malochea2726f02013-05-10 10:19:03 -07003151 u8 led_mode;
3152 const char *mode;
Amy Malochef3813742013-04-11 19:33:47 -07003153
3154 led->mpp_cfg = devm_kzalloc(&led->spmi_dev->dev,
3155 sizeof(struct mpp_config_data), GFP_KERNEL);
3156 if (!led->mpp_cfg) {
3157 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3158 return -ENOMEM;
3159 }
3160
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05303161 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
Amy Malochef3813742013-04-11 19:33:47 -07003162 rc = of_property_read_u32(node, "qcom,current-setting", &val);
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05303163 if (!rc) {
3164 if (led->mpp_cfg->current_setting < LED_MPP_CURRENT_MIN)
3165 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
3166 else if (led->mpp_cfg->current_setting > LED_MPP_CURRENT_MAX)
3167 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MAX;
3168 else
3169 led->mpp_cfg->current_setting = (u8) val;
3170 } else if (rc != -EINVAL)
Amy Malochef3813742013-04-11 19:33:47 -07003171 return rc;
3172
3173 led->mpp_cfg->source_sel = LED_MPP_SOURCE_SEL_DEFAULT;
3174 rc = of_property_read_u32(node, "qcom,source-sel", &val);
3175 if (!rc)
3176 led->mpp_cfg->source_sel = (u8) val;
3177 else if (rc != -EINVAL)
3178 return rc;
3179
3180 led->mpp_cfg->mode_ctrl = LED_MPP_MODE_SINK;
3181 rc = of_property_read_u32(node, "qcom,mode-ctrl", &val);
3182 if (!rc)
3183 led->mpp_cfg->mode_ctrl = (u8) val;
3184 else if (rc != -EINVAL)
3185 return rc;
3186
Chun Zhang874c9ab2013-07-08 17:18:34 -07003187 led->mpp_cfg->vin_ctrl = LED_MPP_VIN_CTRL_DEFAULT;
3188 rc = of_property_read_u32(node, "qcom,vin-ctrl", &val);
3189 if (!rc)
3190 led->mpp_cfg->vin_ctrl = (u8) val;
3191 else if (rc != -EINVAL)
3192 return rc;
3193
3194 led->mpp_cfg->min_brightness = 0;
3195 rc = of_property_read_u32(node, "qcom,min-brightness", &val);
3196 if (!rc)
3197 led->mpp_cfg->min_brightness = (u8) val;
3198 else if (rc != -EINVAL)
3199 return rc;
3200
Amy Malochea2726f02013-05-10 10:19:03 -07003201 rc = of_property_read_string(node, "qcom,mode", &mode);
3202 if (!rc) {
3203 led_mode = qpnp_led_get_mode(mode);
3204 led->mpp_cfg->pwm_mode = led_mode;
3205 if (led_mode == MANUAL_MODE)
3206 return MANUAL_MODE;
3207 else if (led_mode == -EINVAL) {
3208 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3209 "supported for mpp.\n");
3210 return -EINVAL;
3211 }
3212 led->mpp_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3213 sizeof(struct pwm_config_data),
3214 GFP_KERNEL);
3215 if (!led->mpp_cfg->pwm_cfg) {
3216 dev_err(&led->spmi_dev->dev,
3217 "Unable to allocate memory\n");
3218 return -ENOMEM;
3219 }
3220 led->mpp_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003221 led->mpp_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003222 } else
3223 return rc;
3224
3225 rc = qpnp_get_config_pwm(led->mpp_cfg->pwm_cfg, led->spmi_dev, node);
3226 if (rc < 0)
3227 return rc;
3228
Amy Malochef3813742013-04-11 19:33:47 -07003229 return 0;
3230}
3231
Amy Malochef3d5a062012-08-16 19:14:11 -07003232static int __devinit qpnp_leds_probe(struct spmi_device *spmi)
3233{
Amy Malochef9490c62012-11-27 19:26:04 -08003234 struct qpnp_led_data *led, *led_array;
Amy Malochef3d5a062012-08-16 19:14:11 -07003235 struct resource *led_resource;
Amy Malochea5ca5552012-10-23 13:34:46 -07003236 struct device_node *node, *temp;
Amy Malochef9490c62012-11-27 19:26:04 -08003237 int rc, i, num_leds = 0, parsed_leds = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07003238 const char *led_label;
Chun Zhangc3b505b2013-06-03 19:01:49 -07003239 bool regulator_probe = false;
Amy Malochef3d5a062012-08-16 19:14:11 -07003240
Amy Malochea5ca5552012-10-23 13:34:46 -07003241 node = spmi->dev.of_node;
3242 if (node == NULL)
3243 return -ENODEV;
3244
3245 temp = NULL;
3246 while ((temp = of_get_next_child(node, temp)))
3247 num_leds++;
3248
Amy Malochef9490c62012-11-27 19:26:04 -08003249 if (!num_leds)
3250 return -ECHILD;
3251
3252 led_array = devm_kzalloc(&spmi->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07003253 (sizeof(struct qpnp_led_data) * num_leds), GFP_KERNEL);
Amy Malochef9490c62012-11-27 19:26:04 -08003254 if (!led_array) {
Amy Malochef3d5a062012-08-16 19:14:11 -07003255 dev_err(&spmi->dev, "Unable to allocate memory\n");
3256 return -ENOMEM;
3257 }
3258
Amy Malochea5ca5552012-10-23 13:34:46 -07003259 for_each_child_of_node(node, temp) {
Amy Malochef9490c62012-11-27 19:26:04 -08003260 led = &led_array[parsed_leds];
3261 led->num_leds = num_leds;
Amy Malochea5ca5552012-10-23 13:34:46 -07003262 led->spmi_dev = spmi;
Amy Malochef3d5a062012-08-16 19:14:11 -07003263
Amy Malochea5ca5552012-10-23 13:34:46 -07003264 led_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
3265 if (!led_resource) {
3266 dev_err(&spmi->dev, "Unable to get LED base address\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003267 rc = -ENXIO;
3268 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003269 }
3270 led->base = led_resource->start;
Amy Malochef3d5a062012-08-16 19:14:11 -07003271
Amy Malochea5ca5552012-10-23 13:34:46 -07003272 rc = of_property_read_string(temp, "label", &led_label);
Amy Malochef3d5a062012-08-16 19:14:11 -07003273 if (rc < 0) {
3274 dev_err(&led->spmi_dev->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07003275 "Failure reading label, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003276 goto fail_id_check;
Amy Malochef3d5a062012-08-16 19:14:11 -07003277 }
Amy Malochea5ca5552012-10-23 13:34:46 -07003278
3279 rc = of_property_read_string(temp, "linux,name",
3280 &led->cdev.name);
3281 if (rc < 0) {
3282 dev_err(&led->spmi_dev->dev,
3283 "Failure reading led name, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003284 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003285 }
3286
3287 rc = of_property_read_u32(temp, "qcom,max-current",
3288 &led->max_current);
3289 if (rc < 0) {
3290 dev_err(&led->spmi_dev->dev,
3291 "Failure reading max_current, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003292 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003293 }
3294
3295 rc = of_property_read_u32(temp, "qcom,id", &led->id);
3296 if (rc < 0) {
3297 dev_err(&led->spmi_dev->dev,
3298 "Failure reading led id, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003299 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003300 }
3301
3302 rc = qpnp_get_common_configs(led, temp);
3303 if (rc) {
3304 dev_err(&led->spmi_dev->dev,
3305 "Failure reading common led configuration," \
3306 " rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003307 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003308 }
3309
3310 led->cdev.brightness_set = qpnp_led_set;
3311 led->cdev.brightness_get = qpnp_led_get;
3312
3313 if (strncmp(led_label, "wled", sizeof("wled")) == 0) {
3314 rc = qpnp_get_config_wled(led, temp);
3315 if (rc < 0) {
3316 dev_err(&led->spmi_dev->dev,
3317 "Unable to read wled config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003318 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003319 }
Amy Maloche864a6d52012-10-03 15:58:12 -07003320 } else if (strncmp(led_label, "flash", sizeof("flash"))
3321 == 0) {
Chun Zhang0d6ca072013-07-30 21:08:39 -07003322 if (!of_find_property(node, "flash-boost-supply", NULL))
Chun Zhangc3b505b2013-06-03 19:01:49 -07003323 regulator_probe = true;
3324 rc = qpnp_get_config_flash(led, temp, &regulator_probe);
Amy Maloche864a6d52012-10-03 15:58:12 -07003325 if (rc < 0) {
3326 dev_err(&led->spmi_dev->dev,
3327 "Unable to read flash config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003328 goto fail_id_check;
Amy Maloche864a6d52012-10-03 15:58:12 -07003329 }
Amy Malocheeea7b592012-10-03 15:59:36 -07003330 } else if (strncmp(led_label, "rgb", sizeof("rgb")) == 0) {
3331 rc = qpnp_get_config_rgb(led, temp);
3332 if (rc < 0) {
3333 dev_err(&led->spmi_dev->dev,
3334 "Unable to read rgb config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003335 goto fail_id_check;
Amy Malocheeea7b592012-10-03 15:59:36 -07003336 }
Amy Malochef3813742013-04-11 19:33:47 -07003337 } else if (strncmp(led_label, "mpp", sizeof("mpp")) == 0) {
3338 rc = qpnp_get_config_mpp(led, temp);
3339 if (rc < 0) {
3340 dev_err(&led->spmi_dev->dev,
3341 "Unable to read mpp config data\n");
Amy Malochea2726f02013-05-10 10:19:03 -07003342 goto fail_id_check;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303343 }
3344 } else if (strncmp(led_label, "kpdbl", sizeof("kpdbl")) == 0) {
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303345 num_kpbl_leds_on = 0;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303346 rc = qpnp_get_config_kpdbl(led, temp);
3347 if (rc < 0) {
3348 dev_err(&led->spmi_dev->dev,
3349 "Unable to read kpdbl config data\n");
Amy Malochef3813742013-04-11 19:33:47 -07003350 goto fail_id_check;
3351 }
Amy Malochea5ca5552012-10-23 13:34:46 -07003352 } else {
3353 dev_err(&led->spmi_dev->dev, "No LED matching label\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003354 rc = -EINVAL;
3355 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003356 }
3357
Abinaya P9e5dc3e2014-04-14 15:38:11 +05303358 if (led->id != QPNP_ID_FLASH1_LED0 &&
3359 led->id != QPNP_ID_FLASH1_LED1)
3360 mutex_init(&led->lock);
Chun Zhang815a1832013-06-20 13:47:13 -07003361 INIT_WORK(&led->work, qpnp_led_work);
Amy Malochea5ca5552012-10-23 13:34:46 -07003362
3363 rc = qpnp_led_initialize(led);
3364 if (rc < 0)
3365 goto fail_id_check;
3366
3367 rc = qpnp_led_set_max_brightness(led);
3368 if (rc < 0)
3369 goto fail_id_check;
3370
3371 rc = led_classdev_register(&spmi->dev, &led->cdev);
3372 if (rc) {
3373 dev_err(&spmi->dev, "unable to register led %d,rc=%d\n",
3374 led->id, rc);
3375 goto fail_id_check;
3376 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07003377
3378 if (led->id == QPNP_ID_FLASH1_LED0 ||
3379 led->id == QPNP_ID_FLASH1_LED1) {
3380 rc = sysfs_create_group(&led->cdev.dev->kobj,
3381 &led_attr_group);
3382 if (rc)
3383 goto fail_id_check;
3384
3385 }
3386
Amy Malochea5c4ed82013-06-05 11:05:28 -07003387 if (led->id == QPNP_ID_LED_MPP) {
3388 if (!led->mpp_cfg->pwm_cfg)
3389 break;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003390 if (led->mpp_cfg->pwm_cfg->mode == PWM_MODE) {
3391 rc = sysfs_create_group(&led->cdev.dev->kobj,
3392 &pwm_attr_group);
3393 if (rc)
3394 goto fail_id_check;
3395 }
Amy Malochea5c4ed82013-06-05 11:05:28 -07003396 if (led->mpp_cfg->pwm_cfg->use_blink) {
3397 rc = sysfs_create_group(&led->cdev.dev->kobj,
3398 &blink_attr_group);
3399 if (rc)
3400 goto fail_id_check;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003401
3402 rc = sysfs_create_group(&led->cdev.dev->kobj,
3403 &lpg_attr_group);
3404 if (rc)
3405 goto fail_id_check;
3406 } else if (led->mpp_cfg->pwm_cfg->mode == LPG_MODE) {
3407 rc = sysfs_create_group(&led->cdev.dev->kobj,
3408 &lpg_attr_group);
3409 if (rc)
3410 goto fail_id_check;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003411 }
3412 } else if ((led->id == QPNP_ID_RGB_RED) ||
3413 (led->id == QPNP_ID_RGB_GREEN) ||
3414 (led->id == QPNP_ID_RGB_BLUE)) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07003415 if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
3416 rc = sysfs_create_group(&led->cdev.dev->kobj,
3417 &pwm_attr_group);
3418 if (rc)
3419 goto fail_id_check;
3420 }
Amy Malochea5c4ed82013-06-05 11:05:28 -07003421 if (led->rgb_cfg->pwm_cfg->use_blink) {
3422 rc = sysfs_create_group(&led->cdev.dev->kobj,
3423 &blink_attr_group);
3424 if (rc)
3425 goto fail_id_check;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003426
3427 rc = sysfs_create_group(&led->cdev.dev->kobj,
3428 &lpg_attr_group);
3429 if (rc)
3430 goto fail_id_check;
3431 } else if (led->rgb_cfg->pwm_cfg->mode == LPG_MODE) {
3432 rc = sysfs_create_group(&led->cdev.dev->kobj,
3433 &lpg_attr_group);
3434 if (rc)
3435 goto fail_id_check;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003436 }
3437 }
3438
Amy Malochea5ca5552012-10-23 13:34:46 -07003439 /* configure default state */
Asaf Penso55ac8472013-01-21 21:17:37 +02003440 if (led->default_on) {
Amy Malochea5ca5552012-10-23 13:34:46 -07003441 led->cdev.brightness = led->cdev.max_brightness;
Chun Zhang815a1832013-06-20 13:47:13 -07003442 __qpnp_led_work(led, led->cdev.brightness);
Asaf Penso55ac8472013-01-21 21:17:37 +02003443 if (led->turn_off_delay_ms > 0)
3444 qpnp_led_turn_off(led);
3445 } else
Amy Malochea5ca5552012-10-23 13:34:46 -07003446 led->cdev.brightness = LED_OFF;
3447
Amy Malochef9490c62012-11-27 19:26:04 -08003448 parsed_leds++;
Amy Malochef3d5a062012-08-16 19:14:11 -07003449 }
Amy Malochef9490c62012-11-27 19:26:04 -08003450 dev_set_drvdata(&spmi->dev, led_array);
Amy Malochef3d5a062012-08-16 19:14:11 -07003451 return 0;
3452
3453fail_id_check:
Chun Zhang815a1832013-06-20 13:47:13 -07003454 for (i = 0; i < parsed_leds; i++) {
Abinaya P9e5dc3e2014-04-14 15:38:11 +05303455 if (led_array[i].id != QPNP_ID_FLASH1_LED0 &&
3456 led_array[i].id != QPNP_ID_FLASH1_LED1)
3457 mutex_destroy(&led_array[i].lock);
Amy Malochef9490c62012-11-27 19:26:04 -08003458 led_classdev_unregister(&led_array[i].cdev);
Chun Zhang815a1832013-06-20 13:47:13 -07003459 }
3460
Amy Malochef3d5a062012-08-16 19:14:11 -07003461 return rc;
3462}
3463
3464static int __devexit qpnp_leds_remove(struct spmi_device *spmi)
3465{
Amy Malochef9490c62012-11-27 19:26:04 -08003466 struct qpnp_led_data *led_array = dev_get_drvdata(&spmi->dev);
3467 int i, parsed_leds = led_array->num_leds;
Amy Malochef3d5a062012-08-16 19:14:11 -07003468
Amy Malochebc97c0d22013-03-24 22:06:16 -07003469 for (i = 0; i < parsed_leds; i++) {
Chun Zhang815a1832013-06-20 13:47:13 -07003470 cancel_work_sync(&led_array[i].work);
Abinaya P9e5dc3e2014-04-14 15:38:11 +05303471 if (led_array[i].id != QPNP_ID_FLASH1_LED0 &&
3472 led_array[i].id != QPNP_ID_FLASH1_LED1)
3473 mutex_destroy(&led_array[i].lock);
Abinaya P9e5dc3e2014-04-14 15:38:11 +05303474
Amy Malochef9490c62012-11-27 19:26:04 -08003475 led_classdev_unregister(&led_array[i].cdev);
Amy Malochebc97c0d22013-03-24 22:06:16 -07003476 switch (led_array[i].id) {
3477 case QPNP_ID_WLED:
3478 break;
3479 case QPNP_ID_FLASH1_LED0:
3480 case QPNP_ID_FLASH1_LED1:
Chun Zhangda8ad0f2013-07-17 14:46:47 -07003481 if (led_array[i].flash_cfg->flash_reg_get)
Chun Zhangc3b505b2013-06-03 19:01:49 -07003482 regulator_put(led_array[i].flash_cfg-> \
3483 flash_boost_reg);
Chun Zhangdf2d3062013-06-25 20:14:46 -07003484 if (led_array[i].flash_cfg->torch_enable)
3485 regulator_put(led_array[i].flash_cfg->\
3486 torch_boost_reg);
Amy Malochebc97c0d22013-03-24 22:06:16 -07003487 sysfs_remove_group(&led_array[i].cdev.dev->kobj,
3488 &led_attr_group);
3489 break;
3490 case QPNP_ID_RGB_RED:
3491 case QPNP_ID_RGB_GREEN:
3492 case QPNP_ID_RGB_BLUE:
Amy Maloche4cf9f322013-05-29 15:53:46 -07003493 if (led_array[i].rgb_cfg->pwm_cfg->mode == PWM_MODE)
3494 sysfs_remove_group(&led_array[i].cdev.dev->\
3495 kobj, &pwm_attr_group);
3496 if (led_array[i].rgb_cfg->pwm_cfg->use_blink) {
3497 sysfs_remove_group(&led_array[i].cdev.dev->\
3498 kobj, &blink_attr_group);
3499 sysfs_remove_group(&led_array[i].cdev.dev->\
3500 kobj, &lpg_attr_group);
3501 } else if (led_array[i].rgb_cfg->pwm_cfg->mode\
3502 == LPG_MODE)
3503 sysfs_remove_group(&led_array[i].cdev.dev->\
3504 kobj, &lpg_attr_group);
3505 break;
3506 case QPNP_ID_LED_MPP:
3507 if (!led_array[i].mpp_cfg->pwm_cfg)
3508 break;
3509 if (led_array[i].mpp_cfg->pwm_cfg->mode == PWM_MODE)
3510 sysfs_remove_group(&led_array[i].cdev.dev->\
3511 kobj, &pwm_attr_group);
3512 if (led_array[i].mpp_cfg->pwm_cfg->use_blink) {
3513 sysfs_remove_group(&led_array[i].cdev.dev->\
3514 kobj, &blink_attr_group);
3515 sysfs_remove_group(&led_array[i].cdev.dev->\
3516 kobj, &lpg_attr_group);
3517 } else if (led_array[i].mpp_cfg->pwm_cfg->mode\
3518 == LPG_MODE)
3519 sysfs_remove_group(&led_array[i].cdev.dev->\
3520 kobj, &lpg_attr_group);
3521 break;
Amy Malochebc97c0d22013-03-24 22:06:16 -07003522 default:
3523 dev_err(&led_array[i].spmi_dev->dev,
3524 "Invalid LED(%d)\n",
3525 led_array[i].id);
3526 return -EINVAL;
3527 }
3528 }
Amy Malochef3d5a062012-08-16 19:14:11 -07003529
3530 return 0;
3531}
Himanshu Aggarwal4eac46c2013-11-27 13:46:53 +05303532
3533#ifdef CONFIG_OF
Amy Malochef3d5a062012-08-16 19:14:11 -07003534static struct of_device_id spmi_match_table[] = {
Himanshu Aggarwal4eac46c2013-11-27 13:46:53 +05303535 { .compatible = "qcom,leds-qpnp",},
3536 { },
Amy Malochef3d5a062012-08-16 19:14:11 -07003537};
Himanshu Aggarwal4eac46c2013-11-27 13:46:53 +05303538#else
3539#define spmi_match_table NULL
3540#endif
Amy Malochef3d5a062012-08-16 19:14:11 -07003541
3542static struct spmi_driver qpnp_leds_driver = {
3543 .driver = {
3544 .name = "qcom,leds-qpnp",
3545 .of_match_table = spmi_match_table,
3546 },
3547 .probe = qpnp_leds_probe,
3548 .remove = __devexit_p(qpnp_leds_remove),
3549};
3550
3551static int __init qpnp_led_init(void)
3552{
3553 return spmi_driver_register(&qpnp_leds_driver);
3554}
3555module_init(qpnp_led_init);
3556
3557static void __exit qpnp_led_exit(void)
3558{
3559 spmi_driver_unregister(&qpnp_leds_driver);
3560}
3561module_exit(qpnp_led_exit);
3562
3563MODULE_DESCRIPTION("QPNP LEDs driver");
3564MODULE_LICENSE("GPL v2");
3565MODULE_ALIAS("leds:leds-qpnp");
Amy Maloche864a6d52012-10-03 15:58:12 -07003566