blob: 10a61b5e1820097fad7aef30dc753e1eb7a64376 [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
Parth Dixite46b1a22016-06-06 19:16:20 +053037static int qpnp_wled_avdd_target_voltages[NUM_SUPPORTED_AVDD_VOLTAGES] = {
38 7900, 7600, 7300, 6400, 6100, 5800,
39};
40
41static uint8_t qpnp_wled_ovp_reg_settings[NUM_SUPPORTED_AVDD_VOLTAGES] = {
42 0x0, 0x0, 0x1, 0x2, 0x2, 0x3,
43};
44
45static int qpnp_wled_avdd_trim_adjustments[NUM_SUPPORTED_AVDD_VOLTAGES] = {
46 3, 0, -2, 7, 3, 3,
47};
48
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053049static int fls(uint16_t n)
50{
51 int i = 0;
52 for (; n; n >>= 1, i++);
53 return i;
54}
55
56static struct qpnp_wled *gwled;
Aparna Mallavarapua42de902014-12-08 17:25:44 -080057static int qpnp_labibb_regulator_set_voltage(struct qpnp_wled *wled);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053058
59static int qpnp_wled_sec_access(struct qpnp_wled *wled, uint16_t base_addr)
60{
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053061 uint8_t reg = QPNP_WLED_SEC_UNLOCK;
62
63 pm8x41_wled_reg_write(QPNP_WLED_SEC_ACCESS_REG(base_addr), reg);
64
65 return 0;
66}
67
68/* set wled to a level of brightness */
69static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
70{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -080071 int i;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +053072 uint8_t reg;
73
74 /* set brightness registers */
75 for (i = 0; i < wled->num_strings; i++) {
76 reg = level & QPNP_WLED_BRIGHT_LSB_MASK;
77 pm8x41_wled_reg_write(QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base,
78 wled->strings[i]), reg);
79
80 reg = level >> QPNP_WLED_BRIGHT_MSB_SHIFT;
81 reg = reg & QPNP_WLED_BRIGHT_MSB_MASK;
82 pm8x41_wled_reg_write(QPNP_WLED_BRIGHT_MSB_REG(wled->sink_base,
83 wled->strings[i]), reg);
84 }
85
86 /* sync */
87 reg = QPNP_WLED_SYNC;
88 pm8x41_wled_reg_write(QPNP_WLED_SYNC_REG(wled->sink_base), reg);
89
90 reg = QPNP_WLED_SYNC_RESET;
91 pm8x41_wled_reg_write(QPNP_WLED_SYNC_REG(wled->sink_base), reg);
92
93 return 0;
94}
95
96static int qpnp_wled_enable(struct qpnp_wled *wled,
97 uint16_t base_addr, bool state)
98{
99 uint8_t reg;
100
101 reg = pm8x41_wled_reg_read(
102 QPNP_WLED_MODULE_EN_REG(base_addr));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530103 reg &= QPNP_WLED_MODULE_EN_MASK;
104 reg |= (state << QPNP_WLED_MODULE_EN_SHIFT);
105 pm8x41_wled_reg_write(QPNP_WLED_MODULE_EN_REG(base_addr), reg);
106
107 return 0;
108}
109
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530110static int qpnp_wled_ibb_swire_rdy(struct qpnp_wled *wled,
111 uint16_t base_addr, bool state)
112{
113 uint8_t reg;
114
115 reg = pm8x41_wled_reg_read(
116 QPNP_WLED_MODULE_EN_REG(base_addr));
117 /* Do not enable IBB module when SWIRE ready is set */
118 reg &= ~(QPNP_IBB_SWIRE_RDY_MASK | QPNP_IBB_MODULE_EN_MASK);
119 reg |= (state << QPNP_IBB_SWIRE_RDY_SHIFT);
120 pm8x41_wled_reg_write(QPNP_WLED_MODULE_EN_REG(base_addr), reg);
121
122 return 0;
123}
124
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530125int qpnp_ibb_enable(bool state)
126{
127 int rc = 0;
128 uint8_t reg;
129
130 if (!gwled) {
131 dprintf(CRITICAL, "%s: wled is not initialized yet\n", __func__);
132 return ERROR;
133 }
134
135 /* enable lab */
136 if (gwled->ibb_bias_active) {
137 rc = qpnp_wled_enable(gwled, gwled->lab_base, state);
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800138 udelay(QPNP_WLED_LAB_START_DLY_US + 1);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530139 if (rc < 0)
140 return rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530141 } else {
142 reg = pm8x41_wled_reg_read(QPNP_WLED_LAB_IBB_RDY_REG(gwled->lab_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530143
144 reg &= QPNP_WLED_MODULE_EN_MASK;
145 reg |= (state << QPNP_WLED_MODULE_EN_SHIFT);
146 pm8x41_wled_reg_write(QPNP_WLED_LAB_IBB_RDY_REG(gwled->lab_base), reg);
147 }
148
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530149 if (gwled->disp_type_amoled && gwled->lab_ibb_swire_control)
150 rc = qpnp_wled_ibb_swire_rdy(gwled, gwled->ibb_base, state);
151 else
152 rc = qpnp_wled_enable(gwled, gwled->ibb_base, state);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530153
154 return rc;
155}
156
157/* enable / disable wled brightness */
158void qpnp_wled_enable_backlight(int enable)
159{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800160 int rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530161
162 if (!gwled) {
163 dprintf(CRITICAL, "%s: wled is not initialized yet\n", __func__);
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800164 return;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530165 }
166
167 if (enable) {
168 rc = qpnp_wled_set_level(gwled, QPNP_WLED_MAX_BR_LEVEL);
169 if (rc) {
170 dprintf(CRITICAL,"wled set level failed\n");
171 return;
172 }
173 }
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530174
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530175 if (!gwled->disp_type_amoled || !gwled->wled_avdd_control) {
176 rc = qpnp_wled_enable(gwled, gwled->ctrl_base, enable);
177 if (rc) {
178 dprintf(CRITICAL, "wled %sable failed\n",
179 enable ? "en" : "dis");
180 return;
181 }
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530182 }
183
184}
185
186static int qpnp_wled_set_display_type(struct qpnp_wled *wled, uint16_t base_addr)
187{
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530188 uint8_t reg = 0;
Parth Dixitb1461bf2015-06-24 20:51:08 +0530189 int rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530190
191 /* display type */
192 reg = pm8x41_wled_reg_read(QPNP_WLED_DISP_SEL_REG(base_addr));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530193
194 reg &= QPNP_WLED_DISP_SEL_MASK;
195 reg |= (wled->disp_type_amoled << QPNP_WLED_DISP_SEL_SHIFT);
Parth Dixitb1461bf2015-06-24 20:51:08 +0530196
197 rc = qpnp_wled_sec_access(wled, base_addr);
198 if (rc)
199 return rc;
200
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530201 pm8x41_wled_reg_write(QPNP_WLED_DISP_SEL_REG(base_addr), reg);
202
203 return 0;
204}
205
206static int qpnp_wled_module_ready(struct qpnp_wled *wled, uint16_t base_addr, bool state)
207{
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530208 uint8_t reg;
209
210 reg = pm8x41_wled_reg_read(
211 QPNP_WLED_MODULE_RDY_REG(base_addr));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530212 reg &= QPNP_WLED_MODULE_RDY_MASK;
213 reg |= (state << QPNP_WLED_MODULE_RDY_SHIFT);
214 pm8x41_wled_reg_write(QPNP_WLED_MODULE_RDY_REG(base_addr), reg);
215
216 return 0;
217}
218
219/* Configure WLED registers */
220static int qpnp_wled_config(struct qpnp_wled *wled)
221{
222 int rc, i, temp;
223 uint8_t reg = 0;
224
225 /* Configure display type */
226 rc = qpnp_wled_set_display_type(wled, wled->ctrl_base);
227 if (rc < 0)
228 return rc;
229
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530230 /* Recommended WLED MDOS settings for AMOLED */
231 if (wled->disp_type_amoled) {
232 pm8x41_wled_reg_write(QPNP_WLED_VLOOP_COMP_RES(wled->ctrl_base),
233 0x8F);
234 pm8x41_wled_reg_write(QPNP_WLED_VLOOP_COMP_GM(wled->ctrl_base),
235 0x81);
236 pm8x41_wled_reg_write(QPNP_WLED_PSM_CTRL(wled->ctrl_base),
237 0x83);
Mayank Grover0b0ec802016-11-04 11:47:46 +0530238 pm8x41_wled_reg_write(QPNP_WLED_PSM_EN(wled->ctrl_base),
239 0x80);
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530240
241 rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
242 if (rc)
243 return rc;
244 pm8x41_wled_reg_write(QPNP_WLED_TEST4(wled->ctrl_base), 0x13);
245 }
246
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530247 /* Configure the FEEDBACK OUTPUT register */
248 reg = pm8x41_wled_reg_read(
249 QPNP_WLED_FDBK_OP_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530250 reg &= QPNP_WLED_FDBK_OP_MASK;
251 reg |= wled->fdbk_op;
252 pm8x41_wled_reg_write(QPNP_WLED_FDBK_OP_REG(wled->ctrl_base), reg);
253
254 /* Configure the VREF register */
255 if (wled->vref_mv < QPNP_WLED_VREF_MIN_MV)
256 wled->vref_mv = QPNP_WLED_VREF_MIN_MV;
257 else if (wled->vref_mv > QPNP_WLED_VREF_MAX_MV)
258 wled->vref_mv = QPNP_WLED_VREF_MAX_MV;
259
260 reg = pm8x41_wled_reg_read(
261 QPNP_WLED_VREF_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530262 reg &= QPNP_WLED_VREF_MASK;
263 temp = wled->vref_mv - QPNP_WLED_VREF_MIN_MV;
264 reg |= (temp / QPNP_WLED_VREF_STEP_MV);
265 pm8x41_wled_reg_write(QPNP_WLED_VREF_REG(wled->ctrl_base), reg);
266
267 /* Configure the ILIM register */
268 if (wled->ilim_ma < QPNP_WLED_ILIM_MIN_MA)
269 wled->ilim_ma = QPNP_WLED_ILIM_MIN_MA;
270 else if (wled->ilim_ma > QPNP_WLED_ILIM_MAX_MA)
271 wled->ilim_ma = QPNP_WLED_ILIM_MAX_MA;
272
273 reg = pm8x41_wled_reg_read(
274 QPNP_WLED_ILIM_REG(wled->ctrl_base));
Alex Sarraf9bd81b92015-05-20 15:59:32 -0700275 temp = (wled->ilim_ma / QPNP_WLED_ILIM_STEP_MA);
276 if (temp != (reg & ~QPNP_WLED_ILIM_MASK)) {
277 reg &= QPNP_WLED_ILIM_MASK;
278 reg |= temp;
279 reg |= QPNP_WLED_ILIM_OVERWRITE;
280 pm8x41_wled_reg_write(QPNP_WLED_ILIM_REG(wled->ctrl_base), reg);
281 }
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530282
283 /* Configure the MAX BOOST DUTY register */
284 if (wled->boost_duty_ns < QPNP_WLED_BOOST_DUTY_MIN_NS)
285 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
286 else if (wled->boost_duty_ns > QPNP_WLED_BOOST_DUTY_MAX_NS)
287 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MAX_NS;
288
289 reg = pm8x41_wled_reg_read(
290 QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530291 reg &= QPNP_WLED_BOOST_DUTY_MASK;
292 reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS);
293 pm8x41_wled_reg_write(QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base), reg);
294
295 /* Configure the SWITCHING FREQ register */
296 if (wled->switch_freq_khz == QPNP_WLED_SWITCH_FREQ_1600_KHZ)
297 temp = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
298 else
299 temp = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
300
301 reg = pm8x41_wled_reg_read(
302 QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530303 reg &= QPNP_WLED_SWITCH_FREQ_MASK;
304 reg |= temp;
305 pm8x41_wled_reg_write(QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), reg);
306
307 /* Configure the OVP register */
308 if (wled->ovp_mv <= QPNP_WLED_OVP_17800_MV) {
309 wled->ovp_mv = QPNP_WLED_OVP_17800_MV;
310 temp = 3;
311 } else if (wled->ovp_mv <= QPNP_WLED_OVP_19400_MV) {
312 wled->ovp_mv = QPNP_WLED_OVP_19400_MV;
313 temp = 2;
314 } else if (wled->ovp_mv <= QPNP_WLED_OVP_29500_MV) {
315 wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
316 temp = 1;
317 } else {
318 wled->ovp_mv = QPNP_WLED_OVP_31000_MV;
319 temp = 0;
320 }
321
322 reg = pm8x41_wled_reg_read(
323 QPNP_WLED_OVP_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530324 reg &= QPNP_WLED_OVP_MASK;
325 reg |= temp;
326 pm8x41_wled_reg_write(QPNP_WLED_OVP_REG(wled->ctrl_base), reg);
327
Parth Dixite46b1a22016-06-06 19:16:20 +0530328 if (wled->disp_type_amoled) {
329 for (i = 0; i < NUM_SUPPORTED_AVDD_VOLTAGES; i++) {
330 if (QPNP_WLED_AVDD_DEFAULT_VOLTAGE_MV == qpnp_wled_avdd_target_voltages[i])
331 break;
332 }
333 if (i == NUM_SUPPORTED_AVDD_VOLTAGES)
334 {
335 dprintf(CRITICAL, "Invalid avdd target voltage specified \n");
336 return ERR_NOT_VALID;
337 }
338 /* Update WLED_OVP register based on desired target voltage */
339 reg = qpnp_wled_ovp_reg_settings[i];
340 pm8x41_wled_reg_write(QPNP_WLED_OVP_REG(wled->ctrl_base), reg);
341 /* Update WLED_TRIM register based on desired target voltage */
342 reg = pm8x41_wled_reg_read(
343 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base));
344 reg += qpnp_wled_avdd_trim_adjustments[i];
345 if ((int8_t)reg < QPNP_WLED_AVDD_MIN_TRIM_VALUE)
346 reg = QPNP_WLED_AVDD_MIN_TRIM_VALUE;
347 else if((int8_t)reg > QPNP_WLED_AVDD_MAX_TRIM_VALUE)
348 reg = QPNP_WLED_AVDD_MAX_TRIM_VALUE;
349
350 rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
351 if (rc)
352 return rc;
353
354 temp = pm8x41_wled_reg_read(
355 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base));
356 temp &= ~QPNP_WLED_7P7_TRIM_MASK;
357 temp |= (reg & QPNP_WLED_7P7_TRIM_MASK);
358 pm8x41_wled_reg_write(QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), temp);
359 /* Write to spare to avoid reconfiguration in HLOS */
360 reg = pm8x41_wled_reg_read(
361 QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base));
362 reg |= QPNP_WLED_AVDD_SET_BIT;
363 pm8x41_wled_reg_write(QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base), reg);
364 }
365
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530366 /* Configure the MODULATION register */
367 if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_1200_KHZ) {
368 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_1200_KHZ;
369 temp = 3;
370 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_2400_KHZ) {
371 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_2400_KHZ;
372 temp = 2;
373 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_9600_KHZ) {
374 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
375 temp = 1;
376 } else {
377 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
378 temp = 0;
379 }
380 reg = pm8x41_wled_reg_read(QPNP_WLED_MOD_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530381
382 reg &= QPNP_WLED_MOD_FREQ_MASK;
383 reg |= (temp << QPNP_WLED_MOD_FREQ_SHIFT);
384
385 reg &= QPNP_WLED_PHASE_STAG_MASK;
386 reg |= (wled->en_phase_stag << QPNP_WLED_PHASE_STAG_SHIFT);
387
388 reg &= QPNP_WLED_DIM_RES_MASK;
389 reg |= (wled->en_9b_dim_res << QPNP_WLED_DIM_RES_SHIFT);
390
391 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
392 reg &= QPNP_WLED_DIM_HYB_MASK;
393 reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
394 } else {
395 reg &= QPNP_WLED_DIM_HYB_MASK;
396 reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
397 reg &= QPNP_WLED_DIM_ANA_MASK;
398 reg |= wled->dim_mode;
399 }
400
401 pm8x41_wled_reg_write(QPNP_WLED_MOD_REG(wled->sink_base), reg);
402
403 /* Configure the HYBRID THRESHOLD register */
404 if (wled->hyb_thres < QPNP_WLED_HYB_THRES_MIN)
405 wled->hyb_thres = QPNP_WLED_HYB_THRES_MIN;
406 else if (wled->hyb_thres > QPNP_WLED_HYB_THRES_MAX)
407 wled->hyb_thres = QPNP_WLED_HYB_THRES_MAX;
408
409 reg = pm8x41_wled_reg_read(
410 QPNP_WLED_HYB_THRES_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530411
412 reg &= QPNP_WLED_HYB_THRES_MASK;
413 temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1;
414 reg |= temp;
415 pm8x41_wled_reg_write(QPNP_WLED_HYB_THRES_REG(wled->sink_base), reg);
416
417 for (i = 0; i < wled->num_strings; i++) {
418 if (wled->strings[i] >= QPNP_WLED_MAX_STRINGS) {
419 dprintf(CRITICAL,"Invalid string number\n");
420 return ERR_NOT_VALID;
421 }
422
423 /* MODULATOR */
424 reg = pm8x41_wled_reg_read(
425 QPNP_WLED_MOD_EN_REG(wled->sink_base,
426 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530427 reg &= QPNP_WLED_MOD_EN_MASK;
428 reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
429 pm8x41_wled_reg_write(QPNP_WLED_MOD_EN_REG(wled->sink_base,
430 wled->strings[i]), reg);
431
432 /* SYNC DELAY */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800433 if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530434 wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US;
435
436 reg = pm8x41_wled_reg_read(
437 QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
438 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530439 reg &= QPNP_WLED_SYNC_DLY_MASK;
440 temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
441 reg |= temp;
442 pm8x41_wled_reg_write(QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
443 wled->strings[i]), reg);
444
445 /* FULL SCALE CURRENT */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800446 if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530447 wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
448
449 reg = pm8x41_wled_reg_read(
450 QPNP_WLED_FS_CURR_REG(wled->sink_base,
451 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530452 reg &= QPNP_WLED_FS_CURR_MASK;
453 temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
454 reg |= temp;
455 pm8x41_wled_reg_write(QPNP_WLED_FS_CURR_REG(wled->sink_base,
456 wled->strings[i]), reg);
457
458 /* CABC */
459 reg = pm8x41_wled_reg_read(
460 QPNP_WLED_CABC_REG(wled->sink_base,
461 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530462 reg &= QPNP_WLED_CABC_MASK;
463 reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT);
464 pm8x41_wled_reg_write(QPNP_WLED_CABC_REG(wled->sink_base,
465 wled->strings[i]), reg);
466
467 /* Enable CURRENT SINK */
468 reg = pm8x41_wled_reg_read(
469 QPNP_WLED_CURR_SINK_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530470 temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
471 reg |= (1 << temp);
472 pm8x41_wled_reg_write(QPNP_WLED_CURR_SINK_REG(wled->sink_base), reg);
473 }
474
475 /* LAB fast precharge */
476 reg = pm8x41_wled_reg_read(
477 QPNP_WLED_LAB_FAST_PC_REG(wled->lab_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530478 reg &= QPNP_WLED_LAB_FAST_PC_MASK;
479 reg |= (wled->lab_fast_precharge << QPNP_WLED_LAB_FAST_PC_SHIFT);
wufeng.jiangb1008482016-06-14 10:41:39 +0800480 /* LAB max precharge time */
481 reg &= QPNP_WLED_PRECHARGE_MASK;
482 reg |= (wled->lab_max_precharge_time);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530483 pm8x41_wled_reg_write(QPNP_WLED_LAB_FAST_PC_REG(wled->lab_base), reg);
484
485 /* Configure lab display type */
486 rc = qpnp_wled_set_display_type(wled, wled->lab_base);
487 if (rc < 0)
488 return rc;
489
490 /* make LAB module ready */
491 rc = qpnp_wled_module_ready(wled, wled->lab_base, true);
492 if (rc < 0)
493 return rc;
494
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530495 /* Disable LAB pulse skipping for AMOLED */
496 if (wled->disp_type_amoled)
497 pm8x41_wled_reg_write(wled->lab_base +
498 QPNP_LABIBB_PS_CTL, 0x00);
499
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530500 /* IBB active bias */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800501 if (wled->ibb_pwrup_dly_ms > QPNP_WLED_IBB_PWRUP_DLY_MAX_MS)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530502 wled->ibb_pwrup_dly_ms = QPNP_WLED_IBB_PWRUP_DLY_MAX_MS;
503
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800504 if (wled->ibb_pwrdn_dly_ms > QPNP_WLED_IBB_PWRDN_DLY_MAX_MS)
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800505 wled->ibb_pwrdn_dly_ms = QPNP_WLED_IBB_PWRDN_DLY_MAX_MS;
506
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530507 reg = pm8x41_wled_reg_read(
508 QPNP_WLED_IBB_BIAS_REG(wled->ibb_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530509
510 reg &= QPNP_WLED_IBB_BIAS_MASK;
511 reg |= (!wled->ibb_bias_active << QPNP_WLED_IBB_BIAS_SHIFT);
512
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800513 temp = wled->ibb_pwrup_dly_ms;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530514 reg &= QPNP_WLED_IBB_PWRUP_DLY_MASK;
515 reg |= (temp << QPNP_WLED_IBB_PWRUP_DLY_SHIFT);
Channagoud Kadabif5f0ded2015-02-25 13:42:18 -0800516 /* Power down delay bits could already be set, clear them before
517 * or'ing new values
518 */
519 reg &= ~(PWRDN_DLY2_MASK);
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800520 reg |= wled->ibb_pwrdn_dly_ms;
521 reg |= (wled->ibb_discharge_en << 2);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530522
523 rc = qpnp_wled_sec_access(wled, wled->ibb_base);
524 if (rc)
525 return rc;
526
527 pm8x41_wled_reg_write(QPNP_WLED_IBB_BIAS_REG(wled->ibb_base), reg);
528
529 /* Configure ibb display type */
530 rc = qpnp_wled_set_display_type(wled, wled->ibb_base);
531 if (rc < 0)
532 return rc;
533
534 /* make IBB module ready */
535 rc = qpnp_wled_module_ready(wled, wled->ibb_base, true);
536 if (rc < 0)
537 return rc;
538
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800539 rc = qpnp_labibb_regulator_set_voltage(wled);
540 if (rc < 0)
541 return rc;
542
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530543 return 0;
544}
545
546/* Setup wled default parameters */
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800547static int qpnp_wled_setup(struct qpnp_wled *wled, struct qpnp_wled_config_data *config)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530548{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800549 int i;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530550
551 wled->sink_base = QPNP_WLED_SINK_BASE;
552 wled->ctrl_base = QPNP_WLED_CTRL_BASE;
553 wled->ibb_base = QPNP_WLED_IBB_BASE;
554 wled->lab_base = QPNP_WLED_LAB_BASE;
555 wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
556 wled->vref_mv = QPNP_WLED_DFLT_VREF_MV;
557 wled->switch_freq_khz = QPNP_WLED_SWITCH_FREQ_800_KHZ;
558 wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
559 wled->ilim_ma = QPNP_WLED_DFLT_ILIM_MA;
560 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
561 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
562 wled->dim_mode = QPNP_WLED_DIM_HYBRID;
563 wled->dim_shape = QPNP_WLED_DIM_SHAPE_LINEAR;
564
565 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
566 wled->hyb_thres = QPNP_WLED_DFLT_HYB_THRES;
567 }
568
569 wled->sync_dly_us = 800;
570 wled->fs_curr_ua = 16000;
571 wled->en_9b_dim_res = 0;
572 wled->en_phase_stag = true;
573 wled->en_cabc = 0;
574
575 wled->num_strings = QPNP_WLED_MAX_STRINGS;
576 for (i = 0; i < wled->num_strings; i++)
577 wled->strings[i] = i;
578
579 wled->ibb_bias_active = false;
Wufeng7bae3c92016-04-27 13:02:15 +0800580 wled->lab_fast_precharge = false;
wufeng.jiangb1008482016-06-14 10:41:39 +0800581 wled->lab_max_precharge_time = QPNP_WLED_PRECHARGE_US500;
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800582 wled->ibb_pwrup_dly_ms = config->pwr_up_delay;
583 wled->ibb_pwrdn_dly_ms = config->pwr_down_delay;
584 wled->ibb_discharge_en = config->ibb_discharge_en;
585 wled->disp_type_amoled = config->display_type;
586 wled->lab_min_volt = config->lab_min_volt;
587 wled->lab_max_volt = config->lab_max_volt;
588 wled->ibb_min_volt = config->ibb_min_volt;
589 wled->ibb_max_volt = config->ibb_max_volt;
590 wled->ibb_init_volt = config->ibb_init_volt;
591 wled->lab_init_volt = config->lab_init_volt;
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530592 wled->lab_ibb_swire_control = config->lab_ibb_swire_control;
593 wled->wled_avdd_control = config->wled_avdd_control;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530594
595 return 0;
596}
597
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800598int qpnp_wled_init(struct qpnp_wled_config_data *config)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530599{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800600 int rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530601 struct qpnp_wled *wled;
602
603 wled = malloc(sizeof(struct qpnp_wled));
604 if (!wled)
605 return ERR_NO_MEMORY;
606
607 memset(wled, 0, sizeof(struct qpnp_wled));
608
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800609 rc = qpnp_wled_setup(wled, config);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530610 if (rc) {
611 dprintf(CRITICAL, "Setting WLED parameters failed\n");
612 return rc;
613 }
614
615 rc = qpnp_wled_config(wled);
616 if (rc) {
617 dprintf(CRITICAL, "wled config failed\n");
618 return rc;
619 }
620
621 gwled = wled;
622
623 return rc;
624}
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800625
626static int qpnp_labibb_regulator_set_voltage(struct qpnp_wled *wled)
627{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800628 int rc = -1;
629 uint32_t new_uV;
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800630 uint8_t val, mask=0;
631
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530632 if (!wled->disp_type_amoled || !wled->lab_ibb_swire_control) {
633 if (wled->lab_min_volt < wled->lab_init_volt) {
634 dprintf(CRITICAL,"qpnp_lab_regulator_set_voltage failed, min_uV %d is less than init volt %d\n",
635 wled->lab_min_volt, wled->lab_init_volt);
636 return rc;
637 }
638
639 val = (((wled->lab_min_volt - wled->lab_init_volt) +
640 (IBB_LAB_VREG_STEP_SIZE - 1)) / IBB_LAB_VREG_STEP_SIZE);
641 new_uV = val * IBB_LAB_VREG_STEP_SIZE + wled->lab_init_volt;
642
643 if (new_uV > wled->lab_max_volt) {
644 dprintf(CRITICAL,"qpnp_ibb_regulator_set_voltage unable to set voltage (%d %d)\n",
645 wled->lab_min_volt, wled->lab_max_volt);
646 return rc;
647 }
648 val |= QPNP_LAB_OUTPUT_OVERRIDE_EN;
649 mask = pm8x41_wled_reg_read(wled->lab_base +
650 QPNP_LABIBB_OUTPUT_VOLTAGE);
651 mask &= ~(QPNP_LAB_SET_VOLTAGE_MASK
652 | QPNP_LAB_OUTPUT_OVERRIDE_EN);
653 mask |= val & (QPNP_LAB_SET_VOLTAGE_MASK
654 | QPNP_LAB_OUTPUT_OVERRIDE_EN);
655
656 pm8x41_wled_reg_write(wled->lab_base +
657 QPNP_LABIBB_OUTPUT_VOLTAGE, mask);
658 udelay(2);
659
660 /*
661 * IBB Set Voltage.
662 * For AMOLED panels, the IBB voltage needs to be
663 * controlled by panel.
664 */
665 if (wled->ibb_min_volt < wled->ibb_init_volt) {
666 dprintf(CRITICAL, "qpnp_ibb_regulator_set_voltage failed, min_uV %d is less than init volt %d\n",
667 wled->ibb_min_volt, wled->ibb_init_volt);
668 return rc;
669 }
670
671 val = (((wled->ibb_min_volt - wled->ibb_init_volt) +
672 (IBB_LAB_VREG_STEP_SIZE - 1)) / IBB_LAB_VREG_STEP_SIZE);
673 new_uV = val * IBB_LAB_VREG_STEP_SIZE + wled->ibb_init_volt;
674 if (new_uV > wled->ibb_max_volt) {
675 dprintf(CRITICAL, "qpnp_ibb_regulator_set_voltage unable to set voltage %d %d\n",
676 wled->ibb_min_volt, wled->ibb_max_volt);
677 return rc;
678 }
679 val |= QPNP_LAB_OUTPUT_OVERRIDE_EN;
680 mask = pm8x41_wled_reg_read(wled->ibb_base +
681 QPNP_LABIBB_OUTPUT_VOLTAGE);
682 udelay(2);
683 mask &= ~(QPNP_IBB_SET_VOLTAGE_MASK |
684 QPNP_LAB_OUTPUT_OVERRIDE_EN);
685 mask |= (val & (QPNP_IBB_SET_VOLTAGE_MASK |
686 QPNP_LAB_OUTPUT_OVERRIDE_EN));
687
688 pm8x41_wled_reg_write(wled->ibb_base +
689 QPNP_LABIBB_OUTPUT_VOLTAGE, mask);
690 } else {
691 pm8x41_wled_reg_write(wled->ibb_base +
692 QPNP_LABIBB_OUTPUT_VOLTAGE, 0x00);
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800693 }
694
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800695 return 0;
696}