Browse Source

asoc: wcd-mbhc: add support for usbc analog audio on msmnile

Add support for usbc analog audio for msmnile platform
by adding fsa driver calls in mbhc driver to make
any switching orientation changes of gnd/mic.

Change-Id: Iab7511907325a24345734402c10a4cf5f8ebfa23
Signed-off-by: Phani Kumar Uppalapati <[email protected]>
Phani Kumar Uppalapati 7 years ago
parent
commit
8fe02478b7
2 changed files with 49 additions and 268 deletions
  1. 47 252
      asoc/codecs/wcd-mbhc-v2.c
  2. 2 16
      asoc/codecs/wcd-mbhc-v2.h

+ 47 - 252
asoc/codecs/wcd-mbhc-v2.c

@@ -25,6 +25,7 @@
 #include <linux/input.h>
 #include <linux/firmware.h>
 #include <linux/completion.h>
+#include <linux/soc/qcom/fsa4480-i2c.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
 #include "msm-cdc-pinctrl.h"
@@ -958,6 +959,9 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
 		mbhc->extn_cable_hph_rem = false;
 		wcd_mbhc_report_plug(mbhc, 0, jack_type);
 
+		if (mbhc->mbhc_cfg->enable_usbc_analog)
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0);
+
 	} else if (!detection_type) {
 		/* Disable external voltage source to micbias if present */
 		if (mbhc->mbhc_cb->enable_mb_source)
@@ -1291,8 +1295,8 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
 	 * by an external source
 	 */
 	if (mbhc->mbhc_cfg->enable_usbc_analog) {
-		mbhc->hphl_swh = 1;
-		mbhc->gnd_swh = 1;
+		mbhc->hphl_swh = 0;
+		mbhc->gnd_swh = 0;
 
 		if (mbhc->mbhc_cb->hph_pull_up_control_v2)
 			mbhc->mbhc_cb->hph_pull_up_control_v2(codec,
@@ -1310,7 +1314,16 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
 	if (mbhc->mbhc_cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl)
 		mbhc->mbhc_cb->mbhc_gnd_det_ctrl(codec, true);
 	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1);
-	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1);
+
+	/*
+	 * Disable L_DET for USB-C analog audio to avoid spurious interrupts
+	 * when a non-audio accessory is inserted. L_DET_EN sets to 1 when FSA
+	 * I2C driver notifies that ANALOG_AUDIO_ADAPTER is inserted
+	 */
+	if (mbhc->mbhc_cfg->enable_usbc_analog)
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0);
+	else
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1);
 
 	if (mbhc->mbhc_cfg->enable_usbc_analog) {
 		/* Insertion debounce set to 48ms */
@@ -1481,207 +1494,26 @@ static int wcd_mbhc_set_keycode(struct wcd_mbhc *mbhc)
 	return result;
 }
 
