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