dev: qpnp_wled: Add LCDB support for PMI632
Add LCDB support to enable LCD biasing control for PMI632.
Change-Id: I820ac91bb74c1806a48b772a8622c1aec428cdff
Signed-off-by: Umang Agrawal <uagrawal@codeaurora.org>
diff --git a/dev/qpnp_wled/include/qpnp_lcdb.h b/dev/qpnp_wled/include/qpnp_lcdb.h
new file mode 100644
index 0000000..0eef610
--- /dev/null
+++ b/dev/qpnp_wled/include/qpnp_lcdb.h
@@ -0,0 +1,80 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <bits.h>
+#include <debug.h>
+#include <reg.h>
+
+#define QPNP_LCDB_BASE 0x3ec00
+#define QPNP_LCDB_BST_OUTPUT_VOLTAGE_REG 0x41
+#define QPNP_LCDB_LDO_OUTPUT_VOLTAGE_REG 0x71
+#define QPNP_LCDB_NCP_OUTPUT_VOLTAGE_REG 0x81
+#define QPNP_LCDB_ENABLE_CTL_REG 0x46
+#define QPNP_LCDB_PWRUP_PWRDN_CTL_REG 0x66
+#define QPNP_LCDB_MODULE_RDY_REG 0x45
+
+#define QPNP_LCDB_BST_OUTPUT_VOLTAGE_MASK 0x3F
+#define QPNP_LCDB_LDO_OUTPUT_VOLTAGE_MASK 0x1F
+#define QPNP_LCDB_NCP_OUTPUT_VOLTAGE_MASK 0x1F
+#define QPNP_LCDB_ENABLE_CTL_MASK 0x80
+#define QPNP_LCDB_PWRUP_DLY_MASK 0x0C
+#define QPNP_LCDB_PWRDN_DLY_MASK 0x03
+#define QPNP_LCDB_MODULE_RDY_MASK 0x80
+
+#define LCDB_LDO_INIT_VOLTAGE_UV 4000000
+#define LCDB_NCP_INIT_VOLTAGE_UV 4000000
+#define LCDB_BOOST_INIT_VOLTAGE_UV 4700000
+#define LDO_VREG_STEP_SIZE_UV 50000
+#define NCP_VREG_STEP_SIZE_UV 50000
+#define BST_VREG_STEP_SIZE_UV 25000
+#define LCDB_BOOST_HEADROOM_VOLT_UV 150000
+#define QPNP_LCDB_PWRUP_DLY_MAX_MS 3
+#define QPNP_LCDB_PWRDN_DLY_MAX_MS 3
+#define LCDB_MODULE_RDY BIT(7)
+
+#define LCDB_ENABLE_SHIFT 7
+#define LCDB_PWRUP_DLY_SHIFT 2
+
+struct qpnp_lcdb {
+ uint32_t lcdb_base;
+ uint16_t lcdb_pwrup_dly_ms;
+ uint16_t lcdb_pwrdn_dly_ms;
+ uint32_t ldo_min_volt;
+ uint32_t ldo_max_volt;
+ uint32_t ldo_init_volt;
+ uint32_t ncp_min_volt;
+ uint32_t ncp_max_volt;
+ uint32_t ncp_init_volt;
+ uint32_t bst_init_volt;
+};
+
+/* LCDB Init */
+int qpnp_lcdb_init(struct qpnp_wled_config_data *config);
+
+/* LCDB Enable */
+int qpnp_lcdb_enable(bool enable);
diff --git a/dev/qpnp_wled/qpnp_lcdb.c b/dev/qpnp_wled/qpnp_lcdb.c
new file mode 100644
index 0000000..f2eba5b
--- /dev/null
+++ b/dev/qpnp_wled/qpnp_lcdb.c
@@ -0,0 +1,216 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <err.h>
+#include <debug.h>
+#include <spmi.h>
+#include <qpnp_wled.h>
+#include <qpnp_lcdb.h>
+#include <qtimer.h>
+#include <target.h>
+
+struct qpnp_lcdb *lcdb;
+
+int qpnp_lcdb_enable(bool state)
+{
+ if (!lcdb) {
+ dprintf(CRITICAL, "%s: lcdb is not initialized yet\n", __func__);
+ return ERROR;
+ }
+
+ pmic_spmi_reg_mask_write(lcdb->lcdb_base + QPNP_LCDB_ENABLE_CTL_REG,
+ QPNP_LCDB_ENABLE_CTL_MASK, (state << LCDB_ENABLE_SHIFT));
+
+ return 0;
+}
+
+static int qpnp_lcdb_set_voltage(struct qpnp_lcdb *lcdb)
+{
+ int rc = -1;
+ uint32_t new_uV, boost_ref_volt;
+ uint8_t val;
+
+ /* Set LDO Voltage */
+ if (lcdb->ldo_min_volt < lcdb->ldo_init_volt) {
+ dprintf(CRITICAL, "qpnp_ldo_set_voltage failed, min_uV %d is "
+ "less than the minimum supported voltage %d\n",
+ lcdb->ldo_min_volt, lcdb->ldo_init_volt);
+ return rc;
+ }
+
+ val = ((lcdb->ldo_min_volt - lcdb->ldo_init_volt) +
+ LDO_VREG_STEP_SIZE_UV - 1) / LDO_VREG_STEP_SIZE_UV;
+ new_uV = val * LDO_VREG_STEP_SIZE_UV + lcdb->ldo_init_volt;
+ if (new_uV > lcdb->ldo_max_volt) {
+ dprintf(CRITICAL, "qpnp_ldo_set_voltage unable to set voltage "
+ "(%d %d)\n", lcdb->ldo_min_volt, lcdb->ldo_max_volt);
+ return rc;
+ }
+
+ /* Value adjustment as initial 1V has 100mV step and rest 50mV step */
+ if ( val > 18 )
+ val = val - 9;
+ else
+ val = val / 2;
+
+ pmic_spmi_reg_mask_write(lcdb->lcdb_base +
+ QPNP_LCDB_LDO_OUTPUT_VOLTAGE_REG,
+ QPNP_LCDB_LDO_OUTPUT_VOLTAGE_MASK, val);
+
+ udelay(2);
+
+ /* Set NCP voltage */
+ if (lcdb->ncp_min_volt < lcdb->ncp_init_volt) {
+ dprintf(CRITICAL, "qpnp_ncp_set_voltage failed, min_uV %d is "
+ "less than the minimum supported voltage %d\n",
+ lcdb->ncp_min_volt, lcdb->ncp_init_volt);
+ return rc;
+ }
+
+ val = ((lcdb->ncp_min_volt - lcdb->ncp_init_volt) +
+ NCP_VREG_STEP_SIZE_UV - 1) / NCP_VREG_STEP_SIZE_UV;
+ new_uV = val * NCP_VREG_STEP_SIZE_UV + lcdb->ncp_init_volt;
+ if (new_uV > lcdb->ncp_max_volt) {
+ dprintf(CRITICAL, "qpnp_ncp_set_voltage unable to set voltage "
+ "(%d %d)\n", lcdb->ncp_min_volt, lcdb->ncp_max_volt);
+ return rc;
+ }
+
+ /* Value adjustment as initial 1V has 100mV step and rest 50mV step */
+ if ( val > 18 )
+ val = val - 9;
+ else
+ val = val / 2;
+
+ pmic_spmi_reg_mask_write(lcdb->lcdb_base +
+ QPNP_LCDB_NCP_OUTPUT_VOLTAGE_REG,
+ QPNP_LCDB_NCP_OUTPUT_VOLTAGE_MASK, val);
+
+ udelay(2);
+
+ /* Set Boost voltage */
+ boost_ref_volt = ((lcdb->ldo_max_volt > lcdb->ncp_max_volt) ?
+ lcdb->ldo_max_volt : lcdb->ncp_max_volt) +
+ LCDB_BOOST_HEADROOM_VOLT_UV;
+ if (boost_ref_volt < lcdb->bst_init_volt)
+ boost_ref_volt = lcdb->bst_init_volt;
+
+ val = ((boost_ref_volt - lcdb->bst_init_volt) +
+ BST_VREG_STEP_SIZE_UV - 1) / BST_VREG_STEP_SIZE_UV;
+ new_uV = val * BST_VREG_STEP_SIZE_UV + lcdb->bst_init_volt;
+
+ pmic_spmi_reg_mask_write(lcdb->lcdb_base +
+ QPNP_LCDB_BST_OUTPUT_VOLTAGE_REG,
+ QPNP_LCDB_BST_OUTPUT_VOLTAGE_MASK, val);
+
+ return 0;
+}
+
+static int qpnp_lcdb_config(struct qpnp_lcdb *lcdb)
+{
+ int rc = 0;
+ uint8_t reg = 0;
+
+ /* TODO: Set power up & down delay register */
+ if (lcdb->lcdb_pwrup_dly_ms > QPNP_LCDB_PWRUP_DLY_MAX_MS)
+ lcdb->lcdb_pwrup_dly_ms = QPNP_LCDB_PWRUP_DLY_MAX_MS;
+
+ if (lcdb->lcdb_pwrdn_dly_ms > QPNP_LCDB_PWRDN_DLY_MAX_MS)
+ lcdb->lcdb_pwrdn_dly_ms = QPNP_LCDB_PWRDN_DLY_MAX_MS;
+
+ reg = pmic_spmi_reg_read(lcdb->lcdb_base +
+ QPNP_LCDB_PWRUP_PWRDN_CTL_REG);
+
+ /* Set power up delay */
+ reg &= QPNP_LCDB_PWRUP_DLY_MASK;
+ reg |= (lcdb->lcdb_pwrup_dly_ms << LCDB_PWRUP_DLY_SHIFT);
+
+ /* Set power down delay */
+ reg &= QPNP_LCDB_PWRDN_DLY_MASK;
+ reg |= (lcdb->lcdb_pwrdn_dly_ms);
+
+ pmic_spmi_reg_write(lcdb->lcdb_base + QPNP_LCDB_PWRUP_PWRDN_CTL_REG,
+ reg);
+
+ /* Make LCDB module ready */
+ pmic_spmi_reg_mask_write(lcdb->lcdb_base + QPNP_LCDB_MODULE_RDY_REG,
+ QPNP_LCDB_MODULE_RDY_MASK, LCDB_MODULE_RDY);
+
+ /* Set regulator voltage */
+ rc = qpnp_lcdb_set_voltage(lcdb);
+
+ return rc;
+}
+
+static int qpnp_lcdb_setup(struct qpnp_lcdb *lcdb,
+ struct qpnp_wled_config_data *config)
+{
+ lcdb->lcdb_base = QPNP_LCDB_BASE;
+ lcdb->lcdb_pwrup_dly_ms = config->pwr_up_delay;
+ lcdb->lcdb_pwrdn_dly_ms = config->pwr_down_delay;
+ lcdb->ldo_min_volt = config->lab_min_volt;
+ lcdb->ldo_max_volt = config->lab_max_volt;
+ lcdb->ldo_init_volt = LCDB_LDO_INIT_VOLTAGE_UV;
+ lcdb->ncp_min_volt = config->ibb_min_volt;
+ lcdb->ncp_max_volt = config->ibb_max_volt;
+ lcdb->ncp_init_volt = LCDB_NCP_INIT_VOLTAGE_UV;
+ lcdb->bst_init_volt = LCDB_BOOST_INIT_VOLTAGE_UV;
+
+ return 0;
+}
+
+int qpnp_lcdb_init(struct qpnp_wled_config_data *config)
+{
+ int rc;
+
+ if (!target_is_pmi_enabled())
+ return ERR_NOT_FOUND;
+
+ lcdb = malloc(sizeof(struct qpnp_lcdb));
+ if (!lcdb)
+ return ERR_NO_MEMORY;
+
+ memset(lcdb, 0, sizeof(struct qpnp_lcdb));
+
+ rc = qpnp_lcdb_setup(lcdb, config);
+ if(rc) {
+ dprintf(CRITICAL, "Setting LCDB parameters failed\n");
+ return rc;
+ }
+
+ rc = qpnp_lcdb_config(lcdb);
+ if (rc) {
+ dprintf(CRITICAL, "lcdb config failed\n");
+ return rc;
+ }
+
+ return rc;
+}
diff --git a/dev/qpnp_wled/rules.mk b/dev/qpnp_wled/rules.mk
index d60c07f..464d497 100644
--- a/dev/qpnp_wled/rules.mk
+++ b/dev/qpnp_wled/rules.mk
@@ -3,4 +3,5 @@
INCLUDES += -I$(LOCAL_DIR)/include
OBJS += \
- $(LOCAL_DIR)/qpnp_wled.o
+ $(LOCAL_DIR)/qpnp_wled.o \
+ $(LOCAL_DIR)/qpnp_lcdb.o