blob: 6161458de189afa98afeb53b00e88afc6bc6d74a [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>
Parth Dixit550ddf32016-11-28 17:00:29 +053036#include <target.h>
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053037
Parth Dixite46b1a22016-06-06 19:16:20 +053038static int qpnp_wled_avdd_target_voltages[NUM_SUPPORTED_AVDD_VOLTAGES] = {
39 7900, 7600, 7300, 6400, 6100, 5800,
40};
41
42static uint8_t qpnp_wled_ovp_reg_settings[NUM_SUPPORTED_AVDD_VOLTAGES] = {
43 0x0, 0x0, 0x1, 0x2, 0x2, 0x3,
44};
45
46static int qpnp_wled_avdd_trim_adjustments[NUM_SUPPORTED_AVDD_VOLTAGES] = {
47 3, 0, -2, 7, 3, 3,
48};
49
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053050static int fls(uint16_t n)
51{
52 int i = 0;
53 for (; n; n >>= 1, i++);
54 return i;
55}
56
57static struct qpnp_wled *gwled;
Aparna Mallavarapua42de902014-12-08 17:25:44 -080058static int qpnp_labibb_regulator_set_voltage(struct qpnp_wled *wled);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053059
60static int qpnp_wled_sec_access(struct qpnp_wled *wled, uint16_t base_addr)
61{
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053062 uint8_t reg = QPNP_WLED_SEC_UNLOCK;
63
64 pm8x41_wled_reg_write(QPNP_WLED_SEC_ACCESS_REG(base_addr), reg);
65
66 return 0;
67}
68
69/* set wled to a level of brightness */
70static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
71{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -080072 int i;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053073 uint8_t reg;
74
75 /* set brightness registers */
76 for (i = 0; i < wled->num_strings; i++) {
77 reg = level & QPNP_WLED_BRIGHT_LSB_MASK;
78 pm8x41_wled_reg_write(QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base,
79 wled->strings[i]), reg);
80
81 reg = level >> QPNP_WLED_BRIGHT_MSB_SHIFT;
82 reg = reg & QPNP_WLED_BRIGHT_MSB_MASK;
83 pm8x41_wled_reg_write(QPNP_WLED_BRIGHT_MSB_REG(wled->sink_base,
84 wled->strings[i]), reg);
85 }
86
87 /* sync */
88 reg = QPNP_WLED_SYNC;
89 pm8x41_wled_reg_write(QPNP_WLED_SYNC_REG(wled->sink_base), reg);
90
91 reg = QPNP_WLED_SYNC_RESET;
92 pm8x41_wled_reg_write(QPNP_WLED_SYNC_REG(wled->sink_base), reg);
93
94 return 0;
95}
96
97static int qpnp_wled_enable(struct qpnp_wled *wled,
98 uint16_t base_addr, bool state)
99{
100 uint8_t reg;
101
102 reg = pm8x41_wled_reg_read(
103 QPNP_WLED_MODULE_EN_REG(base_addr));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530104 reg &= QPNP_WLED_MODULE_EN_MASK;
105 reg |= (state << QPNP_WLED_MODULE_EN_SHIFT);
106 pm8x41_wled_reg_write(QPNP_WLED_MODULE_EN_REG(base_addr), reg);
107
108 return 0;
109}
110
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530111static int qpnp_wled_ibb_swire_rdy(struct qpnp_wled *wled,
112 uint16_t base_addr, bool state)
113{
114 uint8_t reg;
115
116 reg = pm8x41_wled_reg_read(
117 QPNP_WLED_MODULE_EN_REG(base_addr));
118 /* Do not enable IBB module when SWIRE ready is set */
119 reg &= ~(QPNP_IBB_SWIRE_RDY_MASK | QPNP_IBB_MODULE_EN_MASK);
120 reg |= (state << QPNP_IBB_SWIRE_RDY_SHIFT);
121 pm8x41_wled_reg_write(QPNP_WLED_MODULE_EN_REG(base_addr), reg);
122
123 return 0;
124}
125
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530126int qpnp_ibb_enable(bool state)
127{
128 int rc = 0;
129 uint8_t reg;
130
131 if (!gwled) {
132 dprintf(CRITICAL, "%s: wled is not initialized yet\n", __func__);
133 return ERROR;
134 }
135
136 /* enable lab */
137 if (gwled->ibb_bias_active) {
138 rc = qpnp_wled_enable(gwled, gwled->lab_base, state);
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800139 udelay(QPNP_WLED_LAB_START_DLY_US + 1);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530140 if (rc < 0)
141 return rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530142 } else {
143 reg = pm8x41_wled_reg_read(QPNP_WLED_LAB_IBB_RDY_REG(gwled->lab_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530144
145 reg &= QPNP_WLED_MODULE_EN_MASK;
146 reg |= (state << QPNP_WLED_MODULE_EN_SHIFT);
147 pm8x41_wled_reg_write(QPNP_WLED_LAB_IBB_RDY_REG(gwled->lab_base), reg);
148 }
149
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530150 if (gwled->disp_type_amoled && gwled->lab_ibb_swire_control)
151 rc = qpnp_wled_ibb_swire_rdy(gwled, gwled->ibb_base, state);
152 else
153 rc = qpnp_wled_enable(gwled, gwled->ibb_base, state);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530154
155 return rc;
156}
157
158/* enable / disable wled brightness */
159void qpnp_wled_enable_backlight(int enable)
160{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800161 int rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530162
163 if (!gwled) {
164 dprintf(CRITICAL, "%s: wled is not initialized yet\n", __func__);
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800165 return;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530166 }
167
168 if (enable) {
169 rc = qpnp_wled_set_level(gwled, QPNP_WLED_MAX_BR_LEVEL);
170 if (rc) {
171 dprintf(CRITICAL,"wled set level failed\n");
172 return;
173 }
174 }
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530175
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530176 if (!gwled->disp_type_amoled || !gwled->wled_avdd_control) {
177 rc = qpnp_wled_enable(gwled, gwled->ctrl_base, enable);
178 if (rc) {
179 dprintf(CRITICAL, "wled %sable failed\n",
180 enable ? "en" : "dis");
181 return;
182 }
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530183 }
184
185}
186
187static int qpnp_wled_set_display_type(struct qpnp_wled *wled, uint16_t base_addr)
188{
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530189 uint8_t reg = 0;
Parth Dixitb1461bf2015-06-24 20:51:08 +0530190 int rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530191
192 /* display type */
193 reg = pm8x41_wled_reg_read(QPNP_WLED_DISP_SEL_REG(base_addr));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530194
195 reg &= QPNP_WLED_DISP_SEL_MASK;
196 reg |= (wled->disp_type_amoled << QPNP_WLED_DISP_SEL_SHIFT);
Parth Dixitb1461bf2015-06-24 20:51:08 +0530197
198 rc = qpnp_wled_sec_access(wled, base_addr);
199 if (rc)
200 return rc;
201
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530202 pm8x41_wled_reg_write(QPNP_WLED_DISP_SEL_REG(base_addr), reg);
203
204 return 0;
205}
206
207static int qpnp_wled_module_ready(struct qpnp_wled *wled, uint16_t base_addr, bool state)
208{
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530209 uint8_t reg;
210
211 reg = pm8x41_wled_reg_read(
212 QPNP_WLED_MODULE_RDY_REG(base_addr));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530213 reg &= QPNP_WLED_MODULE_RDY_MASK;
214 reg |= (state << QPNP_WLED_MODULE_RDY_SHIFT);
215 pm8x41_wled_reg_write(QPNP_WLED_MODULE_RDY_REG(base_addr), reg);
216
217 return 0;
218}
219
220/* Configure WLED registers */
221static int qpnp_wled_config(struct qpnp_wled *wled)
222{
223 int rc, i, temp;
224 uint8_t reg = 0;
225
226 /* Configure display type */
227 rc = qpnp_wled_set_display_type(wled, wled->ctrl_base);
228 if (rc < 0)
229 return rc;
230
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530231 /* Recommended WLED MDOS settings for AMOLED */
232 if (wled->disp_type_amoled) {
233 pm8x41_wled_reg_write(QPNP_WLED_VLOOP_COMP_RES(wled->ctrl_base),
234 0x8F);
235 pm8x41_wled_reg_write(QPNP_WLED_VLOOP_COMP_GM(wled->ctrl_base),
236 0x81);
237 pm8x41_wled_reg_write(QPNP_WLED_PSM_CTRL(wled->ctrl_base),
238 0x83);
Mayank Grover0b0ec802016-11-04 11:47:46 +0530239 pm8x41_wled_reg_write(QPNP_WLED_PSM_EN(wled->ctrl_base),
240 0x80);
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530241
242 rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
243 if (rc)
244 return rc;
245 pm8x41_wled_reg_write(QPNP_WLED_TEST4(wled->ctrl_base), 0x13);
246 }
247
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530248 /* Configure the FEEDBACK OUTPUT register */
249 reg = pm8x41_wled_reg_read(
250 QPNP_WLED_FDBK_OP_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530251 reg &= QPNP_WLED_FDBK_OP_MASK;
252 reg |= wled->fdbk_op;
253 pm8x41_wled_reg_write(QPNP_WLED_FDBK_OP_REG(wled->ctrl_base), reg);
254
255 /* Configure the VREF register */
256 if (wled->vref_mv < QPNP_WLED_VREF_MIN_MV)
257 wled->vref_mv = QPNP_WLED_VREF_MIN_MV;
258 else if (wled->vref_mv > QPNP_WLED_VREF_MAX_MV)
259 wled->vref_mv = QPNP_WLED_VREF_MAX_MV;
260
261 reg = pm8x41_wled_reg_read(
262 QPNP_WLED_VREF_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530263 reg &= QPNP_WLED_VREF_MASK;
264 temp = wled->vref_mv - QPNP_WLED_VREF_MIN_MV;
265 reg |= (temp / QPNP_WLED_VREF_STEP_MV);
266 pm8x41_wled_reg_write(QPNP_WLED_VREF_REG(wled->ctrl_base), reg);
267
268 /* Configure the ILIM register */
269 if (wled->ilim_ma < QPNP_WLED_ILIM_MIN_MA)
270 wled->ilim_ma = QPNP_WLED_ILIM_MIN_MA;
271 else if (wled->ilim_ma > QPNP_WLED_ILIM_MAX_MA)
272 wled->ilim_ma = QPNP_WLED_ILIM_MAX_MA;
273
274 reg = pm8x41_wled_reg_read(
275 QPNP_WLED_ILIM_REG(wled->ctrl_base));
Alex Sarraf9bd81b92015-05-20 15:59:32 -0700276 temp = (wled->ilim_ma / QPNP_WLED_ILIM_STEP_MA);
277 if (temp != (reg & ~QPNP_WLED_ILIM_MASK)) {
278 reg &= QPNP_WLED_ILIM_MASK;
279 reg |= temp;
280 reg |= QPNP_WLED_ILIM_OVERWRITE;
281 pm8x41_wled_reg_write(QPNP_WLED_ILIM_REG(wled->ctrl_base), reg);
282 }
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530283
284 /* Configure the MAX BOOST DUTY register */
285 if (wled->boost_duty_ns < QPNP_WLED_BOOST_DUTY_MIN_NS)
286 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
287 else if (wled->boost_duty_ns > QPNP_WLED_BOOST_DUTY_MAX_NS)
288 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MAX_NS;
289
290 reg = pm8x41_wled_reg_read(
291 QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530292 reg &= QPNP_WLED_BOOST_DUTY_MASK;
293 reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS);
294 pm8x41_wled_reg_write(QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base), reg);
295
296 /* Configure the SWITCHING FREQ register */
297 if (wled->switch_freq_khz == QPNP_WLED_SWITCH_FREQ_1600_KHZ)
298 temp = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
299 else
300 temp = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
301
302 reg = pm8x41_wled_reg_read(
303 QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530304 reg &= QPNP_WLED_SWITCH_FREQ_MASK;
305 reg |= temp;
306 pm8x41_wled_reg_write(QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), reg);
307
308 /* Configure the OVP register */
309 if (wled->ovp_mv <= QPNP_WLED_OVP_17800_MV) {
310 wled->ovp_mv = QPNP_WLED_OVP_17800_MV;
311 temp = 3;
312 } else if (wled->ovp_mv <= QPNP_WLED_OVP_19400_MV) {
313 wled->ovp_mv = QPNP_WLED_OVP_19400_MV;
314 temp = 2;
315 } else if (wled->ovp_mv <= QPNP_WLED_OVP_29500_MV) {
316 wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
317 temp = 1;
318 } else {
319 wled->ovp_mv = QPNP_WLED_OVP_31000_MV;
320 temp = 0;
321 }
322
323 reg = pm8x41_wled_reg_read(
324 QPNP_WLED_OVP_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530325 reg &= QPNP_WLED_OVP_MASK;
326 reg |= temp;
327 pm8x41_wled_reg_write(QPNP_WLED_OVP_REG(wled->ctrl_base), reg);
328
Parth Dixite46b1a22016-06-06 19:16:20 +0530329 if (wled->disp_type_amoled) {
330 for (i = 0; i < NUM_SUPPORTED_AVDD_VOLTAGES; i++) {
331 if (QPNP_WLED_AVDD_DEFAULT_VOLTAGE_MV == qpnp_wled_avdd_target_voltages[i])
332 break;
333 }
334 if (i == NUM_SUPPORTED_AVDD_VOLTAGES)
335 {
336 dprintf(CRITICAL, "Invalid avdd target voltage specified \n");
337 return ERR_NOT_VALID;
338 }
339 /* Update WLED_OVP register based on desired target voltage */
340 reg = qpnp_wled_ovp_reg_settings[i];
341 pm8x41_wled_reg_write(QPNP_WLED_OVP_REG(wled->ctrl_base), reg);
342 /* Update WLED_TRIM register based on desired target voltage */
343 reg = pm8x41_wled_reg_read(
344 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base));
345 reg += qpnp_wled_avdd_trim_adjustments[i];
346 if ((int8_t)reg < QPNP_WLED_AVDD_MIN_TRIM_VALUE)
347 reg = QPNP_WLED_AVDD_MIN_TRIM_VALUE;
348 else if((int8_t)reg > QPNP_WLED_AVDD_MAX_TRIM_VALUE)
349 reg = QPNP_WLED_AVDD_MAX_TRIM_VALUE;
350
351 rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
352 if (rc)
353 return rc;
354
355 temp = pm8x41_wled_reg_read(
356 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base));
357 temp &= ~QPNP_WLED_7P7_TRIM_MASK;
358 temp |= (reg & QPNP_WLED_7P7_TRIM_MASK);
359 pm8x41_wled_reg_write(QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), temp);
360 /* Write to spare to avoid reconfiguration in HLOS */
361 reg = pm8x41_wled_reg_read(
362 QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base));
363 reg |= QPNP_WLED_AVDD_SET_BIT;
364 pm8x41_wled_reg_write(QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base), reg);
365 }
366
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530367 /* Configure the MODULATION register */
368 if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_1200_KHZ) {
369 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_1200_KHZ;
370 temp = 3;
371 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_2400_KHZ) {
372 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_2400_KHZ;
373 temp = 2;
374 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_9600_KHZ) {
375 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
376 temp = 1;
377 } else {
378 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
379 temp = 0;
380 }
381 reg = pm8x41_wled_reg_read(QPNP_WLED_MOD_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530382
383 reg &= QPNP_WLED_MOD_FREQ_MASK;
384 reg |= (temp << QPNP_WLED_MOD_FREQ_SHIFT);
385
386 reg &= QPNP_WLED_PHASE_STAG_MASK;
387 reg |= (wled->en_phase_stag << QPNP_WLED_PHASE_STAG_SHIFT);
388
389 reg &= QPNP_WLED_DIM_RES_MASK;
390 reg |= (wled->en_9b_dim_res << QPNP_WLED_DIM_RES_SHIFT);
391
392 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
393 reg &= QPNP_WLED_DIM_HYB_MASK;
394 reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
395 } else {
396 reg &= QPNP_WLED_DIM_HYB_MASK;
397 reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
398 reg &= QPNP_WLED_DIM_ANA_MASK;
399 reg |= wled->dim_mode;
400 }
401
402 pm8x41_wled_reg_write(QPNP_WLED_MOD_REG(wled->sink_base), reg);
403
404 /* Configure the HYBRID THRESHOLD register */
405 if (wled->hyb_thres < QPNP_WLED_HYB_THRES_MIN)
406 wled->hyb_thres = QPNP_WLED_HYB_THRES_MIN;
407 else if (wled->hyb_thres > QPNP_WLED_HYB_THRES_MAX)
408 wled->hyb_thres = QPNP_WLED_HYB_THRES_MAX;
409
410 reg = pm8x41_wled_reg_read(
411 QPNP_WLED_HYB_THRES_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530412
413 reg &= QPNP_WLED_HYB_THRES_MASK;
414 temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1;
415 reg |= temp;
416 pm8x41_wled_reg_write(QPNP_WLED_HYB_THRES_REG(wled->sink_base), reg);
417
418 for (i = 0; i < wled->num_strings; i++) {
419 if (wled->strings[i] >= QPNP_WLED_MAX_STRINGS) {
420 dprintf(CRITICAL,"Invalid string number\n");
421 return ERR_NOT_VALID;
422 }
423
424 /* MODULATOR */
425 reg = pm8x41_wled_reg_read(
426 QPNP_WLED_MOD_EN_REG(wled->sink_base,
427 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530428 reg &= QPNP_WLED_MOD_EN_MASK;
429 reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
430 pm8x41_wled_reg_write(QPNP_WLED_MOD_EN_REG(wled->sink_base,
431 wled->strings[i]), reg);
432
433 /* SYNC DELAY */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800434 if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530435 wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US;
436
437 reg = pm8x41_wled_reg_read(
438 QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
439 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530440 reg &= QPNP_WLED_SYNC_DLY_MASK;
441 temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
442 reg |= temp;
443 pm8x41_wled_reg_write(QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
444 wled->strings[i]), reg);
445
446 /* FULL SCALE CURRENT */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800447 if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530448 wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
449
450 reg = pm8x41_wled_reg_read(
451 QPNP_WLED_FS_CURR_REG(wled->sink_base,
452 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530453 reg &= QPNP_WLED_FS_CURR_MASK;
454 temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
455 reg |= temp;
456 pm8x41_wled_reg_write(QPNP_WLED_FS_CURR_REG(wled->sink_base,
457 wled->strings[i]), reg);
458
459 /* CABC */
460 reg = pm8x41_wled_reg_read(
461 QPNP_WLED_CABC_REG(wled->sink_base,
462 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530463 reg &= QPNP_WLED_CABC_MASK;
464 reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT);
465 pm8x41_wled_reg_write(QPNP_WLED_CABC_REG(wled->sink_base,
466 wled->strings[i]), reg);
467
468 /* Enable CURRENT SINK */
469 reg = pm8x41_wled_reg_read(
470 QPNP_WLED_CURR_SINK_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530471 temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
472 reg |= (1 << temp);
473 pm8x41_wled_reg_write(QPNP_WLED_CURR_SINK_REG(wled->sink_base), reg);
474 }
475
476 /* LAB fast precharge */
477 reg = pm8x41_wled_reg_read(
478 QPNP_WLED_LAB_FAST_PC_REG(wled->lab_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530479 reg &= QPNP_WLED_LAB_FAST_PC_MASK;
480 reg |= (wled->lab_fast_precharge << QPNP_WLED_LAB_FAST_PC_SHIFT);
wufeng.jiangb1008482016-06-14 10:41:39 +0800481 /* LAB max precharge time */
482 reg &= QPNP_WLED_PRECHARGE_MASK;
483 reg |= (wled->lab_max_precharge_time);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530484 pm8x41_wled_reg_write(QPNP_WLED_LAB_FAST_PC_REG(wled->lab_base), reg);
485
486 /* Configure lab display type */
487 rc = qpnp_wled_set_display_type(wled, wled->lab_base);
488 if (rc < 0)
489 return rc;
490
491 /* make LAB module ready */
492 rc = qpnp_wled_module_ready(wled, wled->lab_base, true);
493 if (rc < 0)
494 return rc;
495
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530496 /* Disable LAB pulse skipping for AMOLED */
497 if (wled->disp_type_amoled)
498 pm8x41_wled_reg_write(wled->lab_base +
499 QPNP_LABIBB_PS_CTL, 0x00);
500
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530501 /* IBB active bias */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800502 if (wled->ibb_pwrup_dly_ms > QPNP_WLED_IBB_PWRUP_DLY_MAX_MS)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530503 wled->ibb_pwrup_dly_ms = QPNP_WLED_IBB_PWRUP_DLY_MAX_MS;
504
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800505 if (wled->ibb_pwrdn_dly_ms > QPNP_WLED_IBB_PWRDN_DLY_MAX_MS)
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800506 wled->ibb_pwrdn_dly_ms = QPNP_WLED_IBB_PWRDN_DLY_MAX_MS;
507
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530508 reg = pm8x41_wled_reg_read(
509 QPNP_WLED_IBB_BIAS_REG(wled->ibb_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530510
511 reg &= QPNP_WLED_IBB_BIAS_MASK;
512 reg |= (!wled->ibb_bias_active << QPNP_WLED_IBB_BIAS_SHIFT);
513
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800514 temp = wled->ibb_pwrup_dly_ms;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530515 reg &= QPNP_WLED_IBB_PWRUP_DLY_MASK;
516 reg |= (temp << QPNP_WLED_IBB_PWRUP_DLY_SHIFT);
Channagoud Kadabif5f0ded2015-02-25 13:42:18 -0800517 /* Power down delay bits could already be set, clear them before
518 * or'ing new values
519 */
520 reg &= ~(PWRDN_DLY2_MASK);
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800521 reg |= wled->ibb_pwrdn_dly_ms;
522 reg |= (wled->ibb_discharge_en << 2);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530523
524 rc = qpnp_wled_sec_access(wled, wled->ibb_base);
525 if (rc)
526 return rc;
527
528 pm8x41_wled_reg_write(QPNP_WLED_IBB_BIAS_REG(wled->ibb_base), reg);
529
530 /* Configure ibb display type */
531 rc = qpnp_wled_set_display_type(wled, wled->ibb_base);
532 if (rc < 0)
533 return rc;
534
535 /* make IBB module ready */
536 rc = qpnp_wled_module_ready(wled, wled->ibb_base, true);
537 if (rc < 0)
538 return rc;
539
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800540 rc = qpnp_labibb_regulator_set_voltage(wled);
541 if (rc < 0)
542 return rc;
543
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530544 return 0;
545}
546
547/* Setup wled default parameters */
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800548static int qpnp_wled_setup(struct qpnp_wled *wled, struct qpnp_wled_config_data *config)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530549{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800550 int i;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530551
552 wled->sink_base = QPNP_WLED_SINK_BASE;
553 wled->ctrl_base = QPNP_WLED_CTRL_BASE;
554 wled->ibb_base = QPNP_WLED_IBB_BASE;
555 wled->lab_base = QPNP_WLED_LAB_BASE;
556 wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
557 wled->vref_mv = QPNP_WLED_DFLT_VREF_MV;
558 wled->switch_freq_khz = QPNP_WLED_SWITCH_FREQ_800_KHZ;
559 wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
560 wled->ilim_ma = QPNP_WLED_DFLT_ILIM_MA;
561 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
562 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
563 wled->dim_mode = QPNP_WLED_DIM_HYBRID;
564 wled->dim_shape = QPNP_WLED_DIM_SHAPE_LINEAR;
565
566 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
567 wled->hyb_thres = QPNP_WLED_DFLT_HYB_THRES;
568 }
569
570 wled->sync_dly_us = 800;
571 wled->fs_curr_ua = 16000;
572 wled->en_9b_dim_res = 0;
573 wled->en_phase_stag = true;
574 wled->en_cabc = 0;
575
576 wled->num_strings = QPNP_WLED_MAX_STRINGS;
577 for (i = 0; i < wled->num_strings; i++)
578 wled->strings[i] = i;
579
580 wled->ibb_bias_active = false;
Wufeng7bae3c92016-04-27 13:02:15 +0800581 wled->lab_fast_precharge = false;
wufeng.jiangb1008482016-06-14 10:41:39 +0800582 wled->lab_max_precharge_time = QPNP_WLED_PRECHARGE_US500;
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800583 wled->ibb_pwrup_dly_ms = config->pwr_up_delay;
584 wled->ibb_pwrdn_dly_ms = config->pwr_down_delay;
585 wled->ibb_discharge_en = config->ibb_discharge_en;
586 wled->disp_type_amoled = config->display_type;
587 wled->lab_min_volt = config->lab_min_volt;
588 wled->lab_max_volt = config->lab_max_volt;
589 wled->ibb_min_volt = config->ibb_min_volt;
590 wled->ibb_max_volt = config->ibb_max_volt;
591 wled->ibb_init_volt = config->ibb_init_volt;
592 wled->lab_init_volt = config->lab_init_volt;
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530593 wled->lab_ibb_swire_control = config->lab_ibb_swire_control;
594 wled->wled_avdd_control = config->wled_avdd_control;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530595
596 return 0;
597}
598
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800599int qpnp_wled_init(struct qpnp_wled_config_data *config)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530600{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800601 int rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530602 struct qpnp_wled *wled;
603
Parth Dixit550ddf32016-11-28 17:00:29 +0530604 if(!target_is_pmi_enabled())
605 return ERR_NOT_FOUND;
606
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530607 wled = malloc(sizeof(struct qpnp_wled));
608 if (!wled)
609 return ERR_NO_MEMORY;
610
611 memset(wled, 0, sizeof(struct qpnp_wled));
612
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800613 rc = qpnp_wled_setup(wled, config);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530614 if (rc) {
615 dprintf(CRITICAL, "Setting WLED parameters failed\n");
616 return rc;
617 }
618
619 rc = qpnp_wled_config(wled);
620 if (rc) {
621 dprintf(CRITICAL, "wled config failed\n");
622 return rc;
623 }
624
625 gwled = wled;
626
627 return rc;
628}
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800629
630static int qpnp_labibb_regulator_set_voltage(struct qpnp_wled *wled)
631{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800632 int rc = -1;
633 uint32_t new_uV;
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800634 uint8_t val, mask=0;
635
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530636 if (!wled->disp_type_amoled || !wled->lab_ibb_swire_control) {
637 if (wled->lab_min_volt < wled->lab_init_volt) {
638 dprintf(CRITICAL,"qpnp_lab_regulator_set_voltage failed, min_uV %d is less than init volt %d\n",
639 wled->lab_min_volt, wled->lab_init_volt);
640 return rc;
641 }
642
643 val = (((wled->lab_min_volt - wled->lab_init_volt) +
644 (IBB_LAB_VREG_STEP_SIZE - 1)) / IBB_LAB_VREG_STEP_SIZE);
645 new_uV = val * IBB_LAB_VREG_STEP_SIZE + wled->lab_init_volt;
646
647 if (new_uV > wled->lab_max_volt) {
648 dprintf(CRITICAL,"qpnp_ibb_regulator_set_voltage unable to set voltage (%d %d)\n",
649 wled->lab_min_volt, wled->lab_max_volt);
650 return rc;
651 }
652 val |= QPNP_LAB_OUTPUT_OVERRIDE_EN;
653 mask = pm8x41_wled_reg_read(wled->lab_base +
654 QPNP_LABIBB_OUTPUT_VOLTAGE);
655 mask &= ~(QPNP_LAB_SET_VOLTAGE_MASK
656 | QPNP_LAB_OUTPUT_OVERRIDE_EN);
657 mask |= val & (QPNP_LAB_SET_VOLTAGE_MASK
658 | QPNP_LAB_OUTPUT_OVERRIDE_EN);
659
660 pm8x41_wled_reg_write(wled->lab_base +
661 QPNP_LABIBB_OUTPUT_VOLTAGE, mask);
662 udelay(2);
663
664 /*
665 * IBB Set Voltage.
666 * For AMOLED panels, the IBB voltage needs to be
667 * controlled by panel.
668 */
669 if (wled->ibb_min_volt < wled->ibb_init_volt) {
670 dprintf(CRITICAL, "qpnp_ibb_regulator_set_voltage failed, min_uV %d is less than init volt %d\n",
671 wled->ibb_min_volt, wled->ibb_init_volt);
672 return rc;
673 }
674
675 val = (((wled->ibb_min_volt - wled->ibb_init_volt) +
676 (IBB_LAB_VREG_STEP_SIZE - 1)) / IBB_LAB_VREG_STEP_SIZE);
677 new_uV = val * IBB_LAB_VREG_STEP_SIZE + wled->ibb_init_volt;
678 if (new_uV > wled->ibb_max_volt) {
679 dprintf(CRITICAL, "qpnp_ibb_regulator_set_voltage unable to set voltage %d %d\n",
680 wled->ibb_min_volt, wled->ibb_max_volt);
681 return rc;
682 }
683 val |= QPNP_LAB_OUTPUT_OVERRIDE_EN;
684 mask = pm8x41_wled_reg_read(wled->ibb_base +
685 QPNP_LABIBB_OUTPUT_VOLTAGE);
686 udelay(2);
687 mask &= ~(QPNP_IBB_SET_VOLTAGE_MASK |
688 QPNP_LAB_OUTPUT_OVERRIDE_EN);
689 mask |= (val & (QPNP_IBB_SET_VOLTAGE_MASK |
690 QPNP_LAB_OUTPUT_OVERRIDE_EN));
691
692 pm8x41_wled_reg_write(wled->ibb_base +
693 QPNP_LABIBB_OUTPUT_VOLTAGE, mask);
694 } else {
695 pm8x41_wled_reg_write(wled->ibb_base +
696 QPNP_LABIBB_OUTPUT_VOLTAGE, 0x00);
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800697 }
698
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800699 return 0;
700}