input: misc: qti-haptics: Configure haptics for TWM mode

Configure haptics for TWM entry. Identify TWM entry via
the TWM notifier and configure haptics in the shutdown
callback if TWM entry is requested. Haptics is configured
for external-pin control in TWM mode.

Add a DT property "qcom,haptics-ext-pin-twm" to enable
this feature.

Change-Id: Id1d1335dd03b597e7c05b52f1cb6c0b1e5f6b4e3
Signed-off-by: Umang Chheda <uchheda@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/input/qti-haptics.txt b/Documentation/devicetree/bindings/input/qti-haptics.txt
index b3daa49..0f6cb20 100644
--- a/Documentation/devicetree/bindings/input/qti-haptics.txt
+++ b/Documentation/devicetree/bindings/input/qti-haptics.txt
@@ -86,6 +86,12 @@
 		haptics module through VDD_HAP pin. This is only needed if VDD_HAP
 		is supplied from an external boost regulator instead of VPH_PWR.
 
+- qcom,haptics-ext-pin-twm
+  Usage:      optional
+  Value type: <empty>
+  Definition: A boolean property which configures haptics into external-pin
+		control during TWM entry.
+
 Following properties are specific only when LRA actuator is used:
 
 - qcom,lra-resonance-sig-shape
diff --git a/drivers/input/misc/qti-haptics.c b/drivers/input/misc/qti-haptics.c
index 459faf9..27ce7cf 100644
--- a/drivers/input/misc/qti-haptics.c
+++ b/drivers/input/misc/qti-haptics.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2018, 2020, 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
@@ -24,6 +24,7 @@
 #include <linux/of_address.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
+#include <linux/qpnp/qpnp-misc.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
@@ -173,6 +174,7 @@
 #define HAP_PLAY_BIT			BIT(7)
 
 #define REG_HAP_SEC_ACCESS		0xD0
+#define REG_HAP_PERPH_RESET_CTL3	0xDA
 
 struct qti_hap_effect {
 	int			id;
@@ -222,6 +224,7 @@
 	struct hrtimer			stop_timer;
 	struct hrtimer			hap_disable_timer;
 	struct dentry			*hap_debugfs;
+	struct notifier_block		twm_nb;
 	spinlock_t			bus_lock;
 	ktime_t				last_sc_time;
 	int				play_irq;
@@ -232,6 +235,20 @@
 	bool				perm_disable;
 	bool				play_irq_en;
 	bool				vdd_enabled;
+	bool				twm_state;
+	bool				haptics_ext_pin_twm;
+};
+
+struct hap_addr_val {
+	u16 addr;
+	u8  value;
+};
+
+static struct hap_addr_val twm_ext_cfg[] = {
+	{REG_HAP_PLAY, 0x00}, /* Stop playing haptics waveform */
+	{REG_HAP_PERPH_RESET_CTL3, 0x0D}, /* Disable SHUTDOWN1_RB reset */
+	{REG_HAP_SEL, 0x01}, /* Configure for external-pin mode */
+	{REG_HAP_EN_CTL1, 0x80}, /* Enable haptics driver */
 };
 
 static int wf_repeat[8] = {1, 2, 4, 8, 16, 32, 64, 128};
@@ -1036,6 +1053,24 @@
 	qti_haptics_config_vmax(chip, play->vmax_mv);
 }
 
+static int qti_haptics_twm_config(struct qti_hap_chip *chip)
+{
+	int rc, i;
+
+	for (i = 0; i < ARRAY_SIZE(twm_ext_cfg); i++) {
+		rc = qti_haptics_write(chip, twm_ext_cfg[i].addr,
+					&twm_ext_cfg[i].value, 1);
+		if (rc < 0) {
+			dev_err(chip->dev, "Haptics TWM config failed, rc=%d\n",
+				rc);
+			return rc;
+		}
+	}
+	pr_debug("Enabled haptics for TWM mode\n");
+
+	return 0;
+}
+
 static int qti_haptics_hw_init(struct qti_hap_chip *chip)
 {
 	struct qti_hap_config *config = &chip->config;
@@ -1183,6 +1218,21 @@
 	effect->brake_en = (val != 0);
 }
 
+static int twm_notifier_cb(struct notifier_block *nb,
+			unsigned long action, void *data)
+{
+	struct qti_hap_chip *chip = container_of(nb,
+				struct qti_hap_chip, twm_nb);
+
+	if (action != PMIC_TWM_CLEAR &&
+			action != PMIC_TWM_ENABLE)
+		pr_debug("Unsupported option %lu\n", action);
+	else
+		chip->twm_state = (u8)action;
+
+	return NOTIFY_OK;
+}
+
 static int qti_haptics_parse_dt(struct qti_hap_chip *chip)
 {
 	struct qti_hap_config *config = &chip->config;
@@ -1243,6 +1293,9 @@
 		config->play_rate_us = (tmp >= HAP_PLAY_RATE_US_MAX) ?
 			HAP_PLAY_RATE_US_MAX : tmp;
 
+	chip->haptics_ext_pin_twm = of_property_read_bool(node,
+					"qcom,haptics-ext-pin-twm");
+
 	if (of_find_property(node, "qcom,external-waveform-source", NULL)) {
 		if (!of_property_read_string(node,
 				"qcom,external-waveform-source", &str)) {
@@ -1898,6 +1951,11 @@
 		return rc;
 	}
 
+	chip->twm_nb.notifier_call = twm_notifier_cb;
+	rc = qpnp_misc_twm_notifier_register(&chip->twm_nb);
+	if (rc < 0)
+		pr_err("Failed to register twm_notifier_cb rc=%d\n", rc);
+
 	hrtimer_init(&chip->stop_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
 	chip->stop_timer.function = qti_hap_stop_timer;
 	hrtimer_init(&chip->hap_disable_timer, CLOCK_MONOTONIC,
@@ -1948,6 +2006,7 @@
 
 destroy_ff:
 	input_ff_destroy(chip->input_dev);
+	qpnp_misc_twm_notifier_unregister(&chip->twm_nb);
 	return rc;
 }
 
@@ -1959,6 +2018,7 @@
 	debugfs_remove_recursive(chip->hap_debugfs);
 #endif
 	input_ff_destroy(chip->input_dev);
+	qpnp_misc_twm_notifier_unregister(&chip->twm_nb);
 	dev_set_drvdata(chip->dev, NULL);
 
 	return 0;
@@ -1982,6 +2042,12 @@
 		}
 		chip->vdd_enabled = false;
 	}
+
+	if (chip->twm_state == PMIC_TWM_ENABLE && chip->haptics_ext_pin_twm) {
+		rc = qti_haptics_twm_config(chip);
+		if (rc < 0)
+			pr_err("Haptics TWM config failed rc=%d\n", rc);
+	}
 }
 
 static const struct of_device_id haptics_match_table[] = {