Просмотр исходного кода

asoc: mbhc: add extcon device for MBHC

Add extcon device for MBHC.

Change-Id: I2f22223bc62f4f4b5f2e8cb61cf7e7d41548947f
Signed-off-by: Meng Wang <[email protected]>
Meng Wang 4 лет назад
Родитель
Сommit
ecf424366e
4 измененных файлов с 88 добавлено и 17 удалено
  1. 9 4
      asoc/codecs/wcd-mbhc-adc.c
  2. 12 7
      asoc/codecs/wcd-mbhc-legacy.c
  3. 62 5
      asoc/codecs/wcd-mbhc-v2.c
  4. 5 1
      include/asoc/wcd-mbhc-v2.h

+ 9 - 4
asoc/codecs/wcd-mbhc-adc.c

@@ -1049,16 +1049,21 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
 		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
 		mbhc->btn_press_intr = false;
 		mbhc->is_btn_press = false;
-		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
+		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) {
 			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
-		else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
+			extcon_set_state_sync(mbhc->extdev, EXTCON_JACK_MICROPHONE, 0);
+		} else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
 			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+			extcon_set_state_sync(mbhc->extdev, EXTCON_JACK_HEADPHONE, 0);
+		} else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
 #if IS_ENABLED(CONFIG_AUDIO_QGKI)
-		else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP)
 			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
 #endif /* CONFIG_AUDIO_QGKI */
-		else if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH)
+			extcon_set_state_sync(mbhc->extdev, EXTCON_MECHANICAL, 0);
+		} else if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH) {
 			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_LINEOUT);
+			extcon_set_state_sync(mbhc->extdev, EXTCON_JACK_LINE_OUT, 0);
+		}
 	} else {
 		/*
 		 * ADC COMPLETE and ELEC_REM interrupts are both enabled for

+ 12 - 7
asoc/codecs/wcd-mbhc-legacy.c

@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
  */
 #include <linux/module.h>
 #include <linux/init.h>
@@ -861,23 +861,28 @@ static irqreturn_t wcd_mbhc_hs_rem_irq(int irq, void *data)
 			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
 			mbhc->btn_press_intr = false;
 			mbhc->is_btn_press = false;
-			if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
+			if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) {
 				wcd_mbhc_report_plug(
 					mbhc, 0, SND_JACK_HEADSET);
-			else if (mbhc->current_plug ==
+				extcon_set_state_sync(mbhc->extdev, EXTCON_JACK_MICROPHONE, 0);
+			} else if (mbhc->current_plug ==
 					MBHC_PLUG_TYPE_HEADPHONE)
 				wcd_mbhc_report_plug(
 					mbhc, 0, SND_JACK_HEADPHONE);
-#if IS_ENABLED(CONFIG_AUDIO_QGKI)
-			else if (mbhc->current_plug ==
+				extcon_set_state_sync(mbhc->extdev, EXTCON_JACK_HEADPHONE, 0);
+			} else if (mbhc->current_plug ==
 					MBHC_PLUG_TYPE_GND_MIC_SWAP)
+#if IS_ENABLED(CONFIG_AUDIO_QGKI)
 				wcd_mbhc_report_plug(
 					mbhc, 0, SND_JACK_UNSUPPORTED);
 #endif /* CONFIG_AUDIO_QGKI */
-			else if (mbhc->current_plug ==
-					MBHC_PLUG_TYPE_HIGH_HPH)
+				extcon_set_state_sync(mbhc->extdev, EXTCON_MECHANICAL, 0);
+			} else if (mbhc->current_plug ==
+					MBHC_PLUG_TYPE_HIGH_HPH) {
 				wcd_mbhc_report_plug(
 					mbhc, 0, SND_JACK_LINEOUT);
+				extcon_set_state_sync(mbhc->extdev, EXTCON_JACK_LINE_OUT, 0);
+			}
 		} else {
 			if (!(hphl_sch && mic_sch && hs_comp_result)) {
 				/*

+ 62 - 5
asoc/codecs/wcd-mbhc-v2.c

@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
  */
 #include <linux/module.h>
 #include <linux/init.h>
@@ -27,6 +27,14 @@
 #include "wcd-mbhc-adc.h"
 #include <asoc/wcd-mbhc-v2-api.h>
 
+static const unsigned int mbhc_ext_dev_supported_table[] = {
+	EXTCON_JACK_MICROPHONE,
+	EXTCON_JACK_HEADPHONE,
+	EXTCON_JACK_LINE_OUT,
+	EXTCON_MECHANICAL,
+	EXTCON_NONE,
+};
+
 void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc,
 			  struct snd_soc_jack *jack, int status, int mask)
 {
@@ -560,6 +568,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
 	struct snd_soc_component *component = mbhc->component;
 	bool is_pa_on = false;
 	u8 fsm_en = 0;
+	int extdev_type = 0;
 
 	WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
 
@@ -648,6 +657,16 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
 					 __func__, mbhc->hph_status);
 				wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
 					0, WCD_MBHC_JACK_MASK);
