blob: 18a11633ff8f95c329f5a03249993e17e63866f9 [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>
Kiran Gundae4648712018-07-19 15:02:59 +053025#include <linux/irq.h>
David Collins8885f792017-01-26 14:36:34 -080026#include <linux/err.h>
27#include <linux/delay.h>
28#include <linux/leds-qpnp-wled.h>
29#include <linux/qpnp/qpnp-revid.h>
30
31/* base addresses */
32#define QPNP_WLED_CTRL_BASE "qpnp-wled-ctrl-base"
33#define QPNP_WLED_SINK_BASE "qpnp-wled-sink-base"
34
35/* ctrl registers */
36#define QPNP_WLED_FAULT_STATUS(b) (b + 0x08)
Subbaraman Narayanamurthyb5ae3182017-02-17 15:14:06 -080037#define QPNP_WLED_INT_RT_STS(b) (b + 0x10)
David Collins8885f792017-01-26 14:36:34 -080038#define QPNP_WLED_EN_REG(b) (b + 0x46)
39#define QPNP_WLED_FDBK_OP_REG(b) (b + 0x48)
40#define QPNP_WLED_VREF_REG(b) (b + 0x49)
41#define QPNP_WLED_BOOST_DUTY_REG(b) (b + 0x4B)
42#define QPNP_WLED_SWITCH_FREQ_REG(b) (b + 0x4C)
43#define QPNP_WLED_OVP_REG(b) (b + 0x4D)
44#define QPNP_WLED_ILIM_REG(b) (b + 0x4E)
45#define QPNP_WLED_AMOLED_VOUT_REG(b) (b + 0x4F)
46#define QPNP_WLED_SOFTSTART_RAMP_DLY(b) (b + 0x53)
47#define QPNP_WLED_VLOOP_COMP_RES_REG(b) (b + 0x55)
48#define QPNP_WLED_VLOOP_COMP_GM_REG(b) (b + 0x56)
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -080049#define QPNP_WLED_EN_PSM_REG(b) (b + 0x5A)
David Collins8885f792017-01-26 14:36:34 -080050#define QPNP_WLED_PSM_CTRL_REG(b) (b + 0x5B)
51#define QPNP_WLED_LCD_AUTO_PFM_REG(b) (b + 0x5C)
52#define QPNP_WLED_SC_PRO_REG(b) (b + 0x5E)
53#define QPNP_WLED_SWIRE_AVDD_REG(b) (b + 0x5F)
54#define QPNP_WLED_CTRL_SPARE_REG(b) (b + 0xDF)
55#define QPNP_WLED_TEST1_REG(b) (b + 0xE2)
56#define QPNP_WLED_TEST4_REG(b) (b + 0xE5)
57#define QPNP_WLED_REF_7P7_TRIM_REG(b) (b + 0xF2)
58
59#define QPNP_WLED_7P7_TRIM_MASK GENMASK(3, 0)
60#define QPNP_WLED_EN_MASK 0x7F
61#define QPNP_WLED_EN_SHIFT 7
62#define QPNP_WLED_FDBK_OP_MASK 0xF8
63#define QPNP_WLED_VREF_MASK GENMASK(3, 0)
64
65#define QPNP_WLED_VLOOP_COMP_RES_MASK 0xF0
66#define QPNP_WLED_VLOOP_COMP_RES_OVERWRITE 0x80
David Collins8885f792017-01-26 14:36:34 -080067#define QPNP_WLED_LOOP_COMP_RES_STEP_KOHM 20
68#define QPNP_WLED_LOOP_COMP_RES_MIN_KOHM 20
69#define QPNP_WLED_LOOP_COMP_RES_MAX_KOHM 320
70#define QPNP_WLED_VLOOP_COMP_GM_MASK GENMASK(3, 0)
71#define QPNP_WLED_VLOOP_COMP_GM_OVERWRITE 0x80
72#define QPNP_WLED_VLOOP_COMP_AUTO_GM_EN BIT(6)
73#define QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK GENMASK(5, 4)
74#define QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT 4
75#define QPNP_WLED_LOOP_EA_GM_DFLT_AMOLED_PMI8994 0x03
76#define QPNP_WLED_LOOP_GM_DFLT_AMOLED_PMI8998 0x09
77#define QPNP_WLED_LOOP_GM_DFLT_WLED 0x09
78#define QPNP_WLED_LOOP_EA_GM_MIN 0x0
79#define QPNP_WLED_LOOP_EA_GM_MAX 0xF
80#define QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX 3
81#define QPNP_WLED_LOOP_AUTO_GM_DFLT_THRESH 1
82#define QPNP_WLED_VREF_PSM_MASK 0xF8
83#define QPNP_WLED_VREF_PSM_STEP_MV 50
84#define QPNP_WLED_VREF_PSM_MIN_MV 400
85#define QPNP_WLED_VREF_PSM_MAX_MV 750
86#define QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV 450
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -080087#define QPNP_WLED_PSM_OVERWRITE_BIT BIT(7)
David Collins8885f792017-01-26 14:36:34 -080088#define QPNP_WLED_LCD_AUTO_PFM_DFLT_THRESH 1
89#define QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX 0xF
90#define QPNP_WLED_LCD_AUTO_PFM_EN_SHIFT 7
91#define QPNP_WLED_LCD_AUTO_PFM_EN_BIT BIT(7)
92#define QPNP_WLED_LCD_AUTO_PFM_THRESH_MASK GENMASK(3, 0)
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -080093#define QPNP_WLED_EN_PSM_BIT BIT(7)
David Collins8885f792017-01-26 14:36:34 -080094
95#define QPNP_WLED_ILIM_MASK GENMASK(2, 0)
96#define QPNP_WLED_ILIM_OVERWRITE BIT(7)
97#define PMI8994_WLED_ILIM_MIN_MA 105
98#define PMI8994_WLED_ILIM_MAX_MA 1980
99#define PMI8994_WLED_DFLT_ILIM_MA 980
100#define PMI8994_AMOLED_DFLT_ILIM_MA 385
101#define PMI8998_WLED_ILIM_MAX_MA 1500
102#define PMI8998_WLED_DFLT_ILIM_MA 970
103#define PMI8998_AMOLED_DFLT_ILIM_MA 620
104#define QPNP_WLED_BOOST_DUTY_MASK 0xFC
105#define QPNP_WLED_BOOST_DUTY_STEP_NS 52
106#define QPNP_WLED_BOOST_DUTY_MIN_NS 26
107#define QPNP_WLED_BOOST_DUTY_MAX_NS 156
108#define QPNP_WLED_DEF_BOOST_DUTY_NS 104
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -0700109#define QPNP_WLED_SWITCH_FREQ_MASK GENMASK(3, 0)
110#define QPNP_WLED_SWITCH_FREQ_OVERWRITE BIT(7)
David Collins8885f792017-01-26 14:36:34 -0800111#define QPNP_WLED_OVP_MASK GENMASK(1, 0)
112#define QPNP_WLED_TEST4_EN_DEB_BYPASS_ILIM_BIT BIT(6)
113#define QPNP_WLED_TEST4_EN_SH_FOR_SS_BIT BIT(5)
114#define QPNP_WLED_TEST4_EN_CLAMP_BIT BIT(4)
115#define QPNP_WLED_TEST4_EN_SOFT_START_BIT BIT(1)
116#define QPNP_WLED_TEST4_EN_VREF_UP \
117 (QPNP_WLED_TEST4_EN_SH_FOR_SS_BIT | \
118 QPNP_WLED_TEST4_EN_CLAMP_BIT | \
119 QPNP_WLED_TEST4_EN_SOFT_START_BIT)
120#define QPNP_WLED_TEST4_EN_IIND_UP 0x1
Subbaraman Narayanamurthyb5ae3182017-02-17 15:14:06 -0800121#define QPNP_WLED_ILIM_FAULT_BIT BIT(0)
122#define QPNP_WLED_OVP_FAULT_BIT BIT(1)
123#define QPNP_WLED_SC_FAULT_BIT BIT(2)
Anirudh Ghayalce9914f2017-08-31 12:11:06 +0530124#define QPNP_WLED_OVP_FLT_RT_STS_BIT BIT(1)
David Collins8885f792017-01-26 14:36:34 -0800125
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -0700126/* QPNP_WLED_SOFTSTART_RAMP_DLY */
127#define SOFTSTART_OVERWRITE_BIT BIT(7)
128#define SOFTSTART_RAMP_DELAY_MASK GENMASK(2, 0)
129
David Collins8885f792017-01-26 14:36:34 -0800130/* sink registers */
131#define QPNP_WLED_CURR_SINK_REG(b) (b + 0x46)
132#define QPNP_WLED_SYNC_REG(b) (b + 0x47)
133#define QPNP_WLED_MOD_REG(b) (b + 0x4A)
134#define QPNP_WLED_HYB_THRES_REG(b) (b + 0x4B)
135#define QPNP_WLED_MOD_EN_REG(b, n) (b + 0x50 + (n * 0x10))
136#define QPNP_WLED_SYNC_DLY_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x01)
137#define QPNP_WLED_FS_CURR_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x02)
138#define QPNP_WLED_CABC_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x06)
139#define QPNP_WLED_BRIGHT_LSB_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x07)
140#define QPNP_WLED_BRIGHT_MSB_REG(b, n) (QPNP_WLED_MOD_EN_REG(b, n) + 0x08)
141#define QPNP_WLED_SINK_TEST5_REG(b) (b + 0xE6)
142
143#define QPNP_WLED_MOD_FREQ_1200_KHZ 1200
144#define QPNP_WLED_MOD_FREQ_2400_KHZ 2400
145#define QPNP_WLED_MOD_FREQ_9600_KHZ 9600
146#define QPNP_WLED_MOD_FREQ_19200_KHZ 19200
147#define QPNP_WLED_MOD_FREQ_MASK 0x3F
148#define QPNP_WLED_MOD_FREQ_SHIFT 6
149#define QPNP_WLED_ACC_CLK_FREQ_MASK 0xE7
150#define QPNP_WLED_ACC_CLK_FREQ_SHIFT 3
151#define QPNP_WLED_PHASE_STAG_MASK 0xDF
152#define QPNP_WLED_PHASE_STAG_SHIFT 5
153#define QPNP_WLED_DIM_RES_MASK 0xFD
154#define QPNP_WLED_DIM_RES_SHIFT 1
155#define QPNP_WLED_DIM_HYB_MASK 0xFB
156#define QPNP_WLED_DIM_HYB_SHIFT 2
157#define QPNP_WLED_DIM_ANA_MASK 0xFE
158#define QPNP_WLED_HYB_THRES_MASK 0xF8
159#define QPNP_WLED_HYB_THRES_MIN 78
160#define QPNP_WLED_DEF_HYB_THRES 625
161#define QPNP_WLED_HYB_THRES_MAX 10000
162#define QPNP_WLED_MOD_EN_MASK 0x7F
163#define QPNP_WLED_MOD_EN_SHFT 7
164#define QPNP_WLED_MOD_EN 1
165#define QPNP_WLED_GATE_DRV_MASK 0xFE
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530166#define QPNP_WLED_SYNC_DLY_MASK GENMASK(2, 0)
David Collins8885f792017-01-26 14:36:34 -0800167#define QPNP_WLED_SYNC_DLY_MIN_US 0
168#define QPNP_WLED_SYNC_DLY_MAX_US 1400
169#define QPNP_WLED_SYNC_DLY_STEP_US 200
170#define QPNP_WLED_DEF_SYNC_DLY_US 400
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530171#define QPNP_WLED_FS_CURR_MASK GENMASK(3, 0)
David Collins8885f792017-01-26 14:36:34 -0800172#define QPNP_WLED_FS_CURR_MIN_UA 0
173#define QPNP_WLED_FS_CURR_MAX_UA 30000
174#define QPNP_WLED_FS_CURR_STEP_UA 2500
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530175#define QPNP_WLED_CABC_MASK 0x80
David Collins8885f792017-01-26 14:36:34 -0800176#define QPNP_WLED_CABC_SHIFT 7
177#define QPNP_WLED_CURR_SINK_SHIFT 4
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530178#define QPNP_WLED_CURR_SINK_MASK GENMASK(7, 4)
David Collins8885f792017-01-26 14:36:34 -0800179#define QPNP_WLED_BRIGHT_LSB_MASK 0xFF
180#define QPNP_WLED_BRIGHT_MSB_SHIFT 8
181#define QPNP_WLED_BRIGHT_MSB_MASK 0x0F
182#define QPNP_WLED_SYNC 0x0F
183#define QPNP_WLED_SYNC_RESET 0x00
184
185#define QPNP_WLED_SINK_TEST5_HYB 0x14
186#define QPNP_WLED_SINK_TEST5_DIG 0x1E
187#define QPNP_WLED_SINK_TEST5_HVG_PULL_STR_BIT BIT(3)
188
189#define QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE 0x0B
190#define QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE 0x05
191
192#define QPNP_WLED_DISP_SEL_REG(b) (b + 0x44)
193#define QPNP_WLED_MODULE_RDY_REG(b) (b + 0x45)
194#define QPNP_WLED_MODULE_EN_REG(b) (b + 0x46)
195#define QPNP_WLED_MODULE_RDY_MASK 0x7F
196#define QPNP_WLED_MODULE_RDY_SHIFT 7
197#define QPNP_WLED_MODULE_EN_MASK BIT(7)
198#define QPNP_WLED_MODULE_EN_SHIFT 7
199#define QPNP_WLED_DISP_SEL_MASK 0x7F
200#define QPNP_WLED_DISP_SEL_SHIFT 7
201#define QPNP_WLED_EN_SC_DEB_CYCLES_MASK 0x79
202#define QPNP_WLED_EN_DEB_CYCLES_MASK 0xF9
203#define QPNP_WLED_EN_SC_SHIFT 7
204#define QPNP_WLED_SC_PRO_EN_DSCHGR 0x8
205#define QPNP_WLED_SC_DEB_CYCLES_MIN 2
206#define QPNP_WLED_SC_DEB_CYCLES_MAX 16
207#define QPNP_WLED_SC_DEB_CYCLES_SUB 2
208#define QPNP_WLED_SC_DEB_CYCLES_DFLT 4
209#define QPNP_WLED_EXT_FET_DTEST2 0x09
210
211#define QPNP_WLED_SEC_ACCESS_REG(b) (b + 0xD0)
212#define QPNP_WLED_SEC_UNLOCK 0xA5
213
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800214#define NUM_DDIC_CODES 256
David Collins8885f792017-01-26 14:36:34 -0800215#define QPNP_WLED_MAX_STRINGS 4
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530216#define QPNP_PM660_WLED_MAX_STRINGS 3
David Collins8885f792017-01-26 14:36:34 -0800217#define WLED_MAX_LEVEL_4095 4095
218#define QPNP_WLED_RAMP_DLY_MS 20
219#define QPNP_WLED_TRIGGER_NONE "none"
220#define QPNP_WLED_STR_SIZE 20
221#define QPNP_WLED_MIN_MSLEEP 20
222#define QPNP_WLED_SC_DLY_MS 20
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530223#define QPNP_WLED_SOFT_START_DLY_US 10000
David Collins8885f792017-01-26 14:36:34 -0800224
225#define NUM_SUPPORTED_AVDD_VOLTAGES 6
226#define QPNP_WLED_DFLT_AVDD_MV 7600
227#define QPNP_WLED_AVDD_MIN_MV 5650
228#define QPNP_WLED_AVDD_MAX_MV 7900
229#define QPNP_WLED_AVDD_STEP_MV 150
230#define QPNP_WLED_AVDD_MIN_TRIM_VAL 0x0
231#define QPNP_WLED_AVDD_MAX_TRIM_VAL 0xF
232#define QPNP_WLED_AVDD_SEL_SPMI_BIT BIT(7)
233#define QPNP_WLED_AVDD_SET_BIT BIT(4)
234
235#define NUM_SUPPORTED_OVP_THRESHOLDS 4
236#define NUM_SUPPORTED_ILIM_THRESHOLDS 8
237
238#define QPNP_WLED_AVDD_MV_TO_REG(val) \
239 ((val - QPNP_WLED_AVDD_MIN_MV) / QPNP_WLED_AVDD_STEP_MV)
240
241/* output feedback mode */
242enum qpnp_wled_fdbk_op {
243 QPNP_WLED_FDBK_AUTO,
244 QPNP_WLED_FDBK_WLED1,
245 QPNP_WLED_FDBK_WLED2,
246 QPNP_WLED_FDBK_WLED3,
247 QPNP_WLED_FDBK_WLED4,
248};
249
250/* dimming modes */
251enum qpnp_wled_dim_mode {
252 QPNP_WLED_DIM_ANALOG,
253 QPNP_WLED_DIM_DIGITAL,
254 QPNP_WLED_DIM_HYBRID,
255};
256
257/* wled ctrl debug registers */
258static u8 qpnp_wled_ctrl_dbg_regs[] = {
259 0x44, 0x46, 0x48, 0x49, 0x4b, 0x4c, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53,
260 0x54, 0x55, 0x56, 0x57, 0x58, 0x5a, 0x5b, 0x5d, 0x5e, 0xe2
261};
262
263/* wled sink debug registers */
264static u8 qpnp_wled_sink_dbg_regs[] = {
265 0x46, 0x47, 0x48, 0x4a, 0x4b,
266 0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x58,
267 0x60, 0x61, 0x62, 0x63, 0x66, 0x67, 0x68,
268 0x70, 0x71, 0x72, 0x73, 0x76, 0x77, 0x78,
269 0x80, 0x81, 0x82, 0x83, 0x86, 0x87, 0x88,
270 0xe6,
271};
272
273static int qpnp_wled_avdd_target_voltages[NUM_SUPPORTED_AVDD_VOLTAGES] = {
274 7900, 7600, 7300, 6400, 6100, 5800,
275};
276
277static u8 qpnp_wled_ovp_reg_settings[NUM_SUPPORTED_AVDD_VOLTAGES] = {
278 0x0, 0x0, 0x1, 0x2, 0x2, 0x3,
279};
280
281static int qpnp_wled_avdd_trim_adjustments[NUM_SUPPORTED_AVDD_VOLTAGES] = {
282 3, 0, -2, 7, 3, 3,
283};
284
285static int qpnp_wled_ovp_thresholds_pmi8994[NUM_SUPPORTED_OVP_THRESHOLDS] = {
286 31000, 29500, 19400, 17800,
287};
288
289static int qpnp_wled_ovp_thresholds_pmi8998[NUM_SUPPORTED_OVP_THRESHOLDS] = {
290 31100, 29600, 19600, 18100,
291};
292
293static int qpnp_wled_ilim_settings_pmi8994[NUM_SUPPORTED_ILIM_THRESHOLDS] = {
294 105, 385, 660, 980, 1150, 1420, 1700, 1980,
295};
296
297static int qpnp_wled_ilim_settings_pmi8998[NUM_SUPPORTED_ILIM_THRESHOLDS] = {
298 105, 280, 450, 620, 970, 1150, 1300, 1500,
299};
300
301struct wled_vref_setting {
302 u32 min_uv;
303 u32 max_uv;
304 u32 step_uv;
305 u32 default_uv;
306};
307
308static struct wled_vref_setting vref_setting_pmi8994 = {
309 300000, 675000, 25000, 350000,
310};
311static struct wled_vref_setting vref_setting_pmi8998 = {
312 60000, 397500, 22500, 127500,
313};
314
315/**
316 * qpnp_wled - wed data structure
317 * @ cdev - led class device
318 * @ pdev - platform device
319 * @ work - worker for led operation
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700320 * @ wq - workqueue for setting brightness level
David Collins8885f792017-01-26 14:36:34 -0800321 * @ lock - mutex lock for exclusive access
322 * @ fdbk_op - output feedback mode
323 * @ dim_mode - dimming mode
324 * @ ovp_irq - over voltage protection irq
325 * @ sc_irq - short circuit irq
326 * @ sc_cnt - short circuit irq count
327 * @ avdd_target_voltage_mv - target voltage for AVDD module in mV
328 * @ ctrl_base - base address for wled ctrl
329 * @ sink_base - base address for wled sink
330 * @ mod_freq_khz - modulator frequency in KHZ
331 * @ hyb_thres - threshold for hybrid dimming
332 * @ sync_dly_us - sync delay in us
333 * @ vref_uv - ref voltage in uv
334 * @ vref_psm_mv - ref psm voltage in mv
335 * @ loop_comp_res_kohm - control to select the compensation resistor
336 * @ loop_ea_gm - control to select the gm for the gm stage in control loop
337 * @ sc_deb_cycles - debounce time for short circuit detection
338 * @ switch_freq_khz - switching frequency in KHZ
339 * @ ovp_mv - over voltage protection in mv
340 * @ ilim_ma - current limiter in ma
341 * @ boost_duty_ns - boost duty cycle in ns
342 * @ fs_curr_ua - full scale current in ua
343 * @ ramp_ms - delay between ramp steps in ms
344 * @ ramp_step - ramp step size
345 * @ cons_sync_write_delay_us - delay between two consecutive writes to SYNC
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800346 * @ auto_calibration_ovp_count - OVP fault irq count to run auto calibration
347 * @ max_strings - Number of strings supported in WLED peripheral
348 * @ prev_level - Previous brightness level
349 * @ brt_map_table - Brightness map table
David Collins8885f792017-01-26 14:36:34 -0800350 * @ strings - supported list of strings
351 * @ num_strings - number of strings
352 * @ loop_auto_gm_thresh - the clamping level for auto gm
353 * @ lcd_auto_pfm_thresh - the threshold for lcd auto pfm mode
354 * @ loop_auto_gm_en - select if auto gm is enabled
355 * @ lcd_auto_pfm_en - select if auto pfm is enabled in lcd mode
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800356 * @ lcd_psm_ctrl - select if psm needs to be controlled in lcd mode
David Collins8885f792017-01-26 14:36:34 -0800357 * @ avdd_mode_spmi - enable avdd programming via spmi
358 * @ en_9b_dim_res - enable or disable 9bit dimming
359 * @ en_phase_stag - enable or disable phase staggering
360 * @ en_cabc - enable or disable cabc
361 * @ disp_type_amoled - type of display: LCD/AMOLED
362 * @ en_ext_pfet_sc_pro - enable sc protection on external pfet
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800363 * @ prev_state - previous state of WLED
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700364 * @ stepper_en - Flag to enable stepper algorithm
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800365 * @ ovp_irq_disabled - OVP interrupt disable status
366 * @ auto_calib_enabled - Flag to enable auto calibration feature
367 * @ auto_calib_done - Flag to indicate auto calibration is done
368 * @ module_dis_perm - Flat to keep module permanently disabled
369 * @ start_ovp_fault_time - Time when the OVP fault first occurred
David Collins8885f792017-01-26 14:36:34 -0800370 */
371struct qpnp_wled {
372 struct led_classdev cdev;
373 struct platform_device *pdev;
374 struct regmap *regmap;
375 struct pmic_revid_data *pmic_rev_id;
376 struct work_struct work;
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700377 struct workqueue_struct *wq;
David Collins8885f792017-01-26 14:36:34 -0800378 struct mutex lock;
379 struct mutex bus_lock;
380 enum qpnp_wled_fdbk_op fdbk_op;
381 enum qpnp_wled_dim_mode dim_mode;
382 int ovp_irq;
383 int sc_irq;
384 u32 sc_cnt;
385 u32 avdd_target_voltage_mv;
386 u16 ctrl_base;
387 u16 sink_base;
388 u16 mod_freq_khz;
389 u16 hyb_thres;
390 u16 sync_dly_us;
391 u32 vref_uv;
392 u16 vref_psm_mv;
393 u16 loop_comp_res_kohm;
394 u16 loop_ea_gm;
395 u16 sc_deb_cycles;
396 u16 switch_freq_khz;
397 u16 ovp_mv;
398 u16 ilim_ma;
399 u16 boost_duty_ns;
400 u16 fs_curr_ua;
401 u16 ramp_ms;
402 u16 ramp_step;
403 u16 cons_sync_write_delay_us;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530404 u16 auto_calibration_ovp_count;
405 u16 max_strings;
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800406 u16 prev_level;
407 u16 *brt_map_table;
David Collins8885f792017-01-26 14:36:34 -0800408 u8 strings[QPNP_WLED_MAX_STRINGS];
409 u8 num_strings;
410 u8 loop_auto_gm_thresh;
411 u8 lcd_auto_pfm_thresh;
412 bool loop_auto_gm_en;
413 bool lcd_auto_pfm_en;
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800414 bool lcd_psm_ctrl;
David Collins8885f792017-01-26 14:36:34 -0800415 bool avdd_mode_spmi;
416 bool en_9b_dim_res;
417 bool en_phase_stag;
418 bool en_cabc;
419 bool disp_type_amoled;
420 bool en_ext_pfet_sc_pro;
421 bool prev_state;
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700422 bool stepper_en;
David Collins8885f792017-01-26 14:36:34 -0800423 bool ovp_irq_disabled;
Kiran Gundae4648712018-07-19 15:02:59 +0530424 bool secure_mode;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530425 bool auto_calib_enabled;
426 bool auto_calib_done;
Subbaraman Narayanamurthy15624d92017-10-12 21:04:30 -0700427 bool module_dis_perm;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530428 ktime_t start_ovp_fault_time;
David Collins8885f792017-01-26 14:36:34 -0800429};
430
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700431static int qpnp_wled_step_delay_us = 52000;
432module_param_named(
433 total_step_delay_us, qpnp_wled_step_delay_us, int, 0600
434);
435
436static int qpnp_wled_step_size_threshold = 3;
437module_param_named(
438 step_size_threshold, qpnp_wled_step_size_threshold, int, 0600
439);
440
441static int qpnp_wled_step_delay_gain = 2;
442module_param_named(
443 step_delay_gain, qpnp_wled_step_delay_gain, int, 0600
444);
445
David Collins8885f792017-01-26 14:36:34 -0800446/* helper to read a pmic register */
447static int qpnp_wled_read_reg(struct qpnp_wled *wled, u16 addr, u8 *data)
448{
449 int rc;
450 uint val;
451
452 rc = regmap_read(wled->regmap, addr, &val);
453 if (rc < 0) {
454 dev_err(&wled->pdev->dev,
455 "Error reading address: %x(%d)\n", addr, rc);
456 return rc;
457 }
458
459 *data = (u8)val;
460 return 0;
461}
462
463/* helper to write a pmic register */
464static int qpnp_wled_write_reg(struct qpnp_wled *wled, u16 addr, u8 data)
465{
466 int rc;
467
468 mutex_lock(&wled->bus_lock);
469 rc = regmap_write(wled->regmap, addr, data);
470 if (rc < 0) {
471 dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
472 addr, rc);
473 goto out;
474 }
475
476 dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
477out:
478 mutex_unlock(&wled->bus_lock);
479 return rc;
480}
481
482static int qpnp_wled_masked_write_reg(struct qpnp_wled *wled, u16 addr,
483 u8 mask, u8 data)
484{
485 int rc;
486
487 mutex_lock(&wled->bus_lock);
488 rc = regmap_update_bits(wled->regmap, addr, mask, data);
489 if (rc < 0) {
490 dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
491 addr, rc);
492 goto out;
493 }
494
495 dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
496out:
497 mutex_unlock(&wled->bus_lock);
498 return rc;
499}
500
501static int qpnp_wled_sec_write_reg(struct qpnp_wled *wled, u16 addr, u8 data)
502{
503 int rc;
504 u8 reg = QPNP_WLED_SEC_UNLOCK;
505 u16 base_addr = addr & 0xFF00;
506
507 mutex_lock(&wled->bus_lock);
508 rc = regmap_write(wled->regmap, QPNP_WLED_SEC_ACCESS_REG(base_addr),
509 reg);
510 if (rc < 0) {
511 dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
512 QPNP_WLED_SEC_ACCESS_REG(base_addr), rc);
513 goto out;
514 }
515
516 rc = regmap_write(wled->regmap, addr, data);
517 if (rc < 0) {
518 dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
519 addr, rc);
520 goto out;
521 }
522
523 dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
524out:
525 mutex_unlock(&wled->bus_lock);
526 return rc;
527}
528
529static int qpnp_wled_swire_avdd_config(struct qpnp_wled *wled)
530{
531 int rc;
532 u8 val;
533
534 if (wled->pmic_rev_id->pmic_subtype != PMI8998_SUBTYPE &&
535 wled->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
536 return 0;
537
538 if (!wled->disp_type_amoled || wled->avdd_mode_spmi)
539 return 0;
540
541 val = QPNP_WLED_AVDD_MV_TO_REG(wled->avdd_target_voltage_mv);
542 rc = qpnp_wled_write_reg(wled,
543 QPNP_WLED_SWIRE_AVDD_REG(wled->ctrl_base), val);
544 return rc;
545}
546
547static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled)
548{
549 int rc;
550 u8 reg;
551
552 /* sync */
553 reg = QPNP_WLED_SYNC;
554 rc = qpnp_wled_write_reg(wled, QPNP_WLED_SYNC_REG(wled->sink_base),
555 reg);
556 if (rc < 0)
557 return rc;
558
559 if (wled->cons_sync_write_delay_us)
560 usleep_range(wled->cons_sync_write_delay_us,
561 wled->cons_sync_write_delay_us + 1);
562
563 reg = QPNP_WLED_SYNC_RESET;
564 rc = qpnp_wled_write_reg(wled, QPNP_WLED_SYNC_REG(wled->sink_base),
565 reg);
566 if (rc < 0)
567 return rc;
568
569 return 0;
570}
571
572/* set wled to a level of brightness */
573static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
574{
575 int i, rc;
576 u8 reg;
Subbaraman Narayanamurthycb5b72e2017-08-21 19:16:37 -0700577 u16 low_limit = WLED_MAX_LEVEL_4095 * 4 / 1000;
578
579 /* WLED's lower limit of operation is 0.4% */
580 if (level > 0 && level < low_limit)
581 level = low_limit;
David Collins8885f792017-01-26 14:36:34 -0800582
583 /* set brightness registers */
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530584 for (i = 0; i < wled->max_strings; i++) {
David Collins8885f792017-01-26 14:36:34 -0800585 reg = level & QPNP_WLED_BRIGHT_LSB_MASK;
586 rc = qpnp_wled_write_reg(wled,
587 QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base,
588 wled->strings[i]), reg);
589 if (rc < 0)
590 return rc;
591
592 reg = level >> QPNP_WLED_BRIGHT_MSB_SHIFT;
593 reg = reg & QPNP_WLED_BRIGHT_MSB_MASK;
594 rc = qpnp_wled_write_reg(wled,
595 QPNP_WLED_BRIGHT_MSB_REG(wled->sink_base,
596 wled->strings[i]), reg);
597 if (rc < 0)
598 return rc;
599 }
600
601 rc = qpnp_wled_sync_reg_toggle(wled);
602 if (rc < 0) {
603 dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
604 return rc;
605 }
606
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700607 pr_debug("level:%d\n", level);
David Collins8885f792017-01-26 14:36:34 -0800608 return 0;
609}
610
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -0800611static int qpnp_wled_set_map_level(struct qpnp_wled *wled, int level)
612{
613 int rc, i;
614
615 if (level < wled->prev_level) {
616 for (i = wled->prev_level; i >= level; i--) {
617 rc = qpnp_wled_set_level(wled, wled->brt_map_table[i]);
618 if (rc < 0) {
619 pr_err("set brightness level failed, rc:%d\n",
620 rc);
621 return rc;
622 }
623 }
624 } else if (level > wled->prev_level) {
625 for (i = wled->prev_level; i <= level; i++) {
626 rc = qpnp_wled_set_level(wled, wled->brt_map_table[i]);
627 if (rc < 0) {
628 pr_err("set brightness level failed, rc:%d\n",
629 rc);
630 return rc;
631 }
632 }
633 }
634
635 return 0;
636}
637
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -0700638static int qpnp_wled_set_step_level(struct qpnp_wled *wled, int new_level)
639{
640 int rc, i, num_steps, delay_us;
641 u16 level, start_level, end_level, step_size;
642 bool level_inc = false;
643
644 level = wled->prev_level;
645 start_level = wled->brt_map_table[level];
646 end_level = wled->brt_map_table[new_level];
647 level_inc = (new_level > level);
648
649 num_steps = abs(start_level - end_level);
650 if (!num_steps)
651 return 0;
652
653 delay_us = qpnp_wled_step_delay_us / num_steps;
654 pr_debug("level goes from [%d %d] num_steps: %d, delay: %d\n",
655 start_level, end_level, num_steps, delay_us);
656
657 if (delay_us < 500) {
658 step_size = 1000 / delay_us;
659 num_steps = num_steps / step_size;
660 delay_us = 1000;
661 } else {
662 if (num_steps < qpnp_wled_step_size_threshold)
663 delay_us *= qpnp_wled_step_delay_gain;
664
665 step_size = 1;
666 }
667
668 i = start_level;
669 while (num_steps--) {
670 if (level_inc)
671 i += step_size;
672 else
673 i -= step_size;
674
675 rc = qpnp_wled_set_level(wled, i);
676 if (rc < 0)
677 return rc;
678
679 if (delay_us > 0) {
680 if (delay_us < 20000)
681 usleep_range(delay_us, delay_us + 1);
682 else
683 msleep(delay_us / USEC_PER_MSEC);
684 }
685 }
686
687 if (i != end_level) {
688 i = end_level;
689 rc = qpnp_wled_set_level(wled, i);
690 if (rc < 0)
691 return rc;
692 }
693
694 return 0;
695}
696
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800697static int qpnp_wled_psm_config(struct qpnp_wled *wled, bool enable)
698{
699 int rc;
700
701 if (!wled->lcd_psm_ctrl)
702 return 0;
703
704 rc = qpnp_wled_masked_write_reg(wled,
705 QPNP_WLED_EN_PSM_REG(wled->ctrl_base),
706 QPNP_WLED_EN_PSM_BIT,
707 enable ? QPNP_WLED_EN_PSM_BIT : 0);
708 if (rc < 0)
709 return rc;
710
711 rc = qpnp_wled_masked_write_reg(wled,
712 QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base),
713 QPNP_WLED_PSM_OVERWRITE_BIT,
714 enable ? QPNP_WLED_PSM_OVERWRITE_BIT : 0);
715 if (rc < 0)
716 return rc;
717
718 return 0;
719}
720
David Collins8885f792017-01-26 14:36:34 -0800721static int qpnp_wled_module_en(struct qpnp_wled *wled,
722 u16 base_addr, bool state)
723{
724 int rc;
725
Subbaraman Narayanamurthy15624d92017-10-12 21:04:30 -0700726 if (wled->module_dis_perm)
727 return 0;
728
David Collins8885f792017-01-26 14:36:34 -0800729 rc = qpnp_wled_masked_write_reg(wled,
730 QPNP_WLED_MODULE_EN_REG(base_addr),
731 QPNP_WLED_MODULE_EN_MASK,
732 state << QPNP_WLED_MODULE_EN_SHIFT);
733 if (rc < 0)
734 return rc;
735
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800736 /*
737 * Wait for at least 10ms before enabling OVP fault interrupt after
738 * enabling the module so that soft start is completed. Also, this
739 * delay can be used to control PSM during enable when required. Keep
740 * OVP interrupt disabled when the module is disabled.
741 */
742 if (state) {
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +0530743 usleep_range(QPNP_WLED_SOFT_START_DLY_US,
744 QPNP_WLED_SOFT_START_DLY_US + 1000);
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800745 rc = qpnp_wled_psm_config(wled, false);
746 if (rc < 0)
747 return rc;
748
749 if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) {
David Collins8885f792017-01-26 14:36:34 -0800750 enable_irq(wled->ovp_irq);
751 wled->ovp_irq_disabled = false;
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800752 }
753 } else {
754 if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) {
David Collins8885f792017-01-26 14:36:34 -0800755 disable_irq(wled->ovp_irq);
756 wled->ovp_irq_disabled = true;
757 }
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -0800758
759 rc = qpnp_wled_psm_config(wled, true);
760 if (rc < 0)
761 return rc;
David Collins8885f792017-01-26 14:36:34 -0800762 }
763
764 return 0;
765}
766
767/* sysfs store function for ramp */
768static ssize_t qpnp_wled_ramp_store(struct device *dev,
769 struct device_attribute *attr, const char *buf, size_t count)
770{
771 struct qpnp_wled *wled = dev_get_drvdata(dev);
772 int i, rc;
773
774 mutex_lock(&wled->lock);
775
776 if (!wled->cdev.brightness) {
777 rc = qpnp_wled_module_en(wled, wled->ctrl_base, true);
778 if (rc) {
779 dev_err(&wled->pdev->dev, "wled enable failed\n");
780 goto unlock_mutex;
781 }
782 }
783
784 /* ramp up */
785 for (i = 0; i <= wled->cdev.max_brightness;) {
786 rc = qpnp_wled_set_level(wled, i);
787 if (rc) {
788 dev_err(&wled->pdev->dev, "wled set level failed\n");
789 goto restore_brightness;
790 }
791
792 if (wled->ramp_ms < QPNP_WLED_MIN_MSLEEP)
793 usleep_range(wled->ramp_ms * USEC_PER_MSEC,
794 wled->ramp_ms * USEC_PER_MSEC);
795 else
796 msleep(wled->ramp_ms);
797
798 if (i == wled->cdev.max_brightness)
799 break;
800
801 i += wled->ramp_step;
802 if (i > wled->cdev.max_brightness)
803 i = wled->cdev.max_brightness;
804 }
805
806 /* ramp down */
807 for (i = wled->cdev.max_brightness; i >= 0;) {
808 rc = qpnp_wled_set_level(wled, i);
809 if (rc) {
810 dev_err(&wled->pdev->dev, "wled set level failed\n");
811 goto restore_brightness;
812 }
813
814 if (wled->ramp_ms < QPNP_WLED_MIN_MSLEEP)
815 usleep_range(wled->ramp_ms * USEC_PER_MSEC,
816 wled->ramp_ms * USEC_PER_MSEC);
817 else
818 msleep(wled->ramp_ms);
819
820 if (i == 0)
821 break;
822
823 i -= wled->ramp_step;
824 if (i < 0)
825 i = 0;
826 }
827
828 dev_info(&wled->pdev->dev, "wled ramp complete\n");
829
830restore_brightness:
831 /* restore the old brightness */
832 qpnp_wled_set_level(wled, wled->cdev.brightness);
833 if (!wled->cdev.brightness) {
834 rc = qpnp_wled_module_en(wled, wled->ctrl_base, false);
835 if (rc)
836 dev_err(&wled->pdev->dev, "wled enable failed\n");
837 }
838unlock_mutex:
839 mutex_unlock(&wled->lock);
840
841 return count;
842}
843
844static int qpnp_wled_dump_regs(struct qpnp_wled *wled, u16 base_addr,
845 u8 dbg_regs[], u8 size, char *label,
846 int count, char *buf)
847{
848 int i, rc;
849 u8 reg;
850
851 for (i = 0; i < size; i++) {
852 rc = qpnp_wled_read_reg(wled, base_addr + dbg_regs[i], &reg);
853 if (rc < 0)
854 return rc;
855
856 count += snprintf(buf + count, PAGE_SIZE - count,
857 "%s: REG_0x%x = 0x%x\n", label,
858 base_addr + dbg_regs[i], reg);
859
860 if (count >= PAGE_SIZE)
861 return PAGE_SIZE - 1;
862 }
863
864 return count;
865}
866
867/* sysfs show function for debug registers */
868static ssize_t qpnp_wled_dump_regs_show(struct device *dev,
869 struct device_attribute *attr, char *buf)
870{
871 struct qpnp_wled *wled = dev_get_drvdata(dev);
872 int count = 0;
873
874 count = qpnp_wled_dump_regs(wled, wled->ctrl_base,
875 qpnp_wled_ctrl_dbg_regs,
876 ARRAY_SIZE(qpnp_wled_ctrl_dbg_regs),
877 "wled_ctrl", count, buf);
878
879 if (count < 0 || count == PAGE_SIZE - 1)
880 return count;
881
882 count = qpnp_wled_dump_regs(wled, wled->sink_base,
883 qpnp_wled_sink_dbg_regs,
884 ARRAY_SIZE(qpnp_wled_sink_dbg_regs),
885 "wled_sink", count, buf);
886
887 if (count < 0 || count == PAGE_SIZE - 1)
888 return count;
889
890 return count;
891}
892
893/* sysfs show function for ramp delay in each step */
894static ssize_t qpnp_wled_ramp_ms_show(struct device *dev,
895 struct device_attribute *attr, char *buf)
896{
897 struct qpnp_wled *wled = dev_get_drvdata(dev);
898
899 return snprintf(buf, PAGE_SIZE, "%d\n", wled->ramp_ms);
900}
901
902/* sysfs store function for ramp delay in each step */
903static ssize_t qpnp_wled_ramp_ms_store(struct device *dev,
904 struct device_attribute *attr, const char *buf, size_t count)
905{
906 struct qpnp_wled *wled = dev_get_drvdata(dev);
907 int data, rc;
908
909 rc = kstrtoint(buf, 10, &data);
910 if (rc)
911 return rc;
912
913 wled->ramp_ms = data;
914 return count;
915}
916
917/* sysfs show function for ramp step */
918static ssize_t qpnp_wled_ramp_step_show(struct device *dev,
919 struct device_attribute *attr, char *buf)
920{
921 struct qpnp_wled *wled = dev_get_drvdata(dev);
922
923 return snprintf(buf, PAGE_SIZE, "%d\n", wled->ramp_step);
924}
925
926/* sysfs store function for ramp step */
927static ssize_t qpnp_wled_ramp_step_store(struct device *dev,
928 struct device_attribute *attr, const char *buf, size_t count)
929{
930 struct qpnp_wled *wled = dev_get_drvdata(dev);
931 int data, rc;
932
933 rc = kstrtoint(buf, 10, &data);
934 if (rc)
935 return rc;
936
937 wled->ramp_step = data;
938 return count;
939}
940
Kiran Gundae4648712018-07-19 15:02:59 +0530941/* sysfs function for irqs enable/disable */
942static ssize_t qpnp_wled_irq_control(struct device *dev,
943 struct device_attribute *attr,
944 const char *buf, size_t count)
945{
946 struct qpnp_wled *wled = dev_get_drvdata(dev);
947 int val, rc;
948
949 rc = kstrtouint(buf, 0, &val);
950 if (rc < 0)
951 return rc;
952
953 if (val != 0 && val != 1)
954 return count;
955
956 mutex_lock(&wled->lock);
957 /* Disable irqs */
958 if (val == 1 && !wled->secure_mode) {
959 if (wled->ovp_irq > 0)
960 disable_irq(wled->ovp_irq);
961
962 if (wled->sc_irq > 0)
963 disable_irq(wled->sc_irq);
964
965 wled->secure_mode = true;
966 } else if (val == 0 && wled->secure_mode) {
967 if (wled->ovp_irq > 0)
968 enable_irq(wled->ovp_irq);
969
970 if (wled->sc_irq > 0)
971 enable_irq(wled->sc_irq);
972
973 wled->secure_mode = false;
974 }
975
976 mutex_unlock(&wled->lock);
977
978 return count;
979}
980
David Collins8885f792017-01-26 14:36:34 -0800981/* sysfs show function for dim mode */
982static ssize_t qpnp_wled_dim_mode_show(struct device *dev,
983 struct device_attribute *attr, char *buf)
984{
985 struct qpnp_wled *wled = dev_get_drvdata(dev);
986 char *str;
987
988 if (wled->dim_mode == QPNP_WLED_DIM_ANALOG)
989 str = "analog";
990 else if (wled->dim_mode == QPNP_WLED_DIM_DIGITAL)
991 str = "digital";
992 else
993 str = "hybrid";
994
995 return snprintf(buf, PAGE_SIZE, "%s\n", str);
996}
997
998/* sysfs store function for dim mode*/
999static ssize_t qpnp_wled_dim_mode_store(struct device *dev,
1000 struct device_attribute *attr, const char *buf, size_t count)
1001{
1002 struct qpnp_wled *wled = dev_get_drvdata(dev);
1003 char str[QPNP_WLED_STR_SIZE + 1];
1004 int rc, temp;
1005 u8 reg;
1006
1007 if (snprintf(str, QPNP_WLED_STR_SIZE, "%s", buf) > QPNP_WLED_STR_SIZE)
1008 return -EINVAL;
1009
1010 if (strcmp(str, "analog") == 0)
1011 temp = QPNP_WLED_DIM_ANALOG;
1012 else if (strcmp(str, "digital") == 0)
1013 temp = QPNP_WLED_DIM_DIGITAL;
1014 else
1015 temp = QPNP_WLED_DIM_HYBRID;
1016
1017 if (temp == wled->dim_mode)
1018 return count;
1019
1020 rc = qpnp_wled_read_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), &reg);
1021 if (rc < 0)
1022 return rc;
1023
1024 if (temp == QPNP_WLED_DIM_HYBRID) {
1025 reg &= QPNP_WLED_DIM_HYB_MASK;
1026 reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
1027 } else {
1028 reg &= QPNP_WLED_DIM_HYB_MASK;
1029 reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
1030 reg &= QPNP_WLED_DIM_ANA_MASK;
1031 reg |= temp;
1032 }
1033
1034 rc = qpnp_wled_write_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), reg);
1035 if (rc)
1036 return rc;
1037
1038 wled->dim_mode = temp;
1039
1040 return count;
1041}
1042
1043/* sysfs show function for full scale current in ua*/
1044static ssize_t qpnp_wled_fs_curr_ua_show(struct device *dev,
1045 struct device_attribute *attr, char *buf)
1046{
1047 struct qpnp_wled *wled = dev_get_drvdata(dev);
1048
1049 return snprintf(buf, PAGE_SIZE, "%d\n", wled->fs_curr_ua);
1050}
1051
1052/* sysfs store function for full scale current in ua*/
1053static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev,
1054 struct device_attribute *attr, const char *buf, size_t count)
1055{
1056 struct qpnp_wled *wled = dev_get_drvdata(dev);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301057 int data, i, rc;
David Collins8885f792017-01-26 14:36:34 -08001058 u8 reg;
1059
1060 rc = kstrtoint(buf, 10, &data);
1061 if (rc)
1062 return rc;
1063
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301064 for (i = 0; i < wled->max_strings; i++) {
David Collins8885f792017-01-26 14:36:34 -08001065 if (data < QPNP_WLED_FS_CURR_MIN_UA)
1066 data = QPNP_WLED_FS_CURR_MIN_UA;
1067 else if (data > QPNP_WLED_FS_CURR_MAX_UA)
1068 data = QPNP_WLED_FS_CURR_MAX_UA;
1069
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301070 reg = data / QPNP_WLED_FS_CURR_STEP_UA;
1071 rc = qpnp_wled_masked_write_reg(wled,
1072 QPNP_WLED_FS_CURR_REG(wled->sink_base, i),
1073 QPNP_WLED_FS_CURR_MASK, reg);
David Collins8885f792017-01-26 14:36:34 -08001074 if (rc < 0)
1075 return rc;
David Collins8885f792017-01-26 14:36:34 -08001076 }
1077
1078 wled->fs_curr_ua = data;
1079
1080 rc = qpnp_wled_sync_reg_toggle(wled);
1081 if (rc < 0) {
1082 dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
1083 return rc;
1084 }
1085
1086 return count;
1087}
1088
1089/* sysfs attributes exported by wled */
1090static struct device_attribute qpnp_wled_attrs[] = {
1091 __ATTR(dump_regs, 0664, qpnp_wled_dump_regs_show, NULL),
1092 __ATTR(dim_mode, 0664, qpnp_wled_dim_mode_show,
1093 qpnp_wled_dim_mode_store),
1094 __ATTR(fs_curr_ua, 0664, qpnp_wled_fs_curr_ua_show,
1095 qpnp_wled_fs_curr_ua_store),
1096 __ATTR(start_ramp, 0664, NULL, qpnp_wled_ramp_store),
1097 __ATTR(ramp_ms, 0664, qpnp_wled_ramp_ms_show, qpnp_wled_ramp_ms_store),
1098 __ATTR(ramp_step, 0664, qpnp_wled_ramp_step_show,
1099 qpnp_wled_ramp_step_store),
Kiran Gundae4648712018-07-19 15:02:59 +05301100 __ATTR(secure_mode, 0664, NULL, qpnp_wled_irq_control),
David Collins8885f792017-01-26 14:36:34 -08001101};
1102
1103/* worker for setting wled brightness */
1104static void qpnp_wled_work(struct work_struct *work)
1105{
1106 struct qpnp_wled *wled;
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08001107 int level, level_255, rc;
David Collins8885f792017-01-26 14:36:34 -08001108
1109 wled = container_of(work, struct qpnp_wled, work);
1110
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08001111 mutex_lock(&wled->lock);
Kiran Gundae4648712018-07-19 15:02:59 +05301112
1113 if (wled->secure_mode) {
1114 pr_debug("Can not set brightness in secure_mode\n ");
1115 goto unlock_mutex;
1116 }
1117
David Collins8885f792017-01-26 14:36:34 -08001118 level = wled->cdev.brightness;
1119
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08001120 if (wled->brt_map_table) {
1121 /*
1122 * Change the 12 bit level to 8 bit level and use the mapped
1123 * values for 12 bit level from brightness map table.
1124 */
1125 level_255 = DIV_ROUND_CLOSEST(level, 16);
1126 if (level_255 > 255)
1127 level_255 = 255;
David Collins8885f792017-01-26 14:36:34 -08001128
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08001129 pr_debug("level: %d level_255: %d\n", level, level_255);
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07001130 if (wled->stepper_en)
1131 rc = qpnp_wled_set_step_level(wled, level_255);
1132 else
1133 rc = qpnp_wled_set_map_level(wled, level_255);
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08001134 if (rc) {
1135 dev_err(&wled->pdev->dev, "wled set level failed\n");
1136 goto unlock_mutex;
1137 }
1138 wled->prev_level = level_255;
1139 } else if (level) {
David Collins8885f792017-01-26 14:36:34 -08001140 rc = qpnp_wled_set_level(wled, level);
1141 if (rc) {
1142 dev_err(&wled->pdev->dev, "wled set level failed\n");
1143 goto unlock_mutex;
1144 }
1145 }
1146
1147 if (!!level != wled->prev_state) {
1148 if (!!level) {
1149 /*
1150 * For AMOLED display in pmi8998, SWIRE_AVDD_DEFAULT has
1151 * to be reconfigured every time the module is enabled.
1152 */
1153 rc = qpnp_wled_swire_avdd_config(wled);
1154 if (rc < 0) {
1155 pr_err("Write to SWIRE_AVDD_DEFAULT register failed rc:%d\n",
1156 rc);
1157 goto unlock_mutex;
1158 }
1159 }
1160
1161 rc = qpnp_wled_module_en(wled, wled->ctrl_base, !!level);
1162 if (rc) {
1163 dev_err(&wled->pdev->dev, "wled %sable failed\n",
1164 level ? "en" : "dis");
1165 goto unlock_mutex;
1166 }
1167 }
1168
1169 wled->prev_state = !!level;
1170unlock_mutex:
1171 mutex_unlock(&wled->lock);
1172}
1173
1174/* get api registered with led classdev for wled brightness */
1175static enum led_brightness qpnp_wled_get(struct led_classdev *led_cdev)
1176{
1177 struct qpnp_wled *wled;
1178
1179 wled = container_of(led_cdev, struct qpnp_wled, cdev);
1180
1181 return wled->cdev.brightness;
1182}
1183
1184/* set api registered with led classdev for wled brightness */
1185static void qpnp_wled_set(struct led_classdev *led_cdev,
1186 enum led_brightness level)
1187{
1188 struct qpnp_wled *wled;
1189
1190 wled = container_of(led_cdev, struct qpnp_wled, cdev);
1191
1192 if (level < LED_OFF)
1193 level = LED_OFF;
1194 else if (level > wled->cdev.max_brightness)
1195 level = wled->cdev.max_brightness;
1196
1197 wled->cdev.brightness = level;
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07001198 queue_work(wled->wq, &wled->work);
David Collins8885f792017-01-26 14:36:34 -08001199}
1200
1201static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
1202{
1203 int rc;
1204 u8 reg;
1205
1206 /* display type */
1207 rc = qpnp_wled_read_reg(wled, QPNP_WLED_DISP_SEL_REG(base_addr), &reg);
1208 if (rc < 0)
1209 return rc;
1210
1211 reg &= QPNP_WLED_DISP_SEL_MASK;
1212 reg |= (wled->disp_type_amoled << QPNP_WLED_DISP_SEL_SHIFT);
1213
1214 rc = qpnp_wled_sec_write_reg(wled, QPNP_WLED_DISP_SEL_REG(base_addr),
1215 reg);
1216 if (rc)
1217 return rc;
1218
1219 if (wled->disp_type_amoled) {
1220 /* Configure the PSM CTRL register for AMOLED */
1221 if (wled->vref_psm_mv < QPNP_WLED_VREF_PSM_MIN_MV)
1222 wled->vref_psm_mv = QPNP_WLED_VREF_PSM_MIN_MV;
1223 else if (wled->vref_psm_mv > QPNP_WLED_VREF_PSM_MAX_MV)
1224 wled->vref_psm_mv = QPNP_WLED_VREF_PSM_MAX_MV;
1225
1226 rc = qpnp_wled_read_reg(wled,
1227 QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), &reg);
1228 if (rc < 0)
1229 return rc;
1230
1231 reg &= QPNP_WLED_VREF_PSM_MASK;
1232 reg |= ((wled->vref_psm_mv - QPNP_WLED_VREF_PSM_MIN_MV)/
1233 QPNP_WLED_VREF_PSM_STEP_MV);
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -08001234 reg |= QPNP_WLED_PSM_OVERWRITE_BIT;
David Collins8885f792017-01-26 14:36:34 -08001235 rc = qpnp_wled_write_reg(wled,
1236 QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), reg);
1237 if (rc)
1238 return rc;
1239
1240 /* Configure the VLOOP COMP RES register for AMOLED */
1241 if (wled->loop_comp_res_kohm < QPNP_WLED_LOOP_COMP_RES_MIN_KOHM)
1242 wled->loop_comp_res_kohm =
1243 QPNP_WLED_LOOP_COMP_RES_MIN_KOHM;
1244 else if (wled->loop_comp_res_kohm >
1245 QPNP_WLED_LOOP_COMP_RES_MAX_KOHM)
1246 wled->loop_comp_res_kohm =
1247 QPNP_WLED_LOOP_COMP_RES_MAX_KOHM;
1248
1249 rc = qpnp_wled_read_reg(wled,
1250 QPNP_WLED_VLOOP_COMP_RES_REG(wled->ctrl_base),
1251 &reg);
1252 if (rc < 0)
1253 return rc;
1254
1255 reg &= QPNP_WLED_VLOOP_COMP_RES_MASK;
1256 reg |= ((wled->loop_comp_res_kohm -
1257 QPNP_WLED_LOOP_COMP_RES_MIN_KOHM)/
1258 QPNP_WLED_LOOP_COMP_RES_STEP_KOHM);
1259 reg |= QPNP_WLED_VLOOP_COMP_RES_OVERWRITE;
1260 rc = qpnp_wled_write_reg(wled,
1261 QPNP_WLED_VLOOP_COMP_RES_REG(wled->ctrl_base),
1262 reg);
1263 if (rc)
1264 return rc;
1265
1266 /* Configure the CTRL TEST4 register for AMOLED */
1267 rc = qpnp_wled_read_reg(wled,
1268 QPNP_WLED_TEST4_REG(wled->ctrl_base), &reg);
1269 if (rc < 0)
1270 return rc;
1271
1272 reg |= QPNP_WLED_TEST4_EN_IIND_UP;
1273 rc = qpnp_wled_sec_write_reg(wled,
1274 QPNP_WLED_TEST4_REG(base_addr), reg);
1275 if (rc)
1276 return rc;
1277 } else {
1278 /*
1279 * enable VREF_UP to avoid false ovp on low brightness for LCD
1280 */
1281 reg = QPNP_WLED_TEST4_EN_VREF_UP
1282 | QPNP_WLED_TEST4_EN_DEB_BYPASS_ILIM_BIT;
1283 rc = qpnp_wled_sec_write_reg(wled,
1284 QPNP_WLED_TEST4_REG(base_addr), reg);
1285 if (rc)
1286 return rc;
1287 }
1288
1289 return 0;
1290}
1291
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001292#define AUTO_CALIB_BRIGHTNESS 200
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301293static int wled_auto_calibrate(struct qpnp_wled *wled)
1294{
1295 int rc = 0, i;
1296 u8 reg = 0, sink_config = 0, sink_test = 0, sink_valid = 0, int_sts;
1297
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301298 /* read configured sink configuration */
1299 rc = qpnp_wled_read_reg(wled,
1300 QPNP_WLED_CURR_SINK_REG(wled->sink_base), &sink_config);
1301 if (rc < 0) {
1302 pr_err("Failed to read SINK configuration rc=%d\n", rc);
1303 goto failed_calib;
1304 }
1305
1306 /* disable the module before starting calibration */
1307 rc = qpnp_wled_masked_write_reg(wled,
1308 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
1309 QPNP_WLED_MODULE_EN_MASK, 0);
1310 if (rc < 0) {
1311 pr_err("Failed to disable WLED module rc=%d\n", rc);
1312 goto failed_calib;
1313 }
1314
1315 /* set low brightness across all sinks */
1316 rc = qpnp_wled_set_level(wled, AUTO_CALIB_BRIGHTNESS);
1317 if (rc < 0) {
1318 pr_err("Failed to set brightness for calibration rc=%d\n", rc);
1319 goto failed_calib;
1320 }
1321
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001322 if (wled->en_cabc) {
1323 for (i = 0; i < wled->max_strings; i++) {
1324 reg = 0;
1325 rc = qpnp_wled_masked_write_reg(wled,
1326 QPNP_WLED_CABC_REG(wled->sink_base, i),
1327 QPNP_WLED_CABC_MASK, reg);
1328 if (rc < 0)
1329 goto failed_calib;
1330 }
1331 }
1332
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301333 /* disable all sinks */
1334 rc = qpnp_wled_write_reg(wled,
1335 QPNP_WLED_CURR_SINK_REG(wled->sink_base), 0);
1336 if (rc < 0) {
1337 pr_err("Failed to disable all sinks rc=%d\n", rc);
1338 goto failed_calib;
1339 }
1340
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301341 /* iterate through the strings one by one */
1342 for (i = 0; i < wled->max_strings; i++) {
1343 sink_test = 1 << (QPNP_WLED_CURR_SINK_SHIFT + i);
1344
1345 /* Enable feedback control */
1346 rc = qpnp_wled_write_reg(wled,
1347 QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
1348 i + 1);
1349 if (rc < 0) {
1350 pr_err("Failed to enable feedback for SINK %d rc = %d\n",
1351 i + 1, rc);
1352 goto failed_calib;
1353 }
1354
1355 /* enable the sink */
1356 rc = qpnp_wled_write_reg(wled,
1357 QPNP_WLED_CURR_SINK_REG(wled->sink_base), sink_test);
1358 if (rc < 0) {
1359 pr_err("Failed to configure SINK %d rc=%d\n",
1360 i + 1, rc);
1361 goto failed_calib;
1362 }
1363
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001364 /* Enable the module */
1365 rc = qpnp_wled_masked_write_reg(wled,
1366 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
1367 QPNP_WLED_MODULE_EN_MASK, QPNP_WLED_MODULE_EN_MASK);
1368 if (rc < 0) {
1369 pr_err("Failed to enable WLED module rc=%d\n", rc);
1370 goto failed_calib;
1371 }
1372
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301373 /* delay for WLED soft-start */
1374 usleep_range(QPNP_WLED_SOFT_START_DLY_US,
1375 QPNP_WLED_SOFT_START_DLY_US + 1000);
1376
1377 rc = qpnp_wled_read_reg(wled,
1378 QPNP_WLED_INT_RT_STS(wled->ctrl_base), &int_sts);
1379 if (rc < 0) {
1380 pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc);
1381 goto failed_calib;
1382 }
1383
1384 if (int_sts & QPNP_WLED_OVP_FAULT_BIT)
1385 pr_debug("WLED OVP fault detected with SINK %d\n",
1386 i + 1);
1387 else
1388 sink_valid |= sink_test;
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001389
1390 /* Disable the module */
1391 rc = qpnp_wled_masked_write_reg(wled,
1392 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
1393 QPNP_WLED_MODULE_EN_MASK, 0);
1394 if (rc < 0) {
1395 pr_err("Failed to disable WLED module rc=%d\n", rc);
1396 goto failed_calib;
1397 }
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301398 }
1399
1400 if (sink_valid == sink_config) {
1401 pr_debug("WLED auto-calibration complete, default sink-config=%x OK!\n",
1402 sink_config);
1403 } else {
1404 pr_warn("Invalid WLED default sink config=%x changing it to=%x\n",
1405 sink_config, sink_valid);
1406 sink_config = sink_valid;
1407 }
1408
1409 if (!sink_config) {
1410 pr_warn("No valid WLED sinks found\n");
Subbaraman Narayanamurthy15624d92017-10-12 21:04:30 -07001411 wled->module_dis_perm = true;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301412 goto failed_calib;
1413 }
1414
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301415 /* write the new sink configuration */
1416 rc = qpnp_wled_write_reg(wled,
1417 QPNP_WLED_CURR_SINK_REG(wled->sink_base), sink_config);
1418 if (rc < 0) {
1419 pr_err("Failed to reconfigure the default sink rc=%d\n", rc);
1420 goto failed_calib;
1421 }
1422
1423 /* MODULATOR_EN setting for valid sinks */
1424 for (i = 0; i < wled->max_strings; i++) {
Subbaraman Narayanamurthy0c4e61b2017-09-29 17:16:28 -07001425 if (wled->en_cabc) {
1426 reg = 1 << QPNP_WLED_CABC_SHIFT;
1427 rc = qpnp_wled_masked_write_reg(wled,
1428 QPNP_WLED_CABC_REG(wled->sink_base, i),
1429 QPNP_WLED_CABC_MASK, reg);
1430 if (rc < 0)
1431 goto failed_calib;
1432 }
1433
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301434 if (sink_config & (1 << (QPNP_WLED_CURR_SINK_SHIFT + i)))
1435 reg = (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
1436 else
1437 reg = 0x0; /* disable modulator_en for unused sink */
1438
1439 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID)
1440 reg &= QPNP_WLED_GATE_DRV_MASK;
1441 else
1442 reg |= ~QPNP_WLED_GATE_DRV_MASK;
1443
1444 rc = qpnp_wled_write_reg(wled,
1445 QPNP_WLED_MOD_EN_REG(wled->sink_base, i), reg);
1446 if (rc < 0) {
1447 pr_err("Failed to configure MODULATOR_EN rc=%d\n", rc);
1448 goto failed_calib;
1449 }
1450 }
1451
1452 /* restore the feedback setting */
1453 rc = qpnp_wled_write_reg(wled,
1454 QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
1455 wled->fdbk_op);
1456 if (rc < 0) {
1457 pr_err("Failed to restore feedback setting rc=%d\n", rc);
1458 goto failed_calib;
1459 }
1460
1461 /* restore brightness */
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05301462 rc = qpnp_wled_set_level(wled, !wled->cdev.brightness ?
1463 AUTO_CALIB_BRIGHTNESS : wled->cdev.brightness);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301464 if (rc < 0) {
1465 pr_err("Failed to set brightness after calibration rc=%d\n",
1466 rc);
1467 goto failed_calib;
1468 }
1469
1470 rc = qpnp_wled_masked_write_reg(wled,
1471 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
1472 QPNP_WLED_MODULE_EN_MASK,
1473 QPNP_WLED_MODULE_EN_MASK);
1474 if (rc < 0) {
1475 pr_err("Failed to enable WLED module rc=%d\n", rc);
1476 goto failed_calib;
1477 }
1478
1479 /* delay for WLED soft-start */
1480 usleep_range(QPNP_WLED_SOFT_START_DLY_US,
1481 QPNP_WLED_SOFT_START_DLY_US + 1000);
1482
1483failed_calib:
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301484 return rc;
1485}
1486
1487#define WLED_AUTO_CAL_OVP_COUNT 5
1488#define WLED_AUTO_CAL_CNT_DLY_US 1000000 /* 1 second */
1489static bool qpnp_wled_auto_cal_required(struct qpnp_wled *wled)
1490{
1491 s64 elapsed_time_us;
1492
1493 /*
1494 * Check if the OVP fault was an occasional one
1495 * or if its firing continuously, the latter qualifies
1496 * for an auto-calibration check.
1497 */
1498 if (!wled->auto_calibration_ovp_count) {
1499 wled->start_ovp_fault_time = ktime_get();
1500 wled->auto_calibration_ovp_count++;
1501 } else {
1502 elapsed_time_us = ktime_us_delta(ktime_get(),
1503 wled->start_ovp_fault_time);
1504 if (elapsed_time_us > WLED_AUTO_CAL_CNT_DLY_US)
1505 wled->auto_calibration_ovp_count = 0;
1506 else
1507 wled->auto_calibration_ovp_count++;
1508
1509 if (wled->auto_calibration_ovp_count >=
1510 WLED_AUTO_CAL_OVP_COUNT) {
1511 wled->auto_calibration_ovp_count = 0;
1512 return true;
1513 }
1514 }
1515
1516 return false;
1517}
1518
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05301519static int qpnp_wled_auto_calibrate_at_init(struct qpnp_wled *wled)
1520{
1521 int rc;
1522 u8 fault_status = 0, rt_status = 0;
1523
1524 if (!wled->auto_calib_enabled)
1525 return 0;
1526
1527 rc = qpnp_wled_read_reg(wled,
1528 QPNP_WLED_INT_RT_STS(wled->ctrl_base), &rt_status);
1529 if (rc < 0)
1530 pr_err("Failed to read RT status rc=%d\n", rc);
1531
1532 rc = qpnp_wled_read_reg(wled,
1533 QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_status);
1534 if (rc < 0)
1535 pr_err("Failed to read fault status rc=%d\n", rc);
1536
1537 if ((rt_status & QPNP_WLED_OVP_FLT_RT_STS_BIT) ||
1538 (fault_status & QPNP_WLED_OVP_FAULT_BIT)) {
1539 mutex_lock(&wled->lock);
1540 rc = wled_auto_calibrate(wled);
1541 if (rc < 0)
1542 pr_err("Failed auto-calibration rc=%d\n", rc);
1543 else
1544 wled->auto_calib_done = true;
1545 mutex_unlock(&wled->lock);
1546 }
1547
1548 return rc;
1549}
1550
David Collins8885f792017-01-26 14:36:34 -08001551/* ovp irq handler */
1552static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled)
1553{
1554 struct qpnp_wled *wled = _wled;
1555 int rc;
Subbaraman Narayanamurthyb5ae3182017-02-17 15:14:06 -08001556 u8 fault_sts, int_sts;
David Collins8885f792017-01-26 14:36:34 -08001557
1558 rc = qpnp_wled_read_reg(wled,
Subbaraman Narayanamurthyb5ae3182017-02-17 15:14:06 -08001559 QPNP_WLED_INT_RT_STS(wled->ctrl_base), &int_sts);
1560 if (rc < 0) {
1561 pr_err("Error in reading WLED_INT_RT_STS rc=%d\n", rc);
1562 return IRQ_HANDLED;
1563 }
1564
1565 rc = qpnp_wled_read_reg(wled,
1566 QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_sts);
David Collins8885f792017-01-26 14:36:34 -08001567 if (rc < 0) {
1568 pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc);
1569 return IRQ_HANDLED;
1570 }
1571
Subbaraman Narayanamurthyb5ae3182017-02-17 15:14:06 -08001572 if (fault_sts & (QPNP_WLED_OVP_FAULT_BIT | QPNP_WLED_ILIM_FAULT_BIT))
1573 pr_err("WLED OVP fault detected, int_sts=%x fault_sts= %x\n",
1574 int_sts, fault_sts);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301575
1576 if (fault_sts & QPNP_WLED_OVP_FAULT_BIT) {
1577 if (wled->auto_calib_enabled && !wled->auto_calib_done) {
1578 if (qpnp_wled_auto_cal_required(wled)) {
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05301579 mutex_lock(&wled->lock);
1580 if (wled->ovp_irq > 0 &&
1581 !wled->ovp_irq_disabled) {
1582 disable_irq_nosync(wled->ovp_irq);
1583 wled->ovp_irq_disabled = true;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301584 }
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05301585
1586 rc = wled_auto_calibrate(wled);
1587 if (rc < 0)
1588 pr_err("Failed auto-calibration rc=%d\n",
1589 rc);
1590 else
1591 wled->auto_calib_done = true;
1592
1593 if (wled->ovp_irq > 0 &&
1594 wled->ovp_irq_disabled) {
1595 enable_irq(wled->ovp_irq);
1596 wled->ovp_irq_disabled = false;
1597 }
1598 mutex_unlock(&wled->lock);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301599 }
1600 }
1601 }
1602
David Collins8885f792017-01-26 14:36:34 -08001603 return IRQ_HANDLED;
1604}
1605
1606/* short circuit irq handler */
1607static irqreturn_t qpnp_wled_sc_irq_handler(int irq, void *_wled)
1608{
1609 struct qpnp_wled *wled = _wled;
1610 int rc;
1611 u8 val;
1612
1613 rc = qpnp_wled_read_reg(wled,
1614 QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &val);
1615 if (rc < 0) {
1616 pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc);
1617 return IRQ_HANDLED;
1618 }
1619
1620 pr_err("WLED short circuit detected %d times fault_status=%x\n",
1621 ++wled->sc_cnt, val);
1622 mutex_lock(&wled->lock);
1623 qpnp_wled_module_en(wled, wled->ctrl_base, false);
1624 msleep(QPNP_WLED_SC_DLY_MS);
1625 qpnp_wled_module_en(wled, wled->ctrl_base, true);
1626 mutex_unlock(&wled->lock);
1627
1628 return IRQ_HANDLED;
1629}
1630
1631static bool is_avdd_trim_adjustment_required(struct qpnp_wled *wled)
1632{
1633 int rc;
1634 u8 reg = 0;
1635
1636 /*
1637 * AVDD trim adjustment is not required for pmi8998/pm660l and not
1638 * supported for pmi8994.
1639 */
1640 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1641 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE ||
1642 wled->pmic_rev_id->pmic_subtype == PMI8994_SUBTYPE)
1643 return false;
1644
1645 /*
1646 * Configure TRIM_REG only if disp_type_amoled and it has
1647 * not already been programmed by bootloader.
1648 */
1649 if (!wled->disp_type_amoled)
1650 return false;
1651
1652 rc = qpnp_wled_read_reg(wled,
1653 QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base), &reg);
1654 if (rc < 0)
1655 return false;
1656
1657 return !(reg & QPNP_WLED_AVDD_SET_BIT);
1658}
1659
1660static int qpnp_wled_gm_config(struct qpnp_wled *wled)
1661{
1662 int rc;
1663 u8 mask = 0, reg = 0;
1664
1665 /* Configure the LOOP COMP GM register */
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07001666 if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1667 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)) {
1668 if (wled->disp_type_amoled) {
1669 reg = 0;
1670 mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
1671 QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
1672 } else {
1673 if (wled->loop_auto_gm_en)
1674 reg |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN;
David Collins8885f792017-01-26 14:36:34 -08001675
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07001676 if (wled->loop_auto_gm_thresh >
1677 QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX)
1678 wled->loop_auto_gm_thresh =
1679 QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX;
David Collins8885f792017-01-26 14:36:34 -08001680
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07001681 reg |= wled->loop_auto_gm_thresh <<
1682 QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT;
1683 mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
1684 QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
1685 }
David Collins8885f792017-01-26 14:36:34 -08001686 }
1687
1688 if (wled->loop_ea_gm < QPNP_WLED_LOOP_EA_GM_MIN)
1689 wled->loop_ea_gm = QPNP_WLED_LOOP_EA_GM_MIN;
1690 else if (wled->loop_ea_gm > QPNP_WLED_LOOP_EA_GM_MAX)
1691 wled->loop_ea_gm = QPNP_WLED_LOOP_EA_GM_MAX;
1692
1693 reg |= wled->loop_ea_gm | QPNP_WLED_VLOOP_COMP_GM_OVERWRITE;
1694 mask |= QPNP_WLED_VLOOP_COMP_GM_MASK |
1695 QPNP_WLED_VLOOP_COMP_GM_OVERWRITE;
1696
1697 rc = qpnp_wled_masked_write_reg(wled,
1698 QPNP_WLED_VLOOP_COMP_GM_REG(wled->ctrl_base), mask,
1699 reg);
1700 if (rc)
1701 pr_err("write VLOOP_COMP_GM_REG failed, rc=%d]\n", rc);
1702
1703 return rc;
1704}
1705
1706static int qpnp_wled_ovp_config(struct qpnp_wled *wled)
1707{
1708 int rc, i, *ovp_table;
1709 u8 reg;
1710
1711 /*
1712 * Configure the OVP register based on ovp_mv only if display type is
1713 * not AMOLED.
1714 */
1715 if (wled->disp_type_amoled)
1716 return 0;
1717
1718 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1719 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
1720 ovp_table = qpnp_wled_ovp_thresholds_pmi8998;
1721 else
1722 ovp_table = qpnp_wled_ovp_thresholds_pmi8994;
1723
1724 for (i = 0; i < NUM_SUPPORTED_OVP_THRESHOLDS; i++) {
1725 if (wled->ovp_mv == ovp_table[i])
1726 break;
1727 }
1728
1729 if (i == NUM_SUPPORTED_OVP_THRESHOLDS) {
1730 dev_err(&wled->pdev->dev,
1731 "Invalid ovp threshold specified in device tree\n");
1732 return -EINVAL;
1733 }
1734
1735 reg = i & QPNP_WLED_OVP_MASK;
1736 rc = qpnp_wled_masked_write_reg(wled,
1737 QPNP_WLED_OVP_REG(wled->ctrl_base),
1738 QPNP_WLED_OVP_MASK, reg);
1739 if (rc)
1740 return rc;
1741
1742 return 0;
1743}
1744
1745static int qpnp_wled_avdd_trim_config(struct qpnp_wled *wled)
1746{
1747 int rc, i;
1748 u8 reg;
1749
1750 for (i = 0; i < NUM_SUPPORTED_AVDD_VOLTAGES; i++) {
1751 if (wled->avdd_target_voltage_mv ==
1752 qpnp_wled_avdd_target_voltages[i])
1753 break;
1754 }
1755
1756 if (i == NUM_SUPPORTED_AVDD_VOLTAGES) {
1757 dev_err(&wled->pdev->dev,
1758 "Invalid avdd target voltage specified in device tree\n");
1759 return -EINVAL;
1760 }
1761
1762 /* Update WLED_OVP register based on desired target voltage */
1763 reg = qpnp_wled_ovp_reg_settings[i];
1764 rc = qpnp_wled_masked_write_reg(wled,
1765 QPNP_WLED_OVP_REG(wled->ctrl_base),
1766 QPNP_WLED_OVP_MASK, reg);
1767 if (rc)
1768 return rc;
1769
1770 /* Update WLED_TRIM register based on desired target voltage */
1771 rc = qpnp_wled_read_reg(wled,
1772 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), &reg);
1773 if (rc)
1774 return rc;
1775
1776 reg += qpnp_wled_avdd_trim_adjustments[i];
1777 if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL ||
1778 (s8)reg > QPNP_WLED_AVDD_MAX_TRIM_VAL) {
1779 dev_dbg(&wled->pdev->dev,
1780 "adjusted trim %d is not within range, capping it\n",
1781 (s8)reg);
1782 if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL)
1783 reg = QPNP_WLED_AVDD_MIN_TRIM_VAL;
1784 else
1785 reg = QPNP_WLED_AVDD_MAX_TRIM_VAL;
1786 }
1787
1788 reg &= QPNP_WLED_7P7_TRIM_MASK;
1789 rc = qpnp_wled_sec_write_reg(wled,
1790 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), reg);
1791 if (rc < 0)
1792 dev_err(&wled->pdev->dev, "Write to 7P7_TRIM register failed, rc=%d\n",
1793 rc);
1794 return rc;
1795}
1796
1797static int qpnp_wled_avdd_mode_config(struct qpnp_wled *wled)
1798{
1799 int rc;
1800 u8 reg = 0;
1801
1802 /*
1803 * At present, configuring the mode to SPMI/SWIRE for controlling
1804 * AVDD voltage is available only in pmi8998/pm660l.
1805 */
1806 if (wled->pmic_rev_id->pmic_subtype != PMI8998_SUBTYPE &&
1807 wled->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
1808 return 0;
1809
1810 /* AMOLED_VOUT should be configured for AMOLED */
1811 if (!wled->disp_type_amoled)
1812 return 0;
1813
1814 /* Configure avdd register */
1815 if (wled->avdd_target_voltage_mv > QPNP_WLED_AVDD_MAX_MV) {
1816 dev_dbg(&wled->pdev->dev, "Capping avdd target voltage to %d\n",
1817 QPNP_WLED_AVDD_MAX_MV);
1818 wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MAX_MV;
1819 } else if (wled->avdd_target_voltage_mv < QPNP_WLED_AVDD_MIN_MV) {
1820 dev_info(&wled->pdev->dev, "Capping avdd target voltage to %d\n",
1821 QPNP_WLED_AVDD_MIN_MV);
1822 wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MIN_MV;
1823 }
1824
1825 if (wled->avdd_mode_spmi) {
1826 reg = QPNP_WLED_AVDD_MV_TO_REG(wled->avdd_target_voltage_mv);
1827 reg |= QPNP_WLED_AVDD_SEL_SPMI_BIT;
1828 rc = qpnp_wled_write_reg(wled,
1829 QPNP_WLED_AMOLED_VOUT_REG(wled->ctrl_base),
1830 reg);
1831 if (rc < 0)
1832 pr_err("Write to AMOLED_VOUT register failed, rc=%d\n",
1833 rc);
1834 } else {
1835 rc = qpnp_wled_swire_avdd_config(wled);
1836 if (rc < 0)
1837 pr_err("Write to SWIRE_AVDD_DEFAULT register failed rc:%d\n",
1838 rc);
1839 }
1840
1841 return rc;
1842}
1843
1844static int qpnp_wled_ilim_config(struct qpnp_wled *wled)
1845{
1846 int rc, i, *ilim_table;
1847 u8 reg;
1848
1849 if (wled->ilim_ma < PMI8994_WLED_ILIM_MIN_MA)
1850 wled->ilim_ma = PMI8994_WLED_ILIM_MIN_MA;
1851
1852 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1853 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
1854 ilim_table = qpnp_wled_ilim_settings_pmi8998;
1855 if (wled->ilim_ma > PMI8998_WLED_ILIM_MAX_MA)
1856 wled->ilim_ma = PMI8998_WLED_ILIM_MAX_MA;
1857 } else {
1858 ilim_table = qpnp_wled_ilim_settings_pmi8994;
1859 if (wled->ilim_ma > PMI8994_WLED_ILIM_MAX_MA)
1860 wled->ilim_ma = PMI8994_WLED_ILIM_MAX_MA;
1861 }
1862
1863 for (i = 0; i < NUM_SUPPORTED_ILIM_THRESHOLDS; i++) {
1864 if (wled->ilim_ma == ilim_table[i])
1865 break;
1866 }
1867
1868 if (i == NUM_SUPPORTED_ILIM_THRESHOLDS) {
1869 dev_err(&wled->pdev->dev,
1870 "Invalid ilim threshold specified in device tree\n");
1871 return -EINVAL;
1872 }
1873
1874 reg = (i & QPNP_WLED_ILIM_MASK) | QPNP_WLED_ILIM_OVERWRITE;
1875 rc = qpnp_wled_masked_write_reg(wled,
1876 QPNP_WLED_ILIM_REG(wled->ctrl_base),
1877 QPNP_WLED_ILIM_MASK | QPNP_WLED_ILIM_OVERWRITE, reg);
1878 if (rc < 0)
1879 dev_err(&wled->pdev->dev, "Write to ILIM register failed, rc=%d\n",
1880 rc);
1881 return rc;
1882}
1883
1884static int qpnp_wled_vref_config(struct qpnp_wled *wled)
1885{
1886
1887 struct wled_vref_setting vref_setting;
1888 int rc;
1889 u8 reg = 0;
1890
1891 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1892 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
1893 vref_setting = vref_setting_pmi8998;
1894 else
1895 vref_setting = vref_setting_pmi8994;
1896
1897 if (wled->vref_uv < vref_setting.min_uv)
1898 wled->vref_uv = vref_setting.min_uv;
1899 else if (wled->vref_uv > vref_setting.max_uv)
1900 wled->vref_uv = vref_setting.max_uv;
1901
1902 reg |= DIV_ROUND_CLOSEST(wled->vref_uv - vref_setting.min_uv,
1903 vref_setting.step_uv);
1904
1905 rc = qpnp_wled_masked_write_reg(wled,
1906 QPNP_WLED_VREF_REG(wled->ctrl_base),
1907 QPNP_WLED_VREF_MASK, reg);
1908 if (rc)
1909 pr_err("Write VREF_REG failed, rc=%d\n", rc);
1910
1911 return rc;
1912}
1913
1914/* Configure WLED registers */
1915static int qpnp_wled_config(struct qpnp_wled *wled)
1916{
1917 int rc, i, temp;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05301918 u8 reg = 0, sink_en = 0, mask;
David Collins8885f792017-01-26 14:36:34 -08001919
1920 /* Configure display type */
1921 rc = qpnp_wled_set_disp(wled, wled->ctrl_base);
1922 if (rc < 0)
1923 return rc;
1924
1925 /* Configure the FEEDBACK OUTPUT register */
1926 rc = qpnp_wled_read_reg(wled, QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
1927 &reg);
1928 if (rc < 0)
1929 return rc;
1930 reg &= QPNP_WLED_FDBK_OP_MASK;
1931 reg |= wled->fdbk_op;
1932 rc = qpnp_wled_write_reg(wled, QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
1933 reg);
1934 if (rc)
1935 return rc;
1936
1937 /* Configure the VREF register */
1938 rc = qpnp_wled_vref_config(wled);
1939 if (rc < 0) {
1940 pr_err("Error in configuring wled vref, rc=%d\n", rc);
1941 return rc;
1942 }
1943
1944 /* Configure VLOOP_COMP_GM register */
1945 rc = qpnp_wled_gm_config(wled);
1946 if (rc < 0) {
1947 pr_err("Error in configureing wled gm, rc=%d\n", rc);
1948 return rc;
1949 }
1950
1951 /* Configure the ILIM register */
1952 rc = qpnp_wled_ilim_config(wled);
1953 if (rc < 0) {
1954 pr_err("Error in configuring wled ilim, rc=%d\n", rc);
1955 return rc;
1956 }
1957
1958 /* Configure auto PFM mode for LCD mode only */
1959 if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1960 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
1961 && !wled->disp_type_amoled) {
1962 reg = 0;
1963 reg |= wled->lcd_auto_pfm_thresh;
1964 reg |= wled->lcd_auto_pfm_en <<
1965 QPNP_WLED_LCD_AUTO_PFM_EN_SHIFT;
1966 rc = qpnp_wled_masked_write_reg(wled,
1967 QPNP_WLED_LCD_AUTO_PFM_REG(wled->ctrl_base),
1968 QPNP_WLED_LCD_AUTO_PFM_EN_BIT |
1969 QPNP_WLED_LCD_AUTO_PFM_THRESH_MASK, reg);
1970 if (rc < 0) {
1971 pr_err("Write LCD_AUTO_PFM failed, rc=%d\n", rc);
1972 return rc;
1973 }
1974 }
1975
1976 /* Configure the Soft start Ramp delay: for AMOLED - 0,for LCD - 2 */
1977 reg = (wled->disp_type_amoled) ? 0 : 2;
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07001978 mask = SOFTSTART_RAMP_DELAY_MASK;
1979 if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
1980 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
1981 && wled->disp_type_amoled) {
1982 reg |= SOFTSTART_OVERWRITE_BIT;
1983 mask |= SOFTSTART_OVERWRITE_BIT;
1984 }
1985
1986 rc = qpnp_wled_masked_write_reg(wled,
1987 QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base),
1988 mask, reg);
David Collins8885f792017-01-26 14:36:34 -08001989 if (rc)
1990 return rc;
1991
1992 /* Configure the MAX BOOST DUTY register */
1993 if (wled->boost_duty_ns < QPNP_WLED_BOOST_DUTY_MIN_NS)
1994 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
1995 else if (wled->boost_duty_ns > QPNP_WLED_BOOST_DUTY_MAX_NS)
1996 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MAX_NS;
1997
1998 rc = qpnp_wled_read_reg(wled, QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base),
1999 &reg);
2000 if (rc < 0)
2001 return rc;
2002 reg &= QPNP_WLED_BOOST_DUTY_MASK;
2003 reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS);
2004 rc = qpnp_wled_write_reg(wled,
2005 QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base), reg);
2006 if (rc)
2007 return rc;
2008
2009 /* Configure the SWITCHING FREQ register */
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -07002010 if (wled->switch_freq_khz == 1600)
2011 reg = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
David Collins8885f792017-01-26 14:36:34 -08002012 else
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -07002013 reg = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
David Collins8885f792017-01-26 14:36:34 -08002014
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -07002015 /*
2016 * Do not set the overwrite bit when switching frequency is selected
2017 * for AMOLED. This register is in logic reset block which can cause
2018 * the value to be overwritten during module enable/disable.
2019 */
2020 mask = QPNP_WLED_SWITCH_FREQ_MASK | QPNP_WLED_SWITCH_FREQ_OVERWRITE;
2021 if (!wled->disp_type_amoled)
2022 reg |= QPNP_WLED_SWITCH_FREQ_OVERWRITE;
2023
2024 rc = qpnp_wled_masked_write_reg(wled,
2025 QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), mask, reg);
David Collins8885f792017-01-26 14:36:34 -08002026 if (rc < 0)
2027 return rc;
David Collins8885f792017-01-26 14:36:34 -08002028
2029 rc = qpnp_wled_ovp_config(wled);
2030 if (rc < 0) {
2031 pr_err("Error in configuring OVP threshold, rc=%d\n", rc);
2032 return rc;
2033 }
2034
2035 if (is_avdd_trim_adjustment_required(wled)) {
2036 rc = qpnp_wled_avdd_trim_config(wled);
2037 if (rc < 0)
2038 return rc;
2039 }
2040
2041 rc = qpnp_wled_avdd_mode_config(wled);
2042 if (rc < 0)
2043 return rc;
2044
2045 /* Configure the MODULATION register */
2046 if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_1200_KHZ) {
2047 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_1200_KHZ;
2048 temp = 3;
2049 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_2400_KHZ) {
2050 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_2400_KHZ;
2051 temp = 2;
2052 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_9600_KHZ) {
2053 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
2054 temp = 1;
2055 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_19200_KHZ) {
2056 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
2057 temp = 0;
2058 } else {
2059 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
2060 temp = 1;
2061 }
2062
2063 rc = qpnp_wled_read_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), &reg);
2064 if (rc < 0)
2065 return rc;
2066 reg &= QPNP_WLED_MOD_FREQ_MASK;
2067 reg |= (temp << QPNP_WLED_MOD_FREQ_SHIFT);
2068
2069 reg &= QPNP_WLED_PHASE_STAG_MASK;
2070 reg |= (wled->en_phase_stag << QPNP_WLED_PHASE_STAG_SHIFT);
2071
2072 reg &= QPNP_WLED_ACC_CLK_FREQ_MASK;
2073 reg |= (temp << QPNP_WLED_ACC_CLK_FREQ_SHIFT);
2074
2075 reg &= QPNP_WLED_DIM_RES_MASK;
2076 reg |= (wled->en_9b_dim_res << QPNP_WLED_DIM_RES_SHIFT);
2077
2078 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
2079 reg &= QPNP_WLED_DIM_HYB_MASK;
2080 reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
2081 } else {
2082 reg &= QPNP_WLED_DIM_HYB_MASK;
2083 reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
2084 reg &= QPNP_WLED_DIM_ANA_MASK;
2085 reg |= wled->dim_mode;
2086 }
2087
2088 rc = qpnp_wled_write_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), reg);
2089 if (rc)
2090 return rc;
2091
2092 /* Configure the HYBRID THRESHOLD register */
Kiran Gundaa81bbb32018-09-11 15:03:54 +05302093 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
2094 if (wled->hyb_thres < QPNP_WLED_HYB_THRES_MIN)
2095 wled->hyb_thres = QPNP_WLED_HYB_THRES_MIN;
2096 else if (wled->hyb_thres > QPNP_WLED_HYB_THRES_MAX)
2097 wled->hyb_thres = QPNP_WLED_HYB_THRES_MAX;
David Collins8885f792017-01-26 14:36:34 -08002098
Kiran Gundaa81bbb32018-09-11 15:03:54 +05302099 rc = qpnp_wled_read_reg(wled,
2100 QPNP_WLED_HYB_THRES_REG(wled->sink_base),
2101 &reg);
2102 if (rc < 0)
2103 return rc;
2104 reg &= QPNP_WLED_HYB_THRES_MASK;
2105 temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1;
2106 reg |= temp;
2107 rc = qpnp_wled_write_reg(wled,
2108 QPNP_WLED_HYB_THRES_REG(wled->sink_base),
2109 reg);
2110 if (rc)
2111 return rc;
2112 }
David Collins8885f792017-01-26 14:36:34 -08002113
2114 /* Configure TEST5 register */
2115 if (wled->dim_mode == QPNP_WLED_DIM_DIGITAL) {
2116 reg = QPNP_WLED_SINK_TEST5_DIG;
2117 } else {
2118 reg = QPNP_WLED_SINK_TEST5_HYB;
2119 if (wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2120 reg |= QPNP_WLED_SINK_TEST5_HVG_PULL_STR_BIT;
2121 }
2122
2123 rc = qpnp_wled_sec_write_reg(wled,
2124 QPNP_WLED_SINK_TEST5_REG(wled->sink_base), reg);
2125 if (rc)
2126 return rc;
2127
2128 /* disable all current sinks and enable selected strings */
2129 reg = 0x00;
2130 rc = qpnp_wled_write_reg(wled, QPNP_WLED_CURR_SINK_REG(wled->sink_base),
2131 reg);
2132
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302133 for (i = 0; i < wled->max_strings; i++) {
2134 /* SYNC DELAY */
2135 if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US)
2136 wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US;
2137
2138 reg = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
2139 mask = QPNP_WLED_SYNC_DLY_MASK;
2140 rc = qpnp_wled_masked_write_reg(wled,
2141 QPNP_WLED_SYNC_DLY_REG(wled->sink_base, i),
2142 mask, reg);
2143 if (rc < 0)
2144 return rc;
2145
2146 /* FULL SCALE CURRENT */
2147 if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA)
2148 wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
2149
2150 reg = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
2151 mask = QPNP_WLED_FS_CURR_MASK;
2152 rc = qpnp_wled_masked_write_reg(wled,
2153 QPNP_WLED_FS_CURR_REG(wled->sink_base, i),
2154 mask, reg);
2155 if (rc < 0)
2156 return rc;
2157
2158 /* CABC */
2159 reg = wled->en_cabc ? (1 << QPNP_WLED_CABC_SHIFT) : 0;
2160 mask = QPNP_WLED_CABC_MASK;
2161 rc = qpnp_wled_masked_write_reg(wled,
2162 QPNP_WLED_CABC_REG(wled->sink_base, i),
2163 mask, reg);
2164 if (rc < 0)
2165 return rc;
2166 }
2167
2168 /* Settings specific to valid sinks */
David Collins8885f792017-01-26 14:36:34 -08002169 for (i = 0; i < wled->num_strings; i++) {
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302170 if (wled->strings[i] >= wled->max_strings) {
David Collins8885f792017-01-26 14:36:34 -08002171 dev_err(&wled->pdev->dev, "Invalid string number\n");
2172 return -EINVAL;
2173 }
David Collins8885f792017-01-26 14:36:34 -08002174 /* MODULATOR */
2175 rc = qpnp_wled_read_reg(wled,
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302176 QPNP_WLED_MOD_EN_REG(wled->sink_base, i), &reg);
David Collins8885f792017-01-26 14:36:34 -08002177 if (rc < 0)
2178 return rc;
2179 reg &= QPNP_WLED_MOD_EN_MASK;
2180 reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
2181
2182 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID)
2183 reg &= QPNP_WLED_GATE_DRV_MASK;
2184 else
2185 reg |= ~QPNP_WLED_GATE_DRV_MASK;
2186
2187 rc = qpnp_wled_write_reg(wled,
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302188 QPNP_WLED_MOD_EN_REG(wled->sink_base, i), reg);
David Collins8885f792017-01-26 14:36:34 -08002189 if (rc)
2190 return rc;
2191
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302192 /* SINK EN */
David Collins8885f792017-01-26 14:36:34 -08002193 temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302194 sink_en |= (1 << temp);
2195 }
2196 mask = QPNP_WLED_CURR_SINK_MASK;
2197 rc = qpnp_wled_masked_write_reg(wled,
2198 QPNP_WLED_CURR_SINK_REG(wled->sink_base),
2199 mask, sink_en);
2200 if (rc < 0) {
2201 dev_err(&wled->pdev->dev,
2202 "Failed to enable WLED sink config rc = %d\n", rc);
2203 return rc;
David Collins8885f792017-01-26 14:36:34 -08002204 }
2205
2206 rc = qpnp_wled_sync_reg_toggle(wled);
2207 if (rc < 0) {
2208 dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
2209 return rc;
2210 }
2211
Anirudh Ghayalce9914f2017-08-31 12:11:06 +05302212 rc = qpnp_wled_auto_calibrate_at_init(wled);
2213 if (rc < 0)
2214 pr_err("Failed to auto-calibrate at init rc=%d\n", rc);
2215
David Collins8885f792017-01-26 14:36:34 -08002216 /* setup ovp and sc irqs */
2217 if (wled->ovp_irq >= 0) {
Kiran Gundae4648712018-07-19 15:02:59 +05302218 irq_set_status_flags(wled->ovp_irq, IRQ_DISABLE_UNLAZY);
David Collins8885f792017-01-26 14:36:34 -08002219 rc = devm_request_threaded_irq(&wled->pdev->dev, wled->ovp_irq,
2220 NULL, qpnp_wled_ovp_irq_handler, IRQF_ONESHOT,
2221 "qpnp_wled_ovp_irq", wled);
2222 if (rc < 0) {
2223 dev_err(&wled->pdev->dev,
2224 "Unable to request ovp(%d) IRQ(err:%d)\n",
2225 wled->ovp_irq, rc);
2226 return rc;
2227 }
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302228 rc = qpnp_wled_read_reg(wled,
2229 QPNP_WLED_MODULE_EN_REG(wled->ctrl_base), &reg);
2230 /* disable the OVP irq only if the module is not enabled */
2231 if (!rc && !(reg & QPNP_WLED_MODULE_EN_MASK)) {
2232 disable_irq(wled->ovp_irq);
2233 wled->ovp_irq_disabled = true;
2234 }
David Collins8885f792017-01-26 14:36:34 -08002235 }
2236
2237 if (wled->sc_irq >= 0) {
2238 wled->sc_cnt = 0;
Kiran Gundae4648712018-07-19 15:02:59 +05302239 irq_set_status_flags(wled->sc_irq, IRQ_DISABLE_UNLAZY);
David Collins8885f792017-01-26 14:36:34 -08002240 rc = devm_request_threaded_irq(&wled->pdev->dev, wled->sc_irq,
2241 NULL, qpnp_wled_sc_irq_handler, IRQF_ONESHOT,
2242 "qpnp_wled_sc_irq", wled);
2243 if (rc < 0) {
2244 dev_err(&wled->pdev->dev,
2245 "Unable to request sc(%d) IRQ(err:%d)\n",
2246 wled->sc_irq, rc);
2247 return rc;
2248 }
2249
2250 rc = qpnp_wled_read_reg(wled,
2251 QPNP_WLED_SC_PRO_REG(wled->ctrl_base), &reg);
2252 if (rc < 0)
2253 return rc;
2254 reg &= QPNP_WLED_EN_SC_DEB_CYCLES_MASK;
2255 reg |= 1 << QPNP_WLED_EN_SC_SHIFT;
2256
2257 if (wled->sc_deb_cycles < QPNP_WLED_SC_DEB_CYCLES_MIN)
2258 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MIN;
2259 else if (wled->sc_deb_cycles > QPNP_WLED_SC_DEB_CYCLES_MAX)
2260 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MAX;
2261 temp = fls(wled->sc_deb_cycles) - QPNP_WLED_SC_DEB_CYCLES_SUB;
2262 reg |= (temp << 1);
2263
2264 if (wled->disp_type_amoled)
2265 reg |= QPNP_WLED_SC_PRO_EN_DSCHGR;
2266
2267 rc = qpnp_wled_write_reg(wled,
2268 QPNP_WLED_SC_PRO_REG(wled->ctrl_base), reg);
2269 if (rc)
2270 return rc;
2271
2272 if (wled->en_ext_pfet_sc_pro) {
Subbaraman Narayanamurthy50dae7a2018-05-18 15:34:19 -07002273 if (!(wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE
2274 && wled->pmic_rev_id->rev4 ==
2275 PMI8998_V2P0_REV4)) {
2276 reg = QPNP_WLED_EXT_FET_DTEST2;
2277 rc = qpnp_wled_sec_write_reg(wled,
David Collins8885f792017-01-26 14:36:34 -08002278 QPNP_WLED_TEST1_REG(wled->ctrl_base),
2279 reg);
Subbaraman Narayanamurthy50dae7a2018-05-18 15:34:19 -07002280 if (rc)
2281 return rc;
2282 }
David Collins8885f792017-01-26 14:36:34 -08002283 }
2284 } else {
2285 rc = qpnp_wled_read_reg(wled,
2286 QPNP_WLED_SC_PRO_REG(wled->ctrl_base), &reg);
2287 if (rc < 0)
2288 return rc;
2289 reg &= QPNP_WLED_EN_DEB_CYCLES_MASK;
2290
2291 if (wled->sc_deb_cycles < QPNP_WLED_SC_DEB_CYCLES_MIN)
2292 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MIN;
2293 else if (wled->sc_deb_cycles > QPNP_WLED_SC_DEB_CYCLES_MAX)
2294 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MAX;
2295 temp = fls(wled->sc_deb_cycles) - QPNP_WLED_SC_DEB_CYCLES_SUB;
2296 reg |= (temp << 1);
2297
2298 rc = qpnp_wled_write_reg(wled,
2299 QPNP_WLED_SC_PRO_REG(wled->ctrl_base), reg);
2300 if (rc)
2301 return rc;
2302 }
2303
2304 return 0;
2305}
2306
2307/* parse wled dtsi parameters */
2308static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
2309{
2310 struct platform_device *pdev = wled->pdev;
2311 struct property *prop;
2312 const char *temp_str;
2313 u32 temp_val;
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08002314 int rc, i, size;
David Collins8885f792017-01-26 14:36:34 -08002315 u8 *strings;
2316
2317 wled->cdev.name = "wled";
2318 rc = of_property_read_string(pdev->dev.of_node,
2319 "linux,name", &wled->cdev.name);
2320 if (rc && (rc != -EINVAL)) {
2321 dev_err(&pdev->dev, "Unable to read led name\n");
2322 return rc;
2323 }
2324
2325 wled->cdev.default_trigger = QPNP_WLED_TRIGGER_NONE;
2326 rc = of_property_read_string(pdev->dev.of_node, "linux,default-trigger",
2327 &wled->cdev.default_trigger);
2328 if (rc && (rc != -EINVAL)) {
2329 dev_err(&pdev->dev, "Unable to read led trigger\n");
2330 return rc;
2331 }
2332
Subbaraman Narayanamurthydd687612017-12-05 17:36:43 -08002333 if (of_find_property(pdev->dev.of_node, "qcom,wled-brightness-map",
2334 NULL)) {
2335 size = of_property_count_elems_of_size(pdev->dev.of_node,
2336 "qcom,wled-brightness-map", sizeof(u16));
2337 if (size != NUM_DDIC_CODES) {
2338 pr_err("Invalid WLED brightness map size:%d\n", size);
2339 return rc;
2340 }
2341
2342 wled->brt_map_table = devm_kcalloc(&pdev->dev, NUM_DDIC_CODES,
2343 sizeof(u16), GFP_KERNEL);
2344 if (!wled->brt_map_table)
2345 return -ENOMEM;
2346
2347 rc = of_property_read_u16_array(pdev->dev.of_node,
2348 "qcom,wled-brightness-map", wled->brt_map_table,
2349 NUM_DDIC_CODES);
2350 if (rc < 0) {
2351 pr_err("Error in reading WLED brightness map, rc=%d\n",
2352 rc);
2353 return rc;
2354 }
2355
2356 for (i = 0; i < NUM_DDIC_CODES; i++) {
2357 if (wled->brt_map_table[i] > WLED_MAX_LEVEL_4095) {
2358 pr_err("WLED brightness map not in range\n");
2359 return -EDOM;
2360 }
2361
2362 if ((i > 1) && wled->brt_map_table[i]
2363 < wled->brt_map_table[i - 1]) {
2364 pr_err("WLED brightness map not in ascending order?\n");
2365 return -EDOM;
2366 }
2367 }
2368 }
2369
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07002370 wled->stepper_en = of_property_read_bool(pdev->dev.of_node,
2371 "qcom,wled-stepper-en");
David Collins8885f792017-01-26 14:36:34 -08002372 wled->disp_type_amoled = of_property_read_bool(pdev->dev.of_node,
2373 "qcom,disp-type-amoled");
2374 if (wled->disp_type_amoled) {
2375 wled->vref_psm_mv = QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV;
2376 rc = of_property_read_u32(pdev->dev.of_node,
2377 "qcom,vref-psm-mv", &temp_val);
2378 if (!rc) {
2379 wled->vref_psm_mv = temp_val;
2380 } else if (rc != -EINVAL) {
2381 dev_err(&pdev->dev, "Unable to read vref-psm\n");
2382 return rc;
2383 }
2384
Subbaraman Narayanamurthy80ee57af2017-10-19 18:38:09 -07002385 wled->loop_comp_res_kohm = 320;
2386 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2387 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2388 wled->loop_comp_res_kohm = 300;
2389
David Collins8885f792017-01-26 14:36:34 -08002390 rc = of_property_read_u32(pdev->dev.of_node,
2391 "qcom,loop-comp-res-kohm", &temp_val);
2392 if (!rc) {
2393 wled->loop_comp_res_kohm = temp_val;
2394 } else if (rc != -EINVAL) {
2395 dev_err(&pdev->dev, "Unable to read loop-comp-res-kohm\n");
2396 return rc;
2397 }
2398
2399 wled->avdd_mode_spmi = of_property_read_bool(pdev->dev.of_node,
2400 "qcom,avdd-mode-spmi");
2401
2402 wled->avdd_target_voltage_mv = QPNP_WLED_DFLT_AVDD_MV;
2403 rc = of_property_read_u32(pdev->dev.of_node,
2404 "qcom,avdd-target-voltage-mv", &temp_val);
2405 if (!rc) {
2406 wled->avdd_target_voltage_mv = temp_val;
2407 } else if (rc != -EINVAL) {
2408 dev_err(&pdev->dev, "Unable to read avdd target voltage\n");
2409 return rc;
2410 }
2411 }
2412
2413 if (wled->disp_type_amoled) {
2414 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2415 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2416 wled->loop_ea_gm =
2417 QPNP_WLED_LOOP_GM_DFLT_AMOLED_PMI8998;
2418 else
2419 wled->loop_ea_gm =
2420 QPNP_WLED_LOOP_EA_GM_DFLT_AMOLED_PMI8994;
2421 } else {
2422 wled->loop_ea_gm = QPNP_WLED_LOOP_GM_DFLT_WLED;
2423 }
2424
2425 rc = of_property_read_u32(pdev->dev.of_node,
2426 "qcom,loop-ea-gm", &temp_val);
2427 if (!rc) {
2428 wled->loop_ea_gm = temp_val;
2429 } else if (rc != -EINVAL) {
2430 dev_err(&pdev->dev, "Unable to read loop-ea-gm\n");
2431 return rc;
2432 }
2433
2434 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2435 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
2436 wled->loop_auto_gm_en =
2437 of_property_read_bool(pdev->dev.of_node,
2438 "qcom,loop-auto-gm-en");
2439 wled->loop_auto_gm_thresh = QPNP_WLED_LOOP_AUTO_GM_DFLT_THRESH;
2440 rc = of_property_read_u8(pdev->dev.of_node,
2441 "qcom,loop-auto-gm-thresh",
2442 &wled->loop_auto_gm_thresh);
2443 if (rc && rc != -EINVAL) {
2444 dev_err(&pdev->dev,
2445 "Unable to read loop-auto-gm-thresh\n");
2446 return rc;
2447 }
2448 }
2449
2450 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2451 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
2452
Anirudh Ghayal39526a22017-10-11 19:15:24 +05302453 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE &&
2454 wled->pmic_rev_id->rev4 == PMI8998_V2P0_REV4)
David Collins8885f792017-01-26 14:36:34 -08002455 wled->lcd_auto_pfm_en = false;
2456 else
2457 wled->lcd_auto_pfm_en = true;
2458
2459 wled->lcd_auto_pfm_thresh = QPNP_WLED_LCD_AUTO_PFM_DFLT_THRESH;
2460 rc = of_property_read_u8(pdev->dev.of_node,
2461 "qcom,lcd-auto-pfm-thresh",
2462 &wled->lcd_auto_pfm_thresh);
2463 if (rc && rc != -EINVAL) {
2464 dev_err(&pdev->dev,
2465 "Unable to read lcd-auto-pfm-thresh\n");
2466 return rc;
2467 }
2468
2469 if (wled->lcd_auto_pfm_thresh >
2470 QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX)
2471 wled->lcd_auto_pfm_thresh =
2472 QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX;
2473 }
2474
2475 wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_DFLT;
2476 rc = of_property_read_u32(pdev->dev.of_node,
2477 "qcom,sc-deb-cycles", &temp_val);
2478 if (!rc) {
2479 wled->sc_deb_cycles = temp_val;
2480 } else if (rc != -EINVAL) {
2481 dev_err(&pdev->dev, "Unable to read sc debounce cycles\n");
2482 return rc;
2483 }
2484
2485 wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
2486 rc = of_property_read_string(pdev->dev.of_node,
2487 "qcom,fdbk-output", &temp_str);
2488 if (!rc) {
2489 if (strcmp(temp_str, "wled1") == 0)
2490 wled->fdbk_op = QPNP_WLED_FDBK_WLED1;
2491 else if (strcmp(temp_str, "wled2") == 0)
2492 wled->fdbk_op = QPNP_WLED_FDBK_WLED2;
2493 else if (strcmp(temp_str, "wled3") == 0)
2494 wled->fdbk_op = QPNP_WLED_FDBK_WLED3;
2495 else if (strcmp(temp_str, "wled4") == 0)
2496 wled->fdbk_op = QPNP_WLED_FDBK_WLED4;
2497 else
2498 wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
2499 } else if (rc != -EINVAL) {
2500 dev_err(&pdev->dev, "Unable to read feedback output\n");
2501 return rc;
2502 }
2503
2504 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2505 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2506 wled->vref_uv = vref_setting_pmi8998.default_uv;
2507 else
2508 wled->vref_uv = vref_setting_pmi8994.default_uv;
2509 rc = of_property_read_u32(pdev->dev.of_node,
2510 "qcom,vref-uv", &temp_val);
2511 if (!rc) {
2512 wled->vref_uv = temp_val;
2513 } else if (rc != -EINVAL) {
2514 dev_err(&pdev->dev, "Unable to read vref\n");
2515 return rc;
2516 }
2517
Subbaraman Narayanamurthy644b09ac2017-09-12 15:36:37 -07002518 wled->switch_freq_khz = wled->disp_type_amoled ? 1600 : 800;
David Collins8885f792017-01-26 14:36:34 -08002519 rc = of_property_read_u32(pdev->dev.of_node,
2520 "qcom,switch-freq-khz", &temp_val);
2521 if (!rc) {
2522 wled->switch_freq_khz = temp_val;
2523 } else if (rc != -EINVAL) {
2524 dev_err(&pdev->dev, "Unable to read switch freq\n");
2525 return rc;
2526 }
2527
2528 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2529 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2530 wled->ovp_mv = 29600;
2531 else
2532 wled->ovp_mv = 29500;
2533 rc = of_property_read_u32(pdev->dev.of_node,
2534 "qcom,ovp-mv", &temp_val);
2535 if (!rc) {
2536 wled->ovp_mv = temp_val;
2537 } else if (rc != -EINVAL) {
2538 dev_err(&pdev->dev, "Unable to read ovp\n");
2539 return rc;
2540 }
2541
2542 if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
2543 wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
2544 if (wled->disp_type_amoled)
2545 wled->ilim_ma = PMI8998_AMOLED_DFLT_ILIM_MA;
2546 else
2547 wled->ilim_ma = PMI8998_WLED_DFLT_ILIM_MA;
2548 } else {
2549 if (wled->disp_type_amoled)
2550 wled->ilim_ma = PMI8994_AMOLED_DFLT_ILIM_MA;
2551 else
2552 wled->ilim_ma = PMI8994_WLED_DFLT_ILIM_MA;
2553 }
2554
2555 rc = of_property_read_u32(pdev->dev.of_node,
2556 "qcom,ilim-ma", &temp_val);
2557 if (!rc) {
2558 wled->ilim_ma = temp_val;
2559 } else if (rc != -EINVAL) {
2560 dev_err(&pdev->dev, "Unable to read ilim\n");
2561 return rc;
2562 }
2563
2564 wled->boost_duty_ns = QPNP_WLED_DEF_BOOST_DUTY_NS;
2565 rc = of_property_read_u32(pdev->dev.of_node,
2566 "qcom,boost-duty-ns", &temp_val);
2567 if (!rc) {
2568 wled->boost_duty_ns = temp_val;
2569 } else if (rc != -EINVAL) {
2570 dev_err(&pdev->dev, "Unable to read boost duty\n");
2571 return rc;
2572 }
2573
2574 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
2575 rc = of_property_read_u32(pdev->dev.of_node,
2576 "qcom,mod-freq-khz", &temp_val);
2577 if (!rc) {
2578 wled->mod_freq_khz = temp_val;
2579 } else if (rc != -EINVAL) {
2580 dev_err(&pdev->dev, "Unable to read modulation freq\n");
2581 return rc;
2582 }
2583
2584 wled->dim_mode = QPNP_WLED_DIM_HYBRID;
2585 rc = of_property_read_string(pdev->dev.of_node,
2586 "qcom,dim-mode", &temp_str);
2587 if (!rc) {
2588 if (strcmp(temp_str, "analog") == 0)
2589 wled->dim_mode = QPNP_WLED_DIM_ANALOG;
2590 else if (strcmp(temp_str, "digital") == 0)
2591 wled->dim_mode = QPNP_WLED_DIM_DIGITAL;
2592 else
2593 wled->dim_mode = QPNP_WLED_DIM_HYBRID;
2594 } else if (rc != -EINVAL) {
2595 dev_err(&pdev->dev, "Unable to read dim mode\n");
2596 return rc;
2597 }
2598
2599 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
2600 wled->hyb_thres = QPNP_WLED_DEF_HYB_THRES;
2601 rc = of_property_read_u32(pdev->dev.of_node,
2602 "qcom,hyb-thres", &temp_val);
2603 if (!rc) {
2604 wled->hyb_thres = temp_val;
2605 } else if (rc != -EINVAL) {
2606 dev_err(&pdev->dev, "Unable to read hyb threshold\n");
2607 return rc;
2608 }
2609 }
2610
2611 wled->sync_dly_us = QPNP_WLED_DEF_SYNC_DLY_US;
2612 rc = of_property_read_u32(pdev->dev.of_node,
2613 "qcom,sync-dly-us", &temp_val);
2614 if (!rc) {
2615 wled->sync_dly_us = temp_val;
2616 } else if (rc != -EINVAL) {
2617 dev_err(&pdev->dev, "Unable to read sync delay\n");
2618 return rc;
2619 }
2620
2621 wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
2622 rc = of_property_read_u32(pdev->dev.of_node,
2623 "qcom,fs-curr-ua", &temp_val);
2624 if (!rc) {
2625 wled->fs_curr_ua = temp_val;
2626 } else if (rc != -EINVAL) {
2627 dev_err(&pdev->dev, "Unable to read full scale current\n");
2628 return rc;
2629 }
2630
2631 wled->cons_sync_write_delay_us = 0;
2632 rc = of_property_read_u32(pdev->dev.of_node,
2633 "qcom,cons-sync-write-delay-us", &temp_val);
2634 if (!rc)
2635 wled->cons_sync_write_delay_us = temp_val;
2636
2637 wled->en_9b_dim_res = of_property_read_bool(pdev->dev.of_node,
2638 "qcom,en-9b-dim-res");
2639 wled->en_phase_stag = of_property_read_bool(pdev->dev.of_node,
2640 "qcom,en-phase-stag");
2641 wled->en_cabc = of_property_read_bool(pdev->dev.of_node,
2642 "qcom,en-cabc");
2643
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302644 if (wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
2645 wled->max_strings = QPNP_PM660_WLED_MAX_STRINGS;
2646 else
2647 wled->max_strings = QPNP_WLED_MAX_STRINGS;
2648
Kiran Gundaa81bbb32018-09-11 15:03:54 +05302649 temp_val = 0;
David Collins8885f792017-01-26 14:36:34 -08002650 prop = of_find_property(pdev->dev.of_node,
2651 "qcom,led-strings-list", &temp_val);
Kiran Gundaa81bbb32018-09-11 15:03:54 +05302652 if (!prop || temp_val > QPNP_WLED_MAX_STRINGS) {
David Collins8885f792017-01-26 14:36:34 -08002653 dev_err(&pdev->dev, "Invalid strings info, use default");
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302654 wled->num_strings = wled->max_strings;
David Collins8885f792017-01-26 14:36:34 -08002655 for (i = 0; i < wled->num_strings; i++)
2656 wled->strings[i] = i;
2657 } else {
2658 wled->num_strings = temp_val;
2659 strings = prop->value;
2660 for (i = 0; i < wled->num_strings; ++i)
2661 wled->strings[i] = strings[i];
2662 }
2663
2664 wled->ovp_irq = platform_get_irq_byname(pdev, "ovp-irq");
2665 if (wled->ovp_irq < 0)
2666 dev_dbg(&pdev->dev, "ovp irq is not used\n");
2667
2668 wled->sc_irq = platform_get_irq_byname(pdev, "sc-irq");
2669 if (wled->sc_irq < 0)
2670 dev_dbg(&pdev->dev, "sc irq is not used\n");
2671
2672 wled->en_ext_pfet_sc_pro = of_property_read_bool(pdev->dev.of_node,
2673 "qcom,en-ext-pfet-sc-pro");
2674
Subbaraman Narayanamurthy55698842017-02-17 15:48:47 -08002675 wled->lcd_psm_ctrl = of_property_read_bool(pdev->dev.of_node,
2676 "qcom,lcd-psm-ctrl");
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302677
2678 wled->auto_calib_enabled = of_property_read_bool(pdev->dev.of_node,
2679 "qcom,auto-calibration-enable");
David Collins8885f792017-01-26 14:36:34 -08002680 return 0;
2681}
2682
2683static int qpnp_wled_probe(struct platform_device *pdev)
2684{
2685 struct qpnp_wled *wled;
2686 struct device_node *revid_node;
2687 int rc = 0, i;
2688 const __be32 *prop;
2689
2690 wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL);
2691 if (!wled)
2692 return -ENOMEM;
Stephen Boydd71c7f62017-03-20 15:49:04 -07002693
2694 wled->regmap = dev_get_regmap(pdev->dev.parent, NULL);
2695 if (!wled->regmap) {
2696 dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
2697 return -EINVAL;
2698 }
David Collins8885f792017-01-26 14:36:34 -08002699
2700 wled->pdev = pdev;
2701
2702 revid_node = of_parse_phandle(pdev->dev.of_node, "qcom,pmic-revid", 0);
2703 if (!revid_node) {
2704 pr_err("Missing qcom,pmic-revid property - driver failed\n");
2705 return -EINVAL;
2706 }
2707
2708 wled->pmic_rev_id = get_revid_data(revid_node);
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07002709 of_node_put(revid_node);
David Collins8885f792017-01-26 14:36:34 -08002710 if (IS_ERR_OR_NULL(wled->pmic_rev_id)) {
2711 pr_err("Unable to get pmic_revid rc=%ld\n",
2712 PTR_ERR(wled->pmic_rev_id));
2713 /*
2714 * the revid peripheral must be registered, any failure
2715 * here only indicates that the rev-id module has not
2716 * probed yet.
2717 */
2718 return -EPROBE_DEFER;
2719 }
2720
2721 pr_debug("PMIC subtype %d Digital major %d\n",
2722 wled->pmic_rev_id->pmic_subtype, wled->pmic_rev_id->rev4);
2723
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07002724 wled->wq = alloc_ordered_workqueue("qpnp_wled_wq", WQ_HIGHPRI);
2725 if (!wled->wq) {
2726 pr_err("Unable to alloc workqueue for WLED\n");
2727 return -ENOMEM;
2728 }
2729
David Collins8885f792017-01-26 14:36:34 -08002730 prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_SINK_BASE,
Subbaraman Narayanamurthy8b9dd592017-08-23 18:44:15 -07002731 NULL, NULL);
David Collins8885f792017-01-26 14:36:34 -08002732 if (!prop) {
2733 dev_err(&pdev->dev, "Couldnt find sink's addr rc %d\n", rc);
2734 return rc;
2735 }
2736 wled->sink_base = be32_to_cpu(*prop);
2737
2738 prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_CTRL_BASE,
Subbaraman Narayanamurthy8b9dd592017-08-23 18:44:15 -07002739 NULL, NULL);
David Collins8885f792017-01-26 14:36:34 -08002740 if (!prop) {
2741 dev_err(&pdev->dev, "Couldnt find ctrl's addr rc = %d\n", rc);
2742 return rc;
2743 }
2744 wled->ctrl_base = be32_to_cpu(*prop);
2745
2746 dev_set_drvdata(&pdev->dev, wled);
2747
2748 rc = qpnp_wled_parse_dt(wled);
2749 if (rc) {
2750 dev_err(&pdev->dev, "DT parsing failed\n");
2751 return rc;
2752 }
2753
2754 mutex_init(&wled->bus_lock);
Anirudh Ghayal101e4ef2017-05-26 16:51:53 +05302755 mutex_init(&wled->lock);
David Collins8885f792017-01-26 14:36:34 -08002756 rc = qpnp_wled_config(wled);
2757 if (rc) {
2758 dev_err(&pdev->dev, "wled config failed\n");
2759 return rc;
2760 }
2761
David Collins8885f792017-01-26 14:36:34 -08002762 INIT_WORK(&wled->work, qpnp_wled_work);
2763 wled->ramp_ms = QPNP_WLED_RAMP_DLY_MS;
2764 wled->ramp_step = 1;
2765
2766 wled->cdev.brightness_set = qpnp_wled_set;
2767 wled->cdev.brightness_get = qpnp_wled_get;
2768
2769 wled->cdev.max_brightness = WLED_MAX_LEVEL_4095;
2770
2771 rc = led_classdev_register(&pdev->dev, &wled->cdev);
2772 if (rc) {
2773 dev_err(&pdev->dev, "wled registration failed(%d)\n", rc);
2774 goto wled_register_fail;
2775 }
2776
2777 for (i = 0; i < ARRAY_SIZE(qpnp_wled_attrs); i++) {
2778 rc = sysfs_create_file(&wled->cdev.dev->kobj,
2779 &qpnp_wled_attrs[i].attr);
2780 if (rc < 0) {
2781 dev_err(&pdev->dev, "sysfs creation failed\n");
2782 goto sysfs_fail;
2783 }
2784 }
2785
2786 return 0;
2787
2788sysfs_fail:
2789 for (i--; i >= 0; i--)
2790 sysfs_remove_file(&wled->cdev.dev->kobj,
2791 &qpnp_wled_attrs[i].attr);
2792 led_classdev_unregister(&wled->cdev);
2793wled_register_fail:
2794 cancel_work_sync(&wled->work);
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07002795 destroy_workqueue(wled->wq);
David Collins8885f792017-01-26 14:36:34 -08002796 mutex_destroy(&wled->lock);
2797 return rc;
2798}
2799
2800static int qpnp_wled_remove(struct platform_device *pdev)
2801{
2802 struct qpnp_wled *wled = dev_get_drvdata(&pdev->dev);
2803 int i;
2804
2805 for (i = 0; i < ARRAY_SIZE(qpnp_wled_attrs); i++)
2806 sysfs_remove_file(&wled->cdev.dev->kobj,
2807 &qpnp_wled_attrs[i].attr);
2808
2809 led_classdev_unregister(&wled->cdev);
2810 cancel_work_sync(&wled->work);
Subbaraman Narayanamurthy3c68f422017-09-11 20:01:27 -07002811 destroy_workqueue(wled->wq);
David Collins8885f792017-01-26 14:36:34 -08002812 mutex_destroy(&wled->lock);
2813
2814 return 0;
2815}
2816
2817static const struct of_device_id spmi_match_table[] = {
2818 { .compatible = "qcom,qpnp-wled",},
2819 { },
2820};
2821
2822static struct platform_driver qpnp_wled_driver = {
2823 .driver = {
2824 .name = "qcom,qpnp-wled",
2825 .of_match_table = spmi_match_table,
2826 },
2827 .probe = qpnp_wled_probe,
2828 .remove = qpnp_wled_remove,
2829};
2830
2831static int __init qpnp_wled_init(void)
2832{
2833 return platform_driver_register(&qpnp_wled_driver);
2834}
Subbaraman Narayanamurthy67e30fb2017-05-01 16:29:34 -07002835subsys_initcall(qpnp_wled_init);
David Collins8885f792017-01-26 14:36:34 -08002836
2837static void __exit qpnp_wled_exit(void)
2838{
2839 platform_driver_unregister(&qpnp_wled_driver);
2840}
2841module_exit(qpnp_wled_exit);
2842
2843MODULE_DESCRIPTION("QPNP WLED driver");
2844MODULE_LICENSE("GPL v2");
2845MODULE_ALIAS("leds:leds-qpnp-wled");