-static int wcd_mbhc_usb_c_analog_setup_gpios(struct wcd_mbhc *mbhc,
-					     bool active)
+static int wcd_mbhc_usbc_ana_event_handler(struct notifier_block *nb,
+					   unsigned long mode, void *ptr)
 {
-	int rc = 0;
-	struct usbc_ana_audio_config *config =
-		&mbhc->mbhc_cfg->usbc_analog_cfg;
-	union power_supply_propval pval;
-
-	dev_dbg(mbhc->codec->dev, "%s: setting GPIOs active = %d\n",
-		__func__, active);
+	struct wcd_mbhc *mbhc = container_of(nb, struct wcd_mbhc, fsa_nb);
 
-	memset(&pval, 0, sizeof(pval));
-
-	if (active) {
-		pval.intval = POWER_SUPPLY_TYPEC_PR_SOURCE;
-		if (power_supply_set_property(mbhc->usb_psy,
-				POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval))
-			dev_info(mbhc->codec->dev, "%s: force PR_SOURCE mode unsuccessful\n",
-				 __func__);
-		else
-			mbhc->usbc_force_pr_mode = true;
-
-		if (config->usbc_en1_gpio_p)
-			rc = msm_cdc_pinctrl_select_active_state(
-				config->usbc_en1_gpio_p);
-		if (rc == 0 && config->usbc_force_gpio_p)
-			rc = msm_cdc_pinctrl_select_active_state(
-				config->usbc_force_gpio_p);
-		mbhc->usbc_mode = POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER;
-	} else {
-		/* no delay is required when disabling GPIOs */
-		if (config->usbc_en1_gpio_p)
-			msm_cdc_pinctrl_select_sleep_state(
-				config->usbc_en1_gpio_p);
-		if (config->usbc_force_gpio_p)
-			msm_cdc_pinctrl_select_sleep_state(
-				config->usbc_force_gpio_p);
-
-		if (mbhc->usbc_force_pr_mode) {
-			pval.intval = POWER_SUPPLY_TYPEC_PR_DUAL;
-			if (power_supply_set_property(mbhc->usb_psy,
-				POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval))
-				dev_info(mbhc->codec->dev, "%s: force PR_DUAL mode unsuccessful\n",
-					 __func__);
-
-			mbhc->usbc_force_pr_mode = false;
-		}
-
-		mbhc->usbc_mode = POWER_SUPPLY_TYPEC_NONE;
-		if (mbhc->mbhc_cfg->swap_gnd_mic)
-			mbhc->mbhc_cfg->swap_gnd_mic(mbhc->codec, false);
-	}
-
-	return rc;
-}
-
-/* workqueue */
-static void wcd_mbhc_usbc_analog_work_fn(struct work_struct *work)
-{
-	struct wcd_mbhc *mbhc =
-		container_of(work, struct wcd_mbhc, usbc_analog_work);
-
-	wcd_mbhc_usb_c_analog_setup_gpios(mbhc,
-			mbhc->usbc_mode != POWER_SUPPLY_TYPEC_NONE);
-}
-
-/* this callback function is used to process PMI notification */
-static int wcd_mbhc_usb_c_event_changed(struct notifier_block *nb,
-					unsigned long evt, void *ptr)
-{
-	int ret;
-	union power_supply_propval mode;
-	struct wcd_mbhc *mbhc = container_of(nb, struct wcd_mbhc, psy_nb);
-	struct snd_soc_codec *codec = mbhc->codec;
-
-	if (ptr != mbhc->usb_psy || evt != PSY_EVENT_PROP_CHANGED)
-		return 0;
-
-	ret = power_supply_get_property(mbhc->usb_psy,
-			POWER_SUPPLY_PROP_TYPEC_MODE, &mode);
-	if (ret) {
-		dev_err(codec->dev, "%s: Unable to read USB TYPEC_MODE: %d\n",
-			__func__, ret);
-		return ret;
-	}
-
-	dev_dbg(codec->dev, "%s: USB change event received\n",
-		__func__);
-	dev_dbg(codec->dev, "%s: supply mode %d, expected %d\n", __func__,
-		mode.intval, POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER);
-
-	switch (mode.intval) {
-	case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER:
-	case POWER_SUPPLY_TYPEC_NONE:
-		dev_dbg(codec->dev, "%s: usbc_mode: %d; mode.intval: %d\n",
-			__func__, mbhc->usbc_mode, mode.intval);
-
-		if (mbhc->usbc_mode == mode.intval)
-			break; /* filter notifications received before */
-		mbhc->usbc_mode = mode.intval;
-
-		dev_dbg(codec->dev, "%s: queueing usbc_analog_work\n",
-			__func__);
-		schedule_work(&mbhc->usbc_analog_work);
-		break;
-	default:
-		break;
-	}
-	return ret;
-}
-
-/* PMI registration code */
-static int wcd_mbhc_usb_c_analog_init(struct wcd_mbhc *mbhc)
-{
-	int ret = 0;
-	struct snd_soc_codec *codec = mbhc->codec;
+	if (!mbhc)
+		return -EINVAL;
 
-	dev_dbg(mbhc->codec->dev, "%s: usb-c analog setup start\n", __func__);
-	INIT_WORK(&mbhc->usbc_analog_work, wcd_mbhc_usbc_analog_work_fn);
+	dev_dbg(mbhc->codec->dev, "%s: mode = %lu\n", __func__, mode);
 
-	mbhc->usb_psy = power_supply_get_by_name("usb");
-	if (IS_ERR_OR_NULL(mbhc->usb_psy)) {
-		dev_err(codec->dev, "%s: could not get USB psy info\n",
-			__func__);
-		ret = -EPROBE_DEFER;
-		if (IS_ERR(mbhc->usb_psy))
-			ret = PTR_ERR(mbhc->usb_psy);
-		mbhc->usb_psy = NULL;
-		goto err;
+	if (mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) {
+		/* insertion detected, enable L_DET_EN */
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1);
 	}
-
-	ret = wcd_mbhc_usb_c_analog_setup_gpios(mbhc, false);
-	if (ret) {
-		dev_err(codec->dev, "%s: error while setting USBC ana gpios\n",
-			__func__);
-		goto err;
-	}
-
-	mbhc->psy_nb.notifier_call = wcd_mbhc_usb_c_event_changed;
-	mbhc->psy_nb.priority = 0;
-	ret = power_supply_reg_notifier(&mbhc->psy_nb);
-	if (ret) {
-		dev_err(codec->dev, "%s: power supply registration failed\n",
-			__func__);
-		goto err;
-	}
-
-	/*
-	 * as part of the init sequence check if there is a connected
-	 * USB C analog adapter
-	 */
-	dev_dbg(mbhc->codec->dev, "%s: verify if USB adapter is already inserted\n",
-		__func__);
-	ret = wcd_mbhc_usb_c_event_changed(&mbhc->psy_nb,
-					   PSY_EVENT_PROP_CHANGED,
-					   mbhc->usb_psy);
-
-err:
-	return ret;
-}
-
-static int wcd_mbhc_usb_c_analog_deinit(struct wcd_mbhc *mbhc)
-{
-	wcd_mbhc_usb_c_analog_setup_gpios(mbhc, false);
-
-	/* deregister from PMI */
-	power_supply_unreg_notifier(&mbhc->psy_nb);
-
 	return 0;
 }
 
