blob: 861d9876cf0b21a6228fdd74b25ee8ba9d03bbce [file] [log] [blame]
Subbaraman Narayanamurthy50dae7a2018-05-18 15:34:19 -07001/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
David Collins8885f792017-01-26 14:36:34 -08002 *
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
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700319 * @ wq - workqueue for setting brightness level
David Collins8885f792017-01-26 14:36:34 -0800320 * @ lock - mutex lock for exclusive access
321 * @ fdbk_op - output feedback mode
322 * @ dim_mode - dimming mode
323 * @ ovp_irq - over voltage protection irq
324 * @ sc_irq - short circuit irq
325 * @ sc_cnt - short circuit irq count
326 * @ avdd_target_voltage_mv - target voltage for AVDD module in mV
327 * @ ctrl_base - base address for wled ctrl
328 * @ sink_base - base address for wled sink
329 * @ mod_freq_khz - modulator frequency in KHZ
330 * @ hyb_thres - threshold for hybrid dimming
331 * @ sync_dly_us - sync delay in us
332 * @ vref_uv - ref voltage in uv
333 * @ vref_psm_mv - ref psm voltage in mv
334 * @ loop_comp_res_kohm - control to select the compensation resistor
335 * @ loop_ea_gm - control to select the gm for the gm stage in control loop
336 * @ sc_deb_cycles - debounce time for short circuit detection
337 * @ switch_freq_khz - switching frequency in KHZ
338 * @ ovp_mv - over voltage protection in mv
339 * @ ilim_ma - current limiter in ma
340 * @ boost_duty_ns - boost duty cycle in ns
341 * @ fs_curr_ua - full scale current in ua
342 * @ ramp_ms - delay between ramp steps in ms
343 * @ ramp_step - ramp step size
344 * @ cons_sync_write_delay_us - delay between two consecutive writes to SYNC
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800345 * @ auto_calibration_ovp_count - OVP fault irq count to run auto calibration
346 * @ max_strings - Number of strings supported in WLED peripheral
347 * @ prev_level - Previous brightness level
348 * @ brt_map_table - Brightness map table
David Collins8885f792017-01-26 14:36:34 -0800349 * @ strings - supported list of strings
350 * @ num_strings - number of strings
351 * @ loop_auto_gm_thresh - the clamping level for auto gm
352 * @ lcd_auto_pfm_thresh - the threshold for lcd auto pfm mode
353 * @ loop_auto_gm_en - select if auto gm is enabled
354 * @ lcd_auto_pfm_en - select if auto pfm is enabled in lcd mode
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800355 * @ lcd_psm_ctrl - select if psm needs to be controlled in lcd mode
David Collins8885f792017-01-26 14:36:34 -0800356 * @ avdd_mode_spmi - enable avdd programming via spmi
357 * @ en_9b_dim_res - enable or disable 9bit dimming
358 * @ en_phase_stag - enable or disable phase staggering
359 * @ en_cabc - enable or disable cabc
360 * @ disp_type_amoled - type of display: LCD/AMOLED
361 * @ en_ext_pfet_sc_pro - enable sc protection on external pfet
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800362 * @ prev_state - previous state of WLED
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700363 * @ stepper_en - Flag to enable stepper algorithm
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800364 * @ ovp_irq_disabled - OVP interrupt disable status
365 * @ auto_calib_enabled - Flag to enable auto calibration feature
366 * @ auto_calib_done - Flag to indicate auto calibration is done
367 * @ module_dis_perm - Flat to keep module permanently disabled
368 * @ start_ovp_fault_time - Time when the OVP fault first occurred
David Collins8885f792017-01-26 14:36:34 -0800369 */
370struct qpnp_wled {
371 struct led_classdev cdev;
372 struct platform_device *pdev;
373 struct regmap *regmap;
374 struct pmic_revid_data *pmic_rev_id;
375 struct work_struct work;
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700376 struct workqueue_struct *wq;
David Collins8885f792017-01-26 14:36:34 -0800377 struct mutex lock;
378 struct mutex bus_lock;
379 enum qpnp_wled_fdbk_op fdbk_op;
380 enum qpnp_wled_dim_mode dim_mode;
381 int ovp_irq;
382 int sc_irq;
383 u32 sc_cnt;
384 u32 avdd_target_voltage_mv;
385 u16 ctrl_base;
386 u16 sink_base;
387 u16 mod_freq_khz;
388 u16 hyb_thres;
389 u16 sync_dly_us;
390 u32 vref_uv;
391 u16 vref_psm_mv;
392 u16 loop_comp_res_kohm;
393 u16 loop_ea_gm;
394 u16 sc_deb_cycles;
395 u16 switch_freq_khz;
396 u16 ovp_mv;
397 u16 ilim_ma;
398 u16 boost_duty_ns;
399 u16 fs_curr_ua;
400 u16 ramp_ms;
401 u16 ramp_step;
402 u16 cons_sync_write_delay_us;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530403 u16 auto_calibration_ovp_count;
404 u16 max_strings;
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800405 u16 prev_level;
406 u16 *brt_map_table;
David Collins8885f792017-01-26 14:36:34 -0800407 u8 strings[QPNP_WLED_MAX_STRINGS];
408 u8 num_strings;
409 u8 loop_auto_gm_thresh;
410 u8 lcd_auto_pfm_thresh;
411 bool loop_auto_gm_en;
412 bool lcd_auto_pfm_en;
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800413 bool lcd_psm_ctrl;
David Collins8885f792017-01-26 14:36:34 -0800414 bool avdd_mode_spmi;
415 bool en_9b_dim_res;
416 bool en_phase_stag;
417 bool en_cabc;
418 bool disp_type_amoled;
419 bool en_ext_pfet_sc_pro;
420 bool prev_state;
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700421 bool stepper_en;
David Collins8885f792017-01-26 14:36:34 -0800422 bool ovp_irq_disabled;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530423 bool auto_calib_enabled;
424 bool auto_calib_done;
Subbaraman Narayanamurthy15624d92017-10-12 21:04:30 -0700425 bool module_dis_perm;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530426 ktime_t start_ovp_fault_time;
David Collins8885f792017-01-26 14:36:34 -0800427};
428
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700429static int qpnp_wled_step_delay_us = 52000;
430module_param_named(
431 total_step_delay_us, qpnp_wled_step_delay_us, int, 0600
432);
433
434static int qpnp_wled_step_size_threshold = 3;
435module_param_named(
436 step_size_threshold, qpnp_wled_step_size_threshold, int, 0600
437);
438
439static int qpnp_wled_step_delay_gain = 2;
440module_param_named(
441 step_delay_gain, qpnp_wled_step_delay_gain, int, 0600
442);
443
David Collins8885f792017-01-26 14:36:34 -0800444/* helper to read a pmic register */
445static int qpnp_wled_read_reg(struct qpnp_wled *wled, u16 addr, u8 *data)
446{
447 int rc;
448 uint val;
449
450 rc = regmap_read(wled->regmap, addr, &val);
451 if (rc < 0) {
452 dev_err(&wled->pdev->dev,
453 "Error reading address: %x(%d)\n", addr, rc);
454 return rc;
455 }
456
457 *data = (u8)val;
458 return 0;
459}
460
461/* helper to write a pmic register */
462static int qpnp_wled_write_reg(struct qpnp_wled *wled, u16 addr, u8 data)
463{
464 int rc;
465
466 mutex_lock(&wled->bus_lock);
467 rc = regmap_write(wled->regmap, addr, 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_masked_write_reg(struct qpnp_wled *wled, u16 addr,
481 u8 mask, u8 data)
482{
483 int rc;
484
485 mutex_lock(&wled->bus_lock);
486 rc = regmap_update_bits(wled->regmap, addr, mask, data);
487 if (rc < 0) {
488 dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
489 addr, rc);
490 goto out;
491 }
492
493 dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
494out:
495 mutex_unlock(&wled->bus_lock);
496 return rc;
497}
498
499static int qpnp_wled_sec_write_reg(struct qpnp_wled *wled, u16 addr, u8 data)
500{
501 int rc;
502 u8 reg = QPNP_WLED_SEC_UNLOCK;
503 u16 base_addr = addr & 0xFF00;
504
505 mutex_lock(&wled->bus_lock);
506 rc = regmap_write(wled->regmap, QPNP_WLED_SEC_ACCESS_REG(base_addr),
507 reg);
508 if (rc < 0) {
509 dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
510 QPNP_WLED_SEC_ACCESS_REG(base_addr), rc);
511 goto out;
512 }
513
514 rc = regmap_write(wled->regmap, addr, data);
515 if (rc < 0) {
516 dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
517 addr, rc);
518 goto out;
519 }
520
521 dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
522out:
523 mutex_unlock(&wled->bus_lock);
524 return rc;
525}
526
527static int qpnp_wled_swire_avdd_config(struct qpnp_wled *wled)
528{
529 int rc;
530 u8 val;
531
532 if (wled->pmic_rev_id->pmic_subtype != PMI8998_SUBTYPE &&
533 wled->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
534 return 0;
535
536 if (!wled->disp_type_amoled || wled->avdd_mode_spmi)
537 return 0;
538
539 val = QPNP_WLED_AVDD_MV_TO_REG(wled->avdd_target_voltage_mv);
540 rc = qpnp_wled_write_reg(wled,
541 QPNP_WLED_SWIRE_AVDD_REG(wled->ctrl_base), val);
542 return rc;
543}
544
545static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled)
546{
547 int rc;
548 u8 reg;
549
550 /* sync */
551 reg = QPNP_WLED_SYNC;
552 rc = qpnp_wled_write_reg(wled, QPNP_WLED_SYNC_REG(wled->sink_base),
553 reg);
554 if (rc < 0)
555 return rc;
556
557 if (wled->cons_sync_write_delay_us)
558 usleep_range(wled->cons_sync_write_delay_us,
559 wled->cons_sync_write_delay_us + 1);
560
561 reg = QPNP_WLED_SYNC_RESET;
562 rc = qpnp_wled_write_reg(wled, QPNP_WLED_SYNC_REG(wled->sink_base),
563 reg);
564 if (rc < 0)
565 return rc;
566
567 return 0;
568}
569
570/* set wled to a level of brightness */
571static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
572{
573 int i, rc;
574 u8 reg;
Subbaraman Narayanamurthycb5b72e2017-08-21 19:16:37 -0700575 u16 low_limit = WLED_MAX_LEVEL_4095 * 4 / 1000;
576
577 /* WLED's lower limit of operation is 0.4% */
578 if (level > 0 && level < low_limit)
579 level = low_limit;
David Collins8885f792017-01-26 14:36:34 -0800580
581 /* set brightness registers */
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530582 for (i = 0; i < wled->max_strings; i++) {
David Collins8885f792017-01-26 14:36:34 -0800583 reg = level & QPNP_WLED_BRIGHT_LSB_MASK;
584 rc = qpnp_wled_write_reg(wled,
585 QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base,
586 wled->strings[i]), reg);
587 if (rc < 0)
588 return rc;
589
590 reg = level >> QPNP_WLED_BRIGHT_MSB_SHIFT;
591 reg = reg & QPNP_WLED_BRIGHT_MSB_MASK;
592 rc = qpnp_wled_write_reg(wled,
593 QPNP_WLED_BRIGHT_MSB_REG(wled->sink_base,
594 wled->strings[i]), reg);
595 if (rc < 0)
596 return rc;
597 }
598
599 rc = qpnp_wled_sync_reg_toggle(wled);
600 if (rc < 0) {
601 dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
602 return rc;
603 }
604
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700605 pr_debug("level:%d\n", level);
David Collins8885f792017-01-26 14:36:34 -0800606 return 0;
607}
608
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800609static int qpnp_wled_set_map_level(struct qpnp_wled *wled, int level)
610{
611 int rc, i;
612
613 if (level < wled->prev_level) {
614 for (i = wled->prev_level; i >= level; i--) {
615 rc = qpnp_wled_set_level(wled, wled->brt_map_table[i]);
616 if (rc < 0) {
617 pr_err("set brightness level failed, rc:%d\n",
618 rc);
619 return rc;
620 }
621 }
622 } else if (level > wled->prev_level) {
623 for (i = wled->prev_level; i <= level; i++) {
624 rc = qpnp_wled_set_level(wled, wled->brt_map_table[i]);
625 if (rc < 0) {
626 pr_err("set brightness level failed, rc:%d\n",
627 rc);
628 return rc;
629 }
630 }
631 }
632
633 return 0;
634}
635
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700636static int qpnp_wled_set_step_level(struct qpnp_wled *wled, int new_level)
637{
638 int rc, i, num_steps, delay_us;
639 u16 level, start_level, end_level, step_size;
640 bool level_inc = false;
641
642 level = wled->prev_level;
643 start_level = wled->brt_map_table[level];
644 end_level = wled->brt_map_table[new_level];
645 level_inc = (new_level > level);
646
647 num_steps = abs(start_level - end_level);
648 if (!num_steps)
649 return 0;
650
651 delay_us = qpnp_wled_step_delay_us / num_steps;
652 pr_debug("level goes from [%d %d] num_steps: %d, delay: %d\n",
653 start_level, end_level, num_steps, delay_us);
654
655 if (delay_us < 500) {
656 step_size = 1000 / delay_us;
657 num_steps = num_steps / step_size;
658 delay_us = 1000;
659 } else {
660 if (num_steps < qpnp_wled_step_size_threshold)
661 delay_us *= qpnp_wled_step_delay_gain;
662
663 step_size = 1;
664 }
665
666 i = start_level;
667 while (num_steps--) {
668 if (level_inc)
669 i += step_size;
670 else
671 i -= step_size;
672
673 rc = qpnp_wled_set_level(wled, i);
674 if (rc < 0)
675 return rc;
676
677 if (delay_us > 0) {
678 if (delay_us < 20000)
679 usleep_range(delay_us, delay_us + 1);
680 else
681 msleep(delay_us / USEC_PER_MSEC);
682 }
683 }
684
685 if (i != end_level) {
686 i = end_level;
687 rc = qpnp_wled_set_level(wled, i);
688 if (rc < 0)
689 return rc;
690 }
691
692 return 0;
693}
694
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800695static int qpnp_wled_psm_config(struct qpnp_wled *wled, bool enable)
696{
697 int rc;
698
699 if (!wled->lcd_psm_ctrl)
700 return 0;
701
702 rc = qpnp_wled_masked_write_reg(wled,
703 QPNP_WLED_EN_PSM_REG(wled->ctrl_base),
704 QPNP_WLED_EN_PSM_BIT,
705 enable ? QPNP_WLED_EN_PSM_BIT : 0);
706 if (rc < 0)
707 return rc;
708
709 rc = qpnp_wled_masked_write_reg(wled,
710 QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base),
711 QPNP_WLED_PSM_OVERWRITE_BIT,
712 enable ? QPNP_WLED_PSM_OVERWRITE_BIT : 0);
713 if (rc < 0)
714 return rc;
715
716 return 0;
717}
718
David Collins8885f792017-01-26 14:36:34 -0800719static int qpnp_wled_module_en(struct qpnp_wled *wled,
720 u16 base_addr, bool state)
721{
722 int rc;
723
Subbaraman Narayanamurthy15624d92017-10-12 21:04:30 -0700724 if (wled->module_dis_perm)
725 return 0;
726
David Collins8885f792017-01-26 14:36:34 -0800727 rc = qpnp_wled_masked_write_reg(wled,
728 QPNP_WLED_MODULE_EN_REG(base_addr),
729 QPNP_WLED_MODULE_EN_MASK,
730 state << QPNP_WLED_MODULE_EN_SHIFT);
731 if (rc < 0)
732 return rc;
733
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800734 /*
735 * Wait for at least 10ms before enabling OVP fault interrupt after
736 * enabling the module so that soft start is completed. Also, this
737 * delay can be used to control PSM during enable when required. Keep
738 * OVP interrupt disabled when the module is disabled.
739 */
740 if (state) {
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530741 usleep_range(QPNP_WLED_SOFT_START_DLY_US,
742 QPNP_WLED_SOFT_START_DLY_US + 1000);
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800743 rc = qpnp_wled_psm_config(wled, false);
744 if (rc < 0)
745 return rc;
746
747 if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) {
David Collins8885f792017-01-26 14:36:34 -0800748 enable_irq(wled->ovp_irq);
749 wled->ovp_irq_disabled = false;
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800750 }
751 } else {
752 if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) {
David Collins8885f792017-01-26 14:36:34 -0800753 disable_irq(wled->ovp_irq);
754 wled->ovp_irq_disabled = true;
755 }
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800756
757 rc = qpnp_wled_psm_config(wled, true);
758 if (rc < 0)
759 return rc;
David Collins8885f792017-01-26 14:36:34 -0800760 }
761
762 return 0;
763}
764
765/* sysfs store function for ramp */
766static ssize_t qpnp_wled_ramp_store(struct device *dev,
767 struct device_attribute *attr, const char *buf, size_t count)
768{
769 struct qpnp_wled *wled = dev_get_drvdata(dev);
770 int i, rc;
771
772 mutex_lock(&wled->lock);
773
774 if (!wled->cdev.brightness) {
775 rc = qpnp_wled_module_en(wled, wled->ctrl_base, true);
776 if (rc) {
777 dev_err(&wled->pdev->dev, "wled enable failed\n");
778 goto unlock_mutex;
779 }
780 }
781
782 /* ramp up */
783 for (i = 0; i <= wled->cdev.max_brightness;) {
784 rc = qpnp_wled_set_level(wled, i);
785 if (rc) {
786 dev_err(&wled->pdev->dev, "wled set level failed\n");
787 goto restore_brightness;
788 }
789
790 if (wled->ramp_ms < QPNP_WLED_MIN_MSLEEP)
791 usleep_range(wled->ramp_ms * USEC_PER_MSEC,
792 wled->ramp_ms * USEC_PER_MSEC);
793 else
794 msleep(wled->ramp_ms);
795
796 if (i == wled->cdev.max_brightness)
797 break;
798
799 i += wled->ramp_step;
800 if (i > wled->cdev.max_brightness)
801 i = wled->cdev.max_brightness;
802 }
803
804 /* ramp down */
805 for (i = wled->cdev.max_brightness; i >= 0;) {
806 rc = qpnp_wled_set_level(wled, i);
807 if (rc) {
808 dev_err(&wled->pdev->dev, "wled set level failed\n");
809 goto restore_brightness;
810 }
811
812 if (wled->ramp_ms < QPNP_WLED_MIN_MSLEEP)
813 usleep_range(wled->ramp_ms * USEC_PER_MSEC,
814 wled->ramp_ms * USEC_PER_MSEC);
815 else
816 msleep(wled->ramp_ms);
817
818 if (i == 0)
819 break;
820
821 i -= wled->ramp_step;
822 if (i < 0)
823 i = 0;
824 }
825
826 dev_info(&wled->pdev->dev, "wled ramp complete\n");
827
828restore_brightness:
829 /* restore the old brightness */
830 qpnp_wled_set_level(wled, wled->cdev.brightness);
831 if (!wled->cdev.brightness) {
832 rc = qpnp_wled_module_en(wled, wled->ctrl_base, false);
833 if (rc)
834 dev_err(&wled->pdev->dev, "wled enable failed\n");
835 }
836unlock_mutex:
837 mutex_unlock(&wled->lock);
838
839 return count;
840}
841
842static int qpnp_wled_dump_regs(struct qpnp_wled *wled, u16 base_addr,
843 u8 dbg_regs[], u8 size, char *label,
844 int count, char *buf)
845{
846 int i, rc;
847 u8 reg;
848
849 for (i = 0; i < size; i++) {
850 rc = qpnp_wled_read_reg(wled, base_addr + dbg_regs[i], &reg);
851 if (rc < 0)
852 return rc;
853
854 count += snprintf(buf + count, PAGE_SIZE - count,
855 "%s: REG_0x%x = 0x%x\n", label,
856 base_addr + dbg_regs[i], reg);
857
858 if (count >= PAGE_SIZE)
859 return PAGE_SIZE - 1;
860 }
861
862 return count;
863}
864
865/* sysfs show function for debug registers */
866static ssize_t qpnp_wled_dump_regs_show(struct device *dev,
867 struct device_attribute *attr, char *buf)
868{
869 struct qpnp_wled *wled = dev_get_drvdata(dev);
870 int count = 0;
871
872 count = qpnp_wled_dump_regs(wled, wled->ctrl_base,
873 qpnp_wled_ctrl_dbg_regs,
874 ARRAY_SIZE(qpnp_wled_ctrl_dbg_regs),
875 "wled_ctrl", count, buf);
876
877 if (count < 0 || count == PAGE_SIZE - 1)
878 return count;
879
880 count = qpnp_wled_dump_regs(wled, wled->sink_base,
881 qpnp_wled_sink_dbg_regs,
882 ARRAY_SIZE(qpnp_wled_sink_dbg_regs),
883 "wled_sink", count, buf);
884
885 if (count < 0 || count == PAGE_SIZE - 1)
886 return count;
887
888 return count;
889}
890
891/* sysfs show function for ramp delay in each step */
892static ssize_t qpnp_wled_ramp_ms_show(struct device *dev,
893 struct device_attribute *attr, char *buf)
894{
895 struct qpnp_wled *wled = dev_get_drvdata(dev);
896
897 return snprintf(buf, PAGE_SIZE, "%d\n", wled->ramp_ms);
898}
899
900/* sysfs store function for ramp delay in each step */
901static ssize_t qpnp_wled_ramp_ms_store(struct device *dev,
902 struct device_attribute *attr, const char *buf, size_t count)
903{
904 struct qpnp_wled *wled = dev_get_drvdata(dev);
905 int data, rc;
906
907 rc = kstrtoint(buf, 10, &data);
908 if (rc)
909 return rc;
910
911 wled->ramp_ms = data;
912 return count;
913}
914
915/* sysfs show function for ramp step */
916static ssize_t qpnp_wled_ramp_step_show(struct device *dev,
917 struct device_attribute *attr, char *buf)
918{
919 struct qpnp_wled *wled = dev_get_drvdata(dev);
920
921 return snprintf(buf, PAGE_SIZE, "%d\n", wled->ramp_step);
922}
923
924/* sysfs store function for ramp step */
925static ssize_t qpnp_wled_ramp_step_store(struct device *dev,
926 struct device_attribute *attr, const char *buf, size_t count)
927{
928 struct qpnp_wled *wled = dev_get_drvdata(dev);
929 int data, rc;
930
931 rc = kstrtoint(buf, 10, &data);
932 if (rc)
933 return rc;
934
935 wled->ramp_step = data;
936 return count;
937}
938
939/* sysfs show function for dim mode */
940static ssize_t qpnp_wled_dim_mode_show(struct device *dev,
941 struct device_attribute *attr, char *buf)
942{
943 struct qpnp_wled *wled = dev_get_drvdata(dev);
944 char *str;
945
946 if (wled->dim_mode == QPNP_WLED_DIM_ANALOG)
947 str = "analog";
948 else if (wled->dim_mode == QPNP_WLED_DIM_DIGITAL)
949 str = "digital";
950 else
951 str = "hybrid";
952
953 return snprintf(buf, PAGE_SIZE, "%s\n", str);
954}
955
956/* sysfs store function for dim mode*/
957static ssize_t qpnp_wled_dim_mode_store(struct device *dev,
958 struct device_attribute *attr, const char *buf, size_t count)
959{
960 struct qpnp_wled *wled = dev_get_drvdata(dev);
961 char str[QPNP_WLED_STR_SIZE + 1];
962 int rc, temp;
963 u8 reg;
964
965 if (snprintf(str, QPNP_WLED_STR_SIZE, "%s", buf) > QPNP_WLED_STR_SIZE)
966 return -EINVAL;
967
968 if (strcmp(str, "analog") == 0)
969 temp = QPNP_WLED_DIM_ANALOG;
970 else if (strcmp(str, "digital") == 0)
971 temp = QPNP_WLED_DIM_DIGITAL;
972 else
973 temp = QPNP_WLED_DIM_HYBRID;
974
975 if (temp == wled->dim_mode)
976 return count;
977
978 rc = qpnp_wled_read_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), &reg);
979 if (rc < 0)
980 return rc;
981
982 if (temp == QPNP_WLED_DIM_HYBRID) {
983 reg &= QPNP_WLED_DIM_HYB_MASK;
984 reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
985 } else {
986 reg &= QPNP_WLED_DIM_HYB_MASK;
987 reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
988 reg &= QPNP_WLED_DIM_ANA_MASK;
989 reg |= temp;
990 }
991
992 rc = qpnp_wled_write_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), reg);
993 if (rc)
994 return rc;
995
996 wled->dim_mode = temp;
997
998 return count;
999}
1000
1001/* sysfs show function for full scale current in ua*/
1002static ssize_t qpnp_wled_fs_curr_ua_show(struct device *dev,
1003 struct device_attribute *attr, char *buf)
1004{
1005 struct qpnp_wled *wled = dev_get_drvdata(dev);
1006
1007 return snprintf(buf, PAGE_SIZE, "%d\n", wled->fs_curr_ua);
1008}
1009
1010/* sysfs store function for full scale current in ua*/
1011static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev,
1012 struct device_attribute *attr, const char *buf, size_t count)
1013{
1014 struct qpnp_wled *wled = dev_get_drvdata(dev);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301015 int data, i, rc;
David Collins8885f792017-01-26 14:36:34 -08001016 u8 reg;
1017
1018 rc = kstrtoint(buf, 10, &data);
1019 if (rc)
1020 return rc;
1021
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301022 for (i = 0; i < wled->max_strings; i++) {
David Collins8885f792017-01-26 14:36:34 -08001023 if (data < QPNP_WLED_FS_CURR_MIN_UA)
1024 data = QPNP_WLED_FS_CURR_MIN_UA;
1025 else if (data > QPNP_WLED_FS_CURR_MAX_UA)
1026 data = QPNP_WLED_FS_CURR_MAX_UA;
1027
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301028 reg = data / QPNP_WLED_FS_CURR_STEP_UA;
1029 rc = qpnp_wled_masked_write_reg(wled,
1030 QPNP_WLED_FS_CURR_REG(wled->sink_base, i),
1031 QPNP_WLED_FS_CURR_MASK, reg);
David Collins8885f792017-01-26 14:36:34 -08001032 if (rc < 0)
1033 return rc;
David Collins8885f792017-01-26 14:36:34 -08001034 }
1035
1036 wled->fs_curr_ua = data;
1037
1038 rc = qpnp_wled_sync_reg_toggle(wled);
1039 if (rc < 0) {
1040 dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
1041 return rc;
1042 }
1043
1044 return count;
1045}
1046
1047/* sysfs attributes exported by wled */
1048static struct device_attribute qpnp_wled_attrs[] = {
1049 __ATTR(dump_regs, 0664, qpnp_wled_dump_regs_show, NULL),
1050 __ATTR(dim_mode, 0664, qpnp_wled_dim_mode_show,
1051 qpnp_wled_dim_mode_store),
1052 __ATTR(fs_curr_ua, 0664, qpnp_wled_fs_curr_ua_show,
1053 qpnp_wled_fs_curr_ua_store),
1054 __ATTR(start_ramp, 0664, NULL, qpnp_wled_ramp_store),
1055 __ATTR(ramp_ms, 0664, qpnp_wled_ramp_ms_show, qpnp_wled_ramp_ms_store),
1056 __ATTR(ramp_step, 0664, qpnp_wled_ramp_step_show,
1057 qpnp_wled_ramp_step_store),
1058};
1059
1060/* worker for setting wled brightness */
1061static void qpnp_wled_work(struct work_struct *work)
1062{
1063 struct qpnp_wled *wled;
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08001064 int level, level_255, rc;
David Collins8885f792017-01-26 14:36:34 -08001065
1066 wled = container_of(work, struct qpnp_wled, work);
1067
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08001068 mutex_lock(&wled->lock);
David Collins8885f792017-01-26 14:36:34 -08001069 level = wled->cdev.brightness;
1070
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08001071 if (wled->brt_map_table) {
1072 /*
1073 * Change the 12 bit level to 8 bit level and use the mapped
1074 * values for 12 bit level from brightness map table.
1075 */
1076 level_255 = DIV_ROUND_CLOSEST(level, 16);
1077 if (level_255 > 255)
1078 level_255 = 255;
David Collins8885f792017-01-26 14:36:34 -08001079
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08001080 pr_debug("level: %d level_255: %d\n", level, level_255);
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07001081 if (wled->stepper_en)
1082 rc = qpnp_wled_set_step_level(wled, level_255);
1083 else
1084 rc = qpnp_wled_set_map_level(wled, level_255);
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08001085 if (rc) {
1086 dev_err(&wled->pdev->dev, "wled set level failed\n");
1087 goto unlock_mutex;
1088 }
1089 wled->prev_level = level_255;
1090 } else if (level) {
David Collins8885f792017-01-26 14:36:34 -08001091 rc = qpnp_wled_set_level(wled, level);
1092 if (rc) {
1093 dev_err(&wled->pdev->dev, "wled set level failed\n");
1094 goto unlock_mutex;
1095 }
1096 }
1097
1098 if (!!level != wled->prev_state) {
1099 if (!!level) {
1100 /*
1101 * For AMOLED display in pmi8998, SWIRE_AVDD_DEFAULT has
1102 * to be reconfigured every time the module is enabled.
1103 */
1104 rc = qpnp_wled_swire_avdd_config(wled);
1105 if (rc < 0) {
1106 pr_err("Write to SWIRE_AVDD_DEFAULT register failed rc:%d\n",
1107 rc);
1108 goto unlock_mutex;
1109 }
1110 }
1111
1112 rc = qpnp_wled_module_en(wled, wled->ctrl_base, !!level);
1113 if (rc) {
1114 dev_err(&wled->pdev->dev, "wled %sable failed\n",
1115 level ? "en" : "dis");
1116 goto unlock_mutex;
1117 }
1118 }
1119
1120 wled->prev_state = !!level;
1121unlock_mutex:
1122 mutex_unlock(&wled->lock);
1123}
1124
1125/* get api registered with led classdev for wled brightness */
1126static enum led_brightness qpnp_wled_get(struct led_classdev *led_cdev)
1127{
1128 struct qpnp_wled *wled;
1129
1130 wled = container_of(led_cdev, struct qpnp_wled, cdev);
1131
1132 return wled->cdev.brightness;
1133}
1134
1135/* set api registered with led classdev for wled brightness */
1136static void qpnp_wled_set(struct led_classdev *led_cdev,
1137 enum led_brightness level)
1138{
1139 struct qpnp_wled *wled;
1140
1141 wled = container_of(led_cdev, struct qpnp_wled, cdev);
1142
1143 if (level < LED_OFF)
1144 level = LED_OFF;
1145 else if (level > wled->cdev.max_brightness)
1146 level = wled->cdev.max_brightness;
1147
1148 wled->cdev.brightness = level;
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07001149 queue_work(wled->wq, &wled->work);
David Collins8885f792017-01-26 14:36:34 -08001150}
1151
1152static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
1153{
1154 int rc;
1155 u8 reg;
1156
1157 /* display type */
1158 rc = qpnp_wled_read_reg(wled, QPNP_WLED_DISP_SEL_REG(base_addr), &reg);
1159 if (rc < 0)
1160 return rc;
1161
1162 reg &= QPNP_WLED_DISP_SEL_MASK;
1163 reg |= (wled->disp_type_amoled << QPNP_WLED_DISP_SEL_SHIFT);
1164
1165 rc = qpnp_wled_sec_write_reg(wled, QPNP_WLED_DISP_SEL_REG(base_addr),
1166 reg);
1167 if (rc)
1168 return rc;
1169
1170 if (wled->disp_type_amoled) {
1171 /* Configure the PSM CTRL register for AMOLED */
1172 if (wled->vref_psm_mv < QPNP_WLED_VREF_PSM_MIN_MV)
1173 wled->vref_psm_mv = QPNP_WLED_VREF_PSM_MIN_MV;
1174 else if (wled->vref_psm_mv > QPNP_WLED_VREF_PSM_MAX_MV)
1175 wled->vref_psm_mv = QPNP_WLED_VREF_PSM_MAX_MV;
1176
1177 rc = qpnp_wled_read_reg(wled,
1178 QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), &reg);
1179 if (rc < 0)
1180 return rc;
1181
1182 reg &= QPNP_WLED_VREF_PSM_MASK;
1183 reg |= ((wled->vref_psm_mv - QPNP_WLED_VREF_PSM_MIN_MV)/
1184 QPNP_WLED_VREF_PSM_STEP_MV);
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -08001185 reg |= QPNP_WLED_PSM_OVERWRITE_BIT;
David Collins8885f792017-01-26 14:36:34 -08001186 rc = qpnp_wled_write_reg(wled,
1187 QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), reg);
1188 if (rc)
1189 return rc;
1190
1191 /* Configure the VLOOP COMP RES register for AMOLED */
1192 if (wled->loop_comp_res_kohm < QPNP_WLED_LOOP_COMP_RES_MIN_KOHM)
1193 wled->loop_comp_res_kohm =
1194 QPNP_WLED_LOOP_COMP_RES_MIN_KOHM;
1195 else if (wled->loop_comp_res_kohm >
1196 QPNP_WLED_LOOP_COMP_RES_MAX_KOHM)
1197 wled->loop_comp_res_kohm =
1198 QPNP_WLED_LOOP_COMP_RES_MAX_KOHM;
1199
1200 rc = qpnp_wled_read_reg(wled,
1201 QPNP_WLED_VLOOP_COMP_RES_REG(wled->ctrl_base),
1202 &reg);
1203 if (rc < 0)
1204 return rc;
1205
1206 reg &= QPNP_WLED_VLOOP_COMP_RES_MASK;
1207 reg |= ((wled->loop_comp_res_kohm -
1208 QPNP_WLED_LOOP_COMP_RES_MIN_KOHM)/
1209 QPNP_WLED_LOOP_COMP_RES_STEP_KOHM);
1210 reg |= QPNP_WLED_VLOOP_COMP_RES_OVERWRITE;
1211 rc = qpnp_wled_write_reg(wled,
1212 QPNP_WLED_VLOOP_COMP_RES_REG(wled->ctrl_base),
1213 reg);
1214 if (rc)
1215 return rc;
1216
1217 /* Configure the CTRL TEST4 register for AMOLED */
1218 rc = qpnp_wled_read_reg(wled,
1219 QPNP_WLED_TEST4_REG(wled->ctrl_base), &reg);
1220 if (rc < 0)
1221 return rc;
1222
1223 reg |= QPNP_WLED_TEST4_EN_IIND_UP;
1224 rc = qpnp_wled_sec_write_reg(wled,
1225 QPNP_WLED_TEST4_REG(base_addr), reg);
1226 if (rc)
1227 return rc;
1228 } else {
1229 /*
1230 * enable VREF_UP to avoid false ovp on low brightness for LCD
1231 */
1232 reg = QPNP_WLED_TEST4_EN_VREF_UP
1233 | QPNP_WLED_TEST4_EN_DEB_BYPASS_ILIM_BIT;
1234 rc = qpnp_wled_sec_write_reg(wled,
1235 QPNP_WLED_TEST4_REG(base_addr), reg);
1236 if (rc)
1237 return rc;
1238 }
1239
1240 return 0;
1241}
1242
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001243#define AUTO_CALIB_BRIGHTNESS 200
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301244static int wled_auto_calibrate(struct qpnp_wled *wled)
1245{
1246 int rc = 0, i;
1247 u8 reg = 0, sink_config = 0, sink_test = 0, sink_valid = 0, int_sts;
1248
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301249 /* read configured sink configuration */
1250 rc = qpnp_wled_read_reg(wled,
1251 QPNP_WLED_CURR_SINK_REG(wled->sink_base), &sink_config);
1252 if (rc < 0) {
1253 pr_err("Failed to read SINK configuration rc=%d\n", rc);
1254 goto failed_calib;
1255 }
1256
1257 /* disable the module before starting calibration */
1258 rc = qpnp_wled_masked_write_reg(wled,
1259 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
1260 QPNP_WLED_MODULE_EN_MASK, 0);
1261 if (rc < 0) {
1262 pr_err("Failed to disable WLED module rc=%d\n", rc);
1263 goto failed_calib;
1264 }
1265
1266 /* set low brightness across all sinks */
1267 rc = qpnp_wled_set_level(wled, AUTO_CALIB_BRIGHTNESS);
1268 if (rc < 0) {
1269 pr_err("Failed to set brightness for calibration rc=%d\n", rc);
1270 goto failed_calib;
1271 }
1272
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001273 if (wled->en_cabc) {
1274 for (i = 0; i < wled->max_strings; i++) {
1275 reg = 0;
1276 rc = qpnp_wled_masked_write_reg(wled,
1277 QPNP_WLED_CABC_REG(wled->sink_base, i),
1278 QPNP_WLED_CABC_MASK, reg);
1279 if (rc < 0)
1280 goto failed_calib;
1281 }
1282 }
1283
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301284 /* disable all sinks */
1285 rc = qpnp_wled_write_reg(wled,
1286 QPNP_WLED_CURR_SINK_REG(wled->sink_base), 0);
1287 if (rc < 0) {
1288 pr_err("Failed to disable all sinks rc=%d\n", rc);
1289 goto failed_calib;
1290 }
1291
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301292 /* iterate through the strings one by one */
1293 for (i = 0; i < wled->max_strings; i++) {
1294 sink_test = 1 << (QPNP_WLED_CURR_SINK_SHIFT + i);
1295
1296 /* Enable feedback control */
1297 rc = qpnp_wled_write_reg(wled,
1298 QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
1299 i + 1);
1300 if (rc < 0) {
1301 pr_err("Failed to enable feedback for SINK %d rc = %d\n",
1302 i + 1, rc);
1303 goto failed_calib;
1304 }
1305
1306 /* enable the sink */
1307 rc = qpnp_wled_write_reg(wled,
1308 QPNP_WLED_CURR_SINK_REG(wled->sink_base), sink_test);
1309 if (rc < 0) {
1310 pr_err("Failed to configure SINK %d rc=%d\n",
1311 i + 1, rc);
1312 goto failed_calib;
1313 }
1314
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001315 /* Enable the module */
1316 rc = qpnp_wled_masked_write_reg(wled,
1317 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
1318 QPNP_WLED_MODULE_EN_MASK, QPNP_WLED_MODULE_EN_MASK);
1319 if (rc < 0) {
1320 pr_err("Failed to enable WLED module rc=%d\n", rc);
1321 goto failed_calib;
1322 }
1323
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301324 /* delay for WLED soft-start */
1325 usleep_range(QPNP_WLED_SOFT_START_DLY_US,
1326 QPNP_WLED_SOFT_START_DLY_US + 1000);
1327
1328 rc = qpnp_wled_read_reg(wled,
1329 QPNP_WLED_INT_RT_STS(wled->ctrl_base), &int_sts);
1330 if (rc < 0) {
1331 pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc);
1332 goto failed_calib;
1333 }
1334
1335 if (int_sts & QPNP_WLED_OVP_FAULT_BIT)
1336 pr_debug("WLED OVP fault detected with SINK %d\n",
1337 i + 1);
1338 else
1339 sink_valid |= sink_test;
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001340
1341 /* Disable the module */
1342 rc = qpnp_wled_masked_write_reg(wled,
1343 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
1344 QPNP_WLED_MODULE_EN_MASK, 0);
1345 if (rc < 0) {
1346 pr_err("Failed to disable WLED module rc=%d\n", rc);
1347 goto failed_calib;
1348 }
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301349 }
1350
1351 if (sink_valid == sink_config) {
1352 pr_debug("WLED auto-calibration complete, default sink-config=%x OK!\n",
1353 sink_config);
1354 } else {
1355 pr_warn("Invalid WLED default sink config=%x changing it to=%x\n",
1356 sink_config, sink_valid);
1357 sink_config = sink_valid;
1358 }
1359
1360 if (!sink_config) {
1361 pr_warn("No valid WLED sinks found\n");
Subbaraman Narayanamurthy15624d92017-10-12 21:04:30 -07001362 wled->module_dis_perm = true;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301363 goto failed_calib;
1364 }
1365
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301366 /* write the new sink configuration */
1367 rc = qpnp_wled_write_reg(wled,
1368 QPNP_WLED_CURR_SINK_REG(wled->sink_base), sink_config);
1369 if (rc < 0) {
1370 pr_err("Failed to reconfigure the default sink rc=%d\n", rc);
1371 goto failed_calib;
1372 }
1373
1374 /* MODULATOR_EN setting for valid sinks */
1375 for (i = 0; i < wled->max_strings; i++) {
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001376 if (wled->en_cabc) {
1377 reg = 1 << QPNP_WLED_CABC_SHIFT;
1378 rc = qpnp_wled_masked_write_reg(wled,
1379 QPNP_WLED_CABC_REG(wled->sink_base, i),
1380 QPNP_WLED_CABC_MASK, reg);
1381 if (rc < 0)
1382 goto failed_calib;
1383 }
1384
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301385 if (sink_config & (1 << (QPNP_WLED_CURR_SINK_SHIFT + i)))
1386 reg = (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
1387 else
1388 reg = 0x0; /* disable modulator_en for unused sink */
1389
1390 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID)
1391 reg &= QPNP_WLED_GATE_DRV_MASK;
1392 else
1393 reg |= ~QPNP_WLED_GATE_DRV_MASK;
1394
1395 rc = qpnp_wled_write_reg(wled,
1396 QPNP_WLED_MOD_EN_REG(wled->sink_base, i), reg);
1397 if (rc < 0) {
1398 pr_err("Failed to configure MODULATOR_EN rc=%d\n", rc);
1399 goto failed_calib;
1400 }
1401 }
1402
1403 /* restore the feedback setting */
1404 rc = qpnp_wled_write_reg(wled,
1405 QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
1406 wled->fdbk_op);
1407 if (rc < 0) {
1408 pr_err("Failed to restore feedback setting rc=%d\n", rc);
1409 goto failed_calib;
1410 }
1411
1412 /* restore brightness */
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05301413 rc = qpnp_wled_set_level(wled, !wled->cdev.brightness ?
1414 AUTO_CALIB_BRIGHTNESS : wled->cdev.brightness);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301415 if (rc < 0) {
1416 pr_err("Failed to set brightness after calibration rc=%d\n",
1417 rc);
1418 goto failed_calib;
1419 }
1420
1421 rc = qpnp_wled_masked_write_reg(wled,
1422 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
1423 QPNP_WLED_MODULE_EN_MASK,
1424 QPNP_WLED_MODULE_EN_MASK);
1425 if (rc < 0) {
1426 pr_err("Failed to enable WLED module rc=%d\n", rc);
1427 goto failed_calib;
1428 }
1429
1430 /* delay for WLED soft-start */
1431 usleep_range(QPNP_WLED_SOFT_START_DLY_US,
1432 QPNP_WLED_SOFT_START_DLY_US + 1000);
1433
1434failed_calib:
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301435 return rc;
1436}
1437
1438#define WLED_AUTO_CAL_OVP_COUNT 5
1439#define WLED_AUTO_CAL_CNT_DLY_US 1000000 /* 1 second */
1440static bool qpnp_wled_auto_cal_required(struct qpnp_wled *wled)
1441{
1442 s64 elapsed_time_us;
1443
1444 /*
1445 * Check if the OVP fault was an occasional one
1446 * or if its firing continuously, the latter qualifies
1447 * for an auto-calibration check.
1448 */
1449 if (!wled->auto_calibration_ovp_count) {
1450 wled->start_ovp_fault_time = ktime_get();
1451 wled->auto_calibration_ovp_count++;
1452 } else {
1453 elapsed_time_us = ktime_us_delta(ktime_get(),
1454 wled->start_ovp_fault_time);
1455 if (elapsed_time_us > WLED_AUTO_CAL_CNT_DLY_US)
1456 wled->auto_calibration_ovp_count = 0;
1457 else
1458 wled->auto_calibration_ovp_count++;
1459
1460 if (wled->auto_calibration_ovp_count >=
1461 WLED_AUTO_CAL_OVP_COUNT) {
1462 wled->auto_calibration_ovp_count = 0;
1463 return true;
1464 }
1465 }
1466
1467 return false;
1468}
1469
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05301470static int qpnp_wled_auto_calibrate_at_init(struct qpnp_wled *wled)
1471{
1472 int rc;
1473 u8 fault_status = 0, rt_status = 0;
1474
1475 if (!wled->auto_calib_enabled)
1476 return 0;
1477
1478 rc = qpnp_wled_read_reg(wled,
1479 QPNP_WLED_INT_RT_STS(wled->ctrl_base), &rt_status);
1480 if (rc < 0)
1481 pr_err("Failed to read RT status rc=%d\n", rc);
1482
1483 rc = qpnp_wled_read_reg(wled,
1484 QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_status);
1485 if (rc < 0)
1486 pr_err("Failed to read fault status rc=%d\n", rc);
1487
1488 if ((rt_status & QPNP_WLED_OVP_FLT_RT_STS_BIT) ||
1489 (fault_status & QPNP_WLED_OVP_FAULT_BIT)) {
1490 mutex_lock(&wled->lock);
1491 rc = wled_auto_calibrate(wled);
1492 if (rc < 0)
1493 pr_err("Failed auto-calibration rc=%d\n", rc);
1494 else
1495 wled->auto_calib_done = true;
1496 mutex_unlock(&wled->lock);
1497 }
1498
1499 return rc;
1500}
1501
David Collins8885f792017-01-26 14:36:34 -08001502/* ovp irq handler */
1503static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled)
1504{
1505 struct qpnp_wled *wled = _wled;
1506 int rc;
Subbaraman Narayanamurthyb5ae3182017-02-17 15:14:06 -08001507 u8 fault_sts, int_sts;
David Collins8885f792017-01-26 14:36:34 -08001508
1509 rc = qpnp_wled_read_reg(wled,
Subbaraman Narayanamurthyb5ae3182017-02-17 15:14:06 -08001510 QPNP_WLED_INT_RT_STS(wled->ctrl_base), &int_sts);
1511 if (rc < 0) {
1512 pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc);
1513 return IRQ_HANDLED;
1514 }
1515
1516 rc = qpnp_wled_read_reg(wled,
1517 QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_sts);
David Collins8885f792017-01-26 14:36:34 -08001518 if (rc < 0) {
1519 pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc);
1520 return IRQ_HANDLED;
1521 }
1522
Subbaraman Narayanamurthyb5ae3182017-02-17 15:14:06 -08001523 if (fault_sts & (QPNP_WLED_OVP_FAULT_BIT | QPNP_WLED_ILIM_FAULT_BIT))
1524 pr_err("WLED OVP fault detected, int_sts=%x fault_sts= %x\n",
1525 int_sts, fault_sts);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301526
1527 if (fault_sts & QPNP_WLED_OVP_FAULT_BIT) {
1528 if (wled->auto_calib_enabled && !wled->auto_calib_done) {
1529 if (qpnp_wled_auto_cal_required(wled)) {
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05301530 mutex_lock(&wled->lock);
1531 if (wled->ovp_irq > 0 &&
1532 !wled->ovp_irq_disabled) {
1533 disable_irq_nosync(wled->ovp_irq);
1534 wled->ovp_irq_disabled = true;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301535 }
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05301536
1537 rc = wled_auto_calibrate(wled);
1538 if (rc < 0)
1539 pr_err("Failed auto-calibration rc=%d\n",
1540 rc);
1541 else
1542 wled->auto_calib_done = true;
1543
1544 if (wled->ovp_irq > 0 &&
1545 wled->ovp_irq_disabled) {
1546 enable_irq(wled->ovp_irq);
1547 wled->ovp_irq_disabled = false;
1548 }
1549 mutex_unlock(&wled->lock);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301550 }
1551 }
1552 }
1553
David Collins8885f792017-01-26 14:36:34 -08001554 return IRQ_HANDLED;
1555}
1556
1557/* short circuit irq handler */
1558static irqreturn_t qpnp_wled_sc_irq_handler(int irq, void *_wled)
1559{
1560 struct qpnp_wled *wled = _wled;
1561 int rc;
1562 u8 val;
1563
1564 rc = qpnp_wled_read_reg(wled,
1565 QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &val);
1566 if (rc < 0) {
1567 pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc);
1568 return IRQ_HANDLED;
1569 }
1570
1571 pr_err("WLED short circuit detected %d times fault_status=%x\n",
1572 ++wled->sc_cnt, val);
1573 mutex_lock(&wled->lock);
1574 qpnp_wled_module_en(wled, wled->ctrl_base, false);
1575 msleep(QPNP_WLED_SC_DLY_MS);
1576 qpnp_wled_module_en(wled, wled->ctrl_base, true);
1577 mutex_unlock(&wled->lock);
1578
1579 return IRQ_HANDLED;
1580}
1581
1582static bool is_avdd_trim_adjustment_required(struct qpnp_wled *wled)
1583{
1584 int rc;
1585 u8 reg = 0;
1586
1587 /*
1588 * AVDD trim adjustment is not required for pmi8998/pm660l and not
1589 * supported for pmi8994.
1590 */
1591 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1592 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE ||
1593 wled->pmic_rev_id->pmic_subtype == PMI8994_SUBTYPE)
1594 return false;
1595
1596 /*
1597 * Configure TRIM_REG only if disp_type_amoled and it has
1598 * not already been programmed by bootloader.
1599 */
1600 if (!wled->disp_type_amoled)
1601 return false;
1602
1603 rc = qpnp_wled_read_reg(wled,
1604 QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base), &reg);
1605 if (rc < 0)
1606 return false;
1607
1608 return !(reg & QPNP_WLED_AVDD_SET_BIT);
1609}
1610
1611static int qpnp_wled_gm_config(struct qpnp_wled *wled)
1612{
1613 int rc;
1614 u8 mask = 0, reg = 0;
1615
1616 /* Configure the LOOP COMP GM register */
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07001617 if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1618 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)) {
1619 if (wled->disp_type_amoled) {
1620 reg = 0;
1621 mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
1622 QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
1623 } else {
1624 if (wled->loop_auto_gm_en)
1625 reg |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN;
David Collins8885f792017-01-26 14:36:34 -08001626
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07001627 if (wled->loop_auto_gm_thresh >
1628 QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX)
1629 wled->loop_auto_gm_thresh =
1630 QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX;
David Collins8885f792017-01-26 14:36:34 -08001631
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07001632 reg |= wled->loop_auto_gm_thresh <<
1633 QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT;
1634 mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
1635 QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
1636 }
David Collins8885f792017-01-26 14:36:34 -08001637 }
1638
1639 if (wled->loop_ea_gm < QPNP_WLED_LOOP_EA_GM_MIN)
1640 wled->loop_ea_gm = QPNP_WLED_LOOP_EA_GM_MIN;
1641 else if (wled->loop_ea_gm > QPNP_WLED_LOOP_EA_GM_MAX)
1642 wled->loop_ea_gm = QPNP_WLED_LOOP_EA_GM_MAX;
1643
1644 reg |= wled->loop_ea_gm | QPNP_WLED_VLOOP_COMP_GM_OVERWRITE;
1645 mask |= QPNP_WLED_VLOOP_COMP_GM_MASK |
1646 QPNP_WLED_VLOOP_COMP_GM_OVERWRITE;
1647
1648 rc = qpnp_wled_masked_write_reg(wled,
1649 QPNP_WLED_VLOOP_COMP_GM_REG(wled->ctrl_base), mask,
1650 reg);
1651 if (rc)
1652 pr_err("write VLOOP_COMP_GM_REG failed, rc=%d]\n", rc);
1653
1654 return rc;
1655}
1656
1657static int qpnp_wled_ovp_config(struct qpnp_wled *wled)
1658{
1659 int rc, i, *ovp_table;
1660 u8 reg;
1661
1662 /*
1663 * Configure the OVP register based on ovp_mv only if display type is
1664 * not AMOLED.
1665 */
1666 if (wled->disp_type_amoled)
1667 return 0;
1668
1669 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1670 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
1671 ovp_table = qpnp_wled_ovp_thresholds_pmi8998;
1672 else
1673 ovp_table = qpnp_wled_ovp_thresholds_pmi8994;
1674
1675 for (i = 0; i < NUM_SUPPORTED_OVP_THRESHOLDS; i++) {
1676 if (wled->ovp_mv == ovp_table[i])
1677 break;
1678 }
1679
1680 if (i == NUM_SUPPORTED_OVP_THRESHOLDS) {
1681 dev_err(&wled->pdev->dev,
1682 "Invalid ovp threshold specified in device tree\n");
1683 return -EINVAL;
1684 }
1685
1686 reg = i & QPNP_WLED_OVP_MASK;
1687 rc = qpnp_wled_masked_write_reg(wled,
1688 QPNP_WLED_OVP_REG(wled->ctrl_base),
1689 QPNP_WLED_OVP_MASK, reg);
1690 if (rc)
1691 return rc;
1692
1693 return 0;
1694}
1695
1696static int qpnp_wled_avdd_trim_config(struct qpnp_wled *wled)
1697{
1698 int rc, i;
1699 u8 reg;
1700
1701 for (i = 0; i < NUM_SUPPORTED_AVDD_VOLTAGES; i++) {
1702 if (wled->avdd_target_voltage_mv ==
1703 qpnp_wled_avdd_target_voltages[i])
1704 break;
1705 }
1706
1707 if (i == NUM_SUPPORTED_AVDD_VOLTAGES) {
1708 dev_err(&wled->pdev->dev,
1709 "Invalid avdd target voltage specified in device tree\n");
1710 return -EINVAL;
1711 }
1712
1713 /* Update WLED_OVP register based on desired target voltage */
1714 reg = qpnp_wled_ovp_reg_settings[i];
1715 rc = qpnp_wled_masked_write_reg(wled,
1716 QPNP_WLED_OVP_REG(wled->ctrl_base),
1717 QPNP_WLED_OVP_MASK, reg);
1718 if (rc)
1719 return rc;
1720
1721 /* Update WLED_TRIM register based on desired target voltage */
1722 rc = qpnp_wled_read_reg(wled,
1723 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), &reg);
1724 if (rc)
1725 return rc;
1726
1727 reg += qpnp_wled_avdd_trim_adjustments[i];
1728 if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL ||
1729 (s8)reg > QPNP_WLED_AVDD_MAX_TRIM_VAL) {
1730 dev_dbg(&wled->pdev->dev,
1731 "adjusted trim %d is not within range, capping it\n",
1732 (s8)reg);
1733 if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL)
1734 reg = QPNP_WLED_AVDD_MIN_TRIM_VAL;
1735 else
1736 reg = QPNP_WLED_AVDD_MAX_TRIM_VAL;
1737 }
1738
1739 reg &= QPNP_WLED_7P7_TRIM_MASK;
1740 rc = qpnp_wled_sec_write_reg(wled,
1741 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), reg);
1742 if (rc < 0)
1743 dev_err(&wled->pdev->dev, "Write to 7P7_TRIM register failed, rc=%d\n",
1744 rc);
1745 return rc;
1746}
1747
1748static int qpnp_wled_avdd_mode_config(struct qpnp_wled *wled)
1749{
1750 int rc;
1751 u8 reg = 0;
1752
1753 /*
1754 * At present, configuring the mode to SPMI/SWIRE for controlling
1755 * AVDD voltage is available only in pmi8998/pm660l.
1756 */
1757 if (wled->pmic_rev_id->pmic_subtype != PMI8998_SUBTYPE &&
1758 wled->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
1759 return 0;
1760
1761 /* AMOLED_VOUT should be configured for AMOLED */
1762 if (!wled->disp_type_amoled)
1763 return 0;
1764
1765 /* Configure avdd register */
1766 if (wled->avdd_target_voltage_mv > QPNP_WLED_AVDD_MAX_MV) {
1767 dev_dbg(&wled->pdev->dev, "Capping avdd target voltage to %d\n",
1768 QPNP_WLED_AVDD_MAX_MV);
1769 wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MAX_MV;
1770 } else if (wled->avdd_target_voltage_mv < QPNP_WLED_AVDD_MIN_MV) {
1771 dev_info(&wled->pdev->dev, "Capping avdd target voltage to %d\n",
1772 QPNP_WLED_AVDD_MIN_MV);
1773 wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MIN_MV;
1774 }
1775
1776 if (wled->avdd_mode_spmi) {
1777 reg = QPNP_WLED_AVDD_MV_TO_REG(wled->avdd_target_voltage_mv);
1778 reg |= QPNP_WLED_AVDD_SEL_SPMI_BIT;
1779 rc = qpnp_wled_write_reg(wled,
1780 QPNP_WLED_AMOLED_VOUT_REG(wled->ctrl_base),
1781 reg);
1782 if (rc < 0)
1783 pr_err("Write to AMOLED_VOUT register failed, rc=%d\n",
1784 rc);
1785 } else {
1786 rc = qpnp_wled_swire_avdd_config(wled);
1787 if (rc < 0)
1788 pr_err("Write to SWIRE_AVDD_DEFAULT register failed rc:%d\n",
1789 rc);
1790 }
1791
1792 return rc;
1793}
1794
1795static int qpnp_wled_ilim_config(struct qpnp_wled *wled)
1796{
1797 int rc, i, *ilim_table;
1798 u8 reg;
1799
1800 if (wled->ilim_ma < PMI8994_WLED_ILIM_MIN_MA)
1801 wled->ilim_ma = PMI8994_WLED_ILIM_MIN_MA;
1802
1803 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1804 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
1805 ilim_table = qpnp_wled_ilim_settings_pmi8998;
1806 if (wled->ilim_ma > PMI8998_WLED_ILIM_MAX_MA)
1807 wled->ilim_ma = PMI8998_WLED_ILIM_MAX_MA;
1808 } else {
1809 ilim_table = qpnp_wled_ilim_settings_pmi8994;
1810 if (wled->ilim_ma > PMI8994_WLED_ILIM_MAX_MA)
1811 wled->ilim_ma = PMI8994_WLED_ILIM_MAX_MA;
1812 }
1813
1814 for (i = 0; i < NUM_SUPPORTED_ILIM_THRESHOLDS; i++) {
1815 if (wled->ilim_ma == ilim_table[i])
1816 break;
1817 }
1818
1819 if (i == NUM_SUPPORTED_ILIM_THRESHOLDS) {
1820 dev_err(&wled->pdev->dev,
1821 "Invalid ilim threshold specified in device tree\n");
1822 return -EINVAL;
1823 }
1824
1825 reg = (i & QPNP_WLED_ILIM_MASK) | QPNP_WLED_ILIM_OVERWRITE;
1826 rc = qpnp_wled_masked_write_reg(wled,
1827 QPNP_WLED_ILIM_REG(wled->ctrl_base),
1828 QPNP_WLED_ILIM_MASK | QPNP_WLED_ILIM_OVERWRITE, reg);
1829 if (rc < 0)
1830 dev_err(&wled->pdev->dev, "Write to ILIM register failed, rc=%d\n",
1831 rc);
1832 return rc;
1833}
1834
1835static int qpnp_wled_vref_config(struct qpnp_wled *wled)
1836{
1837
1838 struct wled_vref_setting vref_setting;
1839 int rc;
1840 u8 reg = 0;
1841
1842 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1843 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
1844 vref_setting = vref_setting_pmi8998;
1845 else
1846 vref_setting = vref_setting_pmi8994;
1847
1848 if (wled->vref_uv < vref_setting.min_uv)
1849 wled->vref_uv = vref_setting.min_uv;
1850 else if (wled->vref_uv > vref_setting.max_uv)
1851 wled->vref_uv = vref_setting.max_uv;
1852
1853 reg |= DIV_ROUND_CLOSEST(wled->vref_uv - vref_setting.min_uv,
1854 vref_setting.step_uv);
1855
1856 rc = qpnp_wled_masked_write_reg(wled,
1857 QPNP_WLED_VREF_REG(wled->ctrl_base),
1858 QPNP_WLED_VREF_MASK, reg);
1859 if (rc)
1860 pr_err("Write VREF_REG failed, rc=%d\n", rc);
1861
1862 return rc;
1863}
1864
1865/* Configure WLED registers */
1866static int qpnp_wled_config(struct qpnp_wled *wled)
1867{
1868 int rc, i, temp;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301869 u8 reg = 0, sink_en = 0, mask;
David Collins8885f792017-01-26 14:36:34 -08001870
1871 /* Configure display type */
1872 rc = qpnp_wled_set_disp(wled, wled->ctrl_base);
1873 if (rc < 0)
1874 return rc;
1875
1876 /* Configure the FEEDBACK OUTPUT register */
1877 rc = qpnp_wled_read_reg(wled, QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
1878 &reg);
1879 if (rc < 0)
1880 return rc;
1881 reg &= QPNP_WLED_FDBK_OP_MASK;
1882 reg |= wled->fdbk_op;
1883 rc = qpnp_wled_write_reg(wled, QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
1884 reg);
1885 if (rc)
1886 return rc;
1887
1888 /* Configure the VREF register */
1889 rc = qpnp_wled_vref_config(wled);
1890 if (rc < 0) {
1891 pr_err("Error in configuring wled vref, rc=%d\n", rc);
1892 return rc;
1893 }
1894
1895 /* Configure VLOOP_COMP_GM register */
1896 rc = qpnp_wled_gm_config(wled);
1897 if (rc < 0) {
1898 pr_err("Error in configureing wled gm, rc=%d\n", rc);
1899 return rc;
1900 }
1901
1902 /* Configure the ILIM register */
1903 rc = qpnp_wled_ilim_config(wled);
1904 if (rc < 0) {
1905 pr_err("Error in configuring wled ilim, rc=%d\n", rc);
1906 return rc;
1907 }
1908
1909 /* Configure auto PFM mode for LCD mode only */
1910 if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1911 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
1912 && !wled->disp_type_amoled) {
1913 reg = 0;
1914 reg |= wled->lcd_auto_pfm_thresh;
1915 reg |= wled->lcd_auto_pfm_en <<
1916 QPNP_WLED_LCD_AUTO_PFM_EN_SHIFT;
1917 rc = qpnp_wled_masked_write_reg(wled,
1918 QPNP_WLED_LCD_AUTO_PFM_REG(wled->ctrl_base),
1919 QPNP_WLED_LCD_AUTO_PFM_EN_BIT |
1920 QPNP_WLED_LCD_AUTO_PFM_THRESH_MASK, reg);
1921 if (rc < 0) {
1922 pr_err("Write LCD_AUTO_PFM failed, rc=%d\n", rc);
1923 return rc;
1924 }
1925 }
1926
1927 /* Configure the Soft start Ramp delay: for AMOLED - 0,for LCD - 2 */
1928 reg = (wled->disp_type_amoled) ? 0 : 2;
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07001929 mask = SOFTSTART_RAMP_DELAY_MASK;
1930 if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1931 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
1932 && wled->disp_type_amoled) {
1933 reg |= SOFTSTART_OVERWRITE_BIT;
1934 mask |= SOFTSTART_OVERWRITE_BIT;
1935 }
1936
1937 rc = qpnp_wled_masked_write_reg(wled,
1938 QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base),
1939 mask, reg);
David Collins8885f792017-01-26 14:36:34 -08001940 if (rc)
1941 return rc;
1942
1943 /* Configure the MAX BOOST DUTY register */
1944 if (wled->boost_duty_ns < QPNP_WLED_BOOST_DUTY_MIN_NS)
1945 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
1946 else if (wled->boost_duty_ns > QPNP_WLED_BOOST_DUTY_MAX_NS)
1947 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MAX_NS;
1948
1949 rc = qpnp_wled_read_reg(wled, QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base),
1950 &reg);
1951 if (rc < 0)
1952 return rc;
1953 reg &= QPNP_WLED_BOOST_DUTY_MASK;
1954 reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS);
1955 rc = qpnp_wled_write_reg(wled,
1956 QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base), reg);
1957 if (rc)
1958 return rc;
1959
1960 /* Configure the SWITCHING FREQ register */
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -07001961 if (wled->switch_freq_khz == 1600)
1962 reg = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
David Collins8885f792017-01-26 14:36:34 -08001963 else
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -07001964 reg = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
David Collins8885f792017-01-26 14:36:34 -08001965
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -07001966 /*
1967 * Do not set the overwrite bit when switching frequency is selected
1968 * for AMOLED. This register is in logic reset block which can cause
1969 * the value to be overwritten during module enable/disable.
1970 */
1971 mask = QPNP_WLED_SWITCH_FREQ_MASK | QPNP_WLED_SWITCH_FREQ_OVERWRITE;
1972 if (!wled->disp_type_amoled)
1973 reg |= QPNP_WLED_SWITCH_FREQ_OVERWRITE;
1974
1975 rc = qpnp_wled_masked_write_reg(wled,
1976 QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), mask, reg);
David Collins8885f792017-01-26 14:36:34 -08001977 if (rc < 0)
1978 return rc;
David Collins8885f792017-01-26 14:36:34 -08001979
1980 rc = qpnp_wled_ovp_config(wled);
1981 if (rc < 0) {
1982 pr_err("Error in configuring OVP threshold, rc=%d\n", rc);
1983 return rc;
1984 }
1985
1986 if (is_avdd_trim_adjustment_required(wled)) {
1987 rc = qpnp_wled_avdd_trim_config(wled);
1988 if (rc < 0)
1989 return rc;
1990 }
1991
1992 rc = qpnp_wled_avdd_mode_config(wled);
1993 if (rc < 0)
1994 return rc;
1995
1996 /* Configure the MODULATION register */
1997 if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_1200_KHZ) {
1998 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_1200_KHZ;
1999 temp = 3;
2000 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_2400_KHZ) {
2001 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_2400_KHZ;
2002 temp = 2;
2003 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_9600_KHZ) {
2004 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
2005 temp = 1;
2006 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_19200_KHZ) {
2007 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
2008 temp = 0;
2009 } else {
2010 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
2011 temp = 1;
2012 }
2013
2014 rc = qpnp_wled_read_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), &reg);
2015 if (rc < 0)
2016 return rc;
2017 reg &= QPNP_WLED_MOD_FREQ_MASK;
2018 reg |= (temp << QPNP_WLED_MOD_FREQ_SHIFT);
2019
2020 reg &= QPNP_WLED_PHASE_STAG_MASK;
2021 reg |= (wled->en_phase_stag << QPNP_WLED_PHASE_STAG_SHIFT);
2022
2023 reg &= QPNP_WLED_ACC_CLK_FREQ_MASK;
2024 reg |= (temp << QPNP_WLED_ACC_CLK_FREQ_SHIFT);
2025
2026 reg &= QPNP_WLED_DIM_RES_MASK;
2027 reg |= (wled->en_9b_dim_res << QPNP_WLED_DIM_RES_SHIFT);
2028
2029 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
2030 reg &= QPNP_WLED_DIM_HYB_MASK;
2031 reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
2032 } else {
2033 reg &= QPNP_WLED_DIM_HYB_MASK;
2034 reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
2035 reg &= QPNP_WLED_DIM_ANA_MASK;
2036 reg |= wled->dim_mode;
2037 }
2038
2039 rc = qpnp_wled_write_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), reg);
2040 if (rc)
2041 return rc;
2042
2043 /* Configure the HYBRID THRESHOLD register */
2044 if (wled->hyb_thres < QPNP_WLED_HYB_THRES_MIN)
2045 wled->hyb_thres = QPNP_WLED_HYB_THRES_MIN;
2046 else if (wled->hyb_thres > QPNP_WLED_HYB_THRES_MAX)
2047 wled->hyb_thres = QPNP_WLED_HYB_THRES_MAX;
2048
2049 rc = qpnp_wled_read_reg(wled, QPNP_WLED_HYB_THRES_REG(wled->sink_base),
2050 &reg);
2051 if (rc < 0)
2052 return rc;
2053 reg &= QPNP_WLED_HYB_THRES_MASK;
2054 temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1;
2055 reg |= temp;
2056 rc = qpnp_wled_write_reg(wled, QPNP_WLED_HYB_THRES_REG(wled->sink_base),
2057 reg);
2058 if (rc)
2059 return rc;
2060
2061 /* Configure TEST5 register */
2062 if (wled->dim_mode == QPNP_WLED_DIM_DIGITAL) {
2063 reg = QPNP_WLED_SINK_TEST5_DIG;
2064 } else {
2065 reg = QPNP_WLED_SINK_TEST5_HYB;
2066 if (wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2067 reg |= QPNP_WLED_SINK_TEST5_HVG_PULL_STR_BIT;
2068 }
2069
2070 rc = qpnp_wled_sec_write_reg(wled,
2071 QPNP_WLED_SINK_TEST5_REG(wled->sink_base), reg);
2072 if (rc)
2073 return rc;
2074
2075 /* disable all current sinks and enable selected strings */
2076 reg = 0x00;
2077 rc = qpnp_wled_write_reg(wled, QPNP_WLED_CURR_SINK_REG(wled->sink_base),
2078 reg);
2079
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302080 for (i = 0; i < wled->max_strings; i++) {
2081 /* SYNC DELAY */
2082 if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US)
2083 wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US;
2084
2085 reg = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
2086 mask = QPNP_WLED_SYNC_DLY_MASK;
2087 rc = qpnp_wled_masked_write_reg(wled,
2088 QPNP_WLED_SYNC_DLY_REG(wled->sink_base, i),
2089 mask, reg);
2090 if (rc < 0)
2091 return rc;
2092
2093 /* FULL SCALE CURRENT */
2094 if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA)
2095 wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
2096
2097 reg = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
2098 mask = QPNP_WLED_FS_CURR_MASK;
2099 rc = qpnp_wled_masked_write_reg(wled,
2100 QPNP_WLED_FS_CURR_REG(wled->sink_base, i),
2101 mask, reg);
2102 if (rc < 0)
2103 return rc;
2104
2105 /* CABC */
2106 reg = wled->en_cabc ? (1 << QPNP_WLED_CABC_SHIFT) : 0;
2107 mask = QPNP_WLED_CABC_MASK;
2108 rc = qpnp_wled_masked_write_reg(wled,
2109 QPNP_WLED_CABC_REG(wled->sink_base, i),
2110 mask, reg);
2111 if (rc < 0)
2112 return rc;
2113 }
2114
2115 /* Settings specific to valid sinks */
David Collins8885f792017-01-26 14:36:34 -08002116 for (i = 0; i < wled->num_strings; i++) {
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302117 if (wled->strings[i] >= wled->max_strings) {
David Collins8885f792017-01-26 14:36:34 -08002118 dev_err(&wled->pdev->dev, "Invalid string number\n");
2119 return -EINVAL;
2120 }
David Collins8885f792017-01-26 14:36:34 -08002121 /* MODULATOR */
2122 rc = qpnp_wled_read_reg(wled,
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302123 QPNP_WLED_MOD_EN_REG(wled->sink_base, i), &reg);
David Collins8885f792017-01-26 14:36:34 -08002124 if (rc < 0)
2125 return rc;
2126 reg &= QPNP_WLED_MOD_EN_MASK;
2127 reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
2128
2129 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID)
2130 reg &= QPNP_WLED_GATE_DRV_MASK;
2131 else
2132 reg |= ~QPNP_WLED_GATE_DRV_MASK;
2133
2134 rc = qpnp_wled_write_reg(wled,
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302135 QPNP_WLED_MOD_EN_REG(wled->sink_base, i), reg);
David Collins8885f792017-01-26 14:36:34 -08002136 if (rc)
2137 return rc;
2138
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302139 /* SINK EN */
David Collins8885f792017-01-26 14:36:34 -08002140 temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302141 sink_en |= (1 << temp);
2142 }
2143 mask = QPNP_WLED_CURR_SINK_MASK;
2144 rc = qpnp_wled_masked_write_reg(wled,
2145 QPNP_WLED_CURR_SINK_REG(wled->sink_base),
2146 mask, sink_en);
2147 if (rc < 0) {
2148 dev_err(&wled->pdev->dev,
2149 "Failed to enable WLED sink config rc = %d\n", rc);
2150 return rc;
David Collins8885f792017-01-26 14:36:34 -08002151 }
2152
2153 rc = qpnp_wled_sync_reg_toggle(wled);
2154 if (rc < 0) {
2155 dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
2156 return rc;
2157 }
2158
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05302159 rc = qpnp_wled_auto_calibrate_at_init(wled);
2160 if (rc < 0)
2161 pr_err("Failed to auto-calibrate at init rc=%d\n", rc);
2162
David Collins8885f792017-01-26 14:36:34 -08002163 /* setup ovp and sc irqs */
2164 if (wled->ovp_irq >= 0) {
2165 rc = devm_request_threaded_irq(&wled->pdev->dev, wled->ovp_irq,
2166 NULL, qpnp_wled_ovp_irq_handler, IRQF_ONESHOT,
2167 "qpnp_wled_ovp_irq", wled);
2168 if (rc < 0) {
2169 dev_err(&wled->pdev->dev,
2170 "Unable to request ovp(%d) IRQ(err:%d)\n",
2171 wled->ovp_irq, rc);
2172 return rc;
2173 }
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302174 rc = qpnp_wled_read_reg(wled,
2175 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base), &reg);
2176 /* disable the OVP irq only if the module is not enabled */
2177 if (!rc && !(reg & QPNP_WLED_MODULE_EN_MASK)) {
2178 disable_irq(wled->ovp_irq);
2179 wled->ovp_irq_disabled = true;
2180 }
David Collins8885f792017-01-26 14:36:34 -08002181 }
2182
2183 if (wled->sc_irq >= 0) {
2184 wled->sc_cnt = 0;
2185 rc = devm_request_threaded_irq(&wled->pdev->dev, wled->sc_irq,
2186 NULL, qpnp_wled_sc_irq_handler, IRQF_ONESHOT,
2187 "qpnp_wled_sc_irq", wled);
2188 if (rc < 0) {
2189 dev_err(&wled->pdev->dev,
2190 "Unable to request sc(%d) IRQ(err:%d)\n",
2191 wled->sc_irq, rc);
2192 return rc;
2193 }
2194
2195 rc = qpnp_wled_read_reg(wled,
2196 QPNP_WLED_SC_PRO_REG(wled->ctrl_base), &reg);
2197 if (rc < 0)
2198 return rc;
2199 reg &= QPNP_WLED_EN_SC_DEB_CYCLES_MASK;
2200 reg |= 1 << QPNP_WLED_EN_SC_SHIFT;
2201
2202 if (wled->sc_deb_cycles < QPNP_WLED_SC_DEB_CYCLES_MIN)
2203 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MIN;
2204 else if (wled->sc_deb_cycles > QPNP_WLED_SC_DEB_CYCLES_MAX)
2205 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MAX;
2206 temp = fls(wled->sc_deb_cycles) - QPNP_WLED_SC_DEB_CYCLES_SUB;
2207 reg |= (temp << 1);
2208
2209 if (wled->disp_type_amoled)
2210 reg |= QPNP_WLED_SC_PRO_EN_DSCHGR;
2211
2212 rc = qpnp_wled_write_reg(wled,
2213 QPNP_WLED_SC_PRO_REG(wled->ctrl_base), reg);
2214 if (rc)
2215 return rc;
2216
2217 if (wled->en_ext_pfet_sc_pro) {
Subbaraman Narayanamurthy50dae7a2018-05-18 15:34:19 -07002218 if (!(wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE
2219 && wled->pmic_rev_id->rev4 ==
2220 PMI8998_V2P0_REV4)) {
2221 reg = QPNP_WLED_EXT_FET_DTEST2;
2222 rc = qpnp_wled_sec_write_reg(wled,
David Collins8885f792017-01-26 14:36:34 -08002223 QPNP_WLED_TEST1_REG(wled->ctrl_base),
2224 reg);
Subbaraman Narayanamurthy50dae7a2018-05-18 15:34:19 -07002225 if (rc)
2226 return rc;
2227 }
David Collins8885f792017-01-26 14:36:34 -08002228 }
2229 } else {
2230 rc = qpnp_wled_read_reg(wled,
2231 QPNP_WLED_SC_PRO_REG(wled->ctrl_base), &reg);
2232 if (rc < 0)
2233 return rc;
2234 reg &= QPNP_WLED_EN_DEB_CYCLES_MASK;
2235
2236 if (wled->sc_deb_cycles < QPNP_WLED_SC_DEB_CYCLES_MIN)
2237 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MIN;
2238 else if (wled->sc_deb_cycles > QPNP_WLED_SC_DEB_CYCLES_MAX)
2239 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MAX;
2240 temp = fls(wled->sc_deb_cycles) - QPNP_WLED_SC_DEB_CYCLES_SUB;
2241 reg |= (temp << 1);
2242
2243 rc = qpnp_wled_write_reg(wled,
2244 QPNP_WLED_SC_PRO_REG(wled->ctrl_base), reg);
2245 if (rc)
2246 return rc;
2247 }
2248
2249 return 0;
2250}
2251
2252/* parse wled dtsi parameters */
2253static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
2254{
2255 struct platform_device *pdev = wled->pdev;
2256 struct property *prop;
2257 const char *temp_str;
2258 u32 temp_val;
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08002259 int rc, i, size;
David Collins8885f792017-01-26 14:36:34 -08002260 u8 *strings;
2261
2262 wled->cdev.name = "wled";
2263 rc = of_property_read_string(pdev->dev.of_node,
2264 "linux,name", &wled->cdev.name);
2265 if (rc && (rc != -EINVAL)) {
2266 dev_err(&pdev->dev, "Unable to read led name\n");
2267 return rc;
2268 }
2269
2270 wled->cdev.default_trigger = QPNP_WLED_TRIGGER_NONE;
2271 rc = of_property_read_string(pdev->dev.of_node, "linux,default-trigger",
2272 &wled->cdev.default_trigger);
2273 if (rc && (rc != -EINVAL)) {
2274 dev_err(&pdev->dev, "Unable to read led trigger\n");
2275 return rc;
2276 }
2277
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08002278 if (of_find_property(pdev->dev.of_node, "qcom,wled-brightness-map",
2279 NULL)) {
2280 size = of_property_count_elems_of_size(pdev->dev.of_node,
2281 "qcom,wled-brightness-map", sizeof(u16));
2282 if (size != NUM_DDIC_CODES) {
2283 pr_err("Invalid WLED brightness map size:%d\n", size);
2284 return rc;
2285 }
2286
2287 wled->brt_map_table = devm_kcalloc(&pdev->dev, NUM_DDIC_CODES,
2288 sizeof(u16), GFP_KERNEL);
2289 if (!wled->brt_map_table)
2290 return -ENOMEM;
2291
2292 rc = of_property_read_u16_array(pdev->dev.of_node,
2293 "qcom,wled-brightness-map", wled->brt_map_table,
2294 NUM_DDIC_CODES);
2295 if (rc < 0) {
2296 pr_err("Error in reading WLED brightness map, rc=%d\n",
2297 rc);
2298 return rc;
2299 }
2300
2301 for (i = 0; i < NUM_DDIC_CODES; i++) {
2302 if (wled->brt_map_table[i] > WLED_MAX_LEVEL_4095) {
2303 pr_err("WLED brightness map not in range\n");
2304 return -EDOM;
2305 }
2306
2307 if ((i > 1) && wled->brt_map_table[i]
2308 < wled->brt_map_table[i - 1]) {
2309 pr_err("WLED brightness map not in ascending order?\n");
2310 return -EDOM;
2311 }
2312 }
2313 }
2314
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07002315 wled->stepper_en = of_property_read_bool(pdev->dev.of_node,
2316 "qcom,wled-stepper-en");
David Collins8885f792017-01-26 14:36:34 -08002317 wled->disp_type_amoled = of_property_read_bool(pdev->dev.of_node,
2318 "qcom,disp-type-amoled");
2319 if (wled->disp_type_amoled) {
2320 wled->vref_psm_mv = QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV;
2321 rc = of_property_read_u32(pdev->dev.of_node,
2322 "qcom,vref-psm-mv", &temp_val);
2323 if (!rc) {
2324 wled->vref_psm_mv = temp_val;
2325 } else if (rc != -EINVAL) {
2326 dev_err(&pdev->dev, "Unable to read vref-psm\n");
2327 return rc;
2328 }
2329
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07002330 wled->loop_comp_res_kohm = 320;
2331 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2332 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2333 wled->loop_comp_res_kohm = 300;
2334
David Collins8885f792017-01-26 14:36:34 -08002335 rc = of_property_read_u32(pdev->dev.of_node,
2336 "qcom,loop-comp-res-kohm", &temp_val);
2337 if (!rc) {
2338 wled->loop_comp_res_kohm = temp_val;
2339 } else if (rc != -EINVAL) {
2340 dev_err(&pdev->dev, "Unable to read loop-comp-res-kohm\n");
2341 return rc;
2342 }
2343
2344 wled->avdd_mode_spmi = of_property_read_bool(pdev->dev.of_node,
2345 "qcom,avdd-mode-spmi");
2346
2347 wled->avdd_target_voltage_mv = QPNP_WLED_DFLT_AVDD_MV;
2348 rc = of_property_read_u32(pdev->dev.of_node,
2349 "qcom,avdd-target-voltage-mv", &temp_val);
2350 if (!rc) {
2351 wled->avdd_target_voltage_mv = temp_val;
2352 } else if (rc != -EINVAL) {
2353 dev_err(&pdev->dev, "Unable to read avdd target voltage\n");
2354 return rc;
2355 }
2356 }
2357
2358 if (wled->disp_type_amoled) {
2359 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2360 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2361 wled->loop_ea_gm =
2362 QPNP_WLED_LOOP_GM_DFLT_AMOLED_PMI8998;
2363 else
2364 wled->loop_ea_gm =
2365 QPNP_WLED_LOOP_EA_GM_DFLT_AMOLED_PMI8994;
2366 } else {
2367 wled->loop_ea_gm = QPNP_WLED_LOOP_GM_DFLT_WLED;
2368 }
2369
2370 rc = of_property_read_u32(pdev->dev.of_node,
2371 "qcom,loop-ea-gm", &temp_val);
2372 if (!rc) {
2373 wled->loop_ea_gm = temp_val;
2374 } else if (rc != -EINVAL) {
2375 dev_err(&pdev->dev, "Unable to read loop-ea-gm\n");
2376 return rc;
2377 }
2378
2379 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2380 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
2381 wled->loop_auto_gm_en =
2382 of_property_read_bool(pdev->dev.of_node,
2383 "qcom,loop-auto-gm-en");
2384 wled->loop_auto_gm_thresh = QPNP_WLED_LOOP_AUTO_GM_DFLT_THRESH;
2385 rc = of_property_read_u8(pdev->dev.of_node,
2386 "qcom,loop-auto-gm-thresh",
2387 &wled->loop_auto_gm_thresh);
2388 if (rc && rc != -EINVAL) {
2389 dev_err(&pdev->dev,
2390 "Unable to read loop-auto-gm-thresh\n");
2391 return rc;
2392 }
2393 }
2394
2395 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2396 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
2397
Anirudh Ghayal39526a22017-10-11 19:15:24 +05302398 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE &&
2399 wled->pmic_rev_id->rev4 == PMI8998_V2P0_REV4)
David Collins8885f792017-01-26 14:36:34 -08002400 wled->lcd_auto_pfm_en = false;
2401 else
2402 wled->lcd_auto_pfm_en = true;
2403
2404 wled->lcd_auto_pfm_thresh = QPNP_WLED_LCD_AUTO_PFM_DFLT_THRESH;
2405 rc = of_property_read_u8(pdev->dev.of_node,
2406 "qcom,lcd-auto-pfm-thresh",
2407 &wled->lcd_auto_pfm_thresh);
2408 if (rc && rc != -EINVAL) {
2409 dev_err(&pdev->dev,
2410 "Unable to read lcd-auto-pfm-thresh\n");
2411 return rc;
2412 }
2413
2414 if (wled->lcd_auto_pfm_thresh >
2415 QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX)
2416 wled->lcd_auto_pfm_thresh =
2417 QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX;
2418 }
2419
2420 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_DFLT;
2421 rc = of_property_read_u32(pdev->dev.of_node,
2422 "qcom,sc-deb-cycles", &temp_val);
2423 if (!rc) {
2424 wled->sc_deb_cycles = temp_val;
2425 } else if (rc != -EINVAL) {
2426 dev_err(&pdev->dev, "Unable to read sc debounce cycles\n");
2427 return rc;
2428 }
2429
2430 wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
2431 rc = of_property_read_string(pdev->dev.of_node,
2432 "qcom,fdbk-output", &temp_str);
2433 if (!rc) {
2434 if (strcmp(temp_str, "wled1") == 0)
2435 wled->fdbk_op = QPNP_WLED_FDBK_WLED1;
2436 else if (strcmp(temp_str, "wled2") == 0)
2437 wled->fdbk_op = QPNP_WLED_FDBK_WLED2;
2438 else if (strcmp(temp_str, "wled3") == 0)
2439 wled->fdbk_op = QPNP_WLED_FDBK_WLED3;
2440 else if (strcmp(temp_str, "wled4") == 0)
2441 wled->fdbk_op = QPNP_WLED_FDBK_WLED4;
2442 else
2443 wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
2444 } else if (rc != -EINVAL) {
2445 dev_err(&pdev->dev, "Unable to read feedback output\n");
2446 return rc;
2447 }
2448
2449 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2450 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2451 wled->vref_uv = vref_setting_pmi8998.default_uv;
2452 else
2453 wled->vref_uv = vref_setting_pmi8994.default_uv;
2454 rc = of_property_read_u32(pdev->dev.of_node,
2455 "qcom,vref-uv", &temp_val);
2456 if (!rc) {
2457 wled->vref_uv = temp_val;
2458 } else if (rc != -EINVAL) {
2459 dev_err(&pdev->dev, "Unable to read vref\n");
2460 return rc;
2461 }
2462
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -07002463 wled->switch_freq_khz = wled->disp_type_amoled ? 1600 : 800;
David Collins8885f792017-01-26 14:36:34 -08002464 rc = of_property_read_u32(pdev->dev.of_node,
2465 "qcom,switch-freq-khz", &temp_val);
2466 if (!rc) {
2467 wled->switch_freq_khz = temp_val;
2468 } else if (rc != -EINVAL) {
2469 dev_err(&pdev->dev, "Unable to read switch freq\n");
2470 return rc;
2471 }
2472
2473 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2474 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2475 wled->ovp_mv = 29600;
2476 else
2477 wled->ovp_mv = 29500;
2478 rc = of_property_read_u32(pdev->dev.of_node,
2479 "qcom,ovp-mv", &temp_val);
2480 if (!rc) {
2481 wled->ovp_mv = temp_val;
2482 } else if (rc != -EINVAL) {
2483 dev_err(&pdev->dev, "Unable to read ovp\n");
2484 return rc;
2485 }
2486
2487 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2488 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
2489 if (wled->disp_type_amoled)
2490 wled->ilim_ma = PMI8998_AMOLED_DFLT_ILIM_MA;
2491 else
2492 wled->ilim_ma = PMI8998_WLED_DFLT_ILIM_MA;
2493 } else {
2494 if (wled->disp_type_amoled)
2495 wled->ilim_ma = PMI8994_AMOLED_DFLT_ILIM_MA;
2496 else
2497 wled->ilim_ma = PMI8994_WLED_DFLT_ILIM_MA;
2498 }
2499
2500 rc = of_property_read_u32(pdev->dev.of_node,
2501 "qcom,ilim-ma", &temp_val);
2502 if (!rc) {
2503 wled->ilim_ma = temp_val;
2504 } else if (rc != -EINVAL) {
2505 dev_err(&pdev->dev, "Unable to read ilim\n");
2506 return rc;
2507 }
2508
2509 wled->boost_duty_ns = QPNP_WLED_DEF_BOOST_DUTY_NS;
2510 rc = of_property_read_u32(pdev->dev.of_node,
2511 "qcom,boost-duty-ns", &temp_val);
2512 if (!rc) {
2513 wled->boost_duty_ns = temp_val;
2514 } else if (rc != -EINVAL) {
2515 dev_err(&pdev->dev, "Unable to read boost duty\n");
2516 return rc;
2517 }
2518
2519 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
2520 rc = of_property_read_u32(pdev->dev.of_node,
2521 "qcom,mod-freq-khz", &temp_val);
2522 if (!rc) {
2523 wled->mod_freq_khz = temp_val;
2524 } else if (rc != -EINVAL) {
2525 dev_err(&pdev->dev, "Unable to read modulation freq\n");
2526 return rc;
2527 }
2528
2529 wled->dim_mode = QPNP_WLED_DIM_HYBRID;
2530 rc = of_property_read_string(pdev->dev.of_node,
2531 "qcom,dim-mode", &temp_str);
2532 if (!rc) {
2533 if (strcmp(temp_str, "analog") == 0)
2534 wled->dim_mode = QPNP_WLED_DIM_ANALOG;
2535 else if (strcmp(temp_str, "digital") == 0)
2536 wled->dim_mode = QPNP_WLED_DIM_DIGITAL;
2537 else
2538 wled->dim_mode = QPNP_WLED_DIM_HYBRID;
2539 } else if (rc != -EINVAL) {
2540 dev_err(&pdev->dev, "Unable to read dim mode\n");
2541 return rc;
2542 }
2543
2544 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
2545 wled->hyb_thres = QPNP_WLED_DEF_HYB_THRES;
2546 rc = of_property_read_u32(pdev->dev.of_node,
2547 "qcom,hyb-thres", &temp_val);
2548 if (!rc) {
2549 wled->hyb_thres = temp_val;
2550 } else if (rc != -EINVAL) {
2551 dev_err(&pdev->dev, "Unable to read hyb threshold\n");
2552 return rc;
2553 }
2554 }
2555
2556 wled->sync_dly_us = QPNP_WLED_DEF_SYNC_DLY_US;
2557 rc = of_property_read_u32(pdev->dev.of_node,
2558 "qcom,sync-dly-us", &temp_val);
2559 if (!rc) {
2560 wled->sync_dly_us = temp_val;
2561 } else if (rc != -EINVAL) {
2562 dev_err(&pdev->dev, "Unable to read sync delay\n");
2563 return rc;
2564 }
2565
2566 wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
2567 rc = of_property_read_u32(pdev->dev.of_node,
2568 "qcom,fs-curr-ua", &temp_val);
2569 if (!rc) {
2570 wled->fs_curr_ua = temp_val;
2571 } else if (rc != -EINVAL) {
2572 dev_err(&pdev->dev, "Unable to read full scale current\n");
2573 return rc;
2574 }
2575
2576 wled->cons_sync_write_delay_us = 0;
2577 rc = of_property_read_u32(pdev->dev.of_node,
2578 "qcom,cons-sync-write-delay-us", &temp_val);
2579 if (!rc)
2580 wled->cons_sync_write_delay_us = temp_val;
2581
2582 wled->en_9b_dim_res = of_property_read_bool(pdev->dev.of_node,
2583 "qcom,en-9b-dim-res");
2584 wled->en_phase_stag = of_property_read_bool(pdev->dev.of_node,
2585 "qcom,en-phase-stag");
2586 wled->en_cabc = of_property_read_bool(pdev->dev.of_node,
2587 "qcom,en-cabc");
2588
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302589 if (wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2590 wled->max_strings = QPNP_PM660_WLED_MAX_STRINGS;
2591 else
2592 wled->max_strings = QPNP_WLED_MAX_STRINGS;
2593
David Collins8885f792017-01-26 14:36:34 -08002594 prop = of_find_property(pdev->dev.of_node,
2595 "qcom,led-strings-list", &temp_val);
2596 if (!prop || !temp_val || temp_val > QPNP_WLED_MAX_STRINGS) {
2597 dev_err(&pdev->dev, "Invalid strings info, use default");
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302598 wled->num_strings = wled->max_strings;
David Collins8885f792017-01-26 14:36:34 -08002599 for (i = 0; i < wled->num_strings; i++)
2600 wled->strings[i] = i;
2601 } else {
2602 wled->num_strings = temp_val;
2603 strings = prop->value;
2604 for (i = 0; i < wled->num_strings; ++i)
2605 wled->strings[i] = strings[i];
2606 }
2607
2608 wled->ovp_irq = platform_get_irq_byname(pdev, "ovp-irq");
2609 if (wled->ovp_irq < 0)
2610 dev_dbg(&pdev->dev, "ovp irq is not used\n");
2611
2612 wled->sc_irq = platform_get_irq_byname(pdev, "sc-irq");
2613 if (wled->sc_irq < 0)
2614 dev_dbg(&pdev->dev, "sc irq is not used\n");
2615
2616 wled->en_ext_pfet_sc_pro = of_property_read_bool(pdev->dev.of_node,
2617 "qcom,en-ext-pfet-sc-pro");
2618
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -08002619 wled->lcd_psm_ctrl = of_property_read_bool(pdev->dev.of_node,
2620 "qcom,lcd-psm-ctrl");
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302621
2622 wled->auto_calib_enabled = of_property_read_bool(pdev->dev.of_node,
2623 "qcom,auto-calibration-enable");
David Collins8885f792017-01-26 14:36:34 -08002624 return 0;
2625}
2626
2627static int qpnp_wled_probe(struct platform_device *pdev)
2628{
2629 struct qpnp_wled *wled;
2630 struct device_node *revid_node;
2631 int rc = 0, i;
2632 const __be32 *prop;
2633
2634 wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL);
2635 if (!wled)
2636 return -ENOMEM;
Stephen Boydd71c7f62017-03-20 15:49:04 -07002637
2638 wled->regmap = dev_get_regmap(pdev->dev.parent, NULL);
2639 if (!wled->regmap) {
2640 dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
2641 return -EINVAL;
2642 }
David Collins8885f792017-01-26 14:36:34 -08002643
2644 wled->pdev = pdev;
2645
2646 revid_node = of_parse_phandle(pdev->dev.of_node, "qcom,pmic-revid", 0);
2647 if (!revid_node) {
2648 pr_err("Missing qcom,pmic-revid property - driver failed\n");
2649 return -EINVAL;
2650 }
2651
2652 wled->pmic_rev_id = get_revid_data(revid_node);
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07002653 of_node_put(revid_node);
David Collins8885f792017-01-26 14:36:34 -08002654 if (IS_ERR_OR_NULL(wled->pmic_rev_id)) {
2655 pr_err("Unable to get pmic_revid rc=%ld\n",
2656 PTR_ERR(wled->pmic_rev_id));
2657 /*
2658 * the revid peripheral must be registered, any failure
2659 * here only indicates that the rev-id module has not
2660 * probed yet.
2661 */
2662 return -EPROBE_DEFER;
2663 }
2664
2665 pr_debug("PMIC subtype %d Digital major %d\n",
2666 wled->pmic_rev_id->pmic_subtype, wled->pmic_rev_id->rev4);
2667
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07002668 wled->wq = alloc_ordered_workqueue("qpnp_wled_wq", WQ_HIGHPRI);
2669 if (!wled->wq) {
2670 pr_err("Unable to alloc workqueue for WLED\n");
2671 return -ENOMEM;
2672 }
2673
David Collins8885f792017-01-26 14:36:34 -08002674 prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_SINK_BASE,
Subbaraman Narayanamurthy8b9dd592017-08-23 18:44:15 -07002675 NULL, NULL);
David Collins8885f792017-01-26 14:36:34 -08002676 if (!prop) {
2677 dev_err(&pdev->dev, "Couldnt find sink's addr rc %d\n", rc);
2678 return rc;
2679 }
2680 wled->sink_base = be32_to_cpu(*prop);
2681
2682 prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_CTRL_BASE,
Subbaraman Narayanamurthy8b9dd592017-08-23 18:44:15 -07002683 NULL, NULL);
David Collins8885f792017-01-26 14:36:34 -08002684 if (!prop) {
2685 dev_err(&pdev->dev, "Couldnt find ctrl's addr rc = %d\n", rc);
2686 return rc;
2687 }
2688 wled->ctrl_base = be32_to_cpu(*prop);
2689
2690 dev_set_drvdata(&pdev->dev, wled);
2691
2692 rc = qpnp_wled_parse_dt(wled);
2693 if (rc) {
2694 dev_err(&pdev->dev, "DT parsing failed\n");
2695 return rc;
2696 }
2697
2698 mutex_init(&wled->bus_lock);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302699 mutex_init(&wled->lock);
David Collins8885f792017-01-26 14:36:34 -08002700 rc = qpnp_wled_config(wled);
2701 if (rc) {
2702 dev_err(&pdev->dev, "wled config failed\n");
2703 return rc;
2704 }
2705
David Collins8885f792017-01-26 14:36:34 -08002706 INIT_WORK(&wled->work, qpnp_wled_work);
2707 wled->ramp_ms = QPNP_WLED_RAMP_DLY_MS;
2708 wled->ramp_step = 1;
2709
2710 wled->cdev.brightness_set = qpnp_wled_set;
2711 wled->cdev.brightness_get = qpnp_wled_get;
2712
2713 wled->cdev.max_brightness = WLED_MAX_LEVEL_4095;
2714
2715 rc = led_classdev_register(&pdev->dev, &wled->cdev);
2716 if (rc) {
2717 dev_err(&pdev->dev, "wled registration failed(%d)\n", rc);
2718 goto wled_register_fail;
2719 }
2720
2721 for (i = 0; i < ARRAY_SIZE(qpnp_wled_attrs); i++) {
2722 rc = sysfs_create_file(&wled->cdev.dev->kobj,
2723 &qpnp_wled_attrs[i].attr);
2724 if (rc < 0) {
2725 dev_err(&pdev->dev, "sysfs creation failed\n");
2726 goto sysfs_fail;
2727 }
2728 }
2729
2730 return 0;
2731
2732sysfs_fail:
2733 for (i--; i >= 0; i--)
2734 sysfs_remove_file(&wled->cdev.dev->kobj,
2735 &qpnp_wled_attrs[i].attr);
2736 led_classdev_unregister(&wled->cdev);
2737wled_register_fail:
2738 cancel_work_sync(&wled->work);
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07002739 destroy_workqueue(wled->wq);
David Collins8885f792017-01-26 14:36:34 -08002740 mutex_destroy(&wled->lock);
2741 return rc;
2742}
2743
2744static int qpnp_wled_remove(struct platform_device *pdev)
2745{
2746 struct qpnp_wled *wled = dev_get_drvdata(&pdev->dev);
2747 int i;
2748
2749 for (i = 0; i < ARRAY_SIZE(qpnp_wled_attrs); i++)
2750 sysfs_remove_file(&wled->cdev.dev->kobj,
2751 &qpnp_wled_attrs[i].attr);
2752
2753 led_classdev_unregister(&wled->cdev);
2754 cancel_work_sync(&wled->work);
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07002755 destroy_workqueue(wled->wq);
David Collins8885f792017-01-26 14:36:34 -08002756 mutex_destroy(&wled->lock);
2757
2758 return 0;
2759}
2760
2761static const struct of_device_id spmi_match_table[] = {
2762 { .compatible = "qcom,qpnp-wled",},
2763 { },
2764};
2765
2766static struct platform_driver qpnp_wled_driver = {
2767 .driver = {
2768 .name = "qcom,qpnp-wled",
2769 .of_match_table = spmi_match_table,
2770 },
2771 .probe = qpnp_wled_probe,
2772 .remove = qpnp_wled_remove,
2773};
2774
2775static int __init qpnp_wled_init(void)
2776{
2777 return platform_driver_register(&qpnp_wled_driver);
2778}
Subbaraman Narayanamurthy67e30fb2017-05-01 16:29:34 -07002779subsys_initcall(qpnp_wled_init);
David Collins8885f792017-01-26 14:36:34 -08002780
2781static void __exit qpnp_wled_exit(void)
2782{
2783 platform_driver_unregister(&qpnp_wled_driver);
2784}
2785module_exit(qpnp_wled_exit);
2786
2787MODULE_DESCRIPTION("QPNP WLED driver");
2788MODULE_LICENSE("GPL v2");
2789MODULE_ALIAS("leds:leds-qpnp-wled");