blob: 2af172454018bfdbeda0bcab6bf9221f0efdfc9e [file] [log] [blame]
Amy Malochef3d5a062012-08-16 19:14:11 -07001
Amy Maloche832d90a2013-01-07 10:15:29 -08002/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Amy Malochef3d5a062012-08-16 19:14:11 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/slab.h>
18#include <linux/leds.h>
19#include <linux/err.h>
Amy Malochedc3e5572012-09-25 16:39:06 -070020#include <linux/spinlock.h>
Amy Malochef3d5a062012-08-16 19:14:11 -070021#include <linux/of_platform.h>
22#include <linux/of_device.h>
23#include <linux/spmi.h>
Amy Malocheeea7b592012-10-03 15:59:36 -070024#include <linux/qpnp/pwm.h>
Asaf Penso55ac8472013-01-21 21:17:37 +020025#include <linux/workqueue.h>
Amy Maloche0150b5e2013-08-15 18:18:32 -070026#include <linux/delay.h>
Chun Zhangc3b505b2013-06-03 19:01:49 -070027#include <linux/regulator/consumer.h>
Chun Zhang7ce3a9f2013-10-22 10:48:12 -070028#include <linux/delay.h>
Amy Malochef3d5a062012-08-16 19:14:11 -070029
30#define WLED_MOD_EN_REG(base, n) (base + 0x60 + n*0x10)
31#define WLED_IDAC_DLY_REG(base, n) (WLED_MOD_EN_REG(base, n) + 0x01)
32#define WLED_FULL_SCALE_REG(base, n) (WLED_IDAC_DLY_REG(base, n) + 0x01)
Amy Maloched44516e2013-02-14 17:36:34 -080033#define WLED_MOD_SRC_SEL_REG(base, n) (WLED_FULL_SCALE_REG(base, n) + 0x01)
Amy Malochef3d5a062012-08-16 19:14:11 -070034
35/* wled control registers */
36#define WLED_BRIGHTNESS_CNTL_LSB(base, n) (base + 0x40 + 2*n)
37#define WLED_BRIGHTNESS_CNTL_MSB(base, n) (base + 0x41 + 2*n)
38#define WLED_MOD_CTRL_REG(base) (base + 0x46)
39#define WLED_SYNC_REG(base) (base + 0x47)
40#define WLED_FDBCK_CTRL_REG(base) (base + 0x48)
41#define WLED_SWITCHING_FREQ_REG(base) (base + 0x4C)
42#define WLED_OVP_CFG_REG(base) (base + 0x4D)
43#define WLED_BOOST_LIMIT_REG(base) (base + 0x4E)
44#define WLED_CURR_SINK_REG(base) (base + 0x4F)
45#define WLED_HIGH_POLE_CAP_REG(base) (base + 0x58)
46#define WLED_CURR_SINK_MASK 0xE0
47#define WLED_CURR_SINK_SHFT 0x05
Amy Maloche0150b5e2013-08-15 18:18:32 -070048#define WLED_DISABLE_ALL_SINKS 0x00
Amy Maloche9eccb4c2013-07-12 14:31:56 -070049#define WLED_SWITCH_FREQ_MASK 0x0F
Amy Malochef3d5a062012-08-16 19:14:11 -070050#define WLED_OVP_VAL_MASK 0x03
51#define WLED_OVP_VAL_BIT_SHFT 0x00
52#define WLED_BOOST_LIMIT_MASK 0x07
53#define WLED_BOOST_LIMIT_BIT_SHFT 0x00
54#define WLED_BOOST_ON 0x80
55#define WLED_BOOST_OFF 0x00
56#define WLED_EN_MASK 0x80
57#define WLED_NO_MASK 0x00
58#define WLED_CP_SELECT_MAX 0x03
59#define WLED_CP_SELECT_MASK 0x02
60#define WLED_USE_EXT_GEN_MOD_SRC 0x01
61#define WLED_CTL_DLY_STEP 200
62#define WLED_CTL_DLY_MAX 1400
63#define WLED_MAX_CURR 25
Amy Maloche0150b5e2013-08-15 18:18:32 -070064#define WLED_NO_CURRENT 0x00
65#define WLED_OVP_DELAY 1000
Amy Malochef3d5a062012-08-16 19:14:11 -070066#define WLED_MSB_MASK 0x0F
Himanshu Aggarwale51de272013-09-11 10:02:24 +053067#define WLED_MAX_CURR_MASK 0x1F
Amy Malochef3d5a062012-08-16 19:14:11 -070068#define WLED_OP_FDBCK_MASK 0x07
69#define WLED_OP_FDBCK_BIT_SHFT 0x00
Amy Malochebd687672013-03-18 11:23:45 -070070#define WLED_OP_FDBCK_DEFAULT 0x00
Amy Malochef3d5a062012-08-16 19:14:11 -070071
Amy Maloched55fdb82013-02-26 18:11:57 -080072#define WLED_MAX_LEVEL 4095
Amy Malochef3d5a062012-08-16 19:14:11 -070073#define WLED_8_BIT_MASK 0xFF
74#define WLED_4_BIT_MASK 0x0F
75#define WLED_8_BIT_SHFT 0x08
76#define WLED_MAX_DUTY_CYCLE 0xFFF
77
78#define WLED_SYNC_VAL 0x07
79#define WLED_SYNC_RESET_VAL 0x00
80
Amy Maloche0150b5e2013-08-15 18:18:32 -070081#define PMIC_VER_8026 0x04
82#define PMIC_VERSION_REG 0x0105
83
Amy Malochef3d5a062012-08-16 19:14:11 -070084#define WLED_DEFAULT_STRINGS 0x01
85#define WLED_DEFAULT_OVP_VAL 0x02
86#define WLED_BOOST_LIM_DEFAULT 0x03
87#define WLED_CP_SEL_DEFAULT 0x00
88#define WLED_CTRL_DLY_DEFAULT 0x00
Amy Maloche9eccb4c2013-07-12 14:31:56 -070089#define WLED_SWITCH_FREQ_DEFAULT 0x0B
Amy Malochef3d5a062012-08-16 19:14:11 -070090
Amy Maloche864a6d52012-10-03 15:58:12 -070091#define FLASH_SAFETY_TIMER(base) (base + 0x40)
92#define FLASH_MAX_CURR(base) (base + 0x41)
93#define FLASH_LED_0_CURR(base) (base + 0x42)
94#define FLASH_LED_1_CURR(base) (base + 0x43)
95#define FLASH_CLAMP_CURR(base) (base + 0x44)
96#define FLASH_LED_TMR_CTRL(base) (base + 0x48)
Amy Maloched44516e2013-02-14 17:36:34 -080097#define FLASH_HEADROOM(base) (base + 0x4A)
Amy Maloche864a6d52012-10-03 15:58:12 -070098#define FLASH_STARTUP_DELAY(base) (base + 0x4B)
99#define FLASH_MASK_ENABLE(base) (base + 0x4C)
100#define FLASH_VREG_OK_FORCE(base) (base + 0x4F)
101#define FLASH_ENABLE_CONTROL(base) (base + 0x46)
102#define FLASH_LED_STROBE_CTRL(base) (base + 0x47)
Amy Malochebc97c0d22013-03-24 22:06:16 -0700103#define FLASH_LED_UNLOCK_SECURE(base) (base + 0xD0)
104#define FLASH_LED_TORCH(base) (base + 0xE4)
Chun Zhange8954cf2013-05-02 11:14:34 -0700105#define FLASH_FAULT_DETECT(base) (base + 0x51)
Chun Zhang0d6ca072013-07-30 21:08:39 -0700106#define FLASH_PERIPHERAL_SUBTYPE(base) (base + 0x05)
Chun Zhang7ce3a9f2013-10-22 10:48:12 -0700107#define FLASH_CURRENT_RAMP(base) (base + 0x54)
Amy Maloche864a6d52012-10-03 15:58:12 -0700108
109#define FLASH_MAX_LEVEL 0x4F
Chun Zhang9c1fbaa2013-06-17 15:31:18 -0700110#define TORCH_MAX_LEVEL 0x0F
Amy Maloche864a6d52012-10-03 15:58:12 -0700111#define FLASH_NO_MASK 0x00
112
113#define FLASH_MASK_1 0x20
114#define FLASH_MASK_REG_MASK 0xE0
115#define FLASH_HEADROOM_MASK 0x03
116#define FLASH_SAFETY_TIMER_MASK 0x7F
117#define FLASH_CURRENT_MASK 0xFF
Amy Malochebc97c0d22013-03-24 22:06:16 -0700118#define FLASH_MAX_CURRENT_MASK 0x7F
Amy Maloche864a6d52012-10-03 15:58:12 -0700119#define FLASH_TMR_MASK 0x03
120#define FLASH_TMR_WATCHDOG 0x03
121#define FLASH_TMR_SAFETY 0x00
Chun Zhange8954cf2013-05-02 11:14:34 -0700122#define FLASH_FAULT_DETECT_MASK 0X80
Chun Zhangbcdcf232013-06-06 18:11:41 -0700123#define FLASH_HW_VREG_OK 0x40
Amy Maloche864a6d52012-10-03 15:58:12 -0700124#define FLASH_VREG_MASK 0xC0
Amy Maloche864a6d52012-10-03 15:58:12 -0700125#define FLASH_STARTUP_DLY_MASK 0x02
Chun Zhang7ce3a9f2013-10-22 10:48:12 -0700126#define FLASH_CURRENT_RAMP_MASK 0xBF
Amy Maloche864a6d52012-10-03 15:58:12 -0700127
128#define FLASH_ENABLE_ALL 0xE0
129#define FLASH_ENABLE_MODULE 0x80
130#define FLASH_ENABLE_MODULE_MASK 0x80
131#define FLASH_DISABLE_ALL 0x00
Amy Maloche38b9aae2013-02-07 12:15:34 -0800132#define FLASH_ENABLE_MASK 0xE0
Chun Zhang9d5ff672013-08-01 18:18:26 -0700133#define FLASH_ENABLE_LED_0 0xC0
134#define FLASH_ENABLE_LED_1 0xA0
Amy Maloche864a6d52012-10-03 15:58:12 -0700135#define FLASH_INIT_MASK 0xE0
Chun Zhange8954cf2013-05-02 11:14:34 -0700136#define FLASH_SELFCHECK_ENABLE 0x80
Chun Zhang7ce3a9f2013-10-22 10:48:12 -0700137#define FLASH_RAMP_STEP_27US 0xBF
Amy Maloche864a6d52012-10-03 15:58:12 -0700138
Abinaya Pb6e3ba42014-01-07 15:32:31 +0530139#define FLASH_HW_SW_STROBE_SEL_MASK 0x04
Amy Malochebc97c0d22013-03-24 22:06:16 -0700140#define FLASH_STROBE_MASK 0xC7
Amy Maloche864a6d52012-10-03 15:58:12 -0700141#define FLASH_LED_0_OUTPUT 0x80
142#define FLASH_LED_1_OUTPUT 0x40
Abinaya Pb6e3ba42014-01-07 15:32:31 +0530143#define FLASH_TORCH_OUTPUT 0xC0
Amy Maloche864a6d52012-10-03 15:58:12 -0700144
145#define FLASH_CURRENT_PRGM_MIN 1
146#define FLASH_CURRENT_PRGM_SHIFT 1
Amy Malochebc97c0d22013-03-24 22:06:16 -0700147#define FLASH_CURRENT_MAX 0x4F
Chun Zhange8954cf2013-05-02 11:14:34 -0700148#define FLASH_CURRENT_TORCH 0x07
Amy Maloche864a6d52012-10-03 15:58:12 -0700149
150#define FLASH_DURATION_200ms 0x13
151#define FLASH_CLAMP_200mA 0x0F
152
Amy Malochebc97c0d22013-03-24 22:06:16 -0700153#define FLASH_TORCH_MASK 0x03
154#define FLASH_LED_TORCH_ENABLE 0x00
155#define FLASH_LED_TORCH_DISABLE 0x03
156#define FLASH_UNLOCK_SECURE 0xA5
157#define FLASH_SECURE_MASK 0xFF
158
Chun Zhang0d6ca072013-07-30 21:08:39 -0700159#define FLASH_SUBTYPE_DUAL 0x01
160#define FLASH_SUBTYPE_SINGLE 0x02
161
Chun Zhang7ce3a9f2013-10-22 10:48:12 -0700162#define FLASH_RAMP_UP_DELAY_US 1000
163#define FLASH_RAMP_DN_DELAY_US 2160
164
Amy Malochea5ca5552012-10-23 13:34:46 -0700165#define LED_TRIGGER_DEFAULT "none"
166
Amy Malocheeea7b592012-10-03 15:59:36 -0700167#define RGB_LED_SRC_SEL(base) (base + 0x45)
168#define RGB_LED_EN_CTL(base) (base + 0x46)
169#define RGB_LED_ATC_CTL(base) (base + 0x47)
170
171#define RGB_MAX_LEVEL LED_FULL
172#define RGB_LED_ENABLE_RED 0x80
173#define RGB_LED_ENABLE_GREEN 0x40
174#define RGB_LED_ENABLE_BLUE 0x20
175#define RGB_LED_SOURCE_VPH_PWR 0x01
176#define RGB_LED_ENABLE_MASK 0xE0
177#define RGB_LED_SRC_MASK 0x03
178#define QPNP_LED_PWM_FLAGS (PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP)
Amy Maloche013b35b2013-03-19 12:29:11 -0700179#define QPNP_LUT_RAMP_STEP_DEFAULT 255
Amy Malocheeea7b592012-10-03 15:59:36 -0700180#define PWM_LUT_MAX_SIZE 63
Amy Maloche4cf9f322013-05-29 15:53:46 -0700181#define PWM_GPLED_LUT_MAX_SIZE 31
Amy Malocheeea7b592012-10-03 15:59:36 -0700182#define RGB_LED_DISABLE 0x00
183
Amy Malochef3813742013-04-11 19:33:47 -0700184#define MPP_MAX_LEVEL LED_FULL
185#define LED_MPP_MODE_CTRL(base) (base + 0x40)
186#define LED_MPP_VIN_CTRL(base) (base + 0x41)
187#define LED_MPP_EN_CTRL(base) (base + 0x46)
188#define LED_MPP_SINK_CTRL(base) (base + 0x4C)
189
Mohan Pallaka38ec7f32013-09-11 13:01:48 +0530190#define LED_MPP_CURRENT_MIN 5
191#define LED_MPP_CURRENT_MAX 40
Chun Zhang874c9ab2013-07-08 17:18:34 -0700192#define LED_MPP_VIN_CTRL_DEFAULT 0
Amy Malochea2726f02013-05-10 10:19:03 -0700193#define LED_MPP_CURRENT_PER_SETTING 5
Amy Malochef3813742013-04-11 19:33:47 -0700194#define LED_MPP_SOURCE_SEL_DEFAULT LED_MPP_MODE_ENABLE
195
196#define LED_MPP_SINK_MASK 0x07
197#define LED_MPP_MODE_MASK 0x7F
Chun Zhang874c9ab2013-07-08 17:18:34 -0700198#define LED_MPP_VIN_MASK 0x03
Amy Malochef3813742013-04-11 19:33:47 -0700199#define LED_MPP_EN_MASK 0x80
Amy Malochece59f662013-05-02 10:59:53 -0700200#define LED_MPP_SRC_MASK 0x0F
201#define LED_MPP_MODE_CTRL_MASK 0x70
Amy Malochef3813742013-04-11 19:33:47 -0700202
203#define LED_MPP_MODE_SINK (0x06 << 4)
204#define LED_MPP_MODE_ENABLE 0x01
205#define LED_MPP_MODE_OUTPUT 0x10
206#define LED_MPP_MODE_DISABLE 0x00
207#define LED_MPP_EN_ENABLE 0x80
208#define LED_MPP_EN_DISABLE 0x00
209
210#define MPP_SOURCE_DTEST1 0x08
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530211
212#define KPDBL_MAX_LEVEL LED_FULL
213#define KPDBL_ROW_SRC_SEL(base) (base + 0x40)
214#define KPDBL_ENABLE(base) (base + 0x46)
215#define KPDBL_ROW_SRC(base) (base + 0xE5)
216
217#define KPDBL_ROW_SRC_SEL_VAL_MASK 0x0F
218#define KPDBL_ROW_SCAN_EN_MASK 0x80
219#define KPDBL_ROW_SCAN_VAL_MASK 0x0F
220#define KPDBL_ROW_SCAN_EN_SHIFT 7
221#define KPDBL_MODULE_EN 0x80
222#define KPDBL_MODULE_DIS 0x00
223#define KPDBL_MODULE_EN_MASK 0x80
224
Amy Malochef3d5a062012-08-16 19:14:11 -0700225/**
226 * enum qpnp_leds - QPNP supported led ids
227 * @QPNP_ID_WLED - White led backlight
228 */
229enum qpnp_leds {
Amy Maloche864a6d52012-10-03 15:58:12 -0700230 QPNP_ID_WLED = 0,
231 QPNP_ID_FLASH1_LED0,
232 QPNP_ID_FLASH1_LED1,
Amy Malocheeea7b592012-10-03 15:59:36 -0700233 QPNP_ID_RGB_RED,
234 QPNP_ID_RGB_GREEN,
235 QPNP_ID_RGB_BLUE,
Amy Malochef3813742013-04-11 19:33:47 -0700236 QPNP_ID_LED_MPP,
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530237 QPNP_ID_KPDBL,
Amy Maloche864a6d52012-10-03 15:58:12 -0700238 QPNP_ID_MAX,
Amy Malochef3d5a062012-08-16 19:14:11 -0700239};
240
241/* current boost limit */
242enum wled_current_boost_limit {
243 WLED_CURR_LIMIT_105mA,
244 WLED_CURR_LIMIT_385mA,
245 WLED_CURR_LIMIT_525mA,
246 WLED_CURR_LIMIT_805mA,
247 WLED_CURR_LIMIT_980mA,
248 WLED_CURR_LIMIT_1260mA,
249 WLED_CURR_LIMIT_1400mA,
250 WLED_CURR_LIMIT_1680mA,
251};
252
253/* over voltage protection threshold */
254enum wled_ovp_threshold {
255 WLED_OVP_35V,
256 WLED_OVP_32V,
257 WLED_OVP_29V,
Abinaya Pe65d2cd2013-12-23 19:31:57 +0530258 WLED_OVP_27V,
Amy Malochef3d5a062012-08-16 19:14:11 -0700259};
260
Amy Maloche864a6d52012-10-03 15:58:12 -0700261enum flash_headroom {
262 HEADROOM_250mV = 0,
263 HEADROOM_300mV,
264 HEADROOM_400mV,
265 HEADROOM_500mV,
266};
267
268enum flash_startup_dly {
269 DELAY_10us = 0,
270 DELAY_32us,
271 DELAY_64us,
272 DELAY_128us,
273};
274
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530275enum led_mode {
276 PWM_MODE = 0,
277 LPG_MODE,
Amy Malochea2726f02013-05-10 10:19:03 -0700278 MANUAL_MODE,
Amy Malocheeea7b592012-10-03 15:59:36 -0700279};
280
Amy Malochef3d5a062012-08-16 19:14:11 -0700281static u8 wled_debug_regs[] = {
282 /* common registers */
283 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4f,
284 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
285 /* LED1 */
286 0x60, 0x61, 0x62, 0x63, 0x66,
Amy Malochea5ca5552012-10-23 13:34:46 -0700287 /* LED2 */
Amy Malochef3d5a062012-08-16 19:14:11 -0700288 0x70, 0x71, 0x72, 0x73, 0x76,
Amy Malochea5ca5552012-10-23 13:34:46 -0700289 /* LED3 */
Amy Malochef3d5a062012-08-16 19:14:11 -0700290 0x80, 0x81, 0x82, 0x83, 0x86,
291};
292
Amy Maloche864a6d52012-10-03 15:58:12 -0700293static u8 flash_debug_regs[] = {
294 0x40, 0x41, 0x42, 0x43, 0x44, 0x48, 0x49, 0x4b, 0x4c,
295 0x4f, 0x46, 0x47,
296};
297
Amy Malocheeea7b592012-10-03 15:59:36 -0700298static u8 rgb_pwm_debug_regs[] = {
299 0x45, 0x46, 0x47,
300};
Amy Malochef3813742013-04-11 19:33:47 -0700301
302static u8 mpp_debug_regs[] = {
303 0x40, 0x41, 0x42, 0x45, 0x46, 0x4c,
304};
305
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530306static u8 kpdbl_debug_regs[] = {
307 0x40, 0x46, 0xb1, 0xb3, 0xb4, 0xe5,
308};
309
Amy Malochef3d5a062012-08-16 19:14:11 -0700310/**
Amy Malochea2726f02013-05-10 10:19:03 -0700311 * pwm_config_data - pwm configuration data
312 * @lut_params - lut parameters to be used by pwm driver
313 * @pwm_device - pwm device
314 * @pwm_channel - pwm channel to be configured for led
315 * @pwm_period_us - period for pwm, in us
316 * @mode - mode the led operates in
Amy Maloche4cf9f322013-05-29 15:53:46 -0700317 * @old_duty_pcts - storage for duty pcts that may need to be reused
318 * @default_mode - default mode of LED as set in device tree
319 * @use_blink - use blink sysfs entry
320 * @blinking - device is currently blinking w/LPG mode
Amy Malochea2726f02013-05-10 10:19:03 -0700321 */
322struct pwm_config_data {
323 struct lut_params lut_params;
324 struct pwm_device *pwm_dev;
325 int pwm_channel;
326 u32 pwm_period_us;
327 struct pwm_duty_cycles *duty_cycles;
Amy Maloche4cf9f322013-05-29 15:53:46 -0700328 int *old_duty_pcts;
Amy Malochea2726f02013-05-10 10:19:03 -0700329 u8 mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -0700330 u8 default_mode;
Amy Malochea5c4ed82013-06-05 11:05:28 -0700331 bool use_blink;
Amy Maloche4cf9f322013-05-29 15:53:46 -0700332 bool blinking;
Amy Malochea2726f02013-05-10 10:19:03 -0700333};
334
335/**
Amy Malochef3d5a062012-08-16 19:14:11 -0700336 * wled_config_data - wled configuration data
337 * @num_strings - number of wled strings supported
338 * @ovp_val - over voltage protection threshold
339 * @boost_curr_lim - boot current limit
340 * @cp_select - high pole capacitance
341 * @ctrl_delay_us - delay in activation of led
342 * @dig_mod_gen_en - digital module generator
343 * @cs_out_en - current sink output enable
344 * @op_fdbck - selection of output as feedback for the boost
345 */
346struct wled_config_data {
347 u8 num_strings;
348 u8 ovp_val;
349 u8 boost_curr_lim;
350 u8 cp_select;
351 u8 ctrl_delay_us;
352 u8 switch_freq;
Amy Malochebd687672013-03-18 11:23:45 -0700353 u8 op_fdbck;
Amy Maloche0150b5e2013-08-15 18:18:32 -0700354 u8 pmic_version;
Amy Malochef3d5a062012-08-16 19:14:11 -0700355 bool dig_mod_gen_en;
356 bool cs_out_en;
Amy Malochef3d5a062012-08-16 19:14:11 -0700357};
358
359/**
Amy Malochef3813742013-04-11 19:33:47 -0700360 * mpp_config_data - mpp configuration data
Amy Malochea2726f02013-05-10 10:19:03 -0700361 * @pwm_cfg - device pwm configuration
Amy Malochef3813742013-04-11 19:33:47 -0700362 * @current_setting - current setting, 5ma-40ma in 5ma increments
Amy Malochea2726f02013-05-10 10:19:03 -0700363 * @source_sel - source selection
364 * @mode_ctrl - mode control
Chun Zhang874c9ab2013-07-08 17:18:34 -0700365 * @vin_ctrl - input control
366 * @min_brightness - minimum brightness supported
Amy Malochea2726f02013-05-10 10:19:03 -0700367 * @pwm_mode - pwm mode in use
Amy Malochef3813742013-04-11 19:33:47 -0700368 */
369struct mpp_config_data {
Amy Malochea2726f02013-05-10 10:19:03 -0700370 struct pwm_config_data *pwm_cfg;
Amy Malochef3813742013-04-11 19:33:47 -0700371 u8 current_setting;
372 u8 source_sel;
373 u8 mode_ctrl;
Chun Zhang874c9ab2013-07-08 17:18:34 -0700374 u8 vin_ctrl;
375 u8 min_brightness;
Amy Malochea2726f02013-05-10 10:19:03 -0700376 u8 pwm_mode;
Amy Malochef3813742013-04-11 19:33:47 -0700377};
378
379/**
Amy Maloche864a6d52012-10-03 15:58:12 -0700380 * flash_config_data - flash configuration data
381 * @current_prgm - current to be programmed, scaled by max level
382 * @clamp_curr - clamp current to use
383 * @headroom - headroom value to use
384 * @duration - duration of the flash
385 * @enable_module - enable address for particular flash
386 * @trigger_flash - trigger flash
387 * @startup_dly - startup delay for flash
Amy Malochebc97c0d22013-03-24 22:06:16 -0700388 * @strobe_type - select between sw and hw strobe
Chun Zhang0d6ca072013-07-30 21:08:39 -0700389 * @peripheral_subtype - module peripheral subtype
Amy Maloche864a6d52012-10-03 15:58:12 -0700390 * @current_addr - address to write for current
391 * @second_addr - address of secondary flash to be written
392 * @safety_timer - enable safety timer or watchdog timer
Amy Malochebc97c0d22013-03-24 22:06:16 -0700393 * @torch_enable - enable flash LED torch mode
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700394 * @flash_reg_get - flash regulator attached or not
Chun Zhangc3b505b2013-06-03 19:01:49 -0700395 * @flash_on - flash status, on or off
Chun Zhangdf2d3062013-06-25 20:14:46 -0700396 * @torch_on - torch status, on or off
Chun Zhangc3b505b2013-06-03 19:01:49 -0700397 * @flash_boost_reg - boost regulator for flash
Chun Zhangdf2d3062013-06-25 20:14:46 -0700398 * @torch_boost_reg - boost regulator for torch
Amy Maloche864a6d52012-10-03 15:58:12 -0700399 */
400struct flash_config_data {
401 u8 current_prgm;
402 u8 clamp_curr;
403 u8 headroom;
404 u8 duration;
405 u8 enable_module;
406 u8 trigger_flash;
407 u8 startup_dly;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700408 u8 strobe_type;
Chun Zhang0d6ca072013-07-30 21:08:39 -0700409 u8 peripheral_subtype;
Amy Maloche864a6d52012-10-03 15:58:12 -0700410 u16 current_addr;
411 u16 second_addr;
412 bool safety_timer;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700413 bool torch_enable;
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700414 bool flash_reg_get;
Chun Zhangc3b505b2013-06-03 19:01:49 -0700415 bool flash_on;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700416 bool torch_on;
Chun Zhangc3b505b2013-06-03 19:01:49 -0700417 struct regulator *flash_boost_reg;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700418 struct regulator *torch_boost_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -0700419};
420
421/**
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530422 * kpdbl_config_data - kpdbl configuration data
Amy Malochea2726f02013-05-10 10:19:03 -0700423 * @pwm_cfg - device pwm configuration
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530424 * @mode - running mode: pwm or lut
425 * @row_id - row id of the led
426 * @row_src_vbst - 0 for vph_pwr and 1 for vbst
427 * @row_src_en - enable row source
428 * @always_on - always on row
429 * @lut_params - lut parameters to be used by pwm driver
430 * @duty_cycles - duty cycles for lut
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530431 */
432struct kpdbl_config_data {
Amy Malochea2726f02013-05-10 10:19:03 -0700433 struct pwm_config_data *pwm_cfg;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530434 u32 row_id;
435 bool row_src_vbst;
436 bool row_src_en;
437 bool always_on;
438 struct pwm_duty_cycles *duty_cycles;
439 struct lut_params lut_params;
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530440};
441
442/**
Amy Malocheeea7b592012-10-03 15:59:36 -0700443 * rgb_config_data - rgb configuration data
Amy Malochea2726f02013-05-10 10:19:03 -0700444 * @pwm_cfg - device pwm configuration
445 * @enable - bits to enable led
Amy Malocheeea7b592012-10-03 15:59:36 -0700446 */
447struct rgb_config_data {
Amy Malochea2726f02013-05-10 10:19:03 -0700448 struct pwm_config_data *pwm_cfg;
Amy Malocheeea7b592012-10-03 15:59:36 -0700449 u8 enable;
450};
451
452/**
Amy Malochef3d5a062012-08-16 19:14:11 -0700453 * struct qpnp_led_data - internal led data structure
454 * @led_classdev - led class device
Asaf Penso55ac8472013-01-21 21:17:37 +0200455 * @delayed_work - delayed work for turning off the LED
Chun Zhang815a1832013-06-20 13:47:13 -0700456 * @work - workqueue for led
Amy Malochef3d5a062012-08-16 19:14:11 -0700457 * @id - led index
458 * @base_reg - base register given in device tree
459 * @lock - to protect the transactions
460 * @reg - cached value of led register
Amy Malochea5ca5552012-10-23 13:34:46 -0700461 * @num_leds - number of leds in the module
Amy Malochef3d5a062012-08-16 19:14:11 -0700462 * @max_current - maximum current supported by LED
463 * @default_on - true: default state max, false, default state 0
Asaf Penso55ac8472013-01-21 21:17:37 +0200464 * @turn_off_delay_ms - number of msec before turning off the LED
Amy Malochef3d5a062012-08-16 19:14:11 -0700465 */
466struct qpnp_led_data {
467 struct led_classdev cdev;
468 struct spmi_device *spmi_dev;
Asaf Penso55ac8472013-01-21 21:17:37 +0200469 struct delayed_work dwork;
Chun Zhang815a1832013-06-20 13:47:13 -0700470 struct work_struct work;
Amy Malochef3d5a062012-08-16 19:14:11 -0700471 int id;
472 u16 base;
473 u8 reg;
Amy Malochea5ca5552012-10-23 13:34:46 -0700474 u8 num_leds;
Chun Zhang815a1832013-06-20 13:47:13 -0700475 struct mutex lock;
Amy Malochef3d5a062012-08-16 19:14:11 -0700476 struct wled_config_data *wled_cfg;
Amy Maloche864a6d52012-10-03 15:58:12 -0700477 struct flash_config_data *flash_cfg;
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530478 struct kpdbl_config_data *kpdbl_cfg;
Amy Malocheeea7b592012-10-03 15:59:36 -0700479 struct rgb_config_data *rgb_cfg;
Amy Malochef3813742013-04-11 19:33:47 -0700480 struct mpp_config_data *mpp_cfg;
Amy Malochef3d5a062012-08-16 19:14:11 -0700481 int max_current;
482 bool default_on;
Asaf Penso55ac8472013-01-21 21:17:37 +0200483 int turn_off_delay_ms;
Amy Malochef3d5a062012-08-16 19:14:11 -0700484};
485
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530486static int num_kpbl_leds_on;
Abinaya P9e5dc3e2014-04-14 15:38:11 +0530487static DEFINE_MUTEX(flash_lock);
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530488
Amy Malochef3d5a062012-08-16 19:14:11 -0700489static int
490qpnp_led_masked_write(struct qpnp_led_data *led, u16 addr, u8 mask, u8 val)
491{
492 int rc;
493 u8 reg;
494
495 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
496 addr, &reg, 1);
497 if (rc) {
498 dev_err(&led->spmi_dev->dev,
499 "Unable to read from addr=%x, rc(%d)\n", addr, rc);
500 }
501
502 reg &= ~mask;
503 reg |= val;
504
505 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
506 addr, &reg, 1);
507 if (rc)
508 dev_err(&led->spmi_dev->dev,
509 "Unable to write to addr=%x, rc(%d)\n", addr, rc);
510 return rc;
511}
512
Amy Malochea5ca5552012-10-23 13:34:46 -0700513static void qpnp_dump_regs(struct qpnp_led_data *led, u8 regs[], u8 array_size)
514{
515 int i;
516 u8 val;
517
518 pr_debug("===== %s LED register dump start =====\n", led->cdev.name);
519 for (i = 0; i < array_size; i++) {
520 spmi_ext_register_readl(led->spmi_dev->ctrl,
521 led->spmi_dev->sid,
522 led->base + regs[i],
523 &val, sizeof(val));
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530524 pr_debug("%s: 0x%x = 0x%x\n", led->cdev.name,
525 led->base + regs[i], val);
Amy Malochea5ca5552012-10-23 13:34:46 -0700526 }
527 pr_debug("===== %s LED register dump end =====\n", led->cdev.name);
528}
529
Amy Maloche0150b5e2013-08-15 18:18:32 -0700530static int qpnp_wled_sync(struct qpnp_led_data *led)
531{
532 int rc;
533 u8 val;
534
535 /* sync */
536 val = WLED_SYNC_VAL;
537 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
538 WLED_SYNC_REG(led->base), &val, 1);
539 if (rc) {
540 dev_err(&led->spmi_dev->dev,
541 "WLED set sync reg failed(%d)\n", rc);
542 return rc;
543 }
544
545 val = WLED_SYNC_RESET_VAL;
546 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
547 WLED_SYNC_REG(led->base), &val, 1);
548 if (rc) {
549 dev_err(&led->spmi_dev->dev,
550 "WLED reset sync reg failed(%d)\n", rc);
551 return rc;
552 }
553 return 0;
554}
555
Amy Malochef3d5a062012-08-16 19:14:11 -0700556static int qpnp_wled_set(struct qpnp_led_data *led)
557{
Amy Maloched55fdb82013-02-26 18:11:57 -0800558 int rc, duty, level;
Amy Maloche0150b5e2013-08-15 18:18:32 -0700559 u8 val, i, num_wled_strings, sink_val;
560
561 num_wled_strings = led->wled_cfg->num_strings;
Amy Malochef3d5a062012-08-16 19:14:11 -0700562
563 level = led->cdev.brightness;
564
565 if (level > WLED_MAX_LEVEL)
566 level = WLED_MAX_LEVEL;
567 if (level == 0) {
Amy Maloche0150b5e2013-08-15 18:18:32 -0700568 for (i = 0; i < num_wled_strings; i++) {
569 rc = qpnp_led_masked_write(led,
570 WLED_FULL_SCALE_REG(led->base, i),
571 WLED_MAX_CURR_MASK, WLED_NO_CURRENT);
572 if (rc) {
573 dev_err(&led->spmi_dev->dev,
574 "Write max current failure (%d)\n",
575 rc);
576 return rc;
577 }
578 }
579
580 rc = qpnp_wled_sync(led);
581 if (rc) {
582 dev_err(&led->spmi_dev->dev,
583 "WLED sync failed(%d)\n", rc);
584 return rc;
585 }
586
587 rc = spmi_ext_register_readl(led->spmi_dev->ctrl,
588 led->spmi_dev->sid, WLED_CURR_SINK_REG(led->base),
589 &sink_val, 1);
590 if (rc) {
591 dev_err(&led->spmi_dev->dev,
592 "WLED read sink reg failed(%d)\n", rc);
593 return rc;
594 }
595
596 if (led->wled_cfg->pmic_version == PMIC_VER_8026) {
597 val = WLED_DISABLE_ALL_SINKS;
598 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
599 led->spmi_dev->sid,
600 WLED_CURR_SINK_REG(led->base), &val, 1);
601 if (rc) {
602 dev_err(&led->spmi_dev->dev,
603 "WLED write sink reg failed(%d)\n", rc);
604 return rc;
605 }
606
607 usleep(WLED_OVP_DELAY);
608 }
609
Amy Malochef3d5a062012-08-16 19:14:11 -0700610 val = WLED_BOOST_OFF;
611 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
612 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
613 &val, 1);
614 if (rc) {
615 dev_err(&led->spmi_dev->dev,
616 "WLED write ctrl reg failed(%d)\n", rc);
617 return rc;
618 }
Amy Maloche0150b5e2013-08-15 18:18:32 -0700619
620 for (i = 0; i < num_wled_strings; i++) {
621 rc = qpnp_led_masked_write(led,
622 WLED_FULL_SCALE_REG(led->base, i),
Abinaya P03cee1d2014-02-17 17:51:53 +0530623 WLED_MAX_CURR_MASK, (u8)led->max_current);
Amy Maloche0150b5e2013-08-15 18:18:32 -0700624 if (rc) {
625 dev_err(&led->spmi_dev->dev,
626 "Write max current failure (%d)\n",
627 rc);
628 return rc;
629 }
630 }
631
632 rc = qpnp_wled_sync(led);
633 if (rc) {
634 dev_err(&led->spmi_dev->dev,
635 "WLED sync failed(%d)\n", rc);
636 return rc;
637 }
638
639 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
640 led->spmi_dev->sid, WLED_CURR_SINK_REG(led->base),
641 &sink_val, 1);
642 if (rc) {
643 dev_err(&led->spmi_dev->dev,
644 "WLED write sink reg failed(%d)\n", rc);
645 return rc;
646 }
647
Amy Malochef3d5a062012-08-16 19:14:11 -0700648 } else {
649 val = WLED_BOOST_ON;
650 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
651 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
652 &val, 1);
653 if (rc) {
654 dev_err(&led->spmi_dev->dev,
655 "WLED write ctrl reg failed(%d)\n", rc);
656 return rc;
657 }
658 }
659
660 duty = (WLED_MAX_DUTY_CYCLE * level) / WLED_MAX_LEVEL;
661
Amy Malochef3d5a062012-08-16 19:14:11 -0700662 /* program brightness control registers */
663 for (i = 0; i < num_wled_strings; i++) {
664 rc = qpnp_led_masked_write(led,
665 WLED_BRIGHTNESS_CNTL_MSB(led->base, i), WLED_MSB_MASK,
666 (duty >> WLED_8_BIT_SHFT) & WLED_4_BIT_MASK);
667 if (rc) {
668 dev_err(&led->spmi_dev->dev,
669 "WLED set brightness MSB failed(%d)\n", rc);
670 return rc;
671 }
672 val = duty & WLED_8_BIT_MASK;
673 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
674 led->spmi_dev->sid,
675 WLED_BRIGHTNESS_CNTL_LSB(led->base, i), &val, 1);
676 if (rc) {
677 dev_err(&led->spmi_dev->dev,
678 "WLED set brightness LSB failed(%d)\n", rc);
679 return rc;
680 }
681 }
682
Amy Maloche0150b5e2013-08-15 18:18:32 -0700683 rc = qpnp_wled_sync(led);
Amy Malochef3d5a062012-08-16 19:14:11 -0700684 if (rc) {
Amy Maloche0150b5e2013-08-15 18:18:32 -0700685 dev_err(&led->spmi_dev->dev, "WLED sync failed(%d)\n", rc);
Amy Malochef3d5a062012-08-16 19:14:11 -0700686 return rc;
687 }
688 return 0;
689}
690
Amy Malochef3813742013-04-11 19:33:47 -0700691static int qpnp_mpp_set(struct qpnp_led_data *led)
692{
Abinaya P03cee1d2014-02-17 17:51:53 +0530693 int rc;
694 u8 val;
Xu Kai9d395ce2014-02-28 13:11:09 +0800695 int duty_us, duty_ns, period_us;
Amy Malochef3813742013-04-11 19:33:47 -0700696
697 if (led->cdev.brightness) {
Chun Zhang874c9ab2013-07-08 17:18:34 -0700698 if (led->cdev.brightness < led->mpp_cfg->min_brightness) {
699 dev_warn(&led->spmi_dev->dev,
700 "brightness is less than supported..." \
701 "set to minimum supported\n");
702 led->cdev.brightness = led->mpp_cfg->min_brightness;
703 }
704
Amy Maloche4cf9f322013-05-29 15:53:46 -0700705 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
706 if (!led->mpp_cfg->pwm_cfg->blinking) {
707 led->mpp_cfg->pwm_cfg->mode =
708 led->mpp_cfg->pwm_cfg->default_mode;
709 led->mpp_cfg->pwm_mode =
710 led->mpp_cfg->pwm_cfg->default_mode;
711 }
712 }
Amy Malochea2726f02013-05-10 10:19:03 -0700713 if (led->mpp_cfg->pwm_mode == PWM_MODE) {
Amy Malochea2726f02013-05-10 10:19:03 -0700714 /*config pwm for brightness scaling*/
Xu Kai9d395ce2014-02-28 13:11:09 +0800715 period_us = led->mpp_cfg->pwm_cfg->pwm_period_us;
716 if (period_us > INT_MAX / NSEC_PER_USEC) {
717 duty_us = (period_us * led->cdev.brightness) /
718 LED_FULL;
719 rc = pwm_config_us(
720 led->mpp_cfg->pwm_cfg->pwm_dev,
Amy Malochea2726f02013-05-10 10:19:03 -0700721 duty_us,
Xu Kai9d395ce2014-02-28 13:11:09 +0800722 period_us);
723 } else {
724 duty_ns = ((period_us * NSEC_PER_USEC) /
725 LED_FULL) * led->cdev.brightness;
726 rc = pwm_config(
727 led->mpp_cfg->pwm_cfg->pwm_dev,
728 duty_ns,
729 period_us * NSEC_PER_USEC);
730 }
Amy Malochea2726f02013-05-10 10:19:03 -0700731 if (rc < 0) {
732 dev_err(&led->spmi_dev->dev, "Failed to " \
733 "configure pwm for new values\n");
734 return rc;
735 }
Amy Malochef3813742013-04-11 19:33:47 -0700736 }
737
Amy Malochea2726f02013-05-10 10:19:03 -0700738 if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
739 pwm_enable(led->mpp_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka38ec7f32013-09-11 13:01:48 +0530740 else {
741 if (led->cdev.brightness < LED_MPP_CURRENT_MIN)
742 led->cdev.brightness = LED_MPP_CURRENT_MIN;
743
744 val = (led->cdev.brightness / LED_MPP_CURRENT_MIN) - 1;
745
746 rc = qpnp_led_masked_write(led,
747 LED_MPP_SINK_CTRL(led->base),
748 LED_MPP_SINK_MASK, val);
749 if (rc) {
750 dev_err(&led->spmi_dev->dev,
751 "Failed to write sink control reg\n");
752 return rc;
753 }
754 }
Amy Malochea2726f02013-05-10 10:19:03 -0700755
Amy Malochece59f662013-05-02 10:59:53 -0700756 val = (led->mpp_cfg->source_sel & LED_MPP_SRC_MASK) |
757 (led->mpp_cfg->mode_ctrl & LED_MPP_MODE_CTRL_MASK);
Amy Malochef3813742013-04-11 19:33:47 -0700758
759 rc = qpnp_led_masked_write(led,
Amy Malochea2726f02013-05-10 10:19:03 -0700760 LED_MPP_MODE_CTRL(led->base), LED_MPP_MODE_MASK,
761 val);
Amy Malochef3813742013-04-11 19:33:47 -0700762 if (rc) {
763 dev_err(&led->spmi_dev->dev,
764 "Failed to write led mode reg\n");
765 return rc;
766 }
767
768 rc = qpnp_led_masked_write(led,
769 LED_MPP_EN_CTRL(led->base), LED_MPP_EN_MASK,
770 LED_MPP_EN_ENABLE);
Amy Malochea2726f02013-05-10 10:19:03 -0700771 if (rc) {
772 dev_err(&led->spmi_dev->dev,
773 "Failed to write led enable " \
774 "reg\n");
775 return rc;
776 }
Amy Malochef3813742013-04-11 19:33:47 -0700777 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -0700778 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
779 led->mpp_cfg->pwm_cfg->mode =
780 led->mpp_cfg->pwm_cfg->default_mode;
781 led->mpp_cfg->pwm_mode =
782 led->mpp_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -0700783 pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
Amy Maloche4cf9f322013-05-29 15:53:46 -0700784 }
Amy Malochef3813742013-04-11 19:33:47 -0700785 rc = qpnp_led_masked_write(led,
786 LED_MPP_MODE_CTRL(led->base),
787 LED_MPP_MODE_MASK,
788 LED_MPP_MODE_DISABLE);
789 if (rc) {
790 dev_err(&led->spmi_dev->dev,
791 "Failed to write led mode reg\n");
792 return rc;
793 }
794
795 rc = qpnp_led_masked_write(led,
796 LED_MPP_EN_CTRL(led->base),
797 LED_MPP_EN_MASK,
798 LED_MPP_EN_DISABLE);
799 if (rc) {
800 dev_err(&led->spmi_dev->dev,
801 "Failed to write led enable reg\n");
802 return rc;
803 }
804 }
805
Amy Maloche4cf9f322013-05-29 15:53:46 -0700806 if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
807 led->mpp_cfg->pwm_cfg->blinking = false;
Amy Malochef3813742013-04-11 19:33:47 -0700808 qpnp_dump_regs(led, mpp_debug_regs, ARRAY_SIZE(mpp_debug_regs));
809
810 return 0;
811}
812
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700813static int qpnp_flash_regulator_operate(struct qpnp_led_data *led, bool on)
814{
815 int rc, i;
816 struct qpnp_led_data *led_array;
817 bool regulator_on = false;
818
819 led_array = dev_get_drvdata(&led->spmi_dev->dev);
820 if (!led_array) {
821 dev_err(&led->spmi_dev->dev,
822 "Unable to get LED array\n");
823 return -EINVAL;
824 }
825
826 for (i = 0; i < led->num_leds; i++)
827 regulator_on |= led_array[i].flash_cfg->flash_on;
828
829 if (!on)
830 goto regulator_turn_off;
831
832 if (!regulator_on && !led->flash_cfg->flash_on) {
833 for (i = 0; i < led->num_leds; i++) {
834 if (led_array[i].flash_cfg->flash_reg_get) {
835 rc = regulator_enable(
836 led_array[i].flash_cfg->\
837 flash_boost_reg);
838 if (rc) {
839 dev_err(&led->spmi_dev->dev,
840 "Regulator enable failed(%d)\n",
841 rc);
842 return rc;
843 }
844 led->flash_cfg->flash_on = true;
845 }
846 break;
847 }
848 }
849
850 return 0;
851
852regulator_turn_off:
853 if (regulator_on && led->flash_cfg->flash_on) {
854 for (i = 0; i < led->num_leds; i++) {
855 if (led_array[i].flash_cfg->flash_reg_get) {
Chun Zhang9d5ff672013-08-01 18:18:26 -0700856 rc = qpnp_led_masked_write(led,
857 FLASH_ENABLE_CONTROL(led->base),
858 FLASH_ENABLE_MASK,
859 FLASH_DISABLE_ALL);
860 if (rc) {
861 dev_err(&led->spmi_dev->dev,
862 "Enable reg write failed(%d)\n",
863 rc);
864 }
865
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700866 rc = regulator_disable(led_array[i].flash_cfg->\
867 flash_boost_reg);
868 if (rc) {
869 dev_err(&led->spmi_dev->dev,
870 "Regulator disable failed(%d)\n",
871 rc);
872 return rc;
873 }
874 led->flash_cfg->flash_on = false;
875 }
876 break;
877 }
878 }
879
880 return 0;
881}
882
Chun Zhangdf2d3062013-06-25 20:14:46 -0700883static int qpnp_torch_regulator_operate(struct qpnp_led_data *led, bool on)
Amy Maloche864a6d52012-10-03 15:58:12 -0700884{
885 int rc;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700886
887 if (!on)
888 goto regulator_turn_off;
889
890 if (!led->flash_cfg->torch_on) {
891 rc = regulator_enable(led->flash_cfg->torch_boost_reg);
892 if (rc) {
893 dev_err(&led->spmi_dev->dev,
894 "Regulator enable failed(%d)\n", rc);
895 return rc;
896 }
897 led->flash_cfg->torch_on = true;
898 }
899 return 0;
900
901regulator_turn_off:
902 if (led->flash_cfg->torch_on) {
Chun Zhang9d5ff672013-08-01 18:18:26 -0700903 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
904 FLASH_ENABLE_MODULE_MASK, FLASH_DISABLE_ALL);
905 if (rc) {
906 dev_err(&led->spmi_dev->dev,
907 "Enable reg write failed(%d)\n", rc);
908 }
909
Chun Zhangdf2d3062013-06-25 20:14:46 -0700910 rc = regulator_disable(led->flash_cfg->torch_boost_reg);
911 if (rc) {
912 dev_err(&led->spmi_dev->dev,
913 "Regulator disable failed(%d)\n", rc);
914 return rc;
915 }
916 led->flash_cfg->torch_on = false;
917 }
918 return 0;
919}
920
921static int qpnp_flash_set(struct qpnp_led_data *led)
922{
923 int rc, error;
Amy Maloche864a6d52012-10-03 15:58:12 -0700924 int val = led->cdev.brightness;
925
Chun Zhang9c1fbaa2013-06-17 15:31:18 -0700926 if (led->flash_cfg->torch_enable)
927 led->flash_cfg->current_prgm =
928 (val * TORCH_MAX_LEVEL / led->max_current);
929 else
930 led->flash_cfg->current_prgm =
931 (val * FLASH_MAX_LEVEL / led->max_current);
Amy Maloche864a6d52012-10-03 15:58:12 -0700932
Amy Maloche864a6d52012-10-03 15:58:12 -0700933 /* Set led current */
934 if (val > 0) {
Amy Malochebc97c0d22013-03-24 22:06:16 -0700935 if (led->flash_cfg->torch_enable) {
Chun Zhang0d6ca072013-07-30 21:08:39 -0700936 if (led->flash_cfg->peripheral_subtype ==
937 FLASH_SUBTYPE_DUAL) {
938 rc = qpnp_torch_regulator_operate(led, true);
939 if (rc) {
940 dev_err(&led->spmi_dev->dev,
Chun Zhangdf2d3062013-06-25 20:14:46 -0700941 "Torch regulator operate failed(%d)\n",
942 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700943 return rc;
944 }
945 } else if (led->flash_cfg->peripheral_subtype ==
946 FLASH_SUBTYPE_SINGLE) {
947 rc = qpnp_flash_regulator_operate(led, true);
948 if (rc) {
949 dev_err(&led->spmi_dev->dev,
950 "Flash regulator operate failed(%d)\n",
951 rc);
952 goto error_flash_set;
953 }
Chun Zhangdf2d3062013-06-25 20:14:46 -0700954 }
955
Amy Malochebc97c0d22013-03-24 22:06:16 -0700956 rc = qpnp_led_masked_write(led,
957 FLASH_LED_UNLOCK_SECURE(led->base),
958 FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
959 if (rc) {
960 dev_err(&led->spmi_dev->dev,
961 "Secure reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700962 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700963 }
964
965 rc = qpnp_led_masked_write(led,
966 FLASH_LED_TORCH(led->base),
967 FLASH_TORCH_MASK, FLASH_LED_TORCH_ENABLE);
968 if (rc) {
969 dev_err(&led->spmi_dev->dev,
970 "Torch reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700971 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700972 }
973
Amy Malochebc97c0d22013-03-24 22:06:16 -0700974 rc = qpnp_led_masked_write(led,
975 led->flash_cfg->current_addr,
Chun Zhange8954cf2013-05-02 11:14:34 -0700976 FLASH_CURRENT_MASK,
977 led->flash_cfg->current_prgm);
Amy Malochebc97c0d22013-03-24 22:06:16 -0700978 if (rc) {
979 dev_err(&led->spmi_dev->dev,
980 "Current reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700981 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700982 }
983
984 rc = qpnp_led_masked_write(led,
985 led->flash_cfg->second_addr,
Chun Zhange8954cf2013-05-02 11:14:34 -0700986 FLASH_CURRENT_MASK,
987 led->flash_cfg->current_prgm);
Amy Malochebc97c0d22013-03-24 22:06:16 -0700988 if (rc) {
989 dev_err(&led->spmi_dev->dev,
990 "2nd Current reg write failed(%d)\n",
991 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700992 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700993 }
994
Chun Zhange8954cf2013-05-02 11:14:34 -0700995 qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
996 FLASH_CURRENT_MASK,
Chun Zhang9c1fbaa2013-06-17 15:31:18 -0700997 TORCH_MAX_LEVEL);
Chun Zhange8954cf2013-05-02 11:14:34 -0700998 if (rc) {
999 dev_err(&led->spmi_dev->dev,
1000 "Max current reg write failed(%d)\n",
1001 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -07001002 goto error_reg_write;
Chun Zhange8954cf2013-05-02 11:14:34 -07001003 }
1004
Chun Zhang9d5ff672013-08-01 18:18:26 -07001005 rc = qpnp_led_masked_write(led,
1006 FLASH_ENABLE_CONTROL(led->base),
1007 FLASH_ENABLE_MASK,
1008 led->flash_cfg->enable_module);
1009 if (rc) {
1010 dev_err(&led->spmi_dev->dev,
1011 "Enable reg write failed(%d)\n",
1012 rc);
1013 goto error_reg_write;
1014 }
1015
Abinaya Pb6e3ba42014-01-07 15:32:31 +05301016 if (!led->flash_cfg->strobe_type)
1017 led->flash_cfg->trigger_flash &=
1018 ~FLASH_HW_SW_STROBE_SEL_MASK;
1019 else
1020 led->flash_cfg->trigger_flash |=
1021 FLASH_HW_SW_STROBE_SEL_MASK;
1022
Chun Zhang9d5ff672013-08-01 18:18:26 -07001023 rc = qpnp_led_masked_write(led,
1024 FLASH_LED_STROBE_CTRL(led->base),
1025 led->flash_cfg->trigger_flash,
1026 led->flash_cfg->trigger_flash);
1027 if (rc) {
1028 dev_err(&led->spmi_dev->dev,
1029 "LED %d strobe reg write failed(%d)\n",
1030 led->id, rc);
1031 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001032 }
1033 } else {
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001034 rc = qpnp_flash_regulator_operate(led, true);
1035 if (rc) {
1036 dev_err(&led->spmi_dev->dev,
1037 "Flash regulator operate failed(%d)\n",
1038 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001039 goto error_flash_set;
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001040 }
1041
Chun Zhange8954cf2013-05-02 11:14:34 -07001042 /* Set flash safety timer */
Amy Malochebc97c0d22013-03-24 22:06:16 -07001043 rc = qpnp_led_masked_write(led,
Chun Zhange8954cf2013-05-02 11:14:34 -07001044 FLASH_SAFETY_TIMER(led->base),
1045 FLASH_SAFETY_TIMER_MASK,
1046 led->flash_cfg->duration);
1047 if (rc) {
1048 dev_err(&led->spmi_dev->dev,
1049 "Safety timer reg write failed(%d)\n",
1050 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001051 goto error_flash_set;
Chun Zhange8954cf2013-05-02 11:14:34 -07001052 }
1053
1054 /* Set max current */
1055 rc = qpnp_led_masked_write(led,
1056 FLASH_MAX_CURR(led->base), FLASH_CURRENT_MASK,
1057 FLASH_MAX_LEVEL);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001058 if (rc) {
1059 dev_err(&led->spmi_dev->dev,
1060 "Max current reg write failed(%d)\n",
1061 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001062 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001063 }
1064
Chun Zhange8954cf2013-05-02 11:14:34 -07001065 /* Set clamp current */
1066 rc = qpnp_led_masked_write(led,
1067 FLASH_CLAMP_CURR(led->base),
1068 FLASH_CURRENT_MASK,
1069 led->flash_cfg->clamp_curr);
1070 if (rc) {
1071 dev_err(&led->spmi_dev->dev,
1072 "Clamp current reg write failed(%d)\n",
1073 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001074 goto error_flash_set;
Chun Zhange8954cf2013-05-02 11:14:34 -07001075 }
1076
Amy Malochebc97c0d22013-03-24 22:06:16 -07001077 rc = qpnp_led_masked_write(led,
1078 led->flash_cfg->current_addr,
1079 FLASH_CURRENT_MASK,
1080 led->flash_cfg->current_prgm);
1081 if (rc) {
1082 dev_err(&led->spmi_dev->dev,
1083 "Current reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001084 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001085 }
1086
1087 rc = qpnp_led_masked_write(led,
Amy Malochebc97c0d22013-03-24 22:06:16 -07001088 FLASH_ENABLE_CONTROL(led->base),
Chun Zhang9d5ff672013-08-01 18:18:26 -07001089 led->flash_cfg->enable_module,
1090 led->flash_cfg->enable_module);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001091 if (rc) {
1092 dev_err(&led->spmi_dev->dev,
1093 "Enable reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001094 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001095 }
Amy Maloche38b9aae2013-02-07 12:15:34 -08001096
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07001097 /*
1098 * Add 1ms delay for bharger enter stable state
1099 */
1100 usleep(FLASH_RAMP_UP_DELAY_US);
1101
Abinaya Pb6e3ba42014-01-07 15:32:31 +05301102 if (!led->flash_cfg->strobe_type)
1103 led->flash_cfg->trigger_flash &=
1104 ~FLASH_HW_SW_STROBE_SEL_MASK;
1105 else
1106 led->flash_cfg->trigger_flash |=
1107 FLASH_HW_SW_STROBE_SEL_MASK;
1108
1109 rc = qpnp_led_masked_write(led,
1110 FLASH_LED_STROBE_CTRL(led->base),
1111 led->flash_cfg->trigger_flash,
1112 led->flash_cfg->trigger_flash);
1113 if (rc) {
1114 dev_err(&led->spmi_dev->dev,
1115 "LED %d strobe reg write failed(%d)\n",
1116 led->id, rc);
1117 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001118 }
Amy Maloche38b9aae2013-02-07 12:15:34 -08001119 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001120 } else {
Chun Zhangdf2d3062013-06-25 20:14:46 -07001121 rc = qpnp_led_masked_write(led,
1122 FLASH_LED_STROBE_CTRL(led->base),
Chun Zhang9d5ff672013-08-01 18:18:26 -07001123 led->flash_cfg->trigger_flash,
Chun Zhangdf2d3062013-06-25 20:14:46 -07001124 FLASH_DISABLE_ALL);
1125 if (rc) {
1126 dev_err(&led->spmi_dev->dev,
1127 "LED %d flash write failed(%d)\n", led->id, rc);
1128 if (led->flash_cfg->torch_enable)
1129 goto error_torch_set;
1130 else
1131 goto error_flash_set;
1132 }
1133
Amy Malochebc97c0d22013-03-24 22:06:16 -07001134 if (led->flash_cfg->torch_enable) {
1135 rc = qpnp_led_masked_write(led,
1136 FLASH_LED_UNLOCK_SECURE(led->base),
1137 FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
1138 if (rc) {
1139 dev_err(&led->spmi_dev->dev,
1140 "Secure reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001141 goto error_torch_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001142 }
1143
1144 rc = qpnp_led_masked_write(led,
Chun Zhange8954cf2013-05-02 11:14:34 -07001145 FLASH_LED_TORCH(led->base),
1146 FLASH_TORCH_MASK,
1147 FLASH_LED_TORCH_DISABLE);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001148 if (rc) {
1149 dev_err(&led->spmi_dev->dev,
Chun Zhange8954cf2013-05-02 11:14:34 -07001150 "Torch reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001151 goto error_torch_set;
1152 }
1153
Chun Zhang0d6ca072013-07-30 21:08:39 -07001154 if (led->flash_cfg->peripheral_subtype ==
1155 FLASH_SUBTYPE_DUAL) {
1156 rc = qpnp_torch_regulator_operate(led, false);
1157 if (rc) {
1158 dev_err(&led->spmi_dev->dev,
1159 "Torch regulator operate failed(%d)\n",
1160 rc);
1161 return rc;
1162 }
1163 } else if (led->flash_cfg->peripheral_subtype ==
1164 FLASH_SUBTYPE_SINGLE) {
1165 rc = qpnp_flash_regulator_operate(led, false);
1166 if (rc) {
1167 dev_err(&led->spmi_dev->dev,
1168 "Flash regulator operate failed(%d)\n",
1169 rc);
1170 return rc;
1171 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07001172 }
Chun Zhangdf2d3062013-06-25 20:14:46 -07001173 } else {
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07001174 /*
1175 * Disable module after ramp down complete for stable
1176 * behavior
1177 */
1178 usleep(FLASH_RAMP_DN_DELAY_US);
1179
Chun Zhang9d5ff672013-08-01 18:18:26 -07001180 rc = qpnp_led_masked_write(led,
1181 FLASH_ENABLE_CONTROL(led->base),
1182 led->flash_cfg->enable_module &
1183 ~FLASH_ENABLE_MODULE_MASK,
1184 FLASH_DISABLE_ALL);
1185 if (rc) {
1186 dev_err(&led->spmi_dev->dev,
1187 "Enable reg write failed(%d)\n", rc);
1188 if (led->flash_cfg->torch_enable)
1189 goto error_torch_set;
1190 else
1191 goto error_flash_set;
1192 }
1193
Chun Zhangdf2d3062013-06-25 20:14:46 -07001194 rc = qpnp_flash_regulator_operate(led, false);
1195 if (rc) {
1196 dev_err(&led->spmi_dev->dev,
1197 "Flash regulator operate failed(%d)\n",
1198 rc);
1199 return rc;
1200 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001201 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001202 }
1203
Amy Malocheeea7b592012-10-03 15:59:36 -07001204 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
1205
1206 return 0;
Chun Zhangdf2d3062013-06-25 20:14:46 -07001207
Chun Zhang0d6ca072013-07-30 21:08:39 -07001208error_reg_write:
1209 if (led->flash_cfg->peripheral_subtype == FLASH_SUBTYPE_SINGLE)
1210 goto error_flash_set;
1211
Chun Zhangdf2d3062013-06-25 20:14:46 -07001212error_torch_set:
1213 error = qpnp_torch_regulator_operate(led, false);
1214 if (error) {
1215 dev_err(&led->spmi_dev->dev,
1216 "Torch regulator operate failed(%d)\n", rc);
1217 return error;
1218 }
1219 return rc;
1220
1221error_flash_set:
1222 error = qpnp_flash_regulator_operate(led, false);
1223 if (error) {
1224 dev_err(&led->spmi_dev->dev,
1225 "Flash regulator operate failed(%d)\n", rc);
1226 return error;
1227 }
1228 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07001229}
1230
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301231static int qpnp_kpdbl_set(struct qpnp_led_data *led)
1232{
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301233 int rc;
Xu Kai9d395ce2014-02-28 13:11:09 +08001234 int duty_us, duty_ns, period_us;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301235
1236 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001237 if (!led->kpdbl_cfg->pwm_cfg->blinking)
1238 led->kpdbl_cfg->pwm_cfg->mode =
1239 led->kpdbl_cfg->pwm_cfg->default_mode;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301240 if (!num_kpbl_leds_on) {
1241 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
1242 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
1243 if (rc) {
1244 dev_err(&led->spmi_dev->dev,
1245 "Enable reg write failed(%d)\n", rc);
1246 return rc;
1247 }
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301248 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301249
1250 if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
Xu Kai9d395ce2014-02-28 13:11:09 +08001251 period_us = led->kpdbl_cfg->pwm_cfg->pwm_period_us;
1252 if (period_us > INT_MAX / NSEC_PER_USEC) {
1253 duty_us = (period_us * led->cdev.brightness) /
1254 KPDBL_MAX_LEVEL;
1255 rc = pwm_config_us(
1256 led->kpdbl_cfg->pwm_cfg->pwm_dev,
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301257 duty_us,
Xu Kai9d395ce2014-02-28 13:11:09 +08001258 period_us);
1259 } else {
1260 duty_ns = ((period_us * NSEC_PER_USEC) /
1261 KPDBL_MAX_LEVEL) * led->cdev.brightness;
1262 rc = pwm_config(
1263 led->kpdbl_cfg->pwm_cfg->pwm_dev,
1264 duty_ns,
1265 period_us * NSEC_PER_USEC);
1266 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301267 if (rc < 0) {
1268 dev_err(&led->spmi_dev->dev, "pwm config failed\n");
1269 return rc;
1270 }
1271 }
1272
Amy Malochea2726f02013-05-10 10:19:03 -07001273 rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301274 if (rc < 0) {
1275 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1276 return rc;
1277 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301278
1279 num_kpbl_leds_on++;
1280
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301281 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001282 led->kpdbl_cfg->pwm_cfg->mode =
1283 led->kpdbl_cfg->pwm_cfg->default_mode;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301284
1285 if (led->kpdbl_cfg->always_on) {
Xu Kai0d9a3e22013-12-17 13:53:56 +08001286 rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0,
Xu Kai9d395ce2014-02-28 13:11:09 +08001287 led->kpdbl_cfg->pwm_cfg->pwm_period_us);
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301288 if (rc < 0) {
1289 dev_err(&led->spmi_dev->dev,
1290 "pwm config failed\n");
1291 return rc;
1292 }
1293
1294 rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
1295 if (rc < 0) {
1296 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1297 return rc;
1298 }
1299 } else
1300 pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
1301
1302 if (num_kpbl_leds_on > 0)
1303 num_kpbl_leds_on--;
1304
1305 if (!num_kpbl_leds_on) {
1306 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
1307 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_DIS);
1308 if (rc) {
1309 dev_err(&led->spmi_dev->dev,
1310 "Failed to write led enable reg\n");
1311 return rc;
1312 }
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301313 }
1314 }
1315
Amy Maloche4cf9f322013-05-29 15:53:46 -07001316 led->kpdbl_cfg->pwm_cfg->blinking = false;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301317
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301318 qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
1319
1320 return 0;
1321}
1322
Amy Malocheeea7b592012-10-03 15:59:36 -07001323static int qpnp_rgb_set(struct qpnp_led_data *led)
1324{
Amy Malocheeea7b592012-10-03 15:59:36 -07001325 int rc;
Xu Kai9d395ce2014-02-28 13:11:09 +08001326 int duty_us, duty_ns, period_us;
Amy Malocheeea7b592012-10-03 15:59:36 -07001327
1328 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001329 if (!led->rgb_cfg->pwm_cfg->blinking)
1330 led->rgb_cfg->pwm_cfg->mode =
1331 led->rgb_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07001332 if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
Xu Kai9d395ce2014-02-28 13:11:09 +08001333 period_us = led->rgb_cfg->pwm_cfg->pwm_period_us;
1334 if (period_us > INT_MAX / NSEC_PER_USEC) {
1335 duty_us = (period_us * led->cdev.brightness) /
1336 LED_FULL;
1337 rc = pwm_config_us(
1338 led->rgb_cfg->pwm_cfg->pwm_dev,
Xu Kai0d9a3e22013-12-17 13:53:56 +08001339 duty_us,
Xu Kai9d395ce2014-02-28 13:11:09 +08001340 period_us);
1341 } else {
1342 duty_ns = ((period_us * NSEC_PER_USEC) /
1343 LED_FULL) * led->cdev.brightness;
1344 rc = pwm_config(
1345 led->rgb_cfg->pwm_cfg->pwm_dev,
1346 duty_ns,
1347 period_us * NSEC_PER_USEC);
1348 }
Amy Malocheeea7b592012-10-03 15:59:36 -07001349 if (rc < 0) {
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301350 dev_err(&led->spmi_dev->dev,
1351 "pwm config failed\n");
Amy Malocheeea7b592012-10-03 15:59:36 -07001352 return rc;
1353 }
1354 }
1355 rc = qpnp_led_masked_write(led,
1356 RGB_LED_EN_CTL(led->base),
1357 led->rgb_cfg->enable, led->rgb_cfg->enable);
1358 if (rc) {
1359 dev_err(&led->spmi_dev->dev,
1360 "Failed to write led enable reg\n");
1361 return rc;
1362 }
Amy Malochea2726f02013-05-10 10:19:03 -07001363
1364 rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301365 if (rc < 0) {
1366 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1367 return rc;
1368 }
Amy Malocheeea7b592012-10-03 15:59:36 -07001369 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001370 led->rgb_cfg->pwm_cfg->mode =
1371 led->rgb_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07001372 pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
Amy Malocheeea7b592012-10-03 15:59:36 -07001373 rc = qpnp_led_masked_write(led,
1374 RGB_LED_EN_CTL(led->base),
1375 led->rgb_cfg->enable, RGB_LED_DISABLE);
1376 if (rc) {
1377 dev_err(&led->spmi_dev->dev,
1378 "Failed to write led enable reg\n");
1379 return rc;
1380 }
1381 }
1382
Amy Maloche4cf9f322013-05-29 15:53:46 -07001383 led->rgb_cfg->pwm_cfg->blinking = false;
Amy Malocheeea7b592012-10-03 15:59:36 -07001384 qpnp_dump_regs(led, rgb_pwm_debug_regs, ARRAY_SIZE(rgb_pwm_debug_regs));
1385
Amy Maloche864a6d52012-10-03 15:58:12 -07001386 return 0;
1387}
1388
Amy Malochef3d5a062012-08-16 19:14:11 -07001389static void qpnp_led_set(struct led_classdev *led_cdev,
1390 enum led_brightness value)
1391{
Amy Malochef3d5a062012-08-16 19:14:11 -07001392 struct qpnp_led_data *led;
1393
1394 led = container_of(led_cdev, struct qpnp_led_data, cdev);
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301395 if (value < LED_OFF) {
Amy Malochea5ca5552012-10-23 13:34:46 -07001396 dev_err(&led->spmi_dev->dev, "Invalid brightness value\n");
Amy Malochef3d5a062012-08-16 19:14:11 -07001397 return;
1398 }
1399
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301400 if (value > led->cdev.max_brightness)
1401 value = led->cdev.max_brightness;
1402
Chun Zhang815a1832013-06-20 13:47:13 -07001403 led->cdev.brightness = value;
1404 schedule_work(&led->work);
1405}
1406
1407static void __qpnp_led_work(struct qpnp_led_data *led,
1408 enum led_brightness value)
1409{
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001410 int rc;
Chun Zhangc3b505b2013-06-03 19:01:49 -07001411
Abinaya P9e5dc3e2014-04-14 15:38:11 +05301412 if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1)
1413 mutex_lock(&flash_lock);
1414 else
1415 mutex_lock(&led->lock);
Amy Malochef3d5a062012-08-16 19:14:11 -07001416
1417 switch (led->id) {
1418 case QPNP_ID_WLED:
1419 rc = qpnp_wled_set(led);
1420 if (rc < 0)
Amy Malochea5ca5552012-10-23 13:34:46 -07001421 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -07001422 "WLED set brightness failed (%d)\n", rc);
1423 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07001424 case QPNP_ID_FLASH1_LED0:
1425 case QPNP_ID_FLASH1_LED1:
1426 rc = qpnp_flash_set(led);
1427 if (rc < 0)
1428 dev_err(&led->spmi_dev->dev,
1429 "FLASH set brightness failed (%d)\n", rc);
1430 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07001431 case QPNP_ID_RGB_RED:
1432 case QPNP_ID_RGB_GREEN:
1433 case QPNP_ID_RGB_BLUE:
1434 rc = qpnp_rgb_set(led);
1435 if (rc < 0)
1436 dev_err(&led->spmi_dev->dev,
1437 "RGB set brightness failed (%d)\n", rc);
1438 break;
Amy Malochef3813742013-04-11 19:33:47 -07001439 case QPNP_ID_LED_MPP:
1440 rc = qpnp_mpp_set(led);
1441 if (rc < 0)
1442 dev_err(&led->spmi_dev->dev,
1443 "MPP set brightness failed (%d)\n", rc);
Amy Maloche14c9eb32013-05-06 13:37:58 -07001444 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301445 case QPNP_ID_KPDBL:
1446 rc = qpnp_kpdbl_set(led);
1447 if (rc < 0)
1448 dev_err(&led->spmi_dev->dev,
1449 "KPDBL set brightness failed (%d)\n", rc);
Amy Malochef3813742013-04-11 19:33:47 -07001450 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07001451 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07001452 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -07001453 break;
1454 }
Abinaya P9e5dc3e2014-04-14 15:38:11 +05301455 if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1)
1456 mutex_unlock(&flash_lock);
1457 else
1458 mutex_unlock(&led->lock);
Chun Zhangc3b505b2013-06-03 19:01:49 -07001459
Amy Malochef3d5a062012-08-16 19:14:11 -07001460}
1461
Chun Zhang815a1832013-06-20 13:47:13 -07001462static void qpnp_led_work(struct work_struct *work)
1463{
1464 struct qpnp_led_data *led = container_of(work,
1465 struct qpnp_led_data, work);
1466
1467 __qpnp_led_work(led, led->cdev.brightness);
1468
1469 return;
1470}
1471
Amy Malochef3d5a062012-08-16 19:14:11 -07001472static int __devinit qpnp_led_set_max_brightness(struct qpnp_led_data *led)
1473{
1474 switch (led->id) {
1475 case QPNP_ID_WLED:
1476 led->cdev.max_brightness = WLED_MAX_LEVEL;
1477 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07001478 case QPNP_ID_FLASH1_LED0:
1479 case QPNP_ID_FLASH1_LED1:
1480 led->cdev.max_brightness = led->max_current;
1481 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07001482 case QPNP_ID_RGB_RED:
1483 case QPNP_ID_RGB_GREEN:
1484 case QPNP_ID_RGB_BLUE:
1485 led->cdev.max_brightness = RGB_MAX_LEVEL;
1486 break;
Amy Malochef3813742013-04-11 19:33:47 -07001487 case QPNP_ID_LED_MPP:
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05301488 if (led->mpp_cfg->pwm_mode == MANUAL_MODE)
1489 led->cdev.max_brightness = led->max_current;
1490 else
1491 led->cdev.max_brightness = MPP_MAX_LEVEL;
Amy Malochef3813742013-04-11 19:33:47 -07001492 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301493 case QPNP_ID_KPDBL:
1494 led->cdev.max_brightness = KPDBL_MAX_LEVEL;
1495 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07001496 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07001497 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -07001498 return -EINVAL;
1499 }
1500
1501 return 0;
1502}
1503
1504static enum led_brightness qpnp_led_get(struct led_classdev *led_cdev)
1505{
1506 struct qpnp_led_data *led;
1507
1508 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1509
1510 return led->cdev.brightness;
1511}
1512
Asaf Penso55ac8472013-01-21 21:17:37 +02001513static void qpnp_led_turn_off_delayed(struct work_struct *work)
1514{
1515 struct delayed_work *dwork = to_delayed_work(work);
1516 struct qpnp_led_data *led
1517 = container_of(dwork, struct qpnp_led_data, dwork);
1518
1519 led->cdev.brightness = LED_OFF;
1520 qpnp_led_set(&led->cdev, led->cdev.brightness);
1521}
1522
1523static void qpnp_led_turn_off(struct qpnp_led_data *led)
1524{
1525 INIT_DELAYED_WORK(&led->dwork, qpnp_led_turn_off_delayed);
1526 schedule_delayed_work(&led->dwork,
1527 msecs_to_jiffies(led->turn_off_delay_ms));
1528}
1529
Amy Malochef3d5a062012-08-16 19:14:11 -07001530static int __devinit qpnp_wled_init(struct qpnp_led_data *led)
1531{
1532 int rc, i;
1533 u8 num_wled_strings;
1534
1535 num_wled_strings = led->wled_cfg->num_strings;
1536
1537 /* verify ranges */
Abinaya Pe65d2cd2013-12-23 19:31:57 +05301538 if (led->wled_cfg->ovp_val > WLED_OVP_27V) {
Amy Malochef3d5a062012-08-16 19:14:11 -07001539 dev_err(&led->spmi_dev->dev, "Invalid ovp value\n");
1540 return -EINVAL;
1541 }
1542
1543 if (led->wled_cfg->boost_curr_lim > WLED_CURR_LIMIT_1680mA) {
1544 dev_err(&led->spmi_dev->dev, "Invalid boost current limit\n");
1545 return -EINVAL;
1546 }
1547
1548 if (led->wled_cfg->cp_select > WLED_CP_SELECT_MAX) {
1549 dev_err(&led->spmi_dev->dev, "Invalid pole capacitance\n");
1550 return -EINVAL;
1551 }
1552
1553 if ((led->max_current > WLED_MAX_CURR)) {
1554 dev_err(&led->spmi_dev->dev, "Invalid max current\n");
1555 return -EINVAL;
1556 }
1557
1558 if ((led->wled_cfg->ctrl_delay_us % WLED_CTL_DLY_STEP) ||
1559 (led->wled_cfg->ctrl_delay_us > WLED_CTL_DLY_MAX)) {
1560 dev_err(&led->spmi_dev->dev, "Invalid control delay\n");
1561 return -EINVAL;
1562 }
1563
1564 /* program over voltage protection threshold */
1565 rc = qpnp_led_masked_write(led, WLED_OVP_CFG_REG(led->base),
1566 WLED_OVP_VAL_MASK,
1567 (led->wled_cfg->ovp_val << WLED_OVP_VAL_BIT_SHFT));
1568 if (rc) {
1569 dev_err(&led->spmi_dev->dev,
1570 "WLED OVP reg write failed(%d)\n", rc);
1571 return rc;
1572 }
1573
1574 /* program current boost limit */
1575 rc = qpnp_led_masked_write(led, WLED_BOOST_LIMIT_REG(led->base),
1576 WLED_BOOST_LIMIT_MASK, led->wled_cfg->boost_curr_lim);
1577 if (rc) {
1578 dev_err(&led->spmi_dev->dev,
1579 "WLED boost limit reg write failed(%d)\n", rc);
1580 return rc;
1581 }
1582
1583 /* program output feedback */
1584 rc = qpnp_led_masked_write(led, WLED_FDBCK_CTRL_REG(led->base),
1585 WLED_OP_FDBCK_MASK,
1586 (led->wled_cfg->op_fdbck << WLED_OP_FDBCK_BIT_SHFT));
1587 if (rc) {
1588 dev_err(&led->spmi_dev->dev,
1589 "WLED fdbck ctrl reg write failed(%d)\n", rc);
1590 return rc;
1591 }
1592
1593 /* program switch frequency */
Amy Maloche9eccb4c2013-07-12 14:31:56 -07001594 rc = qpnp_led_masked_write(led,
1595 WLED_SWITCHING_FREQ_REG(led->base),
Amy Malochef3d5a062012-08-16 19:14:11 -07001596 WLED_SWITCH_FREQ_MASK, led->wled_cfg->switch_freq);
1597 if (rc) {
1598 dev_err(&led->spmi_dev->dev,
Amy Maloche9eccb4c2013-07-12 14:31:56 -07001599 "WLED switch freq reg write failed(%d)\n", rc);
Amy Malochef3d5a062012-08-16 19:14:11 -07001600 return rc;
1601 }
1602
1603 /* program current sink */
1604 if (led->wled_cfg->cs_out_en) {
1605 rc = qpnp_led_masked_write(led, WLED_CURR_SINK_REG(led->base),
1606 WLED_CURR_SINK_MASK,
Amy Maloche832d90a2013-01-07 10:15:29 -08001607 (((1 << led->wled_cfg->num_strings) - 1)
1608 << WLED_CURR_SINK_SHFT));
Amy Malochef3d5a062012-08-16 19:14:11 -07001609 if (rc) {
1610 dev_err(&led->spmi_dev->dev,
1611 "WLED curr sink reg write failed(%d)\n", rc);
1612 return rc;
1613 }
1614 }
1615
1616 /* program high pole capacitance */
1617 rc = qpnp_led_masked_write(led, WLED_HIGH_POLE_CAP_REG(led->base),
1618 WLED_CP_SELECT_MASK, led->wled_cfg->cp_select);
1619 if (rc) {
1620 dev_err(&led->spmi_dev->dev,
1621 "WLED pole cap reg write failed(%d)\n", rc);
1622 return rc;
1623 }
1624
1625 /* program modulator, current mod src and cabc */
1626 for (i = 0; i < num_wled_strings; i++) {
1627 rc = qpnp_led_masked_write(led, WLED_MOD_EN_REG(led->base, i),
1628 WLED_NO_MASK, WLED_EN_MASK);
1629 if (rc) {
1630 dev_err(&led->spmi_dev->dev,
1631 "WLED mod enable reg write failed(%d)\n", rc);
1632 return rc;
1633 }
1634
1635 if (led->wled_cfg->dig_mod_gen_en) {
1636 rc = qpnp_led_masked_write(led,
Amy Maloched44516e2013-02-14 17:36:34 -08001637 WLED_MOD_SRC_SEL_REG(led->base, i),
Amy Malochef3d5a062012-08-16 19:14:11 -07001638 WLED_NO_MASK, WLED_USE_EXT_GEN_MOD_SRC);
1639 if (rc) {
1640 dev_err(&led->spmi_dev->dev,
1641 "WLED dig mod en reg write failed(%d)\n", rc);
1642 }
1643 }
1644
1645 rc = qpnp_led_masked_write(led,
1646 WLED_FULL_SCALE_REG(led->base, i), WLED_MAX_CURR_MASK,
Abinaya P03cee1d2014-02-17 17:51:53 +05301647 (u8)led->max_current);
Amy Malochef3d5a062012-08-16 19:14:11 -07001648 if (rc) {
1649 dev_err(&led->spmi_dev->dev,
1650 "WLED max current reg write failed(%d)\n", rc);
1651 return rc;
1652 }
1653
1654 }
1655
Sarada Prasanna Garnayak40f65022013-11-01 20:17:32 +05301656 /* Reset WLED enable register */
1657 rc = qpnp_led_masked_write(led, WLED_MOD_CTRL_REG(led->base),
1658 WLED_8_BIT_MASK, WLED_BOOST_OFF);
1659 if (rc) {
1660 dev_err(&led->spmi_dev->dev,
1661 "WLED write ctrl reg failed(%d)\n", rc);
1662 return rc;
1663 }
1664
Amy Malochef3d5a062012-08-16 19:14:11 -07001665 /* dump wled registers */
Amy Malochea5ca5552012-10-23 13:34:46 -07001666 qpnp_dump_regs(led, wled_debug_regs, ARRAY_SIZE(wled_debug_regs));
Amy Malochef3d5a062012-08-16 19:14:11 -07001667
1668 return 0;
1669}
1670
Amy Malochebc97c0d22013-03-24 22:06:16 -07001671static ssize_t led_mode_store(struct device *dev,
1672 struct device_attribute *attr,
1673 const char *buf, size_t count)
1674{
1675 struct qpnp_led_data *led;
1676 unsigned long state;
1677 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1678 ssize_t ret = -EINVAL;
1679
1680 ret = kstrtoul(buf, 10, &state);
1681 if (ret)
1682 return ret;
1683
1684 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1685
1686 /* '1' to enable torch mode; '0' to switch to flash mode */
1687 if (state == 1)
1688 led->flash_cfg->torch_enable = true;
1689 else
1690 led->flash_cfg->torch_enable = false;
1691
1692 return count;
1693}
1694
1695static ssize_t led_strobe_type_store(struct device *dev,
1696 struct device_attribute *attr,
1697 const char *buf, size_t count)
1698{
1699 struct qpnp_led_data *led;
1700 unsigned long state;
1701 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1702 ssize_t ret = -EINVAL;
1703
1704 ret = kstrtoul(buf, 10, &state);
1705 if (ret)
1706 return ret;
1707
1708 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1709
1710 /* '0' for sw strobe; '1' for hw strobe */
1711 if (state == 1)
1712 led->flash_cfg->strobe_type = 1;
1713 else
1714 led->flash_cfg->strobe_type = 0;
1715
1716 return count;
1717}
1718
Amy Malochea5c4ed82013-06-05 11:05:28 -07001719static int qpnp_pwm_init(struct pwm_config_data *pwm_cfg,
1720 struct spmi_device *spmi_dev,
1721 const char *name)
1722{
1723 int rc, start_idx, idx_len;
1724
1725 if (pwm_cfg->pwm_channel != -1) {
1726 pwm_cfg->pwm_dev =
1727 pwm_request(pwm_cfg->pwm_channel, name);
1728
1729 if (IS_ERR_OR_NULL(pwm_cfg->pwm_dev)) {
1730 dev_err(&spmi_dev->dev,
1731 "could not acquire PWM Channel %d, " \
1732 "error %ld\n",
1733 pwm_cfg->pwm_channel,
1734 PTR_ERR(pwm_cfg->pwm_dev));
1735 pwm_cfg->pwm_dev = NULL;
1736 return -ENODEV;
1737 }
1738
1739 if (pwm_cfg->mode == LPG_MODE) {
1740 start_idx =
1741 pwm_cfg->duty_cycles->start_idx;
1742 idx_len =
1743 pwm_cfg->duty_cycles->num_duty_pcts;
1744
1745 if (idx_len >= PWM_LUT_MAX_SIZE &&
1746 start_idx) {
1747 dev_err(&spmi_dev->dev,
1748 "Wrong LUT size or index\n");
1749 return -EINVAL;
1750 }
1751 if ((start_idx + idx_len) >
1752 PWM_LUT_MAX_SIZE) {
1753 dev_err(&spmi_dev->dev,
1754 "Exceed LUT limit\n");
1755 return -EINVAL;
1756 }
1757 rc = pwm_lut_config(pwm_cfg->pwm_dev,
1758 PM_PWM_PERIOD_MIN, /* ignored by hardware */
1759 pwm_cfg->duty_cycles->duty_pcts,
1760 pwm_cfg->lut_params);
1761 if (rc < 0) {
1762 dev_err(&spmi_dev->dev, "Failed to " \
1763 "configure pwm LUT\n");
1764 return rc;
1765 }
1766 }
1767 } else {
1768 dev_err(&spmi_dev->dev,
1769 "Invalid PWM channel\n");
1770 return -EINVAL;
1771 }
1772
1773 return 0;
1774}
1775
Amy Maloche4cf9f322013-05-29 15:53:46 -07001776static ssize_t pwm_us_store(struct device *dev,
1777 struct device_attribute *attr,
1778 const char *buf, size_t count)
1779{
1780 struct qpnp_led_data *led;
1781 u32 pwm_us;
1782 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1783 ssize_t ret;
1784 u32 previous_pwm_us;
1785 struct pwm_config_data *pwm_cfg;
1786
1787 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1788
1789 ret = kstrtou32(buf, 10, &pwm_us);
1790 if (ret)
1791 return ret;
1792
1793 switch (led->id) {
1794 case QPNP_ID_LED_MPP:
1795 pwm_cfg = led->mpp_cfg->pwm_cfg;
1796 break;
1797 case QPNP_ID_RGB_RED:
1798 case QPNP_ID_RGB_GREEN:
1799 case QPNP_ID_RGB_BLUE:
1800 pwm_cfg = led->rgb_cfg->pwm_cfg;
1801 break;
1802 default:
1803 dev_err(&led->spmi_dev->dev,
1804 "Invalid LED id type for pwm_us\n");
1805 return -EINVAL;
1806 }
1807
1808 if (pwm_cfg->mode == LPG_MODE)
1809 pwm_cfg->blinking = true;
1810
1811 previous_pwm_us = pwm_cfg->pwm_period_us;
1812
1813 pwm_cfg->pwm_period_us = pwm_us;
1814 pwm_free(pwm_cfg->pwm_dev);
1815 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1816 if (ret) {
1817 pwm_cfg->pwm_period_us = previous_pwm_us;
1818 pwm_free(pwm_cfg->pwm_dev);
1819 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1820 qpnp_led_set(&led->cdev, led->cdev.brightness);
1821 dev_err(&led->spmi_dev->dev,
1822 "Failed to initialize pwm with new pwm_us value\n");
1823 return ret;
1824 }
1825 qpnp_led_set(&led->cdev, led->cdev.brightness);
1826 return count;
1827}
1828
1829static ssize_t pause_lo_store(struct device *dev,
1830 struct device_attribute *attr,
1831 const char *buf, size_t count)
1832{
1833 struct qpnp_led_data *led;
1834 u32 pause_lo;
1835 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1836 ssize_t ret;
1837 u32 previous_pause_lo;
1838 struct pwm_config_data *pwm_cfg;
1839
1840 ret = kstrtou32(buf, 10, &pause_lo);
1841 if (ret)
1842 return ret;
1843 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1844
1845 switch (led->id) {
1846 case QPNP_ID_LED_MPP:
1847 pwm_cfg = led->mpp_cfg->pwm_cfg;
1848 break;
1849 case QPNP_ID_RGB_RED:
1850 case QPNP_ID_RGB_GREEN:
1851 case QPNP_ID_RGB_BLUE:
1852 pwm_cfg = led->rgb_cfg->pwm_cfg;
1853 break;
1854 default:
1855 dev_err(&led->spmi_dev->dev,
1856 "Invalid LED id type for pause lo\n");
1857 return -EINVAL;
1858 }
1859
1860 if (pwm_cfg->mode == LPG_MODE)
1861 pwm_cfg->blinking = true;
1862
1863 previous_pause_lo = pwm_cfg->lut_params.lut_pause_lo;
1864
1865 pwm_free(pwm_cfg->pwm_dev);
1866 pwm_cfg->lut_params.lut_pause_lo = pause_lo;
1867 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1868 if (ret) {
1869 pwm_cfg->lut_params.lut_pause_lo = previous_pause_lo;
1870 pwm_free(pwm_cfg->pwm_dev);
1871 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1872 qpnp_led_set(&led->cdev, led->cdev.brightness);
1873 dev_err(&led->spmi_dev->dev,
1874 "Failed to initialize pwm with new pause lo value\n");
1875 return ret;
1876 }
1877 qpnp_led_set(&led->cdev, led->cdev.brightness);
1878 return count;
1879}
1880
1881static ssize_t pause_hi_store(struct device *dev,
1882 struct device_attribute *attr,
1883 const char *buf, size_t count)
1884{
1885 struct qpnp_led_data *led;
1886 u32 pause_hi;
1887 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1888 ssize_t ret;
1889 u32 previous_pause_hi;
1890 struct pwm_config_data *pwm_cfg;
1891
1892 ret = kstrtou32(buf, 10, &pause_hi);
1893 if (ret)
1894 return ret;
1895 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1896
1897 switch (led->id) {
1898 case QPNP_ID_LED_MPP:
1899 pwm_cfg = led->mpp_cfg->pwm_cfg;
1900 break;
1901 case QPNP_ID_RGB_RED:
1902 case QPNP_ID_RGB_GREEN:
1903 case QPNP_ID_RGB_BLUE:
1904 pwm_cfg = led->rgb_cfg->pwm_cfg;
1905 break;
1906 default:
1907 dev_err(&led->spmi_dev->dev,
1908 "Invalid LED id type for pause hi\n");
1909 return -EINVAL;
1910 }
1911
1912 if (pwm_cfg->mode == LPG_MODE)
1913 pwm_cfg->blinking = true;
1914
1915 previous_pause_hi = pwm_cfg->lut_params.lut_pause_hi;
1916
1917 pwm_free(pwm_cfg->pwm_dev);
1918 pwm_cfg->lut_params.lut_pause_hi = pause_hi;
1919 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1920 if (ret) {
1921 pwm_cfg->lut_params.lut_pause_hi = previous_pause_hi;
1922 pwm_free(pwm_cfg->pwm_dev);
1923 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1924 qpnp_led_set(&led->cdev, led->cdev.brightness);
1925 dev_err(&led->spmi_dev->dev,
1926 "Failed to initialize pwm with new pause hi value\n");
1927 return ret;
1928 }
1929 qpnp_led_set(&led->cdev, led->cdev.brightness);
1930 return count;
1931}
1932
1933static ssize_t start_idx_store(struct device *dev,
1934 struct device_attribute *attr,
1935 const char *buf, size_t count)
1936{
1937 struct qpnp_led_data *led;
1938 u32 start_idx;
1939 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1940 ssize_t ret;
1941 u32 previous_start_idx;
1942 struct pwm_config_data *pwm_cfg;
1943
1944 ret = kstrtou32(buf, 10, &start_idx);
1945 if (ret)
1946 return ret;
1947 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1948
1949 switch (led->id) {
1950 case QPNP_ID_LED_MPP:
1951 pwm_cfg = led->mpp_cfg->pwm_cfg;
1952 break;
1953 case QPNP_ID_RGB_RED:
1954 case QPNP_ID_RGB_GREEN:
1955 case QPNP_ID_RGB_BLUE:
1956 pwm_cfg = led->rgb_cfg->pwm_cfg;
1957 break;
1958 default:
1959 dev_err(&led->spmi_dev->dev,
1960 "Invalid LED id type for start idx\n");
1961 return -EINVAL;
1962 }
1963
1964 if (pwm_cfg->mode == LPG_MODE)
1965 pwm_cfg->blinking = true;
1966
1967 previous_start_idx = pwm_cfg->duty_cycles->start_idx;
1968 pwm_cfg->duty_cycles->start_idx = start_idx;
1969 pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
1970 pwm_free(pwm_cfg->pwm_dev);
1971 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1972 if (ret) {
1973 pwm_cfg->duty_cycles->start_idx = previous_start_idx;
1974 pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
1975 pwm_free(pwm_cfg->pwm_dev);
1976 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1977 qpnp_led_set(&led->cdev, led->cdev.brightness);
1978 dev_err(&led->spmi_dev->dev,
1979 "Failed to initialize pwm with new start idx value\n");
1980 return ret;
1981 }
1982 qpnp_led_set(&led->cdev, led->cdev.brightness);
1983 return count;
1984}
1985
1986static ssize_t ramp_step_ms_store(struct device *dev,
1987 struct device_attribute *attr,
1988 const char *buf, size_t count)
1989{
1990 struct qpnp_led_data *led;
1991 u32 ramp_step_ms;
1992 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1993 ssize_t ret;
1994 u32 previous_ramp_step_ms;
1995 struct pwm_config_data *pwm_cfg;
1996
1997 ret = kstrtou32(buf, 10, &ramp_step_ms);
1998 if (ret)
1999 return ret;
2000 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2001
2002 switch (led->id) {
2003 case QPNP_ID_LED_MPP:
2004 pwm_cfg = led->mpp_cfg->pwm_cfg;
2005 break;
2006 case QPNP_ID_RGB_RED:
2007 case QPNP_ID_RGB_GREEN:
2008 case QPNP_ID_RGB_BLUE:
2009 pwm_cfg = led->rgb_cfg->pwm_cfg;
2010 break;
2011 default:
2012 dev_err(&led->spmi_dev->dev,
2013 "Invalid LED id type for ramp step\n");
2014 return -EINVAL;
2015 }
2016
2017 if (pwm_cfg->mode == LPG_MODE)
2018 pwm_cfg->blinking = true;
2019
2020 previous_ramp_step_ms = pwm_cfg->lut_params.ramp_step_ms;
2021
2022 pwm_free(pwm_cfg->pwm_dev);
2023 pwm_cfg->lut_params.ramp_step_ms = ramp_step_ms;
2024 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2025 if (ret) {
2026 pwm_cfg->lut_params.ramp_step_ms = previous_ramp_step_ms;
2027 pwm_free(pwm_cfg->pwm_dev);
2028 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2029 qpnp_led_set(&led->cdev, led->cdev.brightness);
2030 dev_err(&led->spmi_dev->dev,
2031 "Failed to initialize pwm with new ramp step value\n");
2032 return ret;
2033 }
2034 qpnp_led_set(&led->cdev, led->cdev.brightness);
2035 return count;
2036}
2037
2038static ssize_t lut_flags_store(struct device *dev,
2039 struct device_attribute *attr,
2040 const char *buf, size_t count)
2041{
2042 struct qpnp_led_data *led;
2043 u32 lut_flags;
2044 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2045 ssize_t ret;
2046 u32 previous_lut_flags;
2047 struct pwm_config_data *pwm_cfg;
2048
2049 ret = kstrtou32(buf, 10, &lut_flags);
2050 if (ret)
2051 return ret;
2052 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2053
2054 switch (led->id) {
2055 case QPNP_ID_LED_MPP:
2056 pwm_cfg = led->mpp_cfg->pwm_cfg;
2057 break;
2058 case QPNP_ID_RGB_RED:
2059 case QPNP_ID_RGB_GREEN:
2060 case QPNP_ID_RGB_BLUE:
2061 pwm_cfg = led->rgb_cfg->pwm_cfg;
2062 break;
2063 default:
2064 dev_err(&led->spmi_dev->dev,
2065 "Invalid LED id type for lut flags\n");
2066 return -EINVAL;
2067 }
2068
2069 if (pwm_cfg->mode == LPG_MODE)
2070 pwm_cfg->blinking = true;
2071
2072 previous_lut_flags = pwm_cfg->lut_params.flags;
2073
2074 pwm_free(pwm_cfg->pwm_dev);
2075 pwm_cfg->lut_params.flags = lut_flags;
2076 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2077 if (ret) {
2078 pwm_cfg->lut_params.flags = previous_lut_flags;
2079 pwm_free(pwm_cfg->pwm_dev);
2080 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2081 qpnp_led_set(&led->cdev, led->cdev.brightness);
2082 dev_err(&led->spmi_dev->dev,
2083 "Failed to initialize pwm with new lut flags value\n");
2084 return ret;
2085 }
2086 qpnp_led_set(&led->cdev, led->cdev.brightness);
2087 return count;
2088}
2089
2090static ssize_t duty_pcts_store(struct device *dev,
2091 struct device_attribute *attr,
2092 const char *buf, size_t count)
2093{
2094 struct qpnp_led_data *led;
2095 int num_duty_pcts = 0;
2096 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2097 char *buffer;
2098 ssize_t ret;
2099 int i = 0;
2100 int max_duty_pcts;
2101 struct pwm_config_data *pwm_cfg;
2102 u32 previous_num_duty_pcts;
2103 int value;
2104 int *previous_duty_pcts;
2105
2106 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2107
2108 switch (led->id) {
2109 case QPNP_ID_LED_MPP:
2110 pwm_cfg = led->mpp_cfg->pwm_cfg;
2111 max_duty_pcts = PWM_LUT_MAX_SIZE;
2112 break;
2113 case QPNP_ID_RGB_RED:
2114 case QPNP_ID_RGB_GREEN:
2115 case QPNP_ID_RGB_BLUE:
2116 pwm_cfg = led->rgb_cfg->pwm_cfg;
2117 max_duty_pcts = PWM_LUT_MAX_SIZE;
2118 break;
2119 default:
2120 dev_err(&led->spmi_dev->dev,
2121 "Invalid LED id type for duty pcts\n");
2122 return -EINVAL;
2123 }
2124
2125 if (pwm_cfg->mode == LPG_MODE)
2126 pwm_cfg->blinking = true;
2127
2128 buffer = (char *)buf;
2129
2130 for (i = 0; i < max_duty_pcts; i++) {
2131 if (buffer == NULL)
2132 break;
2133 ret = sscanf((const char *)buffer, "%u,%s", &value, buffer);
2134 pwm_cfg->old_duty_pcts[i] = value;
2135 num_duty_pcts++;
2136 if (ret <= 1)
2137 break;
2138 }
2139
2140 if (num_duty_pcts >= max_duty_pcts) {
2141 dev_err(&led->spmi_dev->dev,
2142 "Number of duty pcts given exceeds max (%d)\n",
2143 max_duty_pcts);
2144 return -EINVAL;
2145 }
2146
2147 previous_num_duty_pcts = pwm_cfg->duty_cycles->num_duty_pcts;
2148 previous_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
2149
2150 pwm_cfg->duty_cycles->num_duty_pcts = num_duty_pcts;
2151 pwm_cfg->duty_cycles->duty_pcts = pwm_cfg->old_duty_pcts;
2152 pwm_cfg->old_duty_pcts = previous_duty_pcts;
2153 pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
2154
2155 pwm_free(pwm_cfg->pwm_dev);
2156 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2157 if (ret)
2158 goto restore;
2159
2160 qpnp_led_set(&led->cdev, led->cdev.brightness);
2161 return count;
2162
2163restore:
2164 dev_err(&led->spmi_dev->dev,
2165 "Failed to initialize pwm with new duty pcts value\n");
2166 pwm_cfg->duty_cycles->num_duty_pcts = previous_num_duty_pcts;
2167 pwm_cfg->old_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
2168 pwm_cfg->duty_cycles->duty_pcts = previous_duty_pcts;
2169 pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
2170 pwm_free(pwm_cfg->pwm_dev);
2171 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2172 qpnp_led_set(&led->cdev, led->cdev.brightness);
2173 return ret;
2174}
2175
Amy Malochea5c4ed82013-06-05 11:05:28 -07002176static void led_blink(struct qpnp_led_data *led,
2177 struct pwm_config_data *pwm_cfg)
2178{
Amy Malochea5c4ed82013-06-05 11:05:28 -07002179 if (pwm_cfg->use_blink) {
2180 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07002181 pwm_cfg->blinking = true;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002182 if (led->id == QPNP_ID_LED_MPP)
2183 led->mpp_cfg->pwm_mode = LPG_MODE;
2184 pwm_cfg->mode = LPG_MODE;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002185 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07002186 pwm_cfg->blinking = false;
2187 pwm_cfg->mode = pwm_cfg->default_mode;
2188 if (led->id == QPNP_ID_LED_MPP)
2189 led->mpp_cfg->pwm_mode = pwm_cfg->default_mode;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002190 }
Amy Maloche4cf9f322013-05-29 15:53:46 -07002191 pwm_free(pwm_cfg->pwm_dev);
2192 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2193 qpnp_led_set(&led->cdev, led->cdev.brightness);
Amy Malochea5c4ed82013-06-05 11:05:28 -07002194 }
2195}
2196
2197static ssize_t blink_store(struct device *dev,
2198 struct device_attribute *attr,
2199 const char *buf, size_t count)
2200{
2201 struct qpnp_led_data *led;
2202 unsigned long blinking;
2203 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2204 ssize_t ret = -EINVAL;
2205
2206 ret = kstrtoul(buf, 10, &blinking);
2207 if (ret)
2208 return ret;
2209 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2210 led->cdev.brightness = blinking ? led->cdev.max_brightness : 0;
2211
2212 switch (led->id) {
2213 case QPNP_ID_LED_MPP:
2214 led_blink(led, led->mpp_cfg->pwm_cfg);
2215 break;
2216 case QPNP_ID_RGB_RED:
2217 case QPNP_ID_RGB_GREEN:
2218 case QPNP_ID_RGB_BLUE:
2219 led_blink(led, led->rgb_cfg->pwm_cfg);
2220 break;
2221 default:
2222 dev_err(&led->spmi_dev->dev, "Invalid LED id type for blink\n");
2223 return -EINVAL;
2224 }
2225 return count;
2226}
2227
Amy Malochebc97c0d22013-03-24 22:06:16 -07002228static DEVICE_ATTR(led_mode, 0664, NULL, led_mode_store);
2229static DEVICE_ATTR(strobe, 0664, NULL, led_strobe_type_store);
Amy Maloche4cf9f322013-05-29 15:53:46 -07002230static DEVICE_ATTR(pwm_us, 0664, NULL, pwm_us_store);
2231static DEVICE_ATTR(pause_lo, 0664, NULL, pause_lo_store);
2232static DEVICE_ATTR(pause_hi, 0664, NULL, pause_hi_store);
2233static DEVICE_ATTR(start_idx, 0664, NULL, start_idx_store);
2234static DEVICE_ATTR(ramp_step_ms, 0664, NULL, ramp_step_ms_store);
2235static DEVICE_ATTR(lut_flags, 0664, NULL, lut_flags_store);
2236static DEVICE_ATTR(duty_pcts, 0664, NULL, duty_pcts_store);
Amy Malochea5c4ed82013-06-05 11:05:28 -07002237static DEVICE_ATTR(blink, 0664, NULL, blink_store);
Amy Malochebc97c0d22013-03-24 22:06:16 -07002238
2239static struct attribute *led_attrs[] = {
2240 &dev_attr_led_mode.attr,
2241 &dev_attr_strobe.attr,
Amy Maloche12ad6f32013-04-02 14:39:24 -07002242 NULL
Amy Malochebc97c0d22013-03-24 22:06:16 -07002243};
2244
2245static const struct attribute_group led_attr_group = {
2246 .attrs = led_attrs,
2247};
2248
Amy Maloche4cf9f322013-05-29 15:53:46 -07002249static struct attribute *pwm_attrs[] = {
2250 &dev_attr_pwm_us.attr,
2251 NULL
2252};
2253
2254static struct attribute *lpg_attrs[] = {
2255 &dev_attr_pause_lo.attr,
2256 &dev_attr_pause_hi.attr,
2257 &dev_attr_start_idx.attr,
2258 &dev_attr_ramp_step_ms.attr,
2259 &dev_attr_lut_flags.attr,
2260 &dev_attr_duty_pcts.attr,
2261 NULL
2262};
2263
Amy Malochea5c4ed82013-06-05 11:05:28 -07002264static struct attribute *blink_attrs[] = {
2265 &dev_attr_blink.attr,
2266 NULL
2267};
2268
Amy Maloche4cf9f322013-05-29 15:53:46 -07002269static const struct attribute_group pwm_attr_group = {
2270 .attrs = pwm_attrs,
2271};
2272
2273static const struct attribute_group lpg_attr_group = {
2274 .attrs = lpg_attrs,
2275};
2276
Amy Malochea5c4ed82013-06-05 11:05:28 -07002277static const struct attribute_group blink_attr_group = {
2278 .attrs = blink_attrs,
2279};
2280
Amy Maloche864a6d52012-10-03 15:58:12 -07002281static int __devinit qpnp_flash_init(struct qpnp_led_data *led)
2282{
2283 int rc;
2284
Chun Zhangc3b505b2013-06-03 19:01:49 -07002285 led->flash_cfg->flash_on = false;
2286
Amy Maloche864a6d52012-10-03 15:58:12 -07002287 rc = qpnp_led_masked_write(led,
2288 FLASH_LED_STROBE_CTRL(led->base),
2289 FLASH_STROBE_MASK, FLASH_DISABLE_ALL);
2290 if (rc) {
2291 dev_err(&led->spmi_dev->dev,
2292 "LED %d flash write failed(%d)\n", led->id, rc);
2293 return rc;
2294 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002295
Chun Zhangdf2d3062013-06-25 20:14:46 -07002296 /* Disable flash LED module */
2297 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07002298 FLASH_ENABLE_MASK, FLASH_DISABLE_ALL);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002299 if (rc) {
2300 dev_err(&led->spmi_dev->dev,
2301 "Enable reg write failed(%d)\n", rc);
2302 return rc;
2303 }
2304
2305 if (led->flash_cfg->torch_enable)
2306 return 0;
2307
Amy Maloche864a6d52012-10-03 15:58:12 -07002308 /* Set headroom */
2309 rc = qpnp_led_masked_write(led, FLASH_HEADROOM(led->base),
2310 FLASH_HEADROOM_MASK, led->flash_cfg->headroom);
2311 if (rc) {
2312 dev_err(&led->spmi_dev->dev,
2313 "Headroom reg write failed(%d)\n", rc);
2314 return rc;
2315 }
2316
Chun Zhange8954cf2013-05-02 11:14:34 -07002317 /* Set startup delay */
2318 rc = qpnp_led_masked_write(led,
2319 FLASH_STARTUP_DELAY(led->base), FLASH_STARTUP_DLY_MASK,
2320 led->flash_cfg->startup_dly);
2321 if (rc) {
2322 dev_err(&led->spmi_dev->dev,
2323 "Startup delay reg write failed(%d)\n", rc);
2324 return rc;
2325 }
2326
2327 /* Set timer control - safety or watchdog */
2328 if (led->flash_cfg->safety_timer) {
2329 rc = qpnp_led_masked_write(led,
2330 FLASH_LED_TMR_CTRL(led->base),
2331 FLASH_TMR_MASK, FLASH_TMR_SAFETY);
2332 if (rc) {
2333 dev_err(&led->spmi_dev->dev,
2334 "LED timer ctrl reg write failed(%d)\n",
2335 rc);
2336 return rc;
2337 }
2338 }
2339
2340 /* Set Vreg force */
2341 rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
2342 FLASH_VREG_MASK, FLASH_HW_VREG_OK);
2343 if (rc) {
2344 dev_err(&led->spmi_dev->dev,
2345 "Vreg OK reg write failed(%d)\n", rc);
2346 return rc;
2347 }
2348
2349 /* Set self fault check */
2350 rc = qpnp_led_masked_write(led, FLASH_FAULT_DETECT(led->base),
2351 FLASH_FAULT_DETECT_MASK, FLASH_SELFCHECK_ENABLE);
2352 if (rc) {
2353 dev_err(&led->spmi_dev->dev,
2354 "Fault detect reg write failed(%d)\n", rc);
2355 return rc;
2356 }
2357
Amy Maloche864a6d52012-10-03 15:58:12 -07002358 /* Set mask enable */
2359 rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
2360 FLASH_MASK_REG_MASK, FLASH_MASK_1);
2361 if (rc) {
2362 dev_err(&led->spmi_dev->dev,
2363 "Mask enable reg write failed(%d)\n", rc);
2364 return rc;
2365 }
2366
Chun Zhang7ce3a9f2013-10-22 10:48:12 -07002367 /* Set current ramp */
2368 rc = qpnp_led_masked_write(led, FLASH_CURRENT_RAMP(led->base),
2369 FLASH_CURRENT_RAMP_MASK, FLASH_RAMP_STEP_27US);
2370 if (rc) {
2371 dev_err(&led->spmi_dev->dev,
2372 "Current ramp reg write failed(%d)\n", rc);
2373 return rc;
2374 }
2375
Amy Malochebc97c0d22013-03-24 22:06:16 -07002376 led->flash_cfg->strobe_type = 0;
2377
Amy Maloche864a6d52012-10-03 15:58:12 -07002378 /* dump flash registers */
2379 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
2380
2381 return 0;
2382}
2383
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302384static int __devinit qpnp_kpdbl_init(struct qpnp_led_data *led)
2385{
2386 int rc;
2387 u8 val;
2388
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302389 /* select row source - vbst or vph */
2390 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2391 KPDBL_ROW_SRC_SEL(led->base), &val, 1);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302392 if (rc) {
2393 dev_err(&led->spmi_dev->dev,
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302394 "Unable to read from addr=%x, rc(%d)\n",
2395 KPDBL_ROW_SRC_SEL(led->base), rc);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302396 return rc;
2397 }
2398
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302399 if (led->kpdbl_cfg->row_src_vbst)
2400 val |= 1 << led->kpdbl_cfg->row_id;
2401 else
2402 val &= ~(1 << led->kpdbl_cfg->row_id);
2403
2404 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
2405 KPDBL_ROW_SRC_SEL(led->base), &val, 1);
2406 if (rc) {
2407 dev_err(&led->spmi_dev->dev,
2408 "Unable to read from addr=%x, rc(%d)\n",
2409 KPDBL_ROW_SRC_SEL(led->base), rc);
2410 return rc;
2411 }
2412
2413 /* row source enable */
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302414 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2415 KPDBL_ROW_SRC(led->base), &val, 1);
2416 if (rc) {
2417 dev_err(&led->spmi_dev->dev,
2418 "Unable to read from addr=%x, rc(%d)\n",
2419 KPDBL_ROW_SRC(led->base), rc);
2420 return rc;
2421 }
2422
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302423 if (led->kpdbl_cfg->row_src_en)
2424 val |= KPDBL_ROW_SCAN_EN_MASK | (1 << led->kpdbl_cfg->row_id);
2425 else
2426 val &= ~(1 << led->kpdbl_cfg->row_id);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302427
2428 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
2429 KPDBL_ROW_SRC(led->base), &val, 1);
2430 if (rc) {
2431 dev_err(&led->spmi_dev->dev,
2432 "Unable to write to addr=%x, rc(%d)\n",
2433 KPDBL_ROW_SRC(led->base), rc);
2434 return rc;
2435 }
2436
2437 /* enable module */
2438 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
2439 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
2440 if (rc) {
2441 dev_err(&led->spmi_dev->dev,
2442 "Enable module write failed(%d)\n", rc);
2443 return rc;
2444 }
2445
Amy Malochea2726f02013-05-10 10:19:03 -07002446 rc = qpnp_pwm_init(led->kpdbl_cfg->pwm_cfg, led->spmi_dev,
2447 led->cdev.name);
2448 if (rc) {
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302449 dev_err(&led->spmi_dev->dev,
Amy Malochea2726f02013-05-10 10:19:03 -07002450 "Failed to initialize pwm\n");
2451 return rc;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302452 }
2453
2454 /* dump kpdbl registers */
2455 qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
2456
2457 return 0;
2458}
2459
Amy Malocheeea7b592012-10-03 15:59:36 -07002460static int __devinit qpnp_rgb_init(struct qpnp_led_data *led)
2461{
Amy Malochea2726f02013-05-10 10:19:03 -07002462 int rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07002463
2464 rc = qpnp_led_masked_write(led, RGB_LED_SRC_SEL(led->base),
2465 RGB_LED_SRC_MASK, RGB_LED_SOURCE_VPH_PWR);
2466 if (rc) {
2467 dev_err(&led->spmi_dev->dev,
2468 "Failed to write led source select register\n");
2469 return rc;
2470 }
2471
Amy Malochea2726f02013-05-10 10:19:03 -07002472 rc = qpnp_pwm_init(led->rgb_cfg->pwm_cfg, led->spmi_dev,
2473 led->cdev.name);
2474 if (rc) {
Amy Malocheeea7b592012-10-03 15:59:36 -07002475 dev_err(&led->spmi_dev->dev,
Amy Malochea2726f02013-05-10 10:19:03 -07002476 "Failed to initialize pwm\n");
2477 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07002478 }
Amy Malocheeea7b592012-10-03 15:59:36 -07002479 /* Initialize led for use in auto trickle charging mode */
2480 rc = qpnp_led_masked_write(led, RGB_LED_ATC_CTL(led->base),
2481 led->rgb_cfg->enable, led->rgb_cfg->enable);
2482
2483 return 0;
2484}
2485
Amy Malochea2726f02013-05-10 10:19:03 -07002486static int __devinit qpnp_mpp_init(struct qpnp_led_data *led)
2487{
Abinaya P03cee1d2014-02-17 17:51:53 +05302488 int rc;
2489 u8 val;
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05302490
2491 if (led->max_current < LED_MPP_CURRENT_MIN ||
2492 led->max_current > LED_MPP_CURRENT_MAX) {
2493 dev_err(&led->spmi_dev->dev,
2494 "max current for mpp is not valid\n");
2495 return -EINVAL;
2496 }
2497
Amy Malochea2726f02013-05-10 10:19:03 -07002498 val = (led->mpp_cfg->current_setting / LED_MPP_CURRENT_PER_SETTING) - 1;
2499
2500 if (val < 0)
2501 val = 0;
2502
Chun Zhang874c9ab2013-07-08 17:18:34 -07002503 rc = qpnp_led_masked_write(led, LED_MPP_VIN_CTRL(led->base),
2504 LED_MPP_VIN_MASK, led->mpp_cfg->vin_ctrl);
2505 if (rc) {
2506 dev_err(&led->spmi_dev->dev,
2507 "Failed to write led vin control reg\n");
2508 return rc;
2509 }
2510
Amy Malochea2726f02013-05-10 10:19:03 -07002511 rc = qpnp_led_masked_write(led, LED_MPP_SINK_CTRL(led->base),
2512 LED_MPP_SINK_MASK, val);
2513 if (rc) {
2514 dev_err(&led->spmi_dev->dev,
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05302515 "Failed to write sink control reg\n");
Amy Malochea2726f02013-05-10 10:19:03 -07002516 return rc;
2517 }
2518
2519 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
2520 rc = qpnp_pwm_init(led->mpp_cfg->pwm_cfg, led->spmi_dev,
2521 led->cdev.name);
2522 if (rc) {
2523 dev_err(&led->spmi_dev->dev,
2524 "Failed to initialize pwm\n");
2525 return rc;
2526 }
2527 }
2528
2529 return 0;
2530}
2531
Amy Malochef3d5a062012-08-16 19:14:11 -07002532static int __devinit qpnp_led_initialize(struct qpnp_led_data *led)
2533{
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302534 int rc = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07002535
2536 switch (led->id) {
2537 case QPNP_ID_WLED:
2538 rc = qpnp_wled_init(led);
2539 if (rc)
Amy Malochea5ca5552012-10-23 13:34:46 -07002540 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -07002541 "WLED initialize failed(%d)\n", rc);
2542 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07002543 case QPNP_ID_FLASH1_LED0:
2544 case QPNP_ID_FLASH1_LED1:
2545 rc = qpnp_flash_init(led);
2546 if (rc)
2547 dev_err(&led->spmi_dev->dev,
2548 "FLASH initialize failed(%d)\n", rc);
2549 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07002550 case QPNP_ID_RGB_RED:
2551 case QPNP_ID_RGB_GREEN:
2552 case QPNP_ID_RGB_BLUE:
2553 rc = qpnp_rgb_init(led);
2554 if (rc)
2555 dev_err(&led->spmi_dev->dev,
2556 "RGB initialize failed(%d)\n", rc);
2557 break;
Amy Malochef3813742013-04-11 19:33:47 -07002558 case QPNP_ID_LED_MPP:
Amy Malochea2726f02013-05-10 10:19:03 -07002559 rc = qpnp_mpp_init(led);
2560 if (rc)
2561 dev_err(&led->spmi_dev->dev,
2562 "MPP initialize failed(%d)\n", rc);
Amy Malochef3813742013-04-11 19:33:47 -07002563 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302564 case QPNP_ID_KPDBL:
2565 rc = qpnp_kpdbl_init(led);
2566 if (rc)
2567 dev_err(&led->spmi_dev->dev,
2568 "KPDBL initialize failed(%d)\n", rc);
2569 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07002570 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07002571 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malocheeea7b592012-10-03 15:59:36 -07002572 return -EINVAL;
Amy Malochef3d5a062012-08-16 19:14:11 -07002573 }
2574
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302575 return rc;
Amy Malochef3d5a062012-08-16 19:14:11 -07002576}
2577
Amy Malochea5ca5552012-10-23 13:34:46 -07002578static int __devinit qpnp_get_common_configs(struct qpnp_led_data *led,
2579 struct device_node *node)
2580{
2581 int rc;
Asaf Penso55ac8472013-01-21 21:17:37 +02002582 u32 val;
Amy Malochea5ca5552012-10-23 13:34:46 -07002583 const char *temp_string;
2584
2585 led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
2586 rc = of_property_read_string(node, "linux,default-trigger",
2587 &temp_string);
2588 if (!rc)
2589 led->cdev.default_trigger = temp_string;
2590 else if (rc != -EINVAL)
2591 return rc;
2592
2593 led->default_on = false;
2594 rc = of_property_read_string(node, "qcom,default-state",
2595 &temp_string);
2596 if (!rc) {
2597 if (strncmp(temp_string, "on", sizeof("on")) == 0)
2598 led->default_on = true;
2599 } else if (rc != -EINVAL)
2600 return rc;
2601
Asaf Penso55ac8472013-01-21 21:17:37 +02002602 led->turn_off_delay_ms = 0;
2603 rc = of_property_read_u32(node, "qcom,turn-off-delay-ms", &val);
2604 if (!rc)
2605 led->turn_off_delay_ms = val;
2606 else if (rc != -EINVAL)
2607 return rc;
2608
Amy Malochea5ca5552012-10-23 13:34:46 -07002609 return 0;
2610}
2611
Amy Malochef3d5a062012-08-16 19:14:11 -07002612/*
2613 * Handlers for alternative sources of platform_data
2614 */
2615static int __devinit qpnp_get_config_wled(struct qpnp_led_data *led,
2616 struct device_node *node)
2617{
2618 u32 val;
2619 int rc;
Amy Malochef3d5a062012-08-16 19:14:11 -07002620
2621 led->wled_cfg = devm_kzalloc(&led->spmi_dev->dev,
2622 sizeof(struct wled_config_data), GFP_KERNEL);
2623 if (!led->wled_cfg) {
2624 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2625 return -ENOMEM;
2626 }
2627
Amy Maloche0150b5e2013-08-15 18:18:32 -07002628 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2629 PMIC_VERSION_REG, &led->wled_cfg->pmic_version, 1);
2630 if (rc) {
2631 dev_err(&led->spmi_dev->dev,
2632 "Unable to read pmic ver, rc(%d)\n", rc);
2633 }
2634
Amy Malochef3d5a062012-08-16 19:14:11 -07002635 led->wled_cfg->num_strings = WLED_DEFAULT_STRINGS;
2636 rc = of_property_read_u32(node, "qcom,num-strings", &val);
2637 if (!rc)
2638 led->wled_cfg->num_strings = (u8) val;
2639 else if (rc != -EINVAL)
2640 return rc;
2641
2642 led->wled_cfg->ovp_val = WLED_DEFAULT_OVP_VAL;
2643 rc = of_property_read_u32(node, "qcom,ovp-val", &val);
2644 if (!rc)
2645 led->wled_cfg->ovp_val = (u8) val;
2646 else if (rc != -EINVAL)
2647 return rc;
2648
2649 led->wled_cfg->boost_curr_lim = WLED_BOOST_LIM_DEFAULT;
2650 rc = of_property_read_u32(node, "qcom,boost-curr-lim", &val);
2651 if (!rc)
2652 led->wled_cfg->boost_curr_lim = (u8) val;
2653 else if (rc != -EINVAL)
2654 return rc;
2655
2656 led->wled_cfg->cp_select = WLED_CP_SEL_DEFAULT;
2657 rc = of_property_read_u32(node, "qcom,cp-sel", &val);
2658 if (!rc)
2659 led->wled_cfg->cp_select = (u8) val;
2660 else if (rc != -EINVAL)
2661 return rc;
2662
2663 led->wled_cfg->ctrl_delay_us = WLED_CTRL_DLY_DEFAULT;
2664 rc = of_property_read_u32(node, "qcom,ctrl-delay-us", &val);
2665 if (!rc)
2666 led->wled_cfg->ctrl_delay_us = (u8) val;
2667 else if (rc != -EINVAL)
2668 return rc;
2669
Amy Malochebd687672013-03-18 11:23:45 -07002670 led->wled_cfg->op_fdbck = WLED_OP_FDBCK_DEFAULT;
2671 rc = of_property_read_u32(node, "qcom,op-fdbck", &val);
2672 if (!rc)
2673 led->wled_cfg->op_fdbck = (u8) val;
2674 else if (rc != -EINVAL)
2675 return rc;
2676
Amy Malochef3d5a062012-08-16 19:14:11 -07002677 led->wled_cfg->switch_freq = WLED_SWITCH_FREQ_DEFAULT;
2678 rc = of_property_read_u32(node, "qcom,switch-freq", &val);
2679 if (!rc)
2680 led->wled_cfg->switch_freq = (u8) val;
2681 else if (rc != -EINVAL)
2682 return rc;
2683
2684 led->wled_cfg->dig_mod_gen_en =
2685 of_property_read_bool(node, "qcom,dig-mod-gen-en");
2686
2687 led->wled_cfg->cs_out_en =
2688 of_property_read_bool(node, "qcom,cs-out-en");
2689
Amy Malochef3d5a062012-08-16 19:14:11 -07002690 return 0;
2691}
2692
Amy Maloche864a6d52012-10-03 15:58:12 -07002693static int __devinit qpnp_get_config_flash(struct qpnp_led_data *led,
Chun Zhangc3b505b2013-06-03 19:01:49 -07002694 struct device_node *node, bool *reg_set)
Amy Maloche864a6d52012-10-03 15:58:12 -07002695{
2696 int rc;
2697 u32 val;
2698
2699 led->flash_cfg = devm_kzalloc(&led->spmi_dev->dev,
2700 sizeof(struct flash_config_data), GFP_KERNEL);
2701 if (!led->flash_cfg) {
2702 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2703 return -ENOMEM;
2704 }
2705
Chun Zhang0d6ca072013-07-30 21:08:39 -07002706 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2707 FLASH_PERIPHERAL_SUBTYPE(led->base),
2708 &led->flash_cfg->peripheral_subtype, 1);
2709 if (rc) {
2710 dev_err(&led->spmi_dev->dev,
2711 "Unable to read from addr=%x, rc(%d)\n",
2712 FLASH_PERIPHERAL_SUBTYPE(led->base), rc);
2713 }
2714
Chun Zhang9d5ff672013-08-01 18:18:26 -07002715 led->flash_cfg->torch_enable =
2716 of_property_read_bool(node, "qcom,torch-enable");
2717
Amy Maloche864a6d52012-10-03 15:58:12 -07002718 if (led->id == QPNP_ID_FLASH1_LED0) {
Chun Zhang9d5ff672013-08-01 18:18:26 -07002719 led->flash_cfg->enable_module = FLASH_ENABLE_LED_0;
Amy Maloche864a6d52012-10-03 15:58:12 -07002720 led->flash_cfg->current_addr = FLASH_LED_0_CURR(led->base);
Amy Maloche864a6d52012-10-03 15:58:12 -07002721 led->flash_cfg->trigger_flash = FLASH_LED_0_OUTPUT;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002722 if (!*reg_set) {
2723 led->flash_cfg->flash_boost_reg =
2724 regulator_get(&led->spmi_dev->dev,
Chun Zhang0d6ca072013-07-30 21:08:39 -07002725 "flash-boost");
Chun Zhangc3b505b2013-06-03 19:01:49 -07002726 if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
2727 rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
2728 dev_err(&led->spmi_dev->dev,
2729 "Regulator get failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002730 goto error_get_flash_reg;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002731 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002732 led->flash_cfg->flash_reg_get = true;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002733 *reg_set = true;
2734 } else
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002735 led->flash_cfg->flash_reg_get = false;
Chun Zhang9d5ff672013-08-01 18:18:26 -07002736
2737 if (led->flash_cfg->torch_enable) {
2738 led->flash_cfg->second_addr =
2739 FLASH_LED_1_CURR(led->base);
2740 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002741 } else if (led->id == QPNP_ID_FLASH1_LED1) {
Chun Zhang9d5ff672013-08-01 18:18:26 -07002742 led->flash_cfg->enable_module = FLASH_ENABLE_LED_1;
Amy Maloche864a6d52012-10-03 15:58:12 -07002743 led->flash_cfg->current_addr = FLASH_LED_1_CURR(led->base);
Amy Maloche864a6d52012-10-03 15:58:12 -07002744 led->flash_cfg->trigger_flash = FLASH_LED_1_OUTPUT;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002745 if (!*reg_set) {
2746 led->flash_cfg->flash_boost_reg =
2747 regulator_get(&led->spmi_dev->dev,
Chun Zhang0d6ca072013-07-30 21:08:39 -07002748 "flash-boost");
Chun Zhangc3b505b2013-06-03 19:01:49 -07002749 if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
2750 rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
2751 dev_err(&led->spmi_dev->dev,
2752 "Regulator get failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002753 goto error_get_flash_reg;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002754 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002755 led->flash_cfg->flash_reg_get = true;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002756 *reg_set = true;
2757 } else
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002758 led->flash_cfg->flash_reg_get = false;
Chun Zhang9d5ff672013-08-01 18:18:26 -07002759
2760 if (led->flash_cfg->torch_enable) {
2761 led->flash_cfg->second_addr =
2762 FLASH_LED_0_CURR(led->base);
2763 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002764 } else {
2765 dev_err(&led->spmi_dev->dev, "Unknown flash LED name given\n");
2766 return -EINVAL;
2767 }
2768
Chun Zhangdf2d3062013-06-25 20:14:46 -07002769 if (led->flash_cfg->torch_enable) {
Chun Zhang0d6ca072013-07-30 21:08:39 -07002770 if (of_find_property(of_get_parent(node), "torch-boost-supply",
2771 NULL)) {
2772 led->flash_cfg->torch_boost_reg =
2773 regulator_get(&led->spmi_dev->dev,
2774 "torch-boost");
2775 if (IS_ERR(led->flash_cfg->torch_boost_reg)) {
2776 rc = PTR_ERR(led->flash_cfg->torch_boost_reg);
2777 dev_err(&led->spmi_dev->dev,
2778 "Torch regulator get failed(%d)\n", rc);
2779 goto error_get_torch_reg;
2780 }
Chun Zhang9d5ff672013-08-01 18:18:26 -07002781 led->flash_cfg->enable_module = FLASH_ENABLE_MODULE;
2782 } else
2783 led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
Abinaya Pb6e3ba42014-01-07 15:32:31 +05302784 led->flash_cfg->trigger_flash = FLASH_TORCH_OUTPUT;
Chun Zhangdf2d3062013-06-25 20:14:46 -07002785 }
2786
Amy Maloche864a6d52012-10-03 15:58:12 -07002787 rc = of_property_read_u32(node, "qcom,current", &val);
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002788 if (!rc) {
Chun Zhangdf2d3062013-06-25 20:14:46 -07002789 if (led->flash_cfg->torch_enable) {
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002790 led->flash_cfg->current_prgm = (val *
2791 TORCH_MAX_LEVEL / led->max_current);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002792 return 0;
2793 }
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002794 else
2795 led->flash_cfg->current_prgm = (val *
Amy Maloche864a6d52012-10-03 15:58:12 -07002796 FLASH_MAX_LEVEL / led->max_current);
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002797 } else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002798 if (led->flash_cfg->torch_enable)
2799 goto error_get_torch_reg;
2800 else
2801 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002802
2803 rc = of_property_read_u32(node, "qcom,headroom", &val);
2804 if (!rc)
2805 led->flash_cfg->headroom = (u8) val;
2806 else if (rc == -EINVAL)
Amy Maloche62571b22013-04-17 17:23:10 -07002807 led->flash_cfg->headroom = HEADROOM_500mV;
Amy Maloche864a6d52012-10-03 15:58:12 -07002808 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002809 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002810
2811 rc = of_property_read_u32(node, "qcom,duration", &val);
2812 if (!rc)
Abinaya P41bb8bc2013-12-06 22:55:37 +05302813 led->flash_cfg->duration = (u8)((val - 10) / 10);
Amy Maloche864a6d52012-10-03 15:58:12 -07002814 else if (rc == -EINVAL)
2815 led->flash_cfg->duration = FLASH_DURATION_200ms;
2816 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002817 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002818
2819 rc = of_property_read_u32(node, "qcom,clamp-curr", &val);
2820 if (!rc)
2821 led->flash_cfg->clamp_curr = (val *
2822 FLASH_MAX_LEVEL / led->max_current);
2823 else if (rc == -EINVAL)
2824 led->flash_cfg->clamp_curr = FLASH_CLAMP_200mA;
2825 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002826 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002827
2828 rc = of_property_read_u32(node, "qcom,startup-dly", &val);
2829 if (!rc)
2830 led->flash_cfg->startup_dly = (u8) val;
2831 else if (rc == -EINVAL)
Amy Maloche62571b22013-04-17 17:23:10 -07002832 led->flash_cfg->startup_dly = DELAY_128us;
Amy Maloche864a6d52012-10-03 15:58:12 -07002833 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002834 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002835
2836 led->flash_cfg->safety_timer =
2837 of_property_read_bool(node, "qcom,safety-timer");
2838
2839 return 0;
Chun Zhangdf2d3062013-06-25 20:14:46 -07002840
2841error_get_torch_reg:
2842 regulator_put(led->flash_cfg->torch_boost_reg);
2843
2844error_get_flash_reg:
2845 regulator_put(led->flash_cfg->flash_boost_reg);
2846 return rc;
2847
Amy Maloche864a6d52012-10-03 15:58:12 -07002848}
2849
Amy Malochea2726f02013-05-10 10:19:03 -07002850static int __devinit qpnp_get_config_pwm(struct pwm_config_data *pwm_cfg,
2851 struct spmi_device *spmi_dev,
2852 struct device_node *node)
2853{
2854 struct property *prop;
2855 int rc, i;
2856 u32 val;
2857 u8 *temp_cfg;
2858
2859 rc = of_property_read_u32(node, "qcom,pwm-channel", &val);
2860 if (!rc)
2861 pwm_cfg->pwm_channel = val;
2862 else
2863 return rc;
2864
2865 if (pwm_cfg->mode == PWM_MODE) {
2866 rc = of_property_read_u32(node, "qcom,pwm-us", &val);
2867 if (!rc)
2868 pwm_cfg->pwm_period_us = val;
2869 else
2870 return rc;
2871 }
2872
Amy Malochea5c4ed82013-06-05 11:05:28 -07002873 pwm_cfg->use_blink =
2874 of_property_read_bool(node, "qcom,use-blink");
2875
2876 if (pwm_cfg->mode == LPG_MODE || pwm_cfg->use_blink) {
Amy Malochea2726f02013-05-10 10:19:03 -07002877 pwm_cfg->duty_cycles =
2878 devm_kzalloc(&spmi_dev->dev,
2879 sizeof(struct pwm_duty_cycles), GFP_KERNEL);
2880 if (!pwm_cfg->duty_cycles) {
2881 dev_err(&spmi_dev->dev,
2882 "Unable to allocate memory\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002883 rc = -ENOMEM;
2884 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002885 }
2886
2887 prop = of_find_property(node, "qcom,duty-pcts",
2888 &pwm_cfg->duty_cycles->num_duty_pcts);
2889 if (!prop) {
2890 dev_err(&spmi_dev->dev, "Looking up property " \
2891 "node qcom,duty-pcts failed\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002892 rc = -ENODEV;
2893 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002894 } else if (!pwm_cfg->duty_cycles->num_duty_pcts) {
2895 dev_err(&spmi_dev->dev, "Invalid length of " \
2896 "duty pcts\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002897 rc = -EINVAL;
2898 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002899 }
2900
2901 pwm_cfg->duty_cycles->duty_pcts =
2902 devm_kzalloc(&spmi_dev->dev,
Amy Maloche4cf9f322013-05-29 15:53:46 -07002903 sizeof(int) * PWM_LUT_MAX_SIZE,
Amy Malochea2726f02013-05-10 10:19:03 -07002904 GFP_KERNEL);
2905 if (!pwm_cfg->duty_cycles->duty_pcts) {
2906 dev_err(&spmi_dev->dev,
2907 "Unable to allocate memory\n");
Amy Maloche4cf9f322013-05-29 15:53:46 -07002908 rc = -ENOMEM;
2909 goto bad_lpg_params;
2910 }
2911
2912 pwm_cfg->old_duty_pcts =
2913 devm_kzalloc(&spmi_dev->dev,
2914 sizeof(int) * PWM_LUT_MAX_SIZE,
2915 GFP_KERNEL);
2916 if (!pwm_cfg->old_duty_pcts) {
2917 dev_err(&spmi_dev->dev,
2918 "Unable to allocate memory\n");
2919 rc = -ENOMEM;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002920 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002921 }
2922
2923 temp_cfg = devm_kzalloc(&spmi_dev->dev,
2924 pwm_cfg->duty_cycles->num_duty_pcts *
2925 sizeof(u8), GFP_KERNEL);
2926 if (!temp_cfg) {
2927 dev_err(&spmi_dev->dev, "Failed to allocate " \
2928 "memory for duty pcts\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002929 rc = -ENOMEM;
2930 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002931 }
2932
2933 memcpy(temp_cfg, prop->value,
2934 pwm_cfg->duty_cycles->num_duty_pcts);
2935
2936 for (i = 0; i < pwm_cfg->duty_cycles->num_duty_pcts; i++)
2937 pwm_cfg->duty_cycles->duty_pcts[i] =
2938 (int) temp_cfg[i];
2939
2940 rc = of_property_read_u32(node, "qcom,start-idx", &val);
2941 if (!rc) {
2942 pwm_cfg->lut_params.start_idx = val;
2943 pwm_cfg->duty_cycles->start_idx = val;
2944 } else
Amy Malochea5c4ed82013-06-05 11:05:28 -07002945 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002946
2947 pwm_cfg->lut_params.lut_pause_hi = 0;
2948 rc = of_property_read_u32(node, "qcom,pause-hi", &val);
2949 if (!rc)
2950 pwm_cfg->lut_params.lut_pause_hi = val;
2951 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002952 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002953
2954 pwm_cfg->lut_params.lut_pause_lo = 0;
2955 rc = of_property_read_u32(node, "qcom,pause-lo", &val);
2956 if (!rc)
2957 pwm_cfg->lut_params.lut_pause_lo = val;
2958 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002959 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002960
2961 pwm_cfg->lut_params.ramp_step_ms =
2962 QPNP_LUT_RAMP_STEP_DEFAULT;
2963 rc = of_property_read_u32(node, "qcom,ramp-step-ms", &val);
2964 if (!rc)
2965 pwm_cfg->lut_params.ramp_step_ms = val;
2966 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002967 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002968
2969 pwm_cfg->lut_params.flags = QPNP_LED_PWM_FLAGS;
2970 rc = of_property_read_u32(node, "qcom,lut-flags", &val);
2971 if (!rc)
2972 pwm_cfg->lut_params.flags = (u8) val;
2973 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002974 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002975
2976 pwm_cfg->lut_params.idx_len =
2977 pwm_cfg->duty_cycles->num_duty_pcts;
2978 }
2979 return 0;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002980
2981bad_lpg_params:
2982 pwm_cfg->use_blink = false;
2983 if (pwm_cfg->mode == PWM_MODE) {
2984 dev_err(&spmi_dev->dev, "LPG parameters not set for" \
2985 " blink mode, defaulting to PWM mode\n");
2986 return 0;
2987 }
2988 return rc;
Amy Malochea2726f02013-05-10 10:19:03 -07002989};
2990
2991static int qpnp_led_get_mode(const char *mode)
2992{
2993 if (strncmp(mode, "manual", strlen(mode)) == 0)
2994 return MANUAL_MODE;
2995 else if (strncmp(mode, "pwm", strlen(mode)) == 0)
2996 return PWM_MODE;
2997 else if (strncmp(mode, "lpg", strlen(mode)) == 0)
2998 return LPG_MODE;
2999 else
3000 return -EINVAL;
3001};
3002
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303003static int __devinit qpnp_get_config_kpdbl(struct qpnp_led_data *led,
3004 struct device_node *node)
3005{
3006 int rc;
3007 u32 val;
Amy Malochea2726f02013-05-10 10:19:03 -07003008 u8 led_mode;
3009 const char *mode;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303010
3011 led->kpdbl_cfg = devm_kzalloc(&led->spmi_dev->dev,
3012 sizeof(struct kpdbl_config_data), GFP_KERNEL);
3013 if (!led->kpdbl_cfg) {
3014 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3015 return -ENOMEM;
3016 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303017
Amy Malochea2726f02013-05-10 10:19:03 -07003018 rc = of_property_read_string(node, "qcom,mode", &mode);
3019 if (!rc) {
3020 led_mode = qpnp_led_get_mode(mode);
3021 if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
3022 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3023 "supported for kpdbl.\n");
3024 return -EINVAL;
3025 }
3026 led->kpdbl_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3027 sizeof(struct pwm_config_data),
3028 GFP_KERNEL);
3029 if (!led->kpdbl_cfg->pwm_cfg) {
3030 dev_err(&led->spmi_dev->dev,
3031 "Unable to allocate memory\n");
3032 return -ENOMEM;
3033 }
3034 led->kpdbl_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003035 led->kpdbl_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003036 } else
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303037 return rc;
3038
Amy Malochea2726f02013-05-10 10:19:03 -07003039 rc = qpnp_get_config_pwm(led->kpdbl_cfg->pwm_cfg, led->spmi_dev, node);
3040 if (rc < 0)
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303041 return rc;
3042
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303043 rc = of_property_read_u32(node, "qcom,row-id", &val);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303044 if (!rc)
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303045 led->kpdbl_cfg->row_id = val;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303046 else
3047 return rc;
3048
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303049 led->kpdbl_cfg->row_src_vbst =
3050 of_property_read_bool(node, "qcom,row-src-vbst");
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303051
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303052 led->kpdbl_cfg->row_src_en =
3053 of_property_read_bool(node, "qcom,row-src-en");
3054
3055 led->kpdbl_cfg->always_on =
3056 of_property_read_bool(node, "qcom,always-on");
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303057
3058 return 0;
3059}
3060
Amy Malocheeea7b592012-10-03 15:59:36 -07003061static int __devinit qpnp_get_config_rgb(struct qpnp_led_data *led,
3062 struct device_node *node)
3063{
Amy Malochea2726f02013-05-10 10:19:03 -07003064 int rc;
3065 u8 led_mode;
3066 const char *mode;
Amy Malocheeea7b592012-10-03 15:59:36 -07003067
3068 led->rgb_cfg = devm_kzalloc(&led->spmi_dev->dev,
3069 sizeof(struct rgb_config_data), GFP_KERNEL);
3070 if (!led->rgb_cfg) {
3071 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3072 return -ENOMEM;
3073 }
3074
3075 if (led->id == QPNP_ID_RGB_RED)
3076 led->rgb_cfg->enable = RGB_LED_ENABLE_RED;
3077 else if (led->id == QPNP_ID_RGB_GREEN)
3078 led->rgb_cfg->enable = RGB_LED_ENABLE_GREEN;
3079 else if (led->id == QPNP_ID_RGB_BLUE)
3080 led->rgb_cfg->enable = RGB_LED_ENABLE_BLUE;
3081 else
3082 return -EINVAL;
3083
Amy Malochea2726f02013-05-10 10:19:03 -07003084 rc = of_property_read_string(node, "qcom,mode", &mode);
3085 if (!rc) {
3086 led_mode = qpnp_led_get_mode(mode);
3087 if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
3088 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3089 "supported for rgb.\n");
Amy Malocheeea7b592012-10-03 15:59:36 -07003090 return -EINVAL;
3091 }
Amy Malochea2726f02013-05-10 10:19:03 -07003092 led->rgb_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3093 sizeof(struct pwm_config_data),
3094 GFP_KERNEL);
3095 if (!led->rgb_cfg->pwm_cfg) {
Amy Malocheeea7b592012-10-03 15:59:36 -07003096 dev_err(&led->spmi_dev->dev,
3097 "Unable to allocate memory\n");
3098 return -ENOMEM;
3099 }
Amy Malochea2726f02013-05-10 10:19:03 -07003100 led->rgb_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003101 led->rgb_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003102 } else
3103 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07003104
Amy Malochea2726f02013-05-10 10:19:03 -07003105 rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->spmi_dev, node);
3106 if (rc < 0)
3107 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07003108
3109 return 0;
3110}
3111
Amy Malochef3813742013-04-11 19:33:47 -07003112static int __devinit qpnp_get_config_mpp(struct qpnp_led_data *led,
3113 struct device_node *node)
3114{
3115 int rc;
3116 u32 val;
Amy Malochea2726f02013-05-10 10:19:03 -07003117 u8 led_mode;
3118 const char *mode;
Amy Malochef3813742013-04-11 19:33:47 -07003119
3120 led->mpp_cfg = devm_kzalloc(&led->spmi_dev->dev,
3121 sizeof(struct mpp_config_data), GFP_KERNEL);
3122 if (!led->mpp_cfg) {
3123 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3124 return -ENOMEM;
3125 }
3126
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05303127 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
Amy Malochef3813742013-04-11 19:33:47 -07003128 rc = of_property_read_u32(node, "qcom,current-setting", &val);
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05303129 if (!rc) {
3130 if (led->mpp_cfg->current_setting < LED_MPP_CURRENT_MIN)
3131 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
3132 else if (led->mpp_cfg->current_setting > LED_MPP_CURRENT_MAX)
3133 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MAX;
3134 else
3135 led->mpp_cfg->current_setting = (u8) val;
3136 } else if (rc != -EINVAL)
Amy Malochef3813742013-04-11 19:33:47 -07003137 return rc;
3138
3139 led->mpp_cfg->source_sel = LED_MPP_SOURCE_SEL_DEFAULT;
3140 rc = of_property_read_u32(node, "qcom,source-sel", &val);
3141 if (!rc)
3142 led->mpp_cfg->source_sel = (u8) val;
3143 else if (rc != -EINVAL)
3144 return rc;
3145
3146 led->mpp_cfg->mode_ctrl = LED_MPP_MODE_SINK;
3147 rc = of_property_read_u32(node, "qcom,mode-ctrl", &val);
3148 if (!rc)
3149 led->mpp_cfg->mode_ctrl = (u8) val;
3150 else if (rc != -EINVAL)
3151 return rc;
3152
Chun Zhang874c9ab2013-07-08 17:18:34 -07003153 led->mpp_cfg->vin_ctrl = LED_MPP_VIN_CTRL_DEFAULT;
3154 rc = of_property_read_u32(node, "qcom,vin-ctrl", &val);
3155 if (!rc)
3156 led->mpp_cfg->vin_ctrl = (u8) val;
3157 else if (rc != -EINVAL)
3158 return rc;
3159
3160 led->mpp_cfg->min_brightness = 0;
3161 rc = of_property_read_u32(node, "qcom,min-brightness", &val);
3162 if (!rc)
3163 led->mpp_cfg->min_brightness = (u8) val;
3164 else if (rc != -EINVAL)
3165 return rc;
3166
Amy Malochea2726f02013-05-10 10:19:03 -07003167 rc = of_property_read_string(node, "qcom,mode", &mode);
3168 if (!rc) {
3169 led_mode = qpnp_led_get_mode(mode);
3170 led->mpp_cfg->pwm_mode = led_mode;
3171 if (led_mode == MANUAL_MODE)
3172 return MANUAL_MODE;
3173 else if (led_mode == -EINVAL) {
3174 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3175 "supported for mpp.\n");
3176 return -EINVAL;
3177 }
3178 led->mpp_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3179 sizeof(struct pwm_config_data),
3180 GFP_KERNEL);
3181 if (!led->mpp_cfg->pwm_cfg) {
3182 dev_err(&led->spmi_dev->dev,
3183 "Unable to allocate memory\n");
3184 return -ENOMEM;
3185 }
3186 led->mpp_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003187 led->mpp_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003188 } else
3189 return rc;
3190
3191 rc = qpnp_get_config_pwm(led->mpp_cfg->pwm_cfg, led->spmi_dev, node);
3192 if (rc < 0)
3193 return rc;
3194
Amy Malochef3813742013-04-11 19:33:47 -07003195 return 0;
3196}
3197
Amy Malochef3d5a062012-08-16 19:14:11 -07003198static int __devinit qpnp_leds_probe(struct spmi_device *spmi)
3199{
Amy Malochef9490c62012-11-27 19:26:04 -08003200 struct qpnp_led_data *led, *led_array;
Amy Malochef3d5a062012-08-16 19:14:11 -07003201 struct resource *led_resource;
Amy Malochea5ca5552012-10-23 13:34:46 -07003202 struct device_node *node, *temp;
Amy Malochef9490c62012-11-27 19:26:04 -08003203 int rc, i, num_leds = 0, parsed_leds = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07003204 const char *led_label;
Chun Zhangc3b505b2013-06-03 19:01:49 -07003205 bool regulator_probe = false;
Amy Malochef3d5a062012-08-16 19:14:11 -07003206
Amy Malochea5ca5552012-10-23 13:34:46 -07003207 node = spmi->dev.of_node;
3208 if (node == NULL)
3209 return -ENODEV;
3210
3211 temp = NULL;
3212 while ((temp = of_get_next_child(node, temp)))
3213 num_leds++;
3214
Amy Malochef9490c62012-11-27 19:26:04 -08003215 if (!num_leds)
3216 return -ECHILD;
3217
3218 led_array = devm_kzalloc(&spmi->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07003219 (sizeof(struct qpnp_led_data) * num_leds), GFP_KERNEL);
Amy Malochef9490c62012-11-27 19:26:04 -08003220 if (!led_array) {
Amy Malochef3d5a062012-08-16 19:14:11 -07003221 dev_err(&spmi->dev, "Unable to allocate memory\n");
3222 return -ENOMEM;
3223 }
3224
Amy Malochea5ca5552012-10-23 13:34:46 -07003225 for_each_child_of_node(node, temp) {
Amy Malochef9490c62012-11-27 19:26:04 -08003226 led = &led_array[parsed_leds];
3227 led->num_leds = num_leds;
Amy Malochea5ca5552012-10-23 13:34:46 -07003228 led->spmi_dev = spmi;
Amy Malochef3d5a062012-08-16 19:14:11 -07003229
Amy Malochea5ca5552012-10-23 13:34:46 -07003230 led_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
3231 if (!led_resource) {
3232 dev_err(&spmi->dev, "Unable to get LED base address\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003233 rc = -ENXIO;
3234 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003235 }
3236 led->base = led_resource->start;
Amy Malochef3d5a062012-08-16 19:14:11 -07003237
Amy Malochea5ca5552012-10-23 13:34:46 -07003238 rc = of_property_read_string(temp, "label", &led_label);
Amy Malochef3d5a062012-08-16 19:14:11 -07003239 if (rc < 0) {
3240 dev_err(&led->spmi_dev->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07003241 "Failure reading label, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003242 goto fail_id_check;
Amy Malochef3d5a062012-08-16 19:14:11 -07003243 }
Amy Malochea5ca5552012-10-23 13:34:46 -07003244
3245 rc = of_property_read_string(temp, "linux,name",
3246 &led->cdev.name);
3247 if (rc < 0) {
3248 dev_err(&led->spmi_dev->dev,
3249 "Failure reading led name, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003250 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003251 }
3252
3253 rc = of_property_read_u32(temp, "qcom,max-current",
3254 &led->max_current);
3255 if (rc < 0) {
3256 dev_err(&led->spmi_dev->dev,
3257 "Failure reading max_current, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003258 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003259 }
3260
3261 rc = of_property_read_u32(temp, "qcom,id", &led->id);
3262 if (rc < 0) {
3263 dev_err(&led->spmi_dev->dev,
3264 "Failure reading led id, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003265 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003266 }
3267
3268 rc = qpnp_get_common_configs(led, temp);
3269 if (rc) {
3270 dev_err(&led->spmi_dev->dev,
3271 "Failure reading common led configuration," \
3272 " rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003273 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003274 }
3275
3276 led->cdev.brightness_set = qpnp_led_set;
3277 led->cdev.brightness_get = qpnp_led_get;
3278
3279 if (strncmp(led_label, "wled", sizeof("wled")) == 0) {
3280 rc = qpnp_get_config_wled(led, temp);
3281 if (rc < 0) {
3282 dev_err(&led->spmi_dev->dev,
3283 "Unable to read wled config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003284 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003285 }
Amy Maloche864a6d52012-10-03 15:58:12 -07003286 } else if (strncmp(led_label, "flash", sizeof("flash"))
3287 == 0) {
Chun Zhang0d6ca072013-07-30 21:08:39 -07003288 if (!of_find_property(node, "flash-boost-supply", NULL))
Chun Zhangc3b505b2013-06-03 19:01:49 -07003289 regulator_probe = true;
3290 rc = qpnp_get_config_flash(led, temp, &regulator_probe);
Amy Maloche864a6d52012-10-03 15:58:12 -07003291 if (rc < 0) {
3292 dev_err(&led->spmi_dev->dev,
3293 "Unable to read flash config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003294 goto fail_id_check;
Amy Maloche864a6d52012-10-03 15:58:12 -07003295 }
Amy Malocheeea7b592012-10-03 15:59:36 -07003296 } else if (strncmp(led_label, "rgb", sizeof("rgb")) == 0) {
3297 rc = qpnp_get_config_rgb(led, temp);
3298 if (rc < 0) {
3299 dev_err(&led->spmi_dev->dev,
3300 "Unable to read rgb config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003301 goto fail_id_check;
Amy Malocheeea7b592012-10-03 15:59:36 -07003302 }
Amy Malochef3813742013-04-11 19:33:47 -07003303 } else if (strncmp(led_label, "mpp", sizeof("mpp")) == 0) {
3304 rc = qpnp_get_config_mpp(led, temp);
3305 if (rc < 0) {
3306 dev_err(&led->spmi_dev->dev,
3307 "Unable to read mpp config data\n");
Amy Malochea2726f02013-05-10 10:19:03 -07003308 goto fail_id_check;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303309 }
3310 } else if (strncmp(led_label, "kpdbl", sizeof("kpdbl")) == 0) {
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303311 num_kpbl_leds_on = 0;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303312 rc = qpnp_get_config_kpdbl(led, temp);
3313 if (rc < 0) {
3314 dev_err(&led->spmi_dev->dev,
3315 "Unable to read kpdbl config data\n");
Amy Malochef3813742013-04-11 19:33:47 -07003316 goto fail_id_check;
3317 }
Amy Malochea5ca5552012-10-23 13:34:46 -07003318 } else {
3319 dev_err(&led->spmi_dev->dev, "No LED matching label\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003320 rc = -EINVAL;
3321 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003322 }
3323
Abinaya P9e5dc3e2014-04-14 15:38:11 +05303324 if (led->id != QPNP_ID_FLASH1_LED0 &&
3325 led->id != QPNP_ID_FLASH1_LED1)
3326 mutex_init(&led->lock);
Chun Zhang815a1832013-06-20 13:47:13 -07003327 INIT_WORK(&led->work, qpnp_led_work);
Amy Malochea5ca5552012-10-23 13:34:46 -07003328
3329 rc = qpnp_led_initialize(led);
3330 if (rc < 0)
3331 goto fail_id_check;
3332
3333 rc = qpnp_led_set_max_brightness(led);
3334 if (rc < 0)
3335 goto fail_id_check;
3336
3337 rc = led_classdev_register(&spmi->dev, &led->cdev);
3338 if (rc) {
3339 dev_err(&spmi->dev, "unable to register led %d,rc=%d\n",
3340 led->id, rc);
3341 goto fail_id_check;
3342 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07003343
3344 if (led->id == QPNP_ID_FLASH1_LED0 ||
3345 led->id == QPNP_ID_FLASH1_LED1) {
3346 rc = sysfs_create_group(&led->cdev.dev->kobj,
3347 &led_attr_group);
3348 if (rc)
3349 goto fail_id_check;
3350
3351 }
3352
Amy Malochea5c4ed82013-06-05 11:05:28 -07003353 if (led->id == QPNP_ID_LED_MPP) {
3354 if (!led->mpp_cfg->pwm_cfg)
3355 break;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003356 if (led->mpp_cfg->pwm_cfg->mode == PWM_MODE) {
3357 rc = sysfs_create_group(&led->cdev.dev->kobj,
3358 &pwm_attr_group);
3359 if (rc)
3360 goto fail_id_check;
3361 }
Amy Malochea5c4ed82013-06-05 11:05:28 -07003362 if (led->mpp_cfg->pwm_cfg->use_blink) {
3363 rc = sysfs_create_group(&led->cdev.dev->kobj,
3364 &blink_attr_group);
3365 if (rc)
3366 goto fail_id_check;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003367
3368 rc = sysfs_create_group(&led->cdev.dev->kobj,
3369 &lpg_attr_group);
3370 if (rc)
3371 goto fail_id_check;
3372 } else if (led->mpp_cfg->pwm_cfg->mode == LPG_MODE) {
3373 rc = sysfs_create_group(&led->cdev.dev->kobj,
3374 &lpg_attr_group);
3375 if (rc)
3376 goto fail_id_check;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003377 }
3378 } else if ((led->id == QPNP_ID_RGB_RED) ||
3379 (led->id == QPNP_ID_RGB_GREEN) ||
3380 (led->id == QPNP_ID_RGB_BLUE)) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07003381 if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
3382 rc = sysfs_create_group(&led->cdev.dev->kobj,
3383 &pwm_attr_group);
3384 if (rc)
3385 goto fail_id_check;
3386 }
Amy Malochea5c4ed82013-06-05 11:05:28 -07003387 if (led->rgb_cfg->pwm_cfg->use_blink) {
3388 rc = sysfs_create_group(&led->cdev.dev->kobj,
3389 &blink_attr_group);
3390 if (rc)
3391 goto fail_id_check;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003392
3393 rc = sysfs_create_group(&led->cdev.dev->kobj,
3394 &lpg_attr_group);
3395 if (rc)
3396 goto fail_id_check;
3397 } else if (led->rgb_cfg->pwm_cfg->mode == LPG_MODE) {
3398 rc = sysfs_create_group(&led->cdev.dev->kobj,
3399 &lpg_attr_group);
3400 if (rc)
3401 goto fail_id_check;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003402 }
3403 }
3404
Amy Malochea5ca5552012-10-23 13:34:46 -07003405 /* configure default state */
Asaf Penso55ac8472013-01-21 21:17:37 +02003406 if (led->default_on) {
Amy Malochea5ca5552012-10-23 13:34:46 -07003407 led->cdev.brightness = led->cdev.max_brightness;
Chun Zhang815a1832013-06-20 13:47:13 -07003408 __qpnp_led_work(led, led->cdev.brightness);
Asaf Penso55ac8472013-01-21 21:17:37 +02003409 if (led->turn_off_delay_ms > 0)
3410 qpnp_led_turn_off(led);
3411 } else
Amy Malochea5ca5552012-10-23 13:34:46 -07003412 led->cdev.brightness = LED_OFF;
3413
Amy Malochef9490c62012-11-27 19:26:04 -08003414 parsed_leds++;
Amy Malochef3d5a062012-08-16 19:14:11 -07003415 }
Amy Malochef9490c62012-11-27 19:26:04 -08003416 dev_set_drvdata(&spmi->dev, led_array);
Amy Malochef3d5a062012-08-16 19:14:11 -07003417 return 0;
3418
3419fail_id_check:
Chun Zhang815a1832013-06-20 13:47:13 -07003420 for (i = 0; i < parsed_leds; i++) {
Abinaya P9e5dc3e2014-04-14 15:38:11 +05303421 if (led_array[i].id != QPNP_ID_FLASH1_LED0 &&
3422 led_array[i].id != QPNP_ID_FLASH1_LED1)
3423 mutex_destroy(&led_array[i].lock);
Amy Malochef9490c62012-11-27 19:26:04 -08003424 led_classdev_unregister(&led_array[i].cdev);
Chun Zhang815a1832013-06-20 13:47:13 -07003425 }
3426
Amy Malochef3d5a062012-08-16 19:14:11 -07003427 return rc;
3428}
3429
3430static int __devexit qpnp_leds_remove(struct spmi_device *spmi)
3431{
Amy Malochef9490c62012-11-27 19:26:04 -08003432 struct qpnp_led_data *led_array = dev_get_drvdata(&spmi->dev);
3433 int i, parsed_leds = led_array->num_leds;
Amy Malochef3d5a062012-08-16 19:14:11 -07003434
Amy Malochebc97c0d22013-03-24 22:06:16 -07003435 for (i = 0; i < parsed_leds; i++) {
Chun Zhang815a1832013-06-20 13:47:13 -07003436 cancel_work_sync(&led_array[i].work);
Abinaya P9e5dc3e2014-04-14 15:38:11 +05303437 if (led_array[i].id != QPNP_ID_FLASH1_LED0 &&
3438 led_array[i].id != QPNP_ID_FLASH1_LED1)
3439 mutex_destroy(&led_array[i].lock);
3440 else if (led_array[i].id == QPNP_ID_FLASH1_LED0)
3441 mutex_destroy(&flash_lock);
3442
Amy Malochef9490c62012-11-27 19:26:04 -08003443 led_classdev_unregister(&led_array[i].cdev);
Amy Malochebc97c0d22013-03-24 22:06:16 -07003444 switch (led_array[i].id) {
3445 case QPNP_ID_WLED:
3446 break;
3447 case QPNP_ID_FLASH1_LED0:
3448 case QPNP_ID_FLASH1_LED1:
Chun Zhangda8ad0f2013-07-17 14:46:47 -07003449 if (led_array[i].flash_cfg->flash_reg_get)
Chun Zhangc3b505b2013-06-03 19:01:49 -07003450 regulator_put(led_array[i].flash_cfg-> \
3451 flash_boost_reg);
Chun Zhangdf2d3062013-06-25 20:14:46 -07003452 if (led_array[i].flash_cfg->torch_enable)
3453 regulator_put(led_array[i].flash_cfg->\
3454 torch_boost_reg);
Amy Malochebc97c0d22013-03-24 22:06:16 -07003455 sysfs_remove_group(&led_array[i].cdev.dev->kobj,
3456 &led_attr_group);
3457 break;
3458 case QPNP_ID_RGB_RED:
3459 case QPNP_ID_RGB_GREEN:
3460 case QPNP_ID_RGB_BLUE:
Amy Maloche4cf9f322013-05-29 15:53:46 -07003461 if (led_array[i].rgb_cfg->pwm_cfg->mode == PWM_MODE)
3462 sysfs_remove_group(&led_array[i].cdev.dev->\
3463 kobj, &pwm_attr_group);
3464 if (led_array[i].rgb_cfg->pwm_cfg->use_blink) {
3465 sysfs_remove_group(&led_array[i].cdev.dev->\
3466 kobj, &blink_attr_group);
3467 sysfs_remove_group(&led_array[i].cdev.dev->\
3468 kobj, &lpg_attr_group);
3469 } else if (led_array[i].rgb_cfg->pwm_cfg->mode\
3470 == LPG_MODE)
3471 sysfs_remove_group(&led_array[i].cdev.dev->\
3472 kobj, &lpg_attr_group);
3473 break;
3474 case QPNP_ID_LED_MPP:
3475 if (!led_array[i].mpp_cfg->pwm_cfg)
3476 break;
3477 if (led_array[i].mpp_cfg->pwm_cfg->mode == PWM_MODE)
3478 sysfs_remove_group(&led_array[i].cdev.dev->\
3479 kobj, &pwm_attr_group);
3480 if (led_array[i].mpp_cfg->pwm_cfg->use_blink) {
3481 sysfs_remove_group(&led_array[i].cdev.dev->\
3482 kobj, &blink_attr_group);
3483 sysfs_remove_group(&led_array[i].cdev.dev->\
3484 kobj, &lpg_attr_group);
3485 } else if (led_array[i].mpp_cfg->pwm_cfg->mode\
3486 == LPG_MODE)
3487 sysfs_remove_group(&led_array[i].cdev.dev->\
3488 kobj, &lpg_attr_group);
3489 break;
Amy Malochebc97c0d22013-03-24 22:06:16 -07003490 default:
3491 dev_err(&led_array[i].spmi_dev->dev,
3492 "Invalid LED(%d)\n",
3493 led_array[i].id);
3494 return -EINVAL;
3495 }
3496 }
Amy Malochef3d5a062012-08-16 19:14:11 -07003497
3498 return 0;
3499}
Himanshu Aggarwal4eac46c2013-11-27 13:46:53 +05303500
3501#ifdef CONFIG_OF
Amy Malochef3d5a062012-08-16 19:14:11 -07003502static struct of_device_id spmi_match_table[] = {
Himanshu Aggarwal4eac46c2013-11-27 13:46:53 +05303503 { .compatible = "qcom,leds-qpnp",},
3504 { },
Amy Malochef3d5a062012-08-16 19:14:11 -07003505};
Himanshu Aggarwal4eac46c2013-11-27 13:46:53 +05303506#else
3507#define spmi_match_table NULL
3508#endif
Amy Malochef3d5a062012-08-16 19:14:11 -07003509
3510static struct spmi_driver qpnp_leds_driver = {
3511 .driver = {
3512 .name = "qcom,leds-qpnp",
3513 .of_match_table = spmi_match_table,
3514 },
3515 .probe = qpnp_leds_probe,
3516 .remove = __devexit_p(qpnp_leds_remove),
3517};
3518
3519static int __init qpnp_led_init(void)
3520{
3521 return spmi_driver_register(&qpnp_leds_driver);
3522}
3523module_init(qpnp_led_init);
3524
3525static void __exit qpnp_led_exit(void)
3526{
3527 spmi_driver_unregister(&qpnp_leds_driver);
3528}
3529module_exit(qpnp_led_exit);
3530
3531MODULE_DESCRIPTION("QPNP LEDs driver");
3532MODULE_LICENSE("GPL v2");
3533MODULE_ALIAS("leds:leds-qpnp");
Amy Maloche864a6d52012-10-03 15:58:12 -07003534