blob: 9282a7bdc5492544199a3096108af01390020c85 [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);
238
239 rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
240 if (rc)
241 return rc;
242 pm8x41_wled_reg_write(QPNP_WLED_TEST4(wled->ctrl_base), 0x13);
243 }
244
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530245 /* Configure the FEEDBACK OUTPUT register */
246 reg = pm8x41_wled_reg_read(
247 QPNP_WLED_FDBK_OP_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530248 reg &= QPNP_WLED_FDBK_OP_MASK;
249 reg |= wled->fdbk_op;
250 pm8x41_wled_reg_write(QPNP_WLED_FDBK_OP_REG(wled->ctrl_base), reg);
251
252 /* Configure the VREF register */
253 if (wled->vref_mv < QPNP_WLED_VREF_MIN_MV)
254 wled->vref_mv = QPNP_WLED_VREF_MIN_MV;
255 else if (wled->vref_mv > QPNP_WLED_VREF_MAX_MV)
256 wled->vref_mv = QPNP_WLED_VREF_MAX_MV;
257
258 reg = pm8x41_wled_reg_read(
259 QPNP_WLED_VREF_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530260 reg &= QPNP_WLED_VREF_MASK;
261 temp = wled->vref_mv - QPNP_WLED_VREF_MIN_MV;
262 reg |= (temp / QPNP_WLED_VREF_STEP_MV);
263 pm8x41_wled_reg_write(QPNP_WLED_VREF_REG(wled->ctrl_base), reg);
264
265 /* Configure the ILIM register */
266 if (wled->ilim_ma < QPNP_WLED_ILIM_MIN_MA)
267 wled->ilim_ma = QPNP_WLED_ILIM_MIN_MA;
268 else if (wled->ilim_ma > QPNP_WLED_ILIM_MAX_MA)
269 wled->ilim_ma = QPNP_WLED_ILIM_MAX_MA;
270
271 reg = pm8x41_wled_reg_read(
272 QPNP_WLED_ILIM_REG(wled->ctrl_base));
Alex Sarraf9bd81b92015-05-20 15:59:32 -0700273 temp = (wled->ilim_ma / QPNP_WLED_ILIM_STEP_MA);
274 if (temp != (reg & ~QPNP_WLED_ILIM_MASK)) {
275 reg &= QPNP_WLED_ILIM_MASK;
276 reg |= temp;
277 reg |= QPNP_WLED_ILIM_OVERWRITE;
278 pm8x41_wled_reg_write(QPNP_WLED_ILIM_REG(wled->ctrl_base), reg);
279 }
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530280
281 /* Configure the MAX BOOST DUTY register */
282 if (wled->boost_duty_ns < QPNP_WLED_BOOST_DUTY_MIN_NS)
283 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
284 else if (wled->boost_duty_ns > QPNP_WLED_BOOST_DUTY_MAX_NS)
285 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MAX_NS;
286
287 reg = pm8x41_wled_reg_read(
288 QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530289 reg &= QPNP_WLED_BOOST_DUTY_MASK;
290 reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS);
291 pm8x41_wled_reg_write(QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base), reg);
292
293 /* Configure the SWITCHING FREQ register */
294 if (wled->switch_freq_khz == QPNP_WLED_SWITCH_FREQ_1600_KHZ)
295 temp = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
296 else
297 temp = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
298
299 reg = pm8x41_wled_reg_read(
300 QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530301 reg &= QPNP_WLED_SWITCH_FREQ_MASK;
302 reg |= temp;
303 pm8x41_wled_reg_write(QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), reg);
304
305 /* Configure the OVP register */
306 if (wled->ovp_mv <= QPNP_WLED_OVP_17800_MV) {
307 wled->ovp_mv = QPNP_WLED_OVP_17800_MV;
308 temp = 3;
309 } else if (wled->ovp_mv <= QPNP_WLED_OVP_19400_MV) {
310 wled->ovp_mv = QPNP_WLED_OVP_19400_MV;
311 temp = 2;
312 } else if (wled->ovp_mv <= QPNP_WLED_OVP_29500_MV) {
313 wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
314 temp = 1;
315 } else {
316 wled->ovp_mv = QPNP_WLED_OVP_31000_MV;
317 temp = 0;
318 }
319
320 reg = pm8x41_wled_reg_read(
321 QPNP_WLED_OVP_REG(wled->ctrl_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530322 reg &= QPNP_WLED_OVP_MASK;
323 reg |= temp;
324 pm8x41_wled_reg_write(QPNP_WLED_OVP_REG(wled->ctrl_base), reg);
325
Parth Dixite46b1a22016-06-06 19:16:20 +0530326 if (wled->disp_type_amoled) {
327 for (i = 0; i < NUM_SUPPORTED_AVDD_VOLTAGES; i++) {
328 if (QPNP_WLED_AVDD_DEFAULT_VOLTAGE_MV == qpnp_wled_avdd_target_voltages[i])
329 break;
330 }
331 if (i == NUM_SUPPORTED_AVDD_VOLTAGES)
332 {
333 dprintf(CRITICAL, "Invalid avdd target voltage specified \n");
334 return ERR_NOT_VALID;
335 }
336 /* Update WLED_OVP register based on desired target voltage */
337 reg = qpnp_wled_ovp_reg_settings[i];
338 pm8x41_wled_reg_write(QPNP_WLED_OVP_REG(wled->ctrl_base), reg);
339 /* Update WLED_TRIM register based on desired target voltage */
340 reg = pm8x41_wled_reg_read(
341 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base));
342 reg += qpnp_wled_avdd_trim_adjustments[i];
343 if ((int8_t)reg < QPNP_WLED_AVDD_MIN_TRIM_VALUE)
344 reg = QPNP_WLED_AVDD_MIN_TRIM_VALUE;
345 else if((int8_t)reg > QPNP_WLED_AVDD_MAX_TRIM_VALUE)
346 reg = QPNP_WLED_AVDD_MAX_TRIM_VALUE;
347
348 rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
349 if (rc)
350 return rc;
351
352 temp = pm8x41_wled_reg_read(
353 QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base));
354 temp &= ~QPNP_WLED_7P7_TRIM_MASK;
355 temp |= (reg & QPNP_WLED_7P7_TRIM_MASK);
356 pm8x41_wled_reg_write(QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), temp);
357 /* Write to spare to avoid reconfiguration in HLOS */
358 reg = pm8x41_wled_reg_read(
359 QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base));
360 reg |= QPNP_WLED_AVDD_SET_BIT;
361 pm8x41_wled_reg_write(QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base), reg);
362 }
363
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530364 /* Configure the MODULATION register */
365 if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_1200_KHZ) {
366 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_1200_KHZ;
367 temp = 3;
368 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_2400_KHZ) {
369 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_2400_KHZ;
370 temp = 2;
371 } else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_9600_KHZ) {
372 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
373 temp = 1;
374 } else {
375 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
376 temp = 0;
377 }
378 reg = pm8x41_wled_reg_read(QPNP_WLED_MOD_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530379
380 reg &= QPNP_WLED_MOD_FREQ_MASK;
381 reg |= (temp << QPNP_WLED_MOD_FREQ_SHIFT);
382
383 reg &= QPNP_WLED_PHASE_STAG_MASK;
384 reg |= (wled->en_phase_stag << QPNP_WLED_PHASE_STAG_SHIFT);
385
386 reg &= QPNP_WLED_DIM_RES_MASK;
387 reg |= (wled->en_9b_dim_res << QPNP_WLED_DIM_RES_SHIFT);
388
389 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
390 reg &= QPNP_WLED_DIM_HYB_MASK;
391 reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
392 } else {
393 reg &= QPNP_WLED_DIM_HYB_MASK;
394 reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
395 reg &= QPNP_WLED_DIM_ANA_MASK;
396 reg |= wled->dim_mode;
397 }
398
399 pm8x41_wled_reg_write(QPNP_WLED_MOD_REG(wled->sink_base), reg);
400
401 /* Configure the HYBRID THRESHOLD register */
402 if (wled->hyb_thres < QPNP_WLED_HYB_THRES_MIN)
403 wled->hyb_thres = QPNP_WLED_HYB_THRES_MIN;
404 else if (wled->hyb_thres > QPNP_WLED_HYB_THRES_MAX)
405 wled->hyb_thres = QPNP_WLED_HYB_THRES_MAX;
406
407 reg = pm8x41_wled_reg_read(
408 QPNP_WLED_HYB_THRES_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530409
410 reg &= QPNP_WLED_HYB_THRES_MASK;
411 temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1;
412 reg |= temp;
413 pm8x41_wled_reg_write(QPNP_WLED_HYB_THRES_REG(wled->sink_base), reg);
414
415 for (i = 0; i < wled->num_strings; i++) {
416 if (wled->strings[i] >= QPNP_WLED_MAX_STRINGS) {
417 dprintf(CRITICAL,"Invalid string number\n");
418 return ERR_NOT_VALID;
419 }
420
421 /* MODULATOR */
422 reg = pm8x41_wled_reg_read(
423 QPNP_WLED_MOD_EN_REG(wled->sink_base,
424 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530425 reg &= QPNP_WLED_MOD_EN_MASK;
426 reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
427 pm8x41_wled_reg_write(QPNP_WLED_MOD_EN_REG(wled->sink_base,
428 wled->strings[i]), reg);
429
430 /* SYNC DELAY */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800431 if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530432 wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US;
433
434 reg = pm8x41_wled_reg_read(
435 QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
436 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530437 reg &= QPNP_WLED_SYNC_DLY_MASK;
438 temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
439 reg |= temp;
440 pm8x41_wled_reg_write(QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
441 wled->strings[i]), reg);
442
443 /* FULL SCALE CURRENT */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800444 if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530445 wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
446
447 reg = pm8x41_wled_reg_read(
448 QPNP_WLED_FS_CURR_REG(wled->sink_base,
449 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530450 reg &= QPNP_WLED_FS_CURR_MASK;
451 temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
452 reg |= temp;
453 pm8x41_wled_reg_write(QPNP_WLED_FS_CURR_REG(wled->sink_base,
454 wled->strings[i]), reg);
455
456 /* CABC */
457 reg = pm8x41_wled_reg_read(
458 QPNP_WLED_CABC_REG(wled->sink_base,
459 wled->strings[i]));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530460 reg &= QPNP_WLED_CABC_MASK;
461 reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT);
462 pm8x41_wled_reg_write(QPNP_WLED_CABC_REG(wled->sink_base,
463 wled->strings[i]), reg);
464
465 /* Enable CURRENT SINK */
466 reg = pm8x41_wled_reg_read(
467 QPNP_WLED_CURR_SINK_REG(wled->sink_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530468 temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
469 reg |= (1 << temp);
470 pm8x41_wled_reg_write(QPNP_WLED_CURR_SINK_REG(wled->sink_base), reg);
471 }
472
473 /* LAB fast precharge */
474 reg = pm8x41_wled_reg_read(
475 QPNP_WLED_LAB_FAST_PC_REG(wled->lab_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530476 reg &= QPNP_WLED_LAB_FAST_PC_MASK;
477 reg |= (wled->lab_fast_precharge << QPNP_WLED_LAB_FAST_PC_SHIFT);
wufeng.jiangb1008482016-06-14 10:41:39 +0800478 /* LAB max precharge time */
479 reg &= QPNP_WLED_PRECHARGE_MASK;
480 reg |= (wled->lab_max_precharge_time);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530481 pm8x41_wled_reg_write(QPNP_WLED_LAB_FAST_PC_REG(wled->lab_base), reg);
482
483 /* Configure lab display type */
484 rc = qpnp_wled_set_display_type(wled, wled->lab_base);
485 if (rc < 0)
486 return rc;
487
488 /* make LAB module ready */
489 rc = qpnp_wled_module_ready(wled, wled->lab_base, true);
490 if (rc < 0)
491 return rc;
492
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530493 /* Disable LAB pulse skipping for AMOLED */
494 if (wled->disp_type_amoled)
495 pm8x41_wled_reg_write(wled->lab_base +
496 QPNP_LABIBB_PS_CTL, 0x00);
497
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530498 /* IBB active bias */
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800499 if (wled->ibb_pwrup_dly_ms > QPNP_WLED_IBB_PWRUP_DLY_MAX_MS)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530500 wled->ibb_pwrup_dly_ms = QPNP_WLED_IBB_PWRUP_DLY_MAX_MS;
501
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800502 if (wled->ibb_pwrdn_dly_ms > QPNP_WLED_IBB_PWRDN_DLY_MAX_MS)
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800503 wled->ibb_pwrdn_dly_ms = QPNP_WLED_IBB_PWRDN_DLY_MAX_MS;
504
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530505 reg = pm8x41_wled_reg_read(
506 QPNP_WLED_IBB_BIAS_REG(wled->ibb_base));
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530507
508 reg &= QPNP_WLED_IBB_BIAS_MASK;
509 reg |= (!wled->ibb_bias_active << QPNP_WLED_IBB_BIAS_SHIFT);
510
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800511 temp = wled->ibb_pwrup_dly_ms;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530512 reg &= QPNP_WLED_IBB_PWRUP_DLY_MASK;
513 reg |= (temp << QPNP_WLED_IBB_PWRUP_DLY_SHIFT);
Channagoud Kadabif5f0ded2015-02-25 13:42:18 -0800514 /* Power down delay bits could already be set, clear them before
515 * or'ing new values
516 */
517 reg &= ~(PWRDN_DLY2_MASK);
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800518 reg |= wled->ibb_pwrdn_dly_ms;
519 reg |= (wled->ibb_discharge_en << 2);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530520
521 rc = qpnp_wled_sec_access(wled, wled->ibb_base);
522 if (rc)
523 return rc;
524
525 pm8x41_wled_reg_write(QPNP_WLED_IBB_BIAS_REG(wled->ibb_base), reg);
526
527 /* Configure ibb display type */
528 rc = qpnp_wled_set_display_type(wled, wled->ibb_base);
529 if (rc < 0)
530 return rc;
531
532 /* make IBB module ready */
533 rc = qpnp_wled_module_ready(wled, wled->ibb_base, true);
534 if (rc < 0)
535 return rc;
536
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800537 rc = qpnp_labibb_regulator_set_voltage(wled);
538 if (rc < 0)
539 return rc;
540
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530541 return 0;
542}
543
544/* Setup wled default parameters */
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800545static int qpnp_wled_setup(struct qpnp_wled *wled, struct qpnp_wled_config_data *config)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530546{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800547 int i;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530548
549 wled->sink_base = QPNP_WLED_SINK_BASE;
550 wled->ctrl_base = QPNP_WLED_CTRL_BASE;
551 wled->ibb_base = QPNP_WLED_IBB_BASE;
552 wled->lab_base = QPNP_WLED_LAB_BASE;
553 wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
554 wled->vref_mv = QPNP_WLED_DFLT_VREF_MV;
555 wled->switch_freq_khz = QPNP_WLED_SWITCH_FREQ_800_KHZ;
556 wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
557 wled->ilim_ma = QPNP_WLED_DFLT_ILIM_MA;
558 wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
559 wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
560 wled->dim_mode = QPNP_WLED_DIM_HYBRID;
561 wled->dim_shape = QPNP_WLED_DIM_SHAPE_LINEAR;
562
563 if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
564 wled->hyb_thres = QPNP_WLED_DFLT_HYB_THRES;
565 }
566
567 wled->sync_dly_us = 800;
568 wled->fs_curr_ua = 16000;
569 wled->en_9b_dim_res = 0;
570 wled->en_phase_stag = true;
571 wled->en_cabc = 0;
572
573 wled->num_strings = QPNP_WLED_MAX_STRINGS;
574 for (i = 0; i < wled->num_strings; i++)
575 wled->strings[i] = i;
576
577 wled->ibb_bias_active = false;
Wufeng7bae3c92016-04-27 13:02:15 +0800578 wled->lab_fast_precharge = false;
wufeng.jiangb1008482016-06-14 10:41:39 +0800579 wled->lab_max_precharge_time = QPNP_WLED_PRECHARGE_US500;
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800580 wled->ibb_pwrup_dly_ms = config->pwr_up_delay;
581 wled->ibb_pwrdn_dly_ms = config->pwr_down_delay;
582 wled->ibb_discharge_en = config->ibb_discharge_en;
583 wled->disp_type_amoled = config->display_type;
584 wled->lab_min_volt = config->lab_min_volt;
585 wled->lab_max_volt = config->lab_max_volt;
586 wled->ibb_min_volt = config->ibb_min_volt;
587 wled->ibb_max_volt = config->ibb_max_volt;
588 wled->ibb_init_volt = config->ibb_init_volt;
589 wled->lab_init_volt = config->lab_init_volt;
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530590 wled->lab_ibb_swire_control = config->lab_ibb_swire_control;
591 wled->wled_avdd_control = config->wled_avdd_control;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530592
593 return 0;
594}
595
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800596int qpnp_wled_init(struct qpnp_wled_config_data *config)
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530597{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800598 int rc;
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530599 struct qpnp_wled *wled;
600
601 wled = malloc(sizeof(struct qpnp_wled));
602 if (!wled)
603 return ERR_NO_MEMORY;
604
605 memset(wled, 0, sizeof(struct qpnp_wled));
606
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800607 rc = qpnp_wled_setup(wled, config);
Aparna Mallavarapu083766b2014-07-21 21:04:48 +0530608 if (rc) {
609 dprintf(CRITICAL, "Setting WLED parameters failed\n");
610 return rc;
611 }
612
613 rc = qpnp_wled_config(wled);
614 if (rc) {
615 dprintf(CRITICAL, "wled config failed\n");
616 return rc;
617 }
618
619 gwled = wled;
620
621 return rc;
622}
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800623
624static int qpnp_labibb_regulator_set_voltage(struct qpnp_wled *wled)
625{
Veera Sundaram Sankaran4e23fab2014-12-09 13:54:32 -0800626 int rc = -1;
627 uint32_t new_uV;
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800628 uint8_t val, mask=0;
629
Vishnuvardhan Prodduturiaf59b912015-10-20 21:14:27 +0530630 if (!wled->disp_type_amoled || !wled->lab_ibb_swire_control) {
631 if (wled->lab_min_volt < wled->lab_init_volt) {
632 dprintf(CRITICAL,"qpnp_lab_regulator_set_voltage failed, min_uV %d is less than init volt %d\n",
633 wled->lab_min_volt, wled->lab_init_volt);
634 return rc;
635 }
636
637 val = (((wled->lab_min_volt - wled->lab_init_volt) +
638 (IBB_LAB_VREG_STEP_SIZE - 1)) / IBB_LAB_VREG_STEP_SIZE);
639 new_uV = val * IBB_LAB_VREG_STEP_SIZE + wled->lab_init_volt;
640
641 if (new_uV > wled->lab_max_volt) {
642 dprintf(CRITICAL,"qpnp_ibb_regulator_set_voltage unable to set voltage (%d %d)\n",
643 wled->lab_min_volt, wled->lab_max_volt);
644 return rc;
645 }
646 val |= QPNP_LAB_OUTPUT_OVERRIDE_EN;
647 mask = pm8x41_wled_reg_read(wled->lab_base +
648 QPNP_LABIBB_OUTPUT_VOLTAGE);
649 mask &= ~(QPNP_LAB_SET_VOLTAGE_MASK
650 | QPNP_LAB_OUTPUT_OVERRIDE_EN);
651 mask |= val & (QPNP_LAB_SET_VOLTAGE_MASK
652 | QPNP_LAB_OUTPUT_OVERRIDE_EN);
653
654 pm8x41_wled_reg_write(wled->lab_base +
655 QPNP_LABIBB_OUTPUT_VOLTAGE, mask);
656 udelay(2);
657
658 /*
659 * IBB Set Voltage.
660 * For AMOLED panels, the IBB voltage needs to be
661 * controlled by panel.
662 */
663 if (wled->ibb_min_volt < wled->ibb_init_volt) {
664 dprintf(CRITICAL, "qpnp_ibb_regulator_set_voltage failed, min_uV %d is less than init volt %d\n",
665 wled->ibb_min_volt, wled->ibb_init_volt);
666 return rc;
667 }
668
669 val = (((wled->ibb_min_volt - wled->ibb_init_volt) +
670 (IBB_LAB_VREG_STEP_SIZE - 1)) / IBB_LAB_VREG_STEP_SIZE);
671 new_uV = val * IBB_LAB_VREG_STEP_SIZE + wled->ibb_init_volt;
672 if (new_uV > wled->ibb_max_volt) {
673 dprintf(CRITICAL, "qpnp_ibb_regulator_set_voltage unable to set voltage %d %d\n",
674 wled->ibb_min_volt, wled->ibb_max_volt);
675 return rc;
676 }
677 val |= QPNP_LAB_OUTPUT_OVERRIDE_EN;
678 mask = pm8x41_wled_reg_read(wled->ibb_base +
679 QPNP_LABIBB_OUTPUT_VOLTAGE);
680 udelay(2);
681 mask &= ~(QPNP_IBB_SET_VOLTAGE_MASK |
682 QPNP_LAB_OUTPUT_OVERRIDE_EN);
683 mask |= (val & (QPNP_IBB_SET_VOLTAGE_MASK |
684 QPNP_LAB_OUTPUT_OVERRIDE_EN));
685
686 pm8x41_wled_reg_write(wled->ibb_base +
687 QPNP_LABIBB_OUTPUT_VOLTAGE, mask);
688 } else {
689 pm8x41_wled_reg_write(wled->ibb_base +
690 QPNP_LABIBB_OUTPUT_VOLTAGE, 0x00);
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800691 }
692
Aparna Mallavarapua42de902014-12-08 17:25:44 -0800693 return 0;
694}