blob: 42673726800d36fd595f2a86738054f2d9cbd527 [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);
239
240 rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
241 if (rc)
242 return rc;
243 pm8x41_wled_reg_write(QPNP_WLED_TEST4(wled->ctrl_base), 0x13);
244 }
245
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530246 /* Configure the FEEDBACK OUTPUT register */
247 reg = pm8x41_wled_reg_read(
248 QPNP_WLED_FDBK_OP_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530249 reg &= QPNP_WLED_FDBK_OP_MASK;
250 reg |= wled->fdbk_op;
251 pm8x41_wled_reg_write(QPNP_WLED_FDBK_OP_REG(wled->ctrl_base), reg);
252
253 /* Configure the VREF register */
254 if (wled->vref_mv < QPNP_WLED_VREF_MIN_MV)
255 wled->vref_mv = QPNP_WLED_VREF_MIN_MV;
256 else if (wled->vref_mv > QPNP_WLED_VREF_MAX_MV)
257 wled->vref_mv = QPNP_WLED_VREF_MAX_MV;
258
259 reg = pm8x41_wled_reg_read(
260 QPNP_WLED_VREF_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530261 reg &= QPNP_WLED_VREF_MASK;
262 temp = wled->vref_mv - QPNP_WLED_VREF_MIN_MV;
263 reg |= (temp / QPNP_WLED_VREF_STEP_MV);
264 pm8x41_wled_reg_write(QPNP_WLED_VREF_REG(wled->ctrl_base), reg);
265
266 /* Configure the ILIM register */
267 if (wled->ilim_ma < QPNP_WLED_ILIM_MIN_MA)
268 wled->ilim_ma = QPNP_WLED_ILIM_MIN_MA;
269 else if (wled->ilim_ma > QPNP_WLED_ILIM_MAX_MA)
270 wled->ilim_ma = QPNP_WLED_ILIM_MAX_MA;
271
272 reg = pm8x41_wled_reg_read(
273 QPNP_WLED_ILIM_REG(wled->ctrl_base));
Alex Sarraf9bd81b92015-05-20 15:59:32 -0700274 temp = (wled->ilim_ma / QPNP_WLED_ILIM_STEP_MA);
275 if (temp != (reg & ~QPNP_WLED_ILIM_MASK)) {
276 reg &= QPNP_WLED_ILIM_MASK;
277 reg |= temp;
278 reg |= QPNP_WLED_ILIM_OVERWRITE;
279 pm8x41_wled_reg_write(QPNP_WLED_ILIM_REG(wled->ctrl_base), reg);
280 }
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530281
282 /* Configure the MAX BOOST DUTY register */
283 if (wled->boost_duty_ns < QPNP_WLED_BOOST_DUTY_MIN_NS)
284 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
285 else if (wled->boost_duty_ns > QPNP_WLED_BOOST_DUTY_MAX_NS)
286 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MAX_NS;
287
288 reg = pm8x41_wled_reg_read(
289 QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530290 reg &= QPNP_WLED_BOOST_DUTY_MASK;
291 reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS);
292 pm8x41_wled_reg_write(QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base), reg);
293
294 /* Configure the SWITCHING FREQ register */
295 if (wled->switch_freq_khz == QPNP_WLED_SWITCH_FREQ_1600_KHZ)
296 temp = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
297 else
298 temp = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
299
300 reg = pm8x41_wled_reg_read(
301 QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530302 reg &= QPNP_WLED_SWITCH_FREQ_MASK;
303 reg |= temp;
304 pm8x41_wled_reg_write(QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), reg);
305
306 /* Configure the OVP register */
307 if (wled->ovp_mv <= QPNP_WLED_OVP_17800_MV) {
308 wled->ovp_mv = QPNP_WLED_OVP_17800_MV;
309 temp = 3;
310 } else if (wled->ovp_mv <= QPNP_WLED_OVP_19400_MV) {
311 wled->ovp_mv = QPNP_WLED_OVP_19400_MV;
312 temp = 2;
313 } else if (wled->ovp_mv <= QPNP_WLED_OVP_29500_MV) {
314 wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
315 temp = 1;
316 } else {
317 wled->ovp_mv = QPNP_WLED_OVP_31000_MV;
318 temp = 0;
319 }
320
321 reg = pm8x41_wled_reg_read(
322 QPNP_WLED_OVP_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530323 reg &= QPNP_WLED_OVP_MASK;
324 reg |= temp;
325 pm8x41_wled_reg_write(QPNP_WLED_OVP_REG(wled->ctrl_base), reg);
326
Parth Dixite46b1a22016-06-06 19:16:20 +0530327 if (wled->disp_type_amoled) {
328 for (i = 0; i < NUM_SUPPORTED_AVDD_VOLTAGES; i++) {
329 if (QPNP_WLED_AVDD_DEFAULT_VOLTAGE_MV == qpnp_wled_avdd_target_voltages[i])
330 break;
331 }
332 if (i == NUM_SUPPORTED_AVDD_VOLTAGES)
333 {
334 dprintf(CRITICAL, "Invalid avdd target voltage specified \n");
335 return ERR_NOT_VALID;
336 }
337 /* Update WLED_OVP register based on desired target voltage */
338 reg = qpnp_wled_ovp_reg_settings[i];
339 pm8x41_wled_reg_write(QPNP_WLED_OVP_REG(wled->ctrl_base), reg);
340 /* Update WLED_TRIM register based on desired target voltage */
341 reg = pm8x41_wled_reg_read(
342 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base));
343 reg += qpnp_wled_avdd_trim_adjustments[i];
344 if ((int8_t)reg < QPNP_WLED_AVDD_MIN_TRIM_VALUE)
345 reg = QPNP_WLED_AVDD_MIN_TRIM_VALUE;
346 else if((int8_t)reg > QPNP_WLED_AVDD_MAX_TRIM_VALUE)
347 reg = QPNP_WLED_AVDD_MAX_TRIM_VALUE;
348
349 rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
350 if (rc)
351 return rc;
352
353 temp = pm8x41_wled_reg_read(
354 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base));
355 temp &= ~QPNP_WLED_7P7_TRIM_MASK;
356 temp |= (reg & QPNP_WLED_7P7_TRIM_MASK);
357 pm8x41_wled_reg_write(QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), temp);
358 /* Write to spare to avoid reconfiguration in HLOS */
359 reg = pm8x41_wled_reg_read(
360 QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base));
361 reg |= QPNP_WLED_AVDD_SET_BIT;
362 pm8x41_wled_reg_write(QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base), reg);
363 }
364
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530365 /* Configure the MODULATION register */
366 if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_1200_KHZ) {
367 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_1200_KHZ;
368 temp = 3;
369 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_2400_KHZ) {
370 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_2400_KHZ;
371 temp = 2;
372 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_9600_KHZ) {
373 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
374 temp = 1;
375 } else {
376 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
377 temp = 0;
378 }
379 reg = pm8x41_wled_reg_read(QPNP_WLED_MOD_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530380
381 reg &= QPNP_WLED_MOD_FREQ_MASK;
382 reg |= (temp << QPNP_WLED_MOD_FREQ_SHIFT);
383
384 reg &= QPNP_WLED_PHASE_STAG_MASK;
385 reg |= (wled->en_phase_stag << QPNP_WLED_PHASE_STAG_SHIFT);
386
387 reg &= QPNP_WLED_DIM_RES_MASK;
388 reg |= (wled->en_9b_dim_res << QPNP_WLED_DIM_RES_SHIFT);
389
390 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
391 reg &= QPNP_WLED_DIM_HYB_MASK;
392 reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
393 } else {
394 reg &= QPNP_WLED_DIM_HYB_MASK;
395 reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
396 reg &= QPNP_WLED_DIM_ANA_MASK;
397 reg |= wled->dim_mode;
398 }
399
400 pm8x41_wled_reg_write(QPNP_WLED_MOD_REG(wled->sink_base), reg);
401
402 /* Configure the HYBRID THRESHOLD register */
403 if (wled->hyb_thres < QPNP_WLED_HYB_THRES_MIN)
404 wled->hyb_thres = QPNP_WLED_HYB_THRES_MIN;
405 else if (wled->hyb_thres > QPNP_WLED_HYB_THRES_MAX)
406 wled->hyb_thres = QPNP_WLED_HYB_THRES_MAX;
407
408 reg = pm8x41_wled_reg_read(
409 QPNP_WLED_HYB_THRES_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530410
411 reg &= QPNP_WLED_HYB_THRES_MASK;
412 temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1;
413 reg |= temp;
414 pm8x41_wled_reg_write(QPNP_WLED_HYB_THRES_REG(wled->sink_base), reg);
415
416 for (i = 0; i < wled->num_strings; i++) {
417 if (wled->strings[i] >= QPNP_WLED_MAX_STRINGS) {
418 dprintf(CRITICAL,"Invalid string number\n");
419 return ERR_NOT_VALID;
420 }
421
422 /* MODULATOR */
423 reg = pm8x41_wled_reg_read(
424 QPNP_WLED_MOD_EN_REG(wled->sink_base,
425 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530426 reg &= QPNP_WLED_MOD_EN_MASK;
427 reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
428 pm8x41_wled_reg_write(QPNP_WLED_MOD_EN_REG(wled->sink_base,
429 wled->strings[i]), reg);
430
431 /* SYNC DELAY */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800432 if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530433 wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US;
434
435 reg = pm8x41_wled_reg_read(
436 QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
437 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530438 reg &= QPNP_WLED_SYNC_DLY_MASK;
439 temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
440 reg |= temp;
441 pm8x41_wled_reg_write(QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
442 wled->strings[i]), reg);
443
444 /* FULL SCALE CURRENT */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800445 if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530446 wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
447
448 reg = pm8x41_wled_reg_read(
449 QPNP_WLED_FS_CURR_REG(wled->sink_base,
450 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530451 reg &= QPNP_WLED_FS_CURR_MASK;
452 temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
453 reg |= temp;
454 pm8x41_wled_reg_write(QPNP_WLED_FS_CURR_REG(wled->sink_base,
455 wled->strings[i]), reg);
456
457 /* CABC */
458 reg = pm8x41_wled_reg_read(
459 QPNP_WLED_CABC_REG(wled->sink_base,
460 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530461 reg &= QPNP_WLED_CABC_MASK;
462 reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT);
463 pm8x41_wled_reg_write(QPNP_WLED_CABC_REG(wled->sink_base,
464 wled->strings[i]), reg);
465
466 /* Enable CURRENT SINK */
467 reg = pm8x41_wled_reg_read(
468 QPNP_WLED_CURR_SINK_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530469 temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
470 reg |= (1 << temp);
471 pm8x41_wled_reg_write(QPNP_WLED_CURR_SINK_REG(wled->sink_base), reg);
472 }
473
474 /* LAB fast precharge */
475 reg = pm8x41_wled_reg_read(
476 QPNP_WLED_LAB_FAST_PC_REG(wled->lab_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530477 reg &= QPNP_WLED_LAB_FAST_PC_MASK;
478 reg |= (wled->lab_fast_precharge << QPNP_WLED_LAB_FAST_PC_SHIFT);
wufeng.jiangb1008482016-06-14 10:41:39 +0800479 /* LAB max precharge time */
480 reg &= QPNP_WLED_PRECHARGE_MASK;
481 reg |= (wled->lab_max_precharge_time);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530482 pm8x41_wled_reg_write(QPNP_WLED_LAB_FAST_PC_REG(wled->lab_base), reg);
483
484 /* Configure lab display type */
485 rc = qpnp_wled_set_display_type(wled, wled->lab_base);
486 if (rc < 0)
487 return rc;
488
489 /* make LAB module ready */
490 rc = qpnp_wled_module_ready(wled, wled->lab_base, true);
491 if (rc < 0)
492 return rc;
493
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530494 /* Disable LAB pulse skipping for AMOLED */
495 if (wled->disp_type_amoled)
496 pm8x41_wled_reg_write(wled->lab_base +
497 QPNP_LABIBB_PS_CTL, 0x00);
498
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530499 /* IBB active bias */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800500 if (wled->ibb_pwrup_dly_ms > QPNP_WLED_IBB_PWRUP_DLY_MAX_MS)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530501 wled->ibb_pwrup_dly_ms = QPNP_WLED_IBB_PWRUP_DLY_MAX_MS;
502
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800503 if (wled->ibb_pwrdn_dly_ms > QPNP_WLED_IBB_PWRDN_DLY_MAX_MS)
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800504 wled->ibb_pwrdn_dly_ms = QPNP_WLED_IBB_PWRDN_DLY_MAX_MS;
505
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530506 reg = pm8x41_wled_reg_read(
507 QPNP_WLED_IBB_BIAS_REG(wled->ibb_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530508
509 reg &= QPNP_WLED_IBB_BIAS_MASK;
510 reg |= (!wled->ibb_bias_active << QPNP_WLED_IBB_BIAS_SHIFT);
511
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800512 temp = wled->ibb_pwrup_dly_ms;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530513 reg &= QPNP_WLED_IBB_PWRUP_DLY_MASK;
514 reg |= (temp << QPNP_WLED_IBB_PWRUP_DLY_SHIFT);
Channagoud Kadabif5f0ded2015-02-25 13:42:18 -0800515 /* Power down delay bits could already be set, clear them before
516 * or'ing new values
517 */
518 reg &= ~(PWRDN_DLY2_MASK);
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800519 reg |= wled->ibb_pwrdn_dly_ms;
520 reg |= (wled->ibb_discharge_en << 2);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530521
522 rc = qpnp_wled_sec_access(wled, wled->ibb_base);
523 if (rc)
524 return rc;
525
526 pm8x41_wled_reg_write(QPNP_WLED_IBB_BIAS_REG(wled->ibb_base), reg);
527
528 /* Configure ibb display type */
529 rc = qpnp_wled_set_display_type(wled, wled->ibb_base);
530 if (rc < 0)
531 return rc;
532
533 /* make IBB module ready */
534 rc = qpnp_wled_module_ready(wled, wled->ibb_base, true);
535 if (rc < 0)
536 return rc;
537
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800538 rc = qpnp_labibb_regulator_set_voltage(wled);
539 if (rc < 0)
540 return rc;
541
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530542 return 0;
543}
544
545/* Setup wled default parameters */
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800546static int qpnp_wled_setup(struct qpnp_wled *wled, struct qpnp_wled_config_data *config)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530547{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800548 int i;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530549
550 wled->sink_base = QPNP_WLED_SINK_BASE;
551 wled->ctrl_base = QPNP_WLED_CTRL_BASE;
552 wled->ibb_base = QPNP_WLED_IBB_BASE;
553 wled->lab_base = QPNP_WLED_LAB_BASE;
554 wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
555 wled->vref_mv = QPNP_WLED_DFLT_VREF_MV;
556 wled->switch_freq_khz = QPNP_WLED_SWITCH_FREQ_800_KHZ;
557 wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
558 wled->ilim_ma = QPNP_WLED_DFLT_ILIM_MA;
559 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
560 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
561 wled->dim_mode = QPNP_WLED_DIM_HYBRID;
562 wled->dim_shape = QPNP_WLED_DIM_SHAPE_LINEAR;
563
564 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
565 wled->hyb_thres = QPNP_WLED_DFLT_HYB_THRES;
566 }
567
568 wled->sync_dly_us = 800;
569 wled->fs_curr_ua = 16000;
570 wled->en_9b_dim_res = 0;
571 wled->en_phase_stag = true;
572 wled->en_cabc = 0;
573
574 wled->num_strings = QPNP_WLED_MAX_STRINGS;
575 for (i = 0; i < wled->num_strings; i++)
576 wled->strings[i] = i;
577
578 wled->ibb_bias_active = false;
Wufeng7bae3c92016-04-27 13:02:15 +0800579 wled->lab_fast_precharge = false;
wufeng.jiangb1008482016-06-14 10:41:39 +0800580 wled->lab_max_precharge_time = QPNP_WLED_PRECHARGE_US500;
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800581 wled->ibb_pwrup_dly_ms = config->pwr_up_delay;
582 wled->ibb_pwrdn_dly_ms = config->pwr_down_delay;
583 wled->ibb_discharge_en = config->ibb_discharge_en;
584 wled->disp_type_amoled = config->display_type;
585 wled->lab_min_volt = config->lab_min_volt;
586 wled->lab_max_volt = config->lab_max_volt;
587 wled->ibb_min_volt = config->ibb_min_volt;
588 wled->ibb_max_volt = config->ibb_max_volt;
589 wled->ibb_init_volt = config->ibb_init_volt;
590 wled->lab_init_volt = config->lab_init_volt;
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530591 wled->lab_ibb_swire_control = config->lab_ibb_swire_control;
592 wled->wled_avdd_control = config->wled_avdd_control;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530593
594 return 0;
595}
596
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800597int qpnp_wled_init(struct qpnp_wled_config_data *config)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530598{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800599 int rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530600 struct qpnp_wled *wled;
601
Parth Dixit550ddf32016-11-28 17:00:29 +0530602 if(!target_is_pmi_enabled())
603 return ERR_NOT_FOUND;
604
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530605 wled = malloc(sizeof(struct qpnp_wled));
606 if (!wled)
607 return ERR_NO_MEMORY;
608
609 memset(wled, 0, sizeof(struct qpnp_wled));
610
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800611 rc = qpnp_wled_setup(wled, config);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530612 if (rc) {
613 dprintf(CRITICAL, "Setting WLED parameters failed\n");
614 return rc;
615 }
616
617 rc = qpnp_wled_config(wled);
618 if (rc) {
619 dprintf(CRITICAL, "wled config failed\n");
620 return rc;
621 }
622
623 gwled = wled;
624
625 return rc;
626}
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800627
628static int qpnp_labibb_regulator_set_voltage(struct qpnp_wled *wled)
629{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800630 int rc = -1;
631 uint32_t new_uV;
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800632 uint8_t val, mask=0;
633
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530634 if (!wled->disp_type_amoled || !wled->lab_ibb_swire_control) {
635 if (wled->lab_min_volt < wled->lab_init_volt) {
636 dprintf(CRITICAL,"qpnp_lab_regulator_set_voltage failed, min_uV %d is less than init volt %d\n",
637 wled->lab_min_volt, wled->lab_init_volt);
638 return rc;
639 }
640
641 val = (((wled->lab_min_volt - wled->lab_init_volt) +
642 (IBB_LAB_VREG_STEP_SIZE - 1)) / IBB_LAB_VREG_STEP_SIZE);
643 new_uV = val * IBB_LAB_VREG_STEP_SIZE + wled->lab_init_volt;
644
645 if (new_uV > wled->lab_max_volt) {
646 dprintf(CRITICAL,"qpnp_ibb_regulator_set_voltage unable to set voltage (%d %d)\n",
647 wled->lab_min_volt, wled->lab_max_volt);
648 return rc;
649 }
650 val |= QPNP_LAB_OUTPUT_OVERRIDE_EN;
651 mask = pm8x41_wled_reg_read(wled->lab_base +
652 QPNP_LABIBB_OUTPUT_VOLTAGE);
653 mask &= ~(QPNP_LAB_SET_VOLTAGE_MASK
654 | QPNP_LAB_OUTPUT_OVERRIDE_EN);
655 mask |= val & (QPNP_LAB_SET_VOLTAGE_MASK
656 | QPNP_LAB_OUTPUT_OVERRIDE_EN);
657
658 pm8x41_wled_reg_write(wled->lab_base +
659 QPNP_LABIBB_OUTPUT_VOLTAGE, mask);
660 udelay(2);
661
662 /*
663 * IBB Set Voltage.
664 * For AMOLED panels, the IBB voltage needs to be
665 * controlled by panel.
666 */
667 if (wled->ibb_min_volt < wled->ibb_init_volt) {
668 dprintf(CRITICAL, "qpnp_ibb_regulator_set_voltage failed, min_uV %d is less than init volt %d\n",
669 wled->ibb_min_volt, wled->ibb_init_volt);
670 return rc;
671 }
672
673 val = (((wled->ibb_min_volt - wled->ibb_init_volt) +
674 (IBB_LAB_VREG_STEP_SIZE - 1)) / IBB_LAB_VREG_STEP_SIZE);
675 new_uV = val * IBB_LAB_VREG_STEP_SIZE + wled->ibb_init_volt;
676 if (new_uV > wled->ibb_max_volt) {
677 dprintf(CRITICAL, "qpnp_ibb_regulator_set_voltage unable to set voltage %d %d\n",
678 wled->ibb_min_volt, wled->ibb_max_volt);
679 return rc;
680 }
681 val |= QPNP_LAB_OUTPUT_OVERRIDE_EN;
682 mask = pm8x41_wled_reg_read(wled->ibb_base +
683 QPNP_LABIBB_OUTPUT_VOLTAGE);
684 udelay(2);
685 mask &= ~(QPNP_IBB_SET_VOLTAGE_MASK |
686 QPNP_LAB_OUTPUT_OVERRIDE_EN);
687 mask |= (val & (QPNP_IBB_SET_VOLTAGE_MASK |
688 QPNP_LAB_OUTPUT_OVERRIDE_EN));
689
690 pm8x41_wled_reg_write(wled->ibb_base +
691 QPNP_LABIBB_OUTPUT_VOLTAGE, mask);
692 } else {
693 pm8x41_wled_reg_write(wled->ibb_base +
694 QPNP_LABIBB_OUTPUT_VOLTAGE, 0x00);
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800695 }
696
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800697 return 0;
698}