+				if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
+					extdev_type = EXTCON_JACK_HEADPHONE;
+				else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
+					extdev_type = EXTCON_JACK_MICROPHONE;
+				else if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH)
+					extdev_type = EXTCON_JACK_LINE_OUT;
+				else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP)
+					extdev_type = EXTCON_MECHANICAL;
+
+				extcon_set_state_sync(mbhc->extdev, extdev_type, 0);
 			}
 			if (mbhc->hph_status == SND_JACK_LINEOUT) {
 
@@ -788,6 +807,7 @@ void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc)
 
 	pr_debug("%s: Report extension cable\n", __func__);
 	wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+	extcon_set_state_sync(mbhc->extdev, EXTCON_JACK_LINE_OUT, 1);
 	/*
 	 * If PA is enabled HPHL schmitt trigger can
 	 * be unreliable, make sure to disable it
@@ -818,6 +838,7 @@ void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
 {
 	bool anc_mic_found = false;
 	enum snd_jack_types jack_type;
+	int ret = 0;
 
 	if (mbhc->deinit_in_progress) {
 		pr_info("%s: mbhc deinit in progess: ignore report\n", __func__);
@@ -840,14 +861,20 @@ void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
 		 * report a headphone or unsupported
 		 */
 		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+		ret = extcon_set_state_sync(mbhc->extdev, EXTCON_JACK_HEADPHONE, 1);
 	} else if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
-		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
+		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
 			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
-		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
+			ret = extcon_set_state_sync(mbhc->extdev, EXTCON_JACK_HEADPHONE, 0);
+		}
+		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) {
 			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
+			ret = extcon_set_state_sync(mbhc->extdev, EXTCON_JACK_MICROPHONE, 0);
+		}
 #if IS_ENABLED(CONFIG_AUDIO_QGKI)
 		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
 #endif /* CONFIG_AUDIO_QGKI */
+		ret = extcon_set_state_sync(mbhc->extdev, EXTCON_MECHANICAL, 1);
 	} else if (plug_type == MBHC_PLUG_TYPE_HEADSET) {
 		if (mbhc->mbhc_cfg->enable_anc_mic_detect &&
 		    mbhc->mbhc_fn->wcd_mbhc_detect_anc_plug_type)
@@ -860,10 +887,12 @@ void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
 		 * only report the mic line
 		 */
 		wcd_mbhc_report_plug(mbhc, 1, jack_type);
+		ret = extcon_set_state_sync(mbhc->extdev, EXTCON_JACK_MICROPHONE, 1);
 	} else if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
 		if (mbhc->mbhc_cfg->detect_extn_cable) {
 			/* High impedance device found. Report as LINEOUT */
 			wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+			ret = extcon_set_state_sync(mbhc->extdev, EXTCON_JACK_LINE_OUT, 1);
 			pr_debug("%s: setup mic trigger for further detection\n",
 				 __func__);
 
@@ -883,6 +912,7 @@ void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
 					     true);
 		} else {
 			wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+			ret = extcon_set_state_sync(mbhc->extdev, EXTCON_JACK_LINE_OUT, 1);
 		}
 	} else {
 		WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
@@ -930,6 +960,7 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
 	bool micbias1 = false;
 	struct snd_soc_component *component = mbhc->component;
 	enum snd_jack_types jack_type;
+	int extdev_type = 0;
 
 	dev_dbg(component->dev, "%s: enter\n", __func__);
 	WCD_MBHC_RSC_LOCK(mbhc);
@@ -1017,12 +1048,16 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
 		switch (mbhc->current_plug) {
 		case MBHC_PLUG_TYPE_HEADPHONE:
 			jack_type = SND_JACK_HEADPHONE;
+			extdev_type = EXTCON_JACK_HEADPHONE;
 			break;
-#if IS_ENABLED(CONFIG_AUDIO_QGKI)
 		case MBHC_PLUG_TYPE_GND_MIC_SWAP:
+#if IS_ENABLED(CONFIG_AUDIO_QGKI)
 			jack_type = SND_JACK_UNSUPPORTED;
-			break;
+#else
+			jack_type = SND_JACK_HEADPHONE;
 #endif /* CONFIG_AUDIO_QGKI */
+			extdev_type = EXTCON_MECHANICAL;
+			break;
 		case MBHC_PLUG_TYPE_HEADSET:
 			/* make sure to turn off Rbias */
 			if (mbhc->mbhc_cb->micb_internal)
@@ -1031,12 +1066,14 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
 			/* Pulldown micbias */
 			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_PULLDOWN_CTRL, 1);
 			jack_type = SND_JACK_HEADSET;
+			extdev_type = EXTCON_JACK_MICROPHONE;
 			break;
 		case MBHC_PLUG_TYPE_HIGH_HPH:
 			if (mbhc->mbhc_detection_logic == WCD_DETECTION_ADC)
 			    WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_ISRC_EN, 0);
 			mbhc->is_extn_cable = false;
 			jack_type = SND_JACK_LINEOUT;
