blob: bf59d0321df284b3b00eab77c2147d2c090d4ef3 [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>
Amy Malochef3d5a062012-08-16 19:14:11 -070028
29#define WLED_MOD_EN_REG(base, n) (base + 0x60 + n*0x10)
30#define WLED_IDAC_DLY_REG(base, n) (WLED_MOD_EN_REG(base, n) + 0x01)
31#define WLED_FULL_SCALE_REG(base, n) (WLED_IDAC_DLY_REG(base, n) + 0x01)
Amy Maloched44516e2013-02-14 17:36:34 -080032#define WLED_MOD_SRC_SEL_REG(base, n) (WLED_FULL_SCALE_REG(base, n) + 0x01)
Amy Malochef3d5a062012-08-16 19:14:11 -070033
34/* wled control registers */
35#define WLED_BRIGHTNESS_CNTL_LSB(base, n) (base + 0x40 + 2*n)
36#define WLED_BRIGHTNESS_CNTL_MSB(base, n) (base + 0x41 + 2*n)
37#define WLED_MOD_CTRL_REG(base) (base + 0x46)
38#define WLED_SYNC_REG(base) (base + 0x47)
39#define WLED_FDBCK_CTRL_REG(base) (base + 0x48)
40#define WLED_SWITCHING_FREQ_REG(base) (base + 0x4C)
41#define WLED_OVP_CFG_REG(base) (base + 0x4D)
42#define WLED_BOOST_LIMIT_REG(base) (base + 0x4E)
43#define WLED_CURR_SINK_REG(base) (base + 0x4F)
44#define WLED_HIGH_POLE_CAP_REG(base) (base + 0x58)
45#define WLED_CURR_SINK_MASK 0xE0
46#define WLED_CURR_SINK_SHFT 0x05
Amy Maloche0150b5e2013-08-15 18:18:32 -070047#define WLED_DISABLE_ALL_SINKS 0x00
Amy Maloche9eccb4c2013-07-12 14:31:56 -070048#define WLED_SWITCH_FREQ_MASK 0x0F
Amy Malochef3d5a062012-08-16 19:14:11 -070049#define WLED_OVP_VAL_MASK 0x03
50#define WLED_OVP_VAL_BIT_SHFT 0x00
51#define WLED_BOOST_LIMIT_MASK 0x07
52#define WLED_BOOST_LIMIT_BIT_SHFT 0x00
53#define WLED_BOOST_ON 0x80
54#define WLED_BOOST_OFF 0x00
55#define WLED_EN_MASK 0x80
56#define WLED_NO_MASK 0x00
57#define WLED_CP_SELECT_MAX 0x03
58#define WLED_CP_SELECT_MASK 0x02
59#define WLED_USE_EXT_GEN_MOD_SRC 0x01
60#define WLED_CTL_DLY_STEP 200
61#define WLED_CTL_DLY_MAX 1400
62#define WLED_MAX_CURR 25
Amy Maloche0150b5e2013-08-15 18:18:32 -070063#define WLED_NO_CURRENT 0x00
64#define WLED_OVP_DELAY 1000
Amy Malochef3d5a062012-08-16 19:14:11 -070065#define WLED_MSB_MASK 0x0F
Himanshu Aggarwale51de272013-09-11 10:02:24 +053066#define WLED_MAX_CURR_MASK 0x1F
Amy Malochef3d5a062012-08-16 19:14:11 -070067#define WLED_OP_FDBCK_MASK 0x07
68#define WLED_OP_FDBCK_BIT_SHFT 0x00
Amy Malochebd687672013-03-18 11:23:45 -070069#define WLED_OP_FDBCK_DEFAULT 0x00
Amy Malochef3d5a062012-08-16 19:14:11 -070070
Amy Maloched55fdb82013-02-26 18:11:57 -080071#define WLED_MAX_LEVEL 4095
Amy Malochef3d5a062012-08-16 19:14:11 -070072#define WLED_8_BIT_MASK 0xFF
73#define WLED_4_BIT_MASK 0x0F
74#define WLED_8_BIT_SHFT 0x08
75#define WLED_MAX_DUTY_CYCLE 0xFFF
76
77#define WLED_SYNC_VAL 0x07
78#define WLED_SYNC_RESET_VAL 0x00
79
Amy Maloche0150b5e2013-08-15 18:18:32 -070080#define PMIC_VER_8026 0x04
81#define PMIC_VERSION_REG 0x0105
82
Amy Malochef3d5a062012-08-16 19:14:11 -070083#define WLED_DEFAULT_STRINGS 0x01
84#define WLED_DEFAULT_OVP_VAL 0x02
85#define WLED_BOOST_LIM_DEFAULT 0x03
86#define WLED_CP_SEL_DEFAULT 0x00
87#define WLED_CTRL_DLY_DEFAULT 0x00
Amy Maloche9eccb4c2013-07-12 14:31:56 -070088#define WLED_SWITCH_FREQ_DEFAULT 0x0B
Amy Malochef3d5a062012-08-16 19:14:11 -070089
Amy Maloche864a6d52012-10-03 15:58:12 -070090#define FLASH_SAFETY_TIMER(base) (base + 0x40)
91#define FLASH_MAX_CURR(base) (base + 0x41)
92#define FLASH_LED_0_CURR(base) (base + 0x42)
93#define FLASH_LED_1_CURR(base) (base + 0x43)
94#define FLASH_CLAMP_CURR(base) (base + 0x44)
95#define FLASH_LED_TMR_CTRL(base) (base + 0x48)
Amy Maloched44516e2013-02-14 17:36:34 -080096#define FLASH_HEADROOM(base) (base + 0x4A)
Amy Maloche864a6d52012-10-03 15:58:12 -070097#define FLASH_STARTUP_DELAY(base) (base + 0x4B)
98#define FLASH_MASK_ENABLE(base) (base + 0x4C)
99#define FLASH_VREG_OK_FORCE(base) (base + 0x4F)
100#define FLASH_ENABLE_CONTROL(base) (base + 0x46)
101#define FLASH_LED_STROBE_CTRL(base) (base + 0x47)
Amy Malochebc97c0d22013-03-24 22:06:16 -0700102#define FLASH_LED_UNLOCK_SECURE(base) (base + 0xD0)
103#define FLASH_LED_TORCH(base) (base + 0xE4)
Chun Zhange8954cf2013-05-02 11:14:34 -0700104#define FLASH_FAULT_DETECT(base) (base + 0x51)
Chun Zhang0d6ca072013-07-30 21:08:39 -0700105#define FLASH_PERIPHERAL_SUBTYPE(base) (base + 0x05)
Amy Maloche864a6d52012-10-03 15:58:12 -0700106
107#define FLASH_MAX_LEVEL 0x4F
Chun Zhang9c1fbaa2013-06-17 15:31:18 -0700108#define TORCH_MAX_LEVEL 0x0F
Amy Maloche864a6d52012-10-03 15:58:12 -0700109#define FLASH_NO_MASK 0x00
110
111#define FLASH_MASK_1 0x20
112#define FLASH_MASK_REG_MASK 0xE0
113#define FLASH_HEADROOM_MASK 0x03
114#define FLASH_SAFETY_TIMER_MASK 0x7F
115#define FLASH_CURRENT_MASK 0xFF
Amy Malochebc97c0d22013-03-24 22:06:16 -0700116#define FLASH_MAX_CURRENT_MASK 0x7F
Amy Maloche864a6d52012-10-03 15:58:12 -0700117#define FLASH_TMR_MASK 0x03
118#define FLASH_TMR_WATCHDOG 0x03
119#define FLASH_TMR_SAFETY 0x00
Chun Zhange8954cf2013-05-02 11:14:34 -0700120#define FLASH_FAULT_DETECT_MASK 0X80
Chun Zhangbcdcf232013-06-06 18:11:41 -0700121#define FLASH_HW_VREG_OK 0x40
Amy Maloche864a6d52012-10-03 15:58:12 -0700122#define FLASH_VREG_MASK 0xC0
Amy Maloche864a6d52012-10-03 15:58:12 -0700123#define FLASH_STARTUP_DLY_MASK 0x02
124
125#define FLASH_ENABLE_ALL 0xE0
126#define FLASH_ENABLE_MODULE 0x80
127#define FLASH_ENABLE_MODULE_MASK 0x80
128#define FLASH_DISABLE_ALL 0x00
Amy Maloche38b9aae2013-02-07 12:15:34 -0800129#define FLASH_ENABLE_MASK 0xE0
Chun Zhang9d5ff672013-08-01 18:18:26 -0700130#define FLASH_ENABLE_LED_0 0xC0
131#define FLASH_ENABLE_LED_1 0xA0
Amy Maloche864a6d52012-10-03 15:58:12 -0700132#define FLASH_INIT_MASK 0xE0
Chun Zhange8954cf2013-05-02 11:14:34 -0700133#define FLASH_SELFCHECK_ENABLE 0x80
Amy Maloche864a6d52012-10-03 15:58:12 -0700134
Amy Malochebc97c0d22013-03-24 22:06:16 -0700135#define FLASH_STROBE_SW 0xC0
Chun Zhang9d5ff672013-08-01 18:18:26 -0700136#define FLASH_STROBE_HW 0x04
Amy Malochebc97c0d22013-03-24 22:06:16 -0700137#define FLASH_STROBE_MASK 0xC7
Amy Maloche864a6d52012-10-03 15:58:12 -0700138#define FLASH_LED_0_OUTPUT 0x80
139#define FLASH_LED_1_OUTPUT 0x40
140
141#define FLASH_CURRENT_PRGM_MIN 1
142#define FLASH_CURRENT_PRGM_SHIFT 1
Amy Malochebc97c0d22013-03-24 22:06:16 -0700143#define FLASH_CURRENT_MAX 0x4F
Chun Zhange8954cf2013-05-02 11:14:34 -0700144#define FLASH_CURRENT_TORCH 0x07
Amy Maloche864a6d52012-10-03 15:58:12 -0700145
146#define FLASH_DURATION_200ms 0x13
147#define FLASH_CLAMP_200mA 0x0F
148
Amy Malochebc97c0d22013-03-24 22:06:16 -0700149#define FLASH_TORCH_MASK 0x03
150#define FLASH_LED_TORCH_ENABLE 0x00
151#define FLASH_LED_TORCH_DISABLE 0x03
152#define FLASH_UNLOCK_SECURE 0xA5
153#define FLASH_SECURE_MASK 0xFF
154
Chun Zhang0d6ca072013-07-30 21:08:39 -0700155#define FLASH_SUBTYPE_DUAL 0x01
156#define FLASH_SUBTYPE_SINGLE 0x02
157
Amy Malochea5ca5552012-10-23 13:34:46 -0700158#define LED_TRIGGER_DEFAULT "none"
159
Amy Malocheeea7b592012-10-03 15:59:36 -0700160#define RGB_LED_SRC_SEL(base) (base + 0x45)
161#define RGB_LED_EN_CTL(base) (base + 0x46)
162#define RGB_LED_ATC_CTL(base) (base + 0x47)
163
164#define RGB_MAX_LEVEL LED_FULL
165#define RGB_LED_ENABLE_RED 0x80
166#define RGB_LED_ENABLE_GREEN 0x40
167#define RGB_LED_ENABLE_BLUE 0x20
168#define RGB_LED_SOURCE_VPH_PWR 0x01
169#define RGB_LED_ENABLE_MASK 0xE0
170#define RGB_LED_SRC_MASK 0x03
171#define QPNP_LED_PWM_FLAGS (PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP)
Amy Maloche013b35b2013-03-19 12:29:11 -0700172#define QPNP_LUT_RAMP_STEP_DEFAULT 255
Amy Malocheeea7b592012-10-03 15:59:36 -0700173#define PWM_LUT_MAX_SIZE 63
Amy Maloche4cf9f322013-05-29 15:53:46 -0700174#define PWM_GPLED_LUT_MAX_SIZE 31
Amy Malocheeea7b592012-10-03 15:59:36 -0700175#define RGB_LED_DISABLE 0x00
176
Amy Malochef3813742013-04-11 19:33:47 -0700177#define MPP_MAX_LEVEL LED_FULL
178#define LED_MPP_MODE_CTRL(base) (base + 0x40)
179#define LED_MPP_VIN_CTRL(base) (base + 0x41)
180#define LED_MPP_EN_CTRL(base) (base + 0x46)
181#define LED_MPP_SINK_CTRL(base) (base + 0x4C)
182
Mohan Pallaka38ec7f32013-09-11 13:01:48 +0530183#define LED_MPP_CURRENT_MIN 5
184#define LED_MPP_CURRENT_MAX 40
Chun Zhang874c9ab2013-07-08 17:18:34 -0700185#define LED_MPP_VIN_CTRL_DEFAULT 0
Amy Malochea2726f02013-05-10 10:19:03 -0700186#define LED_MPP_CURRENT_PER_SETTING 5
Amy Malochef3813742013-04-11 19:33:47 -0700187#define LED_MPP_SOURCE_SEL_DEFAULT LED_MPP_MODE_ENABLE
188
189#define LED_MPP_SINK_MASK 0x07
190#define LED_MPP_MODE_MASK 0x7F
Chun Zhang874c9ab2013-07-08 17:18:34 -0700191#define LED_MPP_VIN_MASK 0x03
Amy Malochef3813742013-04-11 19:33:47 -0700192#define LED_MPP_EN_MASK 0x80
Amy Malochece59f662013-05-02 10:59:53 -0700193#define LED_MPP_SRC_MASK 0x0F
194#define LED_MPP_MODE_CTRL_MASK 0x70
Amy Malochef3813742013-04-11 19:33:47 -0700195
196#define LED_MPP_MODE_SINK (0x06 << 4)
197#define LED_MPP_MODE_ENABLE 0x01
198#define LED_MPP_MODE_OUTPUT 0x10
199#define LED_MPP_MODE_DISABLE 0x00
200#define LED_MPP_EN_ENABLE 0x80
201#define LED_MPP_EN_DISABLE 0x00
202
203#define MPP_SOURCE_DTEST1 0x08
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530204
205#define KPDBL_MAX_LEVEL LED_FULL
206#define KPDBL_ROW_SRC_SEL(base) (base + 0x40)
207#define KPDBL_ENABLE(base) (base + 0x46)
208#define KPDBL_ROW_SRC(base) (base + 0xE5)
209
210#define KPDBL_ROW_SRC_SEL_VAL_MASK 0x0F
211#define KPDBL_ROW_SCAN_EN_MASK 0x80
212#define KPDBL_ROW_SCAN_VAL_MASK 0x0F
213#define KPDBL_ROW_SCAN_EN_SHIFT 7
214#define KPDBL_MODULE_EN 0x80
215#define KPDBL_MODULE_DIS 0x00
216#define KPDBL_MODULE_EN_MASK 0x80
217
Amy Malochef3d5a062012-08-16 19:14:11 -0700218/**
219 * enum qpnp_leds - QPNP supported led ids
220 * @QPNP_ID_WLED - White led backlight
221 */
222enum qpnp_leds {
Amy Maloche864a6d52012-10-03 15:58:12 -0700223 QPNP_ID_WLED = 0,
224 QPNP_ID_FLASH1_LED0,
225 QPNP_ID_FLASH1_LED1,
Amy Malocheeea7b592012-10-03 15:59:36 -0700226 QPNP_ID_RGB_RED,
227 QPNP_ID_RGB_GREEN,
228 QPNP_ID_RGB_BLUE,
Amy Malochef3813742013-04-11 19:33:47 -0700229 QPNP_ID_LED_MPP,
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530230 QPNP_ID_KPDBL,
Amy Maloche864a6d52012-10-03 15:58:12 -0700231 QPNP_ID_MAX,
Amy Malochef3d5a062012-08-16 19:14:11 -0700232};
233
234/* current boost limit */
235enum wled_current_boost_limit {
236 WLED_CURR_LIMIT_105mA,
237 WLED_CURR_LIMIT_385mA,
238 WLED_CURR_LIMIT_525mA,
239 WLED_CURR_LIMIT_805mA,
240 WLED_CURR_LIMIT_980mA,
241 WLED_CURR_LIMIT_1260mA,
242 WLED_CURR_LIMIT_1400mA,
243 WLED_CURR_LIMIT_1680mA,
244};
245
246/* over voltage protection threshold */
247enum wled_ovp_threshold {
248 WLED_OVP_35V,
249 WLED_OVP_32V,
250 WLED_OVP_29V,
251 WLED_OVP_37V,
252};
253
Amy Maloche864a6d52012-10-03 15:58:12 -0700254enum flash_headroom {
255 HEADROOM_250mV = 0,
256 HEADROOM_300mV,
257 HEADROOM_400mV,
258 HEADROOM_500mV,
259};
260
261enum flash_startup_dly {
262 DELAY_10us = 0,
263 DELAY_32us,
264 DELAY_64us,
265 DELAY_128us,
266};
267
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530268enum led_mode {
269 PWM_MODE = 0,
270 LPG_MODE,
Amy Malochea2726f02013-05-10 10:19:03 -0700271 MANUAL_MODE,
Amy Malocheeea7b592012-10-03 15:59:36 -0700272};
273
Amy Malochef3d5a062012-08-16 19:14:11 -0700274static u8 wled_debug_regs[] = {
275 /* common registers */
276 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4f,
277 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
278 /* LED1 */
279 0x60, 0x61, 0x62, 0x63, 0x66,
Amy Malochea5ca5552012-10-23 13:34:46 -0700280 /* LED2 */
Amy Malochef3d5a062012-08-16 19:14:11 -0700281 0x70, 0x71, 0x72, 0x73, 0x76,
Amy Malochea5ca5552012-10-23 13:34:46 -0700282 /* LED3 */
Amy Malochef3d5a062012-08-16 19:14:11 -0700283 0x80, 0x81, 0x82, 0x83, 0x86,
284};
285
Amy Maloche864a6d52012-10-03 15:58:12 -0700286static u8 flash_debug_regs[] = {
287 0x40, 0x41, 0x42, 0x43, 0x44, 0x48, 0x49, 0x4b, 0x4c,
288 0x4f, 0x46, 0x47,
289};
290
Amy Malocheeea7b592012-10-03 15:59:36 -0700291static u8 rgb_pwm_debug_regs[] = {
292 0x45, 0x46, 0x47,
293};
Amy Malochef3813742013-04-11 19:33:47 -0700294
295static u8 mpp_debug_regs[] = {
296 0x40, 0x41, 0x42, 0x45, 0x46, 0x4c,
297};
298
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530299static u8 kpdbl_debug_regs[] = {
300 0x40, 0x46, 0xb1, 0xb3, 0xb4, 0xe5,
301};
302
Amy Malochef3d5a062012-08-16 19:14:11 -0700303/**
Amy Malochea2726f02013-05-10 10:19:03 -0700304 * pwm_config_data - pwm configuration data
305 * @lut_params - lut parameters to be used by pwm driver
306 * @pwm_device - pwm device
307 * @pwm_channel - pwm channel to be configured for led
308 * @pwm_period_us - period for pwm, in us
309 * @mode - mode the led operates in
Amy Maloche4cf9f322013-05-29 15:53:46 -0700310 * @old_duty_pcts - storage for duty pcts that may need to be reused
311 * @default_mode - default mode of LED as set in device tree
312 * @use_blink - use blink sysfs entry
313 * @blinking - device is currently blinking w/LPG mode
Amy Malochea2726f02013-05-10 10:19:03 -0700314 */
315struct pwm_config_data {
316 struct lut_params lut_params;
317 struct pwm_device *pwm_dev;
318 int pwm_channel;
319 u32 pwm_period_us;
320 struct pwm_duty_cycles *duty_cycles;
Amy Maloche4cf9f322013-05-29 15:53:46 -0700321 int *old_duty_pcts;
Amy Malochea2726f02013-05-10 10:19:03 -0700322 u8 mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -0700323 u8 default_mode;
Amy Malochea5c4ed82013-06-05 11:05:28 -0700324 bool use_blink;
Amy Maloche4cf9f322013-05-29 15:53:46 -0700325 bool blinking;
Amy Malochea2726f02013-05-10 10:19:03 -0700326};
327
328/**
Amy Malochef3d5a062012-08-16 19:14:11 -0700329 * wled_config_data - wled configuration data
330 * @num_strings - number of wled strings supported
331 * @ovp_val - over voltage protection threshold
332 * @boost_curr_lim - boot current limit
333 * @cp_select - high pole capacitance
334 * @ctrl_delay_us - delay in activation of led
335 * @dig_mod_gen_en - digital module generator
336 * @cs_out_en - current sink output enable
337 * @op_fdbck - selection of output as feedback for the boost
338 */
339struct wled_config_data {
340 u8 num_strings;
341 u8 ovp_val;
342 u8 boost_curr_lim;
343 u8 cp_select;
344 u8 ctrl_delay_us;
345 u8 switch_freq;
Amy Malochebd687672013-03-18 11:23:45 -0700346 u8 op_fdbck;
Amy Maloche0150b5e2013-08-15 18:18:32 -0700347 u8 pmic_version;
Amy Malochef3d5a062012-08-16 19:14:11 -0700348 bool dig_mod_gen_en;
349 bool cs_out_en;
Amy Malochef3d5a062012-08-16 19:14:11 -0700350};
351
352/**
Amy Malochef3813742013-04-11 19:33:47 -0700353 * mpp_config_data - mpp configuration data
Amy Malochea2726f02013-05-10 10:19:03 -0700354 * @pwm_cfg - device pwm configuration
Amy Malochef3813742013-04-11 19:33:47 -0700355 * @current_setting - current setting, 5ma-40ma in 5ma increments
Amy Malochea2726f02013-05-10 10:19:03 -0700356 * @source_sel - source selection
357 * @mode_ctrl - mode control
Chun Zhang874c9ab2013-07-08 17:18:34 -0700358 * @vin_ctrl - input control
359 * @min_brightness - minimum brightness supported
Amy Malochea2726f02013-05-10 10:19:03 -0700360 * @pwm_mode - pwm mode in use
Amy Malochef3813742013-04-11 19:33:47 -0700361 */
362struct mpp_config_data {
Amy Malochea2726f02013-05-10 10:19:03 -0700363 struct pwm_config_data *pwm_cfg;
Amy Malochef3813742013-04-11 19:33:47 -0700364 u8 current_setting;
365 u8 source_sel;
366 u8 mode_ctrl;
Chun Zhang874c9ab2013-07-08 17:18:34 -0700367 u8 vin_ctrl;
368 u8 min_brightness;
Amy Malochea2726f02013-05-10 10:19:03 -0700369 u8 pwm_mode;
Amy Malochef3813742013-04-11 19:33:47 -0700370};
371
372/**
Amy Maloche864a6d52012-10-03 15:58:12 -0700373 * flash_config_data - flash configuration data
374 * @current_prgm - current to be programmed, scaled by max level
375 * @clamp_curr - clamp current to use
376 * @headroom - headroom value to use
377 * @duration - duration of the flash
378 * @enable_module - enable address for particular flash
379 * @trigger_flash - trigger flash
380 * @startup_dly - startup delay for flash
Amy Malochebc97c0d22013-03-24 22:06:16 -0700381 * @strobe_type - select between sw and hw strobe
Chun Zhang0d6ca072013-07-30 21:08:39 -0700382 * @peripheral_subtype - module peripheral subtype
Amy Maloche864a6d52012-10-03 15:58:12 -0700383 * @current_addr - address to write for current
384 * @second_addr - address of secondary flash to be written
385 * @safety_timer - enable safety timer or watchdog timer
Amy Malochebc97c0d22013-03-24 22:06:16 -0700386 * @torch_enable - enable flash LED torch mode
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700387 * @flash_reg_get - flash regulator attached or not
Chun Zhangc3b505b2013-06-03 19:01:49 -0700388 * @flash_on - flash status, on or off
Chun Zhangdf2d3062013-06-25 20:14:46 -0700389 * @torch_on - torch status, on or off
Chun Zhangc3b505b2013-06-03 19:01:49 -0700390 * @flash_boost_reg - boost regulator for flash
Chun Zhangdf2d3062013-06-25 20:14:46 -0700391 * @torch_boost_reg - boost regulator for torch
Amy Maloche864a6d52012-10-03 15:58:12 -0700392 */
393struct flash_config_data {
394 u8 current_prgm;
395 u8 clamp_curr;
396 u8 headroom;
397 u8 duration;
398 u8 enable_module;
399 u8 trigger_flash;
400 u8 startup_dly;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700401 u8 strobe_type;
Chun Zhang0d6ca072013-07-30 21:08:39 -0700402 u8 peripheral_subtype;
Amy Maloche864a6d52012-10-03 15:58:12 -0700403 u16 current_addr;
404 u16 second_addr;
405 bool safety_timer;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700406 bool torch_enable;
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700407 bool flash_reg_get;
Chun Zhangc3b505b2013-06-03 19:01:49 -0700408 bool flash_on;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700409 bool torch_on;
Chun Zhangc3b505b2013-06-03 19:01:49 -0700410 struct regulator *flash_boost_reg;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700411 struct regulator *torch_boost_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -0700412};
413
414/**
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530415 * kpdbl_config_data - kpdbl configuration data
Amy Malochea2726f02013-05-10 10:19:03 -0700416 * @pwm_cfg - device pwm configuration
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530417 * @mode - running mode: pwm or lut
418 * @row_id - row id of the led
419 * @row_src_vbst - 0 for vph_pwr and 1 for vbst
420 * @row_src_en - enable row source
421 * @always_on - always on row
422 * @lut_params - lut parameters to be used by pwm driver
423 * @duty_cycles - duty cycles for lut
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530424 */
425struct kpdbl_config_data {
Amy Malochea2726f02013-05-10 10:19:03 -0700426 struct pwm_config_data *pwm_cfg;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530427 u32 row_id;
428 bool row_src_vbst;
429 bool row_src_en;
430 bool always_on;
431 struct pwm_duty_cycles *duty_cycles;
432 struct lut_params lut_params;
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530433};
434
435/**
Amy Malocheeea7b592012-10-03 15:59:36 -0700436 * rgb_config_data - rgb configuration data
Amy Malochea2726f02013-05-10 10:19:03 -0700437 * @pwm_cfg - device pwm configuration
438 * @enable - bits to enable led
Amy Malocheeea7b592012-10-03 15:59:36 -0700439 */
440struct rgb_config_data {
Amy Malochea2726f02013-05-10 10:19:03 -0700441 struct pwm_config_data *pwm_cfg;
Amy Malocheeea7b592012-10-03 15:59:36 -0700442 u8 enable;
443};
444
445/**
Amy Malochef3d5a062012-08-16 19:14:11 -0700446 * struct qpnp_led_data - internal led data structure
447 * @led_classdev - led class device
Asaf Penso55ac8472013-01-21 21:17:37 +0200448 * @delayed_work - delayed work for turning off the LED
Chun Zhang815a1832013-06-20 13:47:13 -0700449 * @work - workqueue for led
Amy Malochef3d5a062012-08-16 19:14:11 -0700450 * @id - led index
451 * @base_reg - base register given in device tree
452 * @lock - to protect the transactions
453 * @reg - cached value of led register
Amy Malochea5ca5552012-10-23 13:34:46 -0700454 * @num_leds - number of leds in the module
Amy Malochef3d5a062012-08-16 19:14:11 -0700455 * @max_current - maximum current supported by LED
456 * @default_on - true: default state max, false, default state 0
Asaf Penso55ac8472013-01-21 21:17:37 +0200457 * @turn_off_delay_ms - number of msec before turning off the LED
Amy Malochef3d5a062012-08-16 19:14:11 -0700458 */
459struct qpnp_led_data {
460 struct led_classdev cdev;
461 struct spmi_device *spmi_dev;
Asaf Penso55ac8472013-01-21 21:17:37 +0200462 struct delayed_work dwork;
Chun Zhang815a1832013-06-20 13:47:13 -0700463 struct work_struct work;
Amy Malochef3d5a062012-08-16 19:14:11 -0700464 int id;
465 u16 base;
466 u8 reg;
Amy Malochea5ca5552012-10-23 13:34:46 -0700467 u8 num_leds;
Chun Zhang815a1832013-06-20 13:47:13 -0700468 struct mutex lock;
Amy Malochef3d5a062012-08-16 19:14:11 -0700469 struct wled_config_data *wled_cfg;
Amy Maloche864a6d52012-10-03 15:58:12 -0700470 struct flash_config_data *flash_cfg;
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530471 struct kpdbl_config_data *kpdbl_cfg;
Amy Malocheeea7b592012-10-03 15:59:36 -0700472 struct rgb_config_data *rgb_cfg;
Amy Malochef3813742013-04-11 19:33:47 -0700473 struct mpp_config_data *mpp_cfg;
Amy Malochef3d5a062012-08-16 19:14:11 -0700474 int max_current;
475 bool default_on;
Asaf Penso55ac8472013-01-21 21:17:37 +0200476 int turn_off_delay_ms;
Amy Malochef3d5a062012-08-16 19:14:11 -0700477};
478
Mohan Pallaka3a7d3472013-05-02 16:30:19 +0530479static int num_kpbl_leds_on;
480
Amy Malochef3d5a062012-08-16 19:14:11 -0700481static int
482qpnp_led_masked_write(struct qpnp_led_data *led, u16 addr, u8 mask, u8 val)
483{
484 int rc;
485 u8 reg;
486
487 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
488 addr, &reg, 1);
489 if (rc) {
490 dev_err(&led->spmi_dev->dev,
491 "Unable to read from addr=%x, rc(%d)\n", addr, rc);
492 }
493
494 reg &= ~mask;
495 reg |= val;
496
497 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
498 addr, &reg, 1);
499 if (rc)
500 dev_err(&led->spmi_dev->dev,
501 "Unable to write to addr=%x, rc(%d)\n", addr, rc);
502 return rc;
503}
504
Amy Malochea5ca5552012-10-23 13:34:46 -0700505static void qpnp_dump_regs(struct qpnp_led_data *led, u8 regs[], u8 array_size)
506{
507 int i;
508 u8 val;
509
510 pr_debug("===== %s LED register dump start =====\n", led->cdev.name);
511 for (i = 0; i < array_size; i++) {
512 spmi_ext_register_readl(led->spmi_dev->ctrl,
513 led->spmi_dev->sid,
514 led->base + regs[i],
515 &val, sizeof(val));
Mohan Pallaka1f46f022013-03-15 14:56:50 +0530516 pr_debug("%s: 0x%x = 0x%x\n", led->cdev.name,
517 led->base + regs[i], val);
Amy Malochea5ca5552012-10-23 13:34:46 -0700518 }
519 pr_debug("===== %s LED register dump end =====\n", led->cdev.name);
520}
521
Amy Maloche0150b5e2013-08-15 18:18:32 -0700522static int qpnp_wled_sync(struct qpnp_led_data *led)
523{
524 int rc;
525 u8 val;
526
527 /* sync */
528 val = WLED_SYNC_VAL;
529 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
530 WLED_SYNC_REG(led->base), &val, 1);
531 if (rc) {
532 dev_err(&led->spmi_dev->dev,
533 "WLED set sync reg failed(%d)\n", rc);
534 return rc;
535 }
536
537 val = WLED_SYNC_RESET_VAL;
538 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
539 WLED_SYNC_REG(led->base), &val, 1);
540 if (rc) {
541 dev_err(&led->spmi_dev->dev,
542 "WLED reset sync reg failed(%d)\n", rc);
543 return rc;
544 }
545 return 0;
546}
547
Amy Malochef3d5a062012-08-16 19:14:11 -0700548static int qpnp_wled_set(struct qpnp_led_data *led)
549{
Amy Maloched55fdb82013-02-26 18:11:57 -0800550 int rc, duty, level;
Amy Maloche0150b5e2013-08-15 18:18:32 -0700551 u8 val, i, num_wled_strings, sink_val;
552
553 num_wled_strings = led->wled_cfg->num_strings;
Amy Malochef3d5a062012-08-16 19:14:11 -0700554
555 level = led->cdev.brightness;
556
557 if (level > WLED_MAX_LEVEL)
558 level = WLED_MAX_LEVEL;
559 if (level == 0) {
Amy Maloche0150b5e2013-08-15 18:18:32 -0700560 for (i = 0; i < num_wled_strings; i++) {
561 rc = qpnp_led_masked_write(led,
562 WLED_FULL_SCALE_REG(led->base, i),
563 WLED_MAX_CURR_MASK, WLED_NO_CURRENT);
564 if (rc) {
565 dev_err(&led->spmi_dev->dev,
566 "Write max current failure (%d)\n",
567 rc);
568 return rc;
569 }
570 }
571
572 rc = qpnp_wled_sync(led);
573 if (rc) {
574 dev_err(&led->spmi_dev->dev,
575 "WLED sync failed(%d)\n", rc);
576 return rc;
577 }
578
579 rc = spmi_ext_register_readl(led->spmi_dev->ctrl,
580 led->spmi_dev->sid, WLED_CURR_SINK_REG(led->base),
581 &sink_val, 1);
582 if (rc) {
583 dev_err(&led->spmi_dev->dev,
584 "WLED read sink reg failed(%d)\n", rc);
585 return rc;
586 }
587
588 if (led->wled_cfg->pmic_version == PMIC_VER_8026) {
589 val = WLED_DISABLE_ALL_SINKS;
590 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
591 led->spmi_dev->sid,
592 WLED_CURR_SINK_REG(led->base), &val, 1);
593 if (rc) {
594 dev_err(&led->spmi_dev->dev,
595 "WLED write sink reg failed(%d)\n", rc);
596 return rc;
597 }
598
599 usleep(WLED_OVP_DELAY);
600 }
601
Amy Malochef3d5a062012-08-16 19:14:11 -0700602 val = WLED_BOOST_OFF;
603 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
604 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
605 &val, 1);
606 if (rc) {
607 dev_err(&led->spmi_dev->dev,
608 "WLED write ctrl reg failed(%d)\n", rc);
609 return rc;
610 }
Amy Maloche0150b5e2013-08-15 18:18:32 -0700611
612 for (i = 0; i < num_wled_strings; i++) {
613 rc = qpnp_led_masked_write(led,
614 WLED_FULL_SCALE_REG(led->base, i),
615 WLED_MAX_CURR_MASK, led->max_current);
616 if (rc) {
617 dev_err(&led->spmi_dev->dev,
618 "Write max current failure (%d)\n",
619 rc);
620 return rc;
621 }
622 }
623
624 rc = qpnp_wled_sync(led);
625 if (rc) {
626 dev_err(&led->spmi_dev->dev,
627 "WLED sync failed(%d)\n", rc);
628 return rc;
629 }
630
631 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
632 led->spmi_dev->sid, WLED_CURR_SINK_REG(led->base),
633 &sink_val, 1);
634 if (rc) {
635 dev_err(&led->spmi_dev->dev,
636 "WLED write sink reg failed(%d)\n", rc);
637 return rc;
638 }
639
Amy Malochef3d5a062012-08-16 19:14:11 -0700640 } else {
641 val = WLED_BOOST_ON;
642 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
643 led->spmi_dev->sid, WLED_MOD_CTRL_REG(led->base),
644 &val, 1);
645 if (rc) {
646 dev_err(&led->spmi_dev->dev,
647 "WLED write ctrl reg failed(%d)\n", rc);
648 return rc;
649 }
650 }
651
652 duty = (WLED_MAX_DUTY_CYCLE * level) / WLED_MAX_LEVEL;
653
Amy Malochef3d5a062012-08-16 19:14:11 -0700654 /* program brightness control registers */
655 for (i = 0; i < num_wled_strings; i++) {
656 rc = qpnp_led_masked_write(led,
657 WLED_BRIGHTNESS_CNTL_MSB(led->base, i), WLED_MSB_MASK,
658 (duty >> WLED_8_BIT_SHFT) & WLED_4_BIT_MASK);
659 if (rc) {
660 dev_err(&led->spmi_dev->dev,
661 "WLED set brightness MSB failed(%d)\n", rc);
662 return rc;
663 }
664 val = duty & WLED_8_BIT_MASK;
665 rc = spmi_ext_register_writel(led->spmi_dev->ctrl,
666 led->spmi_dev->sid,
667 WLED_BRIGHTNESS_CNTL_LSB(led->base, i), &val, 1);
668 if (rc) {
669 dev_err(&led->spmi_dev->dev,
670 "WLED set brightness LSB failed(%d)\n", rc);
671 return rc;
672 }
673 }
674
Amy Maloche0150b5e2013-08-15 18:18:32 -0700675 rc = qpnp_wled_sync(led);
Amy Malochef3d5a062012-08-16 19:14:11 -0700676 if (rc) {
Amy Maloche0150b5e2013-08-15 18:18:32 -0700677 dev_err(&led->spmi_dev->dev, "WLED sync failed(%d)\n", rc);
Amy Malochef3d5a062012-08-16 19:14:11 -0700678 return rc;
679 }
680 return 0;
681}
682
Amy Malochef3813742013-04-11 19:33:47 -0700683static int qpnp_mpp_set(struct qpnp_led_data *led)
684{
685 int rc, val;
Amy Malochea2726f02013-05-10 10:19:03 -0700686 int duty_us;
Amy Malochef3813742013-04-11 19:33:47 -0700687
688 if (led->cdev.brightness) {
Chun Zhang874c9ab2013-07-08 17:18:34 -0700689 if (led->cdev.brightness < led->mpp_cfg->min_brightness) {
690 dev_warn(&led->spmi_dev->dev,
691 "brightness is less than supported..." \
692 "set to minimum supported\n");
693 led->cdev.brightness = led->mpp_cfg->min_brightness;
694 }
695
Amy Maloche4cf9f322013-05-29 15:53:46 -0700696 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
697 if (!led->mpp_cfg->pwm_cfg->blinking) {
698 led->mpp_cfg->pwm_cfg->mode =
699 led->mpp_cfg->pwm_cfg->default_mode;
700 led->mpp_cfg->pwm_mode =
701 led->mpp_cfg->pwm_cfg->default_mode;
702 }
703 }
Amy Malochea2726f02013-05-10 10:19:03 -0700704 if (led->mpp_cfg->pwm_mode == PWM_MODE) {
705 pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
706 duty_us = (led->mpp_cfg->pwm_cfg->pwm_period_us *
707 led->cdev.brightness) / LED_FULL;
708 /*config pwm for brightness scaling*/
709 rc = pwm_config(led->mpp_cfg->pwm_cfg->pwm_dev,
710 duty_us,
711 led->mpp_cfg->pwm_cfg->pwm_period_us);
712 if (rc < 0) {
713 dev_err(&led->spmi_dev->dev, "Failed to " \
714 "configure pwm for new values\n");
715 return rc;
716 }
Amy Malochef3813742013-04-11 19:33:47 -0700717 }
718
Amy Malochea2726f02013-05-10 10:19:03 -0700719 if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
720 pwm_enable(led->mpp_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka38ec7f32013-09-11 13:01:48 +0530721 else {
722 if (led->cdev.brightness < LED_MPP_CURRENT_MIN)
723 led->cdev.brightness = LED_MPP_CURRENT_MIN;
724
725 val = (led->cdev.brightness / LED_MPP_CURRENT_MIN) - 1;
726
727 rc = qpnp_led_masked_write(led,
728 LED_MPP_SINK_CTRL(led->base),
729 LED_MPP_SINK_MASK, val);
730 if (rc) {
731 dev_err(&led->spmi_dev->dev,
732 "Failed to write sink control reg\n");
733 return rc;
734 }
735 }
Amy Malochea2726f02013-05-10 10:19:03 -0700736
Amy Malochece59f662013-05-02 10:59:53 -0700737 val = (led->mpp_cfg->source_sel & LED_MPP_SRC_MASK) |
738 (led->mpp_cfg->mode_ctrl & LED_MPP_MODE_CTRL_MASK);
Amy Malochef3813742013-04-11 19:33:47 -0700739
740 rc = qpnp_led_masked_write(led,
Amy Malochea2726f02013-05-10 10:19:03 -0700741 LED_MPP_MODE_CTRL(led->base), LED_MPP_MODE_MASK,
742 val);
Amy Malochef3813742013-04-11 19:33:47 -0700743 if (rc) {
744 dev_err(&led->spmi_dev->dev,
745 "Failed to write led mode reg\n");
746 return rc;
747 }
748
749 rc = qpnp_led_masked_write(led,
750 LED_MPP_EN_CTRL(led->base), LED_MPP_EN_MASK,
751 LED_MPP_EN_ENABLE);
Amy Malochea2726f02013-05-10 10:19:03 -0700752 if (rc) {
753 dev_err(&led->spmi_dev->dev,
754 "Failed to write led enable " \
755 "reg\n");
756 return rc;
757 }
Amy Malochef3813742013-04-11 19:33:47 -0700758 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -0700759 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
760 led->mpp_cfg->pwm_cfg->mode =
761 led->mpp_cfg->pwm_cfg->default_mode;
762 led->mpp_cfg->pwm_mode =
763 led->mpp_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -0700764 pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
Amy Maloche4cf9f322013-05-29 15:53:46 -0700765 }
Amy Malochef3813742013-04-11 19:33:47 -0700766 rc = qpnp_led_masked_write(led,
767 LED_MPP_MODE_CTRL(led->base),
768 LED_MPP_MODE_MASK,
769 LED_MPP_MODE_DISABLE);
770 if (rc) {
771 dev_err(&led->spmi_dev->dev,
772 "Failed to write led mode reg\n");
773 return rc;
774 }
775
776 rc = qpnp_led_masked_write(led,
777 LED_MPP_EN_CTRL(led->base),
778 LED_MPP_EN_MASK,
779 LED_MPP_EN_DISABLE);
780 if (rc) {
781 dev_err(&led->spmi_dev->dev,
782 "Failed to write led enable reg\n");
783 return rc;
784 }
785 }
786
Amy Maloche4cf9f322013-05-29 15:53:46 -0700787 if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
788 led->mpp_cfg->pwm_cfg->blinking = false;
Amy Malochef3813742013-04-11 19:33:47 -0700789 qpnp_dump_regs(led, mpp_debug_regs, ARRAY_SIZE(mpp_debug_regs));
790
791 return 0;
792}
793
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700794static int qpnp_flash_regulator_operate(struct qpnp_led_data *led, bool on)
795{
796 int rc, i;
797 struct qpnp_led_data *led_array;
798 bool regulator_on = false;
799
800 led_array = dev_get_drvdata(&led->spmi_dev->dev);
801 if (!led_array) {
802 dev_err(&led->spmi_dev->dev,
803 "Unable to get LED array\n");
804 return -EINVAL;
805 }
806
807 for (i = 0; i < led->num_leds; i++)
808 regulator_on |= led_array[i].flash_cfg->flash_on;
809
810 if (!on)
811 goto regulator_turn_off;
812
813 if (!regulator_on && !led->flash_cfg->flash_on) {
814 for (i = 0; i < led->num_leds; i++) {
815 if (led_array[i].flash_cfg->flash_reg_get) {
816 rc = regulator_enable(
817 led_array[i].flash_cfg->\
818 flash_boost_reg);
819 if (rc) {
820 dev_err(&led->spmi_dev->dev,
821 "Regulator enable failed(%d)\n",
822 rc);
823 return rc;
824 }
825 led->flash_cfg->flash_on = true;
826 }
827 break;
828 }
829 }
830
831 return 0;
832
833regulator_turn_off:
834 if (regulator_on && led->flash_cfg->flash_on) {
835 for (i = 0; i < led->num_leds; i++) {
836 if (led_array[i].flash_cfg->flash_reg_get) {
Chun Zhang9d5ff672013-08-01 18:18:26 -0700837 rc = qpnp_led_masked_write(led,
838 FLASH_ENABLE_CONTROL(led->base),
839 FLASH_ENABLE_MASK,
840 FLASH_DISABLE_ALL);
841 if (rc) {
842 dev_err(&led->spmi_dev->dev,
843 "Enable reg write failed(%d)\n",
844 rc);
845 }
846
Chun Zhangda8ad0f2013-07-17 14:46:47 -0700847 rc = regulator_disable(led_array[i].flash_cfg->\
848 flash_boost_reg);
849 if (rc) {
850 dev_err(&led->spmi_dev->dev,
851 "Regulator disable failed(%d)\n",
852 rc);
853 return rc;
854 }
855 led->flash_cfg->flash_on = false;
856 }
857 break;
858 }
859 }
860
861 return 0;
862}
863
Chun Zhangdf2d3062013-06-25 20:14:46 -0700864static int qpnp_torch_regulator_operate(struct qpnp_led_data *led, bool on)
Amy Maloche864a6d52012-10-03 15:58:12 -0700865{
866 int rc;
Chun Zhangdf2d3062013-06-25 20:14:46 -0700867
868 if (!on)
869 goto regulator_turn_off;
870
871 if (!led->flash_cfg->torch_on) {
872 rc = regulator_enable(led->flash_cfg->torch_boost_reg);
873 if (rc) {
874 dev_err(&led->spmi_dev->dev,
875 "Regulator enable failed(%d)\n", rc);
876 return rc;
877 }
878 led->flash_cfg->torch_on = true;
879 }
880 return 0;
881
882regulator_turn_off:
883 if (led->flash_cfg->torch_on) {
Chun Zhang9d5ff672013-08-01 18:18:26 -0700884 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
885 FLASH_ENABLE_MODULE_MASK, FLASH_DISABLE_ALL);
886 if (rc) {
887 dev_err(&led->spmi_dev->dev,
888 "Enable reg write failed(%d)\n", rc);
889 }
890
Chun Zhangdf2d3062013-06-25 20:14:46 -0700891 rc = regulator_disable(led->flash_cfg->torch_boost_reg);
892 if (rc) {
893 dev_err(&led->spmi_dev->dev,
894 "Regulator disable failed(%d)\n", rc);
895 return rc;
896 }
897 led->flash_cfg->torch_on = false;
898 }
899 return 0;
900}
901
902static int qpnp_flash_set(struct qpnp_led_data *led)
903{
904 int rc, error;
Amy Maloche864a6d52012-10-03 15:58:12 -0700905 int val = led->cdev.brightness;
906
Chun Zhang9c1fbaa2013-06-17 15:31:18 -0700907 if (led->flash_cfg->torch_enable)
908 led->flash_cfg->current_prgm =
909 (val * TORCH_MAX_LEVEL / led->max_current);
910 else
911 led->flash_cfg->current_prgm =
912 (val * FLASH_MAX_LEVEL / led->max_current);
Amy Maloche864a6d52012-10-03 15:58:12 -0700913
Amy Maloche864a6d52012-10-03 15:58:12 -0700914 /* Set led current */
915 if (val > 0) {
Amy Malochebc97c0d22013-03-24 22:06:16 -0700916 if (led->flash_cfg->torch_enable) {
Chun Zhang0d6ca072013-07-30 21:08:39 -0700917 if (led->flash_cfg->peripheral_subtype ==
918 FLASH_SUBTYPE_DUAL) {
919 rc = qpnp_torch_regulator_operate(led, true);
920 if (rc) {
921 dev_err(&led->spmi_dev->dev,
Chun Zhangdf2d3062013-06-25 20:14:46 -0700922 "Torch regulator operate failed(%d)\n",
923 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700924 return rc;
925 }
926 } else if (led->flash_cfg->peripheral_subtype ==
927 FLASH_SUBTYPE_SINGLE) {
928 rc = qpnp_flash_regulator_operate(led, true);
929 if (rc) {
930 dev_err(&led->spmi_dev->dev,
931 "Flash regulator operate failed(%d)\n",
932 rc);
933 goto error_flash_set;
934 }
935
936 /*
937 * Write 0x80 to MODULE_ENABLE before writing
938 * 0xE0 in order to avoid a hardware bug caused
939 * by register value going from 0x00 to 0xE0.
940 */
941 rc = qpnp_led_masked_write(led,
942 FLASH_ENABLE_CONTROL(led->base),
943 FLASH_ENABLE_MODULE_MASK,
944 FLASH_ENABLE_MODULE);
945 if (rc) {
946 dev_err(&led->spmi_dev->dev,
947 "Enable reg write failed(%d)\n",
948 rc);
949 return rc;
950 }
Chun Zhangdf2d3062013-06-25 20:14:46 -0700951 }
952
Amy Malochebc97c0d22013-03-24 22:06:16 -0700953 rc = qpnp_led_masked_write(led,
954 FLASH_LED_UNLOCK_SECURE(led->base),
955 FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
956 if (rc) {
957 dev_err(&led->spmi_dev->dev,
958 "Secure reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700959 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700960 }
961
962 rc = qpnp_led_masked_write(led,
963 FLASH_LED_TORCH(led->base),
964 FLASH_TORCH_MASK, FLASH_LED_TORCH_ENABLE);
965 if (rc) {
966 dev_err(&led->spmi_dev->dev,
967 "Torch reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700968 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700969 }
970
Amy Malochebc97c0d22013-03-24 22:06:16 -0700971 rc = qpnp_led_masked_write(led,
972 led->flash_cfg->current_addr,
Chun Zhange8954cf2013-05-02 11:14:34 -0700973 FLASH_CURRENT_MASK,
974 led->flash_cfg->current_prgm);
Amy Malochebc97c0d22013-03-24 22:06:16 -0700975 if (rc) {
976 dev_err(&led->spmi_dev->dev,
977 "Current reg write failed(%d)\n", rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700978 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700979 }
980
981 rc = qpnp_led_masked_write(led,
982 led->flash_cfg->second_addr,
Chun Zhange8954cf2013-05-02 11:14:34 -0700983 FLASH_CURRENT_MASK,
984 led->flash_cfg->current_prgm);
Amy Malochebc97c0d22013-03-24 22:06:16 -0700985 if (rc) {
986 dev_err(&led->spmi_dev->dev,
987 "2nd Current reg write failed(%d)\n",
988 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700989 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -0700990 }
991
Chun Zhange8954cf2013-05-02 11:14:34 -0700992 qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
993 FLASH_CURRENT_MASK,
Chun Zhang9c1fbaa2013-06-17 15:31:18 -0700994 TORCH_MAX_LEVEL);
Chun Zhange8954cf2013-05-02 11:14:34 -0700995 if (rc) {
996 dev_err(&led->spmi_dev->dev,
997 "Max current reg write failed(%d)\n",
998 rc);
Chun Zhang0d6ca072013-07-30 21:08:39 -0700999 goto error_reg_write;
Chun Zhange8954cf2013-05-02 11:14:34 -07001000 }
1001
Chun Zhang9d5ff672013-08-01 18:18:26 -07001002 rc = qpnp_led_masked_write(led,
1003 FLASH_ENABLE_CONTROL(led->base),
1004 FLASH_ENABLE_MASK,
1005 led->flash_cfg->enable_module);
1006 if (rc) {
1007 dev_err(&led->spmi_dev->dev,
1008 "Enable reg write failed(%d)\n",
1009 rc);
1010 goto error_reg_write;
1011 }
1012
1013 rc = qpnp_led_masked_write(led,
1014 FLASH_LED_STROBE_CTRL(led->base),
1015 led->flash_cfg->trigger_flash,
1016 led->flash_cfg->trigger_flash);
1017 if (rc) {
1018 dev_err(&led->spmi_dev->dev,
1019 "LED %d strobe reg write failed(%d)\n",
1020 led->id, rc);
1021 goto error_reg_write;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001022 }
1023 } else {
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001024 rc = qpnp_flash_regulator_operate(led, true);
1025 if (rc) {
1026 dev_err(&led->spmi_dev->dev,
1027 "Flash regulator operate failed(%d)\n",
1028 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001029 goto error_flash_set;
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001030 }
1031
Chun Zhange8954cf2013-05-02 11:14:34 -07001032 /* Set flash safety timer */
Amy Malochebc97c0d22013-03-24 22:06:16 -07001033 rc = qpnp_led_masked_write(led,
Chun Zhange8954cf2013-05-02 11:14:34 -07001034 FLASH_SAFETY_TIMER(led->base),
1035 FLASH_SAFETY_TIMER_MASK,
1036 led->flash_cfg->duration);
1037 if (rc) {
1038 dev_err(&led->spmi_dev->dev,
1039 "Safety timer reg write failed(%d)\n",
1040 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001041 goto error_flash_set;
Chun Zhange8954cf2013-05-02 11:14:34 -07001042 }
1043
1044 /* Set max current */
1045 rc = qpnp_led_masked_write(led,
1046 FLASH_MAX_CURR(led->base), FLASH_CURRENT_MASK,
1047 FLASH_MAX_LEVEL);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001048 if (rc) {
1049 dev_err(&led->spmi_dev->dev,
1050 "Max current reg write failed(%d)\n",
1051 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001052 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001053 }
1054
Chun Zhange8954cf2013-05-02 11:14:34 -07001055 /* Set clamp current */
1056 rc = qpnp_led_masked_write(led,
1057 FLASH_CLAMP_CURR(led->base),
1058 FLASH_CURRENT_MASK,
1059 led->flash_cfg->clamp_curr);
1060 if (rc) {
1061 dev_err(&led->spmi_dev->dev,
1062 "Clamp current reg write failed(%d)\n",
1063 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001064 goto error_flash_set;
Chun Zhange8954cf2013-05-02 11:14:34 -07001065 }
1066
Chun Zhang0d6ca072013-07-30 21:08:39 -07001067 /*
1068 * Write 0x80 to MODULE_ENABLE before writing
1069 * 0xE0 in order to avoid a hardware bug caused
1070 * by register value going from 0x00 to 0xE0.
Amy Malochebc97c0d22013-03-24 22:06:16 -07001071 */
1072 rc = qpnp_led_masked_write(led,
1073 FLASH_ENABLE_CONTROL(led->base),
Chun Zhang9d5ff672013-08-01 18:18:26 -07001074 FLASH_ENABLE_MODULE_MASK,
1075 FLASH_ENABLE_MODULE);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001076 if (rc) {
1077 dev_err(&led->spmi_dev->dev,
Chun Zhang9d5ff672013-08-01 18:18:26 -07001078 "Enable reg write failed(%d)\n",
1079 rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001080 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001081 }
1082
1083 rc = qpnp_led_masked_write(led,
1084 led->flash_cfg->current_addr,
1085 FLASH_CURRENT_MASK,
1086 led->flash_cfg->current_prgm);
1087 if (rc) {
1088 dev_err(&led->spmi_dev->dev,
1089 "Current reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001090 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001091 }
1092
1093 rc = qpnp_led_masked_write(led,
Amy Malochebc97c0d22013-03-24 22:06:16 -07001094 FLASH_ENABLE_CONTROL(led->base),
Chun Zhang9d5ff672013-08-01 18:18:26 -07001095 led->flash_cfg->enable_module,
1096 led->flash_cfg->enable_module);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001097 if (rc) {
1098 dev_err(&led->spmi_dev->dev,
1099 "Enable reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001100 goto error_flash_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001101 }
Amy Maloche38b9aae2013-02-07 12:15:34 -08001102
Chun Zhang9d5ff672013-08-01 18:18:26 -07001103 if (!led->flash_cfg->strobe_type) {
1104 rc = qpnp_led_masked_write(led,
1105 FLASH_LED_STROBE_CTRL(led->base),
1106 led->flash_cfg->trigger_flash,
1107 led->flash_cfg->trigger_flash);
1108 if (rc) {
1109 dev_err(&led->spmi_dev->dev,
Amy Malochebc97c0d22013-03-24 22:06:16 -07001110 "LED %d strobe reg write failed(%d)\n",
1111 led->id, rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001112 goto error_flash_set;
Chun Zhang9d5ff672013-08-01 18:18:26 -07001113 }
1114 } else {
1115 rc = qpnp_led_masked_write(led,
1116 FLASH_LED_STROBE_CTRL(led->base),
1117 (led->flash_cfg->trigger_flash |
1118 FLASH_STROBE_HW),
1119 (led->flash_cfg->trigger_flash |
1120 FLASH_STROBE_HW));
1121 if (rc) {
1122 dev_err(&led->spmi_dev->dev,
Amy Malochebc97c0d22013-03-24 22:06:16 -07001123 "LED %d strobe reg write failed(%d)\n",
1124 led->id, rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001125 goto error_flash_set;
Chun Zhang9d5ff672013-08-01 18:18:26 -07001126 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07001127 }
Amy Maloche38b9aae2013-02-07 12:15:34 -08001128 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001129 } else {
Chun Zhangdf2d3062013-06-25 20:14:46 -07001130 rc = qpnp_led_masked_write(led,
1131 FLASH_LED_STROBE_CTRL(led->base),
Chun Zhang9d5ff672013-08-01 18:18:26 -07001132 led->flash_cfg->trigger_flash,
Chun Zhangdf2d3062013-06-25 20:14:46 -07001133 FLASH_DISABLE_ALL);
1134 if (rc) {
1135 dev_err(&led->spmi_dev->dev,
1136 "LED %d flash write failed(%d)\n", led->id, rc);
1137 if (led->flash_cfg->torch_enable)
1138 goto error_torch_set;
1139 else
1140 goto error_flash_set;
1141 }
1142
Amy Malochebc97c0d22013-03-24 22:06:16 -07001143 if (led->flash_cfg->torch_enable) {
1144 rc = qpnp_led_masked_write(led,
1145 FLASH_LED_UNLOCK_SECURE(led->base),
1146 FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
1147 if (rc) {
1148 dev_err(&led->spmi_dev->dev,
1149 "Secure reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001150 goto error_torch_set;
Amy Malochebc97c0d22013-03-24 22:06:16 -07001151 }
1152
1153 rc = qpnp_led_masked_write(led,
Chun Zhange8954cf2013-05-02 11:14:34 -07001154 FLASH_LED_TORCH(led->base),
1155 FLASH_TORCH_MASK,
1156 FLASH_LED_TORCH_DISABLE);
Amy Malochebc97c0d22013-03-24 22:06:16 -07001157 if (rc) {
1158 dev_err(&led->spmi_dev->dev,
Chun Zhange8954cf2013-05-02 11:14:34 -07001159 "Torch reg write failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07001160 goto error_torch_set;
1161 }
1162
Chun Zhang0d6ca072013-07-30 21:08:39 -07001163 if (led->flash_cfg->peripheral_subtype ==
1164 FLASH_SUBTYPE_DUAL) {
1165 rc = qpnp_torch_regulator_operate(led, false);
1166 if (rc) {
1167 dev_err(&led->spmi_dev->dev,
1168 "Torch regulator operate failed(%d)\n",
1169 rc);
1170 return rc;
1171 }
1172 } else if (led->flash_cfg->peripheral_subtype ==
1173 FLASH_SUBTYPE_SINGLE) {
1174 rc = qpnp_flash_regulator_operate(led, false);
1175 if (rc) {
1176 dev_err(&led->spmi_dev->dev,
1177 "Flash regulator operate failed(%d)\n",
1178 rc);
1179 return rc;
1180 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07001181 }
Chun Zhangdf2d3062013-06-25 20:14:46 -07001182 } else {
Chun Zhang9d5ff672013-08-01 18:18:26 -07001183 rc = qpnp_led_masked_write(led,
1184 FLASH_ENABLE_CONTROL(led->base),
1185 led->flash_cfg->enable_module &
1186 ~FLASH_ENABLE_MODULE_MASK,
1187 FLASH_DISABLE_ALL);
1188 if (rc) {
1189 dev_err(&led->spmi_dev->dev,
1190 "Enable reg write failed(%d)\n", rc);
1191 if (led->flash_cfg->torch_enable)
1192 goto error_torch_set;
1193 else
1194 goto error_flash_set;
1195 }
1196
Chun Zhangdf2d3062013-06-25 20:14:46 -07001197 rc = qpnp_flash_regulator_operate(led, false);
1198 if (rc) {
1199 dev_err(&led->spmi_dev->dev,
1200 "Flash regulator operate failed(%d)\n",
1201 rc);
1202 return rc;
1203 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001204 }
Amy Maloche864a6d52012-10-03 15:58:12 -07001205 }
1206
Amy Malocheeea7b592012-10-03 15:59:36 -07001207 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
1208
1209 return 0;
Chun Zhangdf2d3062013-06-25 20:14:46 -07001210
Chun Zhang0d6ca072013-07-30 21:08:39 -07001211error_reg_write:
1212 if (led->flash_cfg->peripheral_subtype == FLASH_SUBTYPE_SINGLE)
1213 goto error_flash_set;
1214
Chun Zhangdf2d3062013-06-25 20:14:46 -07001215error_torch_set:
1216 error = qpnp_torch_regulator_operate(led, false);
1217 if (error) {
1218 dev_err(&led->spmi_dev->dev,
1219 "Torch regulator operate failed(%d)\n", rc);
1220 return error;
1221 }
1222 return rc;
1223
1224error_flash_set:
1225 error = qpnp_flash_regulator_operate(led, false);
1226 if (error) {
1227 dev_err(&led->spmi_dev->dev,
1228 "Flash regulator operate failed(%d)\n", rc);
1229 return error;
1230 }
1231 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07001232}
1233
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301234static int qpnp_kpdbl_set(struct qpnp_led_data *led)
1235{
1236 int duty_us;
1237 int rc;
1238
1239 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001240 if (!led->kpdbl_cfg->pwm_cfg->blinking)
1241 led->kpdbl_cfg->pwm_cfg->mode =
1242 led->kpdbl_cfg->pwm_cfg->default_mode;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301243 if (!num_kpbl_leds_on) {
1244 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
1245 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
1246 if (rc) {
1247 dev_err(&led->spmi_dev->dev,
1248 "Enable reg write failed(%d)\n", rc);
1249 return rc;
1250 }
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301251 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301252
1253 if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
1254 duty_us = (led->kpdbl_cfg->pwm_cfg->pwm_period_us *
1255 led->cdev.brightness) / KPDBL_MAX_LEVEL;
1256 rc = pwm_config(led->kpdbl_cfg->pwm_cfg->pwm_dev,
1257 duty_us,
1258 led->kpdbl_cfg->pwm_cfg->pwm_period_us);
1259 if (rc < 0) {
1260 dev_err(&led->spmi_dev->dev, "pwm config failed\n");
1261 return rc;
1262 }
1263 }
1264
Amy Malochea2726f02013-05-10 10:19:03 -07001265 rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301266 if (rc < 0) {
1267 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1268 return rc;
1269 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301270
1271 num_kpbl_leds_on++;
1272
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301273 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001274 led->kpdbl_cfg->pwm_cfg->mode =
1275 led->kpdbl_cfg->pwm_cfg->default_mode;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301276
1277 if (led->kpdbl_cfg->always_on) {
1278 rc = pwm_config(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0,
1279 led->kpdbl_cfg->pwm_cfg->pwm_period_us);
1280 if (rc < 0) {
1281 dev_err(&led->spmi_dev->dev,
1282 "pwm config failed\n");
1283 return rc;
1284 }
1285
1286 rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
1287 if (rc < 0) {
1288 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1289 return rc;
1290 }
1291 } else
1292 pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
1293
1294 if (num_kpbl_leds_on > 0)
1295 num_kpbl_leds_on--;
1296
1297 if (!num_kpbl_leds_on) {
1298 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
1299 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_DIS);
1300 if (rc) {
1301 dev_err(&led->spmi_dev->dev,
1302 "Failed to write led enable reg\n");
1303 return rc;
1304 }
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301305 }
1306 }
1307
Amy Maloche4cf9f322013-05-29 15:53:46 -07001308 led->kpdbl_cfg->pwm_cfg->blinking = false;
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301309
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301310 qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
1311
1312 return 0;
1313}
1314
Amy Malocheeea7b592012-10-03 15:59:36 -07001315static int qpnp_rgb_set(struct qpnp_led_data *led)
1316{
1317 int duty_us;
1318 int rc;
1319
1320 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001321 if (!led->rgb_cfg->pwm_cfg->blinking)
1322 led->rgb_cfg->pwm_cfg->mode =
1323 led->rgb_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07001324 if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
1325 duty_us = (led->rgb_cfg->pwm_cfg->pwm_period_us *
Amy Malocheeea7b592012-10-03 15:59:36 -07001326 led->cdev.brightness) / LED_FULL;
Amy Malochea2726f02013-05-10 10:19:03 -07001327 rc = pwm_config(led->rgb_cfg->pwm_cfg->pwm_dev, duty_us,
1328 led->rgb_cfg->pwm_cfg->pwm_period_us);
Amy Malocheeea7b592012-10-03 15:59:36 -07001329 if (rc < 0) {
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301330 dev_err(&led->spmi_dev->dev,
1331 "pwm config failed\n");
Amy Malocheeea7b592012-10-03 15:59:36 -07001332 return rc;
1333 }
1334 }
1335 rc = qpnp_led_masked_write(led,
1336 RGB_LED_EN_CTL(led->base),
1337 led->rgb_cfg->enable, led->rgb_cfg->enable);
1338 if (rc) {
1339 dev_err(&led->spmi_dev->dev,
1340 "Failed to write led enable reg\n");
1341 return rc;
1342 }
Amy Malochea2726f02013-05-10 10:19:03 -07001343
1344 rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301345 if (rc < 0) {
1346 dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
1347 return rc;
1348 }
Amy Malocheeea7b592012-10-03 15:59:36 -07001349 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07001350 led->rgb_cfg->pwm_cfg->mode =
1351 led->rgb_cfg->pwm_cfg->default_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07001352 pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
Amy Malocheeea7b592012-10-03 15:59:36 -07001353 rc = qpnp_led_masked_write(led,
1354 RGB_LED_EN_CTL(led->base),
1355 led->rgb_cfg->enable, RGB_LED_DISABLE);
1356 if (rc) {
1357 dev_err(&led->spmi_dev->dev,
1358 "Failed to write led enable reg\n");
1359 return rc;
1360 }
1361 }
1362
Amy Maloche4cf9f322013-05-29 15:53:46 -07001363 led->rgb_cfg->pwm_cfg->blinking = false;
Amy Malocheeea7b592012-10-03 15:59:36 -07001364 qpnp_dump_regs(led, rgb_pwm_debug_regs, ARRAY_SIZE(rgb_pwm_debug_regs));
1365
Amy Maloche864a6d52012-10-03 15:58:12 -07001366 return 0;
1367}
1368
Amy Malochef3d5a062012-08-16 19:14:11 -07001369static void qpnp_led_set(struct led_classdev *led_cdev,
1370 enum led_brightness value)
1371{
Amy Malochef3d5a062012-08-16 19:14:11 -07001372 struct qpnp_led_data *led;
1373
1374 led = container_of(led_cdev, struct qpnp_led_data, cdev);
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301375 if (value < LED_OFF) {
Amy Malochea5ca5552012-10-23 13:34:46 -07001376 dev_err(&led->spmi_dev->dev, "Invalid brightness value\n");
Amy Malochef3d5a062012-08-16 19:14:11 -07001377 return;
1378 }
1379
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05301380 if (value > led->cdev.max_brightness)
1381 value = led->cdev.max_brightness;
1382
Chun Zhang815a1832013-06-20 13:47:13 -07001383 led->cdev.brightness = value;
1384 schedule_work(&led->work);
1385}
1386
1387static void __qpnp_led_work(struct qpnp_led_data *led,
1388 enum led_brightness value)
1389{
Chun Zhangda8ad0f2013-07-17 14:46:47 -07001390 int rc;
Chun Zhangc3b505b2013-06-03 19:01:49 -07001391
Chun Zhang815a1832013-06-20 13:47:13 -07001392 mutex_lock(&led->lock);
Amy Malochef3d5a062012-08-16 19:14:11 -07001393
1394 switch (led->id) {
1395 case QPNP_ID_WLED:
1396 rc = qpnp_wled_set(led);
1397 if (rc < 0)
Amy Malochea5ca5552012-10-23 13:34:46 -07001398 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -07001399 "WLED set brightness failed (%d)\n", rc);
1400 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07001401 case QPNP_ID_FLASH1_LED0:
1402 case QPNP_ID_FLASH1_LED1:
1403 rc = qpnp_flash_set(led);
1404 if (rc < 0)
1405 dev_err(&led->spmi_dev->dev,
1406 "FLASH set brightness failed (%d)\n", rc);
1407 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07001408 case QPNP_ID_RGB_RED:
1409 case QPNP_ID_RGB_GREEN:
1410 case QPNP_ID_RGB_BLUE:
1411 rc = qpnp_rgb_set(led);
1412 if (rc < 0)
1413 dev_err(&led->spmi_dev->dev,
1414 "RGB set brightness failed (%d)\n", rc);
1415 break;
Amy Malochef3813742013-04-11 19:33:47 -07001416 case QPNP_ID_LED_MPP:
1417 rc = qpnp_mpp_set(led);
1418 if (rc < 0)
1419 dev_err(&led->spmi_dev->dev,
1420 "MPP set brightness failed (%d)\n", rc);
Amy Maloche14c9eb32013-05-06 13:37:58 -07001421 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301422 case QPNP_ID_KPDBL:
1423 rc = qpnp_kpdbl_set(led);
1424 if (rc < 0)
1425 dev_err(&led->spmi_dev->dev,
1426 "KPDBL set brightness failed (%d)\n", rc);
Amy Malochef3813742013-04-11 19:33:47 -07001427 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07001428 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07001429 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -07001430 break;
1431 }
Chun Zhang815a1832013-06-20 13:47:13 -07001432 mutex_unlock(&led->lock);
Chun Zhangc3b505b2013-06-03 19:01:49 -07001433
Amy Malochef3d5a062012-08-16 19:14:11 -07001434}
1435
Chun Zhang815a1832013-06-20 13:47:13 -07001436static void qpnp_led_work(struct work_struct *work)
1437{
1438 struct qpnp_led_data *led = container_of(work,
1439 struct qpnp_led_data, work);
1440
1441 __qpnp_led_work(led, led->cdev.brightness);
1442
1443 return;
1444}
1445
Amy Malochef3d5a062012-08-16 19:14:11 -07001446static int __devinit qpnp_led_set_max_brightness(struct qpnp_led_data *led)
1447{
1448 switch (led->id) {
1449 case QPNP_ID_WLED:
1450 led->cdev.max_brightness = WLED_MAX_LEVEL;
1451 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07001452 case QPNP_ID_FLASH1_LED0:
1453 case QPNP_ID_FLASH1_LED1:
1454 led->cdev.max_brightness = led->max_current;
1455 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07001456 case QPNP_ID_RGB_RED:
1457 case QPNP_ID_RGB_GREEN:
1458 case QPNP_ID_RGB_BLUE:
1459 led->cdev.max_brightness = RGB_MAX_LEVEL;
1460 break;
Amy Malochef3813742013-04-11 19:33:47 -07001461 case QPNP_ID_LED_MPP:
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05301462 if (led->mpp_cfg->pwm_mode == MANUAL_MODE)
1463 led->cdev.max_brightness = led->max_current;
1464 else
1465 led->cdev.max_brightness = MPP_MAX_LEVEL;
Amy Malochef3813742013-04-11 19:33:47 -07001466 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05301467 case QPNP_ID_KPDBL:
1468 led->cdev.max_brightness = KPDBL_MAX_LEVEL;
1469 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07001470 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07001471 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malochef3d5a062012-08-16 19:14:11 -07001472 return -EINVAL;
1473 }
1474
1475 return 0;
1476}
1477
1478static enum led_brightness qpnp_led_get(struct led_classdev *led_cdev)
1479{
1480 struct qpnp_led_data *led;
1481
1482 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1483
1484 return led->cdev.brightness;
1485}
1486
Asaf Penso55ac8472013-01-21 21:17:37 +02001487static void qpnp_led_turn_off_delayed(struct work_struct *work)
1488{
1489 struct delayed_work *dwork = to_delayed_work(work);
1490 struct qpnp_led_data *led
1491 = container_of(dwork, struct qpnp_led_data, dwork);
1492
1493 led->cdev.brightness = LED_OFF;
1494 qpnp_led_set(&led->cdev, led->cdev.brightness);
1495}
1496
1497static void qpnp_led_turn_off(struct qpnp_led_data *led)
1498{
1499 INIT_DELAYED_WORK(&led->dwork, qpnp_led_turn_off_delayed);
1500 schedule_delayed_work(&led->dwork,
1501 msecs_to_jiffies(led->turn_off_delay_ms));
1502}
1503
Amy Malochef3d5a062012-08-16 19:14:11 -07001504static int __devinit qpnp_wled_init(struct qpnp_led_data *led)
1505{
1506 int rc, i;
1507 u8 num_wled_strings;
1508
1509 num_wled_strings = led->wled_cfg->num_strings;
1510
1511 /* verify ranges */
1512 if (led->wled_cfg->ovp_val > WLED_OVP_37V) {
1513 dev_err(&led->spmi_dev->dev, "Invalid ovp value\n");
1514 return -EINVAL;
1515 }
1516
1517 if (led->wled_cfg->boost_curr_lim > WLED_CURR_LIMIT_1680mA) {
1518 dev_err(&led->spmi_dev->dev, "Invalid boost current limit\n");
1519 return -EINVAL;
1520 }
1521
1522 if (led->wled_cfg->cp_select > WLED_CP_SELECT_MAX) {
1523 dev_err(&led->spmi_dev->dev, "Invalid pole capacitance\n");
1524 return -EINVAL;
1525 }
1526
1527 if ((led->max_current > WLED_MAX_CURR)) {
1528 dev_err(&led->spmi_dev->dev, "Invalid max current\n");
1529 return -EINVAL;
1530 }
1531
1532 if ((led->wled_cfg->ctrl_delay_us % WLED_CTL_DLY_STEP) ||
1533 (led->wled_cfg->ctrl_delay_us > WLED_CTL_DLY_MAX)) {
1534 dev_err(&led->spmi_dev->dev, "Invalid control delay\n");
1535 return -EINVAL;
1536 }
1537
1538 /* program over voltage protection threshold */
1539 rc = qpnp_led_masked_write(led, WLED_OVP_CFG_REG(led->base),
1540 WLED_OVP_VAL_MASK,
1541 (led->wled_cfg->ovp_val << WLED_OVP_VAL_BIT_SHFT));
1542 if (rc) {
1543 dev_err(&led->spmi_dev->dev,
1544 "WLED OVP reg write failed(%d)\n", rc);
1545 return rc;
1546 }
1547
1548 /* program current boost limit */
1549 rc = qpnp_led_masked_write(led, WLED_BOOST_LIMIT_REG(led->base),
1550 WLED_BOOST_LIMIT_MASK, led->wled_cfg->boost_curr_lim);
1551 if (rc) {
1552 dev_err(&led->spmi_dev->dev,
1553 "WLED boost limit reg write failed(%d)\n", rc);
1554 return rc;
1555 }
1556
1557 /* program output feedback */
1558 rc = qpnp_led_masked_write(led, WLED_FDBCK_CTRL_REG(led->base),
1559 WLED_OP_FDBCK_MASK,
1560 (led->wled_cfg->op_fdbck << WLED_OP_FDBCK_BIT_SHFT));
1561 if (rc) {
1562 dev_err(&led->spmi_dev->dev,
1563 "WLED fdbck ctrl reg write failed(%d)\n", rc);
1564 return rc;
1565 }
1566
1567 /* program switch frequency */
Amy Maloche9eccb4c2013-07-12 14:31:56 -07001568 rc = qpnp_led_masked_write(led,
1569 WLED_SWITCHING_FREQ_REG(led->base),
Amy Malochef3d5a062012-08-16 19:14:11 -07001570 WLED_SWITCH_FREQ_MASK, led->wled_cfg->switch_freq);
1571 if (rc) {
1572 dev_err(&led->spmi_dev->dev,
Amy Maloche9eccb4c2013-07-12 14:31:56 -07001573 "WLED switch freq reg write failed(%d)\n", rc);
Amy Malochef3d5a062012-08-16 19:14:11 -07001574 return rc;
1575 }
1576
1577 /* program current sink */
1578 if (led->wled_cfg->cs_out_en) {
1579 rc = qpnp_led_masked_write(led, WLED_CURR_SINK_REG(led->base),
1580 WLED_CURR_SINK_MASK,
Amy Maloche832d90a2013-01-07 10:15:29 -08001581 (((1 << led->wled_cfg->num_strings) - 1)
1582 << WLED_CURR_SINK_SHFT));
Amy Malochef3d5a062012-08-16 19:14:11 -07001583 if (rc) {
1584 dev_err(&led->spmi_dev->dev,
1585 "WLED curr sink reg write failed(%d)\n", rc);
1586 return rc;
1587 }
1588 }
1589
1590 /* program high pole capacitance */
1591 rc = qpnp_led_masked_write(led, WLED_HIGH_POLE_CAP_REG(led->base),
1592 WLED_CP_SELECT_MASK, led->wled_cfg->cp_select);
1593 if (rc) {
1594 dev_err(&led->spmi_dev->dev,
1595 "WLED pole cap reg write failed(%d)\n", rc);
1596 return rc;
1597 }
1598
1599 /* program modulator, current mod src and cabc */
1600 for (i = 0; i < num_wled_strings; i++) {
1601 rc = qpnp_led_masked_write(led, WLED_MOD_EN_REG(led->base, i),
1602 WLED_NO_MASK, WLED_EN_MASK);
1603 if (rc) {
1604 dev_err(&led->spmi_dev->dev,
1605 "WLED mod enable reg write failed(%d)\n", rc);
1606 return rc;
1607 }
1608
1609 if (led->wled_cfg->dig_mod_gen_en) {
1610 rc = qpnp_led_masked_write(led,
Amy Maloched44516e2013-02-14 17:36:34 -08001611 WLED_MOD_SRC_SEL_REG(led->base, i),
Amy Malochef3d5a062012-08-16 19:14:11 -07001612 WLED_NO_MASK, WLED_USE_EXT_GEN_MOD_SRC);
1613 if (rc) {
1614 dev_err(&led->spmi_dev->dev,
1615 "WLED dig mod en reg write failed(%d)\n", rc);
1616 }
1617 }
1618
1619 rc = qpnp_led_masked_write(led,
1620 WLED_FULL_SCALE_REG(led->base, i), WLED_MAX_CURR_MASK,
1621 led->max_current);
1622 if (rc) {
1623 dev_err(&led->spmi_dev->dev,
1624 "WLED max current reg write failed(%d)\n", rc);
1625 return rc;
1626 }
1627
1628 }
1629
1630 /* dump wled registers */
Amy Malochea5ca5552012-10-23 13:34:46 -07001631 qpnp_dump_regs(led, wled_debug_regs, ARRAY_SIZE(wled_debug_regs));
Amy Malochef3d5a062012-08-16 19:14:11 -07001632
1633 return 0;
1634}
1635
Amy Malochebc97c0d22013-03-24 22:06:16 -07001636static ssize_t led_mode_store(struct device *dev,
1637 struct device_attribute *attr,
1638 const char *buf, size_t count)
1639{
1640 struct qpnp_led_data *led;
1641 unsigned long state;
1642 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1643 ssize_t ret = -EINVAL;
1644
1645 ret = kstrtoul(buf, 10, &state);
1646 if (ret)
1647 return ret;
1648
1649 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1650
1651 /* '1' to enable torch mode; '0' to switch to flash mode */
1652 if (state == 1)
1653 led->flash_cfg->torch_enable = true;
1654 else
1655 led->flash_cfg->torch_enable = false;
1656
1657 return count;
1658}
1659
1660static ssize_t led_strobe_type_store(struct device *dev,
1661 struct device_attribute *attr,
1662 const char *buf, size_t count)
1663{
1664 struct qpnp_led_data *led;
1665 unsigned long state;
1666 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1667 ssize_t ret = -EINVAL;
1668
1669 ret = kstrtoul(buf, 10, &state);
1670 if (ret)
1671 return ret;
1672
1673 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1674
1675 /* '0' for sw strobe; '1' for hw strobe */
1676 if (state == 1)
1677 led->flash_cfg->strobe_type = 1;
1678 else
1679 led->flash_cfg->strobe_type = 0;
1680
1681 return count;
1682}
1683
Amy Malochea5c4ed82013-06-05 11:05:28 -07001684static int qpnp_pwm_init(struct pwm_config_data *pwm_cfg,
1685 struct spmi_device *spmi_dev,
1686 const char *name)
1687{
1688 int rc, start_idx, idx_len;
1689
1690 if (pwm_cfg->pwm_channel != -1) {
1691 pwm_cfg->pwm_dev =
1692 pwm_request(pwm_cfg->pwm_channel, name);
1693
1694 if (IS_ERR_OR_NULL(pwm_cfg->pwm_dev)) {
1695 dev_err(&spmi_dev->dev,
1696 "could not acquire PWM Channel %d, " \
1697 "error %ld\n",
1698 pwm_cfg->pwm_channel,
1699 PTR_ERR(pwm_cfg->pwm_dev));
1700 pwm_cfg->pwm_dev = NULL;
1701 return -ENODEV;
1702 }
1703
1704 if (pwm_cfg->mode == LPG_MODE) {
1705 start_idx =
1706 pwm_cfg->duty_cycles->start_idx;
1707 idx_len =
1708 pwm_cfg->duty_cycles->num_duty_pcts;
1709
1710 if (idx_len >= PWM_LUT_MAX_SIZE &&
1711 start_idx) {
1712 dev_err(&spmi_dev->dev,
1713 "Wrong LUT size or index\n");
1714 return -EINVAL;
1715 }
1716 if ((start_idx + idx_len) >
1717 PWM_LUT_MAX_SIZE) {
1718 dev_err(&spmi_dev->dev,
1719 "Exceed LUT limit\n");
1720 return -EINVAL;
1721 }
1722 rc = pwm_lut_config(pwm_cfg->pwm_dev,
1723 PM_PWM_PERIOD_MIN, /* ignored by hardware */
1724 pwm_cfg->duty_cycles->duty_pcts,
1725 pwm_cfg->lut_params);
1726 if (rc < 0) {
1727 dev_err(&spmi_dev->dev, "Failed to " \
1728 "configure pwm LUT\n");
1729 return rc;
1730 }
1731 }
1732 } else {
1733 dev_err(&spmi_dev->dev,
1734 "Invalid PWM channel\n");
1735 return -EINVAL;
1736 }
1737
1738 return 0;
1739}
1740
Amy Maloche4cf9f322013-05-29 15:53:46 -07001741static ssize_t pwm_us_store(struct device *dev,
1742 struct device_attribute *attr,
1743 const char *buf, size_t count)
1744{
1745 struct qpnp_led_data *led;
1746 u32 pwm_us;
1747 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1748 ssize_t ret;
1749 u32 previous_pwm_us;
1750 struct pwm_config_data *pwm_cfg;
1751
1752 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1753
1754 ret = kstrtou32(buf, 10, &pwm_us);
1755 if (ret)
1756 return ret;
1757
1758 switch (led->id) {
1759 case QPNP_ID_LED_MPP:
1760 pwm_cfg = led->mpp_cfg->pwm_cfg;
1761 break;
1762 case QPNP_ID_RGB_RED:
1763 case QPNP_ID_RGB_GREEN:
1764 case QPNP_ID_RGB_BLUE:
1765 pwm_cfg = led->rgb_cfg->pwm_cfg;
1766 break;
1767 default:
1768 dev_err(&led->spmi_dev->dev,
1769 "Invalid LED id type for pwm_us\n");
1770 return -EINVAL;
1771 }
1772
1773 if (pwm_cfg->mode == LPG_MODE)
1774 pwm_cfg->blinking = true;
1775
1776 previous_pwm_us = pwm_cfg->pwm_period_us;
1777
1778 pwm_cfg->pwm_period_us = pwm_us;
1779 pwm_free(pwm_cfg->pwm_dev);
1780 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1781 if (ret) {
1782 pwm_cfg->pwm_period_us = previous_pwm_us;
1783 pwm_free(pwm_cfg->pwm_dev);
1784 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1785 qpnp_led_set(&led->cdev, led->cdev.brightness);
1786 dev_err(&led->spmi_dev->dev,
1787 "Failed to initialize pwm with new pwm_us value\n");
1788 return ret;
1789 }
1790 qpnp_led_set(&led->cdev, led->cdev.brightness);
1791 return count;
1792}
1793
1794static ssize_t pause_lo_store(struct device *dev,
1795 struct device_attribute *attr,
1796 const char *buf, size_t count)
1797{
1798 struct qpnp_led_data *led;
1799 u32 pause_lo;
1800 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1801 ssize_t ret;
1802 u32 previous_pause_lo;
1803 struct pwm_config_data *pwm_cfg;
1804
1805 ret = kstrtou32(buf, 10, &pause_lo);
1806 if (ret)
1807 return ret;
1808 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1809
1810 switch (led->id) {
1811 case QPNP_ID_LED_MPP:
1812 pwm_cfg = led->mpp_cfg->pwm_cfg;
1813 break;
1814 case QPNP_ID_RGB_RED:
1815 case QPNP_ID_RGB_GREEN:
1816 case QPNP_ID_RGB_BLUE:
1817 pwm_cfg = led->rgb_cfg->pwm_cfg;
1818 break;
1819 default:
1820 dev_err(&led->spmi_dev->dev,
1821 "Invalid LED id type for pause lo\n");
1822 return -EINVAL;
1823 }
1824
1825 if (pwm_cfg->mode == LPG_MODE)
1826 pwm_cfg->blinking = true;
1827
1828 previous_pause_lo = pwm_cfg->lut_params.lut_pause_lo;
1829
1830 pwm_free(pwm_cfg->pwm_dev);
1831 pwm_cfg->lut_params.lut_pause_lo = pause_lo;
1832 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1833 if (ret) {
1834 pwm_cfg->lut_params.lut_pause_lo = previous_pause_lo;
1835 pwm_free(pwm_cfg->pwm_dev);
1836 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1837 qpnp_led_set(&led->cdev, led->cdev.brightness);
1838 dev_err(&led->spmi_dev->dev,
1839 "Failed to initialize pwm with new pause lo value\n");
1840 return ret;
1841 }
1842 qpnp_led_set(&led->cdev, led->cdev.brightness);
1843 return count;
1844}
1845
1846static ssize_t pause_hi_store(struct device *dev,
1847 struct device_attribute *attr,
1848 const char *buf, size_t count)
1849{
1850 struct qpnp_led_data *led;
1851 u32 pause_hi;
1852 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1853 ssize_t ret;
1854 u32 previous_pause_hi;
1855 struct pwm_config_data *pwm_cfg;
1856
1857 ret = kstrtou32(buf, 10, &pause_hi);
1858 if (ret)
1859 return ret;
1860 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1861
1862 switch (led->id) {
1863 case QPNP_ID_LED_MPP:
1864 pwm_cfg = led->mpp_cfg->pwm_cfg;
1865 break;
1866 case QPNP_ID_RGB_RED:
1867 case QPNP_ID_RGB_GREEN:
1868 case QPNP_ID_RGB_BLUE:
1869 pwm_cfg = led->rgb_cfg->pwm_cfg;
1870 break;
1871 default:
1872 dev_err(&led->spmi_dev->dev,
1873 "Invalid LED id type for pause hi\n");
1874 return -EINVAL;
1875 }
1876
1877 if (pwm_cfg->mode == LPG_MODE)
1878 pwm_cfg->blinking = true;
1879
1880 previous_pause_hi = pwm_cfg->lut_params.lut_pause_hi;
1881
1882 pwm_free(pwm_cfg->pwm_dev);
1883 pwm_cfg->lut_params.lut_pause_hi = pause_hi;
1884 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1885 if (ret) {
1886 pwm_cfg->lut_params.lut_pause_hi = previous_pause_hi;
1887 pwm_free(pwm_cfg->pwm_dev);
1888 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1889 qpnp_led_set(&led->cdev, led->cdev.brightness);
1890 dev_err(&led->spmi_dev->dev,
1891 "Failed to initialize pwm with new pause hi value\n");
1892 return ret;
1893 }
1894 qpnp_led_set(&led->cdev, led->cdev.brightness);
1895 return count;
1896}
1897
1898static ssize_t start_idx_store(struct device *dev,
1899 struct device_attribute *attr,
1900 const char *buf, size_t count)
1901{
1902 struct qpnp_led_data *led;
1903 u32 start_idx;
1904 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1905 ssize_t ret;
1906 u32 previous_start_idx;
1907 struct pwm_config_data *pwm_cfg;
1908
1909 ret = kstrtou32(buf, 10, &start_idx);
1910 if (ret)
1911 return ret;
1912 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1913
1914 switch (led->id) {
1915 case QPNP_ID_LED_MPP:
1916 pwm_cfg = led->mpp_cfg->pwm_cfg;
1917 break;
1918 case QPNP_ID_RGB_RED:
1919 case QPNP_ID_RGB_GREEN:
1920 case QPNP_ID_RGB_BLUE:
1921 pwm_cfg = led->rgb_cfg->pwm_cfg;
1922 break;
1923 default:
1924 dev_err(&led->spmi_dev->dev,
1925 "Invalid LED id type for start idx\n");
1926 return -EINVAL;
1927 }
1928
1929 if (pwm_cfg->mode == LPG_MODE)
1930 pwm_cfg->blinking = true;
1931
1932 previous_start_idx = pwm_cfg->duty_cycles->start_idx;
1933 pwm_cfg->duty_cycles->start_idx = start_idx;
1934 pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
1935 pwm_free(pwm_cfg->pwm_dev);
1936 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1937 if (ret) {
1938 pwm_cfg->duty_cycles->start_idx = previous_start_idx;
1939 pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
1940 pwm_free(pwm_cfg->pwm_dev);
1941 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1942 qpnp_led_set(&led->cdev, led->cdev.brightness);
1943 dev_err(&led->spmi_dev->dev,
1944 "Failed to initialize pwm with new start idx value\n");
1945 return ret;
1946 }
1947 qpnp_led_set(&led->cdev, led->cdev.brightness);
1948 return count;
1949}
1950
1951static ssize_t ramp_step_ms_store(struct device *dev,
1952 struct device_attribute *attr,
1953 const char *buf, size_t count)
1954{
1955 struct qpnp_led_data *led;
1956 u32 ramp_step_ms;
1957 struct led_classdev *led_cdev = dev_get_drvdata(dev);
1958 ssize_t ret;
1959 u32 previous_ramp_step_ms;
1960 struct pwm_config_data *pwm_cfg;
1961
1962 ret = kstrtou32(buf, 10, &ramp_step_ms);
1963 if (ret)
1964 return ret;
1965 led = container_of(led_cdev, struct qpnp_led_data, cdev);
1966
1967 switch (led->id) {
1968 case QPNP_ID_LED_MPP:
1969 pwm_cfg = led->mpp_cfg->pwm_cfg;
1970 break;
1971 case QPNP_ID_RGB_RED:
1972 case QPNP_ID_RGB_GREEN:
1973 case QPNP_ID_RGB_BLUE:
1974 pwm_cfg = led->rgb_cfg->pwm_cfg;
1975 break;
1976 default:
1977 dev_err(&led->spmi_dev->dev,
1978 "Invalid LED id type for ramp step\n");
1979 return -EINVAL;
1980 }
1981
1982 if (pwm_cfg->mode == LPG_MODE)
1983 pwm_cfg->blinking = true;
1984
1985 previous_ramp_step_ms = pwm_cfg->lut_params.ramp_step_ms;
1986
1987 pwm_free(pwm_cfg->pwm_dev);
1988 pwm_cfg->lut_params.ramp_step_ms = ramp_step_ms;
1989 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1990 if (ret) {
1991 pwm_cfg->lut_params.ramp_step_ms = previous_ramp_step_ms;
1992 pwm_free(pwm_cfg->pwm_dev);
1993 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
1994 qpnp_led_set(&led->cdev, led->cdev.brightness);
1995 dev_err(&led->spmi_dev->dev,
1996 "Failed to initialize pwm with new ramp step value\n");
1997 return ret;
1998 }
1999 qpnp_led_set(&led->cdev, led->cdev.brightness);
2000 return count;
2001}
2002
2003static ssize_t lut_flags_store(struct device *dev,
2004 struct device_attribute *attr,
2005 const char *buf, size_t count)
2006{
2007 struct qpnp_led_data *led;
2008 u32 lut_flags;
2009 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2010 ssize_t ret;
2011 u32 previous_lut_flags;
2012 struct pwm_config_data *pwm_cfg;
2013
2014 ret = kstrtou32(buf, 10, &lut_flags);
2015 if (ret)
2016 return ret;
2017 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2018
2019 switch (led->id) {
2020 case QPNP_ID_LED_MPP:
2021 pwm_cfg = led->mpp_cfg->pwm_cfg;
2022 break;
2023 case QPNP_ID_RGB_RED:
2024 case QPNP_ID_RGB_GREEN:
2025 case QPNP_ID_RGB_BLUE:
2026 pwm_cfg = led->rgb_cfg->pwm_cfg;
2027 break;
2028 default:
2029 dev_err(&led->spmi_dev->dev,
2030 "Invalid LED id type for lut flags\n");
2031 return -EINVAL;
2032 }
2033
2034 if (pwm_cfg->mode == LPG_MODE)
2035 pwm_cfg->blinking = true;
2036
2037 previous_lut_flags = pwm_cfg->lut_params.flags;
2038
2039 pwm_free(pwm_cfg->pwm_dev);
2040 pwm_cfg->lut_params.flags = lut_flags;
2041 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2042 if (ret) {
2043 pwm_cfg->lut_params.flags = previous_lut_flags;
2044 pwm_free(pwm_cfg->pwm_dev);
2045 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2046 qpnp_led_set(&led->cdev, led->cdev.brightness);
2047 dev_err(&led->spmi_dev->dev,
2048 "Failed to initialize pwm with new lut flags value\n");
2049 return ret;
2050 }
2051 qpnp_led_set(&led->cdev, led->cdev.brightness);
2052 return count;
2053}
2054
2055static ssize_t duty_pcts_store(struct device *dev,
2056 struct device_attribute *attr,
2057 const char *buf, size_t count)
2058{
2059 struct qpnp_led_data *led;
2060 int num_duty_pcts = 0;
2061 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2062 char *buffer;
2063 ssize_t ret;
2064 int i = 0;
2065 int max_duty_pcts;
2066 struct pwm_config_data *pwm_cfg;
2067 u32 previous_num_duty_pcts;
2068 int value;
2069 int *previous_duty_pcts;
2070
2071 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2072
2073 switch (led->id) {
2074 case QPNP_ID_LED_MPP:
2075 pwm_cfg = led->mpp_cfg->pwm_cfg;
2076 max_duty_pcts = PWM_LUT_MAX_SIZE;
2077 break;
2078 case QPNP_ID_RGB_RED:
2079 case QPNP_ID_RGB_GREEN:
2080 case QPNP_ID_RGB_BLUE:
2081 pwm_cfg = led->rgb_cfg->pwm_cfg;
2082 max_duty_pcts = PWM_LUT_MAX_SIZE;
2083 break;
2084 default:
2085 dev_err(&led->spmi_dev->dev,
2086 "Invalid LED id type for duty pcts\n");
2087 return -EINVAL;
2088 }
2089
2090 if (pwm_cfg->mode == LPG_MODE)
2091 pwm_cfg->blinking = true;
2092
2093 buffer = (char *)buf;
2094
2095 for (i = 0; i < max_duty_pcts; i++) {
2096 if (buffer == NULL)
2097 break;
2098 ret = sscanf((const char *)buffer, "%u,%s", &value, buffer);
2099 pwm_cfg->old_duty_pcts[i] = value;
2100 num_duty_pcts++;
2101 if (ret <= 1)
2102 break;
2103 }
2104
2105 if (num_duty_pcts >= max_duty_pcts) {
2106 dev_err(&led->spmi_dev->dev,
2107 "Number of duty pcts given exceeds max (%d)\n",
2108 max_duty_pcts);
2109 return -EINVAL;
2110 }
2111
2112 previous_num_duty_pcts = pwm_cfg->duty_cycles->num_duty_pcts;
2113 previous_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
2114
2115 pwm_cfg->duty_cycles->num_duty_pcts = num_duty_pcts;
2116 pwm_cfg->duty_cycles->duty_pcts = pwm_cfg->old_duty_pcts;
2117 pwm_cfg->old_duty_pcts = previous_duty_pcts;
2118 pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
2119
2120 pwm_free(pwm_cfg->pwm_dev);
2121 ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2122 if (ret)
2123 goto restore;
2124
2125 qpnp_led_set(&led->cdev, led->cdev.brightness);
2126 return count;
2127
2128restore:
2129 dev_err(&led->spmi_dev->dev,
2130 "Failed to initialize pwm with new duty pcts value\n");
2131 pwm_cfg->duty_cycles->num_duty_pcts = previous_num_duty_pcts;
2132 pwm_cfg->old_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
2133 pwm_cfg->duty_cycles->duty_pcts = previous_duty_pcts;
2134 pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
2135 pwm_free(pwm_cfg->pwm_dev);
2136 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2137 qpnp_led_set(&led->cdev, led->cdev.brightness);
2138 return ret;
2139}
2140
Amy Malochea5c4ed82013-06-05 11:05:28 -07002141static void led_blink(struct qpnp_led_data *led,
2142 struct pwm_config_data *pwm_cfg)
2143{
Amy Malochea5c4ed82013-06-05 11:05:28 -07002144 if (pwm_cfg->use_blink) {
2145 if (led->cdev.brightness) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07002146 pwm_cfg->blinking = true;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002147 if (led->id == QPNP_ID_LED_MPP)
2148 led->mpp_cfg->pwm_mode = LPG_MODE;
2149 pwm_cfg->mode = LPG_MODE;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002150 } else {
Amy Maloche4cf9f322013-05-29 15:53:46 -07002151 pwm_cfg->blinking = false;
2152 pwm_cfg->mode = pwm_cfg->default_mode;
2153 if (led->id == QPNP_ID_LED_MPP)
2154 led->mpp_cfg->pwm_mode = pwm_cfg->default_mode;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002155 }
Amy Maloche4cf9f322013-05-29 15:53:46 -07002156 pwm_free(pwm_cfg->pwm_dev);
2157 qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
2158 qpnp_led_set(&led->cdev, led->cdev.brightness);
Amy Malochea5c4ed82013-06-05 11:05:28 -07002159 }
2160}
2161
2162static ssize_t blink_store(struct device *dev,
2163 struct device_attribute *attr,
2164 const char *buf, size_t count)
2165{
2166 struct qpnp_led_data *led;
2167 unsigned long blinking;
2168 struct led_classdev *led_cdev = dev_get_drvdata(dev);
2169 ssize_t ret = -EINVAL;
2170
2171 ret = kstrtoul(buf, 10, &blinking);
2172 if (ret)
2173 return ret;
2174 led = container_of(led_cdev, struct qpnp_led_data, cdev);
2175 led->cdev.brightness = blinking ? led->cdev.max_brightness : 0;
2176
2177 switch (led->id) {
2178 case QPNP_ID_LED_MPP:
2179 led_blink(led, led->mpp_cfg->pwm_cfg);
2180 break;
2181 case QPNP_ID_RGB_RED:
2182 case QPNP_ID_RGB_GREEN:
2183 case QPNP_ID_RGB_BLUE:
2184 led_blink(led, led->rgb_cfg->pwm_cfg);
2185 break;
2186 default:
2187 dev_err(&led->spmi_dev->dev, "Invalid LED id type for blink\n");
2188 return -EINVAL;
2189 }
2190 return count;
2191}
2192
Amy Malochebc97c0d22013-03-24 22:06:16 -07002193static DEVICE_ATTR(led_mode, 0664, NULL, led_mode_store);
2194static DEVICE_ATTR(strobe, 0664, NULL, led_strobe_type_store);
Amy Maloche4cf9f322013-05-29 15:53:46 -07002195static DEVICE_ATTR(pwm_us, 0664, NULL, pwm_us_store);
2196static DEVICE_ATTR(pause_lo, 0664, NULL, pause_lo_store);
2197static DEVICE_ATTR(pause_hi, 0664, NULL, pause_hi_store);
2198static DEVICE_ATTR(start_idx, 0664, NULL, start_idx_store);
2199static DEVICE_ATTR(ramp_step_ms, 0664, NULL, ramp_step_ms_store);
2200static DEVICE_ATTR(lut_flags, 0664, NULL, lut_flags_store);
2201static DEVICE_ATTR(duty_pcts, 0664, NULL, duty_pcts_store);
Amy Malochea5c4ed82013-06-05 11:05:28 -07002202static DEVICE_ATTR(blink, 0664, NULL, blink_store);
Amy Malochebc97c0d22013-03-24 22:06:16 -07002203
2204static struct attribute *led_attrs[] = {
2205 &dev_attr_led_mode.attr,
2206 &dev_attr_strobe.attr,
Amy Maloche12ad6f32013-04-02 14:39:24 -07002207 NULL
Amy Malochebc97c0d22013-03-24 22:06:16 -07002208};
2209
2210static const struct attribute_group led_attr_group = {
2211 .attrs = led_attrs,
2212};
2213
Amy Maloche4cf9f322013-05-29 15:53:46 -07002214static struct attribute *pwm_attrs[] = {
2215 &dev_attr_pwm_us.attr,
2216 NULL
2217};
2218
2219static struct attribute *lpg_attrs[] = {
2220 &dev_attr_pause_lo.attr,
2221 &dev_attr_pause_hi.attr,
2222 &dev_attr_start_idx.attr,
2223 &dev_attr_ramp_step_ms.attr,
2224 &dev_attr_lut_flags.attr,
2225 &dev_attr_duty_pcts.attr,
2226 NULL
2227};
2228
Amy Malochea5c4ed82013-06-05 11:05:28 -07002229static struct attribute *blink_attrs[] = {
2230 &dev_attr_blink.attr,
2231 NULL
2232};
2233
Amy Maloche4cf9f322013-05-29 15:53:46 -07002234static const struct attribute_group pwm_attr_group = {
2235 .attrs = pwm_attrs,
2236};
2237
2238static const struct attribute_group lpg_attr_group = {
2239 .attrs = lpg_attrs,
2240};
2241
Amy Malochea5c4ed82013-06-05 11:05:28 -07002242static const struct attribute_group blink_attr_group = {
2243 .attrs = blink_attrs,
2244};
2245
Amy Maloche864a6d52012-10-03 15:58:12 -07002246static int __devinit qpnp_flash_init(struct qpnp_led_data *led)
2247{
2248 int rc;
2249
Chun Zhangc3b505b2013-06-03 19:01:49 -07002250 led->flash_cfg->flash_on = false;
2251
Amy Maloche864a6d52012-10-03 15:58:12 -07002252 rc = qpnp_led_masked_write(led,
2253 FLASH_LED_STROBE_CTRL(led->base),
2254 FLASH_STROBE_MASK, FLASH_DISABLE_ALL);
2255 if (rc) {
2256 dev_err(&led->spmi_dev->dev,
2257 "LED %d flash write failed(%d)\n", led->id, rc);
2258 return rc;
2259 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002260
Chun Zhangdf2d3062013-06-25 20:14:46 -07002261 /* Disable flash LED module */
2262 rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
2263 FLASH_ENABLE_MODULE_MASK, FLASH_DISABLE_ALL);
2264 if (rc) {
2265 dev_err(&led->spmi_dev->dev,
2266 "Enable reg write failed(%d)\n", rc);
2267 return rc;
2268 }
2269
2270 if (led->flash_cfg->torch_enable)
2271 return 0;
2272
Amy Maloche864a6d52012-10-03 15:58:12 -07002273 /* Set headroom */
2274 rc = qpnp_led_masked_write(led, FLASH_HEADROOM(led->base),
2275 FLASH_HEADROOM_MASK, led->flash_cfg->headroom);
2276 if (rc) {
2277 dev_err(&led->spmi_dev->dev,
2278 "Headroom reg write failed(%d)\n", rc);
2279 return rc;
2280 }
2281
Chun Zhange8954cf2013-05-02 11:14:34 -07002282 /* Set startup delay */
2283 rc = qpnp_led_masked_write(led,
2284 FLASH_STARTUP_DELAY(led->base), FLASH_STARTUP_DLY_MASK,
2285 led->flash_cfg->startup_dly);
2286 if (rc) {
2287 dev_err(&led->spmi_dev->dev,
2288 "Startup delay reg write failed(%d)\n", rc);
2289 return rc;
2290 }
2291
2292 /* Set timer control - safety or watchdog */
2293 if (led->flash_cfg->safety_timer) {
2294 rc = qpnp_led_masked_write(led,
2295 FLASH_LED_TMR_CTRL(led->base),
2296 FLASH_TMR_MASK, FLASH_TMR_SAFETY);
2297 if (rc) {
2298 dev_err(&led->spmi_dev->dev,
2299 "LED timer ctrl reg write failed(%d)\n",
2300 rc);
2301 return rc;
2302 }
2303 }
2304
2305 /* Set Vreg force */
2306 rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
2307 FLASH_VREG_MASK, FLASH_HW_VREG_OK);
2308 if (rc) {
2309 dev_err(&led->spmi_dev->dev,
2310 "Vreg OK reg write failed(%d)\n", rc);
2311 return rc;
2312 }
2313
2314 /* Set self fault check */
2315 rc = qpnp_led_masked_write(led, FLASH_FAULT_DETECT(led->base),
2316 FLASH_FAULT_DETECT_MASK, FLASH_SELFCHECK_ENABLE);
2317 if (rc) {
2318 dev_err(&led->spmi_dev->dev,
2319 "Fault detect reg write failed(%d)\n", rc);
2320 return rc;
2321 }
2322
Amy Maloche864a6d52012-10-03 15:58:12 -07002323 /* Set mask enable */
2324 rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
2325 FLASH_MASK_REG_MASK, FLASH_MASK_1);
2326 if (rc) {
2327 dev_err(&led->spmi_dev->dev,
2328 "Mask enable reg write failed(%d)\n", rc);
2329 return rc;
2330 }
2331
Amy Malochebc97c0d22013-03-24 22:06:16 -07002332 led->flash_cfg->strobe_type = 0;
2333
Amy Maloche864a6d52012-10-03 15:58:12 -07002334 /* dump flash registers */
2335 qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
2336
2337 return 0;
2338}
2339
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302340static int __devinit qpnp_kpdbl_init(struct qpnp_led_data *led)
2341{
2342 int rc;
2343 u8 val;
2344
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302345 /* select row source - vbst or vph */
2346 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2347 KPDBL_ROW_SRC_SEL(led->base), &val, 1);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302348 if (rc) {
2349 dev_err(&led->spmi_dev->dev,
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302350 "Unable to read from addr=%x, rc(%d)\n",
2351 KPDBL_ROW_SRC_SEL(led->base), rc);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302352 return rc;
2353 }
2354
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302355 if (led->kpdbl_cfg->row_src_vbst)
2356 val |= 1 << led->kpdbl_cfg->row_id;
2357 else
2358 val &= ~(1 << led->kpdbl_cfg->row_id);
2359
2360 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
2361 KPDBL_ROW_SRC_SEL(led->base), &val, 1);
2362 if (rc) {
2363 dev_err(&led->spmi_dev->dev,
2364 "Unable to read from addr=%x, rc(%d)\n",
2365 KPDBL_ROW_SRC_SEL(led->base), rc);
2366 return rc;
2367 }
2368
2369 /* row source enable */
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302370 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2371 KPDBL_ROW_SRC(led->base), &val, 1);
2372 if (rc) {
2373 dev_err(&led->spmi_dev->dev,
2374 "Unable to read from addr=%x, rc(%d)\n",
2375 KPDBL_ROW_SRC(led->base), rc);
2376 return rc;
2377 }
2378
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302379 if (led->kpdbl_cfg->row_src_en)
2380 val |= KPDBL_ROW_SCAN_EN_MASK | (1 << led->kpdbl_cfg->row_id);
2381 else
2382 val &= ~(1 << led->kpdbl_cfg->row_id);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302383
2384 rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
2385 KPDBL_ROW_SRC(led->base), &val, 1);
2386 if (rc) {
2387 dev_err(&led->spmi_dev->dev,
2388 "Unable to write to addr=%x, rc(%d)\n",
2389 KPDBL_ROW_SRC(led->base), rc);
2390 return rc;
2391 }
2392
2393 /* enable module */
2394 rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
2395 KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
2396 if (rc) {
2397 dev_err(&led->spmi_dev->dev,
2398 "Enable module write failed(%d)\n", rc);
2399 return rc;
2400 }
2401
Amy Malochea2726f02013-05-10 10:19:03 -07002402 rc = qpnp_pwm_init(led->kpdbl_cfg->pwm_cfg, led->spmi_dev,
2403 led->cdev.name);
2404 if (rc) {
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302405 dev_err(&led->spmi_dev->dev,
Amy Malochea2726f02013-05-10 10:19:03 -07002406 "Failed to initialize pwm\n");
2407 return rc;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302408 }
2409
2410 /* dump kpdbl registers */
2411 qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
2412
2413 return 0;
2414}
2415
Amy Malocheeea7b592012-10-03 15:59:36 -07002416static int __devinit qpnp_rgb_init(struct qpnp_led_data *led)
2417{
Amy Malochea2726f02013-05-10 10:19:03 -07002418 int rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07002419
2420 rc = qpnp_led_masked_write(led, RGB_LED_SRC_SEL(led->base),
2421 RGB_LED_SRC_MASK, RGB_LED_SOURCE_VPH_PWR);
2422 if (rc) {
2423 dev_err(&led->spmi_dev->dev,
2424 "Failed to write led source select register\n");
2425 return rc;
2426 }
2427
Amy Malochea2726f02013-05-10 10:19:03 -07002428 rc = qpnp_pwm_init(led->rgb_cfg->pwm_cfg, led->spmi_dev,
2429 led->cdev.name);
2430 if (rc) {
Amy Malocheeea7b592012-10-03 15:59:36 -07002431 dev_err(&led->spmi_dev->dev,
Amy Malochea2726f02013-05-10 10:19:03 -07002432 "Failed to initialize pwm\n");
2433 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07002434 }
Amy Malocheeea7b592012-10-03 15:59:36 -07002435 /* Initialize led for use in auto trickle charging mode */
2436 rc = qpnp_led_masked_write(led, RGB_LED_ATC_CTL(led->base),
2437 led->rgb_cfg->enable, led->rgb_cfg->enable);
2438
2439 return 0;
2440}
2441
Amy Malochea2726f02013-05-10 10:19:03 -07002442static int __devinit qpnp_mpp_init(struct qpnp_led_data *led)
2443{
2444 int rc, val;
2445
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05302446
2447 if (led->max_current < LED_MPP_CURRENT_MIN ||
2448 led->max_current > LED_MPP_CURRENT_MAX) {
2449 dev_err(&led->spmi_dev->dev,
2450 "max current for mpp is not valid\n");
2451 return -EINVAL;
2452 }
2453
Amy Malochea2726f02013-05-10 10:19:03 -07002454 val = (led->mpp_cfg->current_setting / LED_MPP_CURRENT_PER_SETTING) - 1;
2455
2456 if (val < 0)
2457 val = 0;
2458
Chun Zhang874c9ab2013-07-08 17:18:34 -07002459 rc = qpnp_led_masked_write(led, LED_MPP_VIN_CTRL(led->base),
2460 LED_MPP_VIN_MASK, led->mpp_cfg->vin_ctrl);
2461 if (rc) {
2462 dev_err(&led->spmi_dev->dev,
2463 "Failed to write led vin control reg\n");
2464 return rc;
2465 }
2466
Amy Malochea2726f02013-05-10 10:19:03 -07002467 rc = qpnp_led_masked_write(led, LED_MPP_SINK_CTRL(led->base),
2468 LED_MPP_SINK_MASK, val);
2469 if (rc) {
2470 dev_err(&led->spmi_dev->dev,
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05302471 "Failed to write sink control reg\n");
Amy Malochea2726f02013-05-10 10:19:03 -07002472 return rc;
2473 }
2474
2475 if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
2476 rc = qpnp_pwm_init(led->mpp_cfg->pwm_cfg, led->spmi_dev,
2477 led->cdev.name);
2478 if (rc) {
2479 dev_err(&led->spmi_dev->dev,
2480 "Failed to initialize pwm\n");
2481 return rc;
2482 }
2483 }
2484
2485 return 0;
2486}
2487
Amy Malochef3d5a062012-08-16 19:14:11 -07002488static int __devinit qpnp_led_initialize(struct qpnp_led_data *led)
2489{
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302490 int rc = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07002491
2492 switch (led->id) {
2493 case QPNP_ID_WLED:
2494 rc = qpnp_wled_init(led);
2495 if (rc)
Amy Malochea5ca5552012-10-23 13:34:46 -07002496 dev_err(&led->spmi_dev->dev,
Amy Malochef3d5a062012-08-16 19:14:11 -07002497 "WLED initialize failed(%d)\n", rc);
2498 break;
Amy Maloche864a6d52012-10-03 15:58:12 -07002499 case QPNP_ID_FLASH1_LED0:
2500 case QPNP_ID_FLASH1_LED1:
2501 rc = qpnp_flash_init(led);
2502 if (rc)
2503 dev_err(&led->spmi_dev->dev,
2504 "FLASH initialize failed(%d)\n", rc);
2505 break;
Amy Malocheeea7b592012-10-03 15:59:36 -07002506 case QPNP_ID_RGB_RED:
2507 case QPNP_ID_RGB_GREEN:
2508 case QPNP_ID_RGB_BLUE:
2509 rc = qpnp_rgb_init(led);
2510 if (rc)
2511 dev_err(&led->spmi_dev->dev,
2512 "RGB initialize failed(%d)\n", rc);
2513 break;
Amy Malochef3813742013-04-11 19:33:47 -07002514 case QPNP_ID_LED_MPP:
Amy Malochea2726f02013-05-10 10:19:03 -07002515 rc = qpnp_mpp_init(led);
2516 if (rc)
2517 dev_err(&led->spmi_dev->dev,
2518 "MPP initialize failed(%d)\n", rc);
Amy Malochef3813742013-04-11 19:33:47 -07002519 break;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302520 case QPNP_ID_KPDBL:
2521 rc = qpnp_kpdbl_init(led);
2522 if (rc)
2523 dev_err(&led->spmi_dev->dev,
2524 "KPDBL initialize failed(%d)\n", rc);
2525 break;
Amy Malochef3d5a062012-08-16 19:14:11 -07002526 default:
Amy Malochea5ca5552012-10-23 13:34:46 -07002527 dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
Amy Malocheeea7b592012-10-03 15:59:36 -07002528 return -EINVAL;
Amy Malochef3d5a062012-08-16 19:14:11 -07002529 }
2530
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302531 return rc;
Amy Malochef3d5a062012-08-16 19:14:11 -07002532}
2533
Amy Malochea5ca5552012-10-23 13:34:46 -07002534static int __devinit qpnp_get_common_configs(struct qpnp_led_data *led,
2535 struct device_node *node)
2536{
2537 int rc;
Asaf Penso55ac8472013-01-21 21:17:37 +02002538 u32 val;
Amy Malochea5ca5552012-10-23 13:34:46 -07002539 const char *temp_string;
2540
2541 led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
2542 rc = of_property_read_string(node, "linux,default-trigger",
2543 &temp_string);
2544 if (!rc)
2545 led->cdev.default_trigger = temp_string;
2546 else if (rc != -EINVAL)
2547 return rc;
2548
2549 led->default_on = false;
2550 rc = of_property_read_string(node, "qcom,default-state",
2551 &temp_string);
2552 if (!rc) {
2553 if (strncmp(temp_string, "on", sizeof("on")) == 0)
2554 led->default_on = true;
2555 } else if (rc != -EINVAL)
2556 return rc;
2557
Asaf Penso55ac8472013-01-21 21:17:37 +02002558 led->turn_off_delay_ms = 0;
2559 rc = of_property_read_u32(node, "qcom,turn-off-delay-ms", &val);
2560 if (!rc)
2561 led->turn_off_delay_ms = val;
2562 else if (rc != -EINVAL)
2563 return rc;
2564
Amy Malochea5ca5552012-10-23 13:34:46 -07002565 return 0;
2566}
2567
Amy Malochef3d5a062012-08-16 19:14:11 -07002568/*
2569 * Handlers for alternative sources of platform_data
2570 */
2571static int __devinit qpnp_get_config_wled(struct qpnp_led_data *led,
2572 struct device_node *node)
2573{
2574 u32 val;
2575 int rc;
Amy Malochef3d5a062012-08-16 19:14:11 -07002576
2577 led->wled_cfg = devm_kzalloc(&led->spmi_dev->dev,
2578 sizeof(struct wled_config_data), GFP_KERNEL);
2579 if (!led->wled_cfg) {
2580 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2581 return -ENOMEM;
2582 }
2583
Amy Maloche0150b5e2013-08-15 18:18:32 -07002584 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2585 PMIC_VERSION_REG, &led->wled_cfg->pmic_version, 1);
2586 if (rc) {
2587 dev_err(&led->spmi_dev->dev,
2588 "Unable to read pmic ver, rc(%d)\n", rc);
2589 }
2590
Amy Malochef3d5a062012-08-16 19:14:11 -07002591 led->wled_cfg->num_strings = WLED_DEFAULT_STRINGS;
2592 rc = of_property_read_u32(node, "qcom,num-strings", &val);
2593 if (!rc)
2594 led->wled_cfg->num_strings = (u8) val;
2595 else if (rc != -EINVAL)
2596 return rc;
2597
2598 led->wled_cfg->ovp_val = WLED_DEFAULT_OVP_VAL;
2599 rc = of_property_read_u32(node, "qcom,ovp-val", &val);
2600 if (!rc)
2601 led->wled_cfg->ovp_val = (u8) val;
2602 else if (rc != -EINVAL)
2603 return rc;
2604
2605 led->wled_cfg->boost_curr_lim = WLED_BOOST_LIM_DEFAULT;
2606 rc = of_property_read_u32(node, "qcom,boost-curr-lim", &val);
2607 if (!rc)
2608 led->wled_cfg->boost_curr_lim = (u8) val;
2609 else if (rc != -EINVAL)
2610 return rc;
2611
2612 led->wled_cfg->cp_select = WLED_CP_SEL_DEFAULT;
2613 rc = of_property_read_u32(node, "qcom,cp-sel", &val);
2614 if (!rc)
2615 led->wled_cfg->cp_select = (u8) val;
2616 else if (rc != -EINVAL)
2617 return rc;
2618
2619 led->wled_cfg->ctrl_delay_us = WLED_CTRL_DLY_DEFAULT;
2620 rc = of_property_read_u32(node, "qcom,ctrl-delay-us", &val);
2621 if (!rc)
2622 led->wled_cfg->ctrl_delay_us = (u8) val;
2623 else if (rc != -EINVAL)
2624 return rc;
2625
Amy Malochebd687672013-03-18 11:23:45 -07002626 led->wled_cfg->op_fdbck = WLED_OP_FDBCK_DEFAULT;
2627 rc = of_property_read_u32(node, "qcom,op-fdbck", &val);
2628 if (!rc)
2629 led->wled_cfg->op_fdbck = (u8) val;
2630 else if (rc != -EINVAL)
2631 return rc;
2632
Amy Malochef3d5a062012-08-16 19:14:11 -07002633 led->wled_cfg->switch_freq = WLED_SWITCH_FREQ_DEFAULT;
2634 rc = of_property_read_u32(node, "qcom,switch-freq", &val);
2635 if (!rc)
2636 led->wled_cfg->switch_freq = (u8) val;
2637 else if (rc != -EINVAL)
2638 return rc;
2639
2640 led->wled_cfg->dig_mod_gen_en =
2641 of_property_read_bool(node, "qcom,dig-mod-gen-en");
2642
2643 led->wled_cfg->cs_out_en =
2644 of_property_read_bool(node, "qcom,cs-out-en");
2645
Amy Malochef3d5a062012-08-16 19:14:11 -07002646 return 0;
2647}
2648
Amy Maloche864a6d52012-10-03 15:58:12 -07002649static int __devinit qpnp_get_config_flash(struct qpnp_led_data *led,
Chun Zhangc3b505b2013-06-03 19:01:49 -07002650 struct device_node *node, bool *reg_set)
Amy Maloche864a6d52012-10-03 15:58:12 -07002651{
2652 int rc;
2653 u32 val;
2654
2655 led->flash_cfg = devm_kzalloc(&led->spmi_dev->dev,
2656 sizeof(struct flash_config_data), GFP_KERNEL);
2657 if (!led->flash_cfg) {
2658 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2659 return -ENOMEM;
2660 }
2661
Chun Zhang0d6ca072013-07-30 21:08:39 -07002662 rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
2663 FLASH_PERIPHERAL_SUBTYPE(led->base),
2664 &led->flash_cfg->peripheral_subtype, 1);
2665 if (rc) {
2666 dev_err(&led->spmi_dev->dev,
2667 "Unable to read from addr=%x, rc(%d)\n",
2668 FLASH_PERIPHERAL_SUBTYPE(led->base), rc);
2669 }
2670
Chun Zhang9d5ff672013-08-01 18:18:26 -07002671 led->flash_cfg->torch_enable =
2672 of_property_read_bool(node, "qcom,torch-enable");
2673
Amy Maloche864a6d52012-10-03 15:58:12 -07002674 if (led->id == QPNP_ID_FLASH1_LED0) {
Chun Zhang9d5ff672013-08-01 18:18:26 -07002675 led->flash_cfg->enable_module = FLASH_ENABLE_LED_0;
Amy Maloche864a6d52012-10-03 15:58:12 -07002676 led->flash_cfg->current_addr = FLASH_LED_0_CURR(led->base);
Amy Maloche864a6d52012-10-03 15:58:12 -07002677 led->flash_cfg->trigger_flash = FLASH_LED_0_OUTPUT;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002678 if (!*reg_set) {
2679 led->flash_cfg->flash_boost_reg =
2680 regulator_get(&led->spmi_dev->dev,
Chun Zhang0d6ca072013-07-30 21:08:39 -07002681 "flash-boost");
Chun Zhangc3b505b2013-06-03 19:01:49 -07002682 if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
2683 rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
2684 dev_err(&led->spmi_dev->dev,
2685 "Regulator get failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002686 goto error_get_flash_reg;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002687 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002688 led->flash_cfg->flash_reg_get = true;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002689 *reg_set = true;
2690 } else
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002691 led->flash_cfg->flash_reg_get = false;
Chun Zhang9d5ff672013-08-01 18:18:26 -07002692
2693 if (led->flash_cfg->torch_enable) {
2694 led->flash_cfg->second_addr =
2695 FLASH_LED_1_CURR(led->base);
2696 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002697 } else if (led->id == QPNP_ID_FLASH1_LED1) {
Chun Zhang9d5ff672013-08-01 18:18:26 -07002698 led->flash_cfg->enable_module = FLASH_ENABLE_LED_1;
Amy Maloche864a6d52012-10-03 15:58:12 -07002699 led->flash_cfg->current_addr = FLASH_LED_1_CURR(led->base);
Amy Maloche864a6d52012-10-03 15:58:12 -07002700 led->flash_cfg->trigger_flash = FLASH_LED_1_OUTPUT;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002701 if (!*reg_set) {
2702 led->flash_cfg->flash_boost_reg =
2703 regulator_get(&led->spmi_dev->dev,
Chun Zhang0d6ca072013-07-30 21:08:39 -07002704 "flash-boost");
Chun Zhangc3b505b2013-06-03 19:01:49 -07002705 if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
2706 rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
2707 dev_err(&led->spmi_dev->dev,
2708 "Regulator get failed(%d)\n", rc);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002709 goto error_get_flash_reg;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002710 }
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002711 led->flash_cfg->flash_reg_get = true;
Chun Zhangc3b505b2013-06-03 19:01:49 -07002712 *reg_set = true;
2713 } else
Chun Zhangda8ad0f2013-07-17 14:46:47 -07002714 led->flash_cfg->flash_reg_get = false;
Chun Zhang9d5ff672013-08-01 18:18:26 -07002715
2716 if (led->flash_cfg->torch_enable) {
2717 led->flash_cfg->second_addr =
2718 FLASH_LED_0_CURR(led->base);
2719 }
Amy Maloche864a6d52012-10-03 15:58:12 -07002720 } else {
2721 dev_err(&led->spmi_dev->dev, "Unknown flash LED name given\n");
2722 return -EINVAL;
2723 }
2724
Chun Zhangdf2d3062013-06-25 20:14:46 -07002725 if (led->flash_cfg->torch_enable) {
Chun Zhang0d6ca072013-07-30 21:08:39 -07002726 if (of_find_property(of_get_parent(node), "torch-boost-supply",
2727 NULL)) {
2728 led->flash_cfg->torch_boost_reg =
2729 regulator_get(&led->spmi_dev->dev,
2730 "torch-boost");
2731 if (IS_ERR(led->flash_cfg->torch_boost_reg)) {
2732 rc = PTR_ERR(led->flash_cfg->torch_boost_reg);
2733 dev_err(&led->spmi_dev->dev,
2734 "Torch regulator get failed(%d)\n", rc);
2735 goto error_get_torch_reg;
2736 }
Chun Zhang9d5ff672013-08-01 18:18:26 -07002737 led->flash_cfg->enable_module = FLASH_ENABLE_MODULE;
2738 } else
2739 led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
2740 led->flash_cfg->trigger_flash = FLASH_STROBE_SW;
Chun Zhangdf2d3062013-06-25 20:14:46 -07002741 }
2742
Amy Maloche864a6d52012-10-03 15:58:12 -07002743 rc = of_property_read_u32(node, "qcom,current", &val);
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002744 if (!rc) {
Chun Zhangdf2d3062013-06-25 20:14:46 -07002745 if (led->flash_cfg->torch_enable) {
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002746 led->flash_cfg->current_prgm = (val *
2747 TORCH_MAX_LEVEL / led->max_current);
Chun Zhangdf2d3062013-06-25 20:14:46 -07002748 return 0;
2749 }
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002750 else
2751 led->flash_cfg->current_prgm = (val *
Amy Maloche864a6d52012-10-03 15:58:12 -07002752 FLASH_MAX_LEVEL / led->max_current);
Chun Zhang9c1fbaa2013-06-17 15:31:18 -07002753 } else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002754 if (led->flash_cfg->torch_enable)
2755 goto error_get_torch_reg;
2756 else
2757 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002758
2759 rc = of_property_read_u32(node, "qcom,headroom", &val);
2760 if (!rc)
2761 led->flash_cfg->headroom = (u8) val;
2762 else if (rc == -EINVAL)
Amy Maloche62571b22013-04-17 17:23:10 -07002763 led->flash_cfg->headroom = HEADROOM_500mV;
Amy Maloche864a6d52012-10-03 15:58:12 -07002764 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002765 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002766
2767 rc = of_property_read_u32(node, "qcom,duration", &val);
2768 if (!rc)
2769 led->flash_cfg->duration = (((u8) val) - 10) / 10;
2770 else if (rc == -EINVAL)
2771 led->flash_cfg->duration = FLASH_DURATION_200ms;
2772 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002773 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002774
2775 rc = of_property_read_u32(node, "qcom,clamp-curr", &val);
2776 if (!rc)
2777 led->flash_cfg->clamp_curr = (val *
2778 FLASH_MAX_LEVEL / led->max_current);
2779 else if (rc == -EINVAL)
2780 led->flash_cfg->clamp_curr = FLASH_CLAMP_200mA;
2781 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002782 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002783
2784 rc = of_property_read_u32(node, "qcom,startup-dly", &val);
2785 if (!rc)
2786 led->flash_cfg->startup_dly = (u8) val;
2787 else if (rc == -EINVAL)
Amy Maloche62571b22013-04-17 17:23:10 -07002788 led->flash_cfg->startup_dly = DELAY_128us;
Amy Maloche864a6d52012-10-03 15:58:12 -07002789 else
Chun Zhangdf2d3062013-06-25 20:14:46 -07002790 goto error_get_flash_reg;
Amy Maloche864a6d52012-10-03 15:58:12 -07002791
2792 led->flash_cfg->safety_timer =
2793 of_property_read_bool(node, "qcom,safety-timer");
2794
2795 return 0;
Chun Zhangdf2d3062013-06-25 20:14:46 -07002796
2797error_get_torch_reg:
2798 regulator_put(led->flash_cfg->torch_boost_reg);
2799
2800error_get_flash_reg:
2801 regulator_put(led->flash_cfg->flash_boost_reg);
2802 return rc;
2803
Amy Maloche864a6d52012-10-03 15:58:12 -07002804}
2805
Amy Malochea2726f02013-05-10 10:19:03 -07002806static int __devinit qpnp_get_config_pwm(struct pwm_config_data *pwm_cfg,
2807 struct spmi_device *spmi_dev,
2808 struct device_node *node)
2809{
2810 struct property *prop;
2811 int rc, i;
2812 u32 val;
2813 u8 *temp_cfg;
2814
2815 rc = of_property_read_u32(node, "qcom,pwm-channel", &val);
2816 if (!rc)
2817 pwm_cfg->pwm_channel = val;
2818 else
2819 return rc;
2820
2821 if (pwm_cfg->mode == PWM_MODE) {
2822 rc = of_property_read_u32(node, "qcom,pwm-us", &val);
2823 if (!rc)
2824 pwm_cfg->pwm_period_us = val;
2825 else
2826 return rc;
2827 }
2828
Amy Malochea5c4ed82013-06-05 11:05:28 -07002829 pwm_cfg->use_blink =
2830 of_property_read_bool(node, "qcom,use-blink");
2831
2832 if (pwm_cfg->mode == LPG_MODE || pwm_cfg->use_blink) {
Amy Malochea2726f02013-05-10 10:19:03 -07002833 pwm_cfg->duty_cycles =
2834 devm_kzalloc(&spmi_dev->dev,
2835 sizeof(struct pwm_duty_cycles), GFP_KERNEL);
2836 if (!pwm_cfg->duty_cycles) {
2837 dev_err(&spmi_dev->dev,
2838 "Unable to allocate memory\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002839 rc = -ENOMEM;
2840 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002841 }
2842
2843 prop = of_find_property(node, "qcom,duty-pcts",
2844 &pwm_cfg->duty_cycles->num_duty_pcts);
2845 if (!prop) {
2846 dev_err(&spmi_dev->dev, "Looking up property " \
2847 "node qcom,duty-pcts failed\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002848 rc = -ENODEV;
2849 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002850 } else if (!pwm_cfg->duty_cycles->num_duty_pcts) {
2851 dev_err(&spmi_dev->dev, "Invalid length of " \
2852 "duty pcts\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002853 rc = -EINVAL;
2854 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002855 }
2856
2857 pwm_cfg->duty_cycles->duty_pcts =
2858 devm_kzalloc(&spmi_dev->dev,
Amy Maloche4cf9f322013-05-29 15:53:46 -07002859 sizeof(int) * PWM_LUT_MAX_SIZE,
Amy Malochea2726f02013-05-10 10:19:03 -07002860 GFP_KERNEL);
2861 if (!pwm_cfg->duty_cycles->duty_pcts) {
2862 dev_err(&spmi_dev->dev,
2863 "Unable to allocate memory\n");
Amy Maloche4cf9f322013-05-29 15:53:46 -07002864 rc = -ENOMEM;
2865 goto bad_lpg_params;
2866 }
2867
2868 pwm_cfg->old_duty_pcts =
2869 devm_kzalloc(&spmi_dev->dev,
2870 sizeof(int) * PWM_LUT_MAX_SIZE,
2871 GFP_KERNEL);
2872 if (!pwm_cfg->old_duty_pcts) {
2873 dev_err(&spmi_dev->dev,
2874 "Unable to allocate memory\n");
2875 rc = -ENOMEM;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002876 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002877 }
2878
2879 temp_cfg = devm_kzalloc(&spmi_dev->dev,
2880 pwm_cfg->duty_cycles->num_duty_pcts *
2881 sizeof(u8), GFP_KERNEL);
2882 if (!temp_cfg) {
2883 dev_err(&spmi_dev->dev, "Failed to allocate " \
2884 "memory for duty pcts\n");
Amy Malochea5c4ed82013-06-05 11:05:28 -07002885 rc = -ENOMEM;
2886 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002887 }
2888
2889 memcpy(temp_cfg, prop->value,
2890 pwm_cfg->duty_cycles->num_duty_pcts);
2891
2892 for (i = 0; i < pwm_cfg->duty_cycles->num_duty_pcts; i++)
2893 pwm_cfg->duty_cycles->duty_pcts[i] =
2894 (int) temp_cfg[i];
2895
2896 rc = of_property_read_u32(node, "qcom,start-idx", &val);
2897 if (!rc) {
2898 pwm_cfg->lut_params.start_idx = val;
2899 pwm_cfg->duty_cycles->start_idx = val;
2900 } else
Amy Malochea5c4ed82013-06-05 11:05:28 -07002901 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002902
2903 pwm_cfg->lut_params.lut_pause_hi = 0;
2904 rc = of_property_read_u32(node, "qcom,pause-hi", &val);
2905 if (!rc)
2906 pwm_cfg->lut_params.lut_pause_hi = val;
2907 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002908 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002909
2910 pwm_cfg->lut_params.lut_pause_lo = 0;
2911 rc = of_property_read_u32(node, "qcom,pause-lo", &val);
2912 if (!rc)
2913 pwm_cfg->lut_params.lut_pause_lo = val;
2914 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002915 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002916
2917 pwm_cfg->lut_params.ramp_step_ms =
2918 QPNP_LUT_RAMP_STEP_DEFAULT;
2919 rc = of_property_read_u32(node, "qcom,ramp-step-ms", &val);
2920 if (!rc)
2921 pwm_cfg->lut_params.ramp_step_ms = val;
2922 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002923 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002924
2925 pwm_cfg->lut_params.flags = QPNP_LED_PWM_FLAGS;
2926 rc = of_property_read_u32(node, "qcom,lut-flags", &val);
2927 if (!rc)
2928 pwm_cfg->lut_params.flags = (u8) val;
2929 else if (rc != -EINVAL)
Amy Malochea5c4ed82013-06-05 11:05:28 -07002930 goto bad_lpg_params;
Amy Malochea2726f02013-05-10 10:19:03 -07002931
2932 pwm_cfg->lut_params.idx_len =
2933 pwm_cfg->duty_cycles->num_duty_pcts;
2934 }
2935 return 0;
Amy Malochea5c4ed82013-06-05 11:05:28 -07002936
2937bad_lpg_params:
2938 pwm_cfg->use_blink = false;
2939 if (pwm_cfg->mode == PWM_MODE) {
2940 dev_err(&spmi_dev->dev, "LPG parameters not set for" \
2941 " blink mode, defaulting to PWM mode\n");
2942 return 0;
2943 }
2944 return rc;
Amy Malochea2726f02013-05-10 10:19:03 -07002945};
2946
2947static int qpnp_led_get_mode(const char *mode)
2948{
2949 if (strncmp(mode, "manual", strlen(mode)) == 0)
2950 return MANUAL_MODE;
2951 else if (strncmp(mode, "pwm", strlen(mode)) == 0)
2952 return PWM_MODE;
2953 else if (strncmp(mode, "lpg", strlen(mode)) == 0)
2954 return LPG_MODE;
2955 else
2956 return -EINVAL;
2957};
2958
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302959static int __devinit qpnp_get_config_kpdbl(struct qpnp_led_data *led,
2960 struct device_node *node)
2961{
2962 int rc;
2963 u32 val;
Amy Malochea2726f02013-05-10 10:19:03 -07002964 u8 led_mode;
2965 const char *mode;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302966
2967 led->kpdbl_cfg = devm_kzalloc(&led->spmi_dev->dev,
2968 sizeof(struct kpdbl_config_data), GFP_KERNEL);
2969 if (!led->kpdbl_cfg) {
2970 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
2971 return -ENOMEM;
2972 }
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302973
Amy Malochea2726f02013-05-10 10:19:03 -07002974 rc = of_property_read_string(node, "qcom,mode", &mode);
2975 if (!rc) {
2976 led_mode = qpnp_led_get_mode(mode);
2977 if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
2978 dev_err(&led->spmi_dev->dev, "Selected mode not " \
2979 "supported for kpdbl.\n");
2980 return -EINVAL;
2981 }
2982 led->kpdbl_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
2983 sizeof(struct pwm_config_data),
2984 GFP_KERNEL);
2985 if (!led->kpdbl_cfg->pwm_cfg) {
2986 dev_err(&led->spmi_dev->dev,
2987 "Unable to allocate memory\n");
2988 return -ENOMEM;
2989 }
2990 led->kpdbl_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07002991 led->kpdbl_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07002992 } else
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302993 return rc;
2994
Amy Malochea2726f02013-05-10 10:19:03 -07002995 rc = qpnp_get_config_pwm(led->kpdbl_cfg->pwm_cfg, led->spmi_dev, node);
2996 if (rc < 0)
Mohan Pallaka1f46f022013-03-15 14:56:50 +05302997 return rc;
2998
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05302999 rc = of_property_read_u32(node, "qcom,row-id", &val);
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303000 if (!rc)
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303001 led->kpdbl_cfg->row_id = val;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303002 else
3003 return rc;
3004
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303005 led->kpdbl_cfg->row_src_vbst =
3006 of_property_read_bool(node, "qcom,row-src-vbst");
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303007
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303008 led->kpdbl_cfg->row_src_en =
3009 of_property_read_bool(node, "qcom,row-src-en");
3010
3011 led->kpdbl_cfg->always_on =
3012 of_property_read_bool(node, "qcom,always-on");
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303013
3014 return 0;
3015}
3016
Amy Malocheeea7b592012-10-03 15:59:36 -07003017static int __devinit qpnp_get_config_rgb(struct qpnp_led_data *led,
3018 struct device_node *node)
3019{
Amy Malochea2726f02013-05-10 10:19:03 -07003020 int rc;
3021 u8 led_mode;
3022 const char *mode;
Amy Malocheeea7b592012-10-03 15:59:36 -07003023
3024 led->rgb_cfg = devm_kzalloc(&led->spmi_dev->dev,
3025 sizeof(struct rgb_config_data), GFP_KERNEL);
3026 if (!led->rgb_cfg) {
3027 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3028 return -ENOMEM;
3029 }
3030
3031 if (led->id == QPNP_ID_RGB_RED)
3032 led->rgb_cfg->enable = RGB_LED_ENABLE_RED;
3033 else if (led->id == QPNP_ID_RGB_GREEN)
3034 led->rgb_cfg->enable = RGB_LED_ENABLE_GREEN;
3035 else if (led->id == QPNP_ID_RGB_BLUE)
3036 led->rgb_cfg->enable = RGB_LED_ENABLE_BLUE;
3037 else
3038 return -EINVAL;
3039
Amy Malochea2726f02013-05-10 10:19:03 -07003040 rc = of_property_read_string(node, "qcom,mode", &mode);
3041 if (!rc) {
3042 led_mode = qpnp_led_get_mode(mode);
3043 if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
3044 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3045 "supported for rgb.\n");
Amy Malocheeea7b592012-10-03 15:59:36 -07003046 return -EINVAL;
3047 }
Amy Malochea2726f02013-05-10 10:19:03 -07003048 led->rgb_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3049 sizeof(struct pwm_config_data),
3050 GFP_KERNEL);
3051 if (!led->rgb_cfg->pwm_cfg) {
Amy Malocheeea7b592012-10-03 15:59:36 -07003052 dev_err(&led->spmi_dev->dev,
3053 "Unable to allocate memory\n");
3054 return -ENOMEM;
3055 }
Amy Malochea2726f02013-05-10 10:19:03 -07003056 led->rgb_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003057 led->rgb_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003058 } else
3059 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07003060
Amy Malochea2726f02013-05-10 10:19:03 -07003061 rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->spmi_dev, node);
3062 if (rc < 0)
3063 return rc;
Amy Malocheeea7b592012-10-03 15:59:36 -07003064
3065 return 0;
3066}
3067
Amy Malochef3813742013-04-11 19:33:47 -07003068static int __devinit qpnp_get_config_mpp(struct qpnp_led_data *led,
3069 struct device_node *node)
3070{
3071 int rc;
3072 u32 val;
Amy Malochea2726f02013-05-10 10:19:03 -07003073 u8 led_mode;
3074 const char *mode;
Amy Malochef3813742013-04-11 19:33:47 -07003075
3076 led->mpp_cfg = devm_kzalloc(&led->spmi_dev->dev,
3077 sizeof(struct mpp_config_data), GFP_KERNEL);
3078 if (!led->mpp_cfg) {
3079 dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
3080 return -ENOMEM;
3081 }
3082
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05303083 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
Amy Malochef3813742013-04-11 19:33:47 -07003084 rc = of_property_read_u32(node, "qcom,current-setting", &val);
Mohan Pallaka38ec7f32013-09-11 13:01:48 +05303085 if (!rc) {
3086 if (led->mpp_cfg->current_setting < LED_MPP_CURRENT_MIN)
3087 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
3088 else if (led->mpp_cfg->current_setting > LED_MPP_CURRENT_MAX)
3089 led->mpp_cfg->current_setting = LED_MPP_CURRENT_MAX;
3090 else
3091 led->mpp_cfg->current_setting = (u8) val;
3092 } else if (rc != -EINVAL)
Amy Malochef3813742013-04-11 19:33:47 -07003093 return rc;
3094
3095 led->mpp_cfg->source_sel = LED_MPP_SOURCE_SEL_DEFAULT;
3096 rc = of_property_read_u32(node, "qcom,source-sel", &val);
3097 if (!rc)
3098 led->mpp_cfg->source_sel = (u8) val;
3099 else if (rc != -EINVAL)
3100 return rc;
3101
3102 led->mpp_cfg->mode_ctrl = LED_MPP_MODE_SINK;
3103 rc = of_property_read_u32(node, "qcom,mode-ctrl", &val);
3104 if (!rc)
3105 led->mpp_cfg->mode_ctrl = (u8) val;
3106 else if (rc != -EINVAL)
3107 return rc;
3108
Chun Zhang874c9ab2013-07-08 17:18:34 -07003109 led->mpp_cfg->vin_ctrl = LED_MPP_VIN_CTRL_DEFAULT;
3110 rc = of_property_read_u32(node, "qcom,vin-ctrl", &val);
3111 if (!rc)
3112 led->mpp_cfg->vin_ctrl = (u8) val;
3113 else if (rc != -EINVAL)
3114 return rc;
3115
3116 led->mpp_cfg->min_brightness = 0;
3117 rc = of_property_read_u32(node, "qcom,min-brightness", &val);
3118 if (!rc)
3119 led->mpp_cfg->min_brightness = (u8) val;
3120 else if (rc != -EINVAL)
3121 return rc;
3122
Amy Malochea2726f02013-05-10 10:19:03 -07003123 rc = of_property_read_string(node, "qcom,mode", &mode);
3124 if (!rc) {
3125 led_mode = qpnp_led_get_mode(mode);
3126 led->mpp_cfg->pwm_mode = led_mode;
3127 if (led_mode == MANUAL_MODE)
3128 return MANUAL_MODE;
3129 else if (led_mode == -EINVAL) {
3130 dev_err(&led->spmi_dev->dev, "Selected mode not " \
3131 "supported for mpp.\n");
3132 return -EINVAL;
3133 }
3134 led->mpp_cfg->pwm_cfg = devm_kzalloc(&led->spmi_dev->dev,
3135 sizeof(struct pwm_config_data),
3136 GFP_KERNEL);
3137 if (!led->mpp_cfg->pwm_cfg) {
3138 dev_err(&led->spmi_dev->dev,
3139 "Unable to allocate memory\n");
3140 return -ENOMEM;
3141 }
3142 led->mpp_cfg->pwm_cfg->mode = led_mode;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003143 led->mpp_cfg->pwm_cfg->default_mode = led_mode;
Amy Malochea2726f02013-05-10 10:19:03 -07003144 } else
3145 return rc;
3146
3147 rc = qpnp_get_config_pwm(led->mpp_cfg->pwm_cfg, led->spmi_dev, node);
3148 if (rc < 0)
3149 return rc;
3150
Amy Malochef3813742013-04-11 19:33:47 -07003151 return 0;
3152}
3153
Amy Malochef3d5a062012-08-16 19:14:11 -07003154static int __devinit qpnp_leds_probe(struct spmi_device *spmi)
3155{
Amy Malochef9490c62012-11-27 19:26:04 -08003156 struct qpnp_led_data *led, *led_array;
Amy Malochef3d5a062012-08-16 19:14:11 -07003157 struct resource *led_resource;
Amy Malochea5ca5552012-10-23 13:34:46 -07003158 struct device_node *node, *temp;
Amy Malochef9490c62012-11-27 19:26:04 -08003159 int rc, i, num_leds = 0, parsed_leds = 0;
Amy Malochef3d5a062012-08-16 19:14:11 -07003160 const char *led_label;
Chun Zhangc3b505b2013-06-03 19:01:49 -07003161 bool regulator_probe = false;
Amy Malochef3d5a062012-08-16 19:14:11 -07003162
Amy Malochea5ca5552012-10-23 13:34:46 -07003163 node = spmi->dev.of_node;
3164 if (node == NULL)
3165 return -ENODEV;
3166
3167 temp = NULL;
3168 while ((temp = of_get_next_child(node, temp)))
3169 num_leds++;
3170
Amy Malochef9490c62012-11-27 19:26:04 -08003171 if (!num_leds)
3172 return -ECHILD;
3173
3174 led_array = devm_kzalloc(&spmi->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07003175 (sizeof(struct qpnp_led_data) * num_leds), GFP_KERNEL);
Amy Malochef9490c62012-11-27 19:26:04 -08003176 if (!led_array) {
Amy Malochef3d5a062012-08-16 19:14:11 -07003177 dev_err(&spmi->dev, "Unable to allocate memory\n");
3178 return -ENOMEM;
3179 }
3180
Amy Malochea5ca5552012-10-23 13:34:46 -07003181 for_each_child_of_node(node, temp) {
Amy Malochef9490c62012-11-27 19:26:04 -08003182 led = &led_array[parsed_leds];
3183 led->num_leds = num_leds;
Amy Malochea5ca5552012-10-23 13:34:46 -07003184 led->spmi_dev = spmi;
Amy Malochef3d5a062012-08-16 19:14:11 -07003185
Amy Malochea5ca5552012-10-23 13:34:46 -07003186 led_resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
3187 if (!led_resource) {
3188 dev_err(&spmi->dev, "Unable to get LED base address\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003189 rc = -ENXIO;
3190 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003191 }
3192 led->base = led_resource->start;
Amy Malochef3d5a062012-08-16 19:14:11 -07003193
Amy Malochea5ca5552012-10-23 13:34:46 -07003194 rc = of_property_read_string(temp, "label", &led_label);
Amy Malochef3d5a062012-08-16 19:14:11 -07003195 if (rc < 0) {
3196 dev_err(&led->spmi_dev->dev,
Amy Malochea5ca5552012-10-23 13:34:46 -07003197 "Failure reading label, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003198 goto fail_id_check;
Amy Malochef3d5a062012-08-16 19:14:11 -07003199 }
Amy Malochea5ca5552012-10-23 13:34:46 -07003200
3201 rc = of_property_read_string(temp, "linux,name",
3202 &led->cdev.name);
3203 if (rc < 0) {
3204 dev_err(&led->spmi_dev->dev,
3205 "Failure reading led name, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003206 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003207 }
3208
3209 rc = of_property_read_u32(temp, "qcom,max-current",
3210 &led->max_current);
3211 if (rc < 0) {
3212 dev_err(&led->spmi_dev->dev,
3213 "Failure reading max_current, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003214 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003215 }
3216
3217 rc = of_property_read_u32(temp, "qcom,id", &led->id);
3218 if (rc < 0) {
3219 dev_err(&led->spmi_dev->dev,
3220 "Failure reading led id, rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003221 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003222 }
3223
3224 rc = qpnp_get_common_configs(led, temp);
3225 if (rc) {
3226 dev_err(&led->spmi_dev->dev,
3227 "Failure reading common led configuration," \
3228 " rc = %d\n", rc);
Amy Malochef9490c62012-11-27 19:26:04 -08003229 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003230 }
3231
3232 led->cdev.brightness_set = qpnp_led_set;
3233 led->cdev.brightness_get = qpnp_led_get;
3234
3235 if (strncmp(led_label, "wled", sizeof("wled")) == 0) {
3236 rc = qpnp_get_config_wled(led, temp);
3237 if (rc < 0) {
3238 dev_err(&led->spmi_dev->dev,
3239 "Unable to read wled config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003240 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003241 }
Amy Maloche864a6d52012-10-03 15:58:12 -07003242 } else if (strncmp(led_label, "flash", sizeof("flash"))
3243 == 0) {
Chun Zhang0d6ca072013-07-30 21:08:39 -07003244 if (!of_find_property(node, "flash-boost-supply", NULL))
Chun Zhangc3b505b2013-06-03 19:01:49 -07003245 regulator_probe = true;
3246 rc = qpnp_get_config_flash(led, temp, &regulator_probe);
Amy Maloche864a6d52012-10-03 15:58:12 -07003247 if (rc < 0) {
3248 dev_err(&led->spmi_dev->dev,
3249 "Unable to read flash config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003250 goto fail_id_check;
Amy Maloche864a6d52012-10-03 15:58:12 -07003251 }
Amy Malocheeea7b592012-10-03 15:59:36 -07003252 } else if (strncmp(led_label, "rgb", sizeof("rgb")) == 0) {
3253 rc = qpnp_get_config_rgb(led, temp);
3254 if (rc < 0) {
3255 dev_err(&led->spmi_dev->dev,
3256 "Unable to read rgb config data\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003257 goto fail_id_check;
Amy Malocheeea7b592012-10-03 15:59:36 -07003258 }
Amy Malochef3813742013-04-11 19:33:47 -07003259 } else if (strncmp(led_label, "mpp", sizeof("mpp")) == 0) {
3260 rc = qpnp_get_config_mpp(led, temp);
3261 if (rc < 0) {
3262 dev_err(&led->spmi_dev->dev,
3263 "Unable to read mpp config data\n");
Amy Malochea2726f02013-05-10 10:19:03 -07003264 goto fail_id_check;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303265 }
3266 } else if (strncmp(led_label, "kpdbl", sizeof("kpdbl")) == 0) {
Mohan Pallaka3a7d3472013-05-02 16:30:19 +05303267 num_kpbl_leds_on = 0;
Mohan Pallaka1f46f022013-03-15 14:56:50 +05303268 rc = qpnp_get_config_kpdbl(led, temp);
3269 if (rc < 0) {
3270 dev_err(&led->spmi_dev->dev,
3271 "Unable to read kpdbl config data\n");
Amy Malochef3813742013-04-11 19:33:47 -07003272 goto fail_id_check;
3273 }
Amy Malochea5ca5552012-10-23 13:34:46 -07003274 } else {
3275 dev_err(&led->spmi_dev->dev, "No LED matching label\n");
Amy Malochef9490c62012-11-27 19:26:04 -08003276 rc = -EINVAL;
3277 goto fail_id_check;
Amy Malochea5ca5552012-10-23 13:34:46 -07003278 }
3279
Chun Zhang815a1832013-06-20 13:47:13 -07003280 mutex_init(&led->lock);
3281 INIT_WORK(&led->work, qpnp_led_work);
Amy Malochea5ca5552012-10-23 13:34:46 -07003282
3283 rc = qpnp_led_initialize(led);
3284 if (rc < 0)
3285 goto fail_id_check;
3286
3287 rc = qpnp_led_set_max_brightness(led);
3288 if (rc < 0)
3289 goto fail_id_check;
3290
3291 rc = led_classdev_register(&spmi->dev, &led->cdev);
3292 if (rc) {
3293 dev_err(&spmi->dev, "unable to register led %d,rc=%d\n",
3294 led->id, rc);
3295 goto fail_id_check;
3296 }
Amy Malochebc97c0d22013-03-24 22:06:16 -07003297
3298 if (led->id == QPNP_ID_FLASH1_LED0 ||
3299 led->id == QPNP_ID_FLASH1_LED1) {
3300 rc = sysfs_create_group(&led->cdev.dev->kobj,
3301 &led_attr_group);
3302 if (rc)
3303 goto fail_id_check;
3304
3305 }
3306
Amy Malochea5c4ed82013-06-05 11:05:28 -07003307 if (led->id == QPNP_ID_LED_MPP) {
3308 if (!led->mpp_cfg->pwm_cfg)
3309 break;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003310 if (led->mpp_cfg->pwm_cfg->mode == PWM_MODE) {
3311 rc = sysfs_create_group(&led->cdev.dev->kobj,
3312 &pwm_attr_group);
3313 if (rc)
3314 goto fail_id_check;
3315 }
Amy Malochea5c4ed82013-06-05 11:05:28 -07003316 if (led->mpp_cfg->pwm_cfg->use_blink) {
3317 rc = sysfs_create_group(&led->cdev.dev->kobj,
3318 &blink_attr_group);
3319 if (rc)
3320 goto fail_id_check;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003321
3322 rc = sysfs_create_group(&led->cdev.dev->kobj,
3323 &lpg_attr_group);
3324 if (rc)
3325 goto fail_id_check;
3326 } else if (led->mpp_cfg->pwm_cfg->mode == LPG_MODE) {
3327 rc = sysfs_create_group(&led->cdev.dev->kobj,
3328 &lpg_attr_group);
3329 if (rc)
3330 goto fail_id_check;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003331 }
3332 } else if ((led->id == QPNP_ID_RGB_RED) ||
3333 (led->id == QPNP_ID_RGB_GREEN) ||
3334 (led->id == QPNP_ID_RGB_BLUE)) {
Amy Maloche4cf9f322013-05-29 15:53:46 -07003335 if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
3336 rc = sysfs_create_group(&led->cdev.dev->kobj,
3337 &pwm_attr_group);
3338 if (rc)
3339 goto fail_id_check;
3340 }
Amy Malochea5c4ed82013-06-05 11:05:28 -07003341 if (led->rgb_cfg->pwm_cfg->use_blink) {
3342 rc = sysfs_create_group(&led->cdev.dev->kobj,
3343 &blink_attr_group);
3344 if (rc)
3345 goto fail_id_check;
Amy Maloche4cf9f322013-05-29 15:53:46 -07003346
3347 rc = sysfs_create_group(&led->cdev.dev->kobj,
3348 &lpg_attr_group);
3349 if (rc)
3350 goto fail_id_check;
3351 } else if (led->rgb_cfg->pwm_cfg->mode == LPG_MODE) {
3352 rc = sysfs_create_group(&led->cdev.dev->kobj,
3353 &lpg_attr_group);
3354 if (rc)
3355 goto fail_id_check;
Amy Malochea5c4ed82013-06-05 11:05:28 -07003356 }
3357 }
3358
Amy Malochea5ca5552012-10-23 13:34:46 -07003359 /* configure default state */
Asaf Penso55ac8472013-01-21 21:17:37 +02003360 if (led->default_on) {
Amy Malochea5ca5552012-10-23 13:34:46 -07003361 led->cdev.brightness = led->cdev.max_brightness;
Chun Zhang815a1832013-06-20 13:47:13 -07003362 __qpnp_led_work(led, led->cdev.brightness);
3363 schedule_work(&led->work);
Asaf Penso55ac8472013-01-21 21:17:37 +02003364 if (led->turn_off_delay_ms > 0)
3365 qpnp_led_turn_off(led);
3366 } else
Amy Malochea5ca5552012-10-23 13:34:46 -07003367 led->cdev.brightness = LED_OFF;
3368
Amy Malochef9490c62012-11-27 19:26:04 -08003369 parsed_leds++;
Amy Malochef3d5a062012-08-16 19:14:11 -07003370 }
Amy Malochef9490c62012-11-27 19:26:04 -08003371 dev_set_drvdata(&spmi->dev, led_array);
Amy Malochef3d5a062012-08-16 19:14:11 -07003372 return 0;
3373
3374fail_id_check:
Chun Zhang815a1832013-06-20 13:47:13 -07003375 for (i = 0; i < parsed_leds; i++) {
3376 mutex_destroy(&led_array[i].lock);
Amy Malochef9490c62012-11-27 19:26:04 -08003377 led_classdev_unregister(&led_array[i].cdev);
Chun Zhang815a1832013-06-20 13:47:13 -07003378 }
3379
Amy Malochef3d5a062012-08-16 19:14:11 -07003380 return rc;
3381}
3382
3383static int __devexit qpnp_leds_remove(struct spmi_device *spmi)
3384{
Amy Malochef9490c62012-11-27 19:26:04 -08003385 struct qpnp_led_data *led_array = dev_get_drvdata(&spmi->dev);
3386 int i, parsed_leds = led_array->num_leds;
Amy Malochef3d5a062012-08-16 19:14:11 -07003387
Amy Malochebc97c0d22013-03-24 22:06:16 -07003388 for (i = 0; i < parsed_leds; i++) {
Chun Zhang815a1832013-06-20 13:47:13 -07003389 cancel_work_sync(&led_array[i].work);
3390 mutex_destroy(&led_array[i].lock);
Amy Malochef9490c62012-11-27 19:26:04 -08003391 led_classdev_unregister(&led_array[i].cdev);
Amy Malochebc97c0d22013-03-24 22:06:16 -07003392 switch (led_array[i].id) {
3393 case QPNP_ID_WLED:
3394 break;
3395 case QPNP_ID_FLASH1_LED0:
3396 case QPNP_ID_FLASH1_LED1:
Chun Zhangda8ad0f2013-07-17 14:46:47 -07003397 if (led_array[i].flash_cfg->flash_reg_get)
Chun Zhangc3b505b2013-06-03 19:01:49 -07003398 regulator_put(led_array[i].flash_cfg-> \
3399 flash_boost_reg);
Chun Zhangdf2d3062013-06-25 20:14:46 -07003400 if (led_array[i].flash_cfg->torch_enable)
3401 regulator_put(led_array[i].flash_cfg->\
3402 torch_boost_reg);
Amy Malochebc97c0d22013-03-24 22:06:16 -07003403 sysfs_remove_group(&led_array[i].cdev.dev->kobj,
3404 &led_attr_group);
3405 break;
3406 case QPNP_ID_RGB_RED:
3407 case QPNP_ID_RGB_GREEN:
3408 case QPNP_ID_RGB_BLUE:
Amy Maloche4cf9f322013-05-29 15:53:46 -07003409 if (led_array[i].rgb_cfg->pwm_cfg->mode == PWM_MODE)
3410 sysfs_remove_group(&led_array[i].cdev.dev->\
3411 kobj, &pwm_attr_group);
3412 if (led_array[i].rgb_cfg->pwm_cfg->use_blink) {
3413 sysfs_remove_group(&led_array[i].cdev.dev->\
3414 kobj, &blink_attr_group);
3415 sysfs_remove_group(&led_array[i].cdev.dev->\
3416 kobj, &lpg_attr_group);
3417 } else if (led_array[i].rgb_cfg->pwm_cfg->mode\
3418 == LPG_MODE)
3419 sysfs_remove_group(&led_array[i].cdev.dev->\
3420 kobj, &lpg_attr_group);
3421 break;
3422 case QPNP_ID_LED_MPP:
3423 if (!led_array[i].mpp_cfg->pwm_cfg)
3424 break;
3425 if (led_array[i].mpp_cfg->pwm_cfg->mode == PWM_MODE)
3426 sysfs_remove_group(&led_array[i].cdev.dev->\
3427 kobj, &pwm_attr_group);
3428 if (led_array[i].mpp_cfg->pwm_cfg->use_blink) {
3429 sysfs_remove_group(&led_array[i].cdev.dev->\
3430 kobj, &blink_attr_group);
3431 sysfs_remove_group(&led_array[i].cdev.dev->\
3432 kobj, &lpg_attr_group);
3433 } else if (led_array[i].mpp_cfg->pwm_cfg->mode\
3434 == LPG_MODE)
3435 sysfs_remove_group(&led_array[i].cdev.dev->\
3436 kobj, &lpg_attr_group);
3437 break;
Amy Malochebc97c0d22013-03-24 22:06:16 -07003438 default:
3439 dev_err(&led_array[i].spmi_dev->dev,
3440 "Invalid LED(%d)\n",
3441 led_array[i].id);
3442 return -EINVAL;
3443 }
3444 }
Amy Malochef3d5a062012-08-16 19:14:11 -07003445
3446 return 0;
3447}
3448static struct of_device_id spmi_match_table[] = {
3449 { .compatible = "qcom,leds-qpnp",
3450 }
3451};
3452
3453static struct spmi_driver qpnp_leds_driver = {
3454 .driver = {
3455 .name = "qcom,leds-qpnp",
3456 .of_match_table = spmi_match_table,
3457 },
3458 .probe = qpnp_leds_probe,
3459 .remove = __devexit_p(qpnp_leds_remove),
3460};
3461
3462static int __init qpnp_led_init(void)
3463{
3464 return spmi_driver_register(&qpnp_leds_driver);
3465}
3466module_init(qpnp_led_init);
3467
3468static void __exit qpnp_led_exit(void)
3469{
3470 spmi_driver_unregister(&qpnp_leds_driver);
3471}
3472module_exit(qpnp_led_exit);
3473
3474MODULE_DESCRIPTION("QPNP LEDs driver");
3475MODULE_LICENSE("GPL v2");
3476MODULE_ALIAS("leds:leds-qpnp");
Amy Maloche864a6d52012-10-03 15:58:12 -07003477