blob: 9cedbb7eeb2d170dd70564601f0cf6093459cef5 [file] [log] [blame]
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +05301 /* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
Aparna Mallavarapu083766b2014-07-21 21:04:48 +05302
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
12 * * Neither the name of The Linux Foundation, Inc. nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <stdio.h>
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -080030#include <string.h>
31#include <stdlib.h>
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053032#include <err.h>
33#include <qpnp_wled.h>
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -080034#include <pm8x41_wled.h>
35#include <qtimer.h>
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053036
37static int fls(uint16_t n)
38{
39 int i = 0;
40 for (; n; n >>= 1, i++);
41 return i;
42}
43
44static struct qpnp_wled *gwled;
Aparna Mallavarapua42de902014-12-08 17:25:44 -080045static int qpnp_labibb_regulator_set_voltage(struct qpnp_wled *wled);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053046
47static int qpnp_wled_sec_access(struct qpnp_wled *wled, uint16_t base_addr)
48{
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053049 uint8_t reg = QPNP_WLED_SEC_UNLOCK;
50
51 pm8x41_wled_reg_write(QPNP_WLED_SEC_ACCESS_REG(base_addr), reg);
52
53 return 0;
54}
55
56/* set wled to a level of brightness */
57static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
58{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -080059 int i;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053060 uint8_t reg;
61
62 /* set brightness registers */
63 for (i = 0; i < wled->num_strings; i++) {
64 reg = level & QPNP_WLED_BRIGHT_LSB_MASK;
65 pm8x41_wled_reg_write(QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base,
66 wled->strings[i]), reg);
67
68 reg = level >> QPNP_WLED_BRIGHT_MSB_SHIFT;
69 reg = reg & QPNP_WLED_BRIGHT_MSB_MASK;
70 pm8x41_wled_reg_write(QPNP_WLED_BRIGHT_MSB_REG(wled->sink_base,
71 wled->strings[i]), reg);
72 }
73
74 /* sync */
75 reg = QPNP_WLED_SYNC;
76 pm8x41_wled_reg_write(QPNP_WLED_SYNC_REG(wled->sink_base), reg);
77
78 reg = QPNP_WLED_SYNC_RESET;
79 pm8x41_wled_reg_write(QPNP_WLED_SYNC_REG(wled->sink_base), reg);
80
81 return 0;
82}
83
84static int qpnp_wled_enable(struct qpnp_wled *wled,
85 uint16_t base_addr, bool state)
86{
87 uint8_t reg;
88
89 reg = pm8x41_wled_reg_read(
90 QPNP_WLED_MODULE_EN_REG(base_addr));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053091 reg &= QPNP_WLED_MODULE_EN_MASK;
92 reg |= (state << QPNP_WLED_MODULE_EN_SHIFT);
93 pm8x41_wled_reg_write(QPNP_WLED_MODULE_EN_REG(base_addr), reg);
94
95 return 0;
96}
97
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +053098static int qpnp_wled_ibb_swire_rdy(struct qpnp_wled *wled,
99 uint16_t base_addr, bool state)
100{
101 uint8_t reg;
102
103 reg = pm8x41_wled_reg_read(
104 QPNP_WLED_MODULE_EN_REG(base_addr));
105 /* Do not enable IBB module when SWIRE ready is set */
106 reg &= ~(QPNP_IBB_SWIRE_RDY_MASK | QPNP_IBB_MODULE_EN_MASK);
107 reg |= (state << QPNP_IBB_SWIRE_RDY_SHIFT);
108 pm8x41_wled_reg_write(QPNP_WLED_MODULE_EN_REG(base_addr), reg);
109
110 return 0;
111}
112
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530113int qpnp_ibb_enable(bool state)
114{
115 int rc = 0;
116 uint8_t reg;
117
118 if (!gwled) {
119 dprintf(CRITICAL, "%s: wled is not initialized yet\n", __func__);
120 return ERROR;
121 }
122
123 /* enable lab */
124 if (gwled->ibb_bias_active) {
125 rc = qpnp_wled_enable(gwled, gwled->lab_base, state);
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800126 udelay(QPNP_WLED_LAB_START_DLY_US + 1);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530127 if (rc < 0)
128 return rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530129 } else {
130 reg = pm8x41_wled_reg_read(QPNP_WLED_LAB_IBB_RDY_REG(gwled->lab_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530131
132 reg &= QPNP_WLED_MODULE_EN_MASK;
133 reg |= (state << QPNP_WLED_MODULE_EN_SHIFT);
134 pm8x41_wled_reg_write(QPNP_WLED_LAB_IBB_RDY_REG(gwled->lab_base), reg);
135 }
136
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530137 if (gwled->disp_type_amoled && gwled->lab_ibb_swire_control)
138 rc = qpnp_wled_ibb_swire_rdy(gwled, gwled->ibb_base, state);
139 else
140 rc = qpnp_wled_enable(gwled, gwled->ibb_base, state);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530141
142 return rc;
143}
144
145/* enable / disable wled brightness */
146void qpnp_wled_enable_backlight(int enable)
147{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800148 int rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530149
150 if (!gwled) {
151 dprintf(CRITICAL, "%s: wled is not initialized yet\n", __func__);
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800152 return;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530153 }
154
155 if (enable) {
156 rc = qpnp_wled_set_level(gwled, QPNP_WLED_MAX_BR_LEVEL);
157 if (rc) {
158 dprintf(CRITICAL,"wled set level failed\n");
159 return;
160 }
161 }
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530162
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530163 if (!gwled->disp_type_amoled || !gwled->wled_avdd_control) {
164 rc = qpnp_wled_enable(gwled, gwled->ctrl_base, enable);
165 if (rc) {
166 dprintf(CRITICAL, "wled %sable failed\n",
167 enable ? "en" : "dis");
168 return;
169 }
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530170 }
171
172}
173
174static int qpnp_wled_set_display_type(struct qpnp_wled *wled, uint16_t base_addr)
175{
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530176 uint8_t reg = 0;
Parth Dixitb1461bf2015-06-24 20:51:08 +0530177 int rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530178
179 /* display type */
180 reg = pm8x41_wled_reg_read(QPNP_WLED_DISP_SEL_REG(base_addr));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530181
182 reg &= QPNP_WLED_DISP_SEL_MASK;
183 reg |= (wled->disp_type_amoled << QPNP_WLED_DISP_SEL_SHIFT);
Parth Dixitb1461bf2015-06-24 20:51:08 +0530184
185 rc = qpnp_wled_sec_access(wled, base_addr);
186 if (rc)
187 return rc;
188
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530189 pm8x41_wled_reg_write(QPNP_WLED_DISP_SEL_REG(base_addr), reg);
190
191 return 0;
192}
193
194static int qpnp_wled_module_ready(struct qpnp_wled *wled, uint16_t base_addr, bool state)
195{
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530196 uint8_t reg;
197
198 reg = pm8x41_wled_reg_read(
199 QPNP_WLED_MODULE_RDY_REG(base_addr));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530200 reg &= QPNP_WLED_MODULE_RDY_MASK;
201 reg |= (state << QPNP_WLED_MODULE_RDY_SHIFT);
202 pm8x41_wled_reg_write(QPNP_WLED_MODULE_RDY_REG(base_addr), reg);
203
204 return 0;
205}
206
207/* Configure WLED registers */
208static int qpnp_wled_config(struct qpnp_wled *wled)
209{
210 int rc, i, temp;
211 uint8_t reg = 0;
212
213 /* Configure display type */
214 rc = qpnp_wled_set_display_type(wled, wled->ctrl_base);
215 if (rc < 0)
216 return rc;
217
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530218 /* Recommended WLED MDOS settings for AMOLED */
219 if (wled->disp_type_amoled) {
220 pm8x41_wled_reg_write(QPNP_WLED_VLOOP_COMP_RES(wled->ctrl_base),
221 0x8F);
222 pm8x41_wled_reg_write(QPNP_WLED_VLOOP_COMP_GM(wled->ctrl_base),
223 0x81);
224 pm8x41_wled_reg_write(QPNP_WLED_PSM_CTRL(wled->ctrl_base),
225 0x83);
226
227 rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
228 if (rc)
229 return rc;
230 pm8x41_wled_reg_write(QPNP_WLED_TEST4(wled->ctrl_base), 0x13);
231 }
232
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530233 /* Configure the FEEDBACK OUTPUT register */
234 reg = pm8x41_wled_reg_read(
235 QPNP_WLED_FDBK_OP_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530236 reg &= QPNP_WLED_FDBK_OP_MASK;
237 reg |= wled->fdbk_op;
238 pm8x41_wled_reg_write(QPNP_WLED_FDBK_OP_REG(wled->ctrl_base), reg);
239
240 /* Configure the VREF register */
241 if (wled->vref_mv < QPNP_WLED_VREF_MIN_MV)
242 wled->vref_mv = QPNP_WLED_VREF_MIN_MV;
243 else if (wled->vref_mv > QPNP_WLED_VREF_MAX_MV)
244 wled->vref_mv = QPNP_WLED_VREF_MAX_MV;
245
246 reg = pm8x41_wled_reg_read(
247 QPNP_WLED_VREF_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530248 reg &= QPNP_WLED_VREF_MASK;
249 temp = wled->vref_mv - QPNP_WLED_VREF_MIN_MV;
250 reg |= (temp / QPNP_WLED_VREF_STEP_MV);
251 pm8x41_wled_reg_write(QPNP_WLED_VREF_REG(wled->ctrl_base), reg);
252
253 /* Configure the ILIM register */
254 if (wled->ilim_ma < QPNP_WLED_ILIM_MIN_MA)
255 wled->ilim_ma = QPNP_WLED_ILIM_MIN_MA;
256 else if (wled->ilim_ma > QPNP_WLED_ILIM_MAX_MA)
257 wled->ilim_ma = QPNP_WLED_ILIM_MAX_MA;
258
259 reg = pm8x41_wled_reg_read(
260 QPNP_WLED_ILIM_REG(wled->ctrl_base));
Alex Sarraf9bd81b92015-05-20 15:59:32 -0700261 temp = (wled->ilim_ma / QPNP_WLED_ILIM_STEP_MA);
262 if (temp != (reg & ~QPNP_WLED_ILIM_MASK)) {
263 reg &= QPNP_WLED_ILIM_MASK;
264 reg |= temp;
265 reg |= QPNP_WLED_ILIM_OVERWRITE;
266 pm8x41_wled_reg_write(QPNP_WLED_ILIM_REG(wled->ctrl_base), reg);
267 }
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530268
269 /* Configure the MAX BOOST DUTY register */
270 if (wled->boost_duty_ns < QPNP_WLED_BOOST_DUTY_MIN_NS)
271 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
272 else if (wled->boost_duty_ns > QPNP_WLED_BOOST_DUTY_MAX_NS)
273 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MAX_NS;
274
275 reg = pm8x41_wled_reg_read(
276 QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530277 reg &= QPNP_WLED_BOOST_DUTY_MASK;
278 reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS);
279 pm8x41_wled_reg_write(QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base), reg);
280
281 /* Configure the SWITCHING FREQ register */
282 if (wled->switch_freq_khz == QPNP_WLED_SWITCH_FREQ_1600_KHZ)
283 temp = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
284 else
285 temp = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
286
287 reg = pm8x41_wled_reg_read(
288 QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530289 reg &= QPNP_WLED_SWITCH_FREQ_MASK;
290 reg |= temp;
291 pm8x41_wled_reg_write(QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), reg);
292
293 /* Configure the OVP register */
294 if (wled->ovp_mv <= QPNP_WLED_OVP_17800_MV) {
295 wled->ovp_mv = QPNP_WLED_OVP_17800_MV;
296 temp = 3;
297 } else if (wled->ovp_mv <= QPNP_WLED_OVP_19400_MV) {
298 wled->ovp_mv = QPNP_WLED_OVP_19400_MV;
299 temp = 2;
300 } else if (wled->ovp_mv <= QPNP_WLED_OVP_29500_MV) {
301 wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
302 temp = 1;
303 } else {
304 wled->ovp_mv = QPNP_WLED_OVP_31000_MV;
305 temp = 0;
306 }
307
308 reg = pm8x41_wled_reg_read(
309 QPNP_WLED_OVP_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530310 reg &= QPNP_WLED_OVP_MASK;
311 reg |= temp;
312 pm8x41_wled_reg_write(QPNP_WLED_OVP_REG(wled->ctrl_base), reg);
313
314 /* Configure the MODULATION register */
315 if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_1200_KHZ) {
316 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_1200_KHZ;
317 temp = 3;
318 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_2400_KHZ) {
319 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_2400_KHZ;
320 temp = 2;
321 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_9600_KHZ) {
322 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
323 temp = 1;
324 } else {
325 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
326 temp = 0;
327 }
328 reg = pm8x41_wled_reg_read(QPNP_WLED_MOD_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530329
330 reg &= QPNP_WLED_MOD_FREQ_MASK;
331 reg |= (temp << QPNP_WLED_MOD_FREQ_SHIFT);
332
333 reg &= QPNP_WLED_PHASE_STAG_MASK;
334 reg |= (wled->en_phase_stag << QPNP_WLED_PHASE_STAG_SHIFT);
335
336 reg &= QPNP_WLED_DIM_RES_MASK;
337 reg |= (wled->en_9b_dim_res << QPNP_WLED_DIM_RES_SHIFT);
338
339 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
340 reg &= QPNP_WLED_DIM_HYB_MASK;
341 reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
342 } else {
343 reg &= QPNP_WLED_DIM_HYB_MASK;
344 reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
345 reg &= QPNP_WLED_DIM_ANA_MASK;
346 reg |= wled->dim_mode;
347 }
348
349 pm8x41_wled_reg_write(QPNP_WLED_MOD_REG(wled->sink_base), reg);
350
351 /* Configure the HYBRID THRESHOLD register */
352 if (wled->hyb_thres < QPNP_WLED_HYB_THRES_MIN)
353 wled->hyb_thres = QPNP_WLED_HYB_THRES_MIN;
354 else if (wled->hyb_thres > QPNP_WLED_HYB_THRES_MAX)
355 wled->hyb_thres = QPNP_WLED_HYB_THRES_MAX;
356
357 reg = pm8x41_wled_reg_read(
358 QPNP_WLED_HYB_THRES_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530359
360 reg &= QPNP_WLED_HYB_THRES_MASK;
361 temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1;
362 reg |= temp;
363 pm8x41_wled_reg_write(QPNP_WLED_HYB_THRES_REG(wled->sink_base), reg);
364
365 for (i = 0; i < wled->num_strings; i++) {
366 if (wled->strings[i] >= QPNP_WLED_MAX_STRINGS) {
367 dprintf(CRITICAL,"Invalid string number\n");
368 return ERR_NOT_VALID;
369 }
370
371 /* MODULATOR */
372 reg = pm8x41_wled_reg_read(
373 QPNP_WLED_MOD_EN_REG(wled->sink_base,
374 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530375 reg &= QPNP_WLED_MOD_EN_MASK;
376 reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
377 pm8x41_wled_reg_write(QPNP_WLED_MOD_EN_REG(wled->sink_base,
378 wled->strings[i]), reg);
379
380 /* SYNC DELAY */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800381 if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530382 wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US;
383
384 reg = pm8x41_wled_reg_read(
385 QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
386 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530387 reg &= QPNP_WLED_SYNC_DLY_MASK;
388 temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
389 reg |= temp;
390 pm8x41_wled_reg_write(QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
391 wled->strings[i]), reg);
392
393 /* FULL SCALE CURRENT */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800394 if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530395 wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
396
397 reg = pm8x41_wled_reg_read(
398 QPNP_WLED_FS_CURR_REG(wled->sink_base,
399 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530400 reg &= QPNP_WLED_FS_CURR_MASK;
401 temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
402 reg |= temp;
403 pm8x41_wled_reg_write(QPNP_WLED_FS_CURR_REG(wled->sink_base,
404 wled->strings[i]), reg);
405
406 /* CABC */
407 reg = pm8x41_wled_reg_read(
408 QPNP_WLED_CABC_REG(wled->sink_base,
409 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530410 reg &= QPNP_WLED_CABC_MASK;
411 reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT);
412 pm8x41_wled_reg_write(QPNP_WLED_CABC_REG(wled->sink_base,
413 wled->strings[i]), reg);
414
415 /* Enable CURRENT SINK */
416 reg = pm8x41_wled_reg_read(
417 QPNP_WLED_CURR_SINK_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530418 temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
419 reg |= (1 << temp);
420 pm8x41_wled_reg_write(QPNP_WLED_CURR_SINK_REG(wled->sink_base), reg);
421 }
422
423 /* LAB fast precharge */
424 reg = pm8x41_wled_reg_read(
425 QPNP_WLED_LAB_FAST_PC_REG(wled->lab_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530426 reg &= QPNP_WLED_LAB_FAST_PC_MASK;
427 reg |= (wled->lab_fast_precharge << QPNP_WLED_LAB_FAST_PC_SHIFT);
428 pm8x41_wled_reg_write(QPNP_WLED_LAB_FAST_PC_REG(wled->lab_base), reg);
429
430 /* Configure lab display type */
431 rc = qpnp_wled_set_display_type(wled, wled->lab_base);
432 if (rc < 0)
433 return rc;
434
435 /* make LAB module ready */
436 rc = qpnp_wled_module_ready(wled, wled->lab_base, true);
437 if (rc < 0)
438 return rc;
439
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530440 /* Disable LAB pulse skipping for AMOLED */
441 if (wled->disp_type_amoled)
442 pm8x41_wled_reg_write(wled->lab_base +
443 QPNP_LABIBB_PS_CTL, 0x00);
444
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530445 /* IBB active bias */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800446 if (wled->ibb_pwrup_dly_ms > QPNP_WLED_IBB_PWRUP_DLY_MAX_MS)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530447 wled->ibb_pwrup_dly_ms = QPNP_WLED_IBB_PWRUP_DLY_MAX_MS;
448
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800449 if (wled->ibb_pwrdn_dly_ms > QPNP_WLED_IBB_PWRDN_DLY_MAX_MS)
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800450 wled->ibb_pwrdn_dly_ms = QPNP_WLED_IBB_PWRDN_DLY_MAX_MS;
451
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530452 reg = pm8x41_wled_reg_read(
453 QPNP_WLED_IBB_BIAS_REG(wled->ibb_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530454
455 reg &= QPNP_WLED_IBB_BIAS_MASK;
456 reg |= (!wled->ibb_bias_active << QPNP_WLED_IBB_BIAS_SHIFT);
457
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800458 temp = wled->ibb_pwrup_dly_ms;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530459 reg &= QPNP_WLED_IBB_PWRUP_DLY_MASK;
460 reg |= (temp << QPNP_WLED_IBB_PWRUP_DLY_SHIFT);
Channagoud Kadabif5f0ded2015-02-25 13:42:18 -0800461 /* Power down delay bits could already be set, clear them before
462 * or'ing new values
463 */
464 reg &= ~(PWRDN_DLY2_MASK);
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800465 reg |= wled->ibb_pwrdn_dly_ms;
466 reg |= (wled->ibb_discharge_en << 2);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530467
468 rc = qpnp_wled_sec_access(wled, wled->ibb_base);
469 if (rc)
470 return rc;
471
472 pm8x41_wled_reg_write(QPNP_WLED_IBB_BIAS_REG(wled->ibb_base), reg);
473
474 /* Configure ibb display type */
475 rc = qpnp_wled_set_display_type(wled, wled->ibb_base);
476 if (rc < 0)
477 return rc;
478
479 /* make IBB module ready */
480 rc = qpnp_wled_module_ready(wled, wled->ibb_base, true);
481 if (rc < 0)
482 return rc;
483
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800484 rc = qpnp_labibb_regulator_set_voltage(wled);
485 if (rc < 0)
486 return rc;
487
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530488 return 0;
489}
490
491/* Setup wled default parameters */
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800492static int qpnp_wled_setup(struct qpnp_wled *wled, struct qpnp_wled_config_data *config)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530493{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800494 int i;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530495
496 wled->sink_base = QPNP_WLED_SINK_BASE;
497 wled->ctrl_base = QPNP_WLED_CTRL_BASE;
498 wled->ibb_base = QPNP_WLED_IBB_BASE;
499 wled->lab_base = QPNP_WLED_LAB_BASE;
500 wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
501 wled->vref_mv = QPNP_WLED_DFLT_VREF_MV;
502 wled->switch_freq_khz = QPNP_WLED_SWITCH_FREQ_800_KHZ;
503 wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
504 wled->ilim_ma = QPNP_WLED_DFLT_ILIM_MA;
505 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
506 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
507 wled->dim_mode = QPNP_WLED_DIM_HYBRID;
508 wled->dim_shape = QPNP_WLED_DIM_SHAPE_LINEAR;
509
510 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
511 wled->hyb_thres = QPNP_WLED_DFLT_HYB_THRES;
512 }
513
514 wled->sync_dly_us = 800;
515 wled->fs_curr_ua = 16000;
516 wled->en_9b_dim_res = 0;
517 wled->en_phase_stag = true;
518 wled->en_cabc = 0;
519
520 wled->num_strings = QPNP_WLED_MAX_STRINGS;
521 for (i = 0; i < wled->num_strings; i++)
522 wled->strings[i] = i;
523
524 wled->ibb_bias_active = false;
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800525 wled->lab_fast_precharge = true;
526 wled->ibb_pwrup_dly_ms = config->pwr_up_delay;
527 wled->ibb_pwrdn_dly_ms = config->pwr_down_delay;
528 wled->ibb_discharge_en = config->ibb_discharge_en;
529 wled->disp_type_amoled = config->display_type;
530 wled->lab_min_volt = config->lab_min_volt;
531 wled->lab_max_volt = config->lab_max_volt;
532 wled->ibb_min_volt = config->ibb_min_volt;
533 wled->ibb_max_volt = config->ibb_max_volt;
534 wled->ibb_init_volt = config->ibb_init_volt;
535 wled->lab_init_volt = config->lab_init_volt;
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530536 wled->lab_ibb_swire_control = config->lab_ibb_swire_control;
537 wled->wled_avdd_control = config->wled_avdd_control;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530538
539 return 0;
540}
541
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800542int qpnp_wled_init(struct qpnp_wled_config_data *config)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530543{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800544 int rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530545 struct qpnp_wled *wled;
546
547 wled = malloc(sizeof(struct qpnp_wled));
548 if (!wled)
549 return ERR_NO_MEMORY;
550
551 memset(wled, 0, sizeof(struct qpnp_wled));
552
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800553 rc = qpnp_wled_setup(wled, config);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530554 if (rc) {
555 dprintf(CRITICAL, "Setting WLED parameters failed\n");
556 return rc;
557 }
558
559 rc = qpnp_wled_config(wled);
560 if (rc) {
561 dprintf(CRITICAL, "wled config failed\n");
562 return rc;
563 }
564
565 gwled = wled;
566
567 return rc;
568}
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800569
570static int qpnp_labibb_regulator_set_voltage(struct qpnp_wled *wled)
571{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800572 int rc = -1;
573 uint32_t new_uV;
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800574 uint8_t val, mask=0;
575
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530576 if (!wled->disp_type_amoled || !wled->lab_ibb_swire_control) {
577 if (wled->lab_min_volt < wled->lab_init_volt) {
578 dprintf(CRITICAL,"qpnp_lab_regulator_set_voltage failed, min_uV %d is less than init volt %d\n",
579 wled->lab_min_volt, wled->lab_init_volt);
580 return rc;
581 }
582
583 val = (((wled->lab_min_volt - wled->lab_init_volt) +
584 (IBB_LAB_VREG_STEP_SIZE - 1)) / IBB_LAB_VREG_STEP_SIZE);
585 new_uV = val * IBB_LAB_VREG_STEP_SIZE + wled->lab_init_volt;
586
587 if (new_uV > wled->lab_max_volt) {
588 dprintf(CRITICAL,"qpnp_ibb_regulator_set_voltage unable to set voltage (%d %d)\n",
589 wled->lab_min_volt, wled->lab_max_volt);
590 return rc;
591 }
592 val |= QPNP_LAB_OUTPUT_OVERRIDE_EN;
593 mask = pm8x41_wled_reg_read(wled->lab_base +
594 QPNP_LABIBB_OUTPUT_VOLTAGE);
595 mask &= ~(QPNP_LAB_SET_VOLTAGE_MASK
596 | QPNP_LAB_OUTPUT_OVERRIDE_EN);
597 mask |= val & (QPNP_LAB_SET_VOLTAGE_MASK
598 | QPNP_LAB_OUTPUT_OVERRIDE_EN);
599
600 pm8x41_wled_reg_write(wled->lab_base +
601 QPNP_LABIBB_OUTPUT_VOLTAGE, mask);
602 udelay(2);
603
604 /*
605 * IBB Set Voltage.
606 * For AMOLED panels, the IBB voltage needs to be
607 * controlled by panel.
608 */
609 if (wled->ibb_min_volt < wled->ibb_init_volt) {
610 dprintf(CRITICAL, "qpnp_ibb_regulator_set_voltage failed, min_uV %d is less than init volt %d\n",
611 wled->ibb_min_volt, wled->ibb_init_volt);
612 return rc;
613 }
614
615 val = (((wled->ibb_min_volt - wled->ibb_init_volt) +
616 (IBB_LAB_VREG_STEP_SIZE - 1)) / IBB_LAB_VREG_STEP_SIZE);
617 new_uV = val * IBB_LAB_VREG_STEP_SIZE + wled->ibb_init_volt;
618 if (new_uV > wled->ibb_max_volt) {
619 dprintf(CRITICAL, "qpnp_ibb_regulator_set_voltage unable to set voltage %d %d\n",
620 wled->ibb_min_volt, wled->ibb_max_volt);
621 return rc;
622 }
623 val |= QPNP_LAB_OUTPUT_OVERRIDE_EN;
624 mask = pm8x41_wled_reg_read(wled->ibb_base +
625 QPNP_LABIBB_OUTPUT_VOLTAGE);
626 udelay(2);
627 mask &= ~(QPNP_IBB_SET_VOLTAGE_MASK |
628 QPNP_LAB_OUTPUT_OVERRIDE_EN);
629 mask |= (val & (QPNP_IBB_SET_VOLTAGE_MASK |
630 QPNP_LAB_OUTPUT_OVERRIDE_EN));
631
632 pm8x41_wled_reg_write(wled->ibb_base +
633 QPNP_LABIBB_OUTPUT_VOLTAGE, mask);
634 } else {
635 pm8x41_wled_reg_write(wled->ibb_base +
636 QPNP_LABIBB_OUTPUT_VOLTAGE, 0x00);
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800637 }
638
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800639 return 0;
640}