blob: 484c84344918a7581ed951ea3100afd627d12dfe [file] [log] [blame]
David Collins8885f792017-01-26 14:36:34 -08001/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/module.h>
14#include <linux/init.h>
15#include <linux/kernel.h>
16#include <linux/regmap.h>
17#include <linux/errno.h>
18#include <linux/leds.h>
19#include <linux/slab.h>
20#include <linux/of_device.h>
21#include <linux/of_address.h>
22#include <linux/spmi.h>
23#include <linux/platform_device.h>
24#include <linux/interrupt.h>
25#include <linux/err.h>
26#include <linux/delay.h>
27#include <linux/leds-qpnp-wled.h>
28#include <linux/qpnp/qpnp-revid.h>
29
30/* base addresses */
31#define QPNP_WLED_CTRL_BASE "qpnp-wled-ctrl-base"
32#define QPNP_WLED_SINK_BASE "qpnp-wled-sink-base"
33
34/* ctrl registers */
35#define QPNP_WLED_FAULT_STATUS(b) (b + 0x08)
Subbaraman Narayanamurthyb5ae3182017-02-17 15:14:06 -080036#define QPNP_WLED_INT_RT_STS(b) (b + 0x10)
David Collins8885f792017-01-26 14:36:34 -080037#define QPNP_WLED_EN_REG(b) (b + 0x46)
38#define QPNP_WLED_FDBK_OP_REG(b) (b + 0x48)
39#define QPNP_WLED_VREF_REG(b) (b + 0x49)
40#define QPNP_WLED_BOOST_DUTY_REG(b) (b + 0x4B)
41#define QPNP_WLED_SWITCH_FREQ_REG(b) (b + 0x4C)
42#define QPNP_WLED_OVP_REG(b) (b + 0x4D)
43#define QPNP_WLED_ILIM_REG(b) (b + 0x4E)
44#define QPNP_WLED_AMOLED_VOUT_REG(b) (b + 0x4F)
45#define QPNP_WLED_SOFTSTART_RAMP_DLY(b) (b + 0x53)
46#define QPNP_WLED_VLOOP_COMP_RES_REG(b) (b + 0x55)
47#define QPNP_WLED_VLOOP_COMP_GM_REG(b) (b + 0x56)
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -080048#define QPNP_WLED_EN_PSM_REG(b) (b + 0x5A)
David Collins8885f792017-01-26 14:36:34 -080049#define QPNP_WLED_PSM_CTRL_REG(b) (b + 0x5B)
50#define QPNP_WLED_LCD_AUTO_PFM_REG(b) (b + 0x5C)
51#define QPNP_WLED_SC_PRO_REG(b) (b + 0x5E)
52#define QPNP_WLED_SWIRE_AVDD_REG(b) (b + 0x5F)
53#define QPNP_WLED_CTRL_SPARE_REG(b) (b + 0xDF)
54#define QPNP_WLED_TEST1_REG(b) (b + 0xE2)
55#define QPNP_WLED_TEST4_REG(b) (b + 0xE5)
56#define QPNP_WLED_REF_7P7_TRIM_REG(b) (b + 0xF2)
57
58#define QPNP_WLED_7P7_TRIM_MASK GENMASK(3, 0)
59#define QPNP_WLED_EN_MASK 0x7F
60#define QPNP_WLED_EN_SHIFT 7
61#define QPNP_WLED_FDBK_OP_MASK 0xF8
62#define QPNP_WLED_VREF_MASK GENMASK(3, 0)
63
64#define QPNP_WLED_VLOOP_COMP_RES_MASK 0xF0
65#define QPNP_WLED_VLOOP_COMP_RES_OVERWRITE 0x80
David Collins8885f792017-01-26 14:36:34 -080066#define QPNP_WLED_LOOP_COMP_RES_STEP_KOHM 20
67#define QPNP_WLED_LOOP_COMP_RES_MIN_KOHM 20
68#define QPNP_WLED_LOOP_COMP_RES_MAX_KOHM 320
69#define QPNP_WLED_VLOOP_COMP_GM_MASK GENMASK(3, 0)
70#define QPNP_WLED_VLOOP_COMP_GM_OVERWRITE 0x80
71#define QPNP_WLED_VLOOP_COMP_AUTO_GM_EN BIT(6)
72#define QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK GENMASK(5, 4)
73#define QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT 4
74#define QPNP_WLED_LOOP_EA_GM_DFLT_AMOLED_PMI8994 0x03
75#define QPNP_WLED_LOOP_GM_DFLT_AMOLED_PMI8998 0x09
76#define QPNP_WLED_LOOP_GM_DFLT_WLED 0x09
77#define QPNP_WLED_LOOP_EA_GM_MIN 0x0
78#define QPNP_WLED_LOOP_EA_GM_MAX 0xF
79#define QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX 3
80#define QPNP_WLED_LOOP_AUTO_GM_DFLT_THRESH 1
81#define QPNP_WLED_VREF_PSM_MASK 0xF8
82#define QPNP_WLED_VREF_PSM_STEP_MV 50
83#define QPNP_WLED_VREF_PSM_MIN_MV 400
84#define QPNP_WLED_VREF_PSM_MAX_MV 750
85#define QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV 450
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -080086#define QPNP_WLED_PSM_OVERWRITE_BIT BIT(7)
David Collins8885f792017-01-26 14:36:34 -080087#define QPNP_WLED_LCD_AUTO_PFM_DFLT_THRESH 1
88#define QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX 0xF
89#define QPNP_WLED_LCD_AUTO_PFM_EN_SHIFT 7
90#define QPNP_WLED_LCD_AUTO_PFM_EN_BIT BIT(7)
91#define QPNP_WLED_LCD_AUTO_PFM_THRESH_MASK GENMASK(3, 0)
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -080092#define QPNP_WLED_EN_PSM_BIT BIT(7)
David Collins8885f792017-01-26 14:36:34 -080093
94#define QPNP_WLED_ILIM_MASK GENMASK(2, 0)
95#define QPNP_WLED_ILIM_OVERWRITE BIT(7)
96#define PMI8994_WLED_ILIM_MIN_MA 105
97#define PMI8994_WLED_ILIM_MAX_MA 1980
98#define PMI8994_WLED_DFLT_ILIM_MA 980
99#define PMI8994_AMOLED_DFLT_ILIM_MA 385
100#define PMI8998_WLED_ILIM_MAX_MA 1500
101#define PMI8998_WLED_DFLT_ILIM_MA 970
102#define PMI8998_AMOLED_DFLT_ILIM_MA 620
103#define QPNP_WLED_BOOST_DUTY_MASK 0xFC
104#define QPNP_WLED_BOOST_DUTY_STEP_NS 52
105#define QPNP_WLED_BOOST_DUTY_MIN_NS 26
106#define QPNP_WLED_BOOST_DUTY_MAX_NS 156
107#define QPNP_WLED_DEF_BOOST_DUTY_NS 104
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -0700108#define QPNP_WLED_SWITCH_FREQ_MASK GENMASK(3, 0)
109#define QPNP_WLED_SWITCH_FREQ_OVERWRITE BIT(7)
David Collins8885f792017-01-26 14:36:34 -0800110#define QPNP_WLED_OVP_MASK GENMASK(1, 0)
111#define QPNP_WLED_TEST4_EN_DEB_BYPASS_ILIM_BIT BIT(6)
112#define QPNP_WLED_TEST4_EN_SH_FOR_SS_BIT BIT(5)
113#define QPNP_WLED_TEST4_EN_CLAMP_BIT BIT(4)
114#define QPNP_WLED_TEST4_EN_SOFT_START_BIT BIT(1)
115#define QPNP_WLED_TEST4_EN_VREF_UP \
116 (QPNP_WLED_TEST4_EN_SH_FOR_SS_BIT | \
117 QPNP_WLED_TEST4_EN_CLAMP_BIT | \
118 QPNP_WLED_TEST4_EN_SOFT_START_BIT)
119#define QPNP_WLED_TEST4_EN_IIND_UP 0x1
Subbaraman Narayanamurthyb5ae3182017-02-17 15:14:06 -0800120#define QPNP_WLED_ILIM_FAULT_BIT BIT(0)
121#define QPNP_WLED_OVP_FAULT_BIT BIT(1)
122#define QPNP_WLED_SC_FAULT_BIT BIT(2)
Anirudh Ghayalce9914f2017-08-31 12:11:06 +0530123#define QPNP_WLED_OVP_FLT_RT_STS_BIT BIT(1)
David Collins8885f792017-01-26 14:36:34 -0800124
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -0700125/* QPNP_WLED_SOFTSTART_RAMP_DLY */
126#define SOFTSTART_OVERWRITE_BIT BIT(7)
127#define SOFTSTART_RAMP_DELAY_MASK GENMASK(2, 0)
128
David Collins8885f792017-01-26 14:36:34 -0800129/* sink registers */
130#define QPNP_WLED_CURR_SINK_REG(b) (b + 0x46)
131#define QPNP_WLED_SYNC_REG(b) (b + 0x47)
132#define QPNP_WLED_MOD_REG(b) (b + 0x4A)
133#define QPNP_WLED_HYB_THRES_REG(b) (b + 0x4B)
134#define QPNP_WLED_MOD_EN_REG(b, n) (b + 0x50 + (n * 0x10))
135#define QPNP_WLED_SYNC_DLY_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x01)
136#define QPNP_WLED_FS_CURR_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x02)
137#define QPNP_WLED_CABC_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x06)
138#define QPNP_WLED_BRIGHT_LSB_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x07)
139#define QPNP_WLED_BRIGHT_MSB_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x08)
140#define QPNP_WLED_SINK_TEST5_REG(b) (b + 0xE6)
141
142#define QPNP_WLED_MOD_FREQ_1200_KHZ 1200
143#define QPNP_WLED_MOD_FREQ_2400_KHZ 2400
144#define QPNP_WLED_MOD_FREQ_9600_KHZ 9600
145#define QPNP_WLED_MOD_FREQ_19200_KHZ 19200
146#define QPNP_WLED_MOD_FREQ_MASK 0x3F
147#define QPNP_WLED_MOD_FREQ_SHIFT 6
148#define QPNP_WLED_ACC_CLK_FREQ_MASK 0xE7
149#define QPNP_WLED_ACC_CLK_FREQ_SHIFT 3
150#define QPNP_WLED_PHASE_STAG_MASK 0xDF
151#define QPNP_WLED_PHASE_STAG_SHIFT 5
152#define QPNP_WLED_DIM_RES_MASK 0xFD
153#define QPNP_WLED_DIM_RES_SHIFT 1
154#define QPNP_WLED_DIM_HYB_MASK 0xFB
155#define QPNP_WLED_DIM_HYB_SHIFT 2
156#define QPNP_WLED_DIM_ANA_MASK 0xFE
157#define QPNP_WLED_HYB_THRES_MASK 0xF8
158#define QPNP_WLED_HYB_THRES_MIN 78
159#define QPNP_WLED_DEF_HYB_THRES 625
160#define QPNP_WLED_HYB_THRES_MAX 10000
161#define QPNP_WLED_MOD_EN_MASK 0x7F
162#define QPNP_WLED_MOD_EN_SHFT 7
163#define QPNP_WLED_MOD_EN 1
164#define QPNP_WLED_GATE_DRV_MASK 0xFE
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530165#define QPNP_WLED_SYNC_DLY_MASK GENMASK(2, 0)
David Collins8885f792017-01-26 14:36:34 -0800166#define QPNP_WLED_SYNC_DLY_MIN_US 0
167#define QPNP_WLED_SYNC_DLY_MAX_US 1400
168#define QPNP_WLED_SYNC_DLY_STEP_US 200
169#define QPNP_WLED_DEF_SYNC_DLY_US 400
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530170#define QPNP_WLED_FS_CURR_MASK GENMASK(3, 0)
David Collins8885f792017-01-26 14:36:34 -0800171#define QPNP_WLED_FS_CURR_MIN_UA 0
172#define QPNP_WLED_FS_CURR_MAX_UA 30000
173#define QPNP_WLED_FS_CURR_STEP_UA 2500
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530174#define QPNP_WLED_CABC_MASK 0x80
David Collins8885f792017-01-26 14:36:34 -0800175#define QPNP_WLED_CABC_SHIFT 7
176#define QPNP_WLED_CURR_SINK_SHIFT 4
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530177#define QPNP_WLED_CURR_SINK_MASK GENMASK(7, 4)
David Collins8885f792017-01-26 14:36:34 -0800178#define QPNP_WLED_BRIGHT_LSB_MASK 0xFF
179#define QPNP_WLED_BRIGHT_MSB_SHIFT 8
180#define QPNP_WLED_BRIGHT_MSB_MASK 0x0F
181#define QPNP_WLED_SYNC 0x0F
182#define QPNP_WLED_SYNC_RESET 0x00
183
184#define QPNP_WLED_SINK_TEST5_HYB 0x14
185#define QPNP_WLED_SINK_TEST5_DIG 0x1E
186#define QPNP_WLED_SINK_TEST5_HVG_PULL_STR_BIT BIT(3)
187
188#define QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE 0x0B
189#define QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE 0x05
190
191#define QPNP_WLED_DISP_SEL_REG(b) (b + 0x44)
192#define QPNP_WLED_MODULE_RDY_REG(b) (b + 0x45)
193#define QPNP_WLED_MODULE_EN_REG(b) (b + 0x46)
194#define QPNP_WLED_MODULE_RDY_MASK 0x7F
195#define QPNP_WLED_MODULE_RDY_SHIFT 7
196#define QPNP_WLED_MODULE_EN_MASK BIT(7)
197#define QPNP_WLED_MODULE_EN_SHIFT 7
198#define QPNP_WLED_DISP_SEL_MASK 0x7F
199#define QPNP_WLED_DISP_SEL_SHIFT 7
200#define QPNP_WLED_EN_SC_DEB_CYCLES_MASK 0x79
201#define QPNP_WLED_EN_DEB_CYCLES_MASK 0xF9
202#define QPNP_WLED_EN_SC_SHIFT 7
203#define QPNP_WLED_SC_PRO_EN_DSCHGR 0x8
204#define QPNP_WLED_SC_DEB_CYCLES_MIN 2
205#define QPNP_WLED_SC_DEB_CYCLES_MAX 16
206#define QPNP_WLED_SC_DEB_CYCLES_SUB 2
207#define QPNP_WLED_SC_DEB_CYCLES_DFLT 4
208#define QPNP_WLED_EXT_FET_DTEST2 0x09
209
210#define QPNP_WLED_SEC_ACCESS_REG(b) (b + 0xD0)
211#define QPNP_WLED_SEC_UNLOCK 0xA5
212
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800213#define NUM_DDIC_CODES 256
David Collins8885f792017-01-26 14:36:34 -0800214#define QPNP_WLED_MAX_STRINGS 4
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530215#define QPNP_PM660_WLED_MAX_STRINGS 3
David Collins8885f792017-01-26 14:36:34 -0800216#define WLED_MAX_LEVEL_4095 4095
217#define QPNP_WLED_RAMP_DLY_MS 20
218#define QPNP_WLED_TRIGGER_NONE "none"
219#define QPNP_WLED_STR_SIZE 20
220#define QPNP_WLED_MIN_MSLEEP 20
221#define QPNP_WLED_SC_DLY_MS 20
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530222#define QPNP_WLED_SOFT_START_DLY_US 10000
David Collins8885f792017-01-26 14:36:34 -0800223
224#define NUM_SUPPORTED_AVDD_VOLTAGES 6
225#define QPNP_WLED_DFLT_AVDD_MV 7600
226#define QPNP_WLED_AVDD_MIN_MV 5650
227#define QPNP_WLED_AVDD_MAX_MV 7900
228#define QPNP_WLED_AVDD_STEP_MV 150
229#define QPNP_WLED_AVDD_MIN_TRIM_VAL 0x0
230#define QPNP_WLED_AVDD_MAX_TRIM_VAL 0xF
231#define QPNP_WLED_AVDD_SEL_SPMI_BIT BIT(7)
232#define QPNP_WLED_AVDD_SET_BIT BIT(4)
233
234#define NUM_SUPPORTED_OVP_THRESHOLDS 4
235#define NUM_SUPPORTED_ILIM_THRESHOLDS 8
236
237#define QPNP_WLED_AVDD_MV_TO_REG(val) \
238 ((val - QPNP_WLED_AVDD_MIN_MV) / QPNP_WLED_AVDD_STEP_MV)
239
240/* output feedback mode */
241enum qpnp_wled_fdbk_op {
242 QPNP_WLED_FDBK_AUTO,
243 QPNP_WLED_FDBK_WLED1,
244 QPNP_WLED_FDBK_WLED2,
245 QPNP_WLED_FDBK_WLED3,
246 QPNP_WLED_FDBK_WLED4,
247};
248
249/* dimming modes */
250enum qpnp_wled_dim_mode {
251 QPNP_WLED_DIM_ANALOG,
252 QPNP_WLED_DIM_DIGITAL,
253 QPNP_WLED_DIM_HYBRID,
254};
255
256/* wled ctrl debug registers */
257static u8 qpnp_wled_ctrl_dbg_regs[] = {
258 0x44, 0x46, 0x48, 0x49, 0x4b, 0x4c, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53,
259 0x54, 0x55, 0x56, 0x57, 0x58, 0x5a, 0x5b, 0x5d, 0x5e, 0xe2
260};
261
262/* wled sink debug registers */
263static u8 qpnp_wled_sink_dbg_regs[] = {
264 0x46, 0x47, 0x48, 0x4a, 0x4b,
265 0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x58,
266 0x60, 0x61, 0x62, 0x63, 0x66, 0x67, 0x68,
267 0x70, 0x71, 0x72, 0x73, 0x76, 0x77, 0x78,
268 0x80, 0x81, 0x82, 0x83, 0x86, 0x87, 0x88,
269 0xe6,
270};
271
272static int qpnp_wled_avdd_target_voltages[NUM_SUPPORTED_AVDD_VOLTAGES] = {
273 7900, 7600, 7300, 6400, 6100, 5800,
274};
275
276static u8 qpnp_wled_ovp_reg_settings[NUM_SUPPORTED_AVDD_VOLTAGES] = {
277 0x0, 0x0, 0x1, 0x2, 0x2, 0x3,
278};
279
280static int qpnp_wled_avdd_trim_adjustments[NUM_SUPPORTED_AVDD_VOLTAGES] = {
281 3, 0, -2, 7, 3, 3,
282};
283
284static int qpnp_wled_ovp_thresholds_pmi8994[NUM_SUPPORTED_OVP_THRESHOLDS] = {
285 31000, 29500, 19400, 17800,
286};
287
288static int qpnp_wled_ovp_thresholds_pmi8998[NUM_SUPPORTED_OVP_THRESHOLDS] = {
289 31100, 29600, 19600, 18100,
290};
291
292static int qpnp_wled_ilim_settings_pmi8994[NUM_SUPPORTED_ILIM_THRESHOLDS] = {
293 105, 385, 660, 980, 1150, 1420, 1700, 1980,
294};
295
296static int qpnp_wled_ilim_settings_pmi8998[NUM_SUPPORTED_ILIM_THRESHOLDS] = {
297 105, 280, 450, 620, 970, 1150, 1300, 1500,
298};
299
300struct wled_vref_setting {
301 u32 min_uv;
302 u32 max_uv;
303 u32 step_uv;
304 u32 default_uv;
305};
306
307static struct wled_vref_setting vref_setting_pmi8994 = {
308 300000, 675000, 25000, 350000,
309};
310static struct wled_vref_setting vref_setting_pmi8998 = {
311 60000, 397500, 22500, 127500,
312};
313
314/**
315 * qpnp_wled - wed data structure
316 * @ cdev - led class device
317 * @ pdev - platform device
318 * @ work - worker for led operation
319 * @ lock - mutex lock for exclusive access
320 * @ fdbk_op - output feedback mode
321 * @ dim_mode - dimming mode
322 * @ ovp_irq - over voltage protection irq
323 * @ sc_irq - short circuit irq
324 * @ sc_cnt - short circuit irq count
325 * @ avdd_target_voltage_mv - target voltage for AVDD module in mV
326 * @ ctrl_base - base address for wled ctrl
327 * @ sink_base - base address for wled sink
328 * @ mod_freq_khz - modulator frequency in KHZ
329 * @ hyb_thres - threshold for hybrid dimming
330 * @ sync_dly_us - sync delay in us
331 * @ vref_uv - ref voltage in uv
332 * @ vref_psm_mv - ref psm voltage in mv
333 * @ loop_comp_res_kohm - control to select the compensation resistor
334 * @ loop_ea_gm - control to select the gm for the gm stage in control loop
335 * @ sc_deb_cycles - debounce time for short circuit detection
336 * @ switch_freq_khz - switching frequency in KHZ
337 * @ ovp_mv - over voltage protection in mv
338 * @ ilim_ma - current limiter in ma
339 * @ boost_duty_ns - boost duty cycle in ns
340 * @ fs_curr_ua - full scale current in ua
341 * @ ramp_ms - delay between ramp steps in ms
342 * @ ramp_step - ramp step size
343 * @ cons_sync_write_delay_us - delay between two consecutive writes to SYNC
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800344 * @ auto_calibration_ovp_count - OVP fault irq count to run auto calibration
345 * @ max_strings - Number of strings supported in WLED peripheral
346 * @ prev_level - Previous brightness level
347 * @ brt_map_table - Brightness map table
David Collins8885f792017-01-26 14:36:34 -0800348 * @ strings - supported list of strings
349 * @ num_strings - number of strings
350 * @ loop_auto_gm_thresh - the clamping level for auto gm
351 * @ lcd_auto_pfm_thresh - the threshold for lcd auto pfm mode
352 * @ loop_auto_gm_en - select if auto gm is enabled
353 * @ lcd_auto_pfm_en - select if auto pfm is enabled in lcd mode
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800354 * @ lcd_psm_ctrl - select if psm needs to be controlled in lcd mode
David Collins8885f792017-01-26 14:36:34 -0800355 * @ avdd_mode_spmi - enable avdd programming via spmi
356 * @ en_9b_dim_res - enable or disable 9bit dimming
357 * @ en_phase_stag - enable or disable phase staggering
358 * @ en_cabc - enable or disable cabc
359 * @ disp_type_amoled - type of display: LCD/AMOLED
360 * @ en_ext_pfet_sc_pro - enable sc protection on external pfet
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800361 * @ prev_state - previous state of WLED
362 * @ ovp_irq_disabled - OVP interrupt disable status
363 * @ auto_calib_enabled - Flag to enable auto calibration feature
364 * @ auto_calib_done - Flag to indicate auto calibration is done
365 * @ module_dis_perm - Flat to keep module permanently disabled
366 * @ start_ovp_fault_time - Time when the OVP fault first occurred
David Collins8885f792017-01-26 14:36:34 -0800367 */
368struct qpnp_wled {
369 struct led_classdev cdev;
370 struct platform_device *pdev;
371 struct regmap *regmap;
372 struct pmic_revid_data *pmic_rev_id;
373 struct work_struct work;
374 struct mutex lock;
375 struct mutex bus_lock;
376 enum qpnp_wled_fdbk_op fdbk_op;
377 enum qpnp_wled_dim_mode dim_mode;
378 int ovp_irq;
379 int sc_irq;
380 u32 sc_cnt;
381 u32 avdd_target_voltage_mv;
382 u16 ctrl_base;
383 u16 sink_base;
384 u16 mod_freq_khz;
385 u16 hyb_thres;
386 u16 sync_dly_us;
387 u32 vref_uv;
388 u16 vref_psm_mv;
389 u16 loop_comp_res_kohm;
390 u16 loop_ea_gm;
391 u16 sc_deb_cycles;
392 u16 switch_freq_khz;
393 u16 ovp_mv;
394 u16 ilim_ma;
395 u16 boost_duty_ns;
396 u16 fs_curr_ua;
397 u16 ramp_ms;
398 u16 ramp_step;
399 u16 cons_sync_write_delay_us;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530400 u16 auto_calibration_ovp_count;
401 u16 max_strings;
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800402 u16 prev_level;
403 u16 *brt_map_table;
David Collins8885f792017-01-26 14:36:34 -0800404 u8 strings[QPNP_WLED_MAX_STRINGS];
405 u8 num_strings;
406 u8 loop_auto_gm_thresh;
407 u8 lcd_auto_pfm_thresh;
408 bool loop_auto_gm_en;
409 bool lcd_auto_pfm_en;
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800410 bool lcd_psm_ctrl;
David Collins8885f792017-01-26 14:36:34 -0800411 bool avdd_mode_spmi;
412 bool en_9b_dim_res;
413 bool en_phase_stag;
414 bool en_cabc;
415 bool disp_type_amoled;
416 bool en_ext_pfet_sc_pro;
417 bool prev_state;
418 bool ovp_irq_disabled;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530419 bool auto_calib_enabled;
420 bool auto_calib_done;
Subbaraman Narayanamurthy15624d92017-10-12 21:04:30 -0700421 bool module_dis_perm;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530422 ktime_t start_ovp_fault_time;
David Collins8885f792017-01-26 14:36:34 -0800423};
424
425/* helper to read a pmic register */
426static int qpnp_wled_read_reg(struct qpnp_wled *wled, u16 addr, u8 *data)
427{
428 int rc;
429 uint val;
430
431 rc = regmap_read(wled->regmap, addr, &val);
432 if (rc < 0) {
433 dev_err(&wled->pdev->dev,
434 "Error reading address: %x(%d)\n", addr, rc);
435 return rc;
436 }
437
438 *data = (u8)val;
439 return 0;
440}
441
442/* helper to write a pmic register */
443static int qpnp_wled_write_reg(struct qpnp_wled *wled, u16 addr, u8 data)
444{
445 int rc;
446
447 mutex_lock(&wled->bus_lock);
448 rc = regmap_write(wled->regmap, addr, data);
449 if (rc < 0) {
450 dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
451 addr, rc);
452 goto out;
453 }
454
455 dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
456out:
457 mutex_unlock(&wled->bus_lock);
458 return rc;
459}
460
461static int qpnp_wled_masked_write_reg(struct qpnp_wled *wled, u16 addr,
462 u8 mask, u8 data)
463{
464 int rc;
465
466 mutex_lock(&wled->bus_lock);
467 rc = regmap_update_bits(wled->regmap, addr, mask, data);
468 if (rc < 0) {
469 dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
470 addr, rc);
471 goto out;
472 }
473
474 dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
475out:
476 mutex_unlock(&wled->bus_lock);
477 return rc;
478}
479
480static int qpnp_wled_sec_write_reg(struct qpnp_wled *wled, u16 addr, u8 data)
481{
482 int rc;
483 u8 reg = QPNP_WLED_SEC_UNLOCK;
484 u16 base_addr = addr & 0xFF00;
485
486 mutex_lock(&wled->bus_lock);
487 rc = regmap_write(wled->regmap, QPNP_WLED_SEC_ACCESS_REG(base_addr),
488 reg);
489 if (rc < 0) {
490 dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
491 QPNP_WLED_SEC_ACCESS_REG(base_addr), rc);
492 goto out;
493 }
494
495 rc = regmap_write(wled->regmap, addr, data);
496 if (rc < 0) {
497 dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
498 addr, rc);
499 goto out;
500 }
501
502 dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
503out:
504 mutex_unlock(&wled->bus_lock);
505 return rc;
506}
507
508static int qpnp_wled_swire_avdd_config(struct qpnp_wled *wled)
509{
510 int rc;
511 u8 val;
512
513 if (wled->pmic_rev_id->pmic_subtype != PMI8998_SUBTYPE &&
514 wled->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
515 return 0;
516
517 if (!wled->disp_type_amoled || wled->avdd_mode_spmi)
518 return 0;
519
520 val = QPNP_WLED_AVDD_MV_TO_REG(wled->avdd_target_voltage_mv);
521 rc = qpnp_wled_write_reg(wled,
522 QPNP_WLED_SWIRE_AVDD_REG(wled->ctrl_base), val);
523 return rc;
524}
525
526static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled)
527{
528 int rc;
529 u8 reg;
530
531 /* sync */
532 reg = QPNP_WLED_SYNC;
533 rc = qpnp_wled_write_reg(wled, QPNP_WLED_SYNC_REG(wled->sink_base),
534 reg);
535 if (rc < 0)
536 return rc;
537
538 if (wled->cons_sync_write_delay_us)
539 usleep_range(wled->cons_sync_write_delay_us,
540 wled->cons_sync_write_delay_us + 1);
541
542 reg = QPNP_WLED_SYNC_RESET;
543 rc = qpnp_wled_write_reg(wled, QPNP_WLED_SYNC_REG(wled->sink_base),
544 reg);
545 if (rc < 0)
546 return rc;
547
548 return 0;
549}
550
551/* set wled to a level of brightness */
552static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
553{
554 int i, rc;
555 u8 reg;
Subbaraman Narayanamurthycb5b72e2017-08-21 19:16:37 -0700556 u16 low_limit = WLED_MAX_LEVEL_4095 * 4 / 1000;
557
558 /* WLED's lower limit of operation is 0.4% */
559 if (level > 0 && level < low_limit)
560 level = low_limit;
David Collins8885f792017-01-26 14:36:34 -0800561
562 /* set brightness registers */
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530563 for (i = 0; i < wled->max_strings; i++) {
David Collins8885f792017-01-26 14:36:34 -0800564 reg = level & QPNP_WLED_BRIGHT_LSB_MASK;
565 rc = qpnp_wled_write_reg(wled,
566 QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base,
567 wled->strings[i]), reg);
568 if (rc < 0)
569 return rc;
570
571 reg = level >> QPNP_WLED_BRIGHT_MSB_SHIFT;
572 reg = reg & QPNP_WLED_BRIGHT_MSB_MASK;
573 rc = qpnp_wled_write_reg(wled,
574 QPNP_WLED_BRIGHT_MSB_REG(wled->sink_base,
575 wled->strings[i]), reg);
576 if (rc < 0)
577 return rc;
578 }
579
580 rc = qpnp_wled_sync_reg_toggle(wled);
581 if (rc < 0) {
582 dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
583 return rc;
584 }
585
586 return 0;
587}
588
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800589static int qpnp_wled_set_map_level(struct qpnp_wled *wled, int level)
590{
591 int rc, i;
592
593 if (level < wled->prev_level) {
594 for (i = wled->prev_level; i >= level; i--) {
595 rc = qpnp_wled_set_level(wled, wled->brt_map_table[i]);
596 if (rc < 0) {
597 pr_err("set brightness level failed, rc:%d\n",
598 rc);
599 return rc;
600 }
601 }
602 } else if (level > wled->prev_level) {
603 for (i = wled->prev_level; i <= level; i++) {
604 rc = qpnp_wled_set_level(wled, wled->brt_map_table[i]);
605 if (rc < 0) {
606 pr_err("set brightness level failed, rc:%d\n",
607 rc);
608 return rc;
609 }
610 }
611 }
612
613 return 0;
614}
615
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800616static int qpnp_wled_psm_config(struct qpnp_wled *wled, bool enable)
617{
618 int rc;
619
620 if (!wled->lcd_psm_ctrl)
621 return 0;
622
623 rc = qpnp_wled_masked_write_reg(wled,
624 QPNP_WLED_EN_PSM_REG(wled->ctrl_base),
625 QPNP_WLED_EN_PSM_BIT,
626 enable ? QPNP_WLED_EN_PSM_BIT : 0);
627 if (rc < 0)
628 return rc;
629
630 rc = qpnp_wled_masked_write_reg(wled,
631 QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base),
632 QPNP_WLED_PSM_OVERWRITE_BIT,
633 enable ? QPNP_WLED_PSM_OVERWRITE_BIT : 0);
634 if (rc < 0)
635 return rc;
636
637 return 0;
638}
639
David Collins8885f792017-01-26 14:36:34 -0800640static int qpnp_wled_module_en(struct qpnp_wled *wled,
641 u16 base_addr, bool state)
642{
643 int rc;
644
Subbaraman Narayanamurthy15624d92017-10-12 21:04:30 -0700645 if (wled->module_dis_perm)
646 return 0;
647
David Collins8885f792017-01-26 14:36:34 -0800648 rc = qpnp_wled_masked_write_reg(wled,
649 QPNP_WLED_MODULE_EN_REG(base_addr),
650 QPNP_WLED_MODULE_EN_MASK,
651 state << QPNP_WLED_MODULE_EN_SHIFT);
652 if (rc < 0)
653 return rc;
654
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800655 /*
656 * Wait for at least 10ms before enabling OVP fault interrupt after
657 * enabling the module so that soft start is completed. Also, this
658 * delay can be used to control PSM during enable when required. Keep
659 * OVP interrupt disabled when the module is disabled.
660 */
661 if (state) {
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530662 usleep_range(QPNP_WLED_SOFT_START_DLY_US,
663 QPNP_WLED_SOFT_START_DLY_US + 1000);
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800664 rc = qpnp_wled_psm_config(wled, false);
665 if (rc < 0)
666 return rc;
667
668 if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) {
David Collins8885f792017-01-26 14:36:34 -0800669 enable_irq(wled->ovp_irq);
670 wled->ovp_irq_disabled = false;
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800671 }
672 } else {
673 if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) {
David Collins8885f792017-01-26 14:36:34 -0800674 disable_irq(wled->ovp_irq);
675 wled->ovp_irq_disabled = true;
676 }
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800677
678 rc = qpnp_wled_psm_config(wled, true);
679 if (rc < 0)
680 return rc;
David Collins8885f792017-01-26 14:36:34 -0800681 }
682
683 return 0;
684}
685
686/* sysfs store function for ramp */
687static ssize_t qpnp_wled_ramp_store(struct device *dev,
688 struct device_attribute *attr, const char *buf, size_t count)
689{
690 struct qpnp_wled *wled = dev_get_drvdata(dev);
691 int i, rc;
692
693 mutex_lock(&wled->lock);
694
695 if (!wled->cdev.brightness) {
696 rc = qpnp_wled_module_en(wled, wled->ctrl_base, true);
697 if (rc) {
698 dev_err(&wled->pdev->dev, "wled enable failed\n");
699 goto unlock_mutex;
700 }
701 }
702
703 /* ramp up */
704 for (i = 0; i <= wled->cdev.max_brightness;) {
705 rc = qpnp_wled_set_level(wled, i);
706 if (rc) {
707 dev_err(&wled->pdev->dev, "wled set level failed\n");
708 goto restore_brightness;
709 }
710
711 if (wled->ramp_ms < QPNP_WLED_MIN_MSLEEP)
712 usleep_range(wled->ramp_ms * USEC_PER_MSEC,
713 wled->ramp_ms * USEC_PER_MSEC);
714 else
715 msleep(wled->ramp_ms);
716
717 if (i == wled->cdev.max_brightness)
718 break;
719
720 i += wled->ramp_step;
721 if (i > wled->cdev.max_brightness)
722 i = wled->cdev.max_brightness;
723 }
724
725 /* ramp down */
726 for (i = wled->cdev.max_brightness; i >= 0;) {
727 rc = qpnp_wled_set_level(wled, i);
728 if (rc) {
729 dev_err(&wled->pdev->dev, "wled set level failed\n");
730 goto restore_brightness;
731 }
732
733 if (wled->ramp_ms < QPNP_WLED_MIN_MSLEEP)
734 usleep_range(wled->ramp_ms * USEC_PER_MSEC,
735 wled->ramp_ms * USEC_PER_MSEC);
736 else
737 msleep(wled->ramp_ms);
738
739 if (i == 0)
740 break;
741
742 i -= wled->ramp_step;
743 if (i < 0)
744 i = 0;
745 }
746
747 dev_info(&wled->pdev->dev, "wled ramp complete\n");
748
749restore_brightness:
750 /* restore the old brightness */
751 qpnp_wled_set_level(wled, wled->cdev.brightness);
752 if (!wled->cdev.brightness) {
753 rc = qpnp_wled_module_en(wled, wled->ctrl_base, false);
754 if (rc)
755 dev_err(&wled->pdev->dev, "wled enable failed\n");
756 }
757unlock_mutex:
758 mutex_unlock(&wled->lock);
759
760 return count;
761}
762
763static int qpnp_wled_dump_regs(struct qpnp_wled *wled, u16 base_addr,
764 u8 dbg_regs[], u8 size, char *label,
765 int count, char *buf)
766{
767 int i, rc;
768 u8 reg;
769
770 for (i = 0; i < size; i++) {
771 rc = qpnp_wled_read_reg(wled, base_addr + dbg_regs[i], &reg);
772 if (rc < 0)
773 return rc;
774
775 count += snprintf(buf + count, PAGE_SIZE - count,
776 "%s: REG_0x%x = 0x%x\n", label,
777 base_addr + dbg_regs[i], reg);
778
779 if (count >= PAGE_SIZE)
780 return PAGE_SIZE - 1;
781 }
782
783 return count;
784}
785
786/* sysfs show function for debug registers */
787static ssize_t qpnp_wled_dump_regs_show(struct device *dev,
788 struct device_attribute *attr, char *buf)
789{
790 struct qpnp_wled *wled = dev_get_drvdata(dev);
791 int count = 0;
792
793 count = qpnp_wled_dump_regs(wled, wled->ctrl_base,
794 qpnp_wled_ctrl_dbg_regs,
795 ARRAY_SIZE(qpnp_wled_ctrl_dbg_regs),
796 "wled_ctrl", count, buf);
797
798 if (count < 0 || count == PAGE_SIZE - 1)
799 return count;
800
801 count = qpnp_wled_dump_regs(wled, wled->sink_base,
802 qpnp_wled_sink_dbg_regs,
803 ARRAY_SIZE(qpnp_wled_sink_dbg_regs),
804 "wled_sink", count, buf);
805
806 if (count < 0 || count == PAGE_SIZE - 1)
807 return count;
808
809 return count;
810}
811
812/* sysfs show function for ramp delay in each step */
813static ssize_t qpnp_wled_ramp_ms_show(struct device *dev,
814 struct device_attribute *attr, char *buf)
815{
816 struct qpnp_wled *wled = dev_get_drvdata(dev);
817
818 return snprintf(buf, PAGE_SIZE, "%d\n", wled->ramp_ms);
819}
820
821/* sysfs store function for ramp delay in each step */
822static ssize_t qpnp_wled_ramp_ms_store(struct device *dev,
823 struct device_attribute *attr, const char *buf, size_t count)
824{
825 struct qpnp_wled *wled = dev_get_drvdata(dev);
826 int data, rc;
827
828 rc = kstrtoint(buf, 10, &data);
829 if (rc)
830 return rc;
831
832 wled->ramp_ms = data;
833 return count;
834}
835
836/* sysfs show function for ramp step */
837static ssize_t qpnp_wled_ramp_step_show(struct device *dev,
838 struct device_attribute *attr, char *buf)
839{
840 struct qpnp_wled *wled = dev_get_drvdata(dev);
841
842 return snprintf(buf, PAGE_SIZE, "%d\n", wled->ramp_step);
843}
844
845/* sysfs store function for ramp step */
846static ssize_t qpnp_wled_ramp_step_store(struct device *dev,
847 struct device_attribute *attr, const char *buf, size_t count)
848{
849 struct qpnp_wled *wled = dev_get_drvdata(dev);
850 int data, rc;
851
852 rc = kstrtoint(buf, 10, &data);
853 if (rc)
854 return rc;
855
856 wled->ramp_step = data;
857 return count;
858}
859
860/* sysfs show function for dim mode */
861static ssize_t qpnp_wled_dim_mode_show(struct device *dev,
862 struct device_attribute *attr, char *buf)
863{
864 struct qpnp_wled *wled = dev_get_drvdata(dev);
865 char *str;
866
867 if (wled->dim_mode == QPNP_WLED_DIM_ANALOG)
868 str = "analog";
869 else if (wled->dim_mode == QPNP_WLED_DIM_DIGITAL)
870 str = "digital";
871 else
872 str = "hybrid";
873
874 return snprintf(buf, PAGE_SIZE, "%s\n", str);
875}
876
877/* sysfs store function for dim mode*/
878static ssize_t qpnp_wled_dim_mode_store(struct device *dev,
879 struct device_attribute *attr, const char *buf, size_t count)
880{
881 struct qpnp_wled *wled = dev_get_drvdata(dev);
882 char str[QPNP_WLED_STR_SIZE + 1];
883 int rc, temp;
884 u8 reg;
885
886 if (snprintf(str, QPNP_WLED_STR_SIZE, "%s", buf) > QPNP_WLED_STR_SIZE)
887 return -EINVAL;
888
889 if (strcmp(str, "analog") == 0)
890 temp = QPNP_WLED_DIM_ANALOG;
891 else if (strcmp(str, "digital") == 0)
892 temp = QPNP_WLED_DIM_DIGITAL;
893 else
894 temp = QPNP_WLED_DIM_HYBRID;
895
896 if (temp == wled->dim_mode)
897 return count;
898
899 rc = qpnp_wled_read_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), &reg);
900 if (rc < 0)
901 return rc;
902
903 if (temp == QPNP_WLED_DIM_HYBRID) {
904 reg &= QPNP_WLED_DIM_HYB_MASK;
905 reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
906 } else {
907 reg &= QPNP_WLED_DIM_HYB_MASK;
908 reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
909 reg &= QPNP_WLED_DIM_ANA_MASK;
910 reg |= temp;
911 }
912
913 rc = qpnp_wled_write_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), reg);
914 if (rc)
915 return rc;
916
917 wled->dim_mode = temp;
918
919 return count;
920}
921
922/* sysfs show function for full scale current in ua*/
923static ssize_t qpnp_wled_fs_curr_ua_show(struct device *dev,
924 struct device_attribute *attr, char *buf)
925{
926 struct qpnp_wled *wled = dev_get_drvdata(dev);
927
928 return snprintf(buf, PAGE_SIZE, "%d\n", wled->fs_curr_ua);
929}
930
931/* sysfs store function for full scale current in ua*/
932static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev,
933 struct device_attribute *attr, const char *buf, size_t count)
934{
935 struct qpnp_wled *wled = dev_get_drvdata(dev);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530936 int data, i, rc;
David Collins8885f792017-01-26 14:36:34 -0800937 u8 reg;
938
939 rc = kstrtoint(buf, 10, &data);
940 if (rc)
941 return rc;
942
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530943 for (i = 0; i < wled->max_strings; i++) {
David Collins8885f792017-01-26 14:36:34 -0800944 if (data < QPNP_WLED_FS_CURR_MIN_UA)
945 data = QPNP_WLED_FS_CURR_MIN_UA;
946 else if (data > QPNP_WLED_FS_CURR_MAX_UA)
947 data = QPNP_WLED_FS_CURR_MAX_UA;
948
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530949 reg = data / QPNP_WLED_FS_CURR_STEP_UA;
950 rc = qpnp_wled_masked_write_reg(wled,
951 QPNP_WLED_FS_CURR_REG(wled->sink_base, i),
952 QPNP_WLED_FS_CURR_MASK, reg);
David Collins8885f792017-01-26 14:36:34 -0800953 if (rc < 0)
954 return rc;
David Collins8885f792017-01-26 14:36:34 -0800955 }
956
957 wled->fs_curr_ua = data;
958
959 rc = qpnp_wled_sync_reg_toggle(wled);
960 if (rc < 0) {
961 dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
962 return rc;
963 }
964
965 return count;
966}
967
968/* sysfs attributes exported by wled */
969static struct device_attribute qpnp_wled_attrs[] = {
970 __ATTR(dump_regs, 0664, qpnp_wled_dump_regs_show, NULL),
971 __ATTR(dim_mode, 0664, qpnp_wled_dim_mode_show,
972 qpnp_wled_dim_mode_store),
973 __ATTR(fs_curr_ua, 0664, qpnp_wled_fs_curr_ua_show,
974 qpnp_wled_fs_curr_ua_store),
975 __ATTR(start_ramp, 0664, NULL, qpnp_wled_ramp_store),
976 __ATTR(ramp_ms, 0664, qpnp_wled_ramp_ms_show, qpnp_wled_ramp_ms_store),
977 __ATTR(ramp_step, 0664, qpnp_wled_ramp_step_show,
978 qpnp_wled_ramp_step_store),
979};
980
981/* worker for setting wled brightness */
982static void qpnp_wled_work(struct work_struct *work)
983{
984 struct qpnp_wled *wled;
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800985 int level, level_255, rc;
David Collins8885f792017-01-26 14:36:34 -0800986
987 wled = container_of(work, struct qpnp_wled, work);
988
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800989 mutex_lock(&wled->lock);
David Collins8885f792017-01-26 14:36:34 -0800990 level = wled->cdev.brightness;
991
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800992 if (wled->brt_map_table) {
993 /*
994 * Change the 12 bit level to 8 bit level and use the mapped
995 * values for 12 bit level from brightness map table.
996 */
997 level_255 = DIV_ROUND_CLOSEST(level, 16);
998 if (level_255 > 255)
999 level_255 = 255;
David Collins8885f792017-01-26 14:36:34 -08001000
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08001001 pr_debug("level: %d level_255: %d\n", level, level_255);
1002 rc = qpnp_wled_set_map_level(wled, level_255);
1003 if (rc) {
1004 dev_err(&wled->pdev->dev, "wled set level failed\n");
1005 goto unlock_mutex;
1006 }
1007 wled->prev_level = level_255;
1008 } else if (level) {
David Collins8885f792017-01-26 14:36:34 -08001009 rc = qpnp_wled_set_level(wled, level);
1010 if (rc) {
1011 dev_err(&wled->pdev->dev, "wled set level failed\n");
1012 goto unlock_mutex;
1013 }
1014 }
1015
1016 if (!!level != wled->prev_state) {
1017 if (!!level) {
1018 /*
1019 * For AMOLED display in pmi8998, SWIRE_AVDD_DEFAULT has
1020 * to be reconfigured every time the module is enabled.
1021 */
1022 rc = qpnp_wled_swire_avdd_config(wled);
1023 if (rc < 0) {
1024 pr_err("Write to SWIRE_AVDD_DEFAULT register failed rc:%d\n",
1025 rc);
1026 goto unlock_mutex;
1027 }
1028 }
1029
1030 rc = qpnp_wled_module_en(wled, wled->ctrl_base, !!level);
1031 if (rc) {
1032 dev_err(&wled->pdev->dev, "wled %sable failed\n",
1033 level ? "en" : "dis");
1034 goto unlock_mutex;
1035 }
1036 }
1037
1038 wled->prev_state = !!level;
1039unlock_mutex:
1040 mutex_unlock(&wled->lock);
1041}
1042
1043/* get api registered with led classdev for wled brightness */
1044static enum led_brightness qpnp_wled_get(struct led_classdev *led_cdev)
1045{
1046 struct qpnp_wled *wled;
1047
1048 wled = container_of(led_cdev, struct qpnp_wled, cdev);
1049
1050 return wled->cdev.brightness;
1051}
1052
1053/* set api registered with led classdev for wled brightness */
1054static void qpnp_wled_set(struct led_classdev *led_cdev,
1055 enum led_brightness level)
1056{
1057 struct qpnp_wled *wled;
1058
1059 wled = container_of(led_cdev, struct qpnp_wled, cdev);
1060
1061 if (level < LED_OFF)
1062 level = LED_OFF;
1063 else if (level > wled->cdev.max_brightness)
1064 level = wled->cdev.max_brightness;
1065
1066 wled->cdev.brightness = level;
1067 schedule_work(&wled->work);
1068}
1069
1070static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
1071{
1072 int rc;
1073 u8 reg;
1074
1075 /* display type */
1076 rc = qpnp_wled_read_reg(wled, QPNP_WLED_DISP_SEL_REG(base_addr), &reg);
1077 if (rc < 0)
1078 return rc;
1079
1080 reg &= QPNP_WLED_DISP_SEL_MASK;
1081 reg |= (wled->disp_type_amoled << QPNP_WLED_DISP_SEL_SHIFT);
1082
1083 rc = qpnp_wled_sec_write_reg(wled, QPNP_WLED_DISP_SEL_REG(base_addr),
1084 reg);
1085 if (rc)
1086 return rc;
1087
1088 if (wled->disp_type_amoled) {
1089 /* Configure the PSM CTRL register for AMOLED */
1090 if (wled->vref_psm_mv < QPNP_WLED_VREF_PSM_MIN_MV)
1091 wled->vref_psm_mv = QPNP_WLED_VREF_PSM_MIN_MV;
1092 else if (wled->vref_psm_mv > QPNP_WLED_VREF_PSM_MAX_MV)
1093 wled->vref_psm_mv = QPNP_WLED_VREF_PSM_MAX_MV;
1094
1095 rc = qpnp_wled_read_reg(wled,
1096 QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), &reg);
1097 if (rc < 0)
1098 return rc;
1099
1100 reg &= QPNP_WLED_VREF_PSM_MASK;
1101 reg |= ((wled->vref_psm_mv - QPNP_WLED_VREF_PSM_MIN_MV)/
1102 QPNP_WLED_VREF_PSM_STEP_MV);
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -08001103 reg |= QPNP_WLED_PSM_OVERWRITE_BIT;
David Collins8885f792017-01-26 14:36:34 -08001104 rc = qpnp_wled_write_reg(wled,
1105 QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), reg);
1106 if (rc)
1107 return rc;
1108
1109 /* Configure the VLOOP COMP RES register for AMOLED */
1110 if (wled->loop_comp_res_kohm < QPNP_WLED_LOOP_COMP_RES_MIN_KOHM)
1111 wled->loop_comp_res_kohm =
1112 QPNP_WLED_LOOP_COMP_RES_MIN_KOHM;
1113 else if (wled->loop_comp_res_kohm >
1114 QPNP_WLED_LOOP_COMP_RES_MAX_KOHM)
1115 wled->loop_comp_res_kohm =
1116 QPNP_WLED_LOOP_COMP_RES_MAX_KOHM;
1117
1118 rc = qpnp_wled_read_reg(wled,
1119 QPNP_WLED_VLOOP_COMP_RES_REG(wled->ctrl_base),
1120 &reg);
1121 if (rc < 0)
1122 return rc;
1123
1124 reg &= QPNP_WLED_VLOOP_COMP_RES_MASK;
1125 reg |= ((wled->loop_comp_res_kohm -
1126 QPNP_WLED_LOOP_COMP_RES_MIN_KOHM)/
1127 QPNP_WLED_LOOP_COMP_RES_STEP_KOHM);
1128 reg |= QPNP_WLED_VLOOP_COMP_RES_OVERWRITE;
1129 rc = qpnp_wled_write_reg(wled,
1130 QPNP_WLED_VLOOP_COMP_RES_REG(wled->ctrl_base),
1131 reg);
1132 if (rc)
1133 return rc;
1134
1135 /* Configure the CTRL TEST4 register for AMOLED */
1136 rc = qpnp_wled_read_reg(wled,
1137 QPNP_WLED_TEST4_REG(wled->ctrl_base), &reg);
1138 if (rc < 0)
1139 return rc;
1140
1141 reg |= QPNP_WLED_TEST4_EN_IIND_UP;
1142 rc = qpnp_wled_sec_write_reg(wled,
1143 QPNP_WLED_TEST4_REG(base_addr), reg);
1144 if (rc)
1145 return rc;
1146 } else {
1147 /*
1148 * enable VREF_UP to avoid false ovp on low brightness for LCD
1149 */
1150 reg = QPNP_WLED_TEST4_EN_VREF_UP
1151 | QPNP_WLED_TEST4_EN_DEB_BYPASS_ILIM_BIT;
1152 rc = qpnp_wled_sec_write_reg(wled,
1153 QPNP_WLED_TEST4_REG(base_addr), reg);
1154 if (rc)
1155 return rc;
1156 }
1157
1158 return 0;
1159}
1160
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001161#define AUTO_CALIB_BRIGHTNESS 200
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301162static int wled_auto_calibrate(struct qpnp_wled *wled)
1163{
1164 int rc = 0, i;
1165 u8 reg = 0, sink_config = 0, sink_test = 0, sink_valid = 0, int_sts;
1166
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301167 /* read configured sink configuration */
1168 rc = qpnp_wled_read_reg(wled,
1169 QPNP_WLED_CURR_SINK_REG(wled->sink_base), &sink_config);
1170 if (rc < 0) {
1171 pr_err("Failed to read SINK configuration rc=%d\n", rc);
1172 goto failed_calib;
1173 }
1174
1175 /* disable the module before starting calibration */
1176 rc = qpnp_wled_masked_write_reg(wled,
1177 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
1178 QPNP_WLED_MODULE_EN_MASK, 0);
1179 if (rc < 0) {
1180 pr_err("Failed to disable WLED module rc=%d\n", rc);
1181 goto failed_calib;
1182 }
1183
1184 /* set low brightness across all sinks */
1185 rc = qpnp_wled_set_level(wled, AUTO_CALIB_BRIGHTNESS);
1186 if (rc < 0) {
1187 pr_err("Failed to set brightness for calibration rc=%d\n", rc);
1188 goto failed_calib;
1189 }
1190
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001191 if (wled->en_cabc) {
1192 for (i = 0; i < wled->max_strings; i++) {
1193 reg = 0;
1194 rc = qpnp_wled_masked_write_reg(wled,
1195 QPNP_WLED_CABC_REG(wled->sink_base, i),
1196 QPNP_WLED_CABC_MASK, reg);
1197 if (rc < 0)
1198 goto failed_calib;
1199 }
1200 }
1201
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301202 /* disable all sinks */
1203 rc = qpnp_wled_write_reg(wled,
1204 QPNP_WLED_CURR_SINK_REG(wled->sink_base), 0);
1205 if (rc < 0) {
1206 pr_err("Failed to disable all sinks rc=%d\n", rc);
1207 goto failed_calib;
1208 }
1209
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301210 /* iterate through the strings one by one */
1211 for (i = 0; i < wled->max_strings; i++) {
1212 sink_test = 1 << (QPNP_WLED_CURR_SINK_SHIFT + i);
1213
1214 /* Enable feedback control */
1215 rc = qpnp_wled_write_reg(wled,
1216 QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
1217 i + 1);
1218 if (rc < 0) {
1219 pr_err("Failed to enable feedback for SINK %d rc = %d\n",
1220 i + 1, rc);
1221 goto failed_calib;
1222 }
1223
1224 /* enable the sink */
1225 rc = qpnp_wled_write_reg(wled,
1226 QPNP_WLED_CURR_SINK_REG(wled->sink_base), sink_test);
1227 if (rc < 0) {
1228 pr_err("Failed to configure SINK %d rc=%d\n",
1229 i + 1, rc);
1230 goto failed_calib;
1231 }
1232
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001233 /* Enable the module */
1234 rc = qpnp_wled_masked_write_reg(wled,
1235 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
1236 QPNP_WLED_MODULE_EN_MASK, QPNP_WLED_MODULE_EN_MASK);
1237 if (rc < 0) {
1238 pr_err("Failed to enable WLED module rc=%d\n", rc);
1239 goto failed_calib;
1240 }
1241
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301242 /* delay for WLED soft-start */
1243 usleep_range(QPNP_WLED_SOFT_START_DLY_US,
1244 QPNP_WLED_SOFT_START_DLY_US + 1000);
1245
1246 rc = qpnp_wled_read_reg(wled,
1247 QPNP_WLED_INT_RT_STS(wled->ctrl_base), &int_sts);
1248 if (rc < 0) {
1249 pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc);
1250 goto failed_calib;
1251 }
1252
1253 if (int_sts & QPNP_WLED_OVP_FAULT_BIT)
1254 pr_debug("WLED OVP fault detected with SINK %d\n",
1255 i + 1);
1256 else
1257 sink_valid |= sink_test;
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001258
1259 /* Disable the module */
1260 rc = qpnp_wled_masked_write_reg(wled,
1261 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
1262 QPNP_WLED_MODULE_EN_MASK, 0);
1263 if (rc < 0) {
1264 pr_err("Failed to disable WLED module rc=%d\n", rc);
1265 goto failed_calib;
1266 }
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301267 }
1268
1269 if (sink_valid == sink_config) {
1270 pr_debug("WLED auto-calibration complete, default sink-config=%x OK!\n",
1271 sink_config);
1272 } else {
1273 pr_warn("Invalid WLED default sink config=%x changing it to=%x\n",
1274 sink_config, sink_valid);
1275 sink_config = sink_valid;
1276 }
1277
1278 if (!sink_config) {
1279 pr_warn("No valid WLED sinks found\n");
Subbaraman Narayanamurthy15624d92017-10-12 21:04:30 -07001280 wled->module_dis_perm = true;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301281 goto failed_calib;
1282 }
1283
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301284 /* write the new sink configuration */
1285 rc = qpnp_wled_write_reg(wled,
1286 QPNP_WLED_CURR_SINK_REG(wled->sink_base), sink_config);
1287 if (rc < 0) {
1288 pr_err("Failed to reconfigure the default sink rc=%d\n", rc);
1289 goto failed_calib;
1290 }
1291
1292 /* MODULATOR_EN setting for valid sinks */
1293 for (i = 0; i < wled->max_strings; i++) {
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001294 if (wled->en_cabc) {
1295 reg = 1 << QPNP_WLED_CABC_SHIFT;
1296 rc = qpnp_wled_masked_write_reg(wled,
1297 QPNP_WLED_CABC_REG(wled->sink_base, i),
1298 QPNP_WLED_CABC_MASK, reg);
1299 if (rc < 0)
1300 goto failed_calib;
1301 }
1302
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301303 if (sink_config & (1 << (QPNP_WLED_CURR_SINK_SHIFT + i)))
1304 reg = (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
1305 else
1306 reg = 0x0; /* disable modulator_en for unused sink */
1307
1308 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID)
1309 reg &= QPNP_WLED_GATE_DRV_MASK;
1310 else
1311 reg |= ~QPNP_WLED_GATE_DRV_MASK;
1312
1313 rc = qpnp_wled_write_reg(wled,
1314 QPNP_WLED_MOD_EN_REG(wled->sink_base, i), reg);
1315 if (rc < 0) {
1316 pr_err("Failed to configure MODULATOR_EN rc=%d\n", rc);
1317 goto failed_calib;
1318 }
1319 }
1320
1321 /* restore the feedback setting */
1322 rc = qpnp_wled_write_reg(wled,
1323 QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
1324 wled->fdbk_op);
1325 if (rc < 0) {
1326 pr_err("Failed to restore feedback setting rc=%d\n", rc);
1327 goto failed_calib;
1328 }
1329
1330 /* restore brightness */
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05301331 rc = qpnp_wled_set_level(wled, !wled->cdev.brightness ?
1332 AUTO_CALIB_BRIGHTNESS : wled->cdev.brightness);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301333 if (rc < 0) {
1334 pr_err("Failed to set brightness after calibration rc=%d\n",
1335 rc);
1336 goto failed_calib;
1337 }
1338
1339 rc = qpnp_wled_masked_write_reg(wled,
1340 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
1341 QPNP_WLED_MODULE_EN_MASK,
1342 QPNP_WLED_MODULE_EN_MASK);
1343 if (rc < 0) {
1344 pr_err("Failed to enable WLED module rc=%d\n", rc);
1345 goto failed_calib;
1346 }
1347
1348 /* delay for WLED soft-start */
1349 usleep_range(QPNP_WLED_SOFT_START_DLY_US,
1350 QPNP_WLED_SOFT_START_DLY_US + 1000);
1351
1352failed_calib:
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301353 return rc;
1354}
1355
1356#define WLED_AUTO_CAL_OVP_COUNT 5
1357#define WLED_AUTO_CAL_CNT_DLY_US 1000000 /* 1 second */
1358static bool qpnp_wled_auto_cal_required(struct qpnp_wled *wled)
1359{
1360 s64 elapsed_time_us;
1361
1362 /*
1363 * Check if the OVP fault was an occasional one
1364 * or if its firing continuously, the latter qualifies
1365 * for an auto-calibration check.
1366 */
1367 if (!wled->auto_calibration_ovp_count) {
1368 wled->start_ovp_fault_time = ktime_get();
1369 wled->auto_calibration_ovp_count++;
1370 } else {
1371 elapsed_time_us = ktime_us_delta(ktime_get(),
1372 wled->start_ovp_fault_time);
1373 if (elapsed_time_us > WLED_AUTO_CAL_CNT_DLY_US)
1374 wled->auto_calibration_ovp_count = 0;
1375 else
1376 wled->auto_calibration_ovp_count++;
1377
1378 if (wled->auto_calibration_ovp_count >=
1379 WLED_AUTO_CAL_OVP_COUNT) {
1380 wled->auto_calibration_ovp_count = 0;
1381 return true;
1382 }
1383 }
1384
1385 return false;
1386}
1387
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05301388static int qpnp_wled_auto_calibrate_at_init(struct qpnp_wled *wled)
1389{
1390 int rc;
1391 u8 fault_status = 0, rt_status = 0;
1392
1393 if (!wled->auto_calib_enabled)
1394 return 0;
1395
1396 rc = qpnp_wled_read_reg(wled,
1397 QPNP_WLED_INT_RT_STS(wled->ctrl_base), &rt_status);
1398 if (rc < 0)
1399 pr_err("Failed to read RT status rc=%d\n", rc);
1400
1401 rc = qpnp_wled_read_reg(wled,
1402 QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_status);
1403 if (rc < 0)
1404 pr_err("Failed to read fault status rc=%d\n", rc);
1405
1406 if ((rt_status & QPNP_WLED_OVP_FLT_RT_STS_BIT) ||
1407 (fault_status & QPNP_WLED_OVP_FAULT_BIT)) {
1408 mutex_lock(&wled->lock);
1409 rc = wled_auto_calibrate(wled);
1410 if (rc < 0)
1411 pr_err("Failed auto-calibration rc=%d\n", rc);
1412 else
1413 wled->auto_calib_done = true;
1414 mutex_unlock(&wled->lock);
1415 }
1416
1417 return rc;
1418}
1419
David Collins8885f792017-01-26 14:36:34 -08001420/* ovp irq handler */
1421static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled)
1422{
1423 struct qpnp_wled *wled = _wled;
1424 int rc;
Subbaraman Narayanamurthyb5ae3182017-02-17 15:14:06 -08001425 u8 fault_sts, int_sts;
David Collins8885f792017-01-26 14:36:34 -08001426
1427 rc = qpnp_wled_read_reg(wled,
Subbaraman Narayanamurthyb5ae3182017-02-17 15:14:06 -08001428 QPNP_WLED_INT_RT_STS(wled->ctrl_base), &int_sts);
1429 if (rc < 0) {
1430 pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc);
1431 return IRQ_HANDLED;
1432 }
1433
1434 rc = qpnp_wled_read_reg(wled,
1435 QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_sts);
David Collins8885f792017-01-26 14:36:34 -08001436 if (rc < 0) {
1437 pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc);
1438 return IRQ_HANDLED;
1439 }
1440
Subbaraman Narayanamurthyb5ae3182017-02-17 15:14:06 -08001441 if (fault_sts & (QPNP_WLED_OVP_FAULT_BIT | QPNP_WLED_ILIM_FAULT_BIT))
1442 pr_err("WLED OVP fault detected, int_sts=%x fault_sts= %x\n",
1443 int_sts, fault_sts);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301444
1445 if (fault_sts & QPNP_WLED_OVP_FAULT_BIT) {
1446 if (wled->auto_calib_enabled && !wled->auto_calib_done) {
1447 if (qpnp_wled_auto_cal_required(wled)) {
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05301448 mutex_lock(&wled->lock);
1449 if (wled->ovp_irq > 0 &&
1450 !wled->ovp_irq_disabled) {
1451 disable_irq_nosync(wled->ovp_irq);
1452 wled->ovp_irq_disabled = true;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301453 }
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05301454
1455 rc = wled_auto_calibrate(wled);
1456 if (rc < 0)
1457 pr_err("Failed auto-calibration rc=%d\n",
1458 rc);
1459 else
1460 wled->auto_calib_done = true;
1461
1462 if (wled->ovp_irq > 0 &&
1463 wled->ovp_irq_disabled) {
1464 enable_irq(wled->ovp_irq);
1465 wled->ovp_irq_disabled = false;
1466 }
1467 mutex_unlock(&wled->lock);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301468 }
1469 }
1470 }
1471
David Collins8885f792017-01-26 14:36:34 -08001472 return IRQ_HANDLED;
1473}
1474
1475/* short circuit irq handler */
1476static irqreturn_t qpnp_wled_sc_irq_handler(int irq, void *_wled)
1477{
1478 struct qpnp_wled *wled = _wled;
1479 int rc;
1480 u8 val;
1481
1482 rc = qpnp_wled_read_reg(wled,
1483 QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &val);
1484 if (rc < 0) {
1485 pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc);
1486 return IRQ_HANDLED;
1487 }
1488
1489 pr_err("WLED short circuit detected %d times fault_status=%x\n",
1490 ++wled->sc_cnt, val);
1491 mutex_lock(&wled->lock);
1492 qpnp_wled_module_en(wled, wled->ctrl_base, false);
1493 msleep(QPNP_WLED_SC_DLY_MS);
1494 qpnp_wled_module_en(wled, wled->ctrl_base, true);
1495 mutex_unlock(&wled->lock);
1496
1497 return IRQ_HANDLED;
1498}
1499
1500static bool is_avdd_trim_adjustment_required(struct qpnp_wled *wled)
1501{
1502 int rc;
1503 u8 reg = 0;
1504
1505 /*
1506 * AVDD trim adjustment is not required for pmi8998/pm660l and not
1507 * supported for pmi8994.
1508 */
1509 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1510 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE ||
1511 wled->pmic_rev_id->pmic_subtype == PMI8994_SUBTYPE)
1512 return false;
1513
1514 /*
1515 * Configure TRIM_REG only if disp_type_amoled and it has
1516 * not already been programmed by bootloader.
1517 */
1518 if (!wled->disp_type_amoled)
1519 return false;
1520
1521 rc = qpnp_wled_read_reg(wled,
1522 QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base), &reg);
1523 if (rc < 0)
1524 return false;
1525
1526 return !(reg & QPNP_WLED_AVDD_SET_BIT);
1527}
1528
1529static int qpnp_wled_gm_config(struct qpnp_wled *wled)
1530{
1531 int rc;
1532 u8 mask = 0, reg = 0;
1533
1534 /* Configure the LOOP COMP GM register */
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07001535 if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1536 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)) {
1537 if (wled->disp_type_amoled) {
1538 reg = 0;
1539 mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
1540 QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
1541 } else {
1542 if (wled->loop_auto_gm_en)
1543 reg |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN;
David Collins8885f792017-01-26 14:36:34 -08001544
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07001545 if (wled->loop_auto_gm_thresh >
1546 QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX)
1547 wled->loop_auto_gm_thresh =
1548 QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX;
David Collins8885f792017-01-26 14:36:34 -08001549
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07001550 reg |= wled->loop_auto_gm_thresh <<
1551 QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT;
1552 mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
1553 QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
1554 }
David Collins8885f792017-01-26 14:36:34 -08001555 }
1556
1557 if (wled->loop_ea_gm < QPNP_WLED_LOOP_EA_GM_MIN)
1558 wled->loop_ea_gm = QPNP_WLED_LOOP_EA_GM_MIN;
1559 else if (wled->loop_ea_gm > QPNP_WLED_LOOP_EA_GM_MAX)
1560 wled->loop_ea_gm = QPNP_WLED_LOOP_EA_GM_MAX;
1561
1562 reg |= wled->loop_ea_gm | QPNP_WLED_VLOOP_COMP_GM_OVERWRITE;
1563 mask |= QPNP_WLED_VLOOP_COMP_GM_MASK |
1564 QPNP_WLED_VLOOP_COMP_GM_OVERWRITE;
1565
1566 rc = qpnp_wled_masked_write_reg(wled,
1567 QPNP_WLED_VLOOP_COMP_GM_REG(wled->ctrl_base), mask,
1568 reg);
1569 if (rc)
1570 pr_err("write VLOOP_COMP_GM_REG failed, rc=%d]\n", rc);
1571
1572 return rc;
1573}
1574
1575static int qpnp_wled_ovp_config(struct qpnp_wled *wled)
1576{
1577 int rc, i, *ovp_table;
1578 u8 reg;
1579
1580 /*
1581 * Configure the OVP register based on ovp_mv only if display type is
1582 * not AMOLED.
1583 */
1584 if (wled->disp_type_amoled)
1585 return 0;
1586
1587 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1588 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
1589 ovp_table = qpnp_wled_ovp_thresholds_pmi8998;
1590 else
1591 ovp_table = qpnp_wled_ovp_thresholds_pmi8994;
1592
1593 for (i = 0; i < NUM_SUPPORTED_OVP_THRESHOLDS; i++) {
1594 if (wled->ovp_mv == ovp_table[i])
1595 break;
1596 }
1597
1598 if (i == NUM_SUPPORTED_OVP_THRESHOLDS) {
1599 dev_err(&wled->pdev->dev,
1600 "Invalid ovp threshold specified in device tree\n");
1601 return -EINVAL;
1602 }
1603
1604 reg = i & QPNP_WLED_OVP_MASK;
1605 rc = qpnp_wled_masked_write_reg(wled,
1606 QPNP_WLED_OVP_REG(wled->ctrl_base),
1607 QPNP_WLED_OVP_MASK, reg);
1608 if (rc)
1609 return rc;
1610
1611 return 0;
1612}
1613
1614static int qpnp_wled_avdd_trim_config(struct qpnp_wled *wled)
1615{
1616 int rc, i;
1617 u8 reg;
1618
1619 for (i = 0; i < NUM_SUPPORTED_AVDD_VOLTAGES; i++) {
1620 if (wled->avdd_target_voltage_mv ==
1621 qpnp_wled_avdd_target_voltages[i])
1622 break;
1623 }
1624
1625 if (i == NUM_SUPPORTED_AVDD_VOLTAGES) {
1626 dev_err(&wled->pdev->dev,
1627 "Invalid avdd target voltage specified in device tree\n");
1628 return -EINVAL;
1629 }
1630
1631 /* Update WLED_OVP register based on desired target voltage */
1632 reg = qpnp_wled_ovp_reg_settings[i];
1633 rc = qpnp_wled_masked_write_reg(wled,
1634 QPNP_WLED_OVP_REG(wled->ctrl_base),
1635 QPNP_WLED_OVP_MASK, reg);
1636 if (rc)
1637 return rc;
1638
1639 /* Update WLED_TRIM register based on desired target voltage */
1640 rc = qpnp_wled_read_reg(wled,
1641 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), &reg);
1642 if (rc)
1643 return rc;
1644
1645 reg += qpnp_wled_avdd_trim_adjustments[i];
1646 if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL ||
1647 (s8)reg > QPNP_WLED_AVDD_MAX_TRIM_VAL) {
1648 dev_dbg(&wled->pdev->dev,
1649 "adjusted trim %d is not within range, capping it\n",
1650 (s8)reg);
1651 if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL)
1652 reg = QPNP_WLED_AVDD_MIN_TRIM_VAL;
1653 else
1654 reg = QPNP_WLED_AVDD_MAX_TRIM_VAL;
1655 }
1656
1657 reg &= QPNP_WLED_7P7_TRIM_MASK;
1658 rc = qpnp_wled_sec_write_reg(wled,
1659 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), reg);
1660 if (rc < 0)
1661 dev_err(&wled->pdev->dev, "Write to 7P7_TRIM register failed, rc=%d\n",
1662 rc);
1663 return rc;
1664}
1665
1666static int qpnp_wled_avdd_mode_config(struct qpnp_wled *wled)
1667{
1668 int rc;
1669 u8 reg = 0;
1670
1671 /*
1672 * At present, configuring the mode to SPMI/SWIRE for controlling
1673 * AVDD voltage is available only in pmi8998/pm660l.
1674 */
1675 if (wled->pmic_rev_id->pmic_subtype != PMI8998_SUBTYPE &&
1676 wled->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
1677 return 0;
1678
1679 /* AMOLED_VOUT should be configured for AMOLED */
1680 if (!wled->disp_type_amoled)
1681 return 0;
1682
1683 /* Configure avdd register */
1684 if (wled->avdd_target_voltage_mv > QPNP_WLED_AVDD_MAX_MV) {
1685 dev_dbg(&wled->pdev->dev, "Capping avdd target voltage to %d\n",
1686 QPNP_WLED_AVDD_MAX_MV);
1687 wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MAX_MV;
1688 } else if (wled->avdd_target_voltage_mv < QPNP_WLED_AVDD_MIN_MV) {
1689 dev_info(&wled->pdev->dev, "Capping avdd target voltage to %d\n",
1690 QPNP_WLED_AVDD_MIN_MV);
1691 wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MIN_MV;
1692 }
1693
1694 if (wled->avdd_mode_spmi) {
1695 reg = QPNP_WLED_AVDD_MV_TO_REG(wled->avdd_target_voltage_mv);
1696 reg |= QPNP_WLED_AVDD_SEL_SPMI_BIT;
1697 rc = qpnp_wled_write_reg(wled,
1698 QPNP_WLED_AMOLED_VOUT_REG(wled->ctrl_base),
1699 reg);
1700 if (rc < 0)
1701 pr_err("Write to AMOLED_VOUT register failed, rc=%d\n",
1702 rc);
1703 } else {
1704 rc = qpnp_wled_swire_avdd_config(wled);
1705 if (rc < 0)
1706 pr_err("Write to SWIRE_AVDD_DEFAULT register failed rc:%d\n",
1707 rc);
1708 }
1709
1710 return rc;
1711}
1712
1713static int qpnp_wled_ilim_config(struct qpnp_wled *wled)
1714{
1715 int rc, i, *ilim_table;
1716 u8 reg;
1717
1718 if (wled->ilim_ma < PMI8994_WLED_ILIM_MIN_MA)
1719 wled->ilim_ma = PMI8994_WLED_ILIM_MIN_MA;
1720
1721 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1722 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
1723 ilim_table = qpnp_wled_ilim_settings_pmi8998;
1724 if (wled->ilim_ma > PMI8998_WLED_ILIM_MAX_MA)
1725 wled->ilim_ma = PMI8998_WLED_ILIM_MAX_MA;
1726 } else {
1727 ilim_table = qpnp_wled_ilim_settings_pmi8994;
1728 if (wled->ilim_ma > PMI8994_WLED_ILIM_MAX_MA)
1729 wled->ilim_ma = PMI8994_WLED_ILIM_MAX_MA;
1730 }
1731
1732 for (i = 0; i < NUM_SUPPORTED_ILIM_THRESHOLDS; i++) {
1733 if (wled->ilim_ma == ilim_table[i])
1734 break;
1735 }
1736
1737 if (i == NUM_SUPPORTED_ILIM_THRESHOLDS) {
1738 dev_err(&wled->pdev->dev,
1739 "Invalid ilim threshold specified in device tree\n");
1740 return -EINVAL;
1741 }
1742
1743 reg = (i & QPNP_WLED_ILIM_MASK) | QPNP_WLED_ILIM_OVERWRITE;
1744 rc = qpnp_wled_masked_write_reg(wled,
1745 QPNP_WLED_ILIM_REG(wled->ctrl_base),
1746 QPNP_WLED_ILIM_MASK | QPNP_WLED_ILIM_OVERWRITE, reg);
1747 if (rc < 0)
1748 dev_err(&wled->pdev->dev, "Write to ILIM register failed, rc=%d\n",
1749 rc);
1750 return rc;
1751}
1752
1753static int qpnp_wled_vref_config(struct qpnp_wled *wled)
1754{
1755
1756 struct wled_vref_setting vref_setting;
1757 int rc;
1758 u8 reg = 0;
1759
1760 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1761 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
1762 vref_setting = vref_setting_pmi8998;
1763 else
1764 vref_setting = vref_setting_pmi8994;
1765
1766 if (wled->vref_uv < vref_setting.min_uv)
1767 wled->vref_uv = vref_setting.min_uv;
1768 else if (wled->vref_uv > vref_setting.max_uv)
1769 wled->vref_uv = vref_setting.max_uv;
1770
1771 reg |= DIV_ROUND_CLOSEST(wled->vref_uv - vref_setting.min_uv,
1772 vref_setting.step_uv);
1773
1774 rc = qpnp_wled_masked_write_reg(wled,
1775 QPNP_WLED_VREF_REG(wled->ctrl_base),
1776 QPNP_WLED_VREF_MASK, reg);
1777 if (rc)
1778 pr_err("Write VREF_REG failed, rc=%d\n", rc);
1779
1780 return rc;
1781}
1782
1783/* Configure WLED registers */
1784static int qpnp_wled_config(struct qpnp_wled *wled)
1785{
1786 int rc, i, temp;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301787 u8 reg = 0, sink_en = 0, mask;
David Collins8885f792017-01-26 14:36:34 -08001788
1789 /* Configure display type */
1790 rc = qpnp_wled_set_disp(wled, wled->ctrl_base);
1791 if (rc < 0)
1792 return rc;
1793
1794 /* Configure the FEEDBACK OUTPUT register */
1795 rc = qpnp_wled_read_reg(wled, QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
1796 &reg);
1797 if (rc < 0)
1798 return rc;
1799 reg &= QPNP_WLED_FDBK_OP_MASK;
1800 reg |= wled->fdbk_op;
1801 rc = qpnp_wled_write_reg(wled, QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
1802 reg);
1803 if (rc)
1804 return rc;
1805
1806 /* Configure the VREF register */
1807 rc = qpnp_wled_vref_config(wled);
1808 if (rc < 0) {
1809 pr_err("Error in configuring wled vref, rc=%d\n", rc);
1810 return rc;
1811 }
1812
1813 /* Configure VLOOP_COMP_GM register */
1814 rc = qpnp_wled_gm_config(wled);
1815 if (rc < 0) {
1816 pr_err("Error in configureing wled gm, rc=%d\n", rc);
1817 return rc;
1818 }
1819
1820 /* Configure the ILIM register */
1821 rc = qpnp_wled_ilim_config(wled);
1822 if (rc < 0) {
1823 pr_err("Error in configuring wled ilim, rc=%d\n", rc);
1824 return rc;
1825 }
1826
1827 /* Configure auto PFM mode for LCD mode only */
1828 if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1829 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
1830 && !wled->disp_type_amoled) {
1831 reg = 0;
1832 reg |= wled->lcd_auto_pfm_thresh;
1833 reg |= wled->lcd_auto_pfm_en <<
1834 QPNP_WLED_LCD_AUTO_PFM_EN_SHIFT;
1835 rc = qpnp_wled_masked_write_reg(wled,
1836 QPNP_WLED_LCD_AUTO_PFM_REG(wled->ctrl_base),
1837 QPNP_WLED_LCD_AUTO_PFM_EN_BIT |
1838 QPNP_WLED_LCD_AUTO_PFM_THRESH_MASK, reg);
1839 if (rc < 0) {
1840 pr_err("Write LCD_AUTO_PFM failed, rc=%d\n", rc);
1841 return rc;
1842 }
1843 }
1844
1845 /* Configure the Soft start Ramp delay: for AMOLED - 0,for LCD - 2 */
1846 reg = (wled->disp_type_amoled) ? 0 : 2;
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07001847 mask = SOFTSTART_RAMP_DELAY_MASK;
1848 if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1849 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
1850 && wled->disp_type_amoled) {
1851 reg |= SOFTSTART_OVERWRITE_BIT;
1852 mask |= SOFTSTART_OVERWRITE_BIT;
1853 }
1854
1855 rc = qpnp_wled_masked_write_reg(wled,
1856 QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base),
1857 mask, reg);
David Collins8885f792017-01-26 14:36:34 -08001858 if (rc)
1859 return rc;
1860
1861 /* Configure the MAX BOOST DUTY register */
1862 if (wled->boost_duty_ns < QPNP_WLED_BOOST_DUTY_MIN_NS)
1863 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
1864 else if (wled->boost_duty_ns > QPNP_WLED_BOOST_DUTY_MAX_NS)
1865 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MAX_NS;
1866
1867 rc = qpnp_wled_read_reg(wled, QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base),
1868 &reg);
1869 if (rc < 0)
1870 return rc;
1871 reg &= QPNP_WLED_BOOST_DUTY_MASK;
1872 reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS);
1873 rc = qpnp_wled_write_reg(wled,
1874 QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base), reg);
1875 if (rc)
1876 return rc;
1877
1878 /* Configure the SWITCHING FREQ register */
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -07001879 if (wled->switch_freq_khz == 1600)
1880 reg = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
David Collins8885f792017-01-26 14:36:34 -08001881 else
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -07001882 reg = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
David Collins8885f792017-01-26 14:36:34 -08001883
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -07001884 /*
1885 * Do not set the overwrite bit when switching frequency is selected
1886 * for AMOLED. This register is in logic reset block which can cause
1887 * the value to be overwritten during module enable/disable.
1888 */
1889 mask = QPNP_WLED_SWITCH_FREQ_MASK | QPNP_WLED_SWITCH_FREQ_OVERWRITE;
1890 if (!wled->disp_type_amoled)
1891 reg |= QPNP_WLED_SWITCH_FREQ_OVERWRITE;
1892
1893 rc = qpnp_wled_masked_write_reg(wled,
1894 QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), mask, reg);
David Collins8885f792017-01-26 14:36:34 -08001895 if (rc < 0)
1896 return rc;
David Collins8885f792017-01-26 14:36:34 -08001897
1898 rc = qpnp_wled_ovp_config(wled);
1899 if (rc < 0) {
1900 pr_err("Error in configuring OVP threshold, rc=%d\n", rc);
1901 return rc;
1902 }
1903
1904 if (is_avdd_trim_adjustment_required(wled)) {
1905 rc = qpnp_wled_avdd_trim_config(wled);
1906 if (rc < 0)
1907 return rc;
1908 }
1909
1910 rc = qpnp_wled_avdd_mode_config(wled);
1911 if (rc < 0)
1912 return rc;
1913
1914 /* Configure the MODULATION register */
1915 if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_1200_KHZ) {
1916 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_1200_KHZ;
1917 temp = 3;
1918 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_2400_KHZ) {
1919 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_2400_KHZ;
1920 temp = 2;
1921 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_9600_KHZ) {
1922 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
1923 temp = 1;
1924 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_19200_KHZ) {
1925 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
1926 temp = 0;
1927 } else {
1928 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
1929 temp = 1;
1930 }
1931
1932 rc = qpnp_wled_read_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), &reg);
1933 if (rc < 0)
1934 return rc;
1935 reg &= QPNP_WLED_MOD_FREQ_MASK;
1936 reg |= (temp << QPNP_WLED_MOD_FREQ_SHIFT);
1937
1938 reg &= QPNP_WLED_PHASE_STAG_MASK;
1939 reg |= (wled->en_phase_stag << QPNP_WLED_PHASE_STAG_SHIFT);
1940
1941 reg &= QPNP_WLED_ACC_CLK_FREQ_MASK;
1942 reg |= (temp << QPNP_WLED_ACC_CLK_FREQ_SHIFT);
1943
1944 reg &= QPNP_WLED_DIM_RES_MASK;
1945 reg |= (wled->en_9b_dim_res << QPNP_WLED_DIM_RES_SHIFT);
1946
1947 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
1948 reg &= QPNP_WLED_DIM_HYB_MASK;
1949 reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
1950 } else {
1951 reg &= QPNP_WLED_DIM_HYB_MASK;
1952 reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
1953 reg &= QPNP_WLED_DIM_ANA_MASK;
1954 reg |= wled->dim_mode;
1955 }
1956
1957 rc = qpnp_wled_write_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), reg);
1958 if (rc)
1959 return rc;
1960
1961 /* Configure the HYBRID THRESHOLD register */
1962 if (wled->hyb_thres < QPNP_WLED_HYB_THRES_MIN)
1963 wled->hyb_thres = QPNP_WLED_HYB_THRES_MIN;
1964 else if (wled->hyb_thres > QPNP_WLED_HYB_THRES_MAX)
1965 wled->hyb_thres = QPNP_WLED_HYB_THRES_MAX;
1966
1967 rc = qpnp_wled_read_reg(wled, QPNP_WLED_HYB_THRES_REG(wled->sink_base),
1968 &reg);
1969 if (rc < 0)
1970 return rc;
1971 reg &= QPNP_WLED_HYB_THRES_MASK;
1972 temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1;
1973 reg |= temp;
1974 rc = qpnp_wled_write_reg(wled, QPNP_WLED_HYB_THRES_REG(wled->sink_base),
1975 reg);
1976 if (rc)
1977 return rc;
1978
1979 /* Configure TEST5 register */
1980 if (wled->dim_mode == QPNP_WLED_DIM_DIGITAL) {
1981 reg = QPNP_WLED_SINK_TEST5_DIG;
1982 } else {
1983 reg = QPNP_WLED_SINK_TEST5_HYB;
1984 if (wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
1985 reg |= QPNP_WLED_SINK_TEST5_HVG_PULL_STR_BIT;
1986 }
1987
1988 rc = qpnp_wled_sec_write_reg(wled,
1989 QPNP_WLED_SINK_TEST5_REG(wled->sink_base), reg);
1990 if (rc)
1991 return rc;
1992
1993 /* disable all current sinks and enable selected strings */
1994 reg = 0x00;
1995 rc = qpnp_wled_write_reg(wled, QPNP_WLED_CURR_SINK_REG(wled->sink_base),
1996 reg);
1997
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301998 for (i = 0; i < wled->max_strings; i++) {
1999 /* SYNC DELAY */
2000 if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US)
2001 wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US;
2002
2003 reg = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
2004 mask = QPNP_WLED_SYNC_DLY_MASK;
2005 rc = qpnp_wled_masked_write_reg(wled,
2006 QPNP_WLED_SYNC_DLY_REG(wled->sink_base, i),
2007 mask, reg);
2008 if (rc < 0)
2009 return rc;
2010
2011 /* FULL SCALE CURRENT */
2012 if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA)
2013 wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
2014
2015 reg = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
2016 mask = QPNP_WLED_FS_CURR_MASK;
2017 rc = qpnp_wled_masked_write_reg(wled,
2018 QPNP_WLED_FS_CURR_REG(wled->sink_base, i),
2019 mask, reg);
2020 if (rc < 0)
2021 return rc;
2022
2023 /* CABC */
2024 reg = wled->en_cabc ? (1 << QPNP_WLED_CABC_SHIFT) : 0;
2025 mask = QPNP_WLED_CABC_MASK;
2026 rc = qpnp_wled_masked_write_reg(wled,
2027 QPNP_WLED_CABC_REG(wled->sink_base, i),
2028 mask, reg);
2029 if (rc < 0)
2030 return rc;
2031 }
2032
2033 /* Settings specific to valid sinks */
David Collins8885f792017-01-26 14:36:34 -08002034 for (i = 0; i < wled->num_strings; i++) {
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302035 if (wled->strings[i] >= wled->max_strings) {
David Collins8885f792017-01-26 14:36:34 -08002036 dev_err(&wled->pdev->dev, "Invalid string number\n");
2037 return -EINVAL;
2038 }
David Collins8885f792017-01-26 14:36:34 -08002039 /* MODULATOR */
2040 rc = qpnp_wled_read_reg(wled,
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302041 QPNP_WLED_MOD_EN_REG(wled->sink_base, i), &reg);
David Collins8885f792017-01-26 14:36:34 -08002042 if (rc < 0)
2043 return rc;
2044 reg &= QPNP_WLED_MOD_EN_MASK;
2045 reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
2046
2047 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID)
2048 reg &= QPNP_WLED_GATE_DRV_MASK;
2049 else
2050 reg |= ~QPNP_WLED_GATE_DRV_MASK;
2051
2052 rc = qpnp_wled_write_reg(wled,
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302053 QPNP_WLED_MOD_EN_REG(wled->sink_base, i), reg);
David Collins8885f792017-01-26 14:36:34 -08002054 if (rc)
2055 return rc;
2056
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302057 /* SINK EN */
David Collins8885f792017-01-26 14:36:34 -08002058 temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302059 sink_en |= (1 << temp);
2060 }
2061 mask = QPNP_WLED_CURR_SINK_MASK;
2062 rc = qpnp_wled_masked_write_reg(wled,
2063 QPNP_WLED_CURR_SINK_REG(wled->sink_base),
2064 mask, sink_en);
2065 if (rc < 0) {
2066 dev_err(&wled->pdev->dev,
2067 "Failed to enable WLED sink config rc = %d\n", rc);
2068 return rc;
David Collins8885f792017-01-26 14:36:34 -08002069 }
2070
2071 rc = qpnp_wled_sync_reg_toggle(wled);
2072 if (rc < 0) {
2073 dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
2074 return rc;
2075 }
2076
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05302077 rc = qpnp_wled_auto_calibrate_at_init(wled);
2078 if (rc < 0)
2079 pr_err("Failed to auto-calibrate at init rc=%d\n", rc);
2080
David Collins8885f792017-01-26 14:36:34 -08002081 /* setup ovp and sc irqs */
2082 if (wled->ovp_irq >= 0) {
2083 rc = devm_request_threaded_irq(&wled->pdev->dev, wled->ovp_irq,
2084 NULL, qpnp_wled_ovp_irq_handler, IRQF_ONESHOT,
2085 "qpnp_wled_ovp_irq", wled);
2086 if (rc < 0) {
2087 dev_err(&wled->pdev->dev,
2088 "Unable to request ovp(%d) IRQ(err:%d)\n",
2089 wled->ovp_irq, rc);
2090 return rc;
2091 }
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302092 rc = qpnp_wled_read_reg(wled,
2093 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base), &reg);
2094 /* disable the OVP irq only if the module is not enabled */
2095 if (!rc && !(reg & QPNP_WLED_MODULE_EN_MASK)) {
2096 disable_irq(wled->ovp_irq);
2097 wled->ovp_irq_disabled = true;
2098 }
David Collins8885f792017-01-26 14:36:34 -08002099 }
2100
2101 if (wled->sc_irq >= 0) {
2102 wled->sc_cnt = 0;
2103 rc = devm_request_threaded_irq(&wled->pdev->dev, wled->sc_irq,
2104 NULL, qpnp_wled_sc_irq_handler, IRQF_ONESHOT,
2105 "qpnp_wled_sc_irq", wled);
2106 if (rc < 0) {
2107 dev_err(&wled->pdev->dev,
2108 "Unable to request sc(%d) IRQ(err:%d)\n",
2109 wled->sc_irq, rc);
2110 return rc;
2111 }
2112
2113 rc = qpnp_wled_read_reg(wled,
2114 QPNP_WLED_SC_PRO_REG(wled->ctrl_base), &reg);
2115 if (rc < 0)
2116 return rc;
2117 reg &= QPNP_WLED_EN_SC_DEB_CYCLES_MASK;
2118 reg |= 1 << QPNP_WLED_EN_SC_SHIFT;
2119
2120 if (wled->sc_deb_cycles < QPNP_WLED_SC_DEB_CYCLES_MIN)
2121 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MIN;
2122 else if (wled->sc_deb_cycles > QPNP_WLED_SC_DEB_CYCLES_MAX)
2123 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MAX;
2124 temp = fls(wled->sc_deb_cycles) - QPNP_WLED_SC_DEB_CYCLES_SUB;
2125 reg |= (temp << 1);
2126
2127 if (wled->disp_type_amoled)
2128 reg |= QPNP_WLED_SC_PRO_EN_DSCHGR;
2129
2130 rc = qpnp_wled_write_reg(wled,
2131 QPNP_WLED_SC_PRO_REG(wled->ctrl_base), reg);
2132 if (rc)
2133 return rc;
2134
2135 if (wled->en_ext_pfet_sc_pro) {
2136 reg = QPNP_WLED_EXT_FET_DTEST2;
2137 rc = qpnp_wled_sec_write_reg(wled,
2138 QPNP_WLED_TEST1_REG(wled->ctrl_base),
2139 reg);
2140 if (rc)
2141 return rc;
2142 }
2143 } else {
2144 rc = qpnp_wled_read_reg(wled,
2145 QPNP_WLED_SC_PRO_REG(wled->ctrl_base), &reg);
2146 if (rc < 0)
2147 return rc;
2148 reg &= QPNP_WLED_EN_DEB_CYCLES_MASK;
2149
2150 if (wled->sc_deb_cycles < QPNP_WLED_SC_DEB_CYCLES_MIN)
2151 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MIN;
2152 else if (wled->sc_deb_cycles > QPNP_WLED_SC_DEB_CYCLES_MAX)
2153 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MAX;
2154 temp = fls(wled->sc_deb_cycles) - QPNP_WLED_SC_DEB_CYCLES_SUB;
2155 reg |= (temp << 1);
2156
2157 rc = qpnp_wled_write_reg(wled,
2158 QPNP_WLED_SC_PRO_REG(wled->ctrl_base), reg);
2159 if (rc)
2160 return rc;
2161 }
2162
2163 return 0;
2164}
2165
2166/* parse wled dtsi parameters */
2167static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
2168{
2169 struct platform_device *pdev = wled->pdev;
2170 struct property *prop;
2171 const char *temp_str;
2172 u32 temp_val;
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08002173 int rc, i, size;
David Collins8885f792017-01-26 14:36:34 -08002174 u8 *strings;
2175
2176 wled->cdev.name = "wled";
2177 rc = of_property_read_string(pdev->dev.of_node,
2178 "linux,name", &wled->cdev.name);
2179 if (rc && (rc != -EINVAL)) {
2180 dev_err(&pdev->dev, "Unable to read led name\n");
2181 return rc;
2182 }
2183
2184 wled->cdev.default_trigger = QPNP_WLED_TRIGGER_NONE;
2185 rc = of_property_read_string(pdev->dev.of_node, "linux,default-trigger",
2186 &wled->cdev.default_trigger);
2187 if (rc && (rc != -EINVAL)) {
2188 dev_err(&pdev->dev, "Unable to read led trigger\n");
2189 return rc;
2190 }
2191
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08002192 if (of_find_property(pdev->dev.of_node, "qcom,wled-brightness-map",
2193 NULL)) {
2194 size = of_property_count_elems_of_size(pdev->dev.of_node,
2195 "qcom,wled-brightness-map", sizeof(u16));
2196 if (size != NUM_DDIC_CODES) {
2197 pr_err("Invalid WLED brightness map size:%d\n", size);
2198 return rc;
2199 }
2200
2201 wled->brt_map_table = devm_kcalloc(&pdev->dev, NUM_DDIC_CODES,
2202 sizeof(u16), GFP_KERNEL);
2203 if (!wled->brt_map_table)
2204 return -ENOMEM;
2205
2206 rc = of_property_read_u16_array(pdev->dev.of_node,
2207 "qcom,wled-brightness-map", wled->brt_map_table,
2208 NUM_DDIC_CODES);
2209 if (rc < 0) {
2210 pr_err("Error in reading WLED brightness map, rc=%d\n",
2211 rc);
2212 return rc;
2213 }
2214
2215 for (i = 0; i < NUM_DDIC_CODES; i++) {
2216 if (wled->brt_map_table[i] > WLED_MAX_LEVEL_4095) {
2217 pr_err("WLED brightness map not in range\n");
2218 return -EDOM;
2219 }
2220
2221 if ((i > 1) && wled->brt_map_table[i]
2222 < wled->brt_map_table[i - 1]) {
2223 pr_err("WLED brightness map not in ascending order?\n");
2224 return -EDOM;
2225 }
2226 }
2227 }
2228
David Collins8885f792017-01-26 14:36:34 -08002229 wled->disp_type_amoled = of_property_read_bool(pdev->dev.of_node,
2230 "qcom,disp-type-amoled");
2231 if (wled->disp_type_amoled) {
2232 wled->vref_psm_mv = QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV;
2233 rc = of_property_read_u32(pdev->dev.of_node,
2234 "qcom,vref-psm-mv", &temp_val);
2235 if (!rc) {
2236 wled->vref_psm_mv = temp_val;
2237 } else if (rc != -EINVAL) {
2238 dev_err(&pdev->dev, "Unable to read vref-psm\n");
2239 return rc;
2240 }
2241
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07002242 wled->loop_comp_res_kohm = 320;
2243 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2244 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2245 wled->loop_comp_res_kohm = 300;
2246
David Collins8885f792017-01-26 14:36:34 -08002247 rc = of_property_read_u32(pdev->dev.of_node,
2248 "qcom,loop-comp-res-kohm", &temp_val);
2249 if (!rc) {
2250 wled->loop_comp_res_kohm = temp_val;
2251 } else if (rc != -EINVAL) {
2252 dev_err(&pdev->dev, "Unable to read loop-comp-res-kohm\n");
2253 return rc;
2254 }
2255
2256 wled->avdd_mode_spmi = of_property_read_bool(pdev->dev.of_node,
2257 "qcom,avdd-mode-spmi");
2258
2259 wled->avdd_target_voltage_mv = QPNP_WLED_DFLT_AVDD_MV;
2260 rc = of_property_read_u32(pdev->dev.of_node,
2261 "qcom,avdd-target-voltage-mv", &temp_val);
2262 if (!rc) {
2263 wled->avdd_target_voltage_mv = temp_val;
2264 } else if (rc != -EINVAL) {
2265 dev_err(&pdev->dev, "Unable to read avdd target voltage\n");
2266 return rc;
2267 }
2268 }
2269
2270 if (wled->disp_type_amoled) {
2271 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2272 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2273 wled->loop_ea_gm =
2274 QPNP_WLED_LOOP_GM_DFLT_AMOLED_PMI8998;
2275 else
2276 wled->loop_ea_gm =
2277 QPNP_WLED_LOOP_EA_GM_DFLT_AMOLED_PMI8994;
2278 } else {
2279 wled->loop_ea_gm = QPNP_WLED_LOOP_GM_DFLT_WLED;
2280 }
2281
2282 rc = of_property_read_u32(pdev->dev.of_node,
2283 "qcom,loop-ea-gm", &temp_val);
2284 if (!rc) {
2285 wled->loop_ea_gm = temp_val;
2286 } else if (rc != -EINVAL) {
2287 dev_err(&pdev->dev, "Unable to read loop-ea-gm\n");
2288 return rc;
2289 }
2290
2291 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2292 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
2293 wled->loop_auto_gm_en =
2294 of_property_read_bool(pdev->dev.of_node,
2295 "qcom,loop-auto-gm-en");
2296 wled->loop_auto_gm_thresh = QPNP_WLED_LOOP_AUTO_GM_DFLT_THRESH;
2297 rc = of_property_read_u8(pdev->dev.of_node,
2298 "qcom,loop-auto-gm-thresh",
2299 &wled->loop_auto_gm_thresh);
2300 if (rc && rc != -EINVAL) {
2301 dev_err(&pdev->dev,
2302 "Unable to read loop-auto-gm-thresh\n");
2303 return rc;
2304 }
2305 }
2306
2307 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2308 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
2309
Anirudh Ghayal39526a22017-10-11 19:15:24 +05302310 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE &&
2311 wled->pmic_rev_id->rev4 == PMI8998_V2P0_REV4)
David Collins8885f792017-01-26 14:36:34 -08002312 wled->lcd_auto_pfm_en = false;
2313 else
2314 wled->lcd_auto_pfm_en = true;
2315
2316 wled->lcd_auto_pfm_thresh = QPNP_WLED_LCD_AUTO_PFM_DFLT_THRESH;
2317 rc = of_property_read_u8(pdev->dev.of_node,
2318 "qcom,lcd-auto-pfm-thresh",
2319 &wled->lcd_auto_pfm_thresh);
2320 if (rc && rc != -EINVAL) {
2321 dev_err(&pdev->dev,
2322 "Unable to read lcd-auto-pfm-thresh\n");
2323 return rc;
2324 }
2325
2326 if (wled->lcd_auto_pfm_thresh >
2327 QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX)
2328 wled->lcd_auto_pfm_thresh =
2329 QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX;
2330 }
2331
2332 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_DFLT;
2333 rc = of_property_read_u32(pdev->dev.of_node,
2334 "qcom,sc-deb-cycles", &temp_val);
2335 if (!rc) {
2336 wled->sc_deb_cycles = temp_val;
2337 } else if (rc != -EINVAL) {
2338 dev_err(&pdev->dev, "Unable to read sc debounce cycles\n");
2339 return rc;
2340 }
2341
2342 wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
2343 rc = of_property_read_string(pdev->dev.of_node,
2344 "qcom,fdbk-output", &temp_str);
2345 if (!rc) {
2346 if (strcmp(temp_str, "wled1") == 0)
2347 wled->fdbk_op = QPNP_WLED_FDBK_WLED1;
2348 else if (strcmp(temp_str, "wled2") == 0)
2349 wled->fdbk_op = QPNP_WLED_FDBK_WLED2;
2350 else if (strcmp(temp_str, "wled3") == 0)
2351 wled->fdbk_op = QPNP_WLED_FDBK_WLED3;
2352 else if (strcmp(temp_str, "wled4") == 0)
2353 wled->fdbk_op = QPNP_WLED_FDBK_WLED4;
2354 else
2355 wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
2356 } else if (rc != -EINVAL) {
2357 dev_err(&pdev->dev, "Unable to read feedback output\n");
2358 return rc;
2359 }
2360
2361 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2362 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2363 wled->vref_uv = vref_setting_pmi8998.default_uv;
2364 else
2365 wled->vref_uv = vref_setting_pmi8994.default_uv;
2366 rc = of_property_read_u32(pdev->dev.of_node,
2367 "qcom,vref-uv", &temp_val);
2368 if (!rc) {
2369 wled->vref_uv = temp_val;
2370 } else if (rc != -EINVAL) {
2371 dev_err(&pdev->dev, "Unable to read vref\n");
2372 return rc;
2373 }
2374
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -07002375 wled->switch_freq_khz = wled->disp_type_amoled ? 1600 : 800;
David Collins8885f792017-01-26 14:36:34 -08002376 rc = of_property_read_u32(pdev->dev.of_node,
2377 "qcom,switch-freq-khz", &temp_val);
2378 if (!rc) {
2379 wled->switch_freq_khz = temp_val;
2380 } else if (rc != -EINVAL) {
2381 dev_err(&pdev->dev, "Unable to read switch freq\n");
2382 return rc;
2383 }
2384
2385 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2386 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2387 wled->ovp_mv = 29600;
2388 else
2389 wled->ovp_mv = 29500;
2390 rc = of_property_read_u32(pdev->dev.of_node,
2391 "qcom,ovp-mv", &temp_val);
2392 if (!rc) {
2393 wled->ovp_mv = temp_val;
2394 } else if (rc != -EINVAL) {
2395 dev_err(&pdev->dev, "Unable to read ovp\n");
2396 return rc;
2397 }
2398
2399 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2400 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
2401 if (wled->disp_type_amoled)
2402 wled->ilim_ma = PMI8998_AMOLED_DFLT_ILIM_MA;
2403 else
2404 wled->ilim_ma = PMI8998_WLED_DFLT_ILIM_MA;
2405 } else {
2406 if (wled->disp_type_amoled)
2407 wled->ilim_ma = PMI8994_AMOLED_DFLT_ILIM_MA;
2408 else
2409 wled->ilim_ma = PMI8994_WLED_DFLT_ILIM_MA;
2410 }
2411
2412 rc = of_property_read_u32(pdev->dev.of_node,
2413 "qcom,ilim-ma", &temp_val);
2414 if (!rc) {
2415 wled->ilim_ma = temp_val;
2416 } else if (rc != -EINVAL) {
2417 dev_err(&pdev->dev, "Unable to read ilim\n");
2418 return rc;
2419 }
2420
2421 wled->boost_duty_ns = QPNP_WLED_DEF_BOOST_DUTY_NS;
2422 rc = of_property_read_u32(pdev->dev.of_node,
2423 "qcom,boost-duty-ns", &temp_val);
2424 if (!rc) {
2425 wled->boost_duty_ns = temp_val;
2426 } else if (rc != -EINVAL) {
2427 dev_err(&pdev->dev, "Unable to read boost duty\n");
2428 return rc;
2429 }
2430
2431 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
2432 rc = of_property_read_u32(pdev->dev.of_node,
2433 "qcom,mod-freq-khz", &temp_val);
2434 if (!rc) {
2435 wled->mod_freq_khz = temp_val;
2436 } else if (rc != -EINVAL) {
2437 dev_err(&pdev->dev, "Unable to read modulation freq\n");
2438 return rc;
2439 }
2440
2441 wled->dim_mode = QPNP_WLED_DIM_HYBRID;
2442 rc = of_property_read_string(pdev->dev.of_node,
2443 "qcom,dim-mode", &temp_str);
2444 if (!rc) {
2445 if (strcmp(temp_str, "analog") == 0)
2446 wled->dim_mode = QPNP_WLED_DIM_ANALOG;
2447 else if (strcmp(temp_str, "digital") == 0)
2448 wled->dim_mode = QPNP_WLED_DIM_DIGITAL;
2449 else
2450 wled->dim_mode = QPNP_WLED_DIM_HYBRID;
2451 } else if (rc != -EINVAL) {
2452 dev_err(&pdev->dev, "Unable to read dim mode\n");
2453 return rc;
2454 }
2455
2456 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
2457 wled->hyb_thres = QPNP_WLED_DEF_HYB_THRES;
2458 rc = of_property_read_u32(pdev->dev.of_node,
2459 "qcom,hyb-thres", &temp_val);
2460 if (!rc) {
2461 wled->hyb_thres = temp_val;
2462 } else if (rc != -EINVAL) {
2463 dev_err(&pdev->dev, "Unable to read hyb threshold\n");
2464 return rc;
2465 }
2466 }
2467
2468 wled->sync_dly_us = QPNP_WLED_DEF_SYNC_DLY_US;
2469 rc = of_property_read_u32(pdev->dev.of_node,
2470 "qcom,sync-dly-us", &temp_val);
2471 if (!rc) {
2472 wled->sync_dly_us = temp_val;
2473 } else if (rc != -EINVAL) {
2474 dev_err(&pdev->dev, "Unable to read sync delay\n");
2475 return rc;
2476 }
2477
2478 wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
2479 rc = of_property_read_u32(pdev->dev.of_node,
2480 "qcom,fs-curr-ua", &temp_val);
2481 if (!rc) {
2482 wled->fs_curr_ua = temp_val;
2483 } else if (rc != -EINVAL) {
2484 dev_err(&pdev->dev, "Unable to read full scale current\n");
2485 return rc;
2486 }
2487
2488 wled->cons_sync_write_delay_us = 0;
2489 rc = of_property_read_u32(pdev->dev.of_node,
2490 "qcom,cons-sync-write-delay-us", &temp_val);
2491 if (!rc)
2492 wled->cons_sync_write_delay_us = temp_val;
2493
2494 wled->en_9b_dim_res = of_property_read_bool(pdev->dev.of_node,
2495 "qcom,en-9b-dim-res");
2496 wled->en_phase_stag = of_property_read_bool(pdev->dev.of_node,
2497 "qcom,en-phase-stag");
2498 wled->en_cabc = of_property_read_bool(pdev->dev.of_node,
2499 "qcom,en-cabc");
2500
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302501 if (wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2502 wled->max_strings = QPNP_PM660_WLED_MAX_STRINGS;
2503 else
2504 wled->max_strings = QPNP_WLED_MAX_STRINGS;
2505
David Collins8885f792017-01-26 14:36:34 -08002506 prop = of_find_property(pdev->dev.of_node,
2507 "qcom,led-strings-list", &temp_val);
2508 if (!prop || !temp_val || temp_val > QPNP_WLED_MAX_STRINGS) {
2509 dev_err(&pdev->dev, "Invalid strings info, use default");
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302510 wled->num_strings = wled->max_strings;
David Collins8885f792017-01-26 14:36:34 -08002511 for (i = 0; i < wled->num_strings; i++)
2512 wled->strings[i] = i;
2513 } else {
2514 wled->num_strings = temp_val;
2515 strings = prop->value;
2516 for (i = 0; i < wled->num_strings; ++i)
2517 wled->strings[i] = strings[i];
2518 }
2519
2520 wled->ovp_irq = platform_get_irq_byname(pdev, "ovp-irq");
2521 if (wled->ovp_irq < 0)
2522 dev_dbg(&pdev->dev, "ovp irq is not used\n");
2523
2524 wled->sc_irq = platform_get_irq_byname(pdev, "sc-irq");
2525 if (wled->sc_irq < 0)
2526 dev_dbg(&pdev->dev, "sc irq is not used\n");
2527
2528 wled->en_ext_pfet_sc_pro = of_property_read_bool(pdev->dev.of_node,
2529 "qcom,en-ext-pfet-sc-pro");
2530
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -08002531 wled->lcd_psm_ctrl = of_property_read_bool(pdev->dev.of_node,
2532 "qcom,lcd-psm-ctrl");
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302533
2534 wled->auto_calib_enabled = of_property_read_bool(pdev->dev.of_node,
2535 "qcom,auto-calibration-enable");
David Collins8885f792017-01-26 14:36:34 -08002536 return 0;
2537}
2538
2539static int qpnp_wled_probe(struct platform_device *pdev)
2540{
2541 struct qpnp_wled *wled;
2542 struct device_node *revid_node;
2543 int rc = 0, i;
2544 const __be32 *prop;
2545
2546 wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL);
2547 if (!wled)
2548 return -ENOMEM;
Stephen Boydd71c7f62017-03-20 15:49:04 -07002549
2550 wled->regmap = dev_get_regmap(pdev->dev.parent, NULL);
2551 if (!wled->regmap) {
2552 dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
2553 return -EINVAL;
2554 }
David Collins8885f792017-01-26 14:36:34 -08002555
2556 wled->pdev = pdev;
2557
2558 revid_node = of_parse_phandle(pdev->dev.of_node, "qcom,pmic-revid", 0);
2559 if (!revid_node) {
2560 pr_err("Missing qcom,pmic-revid property - driver failed\n");
2561 return -EINVAL;
2562 }
2563
2564 wled->pmic_rev_id = get_revid_data(revid_node);
2565 if (IS_ERR_OR_NULL(wled->pmic_rev_id)) {
2566 pr_err("Unable to get pmic_revid rc=%ld\n",
2567 PTR_ERR(wled->pmic_rev_id));
2568 /*
2569 * the revid peripheral must be registered, any failure
2570 * here only indicates that the rev-id module has not
2571 * probed yet.
2572 */
2573 return -EPROBE_DEFER;
2574 }
2575
2576 pr_debug("PMIC subtype %d Digital major %d\n",
2577 wled->pmic_rev_id->pmic_subtype, wled->pmic_rev_id->rev4);
2578
2579 prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_SINK_BASE,
Subbaraman Narayanamurthy8b9dd592017-08-23 18:44:15 -07002580 NULL, NULL);
David Collins8885f792017-01-26 14:36:34 -08002581 if (!prop) {
2582 dev_err(&pdev->dev, "Couldnt find sink's addr rc %d\n", rc);
2583 return rc;
2584 }
2585 wled->sink_base = be32_to_cpu(*prop);
2586
2587 prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_CTRL_BASE,
Subbaraman Narayanamurthy8b9dd592017-08-23 18:44:15 -07002588 NULL, NULL);
David Collins8885f792017-01-26 14:36:34 -08002589 if (!prop) {
2590 dev_err(&pdev->dev, "Couldnt find ctrl's addr rc = %d\n", rc);
2591 return rc;
2592 }
2593 wled->ctrl_base = be32_to_cpu(*prop);
2594
2595 dev_set_drvdata(&pdev->dev, wled);
2596
2597 rc = qpnp_wled_parse_dt(wled);
2598 if (rc) {
2599 dev_err(&pdev->dev, "DT parsing failed\n");
2600 return rc;
2601 }
2602
2603 mutex_init(&wled->bus_lock);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302604 mutex_init(&wled->lock);
David Collins8885f792017-01-26 14:36:34 -08002605 rc = qpnp_wled_config(wled);
2606 if (rc) {
2607 dev_err(&pdev->dev, "wled config failed\n");
2608 return rc;
2609 }
2610
David Collins8885f792017-01-26 14:36:34 -08002611 INIT_WORK(&wled->work, qpnp_wled_work);
2612 wled->ramp_ms = QPNP_WLED_RAMP_DLY_MS;
2613 wled->ramp_step = 1;
2614
2615 wled->cdev.brightness_set = qpnp_wled_set;
2616 wled->cdev.brightness_get = qpnp_wled_get;
2617
2618 wled->cdev.max_brightness = WLED_MAX_LEVEL_4095;
2619
2620 rc = led_classdev_register(&pdev->dev, &wled->cdev);
2621 if (rc) {
2622 dev_err(&pdev->dev, "wled registration failed(%d)\n", rc);
2623 goto wled_register_fail;
2624 }
2625
2626 for (i = 0; i < ARRAY_SIZE(qpnp_wled_attrs); i++) {
2627 rc = sysfs_create_file(&wled->cdev.dev->kobj,
2628 &qpnp_wled_attrs[i].attr);
2629 if (rc < 0) {
2630 dev_err(&pdev->dev, "sysfs creation failed\n");
2631 goto sysfs_fail;
2632 }
2633 }
2634
2635 return 0;
2636
2637sysfs_fail:
2638 for (i--; i >= 0; i--)
2639 sysfs_remove_file(&wled->cdev.dev->kobj,
2640 &qpnp_wled_attrs[i].attr);
2641 led_classdev_unregister(&wled->cdev);
2642wled_register_fail:
2643 cancel_work_sync(&wled->work);
2644 mutex_destroy(&wled->lock);
2645 return rc;
2646}
2647
2648static int qpnp_wled_remove(struct platform_device *pdev)
2649{
2650 struct qpnp_wled *wled = dev_get_drvdata(&pdev->dev);
2651 int i;
2652
2653 for (i = 0; i < ARRAY_SIZE(qpnp_wled_attrs); i++)
2654 sysfs_remove_file(&wled->cdev.dev->kobj,
2655 &qpnp_wled_attrs[i].attr);
2656
2657 led_classdev_unregister(&wled->cdev);
2658 cancel_work_sync(&wled->work);
2659 mutex_destroy(&wled->lock);
2660
2661 return 0;
2662}
2663
2664static const struct of_device_id spmi_match_table[] = {
2665 { .compatible = "qcom,qpnp-wled",},
2666 { },
2667};
2668
2669static struct platform_driver qpnp_wled_driver = {
2670 .driver = {
2671 .name = "qcom,qpnp-wled",
2672 .of_match_table = spmi_match_table,
2673 },
2674 .probe = qpnp_wled_probe,
2675 .remove = qpnp_wled_remove,
2676};
2677
2678static int __init qpnp_wled_init(void)
2679{
2680 return platform_driver_register(&qpnp_wled_driver);
2681}
Subbaraman Narayanamurthy67e30fb2017-05-01 16:29:34 -07002682subsys_initcall(qpnp_wled_init);
David Collins8885f792017-01-26 14:36:34 -08002683
2684static void __exit qpnp_wled_exit(void)
2685{
2686 platform_driver_unregister(&qpnp_wled_driver);
2687}
2688module_exit(qpnp_wled_exit);
2689
2690MODULE_DESCRIPTION("QPNP WLED driver");
2691MODULE_LICENSE("GPL v2");
2692MODULE_ALIAS("leds:leds-qpnp-wled");