blob: 8774df66db03a45d7a547a405a466f236fb3f7f6 [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
Abinaya Pa1677d22014-08-28 13:11:01 +0530395 * @flash_wa_reg_get - workaround regulator attached or not
Chun Zhangc3b505b2013-06-03 19:01:49 -0700396 * @flash_on - flash status, on or off
Chun Zhangdf2d3062013-06-25 20:14:46 -0700397 * @torch_on - torch status, on or off
Chun Zhangc3b505b2013-06-03 19:01:49 -0700398 * @flash_boost_reg - boost regulator for flash
Chun Zhangdf2d3062013-06-25 20:14:46 -0700399 * @torch_boost_reg - boost regulator for torch
Chunmei Caif094d072014-08-06 13:58:50 +0800400 * @flash_wa_reg - flash regulator for wa
Amy Maloche864a6d52012-10-03 15:58:12 -0700401 */
402struct flash_config_data {
403 u8 current_prgm;
404 u8 clamp_curr;
405 u8 headroom;
406 u8 duration;
407 u8 enable_module;
408 u8 trigger_flash;
409 u8 startup_dly;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700410 u8 strobe_type;
Chun Zhang0d6ca072013-07-30 21:08:39 -0700411 u8 peripheral_subtype;
Amy Maloche864a6d52012-10-03 15:58:12 -0700412 u16 current_addr;
413 u16 second_addr;
414 bool safety_timer;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700415 bool torch_enable;
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700416 bool flash_reg_get;
Abinaya Pa1677d22014-08-28 13:11:01 +0530417 bool flash_wa_reg_get;
Chun Zhangc3b505b2013-06-03 19:01:49 -0700418 bool flash_on;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700419 bool torch_on;
Chun Zhangc3b505b2013-06-03 19:01:49 -0700420 struct regulator *flash_boost_reg;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700421 struct regulator *torch_boost_reg;
Chunmei Caif094d072014-08-06 13:58:50 +0800422 struct regulator *flash_wa_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -0700423};
424
425/**
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530426 * kpdbl_config_data - kpdbl configuration data
Amy Malochea2726f02013-05-10 10:19:03 -0700427 * @pwm_cfg - device pwm configuration
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530428 * @mode - running mode: pwm or lut
429 * @row_id - row id of the led
430 * @row_src_vbst - 0 for vph_pwr and 1 for vbst
431 * @row_src_en - enable row source
432 * @always_on - always on row
433 * @lut_params - lut parameters to be used by pwm driver
434 * @duty_cycles - duty cycles for lut
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530435 */
436struct kpdbl_config_data {
Amy Malochea2726f02013-05-10 10:19:03 -0700437 struct pwm_config_data *pwm_cfg;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530438 u32 row_id;
439 bool row_src_vbst;
440 bool row_src_en;
441 bool always_on;
442 struct pwm_duty_cycles *duty_cycles;
443 struct lut_params lut_params;
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530444};
445
446/**
Amy Malocheeea7b592012-10-03 15:59:36 -0700447 * rgb_config_data - rgb configuration data
Amy Malochea2726f02013-05-10 10:19:03 -0700448 * @pwm_cfg - device pwm configuration
449 * @enable - bits to enable led
Amy Malocheeea7b592012-10-03 15:59:36 -0700450 */
451struct rgb_config_data {
Amy Malochea2726f02013-05-10 10:19:03 -0700452 struct pwm_config_data *pwm_cfg;
Amy Malocheeea7b592012-10-03 15:59:36 -0700453 u8 enable;
454};
455
456/**
Amy Malochef3d5a062012-08-16 19:14:11 -0700457 * struct qpnp_led_data - internal led data structure
458 * @led_classdev - led class device
Asaf Penso55ac8472013-01-21 21:17:37 +0200459 * @delayed_work - delayed work for turning off the LED
Mao Li1ff13762014-07-30 10:00:34 +0800460 * @workqueue - dedicated workqueue to handle concurrency
Chun Zhang815a1832013-06-20 13:47:13 -0700461 * @work - workqueue for led
Amy Malochef3d5a062012-08-16 19:14:11 -0700462 * @id - led index
463 * @base_reg - base register given in device tree
464 * @lock - to protect the transactions
465 * @reg - cached value of led register
Amy Malochea5ca5552012-10-23 13:34:46 -0700466 * @num_leds - number of leds in the module
Amy Malochef3d5a062012-08-16 19:14:11 -0700467 * @max_current - maximum current supported by LED
468 * @default_on - true: default state max, false, default state 0
Asaf Penso55ac8472013-01-21 21:17:37 +0200469 * @turn_off_delay_ms - number of msec before turning off the LED
Amy Malochef3d5a062012-08-16 19:14:11 -0700470 */
471struct qpnp_led_data {
472 struct led_classdev cdev;
473 struct spmi_device *spmi_dev;
Asaf Penso55ac8472013-01-21 21:17:37 +0200474 struct delayed_work dwork;
Mao Li1ff13762014-07-30 10:00:34 +0800475 struct workqueue_struct *workqueue;
Chun Zhang815a1832013-06-20 13:47:13 -0700476 struct work_struct work;
Amy Malochef3d5a062012-08-16 19:14:11 -0700477 int id;
478 u16 base;
479 u8 reg;
Amy Malochea5ca5552012-10-23 13:34:46 -0700480 u8 num_leds;
Chun Zhang815a1832013-06-20 13:47:13 -0700481 struct mutex lock;
Amy Malochef3d5a062012-08-16 19:14:11 -0700482 struct wled_config_data *wled_cfg;
Amy Maloche864a6d52012-10-03 15:58:12 -0700483 struct flash_config_data *flash_cfg;
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530484 struct kpdbl_config_data *kpdbl_cfg;
Amy Malocheeea7b592012-10-03 15:59:36 -0700485 struct rgb_config_data *rgb_cfg;
Amy Malochef3813742013-04-11 19:33:47 -0700486 struct mpp_config_data *mpp_cfg;
Amy Malochef3d5a062012-08-16 19:14:11 -0700487 int max_current;
488 bool default_on;
Mao Li1ff13762014-07-30 10:00:34 +0800489 bool in_order_command_processing;
Asaf Penso55ac8472013-01-21 21:17:37 +0200490 int turn_off_delay_ms;
Amy Malochef3d5a062012-08-16 19:14:11 -0700491};
492
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530493static int num_kpbl_leds_on;
Abinaya P9e5dc3e2014-04-14 15:38:11 +0530494static DEFINE_MUTEX(flash_lock);
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530495
Amy Malochef3d5a062012-08-16 19:14:11 -0700496static int
497qpnp_led_masked_write(struct qpnp_led_data *led, u16 addr, u8 mask, u8 val)
498{
499 int rc;
500 u8 reg;
501
502 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
503 addr, &reg, 1);
504 if (rc) {
505 dev_err(&led->spmi_dev->dev,
506 "Unable to read from addr=%x, rc(%d)\n", addr, rc);
507 }
508
509 reg &= ~mask;
510 reg |= val;
511
512 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
513 addr, &reg, 1);
514 if (rc)
515 dev_err(&led->spmi_dev->dev,
516 "Unable to write to addr=%x, rc(%d)\n", addr, rc);
517 return rc;
518}
519
Amy Malochea5ca5552012-10-23 13:34:46 -0700520static void qpnp_dump_regs(struct qpnp_led_data *led, u8 regs[], u8 array_size)
521{
522 int i;
523 u8 val;
524
525 pr_debug("===== %s LED register dump start =====\n", led->cdev.name);
526 for (i = 0; i < array_size; i++) {
527 spmi_ext_register_readl(led->spmi_dev->ctrl,
528 led->spmi_dev->sid,
529 led->base + regs[i],
530 &val, sizeof(val));
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530531 pr_debug("%s: 0x%x = 0x%x\n", led->cdev.name,
532 led->base + regs[i], val);
Amy Malochea5ca5552012-10-23 13:34:46 -0700533 }
534 pr_debug("===== %s LED register dump end =====\n", led->cdev.name);
535}
536
Amy Maloche0150b5e2013-08-15 18:18:32 -0700537static int qpnp_wled_sync(struct qpnp_led_data *led)
538{
539 int rc;
540 u8 val;
541
542 /* sync */
543 val = WLED_SYNC_VAL;
544 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
545 WLED_SYNC_REG(led->base), &val, 1);
546 if (rc) {
547 dev_err(&led->spmi_dev->dev,
548 "WLED set sync reg failed(%d)\n", rc);
549 return rc;
550 }
551
552 val = WLED_SYNC_RESET_VAL;
553 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
554 WLED_SYNC_REG(led->base), &val, 1);
555 if (rc) {
556 dev_err(&led->spmi_dev->dev,
557 "WLED reset sync reg failed(%d)\n", rc);
558 return rc;
559 }
560 return 0;
561}
562
Amy Malochef3d5a062012-08-16 19:14:11 -0700563static int qpnp_wled_set(struct qpnp_led_data *led)
564{
Amy Maloched55fdb82013-02-26 18:11:57 -0800565 int rc, duty, level;
Amy Maloche0150b5e2013-08-15 18:18:32 -0700566 u8 val, i, num_wled_strings, sink_val;
567
568 num_wled_strings = led->wled_cfg->num_strings;
Amy Malochef3d5a062012-08-16 19:14:11 -0700569
570 level = led->cdev.brightness;
571
572 if (level > WLED_MAX_LEVEL)
573 level = WLED_MAX_LEVEL;
574 if (level == 0) {
Amy Maloche0150b5e2013-08-15 18:18:32 -0700575 for (i = 0; i < num_wled_strings; i++) {
576 rc = qpnp_led_masked_write(led,
577 WLED_FULL_SCALE_REG(led->base, i),
578 WLED_MAX_CURR_MASK, WLED_NO_CURRENT);
579 if (rc) {
580 dev_err(&led->spmi_dev->dev,
581 "Write max current failure (%d)\n",
582 rc);
583 return rc;
584 }
585 }
586
587 rc = qpnp_wled_sync(led);
588 if (rc) {
589 dev_err(&led->spmi_dev->dev,
590 "WLED sync failed(%d)\n", rc);
591 return rc;
592 }
593
594 rc = spmi_ext_register_readl(led->spmi_dev->ctrl,
595 led->spmi_dev->sid, WLED_CURR_SINK_REG(led->base),
596 &sink_val, 1);
597 if (rc) {
598 dev_err(&led->spmi_dev->dev,
599 "WLED read sink reg failed(%d)\n", rc);
600 return rc;
601 }
602
603 if (led->wled_cfg->pmic_version == PMIC_VER_8026) {
604 val = WLED_DISABLE_ALL_SINKS;
605 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
606 led->spmi_dev->sid,
607 WLED_CURR_SINK_REG(led->base), &val, 1);
608 if (rc) {
609 dev_err(&led->spmi_dev->dev,
610 "WLED write sink reg failed(%d)\n", rc);
611 return rc;
612 }
613
614 usleep(WLED_OVP_DELAY);
615 }
616
Amy Malochef3d5a062012-08-16 19:14:11 -0700617 val = WLED_BOOST_OFF;
618 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
619 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
620 &val, 1);
621 if (rc) {
622 dev_err(&led->spmi_dev->dev,
623 "WLED write ctrl reg failed(%d)\n", rc);
624 return rc;
625 }
Amy Maloche0150b5e2013-08-15 18:18:32 -0700626
627 for (i = 0; i < num_wled_strings; i++) {
628 rc = qpnp_led_masked_write(led,
629 WLED_FULL_SCALE_REG(led->base, i),
Abinaya P03cee1d2014-02-17 17:51:53 +0530630 WLED_MAX_CURR_MASK, (u8)led->max_current);
Amy Maloche0150b5e2013-08-15 18:18:32 -0700631 if (rc) {
632 dev_err(&led->spmi_dev->dev,
633 "Write max current failure (%d)\n",
634 rc);
635 return rc;
636 }
637 }
638
639 rc = qpnp_wled_sync(led);
640 if (rc) {
641 dev_err(&led->spmi_dev->dev,
642 "WLED sync failed(%d)\n", rc);
643 return rc;
644 }
645
646 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
647 led->spmi_dev->sid, WLED_CURR_SINK_REG(led->base),
648 &sink_val, 1);
649 if (rc) {
650 dev_err(&led->spmi_dev->dev,
651 "WLED write sink reg failed(%d)\n", rc);
652 return rc;
653 }
654
Amy Malochef3d5a062012-08-16 19:14:11 -0700655 } else {
656 val = WLED_BOOST_ON;
657 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
658 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
659 &val, 1);
660 if (rc) {
661 dev_err(&led->spmi_dev->dev,
662 "WLED write ctrl reg failed(%d)\n", rc);
663 return rc;
664 }
665 }
666
667 duty = (WLED_MAX_DUTY_CYCLE * level) / WLED_MAX_LEVEL;
668
Amy Malochef3d5a062012-08-16 19:14:11 -0700669 /* program brightness control registers */
670 for (i = 0; i < num_wled_strings; i++) {
671 rc = qpnp_led_masked_write(led,
672 WLED_BRIGHTNESS_CNTL_MSB(led->base, i), WLED_MSB_MASK,
673 (duty >> WLED_8_BIT_SHFT) & WLED_4_BIT_MASK);
674 if (rc) {
675 dev_err(&led->spmi_dev->dev,
676 "WLED set brightness MSB failed(%d)\n", rc);
677 return rc;
678 }
679 val = duty & WLED_8_BIT_MASK;
680 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
681 led->spmi_dev->sid,
682 WLED_BRIGHTNESS_CNTL_LSB(led->base, i), &val, 1);
683 if (rc) {
684 dev_err(&led->spmi_dev->dev,
685 "WLED set brightness LSB failed(%d)\n", rc);
686 return rc;
687 }
688 }
689
Amy Maloche0150b5e2013-08-15 18:18:32 -0700690 rc = qpnp_wled_sync(led);
Amy Malochef3d5a062012-08-16 19:14:11 -0700691 if (rc) {
Amy Maloche0150b5e2013-08-15 18:18:32 -0700692 dev_err(&led->spmi_dev->dev, "WLED sync failed(%d)\n", rc);
Amy Malochef3d5a062012-08-16 19:14:11 -0700693 return rc;
694 }
695 return 0;
696}
697
Amy Malochef3813742013-04-11 19:33:47 -0700698static int qpnp_mpp_set(struct qpnp_led_data *led)
699{
Abinaya P03cee1d2014-02-17 17:51:53 +0530700 int rc;
701 u8 val;
Xu Kai9d395ce2014-02-28 13:11:09 +0800702 int duty_us, duty_ns, period_us;
Amy Malochef3813742013-04-11 19:33:47 -0700703
704 if (led->cdev.brightness) {
Chun Zhang874c9ab2013-07-08 17:18:34 -0700705 if (led->cdev.brightness < led->mpp_cfg->min_brightness) {
706 dev_warn(&led->spmi_dev->dev,
707 "brightness is less than supported..." \
708 "set to minimum supported\n");
709 led->cdev.brightness = led->mpp_cfg->min_brightness;
710 }
711
Amy Maloche4cf9f322013-05-29 15:53:46 -0700712 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
713 if (!led->mpp_cfg->pwm_cfg->blinking) {
714 led->mpp_cfg->pwm_cfg->mode =
715 led->mpp_cfg->pwm_cfg->default_mode;
716 led->mpp_cfg->pwm_mode =
717 led->mpp_cfg->pwm_cfg->default_mode;
718 }
719 }
Amy Malochea2726f02013-05-10 10:19:03 -0700720 if (led->mpp_cfg->pwm_mode == PWM_MODE) {
Amy Malochea2726f02013-05-10 10:19:03 -0700721 /*config pwm for brightness scaling*/
Xu Kai9d395ce2014-02-28 13:11:09 +0800722 period_us = led->mpp_cfg->pwm_cfg->pwm_period_us;
723 if (period_us > INT_MAX / NSEC_PER_USEC) {
724 duty_us = (period_us * led->cdev.brightness) /
725 LED_FULL;
726 rc = pwm_config_us(
727 led->mpp_cfg->pwm_cfg->pwm_dev,
Amy Malochea2726f02013-05-10 10:19:03 -0700728 duty_us,
Xu Kai9d395ce2014-02-28 13:11:09 +0800729 period_us);
730 } else {
731 duty_ns = ((period_us * NSEC_PER_USEC) /
732 LED_FULL) * led->cdev.brightness;
733 rc = pwm_config(
734 led->mpp_cfg->pwm_cfg->pwm_dev,
735 duty_ns,
736 period_us * NSEC_PER_USEC);
737 }
Amy Malochea2726f02013-05-10 10:19:03 -0700738 if (rc < 0) {
739 dev_err(&led->spmi_dev->dev, "Failed to " \
740 "configure pwm for new values\n");
741 return rc;
742 }
Amy Malochef3813742013-04-11 19:33:47 -0700743 }
744
Amy Malochea2726f02013-05-10 10:19:03 -0700745 if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
746 pwm_enable(led->mpp_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka38ec7f32013-09-11 13:01:48 +0530747 else {
748 if (led->cdev.brightness < LED_MPP_CURRENT_MIN)
749 led->cdev.brightness = LED_MPP_CURRENT_MIN;
750
751 val = (led->cdev.brightness / LED_MPP_CURRENT_MIN) - 1;
752
753 rc = qpnp_led_masked_write(led,
754 LED_MPP_SINK_CTRL(led->base),
755 LED_MPP_SINK_MASK, val);
756 if (rc) {
757 dev_err(&led->spmi_dev->dev,
758 "Failed to write sink control reg\n");
759 return rc;
760 }
761 }
Amy Malochea2726f02013-05-10 10:19:03 -0700762
Amy Malochece59f662013-05-02 10:59:53 -0700763 val = (led->mpp_cfg->source_sel & LED_MPP_SRC_MASK) |
764 (led->mpp_cfg->mode_ctrl & LED_MPP_MODE_CTRL_MASK);
Amy Malochef3813742013-04-11 19:33:47 -0700765
766 rc = qpnp_led_masked_write(led,
Amy Malochea2726f02013-05-10 10:19:03 -0700767 LED_MPP_MODE_CTRL(led->base), LED_MPP_MODE_MASK,
768 val);
Amy Malochef3813742013-04-11 19:33:47 -0700769 if (rc) {
770 dev_err(&led->spmi_dev->dev,
771 "Failed to write led mode reg\n");
772 return rc;
773 }
774
775 rc = qpnp_led_masked_write(led,
776 LED_MPP_EN_CTRL(led->base), LED_MPP_EN_MASK,
777 LED_MPP_EN_ENABLE);
Amy Malochea2726f02013-05-10 10:19:03 -0700778 if (rc) {
779 dev_err(&led->spmi_dev->dev,
780 "Failed to write led enable " \
781 "reg\n");
782 return rc;
783 }
Amy Malochef3813742013-04-11 19:33:47 -0700784 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -0700785 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
786 led->mpp_cfg->pwm_cfg->mode =
787 led->mpp_cfg->pwm_cfg->default_mode;
788 led->mpp_cfg->pwm_mode =
789 led->mpp_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -0700790 pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
Amy Maloche4cf9f322013-05-29 15:53:46 -0700791 }
Amy Malochef3813742013-04-11 19:33:47 -0700792 rc = qpnp_led_masked_write(led,
793 LED_MPP_MODE_CTRL(led->base),
794 LED_MPP_MODE_MASK,
795 LED_MPP_MODE_DISABLE);
796 if (rc) {
797 dev_err(&led->spmi_dev->dev,
798 "Failed to write led mode reg\n");
799 return rc;
800 }
801
802 rc = qpnp_led_masked_write(led,
803 LED_MPP_EN_CTRL(led->base),
804 LED_MPP_EN_MASK,
805 LED_MPP_EN_DISABLE);
806 if (rc) {
807 dev_err(&led->spmi_dev->dev,
808 "Failed to write led enable reg\n");
809 return rc;
810 }
811 }
812
Amy Maloche4cf9f322013-05-29 15:53:46 -0700813 if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
814 led->mpp_cfg->pwm_cfg->blinking = false;
Amy Malochef3813742013-04-11 19:33:47 -0700815 qpnp_dump_regs(led, mpp_debug_regs, ARRAY_SIZE(mpp_debug_regs));
816
817 return 0;
818}
819
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700820static int qpnp_flash_regulator_operate(struct qpnp_led_data *led, bool on)
821{
822 int rc, i;
823 struct qpnp_led_data *led_array;
824 bool regulator_on = false;
825
826 led_array = dev_get_drvdata(&led->spmi_dev->dev);
827 if (!led_array) {
828 dev_err(&led->spmi_dev->dev,
829 "Unable to get LED array\n");
830 return -EINVAL;
831 }
832
833 for (i = 0; i < led->num_leds; i++)
834 regulator_on |= led_array[i].flash_cfg->flash_on;
835
836 if (!on)
837 goto regulator_turn_off;
838
839 if (!regulator_on && !led->flash_cfg->flash_on) {
840 for (i = 0; i < led->num_leds; i++) {
841 if (led_array[i].flash_cfg->flash_reg_get) {
Abinaya Pa1677d22014-08-28 13:11:01 +0530842 if (led_array[i].flash_cfg->flash_wa_reg_get) {
843 rc = regulator_enable(
844 led_array[i].flash_cfg->
845 flash_wa_reg);
846 if (rc) {
847 dev_err(&led->spmi_dev->dev,
848 "Flash wa regulator"
849 "enable failed(%d)\n",
850 rc);
851 return rc;
852 }
Chunmei Caif094d072014-08-06 13:58:50 +0800853 }
854
855 rc = regulator_enable(
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700856 led_array[i].flash_cfg->\
857 flash_boost_reg);
858 if (rc) {
Abinaya Pa1677d22014-08-28 13:11:01 +0530859 if (led_array[i].flash_cfg->
860 flash_wa_reg_get)
861 /* Disable flash wa regulator
862 * when flash boost regulator
863 * enable fails
864 */
865 regulator_disable(
866 led_array[i].flash_cfg->
867 flash_wa_reg);
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700868 dev_err(&led->spmi_dev->dev,
Abinaya Pa1677d22014-08-28 13:11:01 +0530869 "Flash boost regulator enable"
870 "failed(%d)\n", rc);
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700871 return rc;
872 }
873 led->flash_cfg->flash_on = true;
874 }
875 break;
876 }
877 }
878
879 return 0;
880
881regulator_turn_off:
882 if (regulator_on && led->flash_cfg->flash_on) {
883 for (i = 0; i < led->num_leds; i++) {
884 if (led_array[i].flash_cfg->flash_reg_get) {
Chun Zhang9d5ff672013-08-01 18:18:26 -0700885 rc = qpnp_led_masked_write(led,
886 FLASH_ENABLE_CONTROL(led->base),
887 FLASH_ENABLE_MASK,
888 FLASH_DISABLE_ALL);
889 if (rc) {
890 dev_err(&led->spmi_dev->dev,
891 "Enable reg write failed(%d)\n",
892 rc);
893 }
894
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700895 rc = regulator_disable(led_array[i].flash_cfg->\
896 flash_boost_reg);
897 if (rc) {
898 dev_err(&led->spmi_dev->dev,
Abinaya Pa1677d22014-08-28 13:11:01 +0530899 "Flash boost regulator disable"
900 "failed(%d)\n", rc);
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700901 return rc;
902 }
Abinaya Pa1677d22014-08-28 13:11:01 +0530903 if (led_array[i].flash_cfg->flash_wa_reg_get) {
904 rc = regulator_disable(
905 led_array[i].flash_cfg->
906 flash_wa_reg);
907 if (rc) {
908 dev_err(&led->spmi_dev->dev,
909 "Flash_wa regulator"
910 "disable failed(%d)\n",
911 rc);
912 return rc;
913 }
Chunmei Caif094d072014-08-06 13:58:50 +0800914 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700915 led->flash_cfg->flash_on = false;
916 }
917 break;
918 }
919 }
920
921 return 0;
922}
923
Chun Zhangdf2d3062013-06-25 20:14:46 -0700924static int qpnp_torch_regulator_operate(struct qpnp_led_data *led, bool on)
Amy Maloche864a6d52012-10-03 15:58:12 -0700925{
926 int rc;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700927
928 if (!on)
929 goto regulator_turn_off;
930
931 if (!led->flash_cfg->torch_on) {
932 rc = regulator_enable(led->flash_cfg->torch_boost_reg);
933 if (rc) {
934 dev_err(&led->spmi_dev->dev,
935 "Regulator enable failed(%d)\n", rc);
936 return rc;
937 }
938 led->flash_cfg->torch_on = true;
939 }
940 return 0;
941
942regulator_turn_off:
943 if (led->flash_cfg->torch_on) {
Chun Zhang9d5ff672013-08-01 18:18:26 -0700944 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
945 FLASH_ENABLE_MODULE_MASK, FLASH_DISABLE_ALL);
946 if (rc) {
947 dev_err(&led->spmi_dev->dev,
948 "Enable reg write failed(%d)\n", rc);
949 }
950
Chun Zhangdf2d3062013-06-25 20:14:46 -0700951 rc = regulator_disable(led->flash_cfg->torch_boost_reg);
952 if (rc) {
953 dev_err(&led->spmi_dev->dev,
954 "Regulator disable failed(%d)\n", rc);
955 return rc;
956 }
957 led->flash_cfg->torch_on = false;
958 }
959 return 0;
960}
961
962static int qpnp_flash_set(struct qpnp_led_data *led)
963{
964 int rc, error;
Amy Maloche864a6d52012-10-03 15:58:12 -0700965 int val = led->cdev.brightness;
966
Chun Zhang9c1fbaa2013-06-17 15:31:18 -0700967 if (led->flash_cfg->torch_enable)
968 led->flash_cfg->current_prgm =
969 (val * TORCH_MAX_LEVEL / led->max_current);
970 else
971 led->flash_cfg->current_prgm =
972 (val * FLASH_MAX_LEVEL / led->max_current);
Amy Maloche864a6d52012-10-03 15:58:12 -0700973
Amy Maloche864a6d52012-10-03 15:58:12 -0700974 /* Set led current */
975 if (val > 0) {
Amy Malochebc97c0d22013-03-24 22:06:16 -0700976 if (led->flash_cfg->torch_enable) {
Chun Zhang0d6ca072013-07-30 21:08:39 -0700977 if (led->flash_cfg->peripheral_subtype ==
978 FLASH_SUBTYPE_DUAL) {
979 rc = qpnp_torch_regulator_operate(led, true);
980 if (rc) {
981 dev_err(&led->spmi_dev->dev,
Chun Zhangdf2d3062013-06-25 20:14:46 -0700982 "Torch regulator operate failed(%d)\n",
983 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700984 return rc;
985 }
986 } else if (led->flash_cfg->peripheral_subtype ==
987 FLASH_SUBTYPE_SINGLE) {
988 rc = qpnp_flash_regulator_operate(led, true);
989 if (rc) {
990 dev_err(&led->spmi_dev->dev,
991 "Flash regulator operate failed(%d)\n",
992 rc);
993 goto error_flash_set;
994 }
Chun Zhangdf2d3062013-06-25 20:14:46 -0700995 }
996
Amy Malochebc97c0d22013-03-24 22:06:16 -0700997 rc = qpnp_led_masked_write(led,
998 FLASH_LED_UNLOCK_SECURE(led->base),
999 FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
1000 if (rc) {
1001 dev_err(&led->spmi_dev->dev,
1002 "Secure reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -07001003 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001004 }
1005
1006 rc = qpnp_led_masked_write(led,
1007 FLASH_LED_TORCH(led->base),
1008 FLASH_TORCH_MASK, FLASH_LED_TORCH_ENABLE);
1009 if (rc) {
1010 dev_err(&led->spmi_dev->dev,
1011 "Torch reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -07001012 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001013 }
1014
Amy Malochebc97c0d22013-03-24 22:06:16 -07001015 rc = qpnp_led_masked_write(led,
1016 led->flash_cfg->current_addr,
Chun Zhange8954cf2013-05-02 11:14:34 -07001017 FLASH_CURRENT_MASK,
1018 led->flash_cfg->current_prgm);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001019 if (rc) {
1020 dev_err(&led->spmi_dev->dev,
1021 "Current reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -07001022 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001023 }
1024
1025 rc = qpnp_led_masked_write(led,
1026 led->flash_cfg->second_addr,
Chun Zhange8954cf2013-05-02 11:14:34 -07001027 FLASH_CURRENT_MASK,
1028 led->flash_cfg->current_prgm);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001029 if (rc) {
1030 dev_err(&led->spmi_dev->dev,
1031 "2nd Current reg write failed(%d)\n",
1032 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -07001033 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001034 }
1035
Chun Zhange8954cf2013-05-02 11:14:34 -07001036 qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
1037 FLASH_CURRENT_MASK,
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07001038 TORCH_MAX_LEVEL);
Chun Zhange8954cf2013-05-02 11:14:34 -07001039 if (rc) {
1040 dev_err(&led->spmi_dev->dev,
1041 "Max current reg write failed(%d)\n",
1042 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -07001043 goto error_reg_write;
Chun Zhange8954cf2013-05-02 11:14:34 -07001044 }
1045
Chun Zhang9d5ff672013-08-01 18:18:26 -07001046 rc = qpnp_led_masked_write(led,
1047 FLASH_ENABLE_CONTROL(led->base),
1048 FLASH_ENABLE_MASK,
1049 led->flash_cfg->enable_module);
1050 if (rc) {
1051 dev_err(&led->spmi_dev->dev,
1052 "Enable reg write failed(%d)\n",
1053 rc);
1054 goto error_reg_write;
1055 }
1056
Abinaya Pb6e3ba42014-01-07 15:32:31 +05301057 if (!led->flash_cfg->strobe_type)
1058 led->flash_cfg->trigger_flash &=
1059 ~FLASH_HW_SW_STROBE_SEL_MASK;
1060 else
1061 led->flash_cfg->trigger_flash |=
1062 FLASH_HW_SW_STROBE_SEL_MASK;
1063
Chun Zhang9d5ff672013-08-01 18:18:26 -07001064 rc = qpnp_led_masked_write(led,
1065 FLASH_LED_STROBE_CTRL(led->base),
1066 led->flash_cfg->trigger_flash,
1067 led->flash_cfg->trigger_flash);
1068 if (rc) {
1069 dev_err(&led->spmi_dev->dev,
1070 "LED %d strobe reg write failed(%d)\n",
1071 led->id, rc);
1072 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001073 }
1074 } else {
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001075 rc = qpnp_flash_regulator_operate(led, true);
1076 if (rc) {
1077 dev_err(&led->spmi_dev->dev,
1078 "Flash regulator operate failed(%d)\n",
1079 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001080 goto error_flash_set;
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001081 }
1082
Chun Zhange8954cf2013-05-02 11:14:34 -07001083 /* Set flash safety timer */
Amy Malochebc97c0d22013-03-24 22:06:16 -07001084 rc = qpnp_led_masked_write(led,
Chun Zhange8954cf2013-05-02 11:14:34 -07001085 FLASH_SAFETY_TIMER(led->base),
1086 FLASH_SAFETY_TIMER_MASK,
1087 led->flash_cfg->duration);
1088 if (rc) {
1089 dev_err(&led->spmi_dev->dev,
1090 "Safety timer reg write failed(%d)\n",
1091 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001092 goto error_flash_set;
Chun Zhange8954cf2013-05-02 11:14:34 -07001093 }
1094
1095 /* Set max current */
1096 rc = qpnp_led_masked_write(led,
1097 FLASH_MAX_CURR(led->base), FLASH_CURRENT_MASK,
1098 FLASH_MAX_LEVEL);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001099 if (rc) {
1100 dev_err(&led->spmi_dev->dev,
1101 "Max current reg write failed(%d)\n",
1102 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001103 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001104 }
1105
Chun Zhange8954cf2013-05-02 11:14:34 -07001106 /* Set clamp current */
1107 rc = qpnp_led_masked_write(led,
1108 FLASH_CLAMP_CURR(led->base),
1109 FLASH_CURRENT_MASK,
1110 led->flash_cfg->clamp_curr);
1111 if (rc) {
1112 dev_err(&led->spmi_dev->dev,
1113 "Clamp current reg write failed(%d)\n",
1114 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001115 goto error_flash_set;
Chun Zhange8954cf2013-05-02 11:14:34 -07001116 }
1117
Amy Malochebc97c0d22013-03-24 22:06:16 -07001118 rc = qpnp_led_masked_write(led,
1119 led->flash_cfg->current_addr,
1120 FLASH_CURRENT_MASK,
1121 led->flash_cfg->current_prgm);
1122 if (rc) {
1123 dev_err(&led->spmi_dev->dev,
1124 "Current reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001125 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001126 }
1127
1128 rc = qpnp_led_masked_write(led,
Amy Malochebc97c0d22013-03-24 22:06:16 -07001129 FLASH_ENABLE_CONTROL(led->base),
Chun Zhang9d5ff672013-08-01 18:18:26 -07001130 led->flash_cfg->enable_module,
1131 led->flash_cfg->enable_module);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001132 if (rc) {
1133 dev_err(&led->spmi_dev->dev,
1134 "Enable reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001135 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001136 }
Amy Maloche38b9aae2013-02-07 12:15:34 -08001137
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07001138 /*
1139 * Add 1ms delay for bharger enter stable state
1140 */
1141 usleep(FLASH_RAMP_UP_DELAY_US);
1142
Abinaya Pb6e3ba42014-01-07 15:32:31 +05301143 if (!led->flash_cfg->strobe_type)
1144 led->flash_cfg->trigger_flash &=
1145 ~FLASH_HW_SW_STROBE_SEL_MASK;
1146 else
1147 led->flash_cfg->trigger_flash |=
1148 FLASH_HW_SW_STROBE_SEL_MASK;
1149
1150 rc = qpnp_led_masked_write(led,
1151 FLASH_LED_STROBE_CTRL(led->base),
1152 led->flash_cfg->trigger_flash,
1153 led->flash_cfg->trigger_flash);
1154 if (rc) {
1155 dev_err(&led->spmi_dev->dev,
1156 "LED %d strobe reg write failed(%d)\n",
1157 led->id, rc);
1158 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001159 }
Amy Maloche38b9aae2013-02-07 12:15:34 -08001160 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001161 } else {
Chun Zhangdf2d3062013-06-25 20:14:46 -07001162 rc = qpnp_led_masked_write(led,
1163 FLASH_LED_STROBE_CTRL(led->base),
Chun Zhang9d5ff672013-08-01 18:18:26 -07001164 led->flash_cfg->trigger_flash,
Chun Zhangdf2d3062013-06-25 20:14:46 -07001165 FLASH_DISABLE_ALL);
1166 if (rc) {
1167 dev_err(&led->spmi_dev->dev,
1168 "LED %d flash write failed(%d)\n", led->id, rc);
1169 if (led->flash_cfg->torch_enable)
1170 goto error_torch_set;
1171 else
1172 goto error_flash_set;
1173 }
1174
Amy Malochebc97c0d22013-03-24 22:06:16 -07001175 if (led->flash_cfg->torch_enable) {
1176 rc = qpnp_led_masked_write(led,
1177 FLASH_LED_UNLOCK_SECURE(led->base),
1178 FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
1179 if (rc) {
1180 dev_err(&led->spmi_dev->dev,
1181 "Secure reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001182 goto error_torch_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001183 }
1184
1185 rc = qpnp_led_masked_write(led,
Chun Zhange8954cf2013-05-02 11:14:34 -07001186 FLASH_LED_TORCH(led->base),
1187 FLASH_TORCH_MASK,
1188 FLASH_LED_TORCH_DISABLE);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001189 if (rc) {
1190 dev_err(&led->spmi_dev->dev,
Chun Zhange8954cf2013-05-02 11:14:34 -07001191 "Torch reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001192 goto error_torch_set;
1193 }
1194
Chun Zhang0d6ca072013-07-30 21:08:39 -07001195 if (led->flash_cfg->peripheral_subtype ==
1196 FLASH_SUBTYPE_DUAL) {
1197 rc = qpnp_torch_regulator_operate(led, false);
1198 if (rc) {
1199 dev_err(&led->spmi_dev->dev,
1200 "Torch regulator operate failed(%d)\n",
1201 rc);
1202 return rc;
1203 }
1204 } else if (led->flash_cfg->peripheral_subtype ==
1205 FLASH_SUBTYPE_SINGLE) {
1206 rc = qpnp_flash_regulator_operate(led, false);
1207 if (rc) {
1208 dev_err(&led->spmi_dev->dev,
1209 "Flash regulator operate failed(%d)\n",
1210 rc);
1211 return rc;
1212 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07001213 }
Chun Zhangdf2d3062013-06-25 20:14:46 -07001214 } else {
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07001215 /*
1216 * Disable module after ramp down complete for stable
1217 * behavior
1218 */
1219 usleep(FLASH_RAMP_DN_DELAY_US);
1220
Chun Zhang9d5ff672013-08-01 18:18:26 -07001221 rc = qpnp_led_masked_write(led,
1222 FLASH_ENABLE_CONTROL(led->base),
1223 led->flash_cfg->enable_module &
1224 ~FLASH_ENABLE_MODULE_MASK,
1225 FLASH_DISABLE_ALL);
1226 if (rc) {
1227 dev_err(&led->spmi_dev->dev,
1228 "Enable reg write failed(%d)\n", rc);
1229 if (led->flash_cfg->torch_enable)
1230 goto error_torch_set;
1231 else
1232 goto error_flash_set;
1233 }
1234
Chun Zhangdf2d3062013-06-25 20:14:46 -07001235 rc = qpnp_flash_regulator_operate(led, false);
1236 if (rc) {
1237 dev_err(&led->spmi_dev->dev,
1238 "Flash regulator operate failed(%d)\n",
1239 rc);
1240 return rc;
1241 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001242 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001243 }
1244
Amy Malocheeea7b592012-10-03 15:59:36 -07001245 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
1246
1247 return 0;
Chun Zhangdf2d3062013-06-25 20:14:46 -07001248
Chun Zhang0d6ca072013-07-30 21:08:39 -07001249error_reg_write:
1250 if (led->flash_cfg->peripheral_subtype == FLASH_SUBTYPE_SINGLE)
1251 goto error_flash_set;
1252
Chun Zhangdf2d3062013-06-25 20:14:46 -07001253error_torch_set:
1254 error = qpnp_torch_regulator_operate(led, false);
1255 if (error) {
1256 dev_err(&led->spmi_dev->dev,
1257 "Torch regulator operate failed(%d)\n", rc);
1258 return error;
1259 }
1260 return rc;
1261
1262error_flash_set:
1263 error = qpnp_flash_regulator_operate(led, false);
1264 if (error) {
1265 dev_err(&led->spmi_dev->dev,
1266 "Flash regulator operate failed(%d)\n", rc);
1267 return error;
1268 }
1269 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07001270}
1271
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301272static int qpnp_kpdbl_set(struct qpnp_led_data *led)
1273{
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301274 int rc;
Xu Kai9d395ce2014-02-28 13:11:09 +08001275 int duty_us, duty_ns, period_us;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301276
1277 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001278 if (!led->kpdbl_cfg->pwm_cfg->blinking)
1279 led->kpdbl_cfg->pwm_cfg->mode =
1280 led->kpdbl_cfg->pwm_cfg->default_mode;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301281 if (!num_kpbl_leds_on) {
1282 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
1283 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
1284 if (rc) {
1285 dev_err(&led->spmi_dev->dev,
1286 "Enable reg write failed(%d)\n", rc);
1287 return rc;
1288 }
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301289 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301290
1291 if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
Xu Kai9d395ce2014-02-28 13:11:09 +08001292 period_us = led->kpdbl_cfg->pwm_cfg->pwm_period_us;
1293 if (period_us > INT_MAX / NSEC_PER_USEC) {
1294 duty_us = (period_us * led->cdev.brightness) /
1295 KPDBL_MAX_LEVEL;
1296 rc = pwm_config_us(
1297 led->kpdbl_cfg->pwm_cfg->pwm_dev,
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301298 duty_us,
Xu Kai9d395ce2014-02-28 13:11:09 +08001299 period_us);
1300 } else {
1301 duty_ns = ((period_us * NSEC_PER_USEC) /
1302 KPDBL_MAX_LEVEL) * led->cdev.brightness;
1303 rc = pwm_config(
1304 led->kpdbl_cfg->pwm_cfg->pwm_dev,
1305 duty_ns,
1306 period_us * NSEC_PER_USEC);
1307 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301308 if (rc < 0) {
1309 dev_err(&led->spmi_dev->dev, "pwm config failed\n");
1310 return rc;
1311 }
1312 }
1313
Amy Malochea2726f02013-05-10 10:19:03 -07001314 rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301315 if (rc < 0) {
1316 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1317 return rc;
1318 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301319
1320 num_kpbl_leds_on++;
1321
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301322 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001323 led->kpdbl_cfg->pwm_cfg->mode =
1324 led->kpdbl_cfg->pwm_cfg->default_mode;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301325
1326 if (led->kpdbl_cfg->always_on) {
Xu Kai0d9a3e22013-12-17 13:53:56 +08001327 rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0,
Xu Kai9d395ce2014-02-28 13:11:09 +08001328 led->kpdbl_cfg->pwm_cfg->pwm_period_us);
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301329 if (rc < 0) {
1330 dev_err(&led->spmi_dev->dev,
1331 "pwm config failed\n");
1332 return rc;
1333 }
1334
1335 rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
1336 if (rc < 0) {
1337 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1338 return rc;
1339 }
1340 } else
1341 pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
1342
1343 if (num_kpbl_leds_on > 0)
1344 num_kpbl_leds_on--;
1345
1346 if (!num_kpbl_leds_on) {
1347 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
1348 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_DIS);
1349 if (rc) {
1350 dev_err(&led->spmi_dev->dev,
1351 "Failed to write led enable reg\n");
1352 return rc;
1353 }
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301354 }
1355 }
1356
Amy Maloche4cf9f322013-05-29 15:53:46 -07001357 led->kpdbl_cfg->pwm_cfg->blinking = false;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301358
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301359 qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
1360
1361 return 0;
1362}
1363
Amy Malocheeea7b592012-10-03 15:59:36 -07001364static int qpnp_rgb_set(struct qpnp_led_data *led)
1365{
Amy Malocheeea7b592012-10-03 15:59:36 -07001366 int rc;
Xu Kai9d395ce2014-02-28 13:11:09 +08001367 int duty_us, duty_ns, period_us;
Amy Malocheeea7b592012-10-03 15:59:36 -07001368
1369 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001370 if (!led->rgb_cfg->pwm_cfg->blinking)
1371 led->rgb_cfg->pwm_cfg->mode =
1372 led->rgb_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07001373 if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
Xu Kai9d395ce2014-02-28 13:11:09 +08001374 period_us = led->rgb_cfg->pwm_cfg->pwm_period_us;
1375 if (period_us > INT_MAX / NSEC_PER_USEC) {
1376 duty_us = (period_us * led->cdev.brightness) /
1377 LED_FULL;
1378 rc = pwm_config_us(
1379 led->rgb_cfg->pwm_cfg->pwm_dev,
Xu Kai0d9a3e22013-12-17 13:53:56 +08001380 duty_us,
Xu Kai9d395ce2014-02-28 13:11:09 +08001381 period_us);
1382 } else {
1383 duty_ns = ((period_us * NSEC_PER_USEC) /
1384 LED_FULL) * led->cdev.brightness;
1385 rc = pwm_config(
1386 led->rgb_cfg->pwm_cfg->pwm_dev,
1387 duty_ns,
1388 period_us * NSEC_PER_USEC);
1389 }
Amy Malocheeea7b592012-10-03 15:59:36 -07001390 if (rc < 0) {
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301391 dev_err(&led->spmi_dev->dev,
1392 "pwm config failed\n");
Amy Malocheeea7b592012-10-03 15:59:36 -07001393 return rc;
1394 }
1395 }
1396 rc = qpnp_led_masked_write(led,
1397 RGB_LED_EN_CTL(led->base),
1398 led->rgb_cfg->enable, led->rgb_cfg->enable);
1399 if (rc) {
1400 dev_err(&led->spmi_dev->dev,
1401 "Failed to write led enable reg\n");
1402 return rc;
1403 }
Amy Malochea2726f02013-05-10 10:19:03 -07001404
1405 rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301406 if (rc < 0) {
1407 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1408 return rc;
1409 }
Amy Malocheeea7b592012-10-03 15:59:36 -07001410 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001411 led->rgb_cfg->pwm_cfg->mode =
1412 led->rgb_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07001413 pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
Amy Malocheeea7b592012-10-03 15:59:36 -07001414 rc = qpnp_led_masked_write(led,
1415 RGB_LED_EN_CTL(led->base),
1416 led->rgb_cfg->enable, RGB_LED_DISABLE);
1417 if (rc) {
1418 dev_err(&led->spmi_dev->dev,
1419 "Failed to write led enable reg\n");
1420 return rc;
1421 }
1422 }
1423
Amy Maloche4cf9f322013-05-29 15:53:46 -07001424 led->rgb_cfg->pwm_cfg->blinking = false;
Amy Malocheeea7b592012-10-03 15:59:36 -07001425 qpnp_dump_regs(led, rgb_pwm_debug_regs, ARRAY_SIZE(rgb_pwm_debug_regs));
1426
Amy Maloche864a6d52012-10-03 15:58:12 -07001427 return 0;
1428}
1429
Amy Malochef3d5a062012-08-16 19:14:11 -07001430static void qpnp_led_set(struct led_classdev *led_cdev,
1431 enum led_brightness value)
1432{
Amy Malochef3d5a062012-08-16 19:14:11 -07001433 struct qpnp_led_data *led;
1434
1435 led = container_of(led_cdev, struct qpnp_led_data, cdev);
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301436 if (value < LED_OFF) {
Amy Malochea5ca5552012-10-23 13:34:46 -07001437 dev_err(&led->spmi_dev->dev, "Invalid brightness value\n");
Amy Malochef3d5a062012-08-16 19:14:11 -07001438 return;
1439 }
1440
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301441 if (value > led->cdev.max_brightness)
1442 value = led->cdev.max_brightness;
1443
Chun Zhang815a1832013-06-20 13:47:13 -07001444 led->cdev.brightness = value;
Mao Li1ff13762014-07-30 10:00:34 +08001445 if (led->in_order_command_processing)
1446 queue_work(led->workqueue, &led->work);
1447 else
1448 schedule_work(&led->work);
Chun Zhang815a1832013-06-20 13:47:13 -07001449}
1450
1451static void __qpnp_led_work(struct qpnp_led_data *led,
1452 enum led_brightness value)
1453{
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001454 int rc;
Chun Zhangc3b505b2013-06-03 19:01:49 -07001455
Abinaya P9e5dc3e2014-04-14 15:38:11 +05301456 if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1)
1457 mutex_lock(&flash_lock);
1458 else
1459 mutex_lock(&led->lock);
Amy Malochef3d5a062012-08-16 19:14:11 -07001460
1461 switch (led->id) {
1462 case QPNP_ID_WLED:
1463 rc = qpnp_wled_set(led);
1464 if (rc < 0)
Amy Malochea5ca5552012-10-23 13:34:46 -07001465 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -07001466 "WLED set brightness failed (%d)\n", rc);
1467 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07001468 case QPNP_ID_FLASH1_LED0:
1469 case QPNP_ID_FLASH1_LED1:
1470 rc = qpnp_flash_set(led);
1471 if (rc < 0)
1472 dev_err(&led->spmi_dev->dev,
1473 "FLASH set brightness failed (%d)\n", rc);
1474 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07001475 case QPNP_ID_RGB_RED:
1476 case QPNP_ID_RGB_GREEN:
1477 case QPNP_ID_RGB_BLUE:
1478 rc = qpnp_rgb_set(led);
1479 if (rc < 0)
1480 dev_err(&led->spmi_dev->dev,
1481 "RGB set brightness failed (%d)\n", rc);
1482 break;
Amy Malochef3813742013-04-11 19:33:47 -07001483 case QPNP_ID_LED_MPP:
1484 rc = qpnp_mpp_set(led);
1485 if (rc < 0)
1486 dev_err(&led->spmi_dev->dev,
1487 "MPP set brightness failed (%d)\n", rc);
Amy Maloche14c9eb32013-05-06 13:37:58 -07001488 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301489 case QPNP_ID_KPDBL:
1490 rc = qpnp_kpdbl_set(led);
1491 if (rc < 0)
1492 dev_err(&led->spmi_dev->dev,
1493 "KPDBL set brightness failed (%d)\n", rc);
Amy Malochef3813742013-04-11 19:33:47 -07001494 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07001495 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07001496 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -07001497 break;
1498 }
Abinaya P9e5dc3e2014-04-14 15:38:11 +05301499 if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1)
1500 mutex_unlock(&flash_lock);
1501 else
1502 mutex_unlock(&led->lock);
Chun Zhangc3b505b2013-06-03 19:01:49 -07001503
Amy Malochef3d5a062012-08-16 19:14:11 -07001504}
1505
Chun Zhang815a1832013-06-20 13:47:13 -07001506static void qpnp_led_work(struct work_struct *work)
1507{
1508 struct qpnp_led_data *led = container_of(work,
1509 struct qpnp_led_data, work);
1510
1511 __qpnp_led_work(led, led->cdev.brightness);
1512
1513 return;
1514}
1515
Amy Malochef3d5a062012-08-16 19:14:11 -07001516static int __devinit qpnp_led_set_max_brightness(struct qpnp_led_data *led)
1517{
1518 switch (led->id) {
1519 case QPNP_ID_WLED:
1520 led->cdev.max_brightness = WLED_MAX_LEVEL;
1521 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07001522 case QPNP_ID_FLASH1_LED0:
1523 case QPNP_ID_FLASH1_LED1:
1524 led->cdev.max_brightness = led->max_current;
1525 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07001526 case QPNP_ID_RGB_RED:
1527 case QPNP_ID_RGB_GREEN:
1528 case QPNP_ID_RGB_BLUE:
1529 led->cdev.max_brightness = RGB_MAX_LEVEL;
1530 break;
Amy Malochef3813742013-04-11 19:33:47 -07001531 case QPNP_ID_LED_MPP:
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05301532 if (led->mpp_cfg->pwm_mode == MANUAL_MODE)
1533 led->cdev.max_brightness = led->max_current;
1534 else
1535 led->cdev.max_brightness = MPP_MAX_LEVEL;
Amy Malochef3813742013-04-11 19:33:47 -07001536 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301537 case QPNP_ID_KPDBL:
1538 led->cdev.max_brightness = KPDBL_MAX_LEVEL;
1539 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07001540 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07001541 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -07001542 return -EINVAL;
1543 }
1544
1545 return 0;
1546}
1547
1548static enum led_brightness qpnp_led_get(struct led_classdev *led_cdev)
1549{
1550 struct qpnp_led_data *led;
1551
1552 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1553
1554 return led->cdev.brightness;
1555}
1556
Asaf Penso55ac8472013-01-21 21:17:37 +02001557static void qpnp_led_turn_off_delayed(struct work_struct *work)
1558{
1559 struct delayed_work *dwork = to_delayed_work(work);
1560 struct qpnp_led_data *led
1561 = container_of(dwork, struct qpnp_led_data, dwork);
1562
1563 led->cdev.brightness = LED_OFF;
1564 qpnp_led_set(&led->cdev, led->cdev.brightness);
1565}
1566
1567static void qpnp_led_turn_off(struct qpnp_led_data *led)
1568{
1569 INIT_DELAYED_WORK(&led->dwork, qpnp_led_turn_off_delayed);
1570 schedule_delayed_work(&led->dwork,
1571 msecs_to_jiffies(led->turn_off_delay_ms));
1572}
1573
Amy Malochef3d5a062012-08-16 19:14:11 -07001574static int __devinit qpnp_wled_init(struct qpnp_led_data *led)
1575{
1576 int rc, i;
1577 u8 num_wled_strings;
1578
1579 num_wled_strings = led->wled_cfg->num_strings;
1580
1581 /* verify ranges */
Abinaya Pe65d2cd2013-12-23 19:31:57 +05301582 if (led->wled_cfg->ovp_val > WLED_OVP_27V) {
Amy Malochef3d5a062012-08-16 19:14:11 -07001583 dev_err(&led->spmi_dev->dev, "Invalid ovp value\n");
1584 return -EINVAL;
1585 }
1586
1587 if (led->wled_cfg->boost_curr_lim > WLED_CURR_LIMIT_1680mA) {
1588 dev_err(&led->spmi_dev->dev, "Invalid boost current limit\n");
1589 return -EINVAL;
1590 }
1591
1592 if (led->wled_cfg->cp_select > WLED_CP_SELECT_MAX) {
1593 dev_err(&led->spmi_dev->dev, "Invalid pole capacitance\n");
1594 return -EINVAL;
1595 }
1596
1597 if ((led->max_current > WLED_MAX_CURR)) {
1598 dev_err(&led->spmi_dev->dev, "Invalid max current\n");
1599 return -EINVAL;
1600 }
1601
1602 if ((led->wled_cfg->ctrl_delay_us % WLED_CTL_DLY_STEP) ||
1603 (led->wled_cfg->ctrl_delay_us > WLED_CTL_DLY_MAX)) {
1604 dev_err(&led->spmi_dev->dev, "Invalid control delay\n");
1605 return -EINVAL;
1606 }
1607
1608 /* program over voltage protection threshold */
1609 rc = qpnp_led_masked_write(led, WLED_OVP_CFG_REG(led->base),
1610 WLED_OVP_VAL_MASK,
1611 (led->wled_cfg->ovp_val << WLED_OVP_VAL_BIT_SHFT));
1612 if (rc) {
1613 dev_err(&led->spmi_dev->dev,
1614 "WLED OVP reg write failed(%d)\n", rc);
1615 return rc;
1616 }
1617
1618 /* program current boost limit */
1619 rc = qpnp_led_masked_write(led, WLED_BOOST_LIMIT_REG(led->base),
1620 WLED_BOOST_LIMIT_MASK, led->wled_cfg->boost_curr_lim);
1621 if (rc) {
1622 dev_err(&led->spmi_dev->dev,
1623 "WLED boost limit reg write failed(%d)\n", rc);
1624 return rc;
1625 }
1626
1627 /* program output feedback */
1628 rc = qpnp_led_masked_write(led, WLED_FDBCK_CTRL_REG(led->base),
1629 WLED_OP_FDBCK_MASK,
1630 (led->wled_cfg->op_fdbck << WLED_OP_FDBCK_BIT_SHFT));
1631 if (rc) {
1632 dev_err(&led->spmi_dev->dev,
1633 "WLED fdbck ctrl reg write failed(%d)\n", rc);
1634 return rc;
1635 }
1636
1637 /* program switch frequency */
Amy Maloche9eccb4c2013-07-12 14:31:56 -07001638 rc = qpnp_led_masked_write(led,
1639 WLED_SWITCHING_FREQ_REG(led->base),
Amy Malochef3d5a062012-08-16 19:14:11 -07001640 WLED_SWITCH_FREQ_MASK, led->wled_cfg->switch_freq);
1641 if (rc) {
1642 dev_err(&led->spmi_dev->dev,
Amy Maloche9eccb4c2013-07-12 14:31:56 -07001643 "WLED switch freq reg write failed(%d)\n", rc);
Amy Malochef3d5a062012-08-16 19:14:11 -07001644 return rc;
1645 }
1646
1647 /* program current sink */
1648 if (led->wled_cfg->cs_out_en) {
1649 rc = qpnp_led_masked_write(led, WLED_CURR_SINK_REG(led->base),
1650 WLED_CURR_SINK_MASK,
Amy Maloche832d90a2013-01-07 10:15:29 -08001651 (((1 << led->wled_cfg->num_strings) - 1)
1652 << WLED_CURR_SINK_SHFT));
Amy Malochef3d5a062012-08-16 19:14:11 -07001653 if (rc) {
1654 dev_err(&led->spmi_dev->dev,
1655 "WLED curr sink reg write failed(%d)\n", rc);
1656 return rc;
1657 }
1658 }
1659
1660 /* program high pole capacitance */
1661 rc = qpnp_led_masked_write(led, WLED_HIGH_POLE_CAP_REG(led->base),
1662 WLED_CP_SELECT_MASK, led->wled_cfg->cp_select);
1663 if (rc) {
1664 dev_err(&led->spmi_dev->dev,
1665 "WLED pole cap reg write failed(%d)\n", rc);
1666 return rc;
1667 }
1668
1669 /* program modulator, current mod src and cabc */
1670 for (i = 0; i < num_wled_strings; i++) {
1671 rc = qpnp_led_masked_write(led, WLED_MOD_EN_REG(led->base, i),
1672 WLED_NO_MASK, WLED_EN_MASK);
1673 if (rc) {
1674 dev_err(&led->spmi_dev->dev,
1675 "WLED mod enable reg write failed(%d)\n", rc);
1676 return rc;
1677 }
1678
1679 if (led->wled_cfg->dig_mod_gen_en) {
1680 rc = qpnp_led_masked_write(led,
Amy Maloched44516e2013-02-14 17:36:34 -08001681 WLED_MOD_SRC_SEL_REG(led->base, i),
Amy Malochef3d5a062012-08-16 19:14:11 -07001682 WLED_NO_MASK, WLED_USE_EXT_GEN_MOD_SRC);
1683 if (rc) {
1684 dev_err(&led->spmi_dev->dev,
1685 "WLED dig mod en reg write failed(%d)\n", rc);
1686 }
1687 }
1688
1689 rc = qpnp_led_masked_write(led,
1690 WLED_FULL_SCALE_REG(led->base, i), WLED_MAX_CURR_MASK,
Abinaya P03cee1d2014-02-17 17:51:53 +05301691 (u8)led->max_current);
Amy Malochef3d5a062012-08-16 19:14:11 -07001692 if (rc) {
1693 dev_err(&led->spmi_dev->dev,
1694 "WLED max current reg write failed(%d)\n", rc);
1695 return rc;
1696 }
1697
1698 }
1699
Sarada Prasanna Garnayak40f65022013-11-01 20:17:32 +05301700 /* Reset WLED enable register */
1701 rc = qpnp_led_masked_write(led, WLED_MOD_CTRL_REG(led->base),
1702 WLED_8_BIT_MASK, WLED_BOOST_OFF);
1703 if (rc) {
1704 dev_err(&led->spmi_dev->dev,
1705 "WLED write ctrl reg failed(%d)\n", rc);
1706 return rc;
1707 }
1708
Amy Malochef3d5a062012-08-16 19:14:11 -07001709 /* dump wled registers */
Amy Malochea5ca5552012-10-23 13:34:46 -07001710 qpnp_dump_regs(led, wled_debug_regs, ARRAY_SIZE(wled_debug_regs));
Amy Malochef3d5a062012-08-16 19:14:11 -07001711
1712 return 0;
1713}
1714
Amy Malochebc97c0d22013-03-24 22:06:16 -07001715static ssize_t led_mode_store(struct device *dev,
1716 struct device_attribute *attr,
1717 const char *buf, size_t count)
1718{
1719 struct qpnp_led_data *led;
1720 unsigned long state;
1721 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1722 ssize_t ret = -EINVAL;
1723
1724 ret = kstrtoul(buf, 10, &state);
1725 if (ret)
1726 return ret;
1727
1728 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1729
1730 /* '1' to enable torch mode; '0' to switch to flash mode */
1731 if (state == 1)
1732 led->flash_cfg->torch_enable = true;
1733 else
1734 led->flash_cfg->torch_enable = false;
1735
1736 return count;
1737}
1738
1739static ssize_t led_strobe_type_store(struct device *dev,
1740 struct device_attribute *attr,
1741 const char *buf, size_t count)
1742{
1743 struct qpnp_led_data *led;
1744 unsigned long state;
1745 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1746 ssize_t ret = -EINVAL;
1747
1748 ret = kstrtoul(buf, 10, &state);
1749 if (ret)
1750 return ret;
1751
1752 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1753
1754 /* '0' for sw strobe; '1' for hw strobe */
1755 if (state == 1)
1756 led->flash_cfg->strobe_type = 1;
1757 else
1758 led->flash_cfg->strobe_type = 0;
1759
1760 return count;
1761}
1762
Amy Malochea5c4ed82013-06-05 11:05:28 -07001763static int qpnp_pwm_init(struct pwm_config_data *pwm_cfg,
1764 struct spmi_device *spmi_dev,
1765 const char *name)
1766{
1767 int rc, start_idx, idx_len;
1768
1769 if (pwm_cfg->pwm_channel != -1) {
1770 pwm_cfg->pwm_dev =
1771 pwm_request(pwm_cfg->pwm_channel, name);
1772
1773 if (IS_ERR_OR_NULL(pwm_cfg->pwm_dev)) {
1774 dev_err(&spmi_dev->dev,
1775 "could not acquire PWM Channel %d, " \
1776 "error %ld\n",
1777 pwm_cfg->pwm_channel,
1778 PTR_ERR(pwm_cfg->pwm_dev));
1779 pwm_cfg->pwm_dev = NULL;
1780 return -ENODEV;
1781 }
1782
1783 if (pwm_cfg->mode == LPG_MODE) {
1784 start_idx =
1785 pwm_cfg->duty_cycles->start_idx;
1786 idx_len =
1787 pwm_cfg->duty_cycles->num_duty_pcts;
1788
1789 if (idx_len >= PWM_LUT_MAX_SIZE &&
1790 start_idx) {
1791 dev_err(&spmi_dev->dev,
1792 "Wrong LUT size or index\n");
1793 return -EINVAL;
1794 }
1795 if ((start_idx + idx_len) >
1796 PWM_LUT_MAX_SIZE) {
1797 dev_err(&spmi_dev->dev,
1798 "Exceed LUT limit\n");
1799 return -EINVAL;
1800 }
1801 rc = pwm_lut_config(pwm_cfg->pwm_dev,
Xu Kai757c2b22014-05-08 12:51:24 +08001802 pwm_cfg->pwm_period_us,
Amy Malochea5c4ed82013-06-05 11:05:28 -07001803 pwm_cfg->duty_cycles->duty_pcts,
1804 pwm_cfg->lut_params);
1805 if (rc < 0) {
1806 dev_err(&spmi_dev->dev, "Failed to " \
1807 "configure pwm LUT\n");
1808 return rc;
1809 }
1810 }
1811 } else {
1812 dev_err(&spmi_dev->dev,
1813 "Invalid PWM channel\n");
1814 return -EINVAL;
1815 }
1816
1817 return 0;
1818}
1819
Amy Maloche4cf9f322013-05-29 15:53:46 -07001820static ssize_t pwm_us_store(struct device *dev,
1821 struct device_attribute *attr,
1822 const char *buf, size_t count)
1823{
1824 struct qpnp_led_data *led;
1825 u32 pwm_us;
1826 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1827 ssize_t ret;
1828 u32 previous_pwm_us;
1829 struct pwm_config_data *pwm_cfg;
1830
1831 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1832
1833 ret = kstrtou32(buf, 10, &pwm_us);
1834 if (ret)
1835 return ret;
1836
1837 switch (led->id) {
1838 case QPNP_ID_LED_MPP:
1839 pwm_cfg = led->mpp_cfg->pwm_cfg;
1840 break;
1841 case QPNP_ID_RGB_RED:
1842 case QPNP_ID_RGB_GREEN:
1843 case QPNP_ID_RGB_BLUE:
1844 pwm_cfg = led->rgb_cfg->pwm_cfg;
1845 break;
1846 default:
1847 dev_err(&led->spmi_dev->dev,
1848 "Invalid LED id type for pwm_us\n");
1849 return -EINVAL;
1850 }
1851
1852 if (pwm_cfg->mode == LPG_MODE)
1853 pwm_cfg->blinking = true;
1854
1855 previous_pwm_us = pwm_cfg->pwm_period_us;
1856
1857 pwm_cfg->pwm_period_us = pwm_us;
1858 pwm_free(pwm_cfg->pwm_dev);
1859 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1860 if (ret) {
1861 pwm_cfg->pwm_period_us = previous_pwm_us;
1862 pwm_free(pwm_cfg->pwm_dev);
1863 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1864 qpnp_led_set(&led->cdev, led->cdev.brightness);
1865 dev_err(&led->spmi_dev->dev,
1866 "Failed to initialize pwm with new pwm_us value\n");
1867 return ret;
1868 }
1869 qpnp_led_set(&led->cdev, led->cdev.brightness);
1870 return count;
1871}
1872
1873static ssize_t pause_lo_store(struct device *dev,
1874 struct device_attribute *attr,
1875 const char *buf, size_t count)
1876{
1877 struct qpnp_led_data *led;
1878 u32 pause_lo;
1879 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1880 ssize_t ret;
1881 u32 previous_pause_lo;
1882 struct pwm_config_data *pwm_cfg;
1883
1884 ret = kstrtou32(buf, 10, &pause_lo);
1885 if (ret)
1886 return ret;
1887 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1888
1889 switch (led->id) {
1890 case QPNP_ID_LED_MPP:
1891 pwm_cfg = led->mpp_cfg->pwm_cfg;
1892 break;
1893 case QPNP_ID_RGB_RED:
1894 case QPNP_ID_RGB_GREEN:
1895 case QPNP_ID_RGB_BLUE:
1896 pwm_cfg = led->rgb_cfg->pwm_cfg;
1897 break;
1898 default:
1899 dev_err(&led->spmi_dev->dev,
1900 "Invalid LED id type for pause lo\n");
1901 return -EINVAL;
1902 }
1903
1904 if (pwm_cfg->mode == LPG_MODE)
1905 pwm_cfg->blinking = true;
1906
1907 previous_pause_lo = pwm_cfg->lut_params.lut_pause_lo;
1908
1909 pwm_free(pwm_cfg->pwm_dev);
1910 pwm_cfg->lut_params.lut_pause_lo = pause_lo;
1911 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1912 if (ret) {
1913 pwm_cfg->lut_params.lut_pause_lo = previous_pause_lo;
1914 pwm_free(pwm_cfg->pwm_dev);
1915 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1916 qpnp_led_set(&led->cdev, led->cdev.brightness);
1917 dev_err(&led->spmi_dev->dev,
1918 "Failed to initialize pwm with new pause lo value\n");
1919 return ret;
1920 }
1921 qpnp_led_set(&led->cdev, led->cdev.brightness);
1922 return count;
1923}
1924
1925static ssize_t pause_hi_store(struct device *dev,
1926 struct device_attribute *attr,
1927 const char *buf, size_t count)
1928{
1929 struct qpnp_led_data *led;
1930 u32 pause_hi;
1931 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1932 ssize_t ret;
1933 u32 previous_pause_hi;
1934 struct pwm_config_data *pwm_cfg;
1935
1936 ret = kstrtou32(buf, 10, &pause_hi);
1937 if (ret)
1938 return ret;
1939 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1940
1941 switch (led->id) {
1942 case QPNP_ID_LED_MPP:
1943 pwm_cfg = led->mpp_cfg->pwm_cfg;
1944 break;
1945 case QPNP_ID_RGB_RED:
1946 case QPNP_ID_RGB_GREEN:
1947 case QPNP_ID_RGB_BLUE:
1948 pwm_cfg = led->rgb_cfg->pwm_cfg;
1949 break;
1950 default:
1951 dev_err(&led->spmi_dev->dev,
1952 "Invalid LED id type for pause hi\n");
1953 return -EINVAL;
1954 }
1955
1956 if (pwm_cfg->mode == LPG_MODE)
1957 pwm_cfg->blinking = true;
1958
1959 previous_pause_hi = pwm_cfg->lut_params.lut_pause_hi;
1960
1961 pwm_free(pwm_cfg->pwm_dev);
1962 pwm_cfg->lut_params.lut_pause_hi = pause_hi;
1963 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1964 if (ret) {
1965 pwm_cfg->lut_params.lut_pause_hi = previous_pause_hi;
1966 pwm_free(pwm_cfg->pwm_dev);
1967 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1968 qpnp_led_set(&led->cdev, led->cdev.brightness);
1969 dev_err(&led->spmi_dev->dev,
1970 "Failed to initialize pwm with new pause hi value\n");
1971 return ret;
1972 }
1973 qpnp_led_set(&led->cdev, led->cdev.brightness);
1974 return count;
1975}
1976
1977static ssize_t start_idx_store(struct device *dev,
1978 struct device_attribute *attr,
1979 const char *buf, size_t count)
1980{
1981 struct qpnp_led_data *led;
1982 u32 start_idx;
1983 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1984 ssize_t ret;
1985 u32 previous_start_idx;
1986 struct pwm_config_data *pwm_cfg;
1987
1988 ret = kstrtou32(buf, 10, &start_idx);
1989 if (ret)
1990 return ret;
1991 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1992
1993 switch (led->id) {
1994 case QPNP_ID_LED_MPP:
1995 pwm_cfg = led->mpp_cfg->pwm_cfg;
1996 break;
1997 case QPNP_ID_RGB_RED:
1998 case QPNP_ID_RGB_GREEN:
1999 case QPNP_ID_RGB_BLUE:
2000 pwm_cfg = led->rgb_cfg->pwm_cfg;
2001 break;
2002 default:
2003 dev_err(&led->spmi_dev->dev,
2004 "Invalid LED id type for start idx\n");
2005 return -EINVAL;
2006 }
2007
2008 if (pwm_cfg->mode == LPG_MODE)
2009 pwm_cfg->blinking = true;
2010
2011 previous_start_idx = pwm_cfg->duty_cycles->start_idx;
2012 pwm_cfg->duty_cycles->start_idx = start_idx;
2013 pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
2014 pwm_free(pwm_cfg->pwm_dev);
2015 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2016 if (ret) {
2017 pwm_cfg->duty_cycles->start_idx = previous_start_idx;
2018 pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
2019 pwm_free(pwm_cfg->pwm_dev);
2020 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2021 qpnp_led_set(&led->cdev, led->cdev.brightness);
2022 dev_err(&led->spmi_dev->dev,
2023 "Failed to initialize pwm with new start idx value\n");
2024 return ret;
2025 }
2026 qpnp_led_set(&led->cdev, led->cdev.brightness);
2027 return count;
2028}
2029
2030static ssize_t ramp_step_ms_store(struct device *dev,
2031 struct device_attribute *attr,
2032 const char *buf, size_t count)
2033{
2034 struct qpnp_led_data *led;
2035 u32 ramp_step_ms;
2036 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2037 ssize_t ret;
2038 u32 previous_ramp_step_ms;
2039 struct pwm_config_data *pwm_cfg;
2040
2041 ret = kstrtou32(buf, 10, &ramp_step_ms);
2042 if (ret)
2043 return ret;
2044 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2045
2046 switch (led->id) {
2047 case QPNP_ID_LED_MPP:
2048 pwm_cfg = led->mpp_cfg->pwm_cfg;
2049 break;
2050 case QPNP_ID_RGB_RED:
2051 case QPNP_ID_RGB_GREEN:
2052 case QPNP_ID_RGB_BLUE:
2053 pwm_cfg = led->rgb_cfg->pwm_cfg;
2054 break;
2055 default:
2056 dev_err(&led->spmi_dev->dev,
2057 "Invalid LED id type for ramp step\n");
2058 return -EINVAL;
2059 }
2060
2061 if (pwm_cfg->mode == LPG_MODE)
2062 pwm_cfg->blinking = true;
2063
2064 previous_ramp_step_ms = pwm_cfg->lut_params.ramp_step_ms;
2065
2066 pwm_free(pwm_cfg->pwm_dev);
2067 pwm_cfg->lut_params.ramp_step_ms = ramp_step_ms;
2068 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2069 if (ret) {
2070 pwm_cfg->lut_params.ramp_step_ms = previous_ramp_step_ms;
2071 pwm_free(pwm_cfg->pwm_dev);
2072 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2073 qpnp_led_set(&led->cdev, led->cdev.brightness);
2074 dev_err(&led->spmi_dev->dev,
2075 "Failed to initialize pwm with new ramp step value\n");
2076 return ret;
2077 }
2078 qpnp_led_set(&led->cdev, led->cdev.brightness);
2079 return count;
2080}
2081
2082static ssize_t lut_flags_store(struct device *dev,
2083 struct device_attribute *attr,
2084 const char *buf, size_t count)
2085{
2086 struct qpnp_led_data *led;
2087 u32 lut_flags;
2088 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2089 ssize_t ret;
2090 u32 previous_lut_flags;
2091 struct pwm_config_data *pwm_cfg;
2092
2093 ret = kstrtou32(buf, 10, &lut_flags);
2094 if (ret)
2095 return ret;
2096 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2097
2098 switch (led->id) {
2099 case QPNP_ID_LED_MPP:
2100 pwm_cfg = led->mpp_cfg->pwm_cfg;
2101 break;
2102 case QPNP_ID_RGB_RED:
2103 case QPNP_ID_RGB_GREEN:
2104 case QPNP_ID_RGB_BLUE:
2105 pwm_cfg = led->rgb_cfg->pwm_cfg;
2106 break;
2107 default:
2108 dev_err(&led->spmi_dev->dev,
2109 "Invalid LED id type for lut flags\n");
2110 return -EINVAL;
2111 }
2112
2113 if (pwm_cfg->mode == LPG_MODE)
2114 pwm_cfg->blinking = true;
2115
2116 previous_lut_flags = pwm_cfg->lut_params.flags;
2117
2118 pwm_free(pwm_cfg->pwm_dev);
2119 pwm_cfg->lut_params.flags = lut_flags;
2120 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2121 if (ret) {
2122 pwm_cfg->lut_params.flags = previous_lut_flags;
2123 pwm_free(pwm_cfg->pwm_dev);
2124 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2125 qpnp_led_set(&led->cdev, led->cdev.brightness);
2126 dev_err(&led->spmi_dev->dev,
2127 "Failed to initialize pwm with new lut flags value\n");
2128 return ret;
2129 }
2130 qpnp_led_set(&led->cdev, led->cdev.brightness);
2131 return count;
2132}
2133
2134static ssize_t duty_pcts_store(struct device *dev,
2135 struct device_attribute *attr,
2136 const char *buf, size_t count)
2137{
2138 struct qpnp_led_data *led;
2139 int num_duty_pcts = 0;
2140 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2141 char *buffer;
2142 ssize_t ret;
2143 int i = 0;
2144 int max_duty_pcts;
2145 struct pwm_config_data *pwm_cfg;
2146 u32 previous_num_duty_pcts;
2147 int value;
2148 int *previous_duty_pcts;
2149
2150 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2151
2152 switch (led->id) {
2153 case QPNP_ID_LED_MPP:
2154 pwm_cfg = led->mpp_cfg->pwm_cfg;
2155 max_duty_pcts = PWM_LUT_MAX_SIZE;
2156 break;
2157 case QPNP_ID_RGB_RED:
2158 case QPNP_ID_RGB_GREEN:
2159 case QPNP_ID_RGB_BLUE:
2160 pwm_cfg = led->rgb_cfg->pwm_cfg;
2161 max_duty_pcts = PWM_LUT_MAX_SIZE;
2162 break;
2163 default:
2164 dev_err(&led->spmi_dev->dev,
2165 "Invalid LED id type for duty pcts\n");
2166 return -EINVAL;
2167 }
2168
2169 if (pwm_cfg->mode == LPG_MODE)
2170 pwm_cfg->blinking = true;
2171
2172 buffer = (char *)buf;
2173
2174 for (i = 0; i < max_duty_pcts; i++) {
2175 if (buffer == NULL)
2176 break;
2177 ret = sscanf((const char *)buffer, "%u,%s", &value, buffer);
2178 pwm_cfg->old_duty_pcts[i] = value;
2179 num_duty_pcts++;
2180 if (ret <= 1)
2181 break;
2182 }
2183
2184 if (num_duty_pcts >= max_duty_pcts) {
2185 dev_err(&led->spmi_dev->dev,
2186 "Number of duty pcts given exceeds max (%d)\n",
2187 max_duty_pcts);
2188 return -EINVAL;
2189 }
2190
2191 previous_num_duty_pcts = pwm_cfg->duty_cycles->num_duty_pcts;
2192 previous_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
2193
2194 pwm_cfg->duty_cycles->num_duty_pcts = num_duty_pcts;
2195 pwm_cfg->duty_cycles->duty_pcts = pwm_cfg->old_duty_pcts;
2196 pwm_cfg->old_duty_pcts = previous_duty_pcts;
2197 pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
2198
2199 pwm_free(pwm_cfg->pwm_dev);
2200 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2201 if (ret)
2202 goto restore;
2203
2204 qpnp_led_set(&led->cdev, led->cdev.brightness);
2205 return count;
2206
2207restore:
2208 dev_err(&led->spmi_dev->dev,
2209 "Failed to initialize pwm with new duty pcts value\n");
2210 pwm_cfg->duty_cycles->num_duty_pcts = previous_num_duty_pcts;
2211 pwm_cfg->old_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
2212 pwm_cfg->duty_cycles->duty_pcts = previous_duty_pcts;
2213 pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
2214 pwm_free(pwm_cfg->pwm_dev);
2215 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2216 qpnp_led_set(&led->cdev, led->cdev.brightness);
2217 return ret;
2218}
2219
Amy Malochea5c4ed82013-06-05 11:05:28 -07002220static void led_blink(struct qpnp_led_data *led,
2221 struct pwm_config_data *pwm_cfg)
2222{
Abinaya P005e67e2014-07-18 13:34:45 +05302223 int rc;
2224
Mao Li1ff13762014-07-30 10:00:34 +08002225 flush_work(&led->work);
Abinaya P005e67e2014-07-18 13:34:45 +05302226 mutex_lock(&led->lock);
Amy Malochea5c4ed82013-06-05 11:05:28 -07002227 if (pwm_cfg->use_blink) {
2228 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07002229 pwm_cfg->blinking = true;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002230 if (led->id == QPNP_ID_LED_MPP)
2231 led->mpp_cfg->pwm_mode = LPG_MODE;
2232 pwm_cfg->mode = LPG_MODE;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002233 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07002234 pwm_cfg->blinking = false;
2235 pwm_cfg->mode = pwm_cfg->default_mode;
2236 if (led->id == QPNP_ID_LED_MPP)
2237 led->mpp_cfg->pwm_mode = pwm_cfg->default_mode;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002238 }
Amy Maloche4cf9f322013-05-29 15:53:46 -07002239 pwm_free(pwm_cfg->pwm_dev);
2240 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
Abinaya P005e67e2014-07-18 13:34:45 +05302241 if (led->id == QPNP_ID_RGB_RED || led->id == QPNP_ID_RGB_GREEN
2242 || led->id == QPNP_ID_RGB_BLUE) {
2243 rc = qpnp_rgb_set(led);
2244 if (rc < 0)
2245 dev_err(&led->spmi_dev->dev,
2246 "RGB set brightness failed (%d)\n", rc);
2247 } else if (led->id == QPNP_ID_LED_MPP) {
2248 rc = qpnp_mpp_set(led);
2249 if (rc < 0)
2250 dev_err(&led->spmi_dev->dev,
2251 "MPP set brightness failed (%d)\n", rc);
2252 }
Amy Malochea5c4ed82013-06-05 11:05:28 -07002253 }
Abinaya P005e67e2014-07-18 13:34:45 +05302254 mutex_unlock(&led->lock);
Amy Malochea5c4ed82013-06-05 11:05:28 -07002255}
2256
2257static ssize_t blink_store(struct device *dev,
2258 struct device_attribute *attr,
2259 const char *buf, size_t count)
2260{
2261 struct qpnp_led_data *led;
2262 unsigned long blinking;
2263 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2264 ssize_t ret = -EINVAL;
2265
2266 ret = kstrtoul(buf, 10, &blinking);
2267 if (ret)
2268 return ret;
2269 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2270 led->cdev.brightness = blinking ? led->cdev.max_brightness : 0;
2271
2272 switch (led->id) {
2273 case QPNP_ID_LED_MPP:
2274 led_blink(led, led->mpp_cfg->pwm_cfg);
2275 break;
2276 case QPNP_ID_RGB_RED:
2277 case QPNP_ID_RGB_GREEN:
2278 case QPNP_ID_RGB_BLUE:
2279 led_blink(led, led->rgb_cfg->pwm_cfg);
2280 break;
2281 default:
2282 dev_err(&led->spmi_dev->dev, "Invalid LED id type for blink\n");
2283 return -EINVAL;
2284 }
2285 return count;
2286}
2287
Amy Malochebc97c0d22013-03-24 22:06:16 -07002288static DEVICE_ATTR(led_mode, 0664, NULL, led_mode_store);
2289static DEVICE_ATTR(strobe, 0664, NULL, led_strobe_type_store);
Amy Maloche4cf9f322013-05-29 15:53:46 -07002290static DEVICE_ATTR(pwm_us, 0664, NULL, pwm_us_store);
2291static DEVICE_ATTR(pause_lo, 0664, NULL, pause_lo_store);
2292static DEVICE_ATTR(pause_hi, 0664, NULL, pause_hi_store);
2293static DEVICE_ATTR(start_idx, 0664, NULL, start_idx_store);
2294static DEVICE_ATTR(ramp_step_ms, 0664, NULL, ramp_step_ms_store);
2295static DEVICE_ATTR(lut_flags, 0664, NULL, lut_flags_store);
2296static DEVICE_ATTR(duty_pcts, 0664, NULL, duty_pcts_store);
Amy Malochea5c4ed82013-06-05 11:05:28 -07002297static DEVICE_ATTR(blink, 0664, NULL, blink_store);
Amy Malochebc97c0d22013-03-24 22:06:16 -07002298
2299static struct attribute *led_attrs[] = {
2300 &dev_attr_led_mode.attr,
2301 &dev_attr_strobe.attr,
Amy Maloche12ad6f32013-04-02 14:39:24 -07002302 NULL
Amy Malochebc97c0d22013-03-24 22:06:16 -07002303};
2304
2305static const struct attribute_group led_attr_group = {
2306 .attrs = led_attrs,
2307};
2308
Amy Maloche4cf9f322013-05-29 15:53:46 -07002309static struct attribute *pwm_attrs[] = {
2310 &dev_attr_pwm_us.attr,
2311 NULL
2312};
2313
2314static struct attribute *lpg_attrs[] = {
2315 &dev_attr_pause_lo.attr,
2316 &dev_attr_pause_hi.attr,
2317 &dev_attr_start_idx.attr,
2318 &dev_attr_ramp_step_ms.attr,
2319 &dev_attr_lut_flags.attr,
2320 &dev_attr_duty_pcts.attr,
2321 NULL
2322};
2323
Amy Malochea5c4ed82013-06-05 11:05:28 -07002324static struct attribute *blink_attrs[] = {
2325 &dev_attr_blink.attr,
2326 NULL
2327};
2328
Amy Maloche4cf9f322013-05-29 15:53:46 -07002329static const struct attribute_group pwm_attr_group = {
2330 .attrs = pwm_attrs,
2331};
2332
2333static const struct attribute_group lpg_attr_group = {
2334 .attrs = lpg_attrs,
2335};
2336
Amy Malochea5c4ed82013-06-05 11:05:28 -07002337static const struct attribute_group blink_attr_group = {
2338 .attrs = blink_attrs,
2339};
2340
Amy Maloche864a6d52012-10-03 15:58:12 -07002341static int __devinit qpnp_flash_init(struct qpnp_led_data *led)
2342{
2343 int rc;
2344
Chun Zhangc3b505b2013-06-03 19:01:49 -07002345 led->flash_cfg->flash_on = false;
2346
Amy Maloche864a6d52012-10-03 15:58:12 -07002347 rc = qpnp_led_masked_write(led,
2348 FLASH_LED_STROBE_CTRL(led->base),
2349 FLASH_STROBE_MASK, FLASH_DISABLE_ALL);
2350 if (rc) {
2351 dev_err(&led->spmi_dev->dev,
2352 "LED %d flash write failed(%d)\n", led->id, rc);
2353 return rc;
2354 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002355
Chun Zhangdf2d3062013-06-25 20:14:46 -07002356 /* Disable flash LED module */
2357 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07002358 FLASH_ENABLE_MASK, FLASH_DISABLE_ALL);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002359 if (rc) {
2360 dev_err(&led->spmi_dev->dev,
2361 "Enable reg write failed(%d)\n", rc);
2362 return rc;
2363 }
2364
2365 if (led->flash_cfg->torch_enable)
2366 return 0;
2367
Amy Maloche864a6d52012-10-03 15:58:12 -07002368 /* Set headroom */
2369 rc = qpnp_led_masked_write(led, FLASH_HEADROOM(led->base),
2370 FLASH_HEADROOM_MASK, led->flash_cfg->headroom);
2371 if (rc) {
2372 dev_err(&led->spmi_dev->dev,
2373 "Headroom reg write failed(%d)\n", rc);
2374 return rc;
2375 }
2376
Chun Zhange8954cf2013-05-02 11:14:34 -07002377 /* Set startup delay */
2378 rc = qpnp_led_masked_write(led,
2379 FLASH_STARTUP_DELAY(led->base), FLASH_STARTUP_DLY_MASK,
2380 led->flash_cfg->startup_dly);
2381 if (rc) {
2382 dev_err(&led->spmi_dev->dev,
2383 "Startup delay reg write failed(%d)\n", rc);
2384 return rc;
2385 }
2386
2387 /* Set timer control - safety or watchdog */
2388 if (led->flash_cfg->safety_timer) {
2389 rc = qpnp_led_masked_write(led,
2390 FLASH_LED_TMR_CTRL(led->base),
2391 FLASH_TMR_MASK, FLASH_TMR_SAFETY);
2392 if (rc) {
2393 dev_err(&led->spmi_dev->dev,
2394 "LED timer ctrl reg write failed(%d)\n",
2395 rc);
2396 return rc;
2397 }
2398 }
2399
2400 /* Set Vreg force */
2401 rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
2402 FLASH_VREG_MASK, FLASH_HW_VREG_OK);
2403 if (rc) {
2404 dev_err(&led->spmi_dev->dev,
2405 "Vreg OK reg write failed(%d)\n", rc);
2406 return rc;
2407 }
2408
2409 /* Set self fault check */
2410 rc = qpnp_led_masked_write(led, FLASH_FAULT_DETECT(led->base),
2411 FLASH_FAULT_DETECT_MASK, FLASH_SELFCHECK_ENABLE);
2412 if (rc) {
2413 dev_err(&led->spmi_dev->dev,
2414 "Fault detect reg write failed(%d)\n", rc);
2415 return rc;
2416 }
2417
Amy Maloche864a6d52012-10-03 15:58:12 -07002418 /* Set mask enable */
2419 rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
2420 FLASH_MASK_REG_MASK, FLASH_MASK_1);
2421 if (rc) {
2422 dev_err(&led->spmi_dev->dev,
2423 "Mask enable reg write failed(%d)\n", rc);
2424 return rc;
2425 }
2426
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07002427 /* Set current ramp */
2428 rc = qpnp_led_masked_write(led, FLASH_CURRENT_RAMP(led->base),
2429 FLASH_CURRENT_RAMP_MASK, FLASH_RAMP_STEP_27US);
2430 if (rc) {
2431 dev_err(&led->spmi_dev->dev,
2432 "Current ramp reg write failed(%d)\n", rc);
2433 return rc;
2434 }
2435
Amy Malochebc97c0d22013-03-24 22:06:16 -07002436 led->flash_cfg->strobe_type = 0;
2437
Amy Maloche864a6d52012-10-03 15:58:12 -07002438 /* dump flash registers */
2439 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
2440
2441 return 0;
2442}
2443
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302444static int __devinit qpnp_kpdbl_init(struct qpnp_led_data *led)
2445{
2446 int rc;
2447 u8 val;
2448
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302449 /* select row source - vbst or vph */
2450 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2451 KPDBL_ROW_SRC_SEL(led->base), &val, 1);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302452 if (rc) {
2453 dev_err(&led->spmi_dev->dev,
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302454 "Unable to read from addr=%x, rc(%d)\n",
2455 KPDBL_ROW_SRC_SEL(led->base), rc);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302456 return rc;
2457 }
2458
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302459 if (led->kpdbl_cfg->row_src_vbst)
2460 val |= 1 << led->kpdbl_cfg->row_id;
2461 else
2462 val &= ~(1 << led->kpdbl_cfg->row_id);
2463
2464 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
2465 KPDBL_ROW_SRC_SEL(led->base), &val, 1);
2466 if (rc) {
2467 dev_err(&led->spmi_dev->dev,
2468 "Unable to read from addr=%x, rc(%d)\n",
2469 KPDBL_ROW_SRC_SEL(led->base), rc);
2470 return rc;
2471 }
2472
2473 /* row source enable */
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302474 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2475 KPDBL_ROW_SRC(led->base), &val, 1);
2476 if (rc) {
2477 dev_err(&led->spmi_dev->dev,
2478 "Unable to read from addr=%x, rc(%d)\n",
2479 KPDBL_ROW_SRC(led->base), rc);
2480 return rc;
2481 }
2482
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302483 if (led->kpdbl_cfg->row_src_en)
2484 val |= KPDBL_ROW_SCAN_EN_MASK | (1 << led->kpdbl_cfg->row_id);
2485 else
2486 val &= ~(1 << led->kpdbl_cfg->row_id);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302487
2488 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
2489 KPDBL_ROW_SRC(led->base), &val, 1);
2490 if (rc) {
2491 dev_err(&led->spmi_dev->dev,
2492 "Unable to write to addr=%x, rc(%d)\n",
2493 KPDBL_ROW_SRC(led->base), rc);
2494 return rc;
2495 }
2496
2497 /* enable module */
2498 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
2499 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
2500 if (rc) {
2501 dev_err(&led->spmi_dev->dev,
2502 "Enable module write failed(%d)\n", rc);
2503 return rc;
2504 }
2505
Amy Malochea2726f02013-05-10 10:19:03 -07002506 rc = qpnp_pwm_init(led->kpdbl_cfg->pwm_cfg, led->spmi_dev,
2507 led->cdev.name);
2508 if (rc) {
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302509 dev_err(&led->spmi_dev->dev,
Amy Malochea2726f02013-05-10 10:19:03 -07002510 "Failed to initialize pwm\n");
2511 return rc;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302512 }
2513
2514 /* dump kpdbl registers */
2515 qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
2516
2517 return 0;
2518}
2519
Amy Malocheeea7b592012-10-03 15:59:36 -07002520static int __devinit qpnp_rgb_init(struct qpnp_led_data *led)
2521{
Amy Malochea2726f02013-05-10 10:19:03 -07002522 int rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07002523
2524 rc = qpnp_led_masked_write(led, RGB_LED_SRC_SEL(led->base),
2525 RGB_LED_SRC_MASK, RGB_LED_SOURCE_VPH_PWR);
2526 if (rc) {
2527 dev_err(&led->spmi_dev->dev,
2528 "Failed to write led source select register\n");
2529 return rc;
2530 }
2531
Amy Malochea2726f02013-05-10 10:19:03 -07002532 rc = qpnp_pwm_init(led->rgb_cfg->pwm_cfg, led->spmi_dev,
2533 led->cdev.name);
2534 if (rc) {
Amy Malocheeea7b592012-10-03 15:59:36 -07002535 dev_err(&led->spmi_dev->dev,
Amy Malochea2726f02013-05-10 10:19:03 -07002536 "Failed to initialize pwm\n");
2537 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07002538 }
Amy Malocheeea7b592012-10-03 15:59:36 -07002539 /* Initialize led for use in auto trickle charging mode */
2540 rc = qpnp_led_masked_write(led, RGB_LED_ATC_CTL(led->base),
2541 led->rgb_cfg->enable, led->rgb_cfg->enable);
2542
2543 return 0;
2544}
2545
Amy Malochea2726f02013-05-10 10:19:03 -07002546static int __devinit qpnp_mpp_init(struct qpnp_led_data *led)
2547{
Abinaya P03cee1d2014-02-17 17:51:53 +05302548 int rc;
2549 u8 val;
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05302550
2551 if (led->max_current < LED_MPP_CURRENT_MIN ||
2552 led->max_current > LED_MPP_CURRENT_MAX) {
2553 dev_err(&led->spmi_dev->dev,
2554 "max current for mpp is not valid\n");
2555 return -EINVAL;
2556 }
2557
Amy Malochea2726f02013-05-10 10:19:03 -07002558 val = (led->mpp_cfg->current_setting / LED_MPP_CURRENT_PER_SETTING) - 1;
2559
2560 if (val < 0)
2561 val = 0;
2562
Chun Zhang874c9ab2013-07-08 17:18:34 -07002563 rc = qpnp_led_masked_write(led, LED_MPP_VIN_CTRL(led->base),
2564 LED_MPP_VIN_MASK, led->mpp_cfg->vin_ctrl);
2565 if (rc) {
2566 dev_err(&led->spmi_dev->dev,
2567 "Failed to write led vin control reg\n");
2568 return rc;
2569 }
2570
Amy Malochea2726f02013-05-10 10:19:03 -07002571 rc = qpnp_led_masked_write(led, LED_MPP_SINK_CTRL(led->base),
2572 LED_MPP_SINK_MASK, val);
2573 if (rc) {
2574 dev_err(&led->spmi_dev->dev,
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05302575 "Failed to write sink control reg\n");
Amy Malochea2726f02013-05-10 10:19:03 -07002576 return rc;
2577 }
2578
2579 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
2580 rc = qpnp_pwm_init(led->mpp_cfg->pwm_cfg, led->spmi_dev,
2581 led->cdev.name);
2582 if (rc) {
2583 dev_err(&led->spmi_dev->dev,
2584 "Failed to initialize pwm\n");
2585 return rc;
2586 }
2587 }
2588
2589 return 0;
2590}
2591
Amy Malochef3d5a062012-08-16 19:14:11 -07002592static int __devinit qpnp_led_initialize(struct qpnp_led_data *led)
2593{
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302594 int rc = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07002595
2596 switch (led->id) {
2597 case QPNP_ID_WLED:
2598 rc = qpnp_wled_init(led);
2599 if (rc)
Amy Malochea5ca5552012-10-23 13:34:46 -07002600 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -07002601 "WLED initialize failed(%d)\n", rc);
2602 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07002603 case QPNP_ID_FLASH1_LED0:
2604 case QPNP_ID_FLASH1_LED1:
2605 rc = qpnp_flash_init(led);
2606 if (rc)
2607 dev_err(&led->spmi_dev->dev,
2608 "FLASH initialize failed(%d)\n", rc);
2609 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07002610 case QPNP_ID_RGB_RED:
2611 case QPNP_ID_RGB_GREEN:
2612 case QPNP_ID_RGB_BLUE:
2613 rc = qpnp_rgb_init(led);
2614 if (rc)
2615 dev_err(&led->spmi_dev->dev,
2616 "RGB initialize failed(%d)\n", rc);
2617 break;
Amy Malochef3813742013-04-11 19:33:47 -07002618 case QPNP_ID_LED_MPP:
Amy Malochea2726f02013-05-10 10:19:03 -07002619 rc = qpnp_mpp_init(led);
2620 if (rc)
2621 dev_err(&led->spmi_dev->dev,
2622 "MPP initialize failed(%d)\n", rc);
Amy Malochef3813742013-04-11 19:33:47 -07002623 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302624 case QPNP_ID_KPDBL:
2625 rc = qpnp_kpdbl_init(led);
2626 if (rc)
2627 dev_err(&led->spmi_dev->dev,
2628 "KPDBL initialize failed(%d)\n", rc);
2629 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07002630 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07002631 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malocheeea7b592012-10-03 15:59:36 -07002632 return -EINVAL;
Amy Malochef3d5a062012-08-16 19:14:11 -07002633 }
2634
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302635 return rc;
Amy Malochef3d5a062012-08-16 19:14:11 -07002636}
2637
Amy Malochea5ca5552012-10-23 13:34:46 -07002638static int __devinit qpnp_get_common_configs(struct qpnp_led_data *led,
2639 struct device_node *node)
2640{
2641 int rc;
Asaf Penso55ac8472013-01-21 21:17:37 +02002642 u32 val;
Amy Malochea5ca5552012-10-23 13:34:46 -07002643 const char *temp_string;
2644
2645 led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
2646 rc = of_property_read_string(node, "linux,default-trigger",
2647 &temp_string);
2648 if (!rc)
2649 led->cdev.default_trigger = temp_string;
2650 else if (rc != -EINVAL)
2651 return rc;
2652
2653 led->default_on = false;
2654 rc = of_property_read_string(node, "qcom,default-state",
2655 &temp_string);
2656 if (!rc) {
2657 if (strncmp(temp_string, "on", sizeof("on")) == 0)
2658 led->default_on = true;
2659 } else if (rc != -EINVAL)
2660 return rc;
2661
Asaf Penso55ac8472013-01-21 21:17:37 +02002662 led->turn_off_delay_ms = 0;
2663 rc = of_property_read_u32(node, "qcom,turn-off-delay-ms", &val);
2664 if (!rc)
2665 led->turn_off_delay_ms = val;
2666 else if (rc != -EINVAL)
2667 return rc;
2668
Amy Malochea5ca5552012-10-23 13:34:46 -07002669 return 0;
2670}
2671
Amy Malochef3d5a062012-08-16 19:14:11 -07002672/*
2673 * Handlers for alternative sources of platform_data
2674 */
2675static int __devinit qpnp_get_config_wled(struct qpnp_led_data *led,
2676 struct device_node *node)
2677{
2678 u32 val;
2679 int rc;
Amy Malochef3d5a062012-08-16 19:14:11 -07002680
2681 led->wled_cfg = devm_kzalloc(&led->spmi_dev->dev,
2682 sizeof(struct wled_config_data), GFP_KERNEL);
2683 if (!led->wled_cfg) {
2684 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2685 return -ENOMEM;
2686 }
2687
Amy Maloche0150b5e2013-08-15 18:18:32 -07002688 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2689 PMIC_VERSION_REG, &led->wled_cfg->pmic_version, 1);
2690 if (rc) {
2691 dev_err(&led->spmi_dev->dev,
2692 "Unable to read pmic ver, rc(%d)\n", rc);
2693 }
2694
Amy Malochef3d5a062012-08-16 19:14:11 -07002695 led->wled_cfg->num_strings = WLED_DEFAULT_STRINGS;
2696 rc = of_property_read_u32(node, "qcom,num-strings", &val);
2697 if (!rc)
2698 led->wled_cfg->num_strings = (u8) val;
2699 else if (rc != -EINVAL)
2700 return rc;
2701
2702 led->wled_cfg->ovp_val = WLED_DEFAULT_OVP_VAL;
2703 rc = of_property_read_u32(node, "qcom,ovp-val", &val);
2704 if (!rc)
2705 led->wled_cfg->ovp_val = (u8) val;
2706 else if (rc != -EINVAL)
2707 return rc;
2708
2709 led->wled_cfg->boost_curr_lim = WLED_BOOST_LIM_DEFAULT;
2710 rc = of_property_read_u32(node, "qcom,boost-curr-lim", &val);
2711 if (!rc)
2712 led->wled_cfg->boost_curr_lim = (u8) val;
2713 else if (rc != -EINVAL)
2714 return rc;
2715
2716 led->wled_cfg->cp_select = WLED_CP_SEL_DEFAULT;
2717 rc = of_property_read_u32(node, "qcom,cp-sel", &val);
2718 if (!rc)
2719 led->wled_cfg->cp_select = (u8) val;
2720 else if (rc != -EINVAL)
2721 return rc;
2722
2723 led->wled_cfg->ctrl_delay_us = WLED_CTRL_DLY_DEFAULT;
2724 rc = of_property_read_u32(node, "qcom,ctrl-delay-us", &val);
2725 if (!rc)
2726 led->wled_cfg->ctrl_delay_us = (u8) val;
2727 else if (rc != -EINVAL)
2728 return rc;
2729
Amy Malochebd687672013-03-18 11:23:45 -07002730 led->wled_cfg->op_fdbck = WLED_OP_FDBCK_DEFAULT;
2731 rc = of_property_read_u32(node, "qcom,op-fdbck", &val);
2732 if (!rc)
2733 led->wled_cfg->op_fdbck = (u8) val;
2734 else if (rc != -EINVAL)
2735 return rc;
2736
Amy Malochef3d5a062012-08-16 19:14:11 -07002737 led->wled_cfg->switch_freq = WLED_SWITCH_FREQ_DEFAULT;
2738 rc = of_property_read_u32(node, "qcom,switch-freq", &val);
2739 if (!rc)
2740 led->wled_cfg->switch_freq = (u8) val;
2741 else if (rc != -EINVAL)
2742 return rc;
2743
2744 led->wled_cfg->dig_mod_gen_en =
2745 of_property_read_bool(node, "qcom,dig-mod-gen-en");
2746
2747 led->wled_cfg->cs_out_en =
2748 of_property_read_bool(node, "qcom,cs-out-en");
2749
Amy Malochef3d5a062012-08-16 19:14:11 -07002750 return 0;
2751}
2752
Amy Maloche864a6d52012-10-03 15:58:12 -07002753static int __devinit qpnp_get_config_flash(struct qpnp_led_data *led,
Chun Zhangc3b505b2013-06-03 19:01:49 -07002754 struct device_node *node, bool *reg_set)
Amy Maloche864a6d52012-10-03 15:58:12 -07002755{
2756 int rc;
2757 u32 val;
2758
2759 led->flash_cfg = devm_kzalloc(&led->spmi_dev->dev,
2760 sizeof(struct flash_config_data), GFP_KERNEL);
2761 if (!led->flash_cfg) {
2762 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2763 return -ENOMEM;
2764 }
2765
Chun Zhang0d6ca072013-07-30 21:08:39 -07002766 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2767 FLASH_PERIPHERAL_SUBTYPE(led->base),
2768 &led->flash_cfg->peripheral_subtype, 1);
2769 if (rc) {
2770 dev_err(&led->spmi_dev->dev,
2771 "Unable to read from addr=%x, rc(%d)\n",
2772 FLASH_PERIPHERAL_SUBTYPE(led->base), rc);
2773 }
2774
Chun Zhang9d5ff672013-08-01 18:18:26 -07002775 led->flash_cfg->torch_enable =
2776 of_property_read_bool(node, "qcom,torch-enable");
2777
Chunmei Caif094d072014-08-06 13:58:50 +08002778 if (of_find_property(of_get_parent(node), "flash-wa-supply",
2779 NULL) && (!*reg_set)) {
2780 led->flash_cfg->flash_wa_reg =
Abinaya Pd0c91352014-09-08 14:32:04 +05302781 devm_regulator_get(&led->spmi_dev->dev, "flash-wa");
Chunmei Caif094d072014-08-06 13:58:50 +08002782 if (IS_ERR_OR_NULL(led->flash_cfg->flash_wa_reg)) {
2783 rc = PTR_ERR(led->flash_cfg->flash_wa_reg);
2784 if (rc != EPROBE_DEFER) {
2785 dev_err(&led->spmi_dev->dev,
Abinaya Pd0c91352014-09-08 14:32:04 +05302786 "Flash wa regulator get failed(%d)\n",
2787 rc);
Chunmei Caif094d072014-08-06 13:58:50 +08002788 }
Abinaya Pd0c91352014-09-08 14:32:04 +05302789 } else
2790 led->flash_cfg->flash_wa_reg_get = true;
Chunmei Caif094d072014-08-06 13:58:50 +08002791 }
2792
Amy Maloche864a6d52012-10-03 15:58:12 -07002793 if (led->id == QPNP_ID_FLASH1_LED0) {
Chun Zhang9d5ff672013-08-01 18:18:26 -07002794 led->flash_cfg->enable_module = FLASH_ENABLE_LED_0;
Amy Maloche864a6d52012-10-03 15:58:12 -07002795 led->flash_cfg->current_addr = FLASH_LED_0_CURR(led->base);
Amy Maloche864a6d52012-10-03 15:58:12 -07002796 led->flash_cfg->trigger_flash = FLASH_LED_0_OUTPUT;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002797 if (!*reg_set) {
2798 led->flash_cfg->flash_boost_reg =
2799 regulator_get(&led->spmi_dev->dev,
Chun Zhang0d6ca072013-07-30 21:08:39 -07002800 "flash-boost");
Chun Zhangc3b505b2013-06-03 19:01:49 -07002801 if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
2802 rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
2803 dev_err(&led->spmi_dev->dev,
2804 "Regulator get failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002805 goto error_get_flash_reg;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002806 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002807 led->flash_cfg->flash_reg_get = true;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002808 *reg_set = true;
2809 } else
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002810 led->flash_cfg->flash_reg_get = false;
Chun Zhang9d5ff672013-08-01 18:18:26 -07002811
2812 if (led->flash_cfg->torch_enable) {
2813 led->flash_cfg->second_addr =
2814 FLASH_LED_1_CURR(led->base);
2815 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002816 } else if (led->id == QPNP_ID_FLASH1_LED1) {
Chun Zhang9d5ff672013-08-01 18:18:26 -07002817 led->flash_cfg->enable_module = FLASH_ENABLE_LED_1;
Amy Maloche864a6d52012-10-03 15:58:12 -07002818 led->flash_cfg->current_addr = FLASH_LED_1_CURR(led->base);
Amy Maloche864a6d52012-10-03 15:58:12 -07002819 led->flash_cfg->trigger_flash = FLASH_LED_1_OUTPUT;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002820 if (!*reg_set) {
2821 led->flash_cfg->flash_boost_reg =
2822 regulator_get(&led->spmi_dev->dev,
Chun Zhang0d6ca072013-07-30 21:08:39 -07002823 "flash-boost");
Chun Zhangc3b505b2013-06-03 19:01:49 -07002824 if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
2825 rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
2826 dev_err(&led->spmi_dev->dev,
2827 "Regulator get failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002828 goto error_get_flash_reg;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002829 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002830 led->flash_cfg->flash_reg_get = true;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002831 *reg_set = true;
2832 } else
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002833 led->flash_cfg->flash_reg_get = false;
Chun Zhang9d5ff672013-08-01 18:18:26 -07002834
2835 if (led->flash_cfg->torch_enable) {
2836 led->flash_cfg->second_addr =
2837 FLASH_LED_0_CURR(led->base);
2838 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002839 } else {
2840 dev_err(&led->spmi_dev->dev, "Unknown flash LED name given\n");
2841 return -EINVAL;
2842 }
2843
Chun Zhangdf2d3062013-06-25 20:14:46 -07002844 if (led->flash_cfg->torch_enable) {
Chun Zhang0d6ca072013-07-30 21:08:39 -07002845 if (of_find_property(of_get_parent(node), "torch-boost-supply",
2846 NULL)) {
2847 led->flash_cfg->torch_boost_reg =
2848 regulator_get(&led->spmi_dev->dev,
2849 "torch-boost");
2850 if (IS_ERR(led->flash_cfg->torch_boost_reg)) {
2851 rc = PTR_ERR(led->flash_cfg->torch_boost_reg);
2852 dev_err(&led->spmi_dev->dev,
2853 "Torch regulator get failed(%d)\n", rc);
2854 goto error_get_torch_reg;
2855 }
Chun Zhang9d5ff672013-08-01 18:18:26 -07002856 led->flash_cfg->enable_module = FLASH_ENABLE_MODULE;
2857 } else
2858 led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
Abinaya Pb6e3ba42014-01-07 15:32:31 +05302859 led->flash_cfg->trigger_flash = FLASH_TORCH_OUTPUT;
Chun Zhangdf2d3062013-06-25 20:14:46 -07002860 }
2861
Amy Maloche864a6d52012-10-03 15:58:12 -07002862 rc = of_property_read_u32(node, "qcom,current", &val);
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002863 if (!rc) {
Chun Zhangdf2d3062013-06-25 20:14:46 -07002864 if (led->flash_cfg->torch_enable) {
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002865 led->flash_cfg->current_prgm = (val *
2866 TORCH_MAX_LEVEL / led->max_current);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002867 return 0;
2868 }
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002869 else
2870 led->flash_cfg->current_prgm = (val *
Amy Maloche864a6d52012-10-03 15:58:12 -07002871 FLASH_MAX_LEVEL / led->max_current);
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002872 } else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002873 if (led->flash_cfg->torch_enable)
2874 goto error_get_torch_reg;
2875 else
2876 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002877
2878 rc = of_property_read_u32(node, "qcom,headroom", &val);
2879 if (!rc)
2880 led->flash_cfg->headroom = (u8) val;
2881 else if (rc == -EINVAL)
Amy Maloche62571b22013-04-17 17:23:10 -07002882 led->flash_cfg->headroom = HEADROOM_500mV;
Amy Maloche864a6d52012-10-03 15:58:12 -07002883 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002884 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002885
2886 rc = of_property_read_u32(node, "qcom,duration", &val);
2887 if (!rc)
Abinaya P41bb8bc2013-12-06 22:55:37 +05302888 led->flash_cfg->duration = (u8)((val - 10) / 10);
Amy Maloche864a6d52012-10-03 15:58:12 -07002889 else if (rc == -EINVAL)
2890 led->flash_cfg->duration = FLASH_DURATION_200ms;
2891 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002892 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002893
2894 rc = of_property_read_u32(node, "qcom,clamp-curr", &val);
2895 if (!rc)
2896 led->flash_cfg->clamp_curr = (val *
2897 FLASH_MAX_LEVEL / led->max_current);
2898 else if (rc == -EINVAL)
2899 led->flash_cfg->clamp_curr = FLASH_CLAMP_200mA;
2900 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002901 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002902
2903 rc = of_property_read_u32(node, "qcom,startup-dly", &val);
2904 if (!rc)
2905 led->flash_cfg->startup_dly = (u8) val;
2906 else if (rc == -EINVAL)
Amy Maloche62571b22013-04-17 17:23:10 -07002907 led->flash_cfg->startup_dly = DELAY_128us;
Amy Maloche864a6d52012-10-03 15:58:12 -07002908 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002909 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002910
2911 led->flash_cfg->safety_timer =
2912 of_property_read_bool(node, "qcom,safety-timer");
2913
2914 return 0;
Chun Zhangdf2d3062013-06-25 20:14:46 -07002915
2916error_get_torch_reg:
2917 regulator_put(led->flash_cfg->torch_boost_reg);
2918
2919error_get_flash_reg:
2920 regulator_put(led->flash_cfg->flash_boost_reg);
2921 return rc;
2922
Amy Maloche864a6d52012-10-03 15:58:12 -07002923}
2924
Amy Malochea2726f02013-05-10 10:19:03 -07002925static int __devinit qpnp_get_config_pwm(struct pwm_config_data *pwm_cfg,
2926 struct spmi_device *spmi_dev,
2927 struct device_node *node)
2928{
2929 struct property *prop;
2930 int rc, i;
2931 u32 val;
2932 u8 *temp_cfg;
2933
2934 rc = of_property_read_u32(node, "qcom,pwm-channel", &val);
2935 if (!rc)
2936 pwm_cfg->pwm_channel = val;
2937 else
2938 return rc;
2939
Abinaya P04df8962014-05-20 16:56:09 +05302940 if (pwm_cfg->mode != MANUAL_MODE) {
Amy Malochea2726f02013-05-10 10:19:03 -07002941 rc = of_property_read_u32(node, "qcom,pwm-us", &val);
2942 if (!rc)
2943 pwm_cfg->pwm_period_us = val;
2944 else
2945 return rc;
2946 }
2947
Amy Malochea5c4ed82013-06-05 11:05:28 -07002948 pwm_cfg->use_blink =
2949 of_property_read_bool(node, "qcom,use-blink");
2950
2951 if (pwm_cfg->mode == LPG_MODE || pwm_cfg->use_blink) {
Amy Malochea2726f02013-05-10 10:19:03 -07002952 pwm_cfg->duty_cycles =
2953 devm_kzalloc(&spmi_dev->dev,
2954 sizeof(struct pwm_duty_cycles), GFP_KERNEL);
2955 if (!pwm_cfg->duty_cycles) {
2956 dev_err(&spmi_dev->dev,
2957 "Unable to allocate memory\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002958 rc = -ENOMEM;
2959 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002960 }
2961
2962 prop = of_find_property(node, "qcom,duty-pcts",
2963 &pwm_cfg->duty_cycles->num_duty_pcts);
2964 if (!prop) {
2965 dev_err(&spmi_dev->dev, "Looking up property " \
2966 "node qcom,duty-pcts failed\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002967 rc = -ENODEV;
2968 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002969 } else if (!pwm_cfg->duty_cycles->num_duty_pcts) {
2970 dev_err(&spmi_dev->dev, "Invalid length of " \
2971 "duty pcts\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002972 rc = -EINVAL;
2973 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002974 }
2975
2976 pwm_cfg->duty_cycles->duty_pcts =
2977 devm_kzalloc(&spmi_dev->dev,
Amy Maloche4cf9f322013-05-29 15:53:46 -07002978 sizeof(int) * PWM_LUT_MAX_SIZE,
Amy Malochea2726f02013-05-10 10:19:03 -07002979 GFP_KERNEL);
2980 if (!pwm_cfg->duty_cycles->duty_pcts) {
2981 dev_err(&spmi_dev->dev,
2982 "Unable to allocate memory\n");
Amy Maloche4cf9f322013-05-29 15:53:46 -07002983 rc = -ENOMEM;
2984 goto bad_lpg_params;
2985 }
2986
2987 pwm_cfg->old_duty_pcts =
2988 devm_kzalloc(&spmi_dev->dev,
2989 sizeof(int) * PWM_LUT_MAX_SIZE,
2990 GFP_KERNEL);
2991 if (!pwm_cfg->old_duty_pcts) {
2992 dev_err(&spmi_dev->dev,
2993 "Unable to allocate memory\n");
2994 rc = -ENOMEM;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002995 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002996 }
2997
2998 temp_cfg = devm_kzalloc(&spmi_dev->dev,
2999 pwm_cfg->duty_cycles->num_duty_pcts *
3000 sizeof(u8), GFP_KERNEL);
3001 if (!temp_cfg) {
3002 dev_err(&spmi_dev->dev, "Failed to allocate " \
3003 "memory for duty pcts\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07003004 rc = -ENOMEM;
3005 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07003006 }
3007
3008 memcpy(temp_cfg, prop->value,
3009 pwm_cfg->duty_cycles->num_duty_pcts);
3010
3011 for (i = 0; i < pwm_cfg->duty_cycles->num_duty_pcts; i++)
3012 pwm_cfg->duty_cycles->duty_pcts[i] =
3013 (int) temp_cfg[i];
3014
3015 rc = of_property_read_u32(node, "qcom,start-idx", &val);
3016 if (!rc) {
3017 pwm_cfg->lut_params.start_idx = val;
3018 pwm_cfg->duty_cycles->start_idx = val;
3019 } else
Amy Malochea5c4ed82013-06-05 11:05:28 -07003020 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07003021
3022 pwm_cfg->lut_params.lut_pause_hi = 0;
3023 rc = of_property_read_u32(node, "qcom,pause-hi", &val);
3024 if (!rc)
3025 pwm_cfg->lut_params.lut_pause_hi = val;
3026 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07003027 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07003028
3029 pwm_cfg->lut_params.lut_pause_lo = 0;
3030 rc = of_property_read_u32(node, "qcom,pause-lo", &val);
3031 if (!rc)
3032 pwm_cfg->lut_params.lut_pause_lo = val;
3033 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07003034 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07003035
3036 pwm_cfg->lut_params.ramp_step_ms =
3037 QPNP_LUT_RAMP_STEP_DEFAULT;
3038 rc = of_property_read_u32(node, "qcom,ramp-step-ms", &val);
3039 if (!rc)
3040 pwm_cfg->lut_params.ramp_step_ms = val;
3041 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07003042 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07003043
3044 pwm_cfg->lut_params.flags = QPNP_LED_PWM_FLAGS;
3045 rc = of_property_read_u32(node, "qcom,lut-flags", &val);
3046 if (!rc)
3047 pwm_cfg->lut_params.flags = (u8) val;
3048 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07003049 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07003050
3051 pwm_cfg->lut_params.idx_len =
3052 pwm_cfg->duty_cycles->num_duty_pcts;
3053 }
3054 return 0;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003055
3056bad_lpg_params:
3057 pwm_cfg->use_blink = false;
3058 if (pwm_cfg->mode == PWM_MODE) {
3059 dev_err(&spmi_dev->dev, "LPG parameters not set for" \
3060 " blink mode, defaulting to PWM mode\n");
3061 return 0;
3062 }
3063 return rc;
Amy Malochea2726f02013-05-10 10:19:03 -07003064};
3065
3066static int qpnp_led_get_mode(const char *mode)
3067{
3068 if (strncmp(mode, "manual", strlen(mode)) == 0)
3069 return MANUAL_MODE;
3070 else if (strncmp(mode, "pwm", strlen(mode)) == 0)
3071 return PWM_MODE;
3072 else if (strncmp(mode, "lpg", strlen(mode)) == 0)
3073 return LPG_MODE;
3074 else
3075 return -EINVAL;
3076};
3077
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303078static int __devinit qpnp_get_config_kpdbl(struct qpnp_led_data *led,
3079 struct device_node *node)
3080{
3081 int rc;
3082 u32 val;
Amy Malochea2726f02013-05-10 10:19:03 -07003083 u8 led_mode;
3084 const char *mode;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303085
3086 led->kpdbl_cfg = devm_kzalloc(&led->spmi_dev->dev,
3087 sizeof(struct kpdbl_config_data), GFP_KERNEL);
3088 if (!led->kpdbl_cfg) {
3089 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3090 return -ENOMEM;
3091 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303092
Amy Malochea2726f02013-05-10 10:19:03 -07003093 rc = of_property_read_string(node, "qcom,mode", &mode);
3094 if (!rc) {
3095 led_mode = qpnp_led_get_mode(mode);
3096 if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
3097 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3098 "supported for kpdbl.\n");
3099 return -EINVAL;
3100 }
3101 led->kpdbl_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3102 sizeof(struct pwm_config_data),
3103 GFP_KERNEL);
3104 if (!led->kpdbl_cfg->pwm_cfg) {
3105 dev_err(&led->spmi_dev->dev,
3106 "Unable to allocate memory\n");
3107 return -ENOMEM;
3108 }
3109 led->kpdbl_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003110 led->kpdbl_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003111 } else
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303112 return rc;
3113
Amy Malochea2726f02013-05-10 10:19:03 -07003114 rc = qpnp_get_config_pwm(led->kpdbl_cfg->pwm_cfg, led->spmi_dev, node);
3115 if (rc < 0)
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303116 return rc;
3117
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303118 rc = of_property_read_u32(node, "qcom,row-id", &val);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303119 if (!rc)
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303120 led->kpdbl_cfg->row_id = val;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303121 else
3122 return rc;
3123
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303124 led->kpdbl_cfg->row_src_vbst =
3125 of_property_read_bool(node, "qcom,row-src-vbst");
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303126
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303127 led->kpdbl_cfg->row_src_en =
3128 of_property_read_bool(node, "qcom,row-src-en");
3129
3130 led->kpdbl_cfg->always_on =
3131 of_property_read_bool(node, "qcom,always-on");
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303132
3133 return 0;
3134}
3135
Amy Malocheeea7b592012-10-03 15:59:36 -07003136static int __devinit qpnp_get_config_rgb(struct qpnp_led_data *led,
3137 struct device_node *node)
3138{
Amy Malochea2726f02013-05-10 10:19:03 -07003139 int rc;
3140 u8 led_mode;
3141 const char *mode;
Amy Malocheeea7b592012-10-03 15:59:36 -07003142
3143 led->rgb_cfg = devm_kzalloc(&led->spmi_dev->dev,
3144 sizeof(struct rgb_config_data), GFP_KERNEL);
3145 if (!led->rgb_cfg) {
3146 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3147 return -ENOMEM;
3148 }
3149
3150 if (led->id == QPNP_ID_RGB_RED)
3151 led->rgb_cfg->enable = RGB_LED_ENABLE_RED;
3152 else if (led->id == QPNP_ID_RGB_GREEN)
3153 led->rgb_cfg->enable = RGB_LED_ENABLE_GREEN;
3154 else if (led->id == QPNP_ID_RGB_BLUE)
3155 led->rgb_cfg->enable = RGB_LED_ENABLE_BLUE;
3156 else
3157 return -EINVAL;
3158
Amy Malochea2726f02013-05-10 10:19:03 -07003159 rc = of_property_read_string(node, "qcom,mode", &mode);
3160 if (!rc) {
3161 led_mode = qpnp_led_get_mode(mode);
3162 if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
3163 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3164 "supported for rgb.\n");
Amy Malocheeea7b592012-10-03 15:59:36 -07003165 return -EINVAL;
3166 }
Amy Malochea2726f02013-05-10 10:19:03 -07003167 led->rgb_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3168 sizeof(struct pwm_config_data),
3169 GFP_KERNEL);
3170 if (!led->rgb_cfg->pwm_cfg) {
Amy Malocheeea7b592012-10-03 15:59:36 -07003171 dev_err(&led->spmi_dev->dev,
3172 "Unable to allocate memory\n");
3173 return -ENOMEM;
3174 }
Amy Malochea2726f02013-05-10 10:19:03 -07003175 led->rgb_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003176 led->rgb_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003177 } else
3178 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07003179
Amy Malochea2726f02013-05-10 10:19:03 -07003180 rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->spmi_dev, node);
3181 if (rc < 0)
3182 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07003183
3184 return 0;
3185}
3186
Amy Malochef3813742013-04-11 19:33:47 -07003187static int __devinit qpnp_get_config_mpp(struct qpnp_led_data *led,
3188 struct device_node *node)
3189{
3190 int rc;
3191 u32 val;
Amy Malochea2726f02013-05-10 10:19:03 -07003192 u8 led_mode;
3193 const char *mode;
Amy Malochef3813742013-04-11 19:33:47 -07003194
3195 led->mpp_cfg = devm_kzalloc(&led->spmi_dev->dev,
3196 sizeof(struct mpp_config_data), GFP_KERNEL);
3197 if (!led->mpp_cfg) {
3198 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3199 return -ENOMEM;
3200 }
3201
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05303202 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
Amy Malochef3813742013-04-11 19:33:47 -07003203 rc = of_property_read_u32(node, "qcom,current-setting", &val);
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05303204 if (!rc) {
3205 if (led->mpp_cfg->current_setting < LED_MPP_CURRENT_MIN)
3206 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
3207 else if (led->mpp_cfg->current_setting > LED_MPP_CURRENT_MAX)
3208 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MAX;
3209 else
3210 led->mpp_cfg->current_setting = (u8) val;
3211 } else if (rc != -EINVAL)
Amy Malochef3813742013-04-11 19:33:47 -07003212 return rc;
3213
3214 led->mpp_cfg->source_sel = LED_MPP_SOURCE_SEL_DEFAULT;
3215 rc = of_property_read_u32(node, "qcom,source-sel", &val);
3216 if (!rc)
3217 led->mpp_cfg->source_sel = (u8) val;
3218 else if (rc != -EINVAL)
3219 return rc;
3220
3221 led->mpp_cfg->mode_ctrl = LED_MPP_MODE_SINK;
3222 rc = of_property_read_u32(node, "qcom,mode-ctrl", &val);
3223 if (!rc)
3224 led->mpp_cfg->mode_ctrl = (u8) val;
3225 else if (rc != -EINVAL)
3226 return rc;
3227
Chun Zhang874c9ab2013-07-08 17:18:34 -07003228 led->mpp_cfg->vin_ctrl = LED_MPP_VIN_CTRL_DEFAULT;
3229 rc = of_property_read_u32(node, "qcom,vin-ctrl", &val);
3230 if (!rc)
3231 led->mpp_cfg->vin_ctrl = (u8) val;
3232 else if (rc != -EINVAL)
3233 return rc;
3234
3235 led->mpp_cfg->min_brightness = 0;
3236 rc = of_property_read_u32(node, "qcom,min-brightness", &val);
3237 if (!rc)
3238 led->mpp_cfg->min_brightness = (u8) val;
3239 else if (rc != -EINVAL)
3240 return rc;
3241
Amy Malochea2726f02013-05-10 10:19:03 -07003242 rc = of_property_read_string(node, "qcom,mode", &mode);
3243 if (!rc) {
3244 led_mode = qpnp_led_get_mode(mode);
3245 led->mpp_cfg->pwm_mode = led_mode;
3246 if (led_mode == MANUAL_MODE)
3247 return MANUAL_MODE;
3248 else if (led_mode == -EINVAL) {
3249 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3250 "supported for mpp.\n");
3251 return -EINVAL;
3252 }
3253 led->mpp_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3254 sizeof(struct pwm_config_data),
3255 GFP_KERNEL);
3256 if (!led->mpp_cfg->pwm_cfg) {
3257 dev_err(&led->spmi_dev->dev,
3258 "Unable to allocate memory\n");
3259 return -ENOMEM;
3260 }
3261 led->mpp_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003262 led->mpp_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003263 } else
3264 return rc;
3265
3266 rc = qpnp_get_config_pwm(led->mpp_cfg->pwm_cfg, led->spmi_dev, node);
3267 if (rc < 0)
3268 return rc;
3269
Amy Malochef3813742013-04-11 19:33:47 -07003270 return 0;
3271}
3272
Amy Malochef3d5a062012-08-16 19:14:11 -07003273static int __devinit qpnp_leds_probe(struct spmi_device *spmi)
3274{
Amy Malochef9490c62012-11-27 19:26:04 -08003275 struct qpnp_led_data *led, *led_array;
Amy Malochef3d5a062012-08-16 19:14:11 -07003276 struct resource *led_resource;
Amy Malochea5ca5552012-10-23 13:34:46 -07003277 struct device_node *node, *temp;
Amy Malochef9490c62012-11-27 19:26:04 -08003278 int rc, i, num_leds = 0, parsed_leds = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07003279 const char *led_label;
Chun Zhangc3b505b2013-06-03 19:01:49 -07003280 bool regulator_probe = false;
Amy Malochef3d5a062012-08-16 19:14:11 -07003281
Amy Malochea5ca5552012-10-23 13:34:46 -07003282 node = spmi->dev.of_node;
3283 if (node == NULL)
3284 return -ENODEV;
3285
3286 temp = NULL;
3287 while ((temp = of_get_next_child(node, temp)))
3288 num_leds++;
3289
Amy Malochef9490c62012-11-27 19:26:04 -08003290 if (!num_leds)
3291 return -ECHILD;
3292
3293 led_array = devm_kzalloc(&spmi->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07003294 (sizeof(struct qpnp_led_data) * num_leds), GFP_KERNEL);
Amy Malochef9490c62012-11-27 19:26:04 -08003295 if (!led_array) {
Amy Malochef3d5a062012-08-16 19:14:11 -07003296 dev_err(&spmi->dev, "Unable to allocate memory\n");
3297 return -ENOMEM;
3298 }
3299
Amy Malochea5ca5552012-10-23 13:34:46 -07003300 for_each_child_of_node(node, temp) {
Amy Malochef9490c62012-11-27 19:26:04 -08003301 led = &led_array[parsed_leds];
3302 led->num_leds = num_leds;
Amy Malochea5ca5552012-10-23 13:34:46 -07003303 led->spmi_dev = spmi;
Amy Malochef3d5a062012-08-16 19:14:11 -07003304
Amy Malochea5ca5552012-10-23 13:34:46 -07003305 led_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
3306 if (!led_resource) {
3307 dev_err(&spmi->dev, "Unable to get LED base address\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003308 rc = -ENXIO;
3309 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003310 }
3311 led->base = led_resource->start;
Amy Malochef3d5a062012-08-16 19:14:11 -07003312
Amy Malochea5ca5552012-10-23 13:34:46 -07003313 rc = of_property_read_string(temp, "label", &led_label);
Amy Malochef3d5a062012-08-16 19:14:11 -07003314 if (rc < 0) {
3315 dev_err(&led->spmi_dev->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07003316 "Failure reading label, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003317 goto fail_id_check;
Amy Malochef3d5a062012-08-16 19:14:11 -07003318 }
Amy Malochea5ca5552012-10-23 13:34:46 -07003319
3320 rc = of_property_read_string(temp, "linux,name",
3321 &led->cdev.name);
3322 if (rc < 0) {
3323 dev_err(&led->spmi_dev->dev,
3324 "Failure reading led name, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003325 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003326 }
3327
3328 rc = of_property_read_u32(temp, "qcom,max-current",
3329 &led->max_current);
3330 if (rc < 0) {
3331 dev_err(&led->spmi_dev->dev,
3332 "Failure reading max_current, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003333 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003334 }
3335
3336 rc = of_property_read_u32(temp, "qcom,id", &led->id);
3337 if (rc < 0) {
3338 dev_err(&led->spmi_dev->dev,
3339 "Failure reading led id, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003340 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003341 }
3342
3343 rc = qpnp_get_common_configs(led, temp);
3344 if (rc) {
3345 dev_err(&led->spmi_dev->dev,
3346 "Failure reading common led configuration," \
3347 " rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003348 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003349 }
3350
3351 led->cdev.brightness_set = qpnp_led_set;
3352 led->cdev.brightness_get = qpnp_led_get;
3353
3354 if (strncmp(led_label, "wled", sizeof("wled")) == 0) {
3355 rc = qpnp_get_config_wled(led, temp);
3356 if (rc < 0) {
3357 dev_err(&led->spmi_dev->dev,
3358 "Unable to read wled config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003359 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003360 }
Amy Maloche864a6d52012-10-03 15:58:12 -07003361 } else if (strncmp(led_label, "flash", sizeof("flash"))
3362 == 0) {
Chun Zhang0d6ca072013-07-30 21:08:39 -07003363 if (!of_find_property(node, "flash-boost-supply", NULL))
Chun Zhangc3b505b2013-06-03 19:01:49 -07003364 regulator_probe = true;
3365 rc = qpnp_get_config_flash(led, temp, &regulator_probe);
Amy Maloche864a6d52012-10-03 15:58:12 -07003366 if (rc < 0) {
3367 dev_err(&led->spmi_dev->dev,
3368 "Unable to read flash config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003369 goto fail_id_check;
Amy Maloche864a6d52012-10-03 15:58:12 -07003370 }
Amy Malocheeea7b592012-10-03 15:59:36 -07003371 } else if (strncmp(led_label, "rgb", sizeof("rgb")) == 0) {
3372 rc = qpnp_get_config_rgb(led, temp);
3373 if (rc < 0) {
3374 dev_err(&led->spmi_dev->dev,
3375 "Unable to read rgb config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003376 goto fail_id_check;
Amy Malocheeea7b592012-10-03 15:59:36 -07003377 }
Amy Malochef3813742013-04-11 19:33:47 -07003378 } else if (strncmp(led_label, "mpp", sizeof("mpp")) == 0) {
3379 rc = qpnp_get_config_mpp(led, temp);
3380 if (rc < 0) {
3381 dev_err(&led->spmi_dev->dev,
3382 "Unable to read mpp config data\n");
Amy Malochea2726f02013-05-10 10:19:03 -07003383 goto fail_id_check;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303384 }
3385 } else if (strncmp(led_label, "kpdbl", sizeof("kpdbl")) == 0) {
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303386 num_kpbl_leds_on = 0;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303387 rc = qpnp_get_config_kpdbl(led, temp);
3388 if (rc < 0) {
3389 dev_err(&led->spmi_dev->dev,
3390 "Unable to read kpdbl config data\n");
Amy Malochef3813742013-04-11 19:33:47 -07003391 goto fail_id_check;
3392 }
Amy Malochea5ca5552012-10-23 13:34:46 -07003393 } else {
3394 dev_err(&led->spmi_dev->dev, "No LED matching label\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003395 rc = -EINVAL;
3396 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003397 }
3398
Abinaya P9e5dc3e2014-04-14 15:38:11 +05303399 if (led->id != QPNP_ID_FLASH1_LED0 &&
3400 led->id != QPNP_ID_FLASH1_LED1)
3401 mutex_init(&led->lock);
Mao Li1ff13762014-07-30 10:00:34 +08003402
3403 led->in_order_command_processing = of_property_read_bool
3404 (temp, "qcom,in-order-command-processing");
3405
3406 if (led->in_order_command_processing) {
3407 /*
3408 * the command order from user space needs to be
3409 * maintained use ordered workqueue to prevent
3410 * concurrency
3411 */
3412 led->workqueue = alloc_ordered_workqueue
3413 ("led_workqueue", 0);
3414 if (!led->workqueue) {
3415 rc = -ENOMEM;
3416 goto fail_id_check;
3417 }
3418 }
3419
Chun Zhang815a1832013-06-20 13:47:13 -07003420 INIT_WORK(&led->work, qpnp_led_work);
Amy Malochea5ca5552012-10-23 13:34:46 -07003421
3422 rc = qpnp_led_initialize(led);
3423 if (rc < 0)
3424 goto fail_id_check;
3425
3426 rc = qpnp_led_set_max_brightness(led);
3427 if (rc < 0)
3428 goto fail_id_check;
3429
3430 rc = led_classdev_register(&spmi->dev, &led->cdev);
3431 if (rc) {
3432 dev_err(&spmi->dev, "unable to register led %d,rc=%d\n",
3433 led->id, rc);
3434 goto fail_id_check;
3435 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07003436
3437 if (led->id == QPNP_ID_FLASH1_LED0 ||
3438 led->id == QPNP_ID_FLASH1_LED1) {
3439 rc = sysfs_create_group(&led->cdev.dev->kobj,
3440 &led_attr_group);
3441 if (rc)
3442 goto fail_id_check;
3443
3444 }
3445
Amy Malochea5c4ed82013-06-05 11:05:28 -07003446 if (led->id == QPNP_ID_LED_MPP) {
3447 if (!led->mpp_cfg->pwm_cfg)
3448 break;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003449 if (led->mpp_cfg->pwm_cfg->mode == PWM_MODE) {
3450 rc = sysfs_create_group(&led->cdev.dev->kobj,
3451 &pwm_attr_group);
3452 if (rc)
3453 goto fail_id_check;
3454 }
Amy Malochea5c4ed82013-06-05 11:05:28 -07003455 if (led->mpp_cfg->pwm_cfg->use_blink) {
3456 rc = sysfs_create_group(&led->cdev.dev->kobj,
3457 &blink_attr_group);
3458 if (rc)
3459 goto fail_id_check;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003460
3461 rc = sysfs_create_group(&led->cdev.dev->kobj,
3462 &lpg_attr_group);
3463 if (rc)
3464 goto fail_id_check;
3465 } else if (led->mpp_cfg->pwm_cfg->mode == LPG_MODE) {
3466 rc = sysfs_create_group(&led->cdev.dev->kobj,
3467 &lpg_attr_group);
3468 if (rc)
3469 goto fail_id_check;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003470 }
3471 } else if ((led->id == QPNP_ID_RGB_RED) ||
3472 (led->id == QPNP_ID_RGB_GREEN) ||
3473 (led->id == QPNP_ID_RGB_BLUE)) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07003474 if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
3475 rc = sysfs_create_group(&led->cdev.dev->kobj,
3476 &pwm_attr_group);
3477 if (rc)
3478 goto fail_id_check;
3479 }
Amy Malochea5c4ed82013-06-05 11:05:28 -07003480 if (led->rgb_cfg->pwm_cfg->use_blink) {
3481 rc = sysfs_create_group(&led->cdev.dev->kobj,
3482 &blink_attr_group);
3483 if (rc)
3484 goto fail_id_check;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003485
3486 rc = sysfs_create_group(&led->cdev.dev->kobj,
3487 &lpg_attr_group);
3488 if (rc)
3489 goto fail_id_check;
3490 } else if (led->rgb_cfg->pwm_cfg->mode == LPG_MODE) {
3491 rc = sysfs_create_group(&led->cdev.dev->kobj,
3492 &lpg_attr_group);
3493 if (rc)
3494 goto fail_id_check;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003495 }
3496 }
3497
Amy Malochea5ca5552012-10-23 13:34:46 -07003498 /* configure default state */
Asaf Penso55ac8472013-01-21 21:17:37 +02003499 if (led->default_on) {
Amy Malochea5ca5552012-10-23 13:34:46 -07003500 led->cdev.brightness = led->cdev.max_brightness;
Chun Zhang815a1832013-06-20 13:47:13 -07003501 __qpnp_led_work(led, led->cdev.brightness);
Asaf Penso55ac8472013-01-21 21:17:37 +02003502 if (led->turn_off_delay_ms > 0)
3503 qpnp_led_turn_off(led);
3504 } else
Amy Malochea5ca5552012-10-23 13:34:46 -07003505 led->cdev.brightness = LED_OFF;
3506
Amy Malochef9490c62012-11-27 19:26:04 -08003507 parsed_leds++;
Amy Malochef3d5a062012-08-16 19:14:11 -07003508 }
Amy Malochef9490c62012-11-27 19:26:04 -08003509 dev_set_drvdata(&spmi->dev, led_array);
Amy Malochef3d5a062012-08-16 19:14:11 -07003510 return 0;
3511
3512fail_id_check:
Chun Zhang815a1832013-06-20 13:47:13 -07003513 for (i = 0; i < parsed_leds; i++) {
Abinaya P9e5dc3e2014-04-14 15:38:11 +05303514 if (led_array[i].id != QPNP_ID_FLASH1_LED0 &&
3515 led_array[i].id != QPNP_ID_FLASH1_LED1)
3516 mutex_destroy(&led_array[i].lock);
Mao Li1ff13762014-07-30 10:00:34 +08003517 if (led_array[i].in_order_command_processing)
3518 destroy_workqueue(led_array[i].workqueue);
Amy Malochef9490c62012-11-27 19:26:04 -08003519 led_classdev_unregister(&led_array[i].cdev);
Chun Zhang815a1832013-06-20 13:47:13 -07003520 }
3521
Amy Malochef3d5a062012-08-16 19:14:11 -07003522 return rc;
3523}
3524
3525static int __devexit qpnp_leds_remove(struct spmi_device *spmi)
3526{
Amy Malochef9490c62012-11-27 19:26:04 -08003527 struct qpnp_led_data *led_array = dev_get_drvdata(&spmi->dev);
3528 int i, parsed_leds = led_array->num_leds;
Amy Malochef3d5a062012-08-16 19:14:11 -07003529
Amy Malochebc97c0d22013-03-24 22:06:16 -07003530 for (i = 0; i < parsed_leds; i++) {
Chun Zhang815a1832013-06-20 13:47:13 -07003531 cancel_work_sync(&led_array[i].work);
Abinaya P9e5dc3e2014-04-14 15:38:11 +05303532 if (led_array[i].id != QPNP_ID_FLASH1_LED0 &&
3533 led_array[i].id != QPNP_ID_FLASH1_LED1)
3534 mutex_destroy(&led_array[i].lock);
Abinaya P9e5dc3e2014-04-14 15:38:11 +05303535
Mao Li1ff13762014-07-30 10:00:34 +08003536 if (led_array[i].in_order_command_processing)
3537 destroy_workqueue(led_array[i].workqueue);
Amy Malochef9490c62012-11-27 19:26:04 -08003538 led_classdev_unregister(&led_array[i].cdev);
Amy Malochebc97c0d22013-03-24 22:06:16 -07003539 switch (led_array[i].id) {
3540 case QPNP_ID_WLED:
3541 break;
3542 case QPNP_ID_FLASH1_LED0:
3543 case QPNP_ID_FLASH1_LED1:
Chun Zhangda8ad0f2013-07-17 14:46:47 -07003544 if (led_array[i].flash_cfg->flash_reg_get)
Chun Zhangc3b505b2013-06-03 19:01:49 -07003545 regulator_put(led_array[i].flash_cfg-> \
3546 flash_boost_reg);
Chun Zhangdf2d3062013-06-25 20:14:46 -07003547 if (led_array[i].flash_cfg->torch_enable)
3548 regulator_put(led_array[i].flash_cfg->\
3549 torch_boost_reg);
Amy Malochebc97c0d22013-03-24 22:06:16 -07003550 sysfs_remove_group(&led_array[i].cdev.dev->kobj,
3551 &led_attr_group);
3552 break;
3553 case QPNP_ID_RGB_RED:
3554 case QPNP_ID_RGB_GREEN:
3555 case QPNP_ID_RGB_BLUE:
Amy Maloche4cf9f322013-05-29 15:53:46 -07003556 if (led_array[i].rgb_cfg->pwm_cfg->mode == PWM_MODE)
3557 sysfs_remove_group(&led_array[i].cdev.dev->\
3558 kobj, &pwm_attr_group);
3559 if (led_array[i].rgb_cfg->pwm_cfg->use_blink) {
3560 sysfs_remove_group(&led_array[i].cdev.dev->\
3561 kobj, &blink_attr_group);
3562 sysfs_remove_group(&led_array[i].cdev.dev->\
3563 kobj, &lpg_attr_group);
3564 } else if (led_array[i].rgb_cfg->pwm_cfg->mode\
3565 == LPG_MODE)
3566 sysfs_remove_group(&led_array[i].cdev.dev->\
3567 kobj, &lpg_attr_group);
3568 break;
3569 case QPNP_ID_LED_MPP:
3570 if (!led_array[i].mpp_cfg->pwm_cfg)
3571 break;
3572 if (led_array[i].mpp_cfg->pwm_cfg->mode == PWM_MODE)
3573 sysfs_remove_group(&led_array[i].cdev.dev->\
3574 kobj, &pwm_attr_group);
3575 if (led_array[i].mpp_cfg->pwm_cfg->use_blink) {
3576 sysfs_remove_group(&led_array[i].cdev.dev->\
3577 kobj, &blink_attr_group);
3578 sysfs_remove_group(&led_array[i].cdev.dev->\
3579 kobj, &lpg_attr_group);
3580 } else if (led_array[i].mpp_cfg->pwm_cfg->mode\
3581 == LPG_MODE)
3582 sysfs_remove_group(&led_array[i].cdev.dev->\
3583 kobj, &lpg_attr_group);
3584 break;
Amy Malochebc97c0d22013-03-24 22:06:16 -07003585 default:
3586 dev_err(&led_array[i].spmi_dev->dev,
3587 "Invalid LED(%d)\n",
3588 led_array[i].id);
3589 return -EINVAL;
3590 }
3591 }
Amy Malochef3d5a062012-08-16 19:14:11 -07003592
3593 return 0;
3594}
Himanshu Aggarwal4eac46c2013-11-27 13:46:53 +05303595
3596#ifdef CONFIG_OF
Amy Malochef3d5a062012-08-16 19:14:11 -07003597static struct of_device_id spmi_match_table[] = {
Himanshu Aggarwal4eac46c2013-11-27 13:46:53 +05303598 { .compatible = "qcom,leds-qpnp",},
3599 { },
Amy Malochef3d5a062012-08-16 19:14:11 -07003600};
Himanshu Aggarwal4eac46c2013-11-27 13:46:53 +05303601#else
3602#define spmi_match_table NULL
3603#endif
Amy Malochef3d5a062012-08-16 19:14:11 -07003604
3605static struct spmi_driver qpnp_leds_driver = {
3606 .driver = {
3607 .name = "qcom,leds-qpnp",
3608 .of_match_table = spmi_match_table,
3609 },
3610 .probe = qpnp_leds_probe,
3611 .remove = __devexit_p(qpnp_leds_remove),
3612};
3613
3614static int __init qpnp_led_init(void)
3615{
3616 return spmi_driver_register(&qpnp_leds_driver);
3617}
3618module_init(qpnp_led_init);
3619
3620static void __exit qpnp_led_exit(void)
3621{
3622 spmi_driver_unregister(&qpnp_leds_driver);
3623}
3624module_exit(qpnp_led_exit);
3625
3626MODULE_DESCRIPTION("QPNP LEDs driver");
3627MODULE_LICENSE("GPL v2");
3628MODULE_ALIAS("leds:leds-qpnp");
Amy Maloche864a6d52012-10-03 15:58:12 -07003629