+			extdev_type = EXTCON_JACK_LINE_OUT;
 			break;
 		default:
 			pr_info("%s: Invalid current plug: %d\n",
@@ -1046,6 +1083,7 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
 #else
 			jack_type = SND_JACK_HEADPHONE;
 #endif /* CONFIG_AUDIO_QGKI */
+			extdev_type = EXTCON_MECHANICAL;
 			break;
 		}
 		wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, false);
@@ -1054,6 +1092,7 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
 		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
 		mbhc->extn_cable_hph_rem = false;
 		wcd_mbhc_report_plug(mbhc, 0, jack_type);
+		extcon_set_state_sync(mbhc->extdev, extdev_type, 0);
 
 		if (mbhc->mbhc_cfg->enable_usbc_analog) {
 			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0);
@@ -2010,11 +2049,26 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_component *component,
 		       mbhc->intr_ids->hph_right_ocp);
 		goto err_hphr_ocp_irq;
 	}
+	if (!mbhc->extdev)
+		mbhc->extdev =
+			devm_extcon_dev_allocate(component->dev,
+				mbhc_ext_dev_supported_table);
+	if (IS_ERR(mbhc->extdev)) {
+		goto err_ext_dev;
+		ret = PTR_ERR(mbhc->extdev);
+	}
+	ret = devm_extcon_dev_register(component->dev, mbhc->extdev);
+	if (ret) {
+		pr_err("%s:audio registration failed\n", __func__);
+		goto err_ext_dev;
+	}
 
 	mbhc->deinit_in_progress = false;
 	pr_debug("%s: leave ret %d\n", __func__, ret);
 	return ret;
 
+err_ext_dev:
+	mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->hph_right_ocp, mbhc);
 err_hphr_ocp_irq:
 	mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->hph_left_ocp, mbhc);
 err_hphl_ocp_irq:
@@ -2046,6 +2100,9 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
 {
 	struct snd_soc_component *component = mbhc->component;
 
+	if (mbhc->extdev)
+		devm_extcon_dev_unregister(component->dev, mbhc->extdev);
+
 	mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->mbhc_sw_intr, mbhc);
 	mbhc->mbhc_cb->free_irq(component, mbhc->intr_ids->mbhc_btn_press_intr,
 				mbhc);

+ 5 - 1
include/asoc/wcd-mbhc-v2.h

@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
  */
 #ifndef __WCD_MBHC_V2_H__
 #define __WCD_MBHC_V2_H__
@@ -7,6 +7,8 @@
 #include <linux/wait.h>
 #include <linux/stringify.h>
 #include <linux/power_supply.h>
+#include <linux/extcon.h>
+#include <linux/extcon-provider.h>
 #include "wcdcal-hwdep.h"
 #include <sound/jack.h>
 
@@ -621,6 +623,8 @@ struct wcd_mbhc {
 	bool force_linein;
 	struct device_node *fsa_np;
 	struct notifier_block fsa_nb;
+
+	struct extcon_dev *extdev;
 };
 
 void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,