regulator: qpnp-lcdb: Add a DT property for LDO power-down delay

Add "qcom,pwrdn-delay-ms" device tree property to meet the power
down delay spec for few display panels. Also, enable force-pull
down on module disable to do a quick discharge of the LDO.

Change-Id: I07d55cdbfe3b253579ca3722fc02604a4a7483e0
Signed-off-by: Kiran Gunda <kgunda@codeaurora.org>
diff --git a/drivers/regulator/qpnp-lcdb-regulator.c b/drivers/regulator/qpnp-lcdb-regulator.c
index 0fd7802..744e35a 100644
--- a/drivers/regulator/qpnp-lcdb-regulator.c
+++ b/drivers/regulator/qpnp-lcdb-regulator.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -114,6 +114,10 @@
 #define PFM_CURRENT_SHIFT		2
 
 #define LCDB_PWRUP_PWRDN_CTL_REG	0x66
+#define PWRUP_DELAY_MASK		GENAMSK(3, 2)
+#define PWRDN_DELAY_MASK		GENMASK(1, 0)
+#define PWRDN_DELAY_MIN_MS		0
+#define PWRDN_DELAY_MAX_MS		8
 
 /* LDO */
 #define LCDB_LDO_OUTPUT_VOLTAGE_REG	0x71
@@ -126,6 +130,10 @@
 #define LDO_DIS_PULLDOWN_BIT		BIT(1)
 #define LDO_PD_STRENGTH_BIT		BIT(0)
 
+#define LCDB_LDO_FORCE_PD_CTL_REG	0x79
+#define LDO_FORCE_PD_EN_BIT		BIT(0)
+#define LDO_FORCE_PD_MODE		BIT(7)
+
 #define LCDB_LDO_ILIM_CTL1_REG		0x7B
 #define EN_LDO_ILIM_BIT			BIT(7)
 #define SET_LDO_ILIM_MASK		GENMASK(2, 0)
@@ -223,6 +231,7 @@
 	u32				base;
 	u32				wa_flags;
 	int				sc_irq;
+	int				pwrdn_delay_ms;
 
 	/* TTW params */
 	bool				ttw_enable;
@@ -297,6 +306,7 @@
 
 enum lcdb_wa_flags {
 	NCP_SCP_DISABLE_WA = BIT(0),
+	FORCE_PD_ENABLE_WA = BIT(1),
 };
 
 static u32 soft_start_us[] = {
@@ -320,6 +330,13 @@
 	810,
 };
 
+static const u32 pwrup_pwrdn_ms[] = {
+	0,
+	1,
+	4,
+	8,
+};
+
 #define SETTING(_id, _sec_access, _valid)	\
 	[_id] = {				\
 		.address = _id##_REG,		\
@@ -922,6 +939,18 @@
 		return 0;
 	}
 
+	if (lcdb->wa_flags & FORCE_PD_ENABLE_WA) {
+		/*
+		 * force pull-down to enable quick discharge after
+		 * turning off
+		 */
+		val = LDO_FORCE_PD_EN_BIT | LDO_FORCE_PD_MODE;
+		rc = qpnp_lcdb_write(lcdb, lcdb->base +
+				     LCDB_LDO_FORCE_PD_CTL_REG, &val, 1);
+		if (rc < 0)
+			return rc;
+	}
+
 	val = 0;
 	rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
 							&val, 1);
@@ -930,6 +959,17 @@
 	else
 		lcdb->lcdb_enabled = false;
 
+	if (lcdb->wa_flags & FORCE_PD_ENABLE_WA) {
+		/* wait for 10 msec after module disable for LDO to discharge */
+		usleep_range(10000, 11000);
+
+		val = 0;
+		rc = qpnp_lcdb_write(lcdb, lcdb->base +
+				     LCDB_LDO_FORCE_PD_CTL_REG, &val, 1);
+		if (rc < 0)
+			return rc;
+	}
+
 	return rc;
 }
 
@@ -2052,6 +2092,10 @@
 		if (lcdb->pmic_rev_id->rev4 < PM660L_V2P0_REV4)
 			lcdb->wa_flags |= NCP_SCP_DISABLE_WA;
 		break;
+	case PMI632_SUBTYPE:
+	case PM855L_SUBTYPE:
+		lcdb->wa_flags |= FORCE_PD_ENABLE_WA;
+		break;
 	default:
 		break;
 	}
@@ -2066,6 +2110,15 @@
 
 	qpnp_lcdb_pmic_config(lcdb);
 
+	if (lcdb->pwrdn_delay_ms != -EINVAL) {
+		rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+					    LCDB_PWRUP_PWRDN_CTL_REG,
+					    PWRDN_DELAY_MASK,
+					    lcdb->pwrdn_delay_ms);
+		if (rc < 0)
+			return rc;
+	}
+
 	rc = qpnp_lcdb_init_bst(lcdb);
 	if (rc < 0) {
 		pr_err("Failed to initialize BOOST rc=%d\n", rc);
@@ -2124,7 +2177,8 @@
 
 static int qpnp_lcdb_parse_dt(struct qpnp_lcdb *lcdb)
 {
-	int rc = 0;
+	int rc = 0, i = 0;
+	u32 tmp;
 	const char *label;
 	struct device_node *revid_dev_node, *temp, *node = lcdb->dev->of_node;
 
@@ -2189,7 +2243,24 @@
 	lcdb->voltage_step_ramp =
 			of_property_read_bool(node, "qcom,voltage-step-ramp");
 
-	return rc;
+	lcdb->pwrdn_delay_ms = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,pwrdn-delay-ms", &tmp);
+	if (!rc) {
+		if (!is_between(tmp, PWRDN_DELAY_MIN_MS, PWRDN_DELAY_MAX_MS)) {
+			pr_err("Invalid PWRDN_DLY val %d (min=%d max=%d)\n",
+				tmp, PWRDN_DELAY_MIN_MS, PWRDN_DELAY_MAX_MS);
+			return -EINVAL;
+		}
+
+		for (i = 0; i < ARRAY_SIZE(pwrup_pwrdn_ms); i++) {
+			if (tmp == pwrup_pwrdn_ms[i]) {
+				lcdb->pwrdn_delay_ms = i;
+				break;
+			}
+		}
+	}
+
+	return 0;
 }
 
 static ssize_t qpnp_lcdb_irq_control(struct class *c,