-static int wcd_mbhc_init_gpio(struct wcd_mbhc *mbhc,
-			      struct wcd_mbhc_config *mbhc_cfg,
-			      const char *gpio_dt_str,
-			      int *gpio, struct device_node **gpio_dn)
-{
-	int rc = 0;
-	struct snd_soc_codec *codec = mbhc->codec;
-	struct snd_soc_card *card = codec->component.card;
-
-	dev_dbg(mbhc->codec->dev, "%s: gpio %s\n", __func__, gpio_dt_str);
-
-	*gpio_dn = of_parse_phandle(card->dev->of_node, gpio_dt_str, 0);
-
-	if (!(*gpio_dn)) {
-		*gpio = of_get_named_gpio(card->dev->of_node, gpio_dt_str, 0);
-		if (!gpio_is_valid(*gpio)) {
-			dev_err(card->dev, "%s, property %s not in node %s",
-				__func__, gpio_dt_str,
-				card->dev->of_node->full_name);
-			rc = -EINVAL;
-		}
-	}
-
-	return rc;
-}
-
 int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg)
 {
 	int rc = 0;
-	struct usbc_ana_audio_config *config;
 	struct snd_soc_codec *codec;
 	struct snd_soc_card *card;
 	const char *usb_c_dt = "qcom,msm-mbhc-usbc-audio-supported";
@@ -1689,7 +1521,6 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg)
 	if (!mbhc || !mbhc_cfg)
 		return -EINVAL;
 
-	config = &mbhc_cfg->usbc_analog_cfg;
 	codec = mbhc->codec;
 	card = codec->component.card;
 
@@ -1705,42 +1536,24 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg)
 				&mbhc_cfg->enable_usbc_analog);
 	}
 	if (mbhc_cfg->enable_usbc_analog == 0 || rc != 0) {
-		dev_info(card->dev,
+		dev_dbg(card->dev,
 				"%s: %s in dt node is missing or false\n",
 				__func__, usb_c_dt);
-		dev_info(card->dev,
+		dev_dbg(card->dev,
 			"%s: skipping USB c analog configuration\n", __func__);
 	}
 
-	/* initialize GPIOs */
+	/* Parse fsa switch handle */
 	if (mbhc_cfg->enable_usbc_analog) {
 		dev_dbg(mbhc->codec->dev, "%s: usbc analog enabled\n",
 				__func__);
 		mbhc->swap_thr = GND_MIC_USBC_SWAP_THRESHOLD;
-		rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg,
-				"qcom,usbc-analog-en1-gpio",
-				&config->usbc_en1_gpio,
-				&config->usbc_en1_gpio_p);
-		if (rc)
-			goto err;
-
-		if (of_find_property(card->dev->of_node,
-				     "qcom,usbc-analog-force_detect_gpio",
-				     NULL)) {
-			rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg,
-					"qcom,usbc-analog-force_detect_gpio",
-					&config->usbc_force_gpio,
-					&config->usbc_force_gpio_p);
-			if (rc)
-				goto err;
-		}
-
-		dev_dbg(mbhc->codec->dev, "%s: calling usb_c_analog_init\n",
-			__func__);
-		/* init PMI notifier */
-		rc = wcd_mbhc_usb_c_analog_init(mbhc);
-		if (rc) {
-			rc = EPROBE_DEFER;
+		mbhc->fsa_np = of_parse_phandle(card->dev->of_node,
+				"fsa4480-i2c-handle", 0);
+		if (!mbhc->fsa_np) {
+			dev_err(card->dev, "%s: fsa4480 i2c node not found\n",
+				__func__);
+			rc = -EINVAL;
 			goto err;
 		}
 	}
@@ -1753,6 +1566,11 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg)
 	    (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) ||
 	    (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) {
 		rc = wcd_mbhc_initialise(mbhc);
+		if (rc) {
+			dev_err(card->dev, "%s: wcd mbhc initialize failed\n",
+				__func__);
+			goto err;
+		}
 	} else {
 		if (!mbhc->mbhc_fw || !mbhc->mbhc_cal)
 			schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
@@ -1762,24 +1580,14 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg)
 				 __func__, mbhc->mbhc_fw, mbhc->mbhc_cal);
 	}
 
+	if (mbhc_cfg->enable_usbc_analog) {
+		mbhc->fsa_nb.notifier_call = wcd_mbhc_usbc_ana_event_handler;
+		mbhc->fsa_nb.priority = 0;
+		rc = fsa4480_reg_notifier(&mbhc->fsa_nb, mbhc->fsa_np);
+	}
+
 	return rc;
 err:
-	if (config->usbc_en1_gpio > 0) {
-		dev_dbg(card->dev, "%s free usb en1 gpio %d\n",
-			__func__, config->usbc_en1_gpio);
-		gpio_free(config->usbc_en1_gpio);
-		config->usbc_en1_gpio = 0;
-	}
-	if (config->usbc_force_gpio > 0) {
-		dev_dbg(card->dev, "%s free usb_force gpio %d\n",
-			__func__, config->usbc_force_gpio);
-		gpio_free(config->usbc_force_gpio);
-		config->usbc_force_gpio = 0;
-	}
-	if (config->usbc_en1_gpio_p)
-		of_node_put(config->usbc_en1_gpio_p);
-	if (config->usbc_force_gpio_p)
-		of_node_put(config->usbc_force_gpio_p);
 	dev_dbg(mbhc->codec->dev, "%s: leave %d\n", __func__, rc);
 	return rc;
 }
@@ -1787,8 +1595,6 @@ EXPORT_SYMBOL(wcd_mbhc_start);
 
 void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
 {
-	struct usbc_ana_audio_config *config = &mbhc->mbhc_cfg->usbc_analog_cfg;
-
 	pr_debug("%s: enter\n", __func__);
 
 	if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE) {
@@ -1813,19 +1619,8 @@ void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
 		mbhc->mbhc_cal = NULL;
 	}
 
-	if (mbhc->mbhc_cfg->enable_usbc_analog) {
-		wcd_mbhc_usb_c_analog_deinit(mbhc);
-		/* free GPIOs */
-		if (config->usbc_en1_gpio > 0)
-			gpio_free(config->usbc_en1_gpio);
-		if (config->usbc_force_gpio)
-			gpio_free(config->usbc_force_gpio);
-
-		if (config->usbc_en1_gpio_p)
-			of_node_put(config->usbc_en1_gpio_p);
-		if (config->usbc_force_gpio_p)
-			of_node_put(config->usbc_force_gpio_p);
-	}
+	if (mbhc->mbhc_cfg->enable_usbc_analog)
+		fsa4480_unreg_notifier(&mbhc->fsa_nb, mbhc->fsa_np);
 
 	pr_debug("%s: leave\n", __func__);
 }

+ 2 - 16
asoc/codecs/wcd-mbhc-v2.h

@@ -422,15 +422,6 @@ enum mbhc_moisture_rref {
 	R_184_KOHM,
 };
 
-struct usbc_ana_audio_config {
-	int usbc_en1_gpio;
-	int usbc_en2_gpio;
-	int usbc_force_gpio;
-	struct device_node *usbc_en1_gpio_p; /* used by pinctrl API */
-	struct device_node *usbc_en2_gpio_p; /* used by pinctrl API */
-	struct device_node *usbc_force_gpio_p; /* used by pinctrl API */
-};
-
 struct wcd_mbhc_config {
 	bool read_fw_bin;
 	void *calibration;
@@ -446,7 +437,6 @@ struct wcd_mbhc_config {
 	int anc_micbias;
 	bool enable_anc_mic_detect;
 	u32 enable_usbc_analog;
-	struct usbc_ana_audio_config usbc_analog_cfg;
 };
 
 struct wcd_mbhc_intr {
@@ -599,14 +589,10 @@ struct wcd_mbhc {
 	unsigned long intr_status;
 	bool is_hph_ocp_pending;
 
-	bool usbc_force_pr_mode;
-	int usbc_mode;
-	struct notifier_block psy_nb;
-	struct power_supply *usb_psy;
-	struct work_struct usbc_analog_work;
-
 	struct wcd_mbhc_fn *mbhc_fn;
 	bool force_linein;
+	struct device_node *fsa_np;
+	struct notifier_block fsa_nb;
 };
 
 void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,