All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/6] ASoC: codecs: msm8916-wcd-analog: Add support to MBHC
@ 2017-08-02 17:09 ` srinivas.kandagatla
  0 siblings, 0 replies; 22+ messages in thread
From: srinivas.kandagatla @ 2017-08-02 17:09 UTC (permalink / raw)
  To: Mark Brown, Banajit Goswami, alsa-devel
  Cc: damien.riegel, Jaroslav Kysela, Takashi Iwai, Patrick Lai,
	linux-kernel, Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patchset adds support to MBHC(Multibutton headset control) block in PM8916
analog block. MBHC support comes from2 blocks first mechanical headset detection
and second headset type, 5 button detection.

This patchset adds support to:
1> Support to NC and NO type of headset Jacks.
2> Mechanical insertion and detection of headset jack.
3> Detect a 3 pole Headphone and a 4 pole Headset.
4> Detect 5 buttons.

Damien sent a similar patchset to add support to mechanical detection,
but that patch has issues and will not work on most usecases (for example
after a playback/capture session, multicodec case). So I only picked up
the BIT mask patch from that series.

Tested it on DB410c with Audio Mezz board with 4 pole and 3 pole
and during playback and recording use cases.

Changes since v1(https://www.spinics.net/lists/kernel/msg2565463.html):
	- Micbias left to hardware default value suggested by Mark.
	- Updated commit log to reflect low power state for mbhc
	- cleaned up code spotted by Mark.
	- Moved the button voltage threshold to dt, suggested by Mark
	- Fixed key release issue reported by Damien

Srinivas Kandagatla (6):
  ASoC: jack: fix snd_soc_codec_set_jack return error
  ASoC: codecs: msm8916-wcd-analog: move codec reset to probe
  ASoC: codecs: msm8916-wcd-analog: get micbias voltage from dt
  ASoC: codecs: msm8916-wcd-analog: add MBHC support
  ASoC: qcom: apq8016-sbc: Add support to Headset JACK
  arm64: dts: apq8016-sbc: add mbhc buttons support

 .../bindings/sound/qcom,msm8916-wcd-analog.txt     |  18 +-
 arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi          |   2 +
 sound/soc/codecs/msm8916-wcd-analog.c              | 412 +++++++++++++++++++--
 sound/soc/qcom/apq8016_sbc.c                       |  34 ++
 sound/soc/soc-jack.c                               |   2 +-
 5 files changed, 444 insertions(+), 24 deletions(-)

-- 
2.9.3

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 0/6] ASoC: codecs: msm8916-wcd-analog: Add support to MBHC
@ 2017-08-02 17:09 ` srinivas.kandagatla
  0 siblings, 0 replies; 22+ messages in thread
From: srinivas.kandagatla @ 2017-08-02 17:09 UTC (permalink / raw)
  To: Mark Brown, Banajit Goswami, alsa-devel
  Cc: damien.riegel, Patrick Lai, linux-kernel, Takashi Iwai,
	Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patchset adds support to MBHC(Multibutton headset control) block in PM8916
analog block. MBHC support comes from2 blocks first mechanical headset detection
and second headset type, 5 button detection.

This patchset adds support to:
1> Support to NC and NO type of headset Jacks.
2> Mechanical insertion and detection of headset jack.
3> Detect a 3 pole Headphone and a 4 pole Headset.
4> Detect 5 buttons.

Damien sent a similar patchset to add support to mechanical detection,
but that patch has issues and will not work on most usecases (for example
after a playback/capture session, multicodec case). So I only picked up
the BIT mask patch from that series.

Tested it on DB410c with Audio Mezz board with 4 pole and 3 pole
and during playback and recording use cases.

Changes since v1(https://www.spinics.net/lists/kernel/msg2565463.html):
	- Micbias left to hardware default value suggested by Mark.
	- Updated commit log to reflect low power state for mbhc
	- cleaned up code spotted by Mark.
	- Moved the button voltage threshold to dt, suggested by Mark
	- Fixed key release issue reported by Damien

Srinivas Kandagatla (6):
  ASoC: jack: fix snd_soc_codec_set_jack return error
  ASoC: codecs: msm8916-wcd-analog: move codec reset to probe
  ASoC: codecs: msm8916-wcd-analog: get micbias voltage from dt
  ASoC: codecs: msm8916-wcd-analog: add MBHC support
  ASoC: qcom: apq8016-sbc: Add support to Headset JACK
  arm64: dts: apq8016-sbc: add mbhc buttons support

 .../bindings/sound/qcom,msm8916-wcd-analog.txt     |  18 +-
 arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi          |   2 +
 sound/soc/codecs/msm8916-wcd-analog.c              | 412 +++++++++++++++++++--
 sound/soc/qcom/apq8016_sbc.c                       |  34 ++
 sound/soc/soc-jack.c                               |   2 +-
 5 files changed, 444 insertions(+), 24 deletions(-)

-- 
2.9.3

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v2 1/6] ASoC: jack: fix snd_soc_codec_set_jack return error
  2017-08-02 17:09 ` srinivas.kandagatla
@ 2017-08-02 17:09   ` srinivas.kandagatla
  -1 siblings, 0 replies; 22+ messages in thread
From: srinivas.kandagatla @ 2017-08-02 17:09 UTC (permalink / raw)
  To: Mark Brown, Banajit Goswami, alsa-devel
  Cc: damien.riegel, Jaroslav Kysela, Takashi Iwai, Patrick Lai,
	linux-kernel, Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch changes the error code returned by snd_soc_codec_set_jack()
from -EINVAL to -ENOTSUPP. The reason to do this is to make the caller
aware that the underlying codec does not support this callback. This can
make the caller write the code to handle this case properly.
Other reason is that -EINVAL is not the correct error to return in this
case anyway.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/soc-jack.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index 7daf21f..42ca9f1 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -36,7 +36,7 @@ int snd_soc_codec_set_jack(struct snd_soc_codec *codec,
 	if (codec->driver->set_jack)
 		return codec->driver->set_jack(codec, jack, data);
 	else
-		return -EINVAL;
+		return -ENOTSUPP;
 }
 EXPORT_SYMBOL_GPL(snd_soc_codec_set_jack);
 
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 1/6] ASoC: jack: fix snd_soc_codec_set_jack return error
@ 2017-08-02 17:09   ` srinivas.kandagatla
  0 siblings, 0 replies; 22+ messages in thread
From: srinivas.kandagatla @ 2017-08-02 17:09 UTC (permalink / raw)
  To: Mark Brown, Banajit Goswami, alsa-devel
  Cc: damien.riegel, Patrick Lai, linux-kernel, Takashi Iwai,
	Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch changes the error code returned by snd_soc_codec_set_jack()
from -EINVAL to -ENOTSUPP. The reason to do this is to make the caller
aware that the underlying codec does not support this callback. This can
make the caller write the code to handle this case properly.
Other reason is that -EINVAL is not the correct error to return in this
case anyway.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/soc-jack.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index 7daf21f..42ca9f1 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -36,7 +36,7 @@ int snd_soc_codec_set_jack(struct snd_soc_codec *codec,
 	if (codec->driver->set_jack)
 		return codec->driver->set_jack(codec, jack, data);
 	else
-		return -EINVAL;
+		return -ENOTSUPP;
 }
 EXPORT_SYMBOL_GPL(snd_soc_codec_set_jack);
 
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 2/6] ASoC: codecs: msm8916-wcd-analog: move codec reset to probe
  2017-08-02 17:09 ` srinivas.kandagatla
@ 2017-08-02 17:09   ` srinivas.kandagatla
  -1 siblings, 0 replies; 22+ messages in thread
From: srinivas.kandagatla @ 2017-08-02 17:09 UTC (permalink / raw)
  To: Mark Brown, Banajit Goswami, alsa-devel
  Cc: damien.riegel, Jaroslav Kysela, Takashi Iwai, Patrick Lai,
	linux-kernel, Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch move the codec reset code from dai ops to codec probe, so
that the codec is not held in reset when headset detection block is
still active.

Without this patch the codec block will be in reset as long as its not
actively used, which means headset events will not be functional if the
codec dai is not actively used. Point to note is that the headset
detection blocks will work in low power when there is no active audio
usecase and switch to micbias source when audio usecase is active.

Existing dapms should put the codec in low power state anyway when there
is no audio usecase.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/codecs/msm8916-wcd-analog.c | 30 ++++++------------------------
 1 file changed, 6 insertions(+), 24 deletions(-)

diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
index 5710fd4..6606954 100644
--- a/sound/soc/codecs/msm8916-wcd-analog.c
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -536,6 +536,9 @@ static int pm8916_wcd_analog_probe(struct snd_soc_codec *codec)
 		snd_soc_write(codec, wcd_reg_defaults_2_0[reg].reg,
 			      wcd_reg_defaults_2_0[reg].def);
 
+	snd_soc_update_bits(codec, CDC_D_CDC_RST_CTL,
+			    RST_CTL_DIG_SW_RST_N_MASK,
+			    RST_CTL_DIG_SW_RST_N_REMOVE_RESET);
 	return 0;
 }
 
@@ -543,6 +546,9 @@ static int pm8916_wcd_analog_remove(struct snd_soc_codec *codec)
 {
 	struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(codec->dev);
 
+	snd_soc_update_bits(codec, CDC_D_CDC_RST_CTL,
+			    RST_CTL_DIG_SW_RST_N_MASK, 0);
+
 	return regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
 				      priv->supplies);
 }
@@ -736,28 +742,6 @@ static struct regmap *pm8916_get_regmap(struct device *dev)
 	return dev_get_regmap(dev->parent, NULL);
 }
 
-static int pm8916_wcd_analog_startup(struct snd_pcm_substream *substream,
-				      struct snd_soc_dai *dai)
-{
-	snd_soc_update_bits(dai->codec, CDC_D_CDC_RST_CTL,
-			    RST_CTL_DIG_SW_RST_N_MASK,
-			    RST_CTL_DIG_SW_RST_N_REMOVE_RESET);
-
-	return 0;
-}
-
-static void pm8916_wcd_analog_shutdown(struct snd_pcm_substream *substream,
-					 struct snd_soc_dai *dai)
-{
-	snd_soc_update_bits(dai->codec, CDC_D_CDC_RST_CTL,
-			    RST_CTL_DIG_SW_RST_N_MASK, 0);
-}
-
-static struct snd_soc_dai_ops pm8916_wcd_analog_dai_ops = {
-	.startup = pm8916_wcd_analog_startup,
-	.shutdown = pm8916_wcd_analog_shutdown,
-};
-
 static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
 	[0] = {
 	       .name = "pm8916_wcd_analog_pdm_rx",
@@ -769,7 +753,6 @@ static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
 			    .channels_min = 1,
 			    .channels_max = 3,
 			    },
-	       .ops = &pm8916_wcd_analog_dai_ops,
 	       },
 	[1] = {
 	       .name = "pm8916_wcd_analog_pdm_tx",
@@ -781,7 +764,6 @@ static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
 			   .channels_min = 1,
 			   .channels_max = 4,
 			   },
-	       .ops = &pm8916_wcd_analog_dai_ops,
 	       },
 };
 
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 2/6] ASoC: codecs: msm8916-wcd-analog: move codec reset to probe
@ 2017-08-02 17:09   ` srinivas.kandagatla
  0 siblings, 0 replies; 22+ messages in thread
From: srinivas.kandagatla @ 2017-08-02 17:09 UTC (permalink / raw)
  To: Mark Brown, Banajit Goswami, alsa-devel
  Cc: damien.riegel, Patrick Lai, linux-kernel, Takashi Iwai,
	Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch move the codec reset code from dai ops to codec probe, so
that the codec is not held in reset when headset detection block is
still active.

Without this patch the codec block will be in reset as long as its not
actively used, which means headset events will not be functional if the
codec dai is not actively used. Point to note is that the headset
detection blocks will work in low power when there is no active audio
usecase and switch to micbias source when audio usecase is active.

Existing dapms should put the codec in low power state anyway when there
is no audio usecase.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/codecs/msm8916-wcd-analog.c | 30 ++++++------------------------
 1 file changed, 6 insertions(+), 24 deletions(-)

diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
index 5710fd4..6606954 100644
--- a/sound/soc/codecs/msm8916-wcd-analog.c
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -536,6 +536,9 @@ static int pm8916_wcd_analog_probe(struct snd_soc_codec *codec)
 		snd_soc_write(codec, wcd_reg_defaults_2_0[reg].reg,
 			      wcd_reg_defaults_2_0[reg].def);
 
+	snd_soc_update_bits(codec, CDC_D_CDC_RST_CTL,
+			    RST_CTL_DIG_SW_RST_N_MASK,
+			    RST_CTL_DIG_SW_RST_N_REMOVE_RESET);
 	return 0;
 }
 
@@ -543,6 +546,9 @@ static int pm8916_wcd_analog_remove(struct snd_soc_codec *codec)
 {
 	struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(codec->dev);
 
+	snd_soc_update_bits(codec, CDC_D_CDC_RST_CTL,
+			    RST_CTL_DIG_SW_RST_N_MASK, 0);
+
 	return regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
 				      priv->supplies);
 }
@@ -736,28 +742,6 @@ static struct regmap *pm8916_get_regmap(struct device *dev)
 	return dev_get_regmap(dev->parent, NULL);
 }
 
-static int pm8916_wcd_analog_startup(struct snd_pcm_substream *substream,
-				      struct snd_soc_dai *dai)
-{
-	snd_soc_update_bits(dai->codec, CDC_D_CDC_RST_CTL,
-			    RST_CTL_DIG_SW_RST_N_MASK,
-			    RST_CTL_DIG_SW_RST_N_REMOVE_RESET);
-
-	return 0;
-}
-
-static void pm8916_wcd_analog_shutdown(struct snd_pcm_substream *substream,
-					 struct snd_soc_dai *dai)
-{
-	snd_soc_update_bits(dai->codec, CDC_D_CDC_RST_CTL,
-			    RST_CTL_DIG_SW_RST_N_MASK, 0);
-}
-
-static struct snd_soc_dai_ops pm8916_wcd_analog_dai_ops = {
-	.startup = pm8916_wcd_analog_startup,
-	.shutdown = pm8916_wcd_analog_shutdown,
-};
-
 static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
 	[0] = {
 	       .name = "pm8916_wcd_analog_pdm_rx",
@@ -769,7 +753,6 @@ static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
 			    .channels_min = 1,
 			    .channels_max = 3,
 			    },
-	       .ops = &pm8916_wcd_analog_dai_ops,
 	       },
 	[1] = {
 	       .name = "pm8916_wcd_analog_pdm_tx",
@@ -781,7 +764,6 @@ static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
 			   .channels_min = 1,
 			   .channels_max = 4,
 			   },
-	       .ops = &pm8916_wcd_analog_dai_ops,
 	       },
 };
 
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 3/6] ASoC: codecs: msm8916-wcd-analog: get micbias voltage from dt
  2017-08-02 17:09 ` srinivas.kandagatla
                   ` (2 preceding siblings ...)
  (?)
@ 2017-08-02 17:09 ` srinivas.kandagatla
  -1 siblings, 0 replies; 22+ messages in thread
From: srinivas.kandagatla @ 2017-08-02 17:09 UTC (permalink / raw)
  To: Mark Brown, Banajit Goswami, alsa-devel
  Cc: damien.riegel, Jaroslav Kysela, Takashi Iwai, Patrick Lai,
	linux-kernel, Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch adds bindings in DT to provide required micbias voltage which
could be specific to board. With this new binding, now the mic bias
voltage is left at hardware default value if the device tree does not
specify any mic bias voltage value. Correct micbias value is required
for mbhc buttons to work.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 .../bindings/sound/qcom,msm8916-wcd-analog.txt       |  1 +
 sound/soc/codecs/msm8916-wcd-analog.c                | 20 ++++++++++++++------
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
index ccb401c..05b67a1 100644
--- a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
+++ b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
@@ -33,6 +33,7 @@ Required properties
  - vdd-micbias-supply: phandle of VDD_MICBIAS supply's regulator DT node.
 
 Optional Properties:
+- qcom,micbias-lvl:  Voltage (mV) for Mic Bias
 - qcom,micbias1-ext-cap: boolean, present if micbias1 has external capacitor
 			 connected.
 - qcom,micbias2-ext-cap: boolean, present if micbias2 has external capacitor
diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
index 6606954..dec4978 100644
--- a/sound/soc/codecs/msm8916-wcd-analog.c
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -93,8 +93,12 @@
 #define MICB_1_EN_TX3_GND_SEL_TX_GND	0
 
 #define CDC_A_MICB_1_VAL		(0xf141)
+#define MICB_MIN_VAL 1600
+#define MICB_STEP_SIZE 50
+#define MICB_VOLTAGE_REGVAL(v)		((v - MICB_MIN_VAL)/MICB_STEP_SIZE)
 #define MICB_1_VAL_MICB_OUT_VAL_MASK	GENMASK(7, 3)
 #define MICB_1_VAL_MICB_OUT_VAL_V2P70V	((0x16)  << 3)
+#define MICB_1_VAL_MICB_OUT_VAL_V1P80V	((0x4)  << 3)
 #define CDC_A_MICB_1_CTL		(0xf142)
 
 #define MICB_1_CTL_CFILT_REF_SEL_MASK		BIT(1)
@@ -225,6 +229,7 @@ struct pm8916_wcd_analog_priv {
 	struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
 	unsigned int micbias1_cap_mode;
 	unsigned int micbias2_cap_mode;
+	unsigned int micbias_mv;
 };
 
 static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" };
@@ -265,18 +270,18 @@ static const struct snd_kcontrol_new pm8916_wcd_analog_snd_controls[] = {
 
 static void pm8916_wcd_analog_micbias_enable(struct snd_soc_codec *codec)
 {
+	struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec);
+
 	snd_soc_update_bits(codec, CDC_A_MICB_1_CTL,
 			    MICB_1_CTL_EXT_PRECHARG_EN_MASK |
 			    MICB_1_CTL_INT_PRECHARG_BYP_MASK,
 			    MICB_1_CTL_INT_PRECHARG_BYP_EXT_PRECHRG_SEL
 			    | MICB_1_CTL_EXT_PRECHARG_EN_ENABLE);
 
-	snd_soc_write(codec, CDC_A_MICB_1_VAL, MICB_1_VAL_MICB_OUT_VAL_V2P70V);
-	/*
-	 * Special headset needs MICBIAS as 2.7V so wait for
-	 * 50 msec for the MICBIAS to reach 2.7 volts.
-	 */
-	msleep(50);
+	if (wcd->micbias_mv)
+		snd_soc_write(codec, CDC_A_MICB_1_VAL,
+			      MICB_VOLTAGE_REGVAL(wcd->micbias_mv));
+
 	snd_soc_update_bits(codec, CDC_A_MICB_1_CTL,
 			    MICB_1_CTL_EXT_PRECHARG_EN_MASK |
 			    MICB_1_CTL_INT_PRECHARG_BYP_MASK, 0);
@@ -795,6 +800,9 @@ static int pm8916_wcd_analog_parse_dt(struct device *dev,
 	else
 		priv->micbias2_cap_mode = MICB_1_EN_NO_EXT_BYP_CAP;
 
+	of_property_read_u32(dev->of_node, "qcom,micbias-lvl",
+			     &priv->micbias_mv);
+
 	return 0;
 }
 
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 4/6] ASoC: codecs: msm8916-wcd-analog: add MBHC support
  2017-08-02 17:09 ` srinivas.kandagatla
@ 2017-08-02 17:09   ` srinivas.kandagatla
  -1 siblings, 0 replies; 22+ messages in thread
From: srinivas.kandagatla @ 2017-08-02 17:09 UTC (permalink / raw)
  To: Mark Brown, Banajit Goswami, alsa-devel
  Cc: damien.riegel, Jaroslav Kysela, Takashi Iwai, Patrick Lai,
	linux-kernel, Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

MBHC (MultiButton Headset Control) support is available in pm8921 in two
blocks, one to detect mechanical headset insertion and removal and other
block to support headset type detection and 5 button detection and othe
features like impedance calculation.

This patch adds support to:
1> Support to NC and NO type of headset Jacks.
2> Mechanical insertion and detection of headset jack.
3> Detect a 3 pole Headphone and a 4 pole Headset.
4> Detect 5 buttons.

Tested it on DB410c with Audio Mezz board with 4 pole and 3 pole
headset/headphones.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 .../bindings/sound/qcom,msm8916-wcd-analog.txt     |  17 +-
 sound/soc/codecs/msm8916-wcd-analog.c              | 378 +++++++++++++++++++++
 2 files changed, 394 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
index 05b67a1..38668ed 100644
--- a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
+++ b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
@@ -31,9 +31,22 @@ Required properties
  - vdd-cdc-io-supply: phandle to VDD_CDC_IO regulator DT node.
  - vdd-cdc-tx-rx-cx-supply: phandle to VDD_CDC_TX/RX/CX regulator DT node.
  - vdd-micbias-supply: phandle of VDD_MICBIAS supply's regulator DT node.
-
 Optional Properties:
+ - qcom,mbhc-vthreshold-low: Array of 5 threshold voltages in mV for 5 buttons
+ 			      detection on headset when the mbhc is powered up
+			      by internal current source, this is a low power.
+ - qcom,mbhc-vthreshold-high: Array of 5 thresold voltages in mV for 5 buttons
+ 			       detection on headset when mbhc is powered up
+			       from micbias.
 - qcom,micbias-lvl:  Voltage (mV) for Mic Bias
+- qcom,hphl-jack-type-normally-open: boolean, present if hphl pin on jack is a
+				     NO (Normally Open). If not specified, then
+				     its assumed that hphl pin on jack is NC
+				     (Normally Closed).
+- qcom,gnd-jack-type-normally-open: boolean, present if gnd pin on jack is
+				    NO (Normally Open). If not specified, then
+				    its assumed that gnd pin on jack is NC
+				    (Normally Closed).
 - qcom,micbias1-ext-cap: boolean, present if micbias1 has external capacitor
 			 connected.
 - qcom,micbias2-ext-cap: boolean, present if micbias2 has external capacitor
@@ -49,6 +62,8 @@ spmi_bus {
 		reg-names = "pmic-codec-core";
 		clocks = <&gcc GCC_CODEC_DIGCODEC_CLK>;
 		clock-names = "mclk";
+		qcom,mbhc-vthreshold-low = <75 150 237 450 500>;
+		qcom,mbhc-vthreshold-high = <75 150 237 450 500>;
 		interrupt-parent = <&spmi_bus>;
 		interrupts = <0x1 0xf0 0x0 IRQ_TYPE_NONE>,
 			     <0x1 0xf0 0x1 IRQ_TYPE_NONE>,
diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
index dec4978..04a2112 100644
--- a/sound/soc/codecs/msm8916-wcd-analog.c
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -12,9 +12,20 @@
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/tlv.h>
+#include <sound/jack.h>
 
 #define CDC_D_REVISION1			(0xf000)
 #define CDC_D_PERPH_SUBTYPE		(0xf005)
+#define CDC_D_INT_EN_SET		(0x015)
+#define CDC_D_INT_EN_CLR		(0x016)
+#define MBHC_SWITCH_INT			BIT(7)
+#define MBHC_MIC_ELECTRICAL_INS_REM_DET	BIT(6)
+#define MBHC_BUTTON_PRESS_DET		BIT(5)
+#define MBHC_BUTTON_RELEASE_DET		BIT(4)
+#define CDC_DEF_INT_MASK	(MBHC_SWITCH_INT | \
+			MBHC_MIC_ELECTRICAL_INS_REM_DET	| \
+			MBHC_BUTTON_PRESS_DET | \
+			MBHC_BUTTON_RELEASE_DET)
 #define CDC_D_CDC_RST_CTL		(0xf046)
 #define RST_CTL_DIG_SW_RST_N_MASK	BIT(7)
 #define RST_CTL_DIG_SW_RST_N_RESET	0
@@ -37,6 +48,8 @@
 #define DIG_CLK_CTL_RXD1_CLK_EN		BIT(0)
 #define DIG_CLK_CTL_RXD2_CLK_EN		BIT(1)
 #define DIG_CLK_CTL_RXD3_CLK_EN		BIT(2)
+#define DIG_CLK_CTL_D_MBHC_CLK_EN_MASK	BIT(3)
+#define DIG_CLK_CTL_D_MBHC_CLK_EN	BIT(3)
 #define DIG_CLK_CTL_TXD_CLK_EN		BIT(4)
 #define DIG_CLK_CTL_NCP_CLK_EN_MASK	BIT(6)
 #define DIG_CLK_CTL_NCP_CLK_EN		BIT(6)
@@ -132,8 +145,51 @@
 #define MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_GND	0
 
 #define CDC_A_MICB_2_EN			(0xf144)
+#define CDC_A_MICB_2_EN_ENABLE		BIT(7)
+#define CDC_A_MICB_2_PULL_DOWN_EN_MASK	BIT(5)
+#define CDC_A_MICB_2_PULL_DOWN_EN	BIT(5)
 #define CDC_A_TX_1_2_ATEST_CTL_2	(0xf145)
 #define CDC_A_MASTER_BIAS_CTL		(0xf146)
+#define CDC_A_MBHC_DET_CTL_1		(0xf147)
+#define CDC_A_MBHC_DET_CTL_L_DET_EN			BIT(7)
+#define CDC_A_MBHC_DET_CTL_GND_DET_EN			BIT(6)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION	BIT(5)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_REMOVAL	(0)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK		BIT(5)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT		(5)
+#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO		BIT(4)
+#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_MANUAL		BIT(3)
+#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_MASK		GENMASK(4, 3)
+#define CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN			BIT(2)
+#define CDC_A_MBHC_DET_CTL_2		(0xf150)
+#define CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0	(BIT(7) | BIT(6))
+#define CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD	BIT(5)
+#define CDC_A_PLUG_TYPE_MASK				GENMASK(4, 3)
+#define CDC_A_HPHL_PLUG_TYPE_NO				BIT(4)
+#define CDC_A_GND_PLUG_TYPE_NO				BIT(3)
+#define CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN_MASK	BIT(0)
+#define CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN		BIT(0)
+#define CDC_A_MBHC_FSM_CTL		(0xf151)
+#define CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN			BIT(7)
+#define CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK		BIT(7)
+#define CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA	(0x3 << 4)
+#define CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK		GENMASK(6, 4)
+#define CDC_A_MBHC_DBNC_TIMER		(0xf152)
+#define CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS		BIT(3)
+#define CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS	(0x9 << 4)
+#define CDC_A_MBHC_BTN0_ZDET_CTL_0	(0xf153)
+#define CDC_A_MBHC_BTN1_ZDET_CTL_1	(0xf154)
+#define CDC_A_MBHC_BTN2_ZDET_CTL_2	(0xf155)
+#define CDC_A_MBHC_BTN3_CTL		(0xf156)
+#define CDC_A_MBHC_BTN4_CTL		(0xf157)
+#define CDC_A_MBHC_BTN_VREF_FINE_SHIFT	(2)
+#define CDC_A_MBHC_BTN_VREF_FINE_MASK	GENMASK(4, 2)
+#define CDC_A_MBHC_BTN_VREF_COARSE_MASK	GENMASK(7, 5)
+#define CDC_A_MBHC_BTN_VREF_COARSE_SHIFT (5)
+#define CDC_A_MBHC_BTN_VREF_MASK	(CDC_A_MBHC_BTN_VREF_COARSE_MASK | \
+					CDC_A_MBHC_BTN_VREF_FINE_MASK)
+#define CDC_A_MBHC_RESULT_1		(0xf158)
+#define CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK	GENMASK(4, 0)
 #define CDC_A_TX_1_EN			(0xf160)
 #define CDC_A_TX_2_EN			(0xf161)
 #define CDC_A_TX_1_2_TEST_CTL_1		(0xf162)
@@ -217,16 +273,37 @@
 #define MSM8916_WCD_ANALOG_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
 				    SNDRV_PCM_FMTBIT_S24_LE)
 
+int btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+	       SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4;
+int hs_jack_mask = SND_JACK_HEADPHONE | SND_JACK_HEADSET;
+
 static const char * const supply_names[] = {
 	"vdd-cdc-io",
 	"vdd-cdc-tx-rx-cx",
 };
 
+#define MBHC_MAX_BUTTONS	(5)
+
 struct pm8916_wcd_analog_priv {
 	u16 pmic_rev;
 	u16 codec_version;
+	int	mbhc_sw_irq;
+	int	mbhc_btn_press_irq;
+	int	mbhc_btn_release_irq;
+	bool	mbhc_btn_enabled;
+	/* special event to detect accessory type */
+	bool	mbhc_btn0_pressed;
+	bool	detect_accessory_type;
 	struct clk *mclk;
+	struct snd_soc_codec *codec;
 	struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+	struct snd_soc_jack *jack;
+	bool hphl_jack_type_normally_open;
+	bool gnd_jack_type_normally_open;
+	/* Voltage threshold when internal current source of 100uA is used */
+	u32 vref_btn_cs[MBHC_MAX_BUTTONS];
+	/* Voltage threshold when microphone bias is ON */
+	u32 vref_btn_micb[MBHC_MAX_BUTTONS];
 	unsigned int micbias1_cap_mode;
 	unsigned int micbias2_cap_mode;
 	unsigned int micbias_mv;
@@ -366,6 +443,93 @@ static int pm8916_wcd_analog_enable_micbias_int1(struct
 						     wcd->micbias1_cap_mode);
 }
 
+static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd)
+{
+	struct snd_soc_codec *codec = wcd->codec;
+	u32 plug_type = 0;
+
+	snd_soc_write(codec, CDC_A_MBHC_DET_CTL_1,
+		      CDC_A_MBHC_DET_CTL_L_DET_EN |
+		      CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION |
+		      CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO |
+		      CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN);
+
+	if (wcd->hphl_jack_type_normally_open)
+		plug_type |= CDC_A_HPHL_PLUG_TYPE_NO;
+
+	if (wcd->gnd_jack_type_normally_open)
+		plug_type |= CDC_A_GND_PLUG_TYPE_NO;
+
+	snd_soc_write(codec, CDC_A_MBHC_DET_CTL_2,
+		      CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0 |
+		      CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD |
+		      plug_type |
+		      CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN);
+
+
+	snd_soc_write(codec, CDC_A_MBHC_DBNC_TIMER,
+		      CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS |
+		      CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS);
+
+	/* enable MBHC clock */
+	snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL,
+			    DIG_CLK_CTL_D_MBHC_CLK_EN_MASK,
+			    DIG_CLK_CTL_D_MBHC_CLK_EN);
+
+	snd_soc_update_bits(codec, CDC_D_INT_EN_CLR, CDC_DEF_INT_MASK, 0);
+	snd_soc_update_bits(codec, CDC_D_INT_EN_SET, CDC_DEF_INT_MASK,
+			    CDC_DEF_INT_MASK);
+	wcd->mbhc_btn0_pressed = false;
+	wcd->detect_accessory_type = true;
+}
+
+static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv,
+				      bool micbias2_enabled)
+{
+	struct snd_soc_codec *codec = priv->codec;
+	u32 coarse, fine, reg_val, reg_addr;
+	int *vrefs, i;
+
+	if (!micbias2_enabled) { /* use internal 100uA Current source */
+		/* Enable internal 2.2k Internal Rbias Resistor */
+		snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS,
+				    MICB_1_INT_TX2_INT_RBIAS_EN_MASK,
+				    MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE);
+		/* Remove pull down on MIC BIAS2 */
+		snd_soc_update_bits(codec, CDC_A_MICB_2_EN,
+				   CDC_A_MICB_2_PULL_DOWN_EN_MASK,
+				   0);
+		/* enable 100uA internal current source */
+		snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
+				    CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK,
+				    CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA);
+	}
+	snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
+			CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK,
+			CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN);
+
+	if (micbias2_enabled)
+		vrefs = &priv->vref_btn_micb[0];
+	else
+		vrefs = &priv->vref_btn_cs[0];
+
+	/* program vref ranges for all the buttons */
+	reg_addr = CDC_A_MBHC_BTN0_ZDET_CTL_0;
+	for (i = 0; i <  MBHC_MAX_BUTTONS; i++) {
+		/* split mv in to coarse parts of 100mv & fine parts of 12mv */
+		coarse = (vrefs[i] / 100);
+		fine = ((vrefs[i] % 100) / 12);
+		reg_val = (coarse << CDC_A_MBHC_BTN_VREF_COARSE_SHIFT) |
+			 (fine << CDC_A_MBHC_BTN_VREF_FINE_SHIFT);
+		snd_soc_update_bits(codec, reg_addr,
+			       CDC_A_MBHC_BTN_VREF_MASK,
+			       reg_val);
+		reg_addr++;
+	}
+
+	return 0;
+}
+
 static int pm8916_wcd_analog_enable_micbias_int2(struct
 						  snd_soc_dapm_widget
 						  *w, struct snd_kcontrol
@@ -374,6 +538,15 @@ static int pm8916_wcd_analog_enable_micbias_int2(struct
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec);
 
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		pm8916_mbhc_configure_bias(wcd, true);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		pm8916_mbhc_configure_bias(wcd, false);
+		break;
+	}
+
 	return pm8916_wcd_analog_enable_micbias_int(codec, event, w->reg,
 						     wcd->micbias2_cap_mode);
 }
@@ -541,9 +714,14 @@ static int pm8916_wcd_analog_probe(struct snd_soc_codec *codec)
 		snd_soc_write(codec, wcd_reg_defaults_2_0[reg].reg,
 			      wcd_reg_defaults_2_0[reg].def);
 
+	priv->codec = codec;
+
 	snd_soc_update_bits(codec, CDC_D_CDC_RST_CTL,
 			    RST_CTL_DIG_SW_RST_N_MASK,
 			    RST_CTL_DIG_SW_RST_N_REMOVE_RESET);
+
+	pm8916_wcd_setup_mbhc(priv);
+
 	return 0;
 }
 
@@ -742,11 +920,128 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
 	SND_SOC_DAPM_SUPPLY("A_MCLK2", CDC_D_CDC_TOP_CLK_CTL, 3, 0, NULL, 0),
 };
 
+static int pm8916_wcd_analog_set_jack(struct snd_soc_codec *codec,
+				      struct snd_soc_jack *jack,
+				      void *data)
+{
+	struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec);
+
+	wcd->jack = jack;
+
+	return 0;
+}
+
 static struct regmap *pm8916_get_regmap(struct device *dev)
 {
 	return dev_get_regmap(dev->parent, NULL);
 }
 
+static irqreturn_t mbhc_btn_release_irq_handler(int irq, void *arg)
+{
+	struct pm8916_wcd_analog_priv *priv = arg;
+
+	if (priv->detect_accessory_type) {
+		struct snd_soc_codec *codec = priv->codec;
+		u32 val = snd_soc_read(codec, CDC_A_MBHC_RESULT_1);
+
+		/* check if its BTN0 thats released */
+		if ((val >= 0) && !(val & CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK))
+			priv->mbhc_btn0_pressed = false;
+
+	} else {
+		snd_soc_jack_report(priv->jack, 0, btn_mask);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mbhc_btn_press_irq_handler(int irq, void *arg)
+{
+	struct pm8916_wcd_analog_priv *priv = arg;
+	struct snd_soc_codec *codec = priv->codec;
+	u32 btn_result;
+
+	btn_result = snd_soc_read(codec, CDC_A_MBHC_RESULT_1) &
+				  CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK;
+
+	switch (btn_result) {
+	case 0xf:
+		snd_soc_jack_report(priv->jack, SND_JACK_BTN_4, btn_mask);
+		break;
+	case 0x7:
+		snd_soc_jack_report(priv->jack, SND_JACK_BTN_3, btn_mask);
+		break;
+	case 0x3:
+		snd_soc_jack_report(priv->jack, SND_JACK_BTN_2, btn_mask);
+		break;
+	case 0x1:
+		snd_soc_jack_report(priv->jack, SND_JACK_BTN_1, btn_mask);
+		break;
+	case 0x0:
+		/* handle BTN_0 specially for type detection */
+		if (priv->detect_accessory_type)
+			priv->mbhc_btn0_pressed = true;
+		else
+			snd_soc_jack_report(priv->jack,
+					    SND_JACK_BTN_0, btn_mask);
+		break;
+	default:
+		dev_err(codec->dev,
+			"Unexpected button press result (%x)", btn_result);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
+{
+	struct pm8916_wcd_analog_priv *priv = arg;
+	struct snd_soc_codec *codec = priv->codec;
+	bool micbias_enabled = false;
+	bool ins = false;
+
+	if (snd_soc_read(codec, CDC_A_MBHC_DET_CTL_1) &
+				CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK)
+		ins = true;
+
+	/* Set the detection type appropriately */
+	snd_soc_update_bits(codec, CDC_A_MBHC_DET_CTL_1,
+			    CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK,
+			    (!ins << CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT));
+
+
+	if (ins) { /* hs insertion */
+		if (snd_soc_read(codec, CDC_A_MICB_2_EN) &
+				CDC_A_MICB_2_EN_ENABLE)
+			micbias_enabled = true;
+
+		pm8916_mbhc_configure_bias(priv, micbias_enabled);
+
+		/*
+		 * if only a btn0 press event is receive just before
+		 * insert event then its a 3 pole headphone else if
+		 * both press and release event received then its
+		 * a headset.
+		 */
+		if (priv->mbhc_btn0_pressed)
+			snd_soc_jack_report(priv->jack,
+					    SND_JACK_HEADPHONE, hs_jack_mask);
+		else
+			snd_soc_jack_report(priv->jack,
+					    SND_JACK_HEADSET, hs_jack_mask);
+
+		priv->detect_accessory_type = false;
+
+	} else { /* removal */
+		snd_soc_jack_report(priv->jack, 0, hs_jack_mask);
+		priv->detect_accessory_type = true;
+	}
+
+	return IRQ_HANDLED;
+}
+
 static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
 	[0] = {
 	       .name = "pm8916_wcd_analog_pdm_rx",
@@ -775,6 +1070,7 @@ static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
 static struct snd_soc_codec_driver pm8916_wcd_analog = {
 	.probe = pm8916_wcd_analog_probe,
 	.remove = pm8916_wcd_analog_remove,
+	.set_jack = pm8916_wcd_analog_set_jack,
 	.get_regmap = pm8916_get_regmap,
 	.component_driver = {
 		.controls = pm8916_wcd_analog_snd_controls,
@@ -789,6 +1085,7 @@ static struct snd_soc_codec_driver pm8916_wcd_analog = {
 static int pm8916_wcd_analog_parse_dt(struct device *dev,
 				       struct pm8916_wcd_analog_priv *priv)
 {
+	int rval;
 
 	if (of_property_read_bool(dev->of_node, "qcom,micbias1-ext-cap"))
 		priv->micbias1_cap_mode = MICB_1_EN_EXT_BYP_CAP;
@@ -803,6 +1100,34 @@ static int pm8916_wcd_analog_parse_dt(struct device *dev,
 	of_property_read_u32(dev->of_node, "qcom,micbias-lvl",
 			     &priv->micbias_mv);
 
+	if (of_property_read_bool(dev->of_node,
+				  "qcom,hphl-jack-type-normally-open"))
+		priv->hphl_jack_type_normally_open = true;
+	else
+		priv->hphl_jack_type_normally_open = false;
+
+	if (of_property_read_bool(dev->of_node,
+				  "qcom,gnd-jack-type-normally-open"))
+		priv->gnd_jack_type_normally_open = true;
+	else
+		priv->gnd_jack_type_normally_open = false;
+
+	priv->mbhc_btn_enabled = true;
+	rval = of_property_read_u32_array(dev->of_node,
+					  "qcom,mbhc-vthreshold-low",
+					  &priv->vref_btn_cs[0],
+					  MBHC_MAX_BUTTONS);
+	if (rval < 0)
+		priv->mbhc_btn_enabled = false;
+	else {
+		rval = of_property_read_u32_array(dev->of_node,
+						  "qcom,mbhc-vthreshold-high",
+						  &priv->vref_btn_micb[0],
+						  MBHC_MAX_BUTTONS);
+		if (rval < 0)
+			priv->mbhc_btn_enabled = false;
+	}
+
 	return 0;
 }
 
@@ -842,6 +1167,59 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	priv->mbhc_sw_irq = platform_get_irq_byname(pdev, "mbhc_switch_int");
+	if (priv->mbhc_sw_irq < 0) {
+		dev_err(dev, "failed to get mbhc switch irq\n");
+		return priv->mbhc_sw_irq;
+	}
+
+	if (priv->mbhc_btn_enabled) {
+		int irq;
+
+		irq = platform_get_irq_byname(pdev, "mbhc_but_press_det");
+		if (irq < 0) {
+			dev_err(dev, "failed to get button press irq\n");
+			return irq;
+		}
+
+		priv->mbhc_btn_press_irq = irq;
+
+		irq = platform_get_irq_byname(pdev, "mbhc_but_rel_det");
+		if (irq < 0) {
+			dev_err(dev, "failed to get button release irq\n");
+			return irq;
+		}
+
+		priv->mbhc_btn_release_irq = irq;
+
+		ret = devm_request_irq(dev, priv->mbhc_btn_release_irq,
+				       mbhc_btn_release_irq_handler,
+				       IRQF_TRIGGER_RISING |
+				       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				       "mbhc btn release irq", priv);
+		if (ret)
+			dev_err(dev, "cannot request mbhc button release irq\n");
+
+		ret = devm_request_irq(dev, priv->mbhc_btn_press_irq,
+				       mbhc_btn_press_irq_handler,
+				       IRQF_TRIGGER_RISING |
+				       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				       "mbhc btn press irq", priv);
+		if (ret)
+			dev_err(dev, "cannot request mbhc button press irq\n");
+	} else {
+		dev_err(dev, "MBHC button detection disabled\n");
+	}
+
+
+	ret = devm_request_irq(dev, priv->mbhc_sw_irq,
+			       pm8916_mbhc_switch_irq_handler,
+			       IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+			       IRQF_ONESHOT,
+			       "mbhc switch irq", priv);
+	if (ret)
+		dev_err(dev, "cannot request mbhc switch irq\n");
+
 	dev_set_drvdata(dev, priv);
 
 	return snd_soc_register_codec(dev, &pm8916_wcd_analog,
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 4/6] ASoC: codecs: msm8916-wcd-analog: add MBHC support
@ 2017-08-02 17:09   ` srinivas.kandagatla
  0 siblings, 0 replies; 22+ messages in thread
From: srinivas.kandagatla @ 2017-08-02 17:09 UTC (permalink / raw)
  To: Mark Brown, Banajit Goswami, alsa-devel
  Cc: damien.riegel, Patrick Lai, linux-kernel, Takashi Iwai,
	Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

MBHC (MultiButton Headset Control) support is available in pm8921 in two
blocks, one to detect mechanical headset insertion and removal and other
block to support headset type detection and 5 button detection and othe
features like impedance calculation.

This patch adds support to:
1> Support to NC and NO type of headset Jacks.
2> Mechanical insertion and detection of headset jack.
3> Detect a 3 pole Headphone and a 4 pole Headset.
4> Detect 5 buttons.

Tested it on DB410c with Audio Mezz board with 4 pole and 3 pole
headset/headphones.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 .../bindings/sound/qcom,msm8916-wcd-analog.txt     |  17 +-
 sound/soc/codecs/msm8916-wcd-analog.c              | 378 +++++++++++++++++++++
 2 files changed, 394 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
index 05b67a1..38668ed 100644
--- a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
+++ b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
@@ -31,9 +31,22 @@ Required properties
  - vdd-cdc-io-supply: phandle to VDD_CDC_IO regulator DT node.
  - vdd-cdc-tx-rx-cx-supply: phandle to VDD_CDC_TX/RX/CX regulator DT node.
  - vdd-micbias-supply: phandle of VDD_MICBIAS supply's regulator DT node.
-
 Optional Properties:
+ - qcom,mbhc-vthreshold-low: Array of 5 threshold voltages in mV for 5 buttons
+ 			      detection on headset when the mbhc is powered up
+			      by internal current source, this is a low power.
+ - qcom,mbhc-vthreshold-high: Array of 5 thresold voltages in mV for 5 buttons
+ 			       detection on headset when mbhc is powered up
+			       from micbias.
 - qcom,micbias-lvl:  Voltage (mV) for Mic Bias
+- qcom,hphl-jack-type-normally-open: boolean, present if hphl pin on jack is a
+				     NO (Normally Open). If not specified, then
+				     its assumed that hphl pin on jack is NC
+				     (Normally Closed).
+- qcom,gnd-jack-type-normally-open: boolean, present if gnd pin on jack is
+				    NO (Normally Open). If not specified, then
+				    its assumed that gnd pin on jack is NC
+				    (Normally Closed).
 - qcom,micbias1-ext-cap: boolean, present if micbias1 has external capacitor
 			 connected.
 - qcom,micbias2-ext-cap: boolean, present if micbias2 has external capacitor
@@ -49,6 +62,8 @@ spmi_bus {
 		reg-names = "pmic-codec-core";
 		clocks = <&gcc GCC_CODEC_DIGCODEC_CLK>;
 		clock-names = "mclk";
+		qcom,mbhc-vthreshold-low = <75 150 237 450 500>;
+		qcom,mbhc-vthreshold-high = <75 150 237 450 500>;
 		interrupt-parent = <&spmi_bus>;
 		interrupts = <0x1 0xf0 0x0 IRQ_TYPE_NONE>,
 			     <0x1 0xf0 0x1 IRQ_TYPE_NONE>,
diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
index dec4978..04a2112 100644
--- a/sound/soc/codecs/msm8916-wcd-analog.c
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -12,9 +12,20 @@
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/tlv.h>
+#include <sound/jack.h>
 
 #define CDC_D_REVISION1			(0xf000)
 #define CDC_D_PERPH_SUBTYPE		(0xf005)
+#define CDC_D_INT_EN_SET		(0x015)
+#define CDC_D_INT_EN_CLR		(0x016)
+#define MBHC_SWITCH_INT			BIT(7)
+#define MBHC_MIC_ELECTRICAL_INS_REM_DET	BIT(6)
+#define MBHC_BUTTON_PRESS_DET		BIT(5)
+#define MBHC_BUTTON_RELEASE_DET		BIT(4)
+#define CDC_DEF_INT_MASK	(MBHC_SWITCH_INT | \
+			MBHC_MIC_ELECTRICAL_INS_REM_DET	| \
+			MBHC_BUTTON_PRESS_DET | \
+			MBHC_BUTTON_RELEASE_DET)
 #define CDC_D_CDC_RST_CTL		(0xf046)
 #define RST_CTL_DIG_SW_RST_N_MASK	BIT(7)
 #define RST_CTL_DIG_SW_RST_N_RESET	0
@@ -37,6 +48,8 @@
 #define DIG_CLK_CTL_RXD1_CLK_EN		BIT(0)
 #define DIG_CLK_CTL_RXD2_CLK_EN		BIT(1)
 #define DIG_CLK_CTL_RXD3_CLK_EN		BIT(2)
+#define DIG_CLK_CTL_D_MBHC_CLK_EN_MASK	BIT(3)
+#define DIG_CLK_CTL_D_MBHC_CLK_EN	BIT(3)
 #define DIG_CLK_CTL_TXD_CLK_EN		BIT(4)
 #define DIG_CLK_CTL_NCP_CLK_EN_MASK	BIT(6)
 #define DIG_CLK_CTL_NCP_CLK_EN		BIT(6)
@@ -132,8 +145,51 @@
 #define MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_GND	0
 
 #define CDC_A_MICB_2_EN			(0xf144)
+#define CDC_A_MICB_2_EN_ENABLE		BIT(7)
+#define CDC_A_MICB_2_PULL_DOWN_EN_MASK	BIT(5)
+#define CDC_A_MICB_2_PULL_DOWN_EN	BIT(5)
 #define CDC_A_TX_1_2_ATEST_CTL_2	(0xf145)
 #define CDC_A_MASTER_BIAS_CTL		(0xf146)
+#define CDC_A_MBHC_DET_CTL_1		(0xf147)
+#define CDC_A_MBHC_DET_CTL_L_DET_EN			BIT(7)
+#define CDC_A_MBHC_DET_CTL_GND_DET_EN			BIT(6)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION	BIT(5)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_REMOVAL	(0)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK		BIT(5)
+#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT		(5)
+#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO		BIT(4)
+#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_MANUAL		BIT(3)
+#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_MASK		GENMASK(4, 3)
+#define CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN			BIT(2)
+#define CDC_A_MBHC_DET_CTL_2		(0xf150)
+#define CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0	(BIT(7) | BIT(6))
+#define CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD	BIT(5)
+#define CDC_A_PLUG_TYPE_MASK				GENMASK(4, 3)
+#define CDC_A_HPHL_PLUG_TYPE_NO				BIT(4)
+#define CDC_A_GND_PLUG_TYPE_NO				BIT(3)
+#define CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN_MASK	BIT(0)
+#define CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN		BIT(0)
+#define CDC_A_MBHC_FSM_CTL		(0xf151)
+#define CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN			BIT(7)
+#define CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK		BIT(7)
+#define CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA	(0x3 << 4)
+#define CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK		GENMASK(6, 4)
+#define CDC_A_MBHC_DBNC_TIMER		(0xf152)
+#define CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS		BIT(3)
+#define CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS	(0x9 << 4)
+#define CDC_A_MBHC_BTN0_ZDET_CTL_0	(0xf153)
+#define CDC_A_MBHC_BTN1_ZDET_CTL_1	(0xf154)
+#define CDC_A_MBHC_BTN2_ZDET_CTL_2	(0xf155)
+#define CDC_A_MBHC_BTN3_CTL		(0xf156)
+#define CDC_A_MBHC_BTN4_CTL		(0xf157)
+#define CDC_A_MBHC_BTN_VREF_FINE_SHIFT	(2)
+#define CDC_A_MBHC_BTN_VREF_FINE_MASK	GENMASK(4, 2)
+#define CDC_A_MBHC_BTN_VREF_COARSE_MASK	GENMASK(7, 5)
+#define CDC_A_MBHC_BTN_VREF_COARSE_SHIFT (5)
+#define CDC_A_MBHC_BTN_VREF_MASK	(CDC_A_MBHC_BTN_VREF_COARSE_MASK | \
+					CDC_A_MBHC_BTN_VREF_FINE_MASK)
+#define CDC_A_MBHC_RESULT_1		(0xf158)
+#define CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK	GENMASK(4, 0)
 #define CDC_A_TX_1_EN			(0xf160)
 #define CDC_A_TX_2_EN			(0xf161)
 #define CDC_A_TX_1_2_TEST_CTL_1		(0xf162)
@@ -217,16 +273,37 @@
 #define MSM8916_WCD_ANALOG_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
 				    SNDRV_PCM_FMTBIT_S24_LE)
 
+int btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+	       SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4;
+int hs_jack_mask = SND_JACK_HEADPHONE | SND_JACK_HEADSET;
+
 static const char * const supply_names[] = {
 	"vdd-cdc-io",
 	"vdd-cdc-tx-rx-cx",
 };
 
+#define MBHC_MAX_BUTTONS	(5)
+
 struct pm8916_wcd_analog_priv {
 	u16 pmic_rev;
 	u16 codec_version;
+	int	mbhc_sw_irq;
+	int	mbhc_btn_press_irq;
+	int	mbhc_btn_release_irq;
+	bool	mbhc_btn_enabled;
+	/* special event to detect accessory type */
+	bool	mbhc_btn0_pressed;
+	bool	detect_accessory_type;
 	struct clk *mclk;
+	struct snd_soc_codec *codec;
 	struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+	struct snd_soc_jack *jack;
+	bool hphl_jack_type_normally_open;
+	bool gnd_jack_type_normally_open;
+	/* Voltage threshold when internal current source of 100uA is used */
+	u32 vref_btn_cs[MBHC_MAX_BUTTONS];
+	/* Voltage threshold when microphone bias is ON */
+	u32 vref_btn_micb[MBHC_MAX_BUTTONS];
 	unsigned int micbias1_cap_mode;
 	unsigned int micbias2_cap_mode;
 	unsigned int micbias_mv;
@@ -366,6 +443,93 @@ static int pm8916_wcd_analog_enable_micbias_int1(struct
 						     wcd->micbias1_cap_mode);
 }
 
+static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd)
+{
+	struct snd_soc_codec *codec = wcd->codec;
+	u32 plug_type = 0;
+
+	snd_soc_write(codec, CDC_A_MBHC_DET_CTL_1,
+		      CDC_A_MBHC_DET_CTL_L_DET_EN |
+		      CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION |
+		      CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO |
+		      CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN);
+
+	if (wcd->hphl_jack_type_normally_open)
+		plug_type |= CDC_A_HPHL_PLUG_TYPE_NO;
+
+	if (wcd->gnd_jack_type_normally_open)
+		plug_type |= CDC_A_GND_PLUG_TYPE_NO;
+
+	snd_soc_write(codec, CDC_A_MBHC_DET_CTL_2,
+		      CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0 |
+		      CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD |
+		      plug_type |
+		      CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN);
+
+
+	snd_soc_write(codec, CDC_A_MBHC_DBNC_TIMER,
+		      CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS |
+		      CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS);
+
+	/* enable MBHC clock */
+	snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL,
+			    DIG_CLK_CTL_D_MBHC_CLK_EN_MASK,
+			    DIG_CLK_CTL_D_MBHC_CLK_EN);
+
+	snd_soc_update_bits(codec, CDC_D_INT_EN_CLR, CDC_DEF_INT_MASK, 0);
+	snd_soc_update_bits(codec, CDC_D_INT_EN_SET, CDC_DEF_INT_MASK,
+			    CDC_DEF_INT_MASK);
+	wcd->mbhc_btn0_pressed = false;
+	wcd->detect_accessory_type = true;
+}
+
+static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv,
+				      bool micbias2_enabled)
+{
+	struct snd_soc_codec *codec = priv->codec;
+	u32 coarse, fine, reg_val, reg_addr;
+	int *vrefs, i;
+
+	if (!micbias2_enabled) { /* use internal 100uA Current source */
+		/* Enable internal 2.2k Internal Rbias Resistor */
+		snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS,
+				    MICB_1_INT_TX2_INT_RBIAS_EN_MASK,
+				    MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE);
+		/* Remove pull down on MIC BIAS2 */
+		snd_soc_update_bits(codec, CDC_A_MICB_2_EN,
+				   CDC_A_MICB_2_PULL_DOWN_EN_MASK,
+				   0);
+		/* enable 100uA internal current source */
+		snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
+				    CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK,
+				    CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA);
+	}
+	snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
+			CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK,
+			CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN);
+
+	if (micbias2_enabled)
+		vrefs = &priv->vref_btn_micb[0];
+	else
+		vrefs = &priv->vref_btn_cs[0];
+
+	/* program vref ranges for all the buttons */
+	reg_addr = CDC_A_MBHC_BTN0_ZDET_CTL_0;
+	for (i = 0; i <  MBHC_MAX_BUTTONS; i++) {
+		/* split mv in to coarse parts of 100mv & fine parts of 12mv */
+		coarse = (vrefs[i] / 100);
+		fine = ((vrefs[i] % 100) / 12);
+		reg_val = (coarse << CDC_A_MBHC_BTN_VREF_COARSE_SHIFT) |
+			 (fine << CDC_A_MBHC_BTN_VREF_FINE_SHIFT);
+		snd_soc_update_bits(codec, reg_addr,
+			       CDC_A_MBHC_BTN_VREF_MASK,
+			       reg_val);
+		reg_addr++;
+	}
+
+	return 0;
+}
+
 static int pm8916_wcd_analog_enable_micbias_int2(struct
 						  snd_soc_dapm_widget
 						  *w, struct snd_kcontrol
@@ -374,6 +538,15 @@ static int pm8916_wcd_analog_enable_micbias_int2(struct
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec);
 
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		pm8916_mbhc_configure_bias(wcd, true);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		pm8916_mbhc_configure_bias(wcd, false);
+		break;
+	}
+
 	return pm8916_wcd_analog_enable_micbias_int(codec, event, w->reg,
 						     wcd->micbias2_cap_mode);
 }
@@ -541,9 +714,14 @@ static int pm8916_wcd_analog_probe(struct snd_soc_codec *codec)
 		snd_soc_write(codec, wcd_reg_defaults_2_0[reg].reg,
 			      wcd_reg_defaults_2_0[reg].def);
 
+	priv->codec = codec;
+
 	snd_soc_update_bits(codec, CDC_D_CDC_RST_CTL,
 			    RST_CTL_DIG_SW_RST_N_MASK,
 			    RST_CTL_DIG_SW_RST_N_REMOVE_RESET);
+
+	pm8916_wcd_setup_mbhc(priv);
+
 	return 0;
 }
 
@@ -742,11 +920,128 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
 	SND_SOC_DAPM_SUPPLY("A_MCLK2", CDC_D_CDC_TOP_CLK_CTL, 3, 0, NULL, 0),
 };
 
+static int pm8916_wcd_analog_set_jack(struct snd_soc_codec *codec,
+				      struct snd_soc_jack *jack,
+				      void *data)
+{
+	struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec);
+
+	wcd->jack = jack;
+
+	return 0;
+}
+
 static struct regmap *pm8916_get_regmap(struct device *dev)
 {
 	return dev_get_regmap(dev->parent, NULL);
 }
 
+static irqreturn_t mbhc_btn_release_irq_handler(int irq, void *arg)
+{
+	struct pm8916_wcd_analog_priv *priv = arg;
+
+	if (priv->detect_accessory_type) {
+		struct snd_soc_codec *codec = priv->codec;
+		u32 val = snd_soc_read(codec, CDC_A_MBHC_RESULT_1);
+
+		/* check if its BTN0 thats released */
+		if ((val >= 0) && !(val & CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK))
+			priv->mbhc_btn0_pressed = false;
+
+	} else {
+		snd_soc_jack_report(priv->jack, 0, btn_mask);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mbhc_btn_press_irq_handler(int irq, void *arg)
+{
+	struct pm8916_wcd_analog_priv *priv = arg;
+	struct snd_soc_codec *codec = priv->codec;
+	u32 btn_result;
+
+	btn_result = snd_soc_read(codec, CDC_A_MBHC_RESULT_1) &
+				  CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK;
+
+	switch (btn_result) {
+	case 0xf:
+		snd_soc_jack_report(priv->jack, SND_JACK_BTN_4, btn_mask);
+		break;
+	case 0x7:
+		snd_soc_jack_report(priv->jack, SND_JACK_BTN_3, btn_mask);
+		break;
+	case 0x3:
+		snd_soc_jack_report(priv->jack, SND_JACK_BTN_2, btn_mask);
+		break;
+	case 0x1:
+		snd_soc_jack_report(priv->jack, SND_JACK_BTN_1, btn_mask);
+		break;
+	case 0x0:
+		/* handle BTN_0 specially for type detection */
+		if (priv->detect_accessory_type)
+			priv->mbhc_btn0_pressed = true;
+		else
+			snd_soc_jack_report(priv->jack,
+					    SND_JACK_BTN_0, btn_mask);
+		break;
+	default:
+		dev_err(codec->dev,
+			"Unexpected button press result (%x)", btn_result);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
+{
+	struct pm8916_wcd_analog_priv *priv = arg;
+	struct snd_soc_codec *codec = priv->codec;
+	bool micbias_enabled = false;
+	bool ins = false;
+
+	if (snd_soc_read(codec, CDC_A_MBHC_DET_CTL_1) &
+				CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK)
+		ins = true;
+
+	/* Set the detection type appropriately */
+	snd_soc_update_bits(codec, CDC_A_MBHC_DET_CTL_1,
+			    CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK,
+			    (!ins << CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT));
+
+
+	if (ins) { /* hs insertion */
+		if (snd_soc_read(codec, CDC_A_MICB_2_EN) &
+				CDC_A_MICB_2_EN_ENABLE)
+			micbias_enabled = true;
+
+		pm8916_mbhc_configure_bias(priv, micbias_enabled);
+
+		/*
+		 * if only a btn0 press event is receive just before
+		 * insert event then its a 3 pole headphone else if
+		 * both press and release event received then its
+		 * a headset.
+		 */
+		if (priv->mbhc_btn0_pressed)
+			snd_soc_jack_report(priv->jack,
+					    SND_JACK_HEADPHONE, hs_jack_mask);
+		else
+			snd_soc_jack_report(priv->jack,
+					    SND_JACK_HEADSET, hs_jack_mask);
+
+		priv->detect_accessory_type = false;
+
+	} else { /* removal */
+		snd_soc_jack_report(priv->jack, 0, hs_jack_mask);
+		priv->detect_accessory_type = true;
+	}
+
+	return IRQ_HANDLED;
+}
+
 static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
 	[0] = {
 	       .name = "pm8916_wcd_analog_pdm_rx",
@@ -775,6 +1070,7 @@ static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
 static struct snd_soc_codec_driver pm8916_wcd_analog = {
 	.probe = pm8916_wcd_analog_probe,
 	.remove = pm8916_wcd_analog_remove,
+	.set_jack = pm8916_wcd_analog_set_jack,
 	.get_regmap = pm8916_get_regmap,
 	.component_driver = {
 		.controls = pm8916_wcd_analog_snd_controls,
@@ -789,6 +1085,7 @@ static struct snd_soc_codec_driver pm8916_wcd_analog = {
 static int pm8916_wcd_analog_parse_dt(struct device *dev,
 				       struct pm8916_wcd_analog_priv *priv)
 {
+	int rval;
 
 	if (of_property_read_bool(dev->of_node, "qcom,micbias1-ext-cap"))
 		priv->micbias1_cap_mode = MICB_1_EN_EXT_BYP_CAP;
@@ -803,6 +1100,34 @@ static int pm8916_wcd_analog_parse_dt(struct device *dev,
 	of_property_read_u32(dev->of_node, "qcom,micbias-lvl",
 			     &priv->micbias_mv);
 
+	if (of_property_read_bool(dev->of_node,
+				  "qcom,hphl-jack-type-normally-open"))
+		priv->hphl_jack_type_normally_open = true;
+	else
+		priv->hphl_jack_type_normally_open = false;
+
+	if (of_property_read_bool(dev->of_node,
+				  "qcom,gnd-jack-type-normally-open"))
+		priv->gnd_jack_type_normally_open = true;
+	else
+		priv->gnd_jack_type_normally_open = false;
+
+	priv->mbhc_btn_enabled = true;
+	rval = of_property_read_u32_array(dev->of_node,
+					  "qcom,mbhc-vthreshold-low",
+					  &priv->vref_btn_cs[0],
+					  MBHC_MAX_BUTTONS);
+	if (rval < 0)
+		priv->mbhc_btn_enabled = false;
+	else {
+		rval = of_property_read_u32_array(dev->of_node,
+						  "qcom,mbhc-vthreshold-high",
+						  &priv->vref_btn_micb[0],
+						  MBHC_MAX_BUTTONS);
+		if (rval < 0)
+			priv->mbhc_btn_enabled = false;
+	}
+
 	return 0;
 }
 
@@ -842,6 +1167,59 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	priv->mbhc_sw_irq = platform_get_irq_byname(pdev, "mbhc_switch_int");
+	if (priv->mbhc_sw_irq < 0) {
+		dev_err(dev, "failed to get mbhc switch irq\n");
+		return priv->mbhc_sw_irq;
+	}
+
+	if (priv->mbhc_btn_enabled) {
+		int irq;
+
+		irq = platform_get_irq_byname(pdev, "mbhc_but_press_det");
+		if (irq < 0) {
+			dev_err(dev, "failed to get button press irq\n");
+			return irq;
+		}
+
+		priv->mbhc_btn_press_irq = irq;
+
+		irq = platform_get_irq_byname(pdev, "mbhc_but_rel_det");
+		if (irq < 0) {
+			dev_err(dev, "failed to get button release irq\n");
+			return irq;
+		}
+
+		priv->mbhc_btn_release_irq = irq;
+
+		ret = devm_request_irq(dev, priv->mbhc_btn_release_irq,
+				       mbhc_btn_release_irq_handler,
+				       IRQF_TRIGGER_RISING |
+				       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				       "mbhc btn release irq", priv);
+		if (ret)
+			dev_err(dev, "cannot request mbhc button release irq\n");
+
+		ret = devm_request_irq(dev, priv->mbhc_btn_press_irq,
+				       mbhc_btn_press_irq_handler,
+				       IRQF_TRIGGER_RISING |
+				       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				       "mbhc btn press irq", priv);
+		if (ret)
+			dev_err(dev, "cannot request mbhc button press irq\n");
+	} else {
+		dev_err(dev, "MBHC button detection disabled\n");
+	}
+
+
+	ret = devm_request_irq(dev, priv->mbhc_sw_irq,
+			       pm8916_mbhc_switch_irq_handler,
+			       IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+			       IRQF_ONESHOT,
+			       "mbhc switch irq", priv);
+	if (ret)
+		dev_err(dev, "cannot request mbhc switch irq\n");
+
 	dev_set_drvdata(dev, priv);
 
 	return snd_soc_register_codec(dev, &pm8916_wcd_analog,
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 5/6] ASoC: qcom: apq8016-sbc: Add support to Headset JACK
  2017-08-02 17:09 ` srinivas.kandagatla
@ 2017-08-02 17:09   ` srinivas.kandagatla
  -1 siblings, 0 replies; 22+ messages in thread
From: srinivas.kandagatla @ 2017-08-02 17:09 UTC (permalink / raw)
  To: Mark Brown, Banajit Goswami, alsa-devel
  Cc: damien.riegel, Jaroslav Kysela, Takashi Iwai, Patrick Lai,
	linux-kernel, Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch adds support to Headset JACK, also provides board specific
vref ranges for mbhc buttons to be detected.

This headset supports both 3 pole and 4 pole headset type and 5 buttons.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/qcom/apq8016_sbc.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c
index f07aa1e..e6e632a 100644
--- a/sound/soc/qcom/apq8016_sbc.c
+++ b/sound/soc/qcom/apq8016_sbc.c
@@ -21,12 +21,16 @@
 #include <linux/platform_device.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
+#include <sound/jack.h>
 #include <sound/soc.h>
+#include <uapi/linux/input-event-codes.h>
 #include <dt-bindings/sound/apq8016-lpass.h>
 
 struct apq8016_sbc_data {
 	void __iomem *mic_iomux;
 	void __iomem *spkr_iomux;
+	struct snd_soc_jack jack;
+	bool jack_setup;
 	struct snd_soc_dai_link dai_link[];	/* dynamically allocated */
 };
 
@@ -70,6 +74,31 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
 
 	}
 
+	if (!pdata->jack_setup) {
+		struct snd_jack *jack;
+
+		rval = snd_soc_card_jack_new(card, "Headset Jack",
+					     SND_JACK_HEADSET |
+					     SND_JACK_HEADPHONE |
+					     SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+					     SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+					     SND_JACK_BTN_4,
+					     &pdata->jack, NULL, 0);
+
+		if (rval < 0) {
+			dev_err(card->dev, "Unable to add Headphone Jack\n");
+			return rval;
+		}
+
+		jack = pdata->jack.jack;
+
+		snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_MEDIA);
+		snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+		snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+		snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+		pdata->jack_setup = true;
+	}
+
 	for (i = 0 ; i < dai_link->num_codecs; i++) {
 		struct snd_soc_dai *dai = rtd->codec_dais[i];
 
@@ -81,6 +110,11 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
 			dev_warn(card->dev, "Failed to set mclk: %d\n", rval);
 			return rval;
 		}
+		rval = snd_soc_codec_set_jack(codec, &pdata->jack, NULL);
+		if (rval != 0 && rval != -ENOTSUPP) {
+			dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+			return rval;
+		}
 	}
 
 	return 0;
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 5/6] ASoC: qcom: apq8016-sbc: Add support to Headset JACK
@ 2017-08-02 17:09   ` srinivas.kandagatla
  0 siblings, 0 replies; 22+ messages in thread
From: srinivas.kandagatla @ 2017-08-02 17:09 UTC (permalink / raw)
  To: Mark Brown, Banajit Goswami, alsa-devel
  Cc: damien.riegel, Patrick Lai, linux-kernel, Takashi Iwai,
	Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch adds support to Headset JACK, also provides board specific
vref ranges for mbhc buttons to be detected.

This headset supports both 3 pole and 4 pole headset type and 5 buttons.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/qcom/apq8016_sbc.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c
index f07aa1e..e6e632a 100644
--- a/sound/soc/qcom/apq8016_sbc.c
+++ b/sound/soc/qcom/apq8016_sbc.c
@@ -21,12 +21,16 @@
 #include <linux/platform_device.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
+#include <sound/jack.h>
 #include <sound/soc.h>
+#include <uapi/linux/input-event-codes.h>
 #include <dt-bindings/sound/apq8016-lpass.h>
 
 struct apq8016_sbc_data {
 	void __iomem *mic_iomux;
 	void __iomem *spkr_iomux;
+	struct snd_soc_jack jack;
+	bool jack_setup;
 	struct snd_soc_dai_link dai_link[];	/* dynamically allocated */
 };
 
@@ -70,6 +74,31 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
 
 	}
 
+	if (!pdata->jack_setup) {
+		struct snd_jack *jack;
+
+		rval = snd_soc_card_jack_new(card, "Headset Jack",
+					     SND_JACK_HEADSET |
+					     SND_JACK_HEADPHONE |
+					     SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+					     SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+					     SND_JACK_BTN_4,
+					     &pdata->jack, NULL, 0);
+
+		if (rval < 0) {
+			dev_err(card->dev, "Unable to add Headphone Jack\n");
+			return rval;
+		}
+
+		jack = pdata->jack.jack;
+
+		snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_MEDIA);
+		snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+		snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+		snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+		pdata->jack_setup = true;
+	}
+
 	for (i = 0 ; i < dai_link->num_codecs; i++) {
 		struct snd_soc_dai *dai = rtd->codec_dais[i];
 
@@ -81,6 +110,11 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
 			dev_warn(card->dev, "Failed to set mclk: %d\n", rval);
 			return rval;
 		}
+		rval = snd_soc_codec_set_jack(codec, &pdata->jack, NULL);
+		if (rval != 0 && rval != -ENOTSUPP) {
+			dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+			return rval;
+		}
 	}
 
 	return 0;
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 6/6] arm64: dts: apq8016-sbc: add mbhc buttons support
  2017-08-02 17:09 ` srinivas.kandagatla
@ 2017-08-02 17:09   ` srinivas.kandagatla
  -1 siblings, 0 replies; 22+ messages in thread
From: srinivas.kandagatla @ 2017-08-02 17:09 UTC (permalink / raw)
  To: Mark Brown, Banajit Goswami, alsa-devel
  Cc: damien.riegel, Jaroslav Kysela, Takashi Iwai, Patrick Lai,
	linux-kernel, Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
index 0981efc..bce458c 100644
--- a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
@@ -387,6 +387,8 @@
         status = "okay";
         clocks = <&gcc GCC_CODEC_DIGCODEC_CLK>;
         clock-names = "mclk";
+	qcom,mbhc-vthreshold-low = <75 150 237 450 500>;
+	qcom,mbhc-vthreshold-high = <75 150 237 450 500>;
 };
 
 &spmi_pon {
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v2 6/6] arm64: dts: apq8016-sbc: add mbhc buttons support
@ 2017-08-02 17:09   ` srinivas.kandagatla
  0 siblings, 0 replies; 22+ messages in thread
From: srinivas.kandagatla @ 2017-08-02 17:09 UTC (permalink / raw)
  To: Mark Brown, Banajit Goswami, alsa-devel
  Cc: damien.riegel, Patrick Lai, linux-kernel, Takashi Iwai,
	Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
index 0981efc..bce458c 100644
--- a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi
@@ -387,6 +387,8 @@
         status = "okay";
         clocks = <&gcc GCC_CODEC_DIGCODEC_CLK>;
         clock-names = "mclk";
+	qcom,mbhc-vthreshold-low = <75 150 237 450 500>;
+	qcom,mbhc-vthreshold-high = <75 150 237 450 500>;
 };
 
 &spmi_pon {
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 4/6] ASoC: codecs: msm8916-wcd-analog: add MBHC support
  2017-08-02 17:09   ` srinivas.kandagatla
@ 2017-08-02 23:33     ` Damien Riegel
  -1 siblings, 0 replies; 22+ messages in thread
From: Damien Riegel @ 2017-08-02 23:33 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: Mark Brown, Banajit Goswami, alsa-devel, Jaroslav Kysela,
	Takashi Iwai, Patrick Lai, linux-kernel

Hi Srinivas,


On Wed, Aug 02, 2017 at 07:09:28PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> 
> MBHC (MultiButton Headset Control) support is available in pm8921 in two
> blocks, one to detect mechanical headset insertion and removal and other
> block to support headset type detection and 5 button detection and othe
> features like impedance calculation.
> 
> This patch adds support to:
> 1> Support to NC and NO type of headset Jacks.
> 2> Mechanical insertion and detection of headset jack.
> 3> Detect a 3 pole Headphone and a 4 pole Headset.
> 4> Detect 5 buttons.
> 
> Tested it on DB410c with Audio Mezz board with 4 pole and 3 pole
> headset/headphones.

Good news! This version has better results. I still have an issue where
a headphone is reported as a headset when I do this sequence:
	1. connect headset
	2. disconnect headset
	3. connect headphone
Following headphone connections/disconnections are reported correctly.
Note that I don't press the media key, it's wrongly detected when I pull
off the cable.

evtest logs:

	Plugging headset:
Event: time 10181.936746, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1
Event: time 10181.936746, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 1
Event: time 10181.936746, -------------- SYN_REPORT ------------
	Unplugging headset:
Event: time 10183.432797, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 1
Event: time 10183.432797, -------------- SYN_REPORT ------------
Event: time 10183.871029, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 0
Event: time 10183.871029, -------------- SYN_REPORT ------------
Event: time 10183.971521, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 1
Event: time 10183.971521, -------------- SYN_REPORT ------------
Event: time 10184.249429, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0
Event: time 10184.249429, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 0
Event: time 10184.249429, -------------- SYN_REPORT ------------
	Plugin headphone:
Event: time 10190.033748, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1
Event: time 10190.033748, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 1
Event: time 10190.033748, -------------- SYN_REPORT ------------
	Unplugging headphone:
Event: time 10191.267473, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 0
Event: time 10191.267473, -------------- SYN_REPORT ------------
Event: time 10191.548238, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0
Event: time 10191.548238, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 0
Event: time 10191.548238, -------------- SYN_REPORT ------------
	Plugging headphone:
Event: time 10194.342217, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1
Event: time 10194.342217, -------------- SYN_REPORT ------------
	Unplugging headphone:
Event: time 10195.446049, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0
Event: time 10195.446049, -------------- SYN_REPORT -----------


Also, the microphone is not reported when cable is connected and a
button is pressed down. That might be a bit of a corner case, so I don't
think it's worth complexifying the driver to handle that.

A few additional comments inline.


> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  .../bindings/sound/qcom,msm8916-wcd-analog.txt     |  17 +-
>  sound/soc/codecs/msm8916-wcd-analog.c              | 378 +++++++++++++++++++++
>  2 files changed, 394 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
> index 05b67a1..38668ed 100644
> --- a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
> +++ b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
> @@ -31,9 +31,22 @@ Required properties
>   - vdd-cdc-io-supply: phandle to VDD_CDC_IO regulator DT node.
>   - vdd-cdc-tx-rx-cx-supply: phandle to VDD_CDC_TX/RX/CX regulator DT node.
>   - vdd-micbias-supply: phandle of VDD_MICBIAS supply's regulator DT node.
> -
>  Optional Properties:
> + - qcom,mbhc-vthreshold-low: Array of 5 threshold voltages in mV for 5 buttons
> + 			      detection on headset when the mbhc is powered up
   ^
   git am complains about the leading space

> +			      by internal current source, this is a low power.
> + - qcom,mbhc-vthreshold-high: Array of 5 thresold voltages in mV for 5 buttons
> + 			       detection on headset when mbhc is powered up
   ^
   same here

> diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
> index dec4978..04a2112 100644
> --- a/sound/soc/codecs/msm8916-wcd-analog.c
> +++ b/sound/soc/codecs/msm8916-wcd-analog.c

[...]

> +int btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 |
> +	       SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4;
> +int hs_jack_mask = SND_JACK_HEADPHONE | SND_JACK_HEADSET;
> +

I think they're missing a 'static' (or should they be macros?).

> +#define MBHC_MAX_BUTTONS	(5)
> +
>  struct pm8916_wcd_analog_priv {
>  	u16 pmic_rev;
>  	u16 codec_version;
> +	int	mbhc_sw_irq;
> +	int	mbhc_btn_press_irq;
> +	int	mbhc_btn_release_irq;

Is is worth storing these irqs as these values are not used outside of
pm8916_wcd_analog_spmi_probe?

> +	bool	mbhc_btn_enabled;
> +	/* special event to detect accessory type */
> +	bool	mbhc_btn0_pressed;
> +	bool	detect_accessory_type;
>  	struct clk *mclk;
> +	struct snd_soc_codec *codec;
>  	struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
> +	struct snd_soc_jack *jack;
> +	bool hphl_jack_type_normally_open;
> +	bool gnd_jack_type_normally_open;
> +	/* Voltage threshold when internal current source of 100uA is used */
> +	u32 vref_btn_cs[MBHC_MAX_BUTTONS];
> +	/* Voltage threshold when microphone bias is ON */
> +	u32 vref_btn_micb[MBHC_MAX_BUTTONS];
>  	unsigned int micbias1_cap_mode;
>  	unsigned int micbias2_cap_mode;
>  	unsigned int micbias_mv;

[...]

> +static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv,
> +				      bool micbias2_enabled)
> +{
> +	struct snd_soc_codec *codec = priv->codec;
> +	u32 coarse, fine, reg_val, reg_addr;
> +	int *vrefs, i;
> +
> +	if (!micbias2_enabled) { /* use internal 100uA Current source */
> +		/* Enable internal 2.2k Internal Rbias Resistor */
> +		snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS,
> +				    MICB_1_INT_TX2_INT_RBIAS_EN_MASK,
> +				    MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE);
> +		/* Remove pull down on MIC BIAS2 */
> +		snd_soc_update_bits(codec, CDC_A_MICB_2_EN,
> +				   CDC_A_MICB_2_PULL_DOWN_EN_MASK,
> +				   0);
> +		/* enable 100uA internal current source */
> +		snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
> +				    CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK,
> +				    CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA);
> +	}
> +	snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
> +			CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK,
> +			CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN);

Maybe test the value of priv->mbhc_btn_enabled here, otherwise it will
use erroneous values.

> +
> +	if (micbias2_enabled)
> +		vrefs = &priv->vref_btn_micb[0];
> +	else
> +		vrefs = &priv->vref_btn_cs[0];
> +
> +	/* program vref ranges for all the buttons */
> +	reg_addr = CDC_A_MBHC_BTN0_ZDET_CTL_0;
> +	for (i = 0; i <  MBHC_MAX_BUTTONS; i++) {
> +		/* split mv in to coarse parts of 100mv & fine parts of 12mv */
> +		coarse = (vrefs[i] / 100);
> +		fine = ((vrefs[i] % 100) / 12);
> +		reg_val = (coarse << CDC_A_MBHC_BTN_VREF_COARSE_SHIFT) |
> +			 (fine << CDC_A_MBHC_BTN_VREF_FINE_SHIFT);
> +		snd_soc_update_bits(codec, reg_addr,
> +			       CDC_A_MBHC_BTN_VREF_MASK,
> +			       reg_val);
> +		reg_addr++;
> +	}
> +
> +	return 0;
> +}

[...]

> +static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
> +{
> +	struct pm8916_wcd_analog_priv *priv = arg;
> +	struct snd_soc_codec *codec = priv->codec;
> +	bool micbias_enabled = false;

micbias_enabled can be moved to the ins case as it's only used there.

> +	bool ins = false;
> +
> +	if (snd_soc_read(codec, CDC_A_MBHC_DET_CTL_1) &
> +				CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK)
> +		ins = true;
> +
> +	/* Set the detection type appropriately */
> +	snd_soc_update_bits(codec, CDC_A_MBHC_DET_CTL_1,
> +			    CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK,
> +			    (!ins << CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT));
> +
> +
> +	if (ins) { /* hs insertion */
> +		if (snd_soc_read(codec, CDC_A_MICB_2_EN) &
> +				CDC_A_MICB_2_EN_ENABLE)
> +			micbias_enabled = true;
> +
> +		pm8916_mbhc_configure_bias(priv, micbias_enabled);
> +
> +		/*
> +		 * if only a btn0 press event is receive just before
> +		 * insert event then its a 3 pole headphone else if
> +		 * both press and release event received then its
> +		 * a headset.
> +		 */
> +		if (priv->mbhc_btn0_pressed)
> +			snd_soc_jack_report(priv->jack,
> +					    SND_JACK_HEADPHONE, hs_jack_mask);
> +		else
> +			snd_soc_jack_report(priv->jack,
> +					    SND_JACK_HEADSET, hs_jack_mask);
> +
> +		priv->detect_accessory_type = false;
> +
> +	} else { /* removal */
> +		snd_soc_jack_report(priv->jack, 0, hs_jack_mask);
> +		priv->detect_accessory_type = true;
> +	}
> +
> +	return IRQ_HANDLED;
> +}

[...]

> @@ -789,6 +1085,7 @@ static struct snd_soc_codec_driver pm8916_wcd_analog = {
>  static int pm8916_wcd_analog_parse_dt(struct device *dev,
>  				       struct pm8916_wcd_analog_priv *priv)
>  {
> +	int rval;
>  
>  	if (of_property_read_bool(dev->of_node, "qcom,micbias1-ext-cap"))
>  		priv->micbias1_cap_mode = MICB_1_EN_EXT_BYP_CAP;
> @@ -803,6 +1100,34 @@ static int pm8916_wcd_analog_parse_dt(struct device *dev,
>  	of_property_read_u32(dev->of_node, "qcom,micbias-lvl",
>  			     &priv->micbias_mv);
>  
> +	if (of_property_read_bool(dev->of_node,
> +				  "qcom,hphl-jack-type-normally-open"))
> +		priv->hphl_jack_type_normally_open = true;
> +	else
> +		priv->hphl_jack_type_normally_open = false;
> +
> +	if (of_property_read_bool(dev->of_node,
> +				  "qcom,gnd-jack-type-normally-open"))
> +		priv->gnd_jack_type_normally_open = true;
> +	else
> +		priv->gnd_jack_type_normally_open = false;
> +
> +	priv->mbhc_btn_enabled = true;
> +	rval = of_property_read_u32_array(dev->of_node,
> +					  "qcom,mbhc-vthreshold-low",
> +					  &priv->vref_btn_cs[0],
> +					  MBHC_MAX_BUTTONS);
> +	if (rval < 0)
> +		priv->mbhc_btn_enabled = false;

You should add brackets and maybe move the error message here (see my
comment at the end).

> +	else {
> +		rval = of_property_read_u32_array(dev->of_node,
> +						  "qcom,mbhc-vthreshold-high",
> +						  &priv->vref_btn_micb[0],
> +						  MBHC_MAX_BUTTONS);
> +		if (rval < 0)
> +			priv->mbhc_btn_enabled = false;
> +	}
> +
>  	return 0;
>  }
>  
> @@ -842,6 +1167,59 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  
> +	priv->mbhc_sw_irq = platform_get_irq_byname(pdev, "mbhc_switch_int");
> +	if (priv->mbhc_sw_irq < 0) {
> +		dev_err(dev, "failed to get mbhc switch irq\n");
> +		return priv->mbhc_sw_irq;
> +	}
> +
> +	if (priv->mbhc_btn_enabled) {
> +		int irq;
> +
> +		irq = platform_get_irq_byname(pdev, "mbhc_but_press_det");
> +		if (irq < 0) {
> +			dev_err(dev, "failed to get button press irq\n");
> +			return irq;
> +		}
> +
> +		priv->mbhc_btn_press_irq = irq;
> +
> +		irq = platform_get_irq_byname(pdev, "mbhc_but_rel_det");
> +		if (irq < 0) {
> +			dev_err(dev, "failed to get button release irq\n");
> +			return irq;
> +		}
> +
> +		priv->mbhc_btn_release_irq = irq;
> +
> +		ret = devm_request_irq(dev, priv->mbhc_btn_release_irq,
> +				       mbhc_btn_release_irq_handler,
> +				       IRQF_TRIGGER_RISING |
> +				       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +				       "mbhc btn release irq", priv);
> +		if (ret)
> +			dev_err(dev, "cannot request mbhc button release irq\n");
> +
> +		ret = devm_request_irq(dev, priv->mbhc_btn_press_irq,
> +				       mbhc_btn_press_irq_handler,
> +				       IRQF_TRIGGER_RISING |
> +				       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +				       "mbhc btn press irq", priv);
> +		if (ret)
> +			dev_err(dev, "cannot request mbhc button press irq\n");
> +	} else {
> +		dev_err(dev, "MBHC button detection disabled\n");

imho, if you want to print an error message if this feature is
unavailable, you should do that when parsing properties and let the
users know which missing property caused the error.


Regards,
-- 
Damien

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 4/6] ASoC: codecs: msm8916-wcd-analog: add MBHC support
@ 2017-08-02 23:33     ` Damien Riegel
  0 siblings, 0 replies; 22+ messages in thread
From: Damien Riegel @ 2017-08-02 23:33 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: alsa-devel, Banajit Goswami, linux-kernel, Patrick Lai,
	Takashi Iwai, Mark Brown

Hi Srinivas,


On Wed, Aug 02, 2017 at 07:09:28PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> 
> MBHC (MultiButton Headset Control) support is available in pm8921 in two
> blocks, one to detect mechanical headset insertion and removal and other
> block to support headset type detection and 5 button detection and othe
> features like impedance calculation.
> 
> This patch adds support to:
> 1> Support to NC and NO type of headset Jacks.
> 2> Mechanical insertion and detection of headset jack.
> 3> Detect a 3 pole Headphone and a 4 pole Headset.
> 4> Detect 5 buttons.
> 
> Tested it on DB410c with Audio Mezz board with 4 pole and 3 pole
> headset/headphones.

Good news! This version has better results. I still have an issue where
a headphone is reported as a headset when I do this sequence:
	1. connect headset
	2. disconnect headset
	3. connect headphone
Following headphone connections/disconnections are reported correctly.
Note that I don't press the media key, it's wrongly detected when I pull
off the cable.

evtest logs:

	Plugging headset:
Event: time 10181.936746, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1
Event: time 10181.936746, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 1
Event: time 10181.936746, -------------- SYN_REPORT ------------
	Unplugging headset:
Event: time 10183.432797, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 1
Event: time 10183.432797, -------------- SYN_REPORT ------------
Event: time 10183.871029, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 0
Event: time 10183.871029, -------------- SYN_REPORT ------------
Event: time 10183.971521, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 1
Event: time 10183.971521, -------------- SYN_REPORT ------------
Event: time 10184.249429, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0
Event: time 10184.249429, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 0
Event: time 10184.249429, -------------- SYN_REPORT ------------
	Plugin headphone:
Event: time 10190.033748, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1
Event: time 10190.033748, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 1
Event: time 10190.033748, -------------- SYN_REPORT ------------
	Unplugging headphone:
Event: time 10191.267473, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 0
Event: time 10191.267473, -------------- SYN_REPORT ------------
Event: time 10191.548238, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0
Event: time 10191.548238, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 0
Event: time 10191.548238, -------------- SYN_REPORT ------------
	Plugging headphone:
Event: time 10194.342217, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1
Event: time 10194.342217, -------------- SYN_REPORT ------------
	Unplugging headphone:
Event: time 10195.446049, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0
Event: time 10195.446049, -------------- SYN_REPORT -----------


Also, the microphone is not reported when cable is connected and a
button is pressed down. That might be a bit of a corner case, so I don't
think it's worth complexifying the driver to handle that.

A few additional comments inline.


> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  .../bindings/sound/qcom,msm8916-wcd-analog.txt     |  17 +-
>  sound/soc/codecs/msm8916-wcd-analog.c              | 378 +++++++++++++++++++++
>  2 files changed, 394 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
> index 05b67a1..38668ed 100644
> --- a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
> +++ b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
> @@ -31,9 +31,22 @@ Required properties
>   - vdd-cdc-io-supply: phandle to VDD_CDC_IO regulator DT node.
>   - vdd-cdc-tx-rx-cx-supply: phandle to VDD_CDC_TX/RX/CX regulator DT node.
>   - vdd-micbias-supply: phandle of VDD_MICBIAS supply's regulator DT node.
> -
>  Optional Properties:
> + - qcom,mbhc-vthreshold-low: Array of 5 threshold voltages in mV for 5 buttons
> + 			      detection on headset when the mbhc is powered up
   ^
   git am complains about the leading space

> +			      by internal current source, this is a low power.
> + - qcom,mbhc-vthreshold-high: Array of 5 thresold voltages in mV for 5 buttons
> + 			       detection on headset when mbhc is powered up
   ^
   same here

> diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
> index dec4978..04a2112 100644
> --- a/sound/soc/codecs/msm8916-wcd-analog.c
> +++ b/sound/soc/codecs/msm8916-wcd-analog.c

[...]

> +int btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 |
> +	       SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4;
> +int hs_jack_mask = SND_JACK_HEADPHONE | SND_JACK_HEADSET;
> +

I think they're missing a 'static' (or should they be macros?).

> +#define MBHC_MAX_BUTTONS	(5)
> +
>  struct pm8916_wcd_analog_priv {
>  	u16 pmic_rev;
>  	u16 codec_version;
> +	int	mbhc_sw_irq;
> +	int	mbhc_btn_press_irq;
> +	int	mbhc_btn_release_irq;

Is is worth storing these irqs as these values are not used outside of
pm8916_wcd_analog_spmi_probe?

> +	bool	mbhc_btn_enabled;
> +	/* special event to detect accessory type */
> +	bool	mbhc_btn0_pressed;
> +	bool	detect_accessory_type;
>  	struct clk *mclk;
> +	struct snd_soc_codec *codec;
>  	struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
> +	struct snd_soc_jack *jack;
> +	bool hphl_jack_type_normally_open;
> +	bool gnd_jack_type_normally_open;
> +	/* Voltage threshold when internal current source of 100uA is used */
> +	u32 vref_btn_cs[MBHC_MAX_BUTTONS];
> +	/* Voltage threshold when microphone bias is ON */
> +	u32 vref_btn_micb[MBHC_MAX_BUTTONS];
>  	unsigned int micbias1_cap_mode;
>  	unsigned int micbias2_cap_mode;
>  	unsigned int micbias_mv;

[...]

> +static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv,
> +				      bool micbias2_enabled)
> +{
> +	struct snd_soc_codec *codec = priv->codec;
> +	u32 coarse, fine, reg_val, reg_addr;
> +	int *vrefs, i;
> +
> +	if (!micbias2_enabled) { /* use internal 100uA Current source */
> +		/* Enable internal 2.2k Internal Rbias Resistor */
> +		snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS,
> +				    MICB_1_INT_TX2_INT_RBIAS_EN_MASK,
> +				    MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE);
> +		/* Remove pull down on MIC BIAS2 */
> +		snd_soc_update_bits(codec, CDC_A_MICB_2_EN,
> +				   CDC_A_MICB_2_PULL_DOWN_EN_MASK,
> +				   0);
> +		/* enable 100uA internal current source */
> +		snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
> +				    CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK,
> +				    CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA);
> +	}
> +	snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
> +			CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK,
> +			CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN);

Maybe test the value of priv->mbhc_btn_enabled here, otherwise it will
use erroneous values.

> +
> +	if (micbias2_enabled)
> +		vrefs = &priv->vref_btn_micb[0];
> +	else
> +		vrefs = &priv->vref_btn_cs[0];
> +
> +	/* program vref ranges for all the buttons */
> +	reg_addr = CDC_A_MBHC_BTN0_ZDET_CTL_0;
> +	for (i = 0; i <  MBHC_MAX_BUTTONS; i++) {
> +		/* split mv in to coarse parts of 100mv & fine parts of 12mv */
> +		coarse = (vrefs[i] / 100);
> +		fine = ((vrefs[i] % 100) / 12);
> +		reg_val = (coarse << CDC_A_MBHC_BTN_VREF_COARSE_SHIFT) |
> +			 (fine << CDC_A_MBHC_BTN_VREF_FINE_SHIFT);
> +		snd_soc_update_bits(codec, reg_addr,
> +			       CDC_A_MBHC_BTN_VREF_MASK,
> +			       reg_val);
> +		reg_addr++;
> +	}
> +
> +	return 0;
> +}

[...]

> +static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
> +{
> +	struct pm8916_wcd_analog_priv *priv = arg;
> +	struct snd_soc_codec *codec = priv->codec;
> +	bool micbias_enabled = false;

micbias_enabled can be moved to the ins case as it's only used there.

> +	bool ins = false;
> +
> +	if (snd_soc_read(codec, CDC_A_MBHC_DET_CTL_1) &
> +				CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK)
> +		ins = true;
> +
> +	/* Set the detection type appropriately */
> +	snd_soc_update_bits(codec, CDC_A_MBHC_DET_CTL_1,
> +			    CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK,
> +			    (!ins << CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT));
> +
> +
> +	if (ins) { /* hs insertion */
> +		if (snd_soc_read(codec, CDC_A_MICB_2_EN) &
> +				CDC_A_MICB_2_EN_ENABLE)
> +			micbias_enabled = true;
> +
> +		pm8916_mbhc_configure_bias(priv, micbias_enabled);
> +
> +		/*
> +		 * if only a btn0 press event is receive just before
> +		 * insert event then its a 3 pole headphone else if
> +		 * both press and release event received then its
> +		 * a headset.
> +		 */
> +		if (priv->mbhc_btn0_pressed)
> +			snd_soc_jack_report(priv->jack,
> +					    SND_JACK_HEADPHONE, hs_jack_mask);
> +		else
> +			snd_soc_jack_report(priv->jack,
> +					    SND_JACK_HEADSET, hs_jack_mask);
> +
> +		priv->detect_accessory_type = false;
> +
> +	} else { /* removal */
> +		snd_soc_jack_report(priv->jack, 0, hs_jack_mask);
> +		priv->detect_accessory_type = true;
> +	}
> +
> +	return IRQ_HANDLED;
> +}

[...]

> @@ -789,6 +1085,7 @@ static struct snd_soc_codec_driver pm8916_wcd_analog = {
>  static int pm8916_wcd_analog_parse_dt(struct device *dev,
>  				       struct pm8916_wcd_analog_priv *priv)
>  {
> +	int rval;
>  
>  	if (of_property_read_bool(dev->of_node, "qcom,micbias1-ext-cap"))
>  		priv->micbias1_cap_mode = MICB_1_EN_EXT_BYP_CAP;
> @@ -803,6 +1100,34 @@ static int pm8916_wcd_analog_parse_dt(struct device *dev,
>  	of_property_read_u32(dev->of_node, "qcom,micbias-lvl",
>  			     &priv->micbias_mv);
>  
> +	if (of_property_read_bool(dev->of_node,
> +				  "qcom,hphl-jack-type-normally-open"))
> +		priv->hphl_jack_type_normally_open = true;
> +	else
> +		priv->hphl_jack_type_normally_open = false;
> +
> +	if (of_property_read_bool(dev->of_node,
> +				  "qcom,gnd-jack-type-normally-open"))
> +		priv->gnd_jack_type_normally_open = true;
> +	else
> +		priv->gnd_jack_type_normally_open = false;
> +
> +	priv->mbhc_btn_enabled = true;
> +	rval = of_property_read_u32_array(dev->of_node,
> +					  "qcom,mbhc-vthreshold-low",
> +					  &priv->vref_btn_cs[0],
> +					  MBHC_MAX_BUTTONS);
> +	if (rval < 0)
> +		priv->mbhc_btn_enabled = false;

You should add brackets and maybe move the error message here (see my
comment at the end).

> +	else {
> +		rval = of_property_read_u32_array(dev->of_node,
> +						  "qcom,mbhc-vthreshold-high",
> +						  &priv->vref_btn_micb[0],
> +						  MBHC_MAX_BUTTONS);
> +		if (rval < 0)
> +			priv->mbhc_btn_enabled = false;
> +	}
> +
>  	return 0;
>  }
>  
> @@ -842,6 +1167,59 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  
> +	priv->mbhc_sw_irq = platform_get_irq_byname(pdev, "mbhc_switch_int");
> +	if (priv->mbhc_sw_irq < 0) {
> +		dev_err(dev, "failed to get mbhc switch irq\n");
> +		return priv->mbhc_sw_irq;
> +	}
> +
> +	if (priv->mbhc_btn_enabled) {
> +		int irq;
> +
> +		irq = platform_get_irq_byname(pdev, "mbhc_but_press_det");
> +		if (irq < 0) {
> +			dev_err(dev, "failed to get button press irq\n");
> +			return irq;
> +		}
> +
> +		priv->mbhc_btn_press_irq = irq;
> +
> +		irq = platform_get_irq_byname(pdev, "mbhc_but_rel_det");
> +		if (irq < 0) {
> +			dev_err(dev, "failed to get button release irq\n");
> +			return irq;
> +		}
> +
> +		priv->mbhc_btn_release_irq = irq;
> +
> +		ret = devm_request_irq(dev, priv->mbhc_btn_release_irq,
> +				       mbhc_btn_release_irq_handler,
> +				       IRQF_TRIGGER_RISING |
> +				       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +				       "mbhc btn release irq", priv);
> +		if (ret)
> +			dev_err(dev, "cannot request mbhc button release irq\n");
> +
> +		ret = devm_request_irq(dev, priv->mbhc_btn_press_irq,
> +				       mbhc_btn_press_irq_handler,
> +				       IRQF_TRIGGER_RISING |
> +				       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +				       "mbhc btn press irq", priv);
> +		if (ret)
> +			dev_err(dev, "cannot request mbhc button press irq\n");
> +	} else {
> +		dev_err(dev, "MBHC button detection disabled\n");

imho, if you want to print an error message if this feature is
unavailable, you should do that when parsing properties and let the
users know which missing property caused the error.


Regards,
-- 
Damien

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 4/6] ASoC: codecs: msm8916-wcd-analog: add MBHC support
  2017-08-02 23:33     ` Damien Riegel
@ 2017-08-03 11:11       ` Srinivas Kandagatla
  -1 siblings, 0 replies; 22+ messages in thread
From: Srinivas Kandagatla @ 2017-08-03 11:11 UTC (permalink / raw)
  To: Damien Riegel
  Cc: Mark Brown, Banajit Goswami, alsa-devel, Jaroslav Kysela,
	Takashi Iwai, Patrick Lai, linux-kernel



On 03/08/17 00:33, Damien Riegel wrote:
> Hi Srinivas,
> 
> 
> On Wed, Aug 02, 2017 at 07:09:28PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>>
>> MBHC (MultiButton Headset Control) support is available in pm8921 in two
>> blocks, one to detect mechanical headset insertion and removal and other
>> block to support headset type detection and 5 button detection and othe
>> features like impedance calculation.
>>
>> This patch adds support to:
>> 1> Support to NC and NO type of headset Jacks.
>> 2> Mechanical insertion and detection of headset jack.
>> 3> Detect a 3 pole Headphone and a 4 pole Headset.
>> 4> Detect 5 buttons.
>>
>> Tested it on DB410c with Audio Mezz board with 4 pole and 3 pole
>> headset/headphones.
> 

> Good news! This version has better results. I still have an issue where
> a headphone is reported as a headset when I do this sequence:

Hi Damien,

Thanks for testing..


> 	1. connect headset
> 	2. disconnect headset
> 	3. connect headphone

> Following headphone connections/disconnections are reported correctly.
> Note that I don't press the media key, it's wrongly detected when I pull
> off the cable.

If you do step 3 slowly there is a chance that we get KEY_MEDIA events, 
as headphone is a 3 pole, during hp inserting we would end up shorting 
the MIC and GND lines on the connector, as 3-pole connectors have longer 
GND pole/connector length, this action is same as pressing MEDIA KEY.

The logic in the driver uses this MEDIA KEY press release as advantage 
to detect the accessory type before hs insertion, but this logic could 
break if we are inserting headphone slowly, which is not a normal usecase.

Any solution to this issue can be always break if we are doing slow 
insertion of hs or we endup making the driver complex. I will think 
about this once again and see how other drivers handle this usecase.

> 
> evtest logs:
> 
> 	Plugging headset:
> Event: time 10181.936746, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1
> Event: time 10181.936746, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 1
> Event: time 10181.936746, -------------- SYN_REPORT ------------
> 	Unplugging headset:
> Event: time 10183.432797, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 1
> Event: time 10183.432797, -------------- SYN_REPORT ------------
> Event: time 10183.871029, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 0
> Event: time 10183.871029, -------------- SYN_REPORT ------------
> Event: time 10183.971521, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 1
> Event: time 10183.971521, -------------- SYN_REPORT ------------
> Event: time 10184.249429, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0
> Event: time 10184.249429, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 0
> Event: time 10184.249429, -------------- SYN_REPORT ------------
> 	Plugin headphone:
> Event: time 10190.033748, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1
> Event: time 10190.033748, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 1
> Event: time 10190.033748, -------------- SYN_REPORT ------------
> 	Unplugging headphone:
> Event: time 10191.267473, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 0
> Event: time 10191.267473, -------------- SYN_REPORT ------------
> Event: time 10191.548238, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0
> Event: time 10191.548238, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 0
> Event: time 10191.548238, -------------- SYN_REPORT ------------
> 	Plugging headphone:
> Event: time 10194.342217, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1
> Event: time 10194.342217, -------------- SYN_REPORT ------------
> 	Unplugging headphone:
> Event: time 10195.446049, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0
> Event: time 10195.446049, -------------- SYN_REPORT -----------
> 
> 
> Also, the microphone is not reported when cable is connected and a
> button is pressed down. That might be a bit of a corner case, so I don't
> think it's worth complexifying the driver to handle that.
> 
> A few additional comments inline.
> 
> =
>     ^
>     git am complains about the leading space
> 
>> +			      by internal current source, this is a low power.
>> + - qcom,mbhc-vthreshold-high: Array of 5 thresold voltages in mV for 5 buttons
>> + 			       detection on headset when mbhc is powered up
>     ^
>     same here

Yep, I will fix this in next version.
> 
>> diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
>> index dec4978..04a2112 100644
>> --- a/sound/soc/codecs/msm8916-wcd-analog.c
>> +++ b/sound/soc/codecs/msm8916-wcd-analog.c
> 
> [...]
> 
>> +int btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 |
>> +	       SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4;
>> +int hs_jack_mask = SND_JACK_HEADPHONE | SND_JACK_HEADSET;
>> +
> 
> I think they're missing a 'static' (or should they be macros?).
> 
Yep.
>> +#define MBHC_MAX_BUTTONS	(5)
>> +
>>   struct pm8916_wcd_analog_priv {
>>   	u16 pmic_rev;
>>   	u16 codec_version;
>> +	int	mbhc_sw_irq;
>> +	int	mbhc_btn_press_irq;
>> +	int	mbhc_btn_release_irq;
> 
> Is is worth storing these irqs as these values are not used outside of
> pm8916_wcd_analog_spmi_probe?
Will clean this up in next version.
> 
>> +	bool	mbhc_btn_enabled;
>> +	/* special event to detect accessory type */
>> +	bool	mbhc_btn0_pressed;
>> +	bool	detect_accessory_type;
>>   	struct clk *mclk;
>> +	struct snd_soc_codec *codec;
>>   	struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
>> +	struct snd_soc_jack *jack;
>> +	bool hphl_jack_type_normally_open;
>> +	bool gnd_jack_type_normally_open;
>> +	/* Voltage threshold when internal current source of 100uA is used */
>> +	u32 vref_btn_cs[MBHC_MAX_BUTTONS];
>> +	/* Voltage threshold when microphone bias is ON */
>> +	u32 vref_btn_micb[MBHC_MAX_BUTTONS];
>>   	unsigned int micbias1_cap_mode;
>>   	unsigned int micbias2_cap_mode;
>>   	unsigned int micbias_mv;
> 
> [...]
> 
>> +static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv,
>> +				      bool micbias2_enabled)
>> +{
>> +	struct snd_soc_codec *codec = priv->codec;
>> +	u32 coarse, fine, reg_val, reg_addr;
>> +	int *vrefs, i;
>> +
>> +	if (!micbias2_enabled) { /* use internal 100uA Current source */
>> +		/* Enable internal 2.2k Internal Rbias Resistor */
>> +		snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS,
>> +				    MICB_1_INT_TX2_INT_RBIAS_EN_MASK,
>> +				    MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE);
>> +		/* Remove pull down on MIC BIAS2 */
>> +		snd_soc_update_bits(codec, CDC_A_MICB_2_EN,
>> +				   CDC_A_MICB_2_PULL_DOWN_EN_MASK,
>> +				   0);
>> +		/* enable 100uA internal current source */
>> +		snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
>> +				    CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK,
>> +				    CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA);
>> +	}
>> +	snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
>> +			CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK,
>> +			CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN);
> 
> Maybe test the value of priv->mbhc_btn_enabled here, otherwise it will
> use erroneous values.
This should be harmless, as we are not handling any of those interrupts, 
but I will fix this in next version.
> 
>> +
>> +	if (micbias2_enabled)
>> +		vrefs = &priv->vref_btn_micb[0];
>> +	else
>> +		vrefs = &priv->vref_btn_cs[0];
>> +
>> +	/* program vref ranges for all the buttons */
>> +	reg_addr = CDC_A_MBHC_BTN0_ZDET_CTL_0;
>> +	for (i = 0; i <  MBHC_MAX_BUTTONS; i++) {
>> +		/* split mv in to coarse parts of 100mv & fine parts of 12mv */
>> +		coarse = (vrefs[i] / 100);
>> +		fine = ((vrefs[i] % 100) / 12);
>> +		reg_val = (coarse << CDC_A_MBHC_BTN_VREF_COARSE_SHIFT) |
>> +			 (fine << CDC_A_MBHC_BTN_VREF_FINE_SHIFT);
>> +		snd_soc_update_bits(codec, reg_addr,
>> +			       CDC_A_MBHC_BTN_VREF_MASK,
>> +			       reg_val);
>> +		reg_addr++;
>> +	}
>> +
>> +	return 0;
>> +}
> 
> [...]
> 
>> +static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
>> +{
>> +	struct pm8916_wcd_analog_priv *priv = arg;
>> +	struct snd_soc_codec *codec = priv->codec;
>> +	bool micbias_enabled = false;
> 
> micbias_enabled can be moved to the ins case as it's only used there.
> 
okay..

>> +	bool ins = false;
>> +
>> +					  &priv->vref_btn_cs[0],
>> +					  MBHC_MAX_BUTTONS);
>> +	if (rval < 0)
>> +		priv->mbhc_btn_enabled = false;
> 
> You should add brackets and maybe move the error message here (see my
> comment at the end).

Yes, if it make it more readable.
> 
>> +	else {
>> +		rval = of_property_read_u32_array(dev->of_node,
>> +						  "qcom,mbhc-vthreshold-high",
>> +						  &priv->vref_btn_micb[0],
>> +						  MBHC_MAX_BUTTONS);
>> +		if (rval < 0)
>> +			priv->mbhc_btn_enabled = false;
>> +	}
>> +
>>   	return 0;
>>   }

>> +		dev_err(dev, "MBHC button detection disabled\n");
> 
> imho, if you want to print an error message if this feature is
> unavailable, you should do that when parsing properties and let the
> users know which missing property caused the error.
Okay will do that..
> 
> 
> Regards,
> 

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 4/6] ASoC: codecs: msm8916-wcd-analog: add MBHC support
@ 2017-08-03 11:11       ` Srinivas Kandagatla
  0 siblings, 0 replies; 22+ messages in thread
From: Srinivas Kandagatla @ 2017-08-03 11:11 UTC (permalink / raw)
  To: Damien Riegel
  Cc: alsa-devel, Banajit Goswami, linux-kernel, Patrick Lai,
	Takashi Iwai, Mark Brown



On 03/08/17 00:33, Damien Riegel wrote:
> Hi Srinivas,
> 
> 
> On Wed, Aug 02, 2017 at 07:09:28PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>>
>> MBHC (MultiButton Headset Control) support is available in pm8921 in two
>> blocks, one to detect mechanical headset insertion and removal and other
>> block to support headset type detection and 5 button detection and othe
>> features like impedance calculation.
>>
>> This patch adds support to:
>> 1> Support to NC and NO type of headset Jacks.
>> 2> Mechanical insertion and detection of headset jack.
>> 3> Detect a 3 pole Headphone and a 4 pole Headset.
>> 4> Detect 5 buttons.
>>
>> Tested it on DB410c with Audio Mezz board with 4 pole and 3 pole
>> headset/headphones.
> 

> Good news! This version has better results. I still have an issue where
> a headphone is reported as a headset when I do this sequence:

Hi Damien,

Thanks for testing..


> 	1. connect headset
> 	2. disconnect headset
> 	3. connect headphone

> Following headphone connections/disconnections are reported correctly.
> Note that I don't press the media key, it's wrongly detected when I pull
> off the cable.

If you do step 3 slowly there is a chance that we get KEY_MEDIA events, 
as headphone is a 3 pole, during hp inserting we would end up shorting 
the MIC and GND lines on the connector, as 3-pole connectors have longer 
GND pole/connector length, this action is same as pressing MEDIA KEY.

The logic in the driver uses this MEDIA KEY press release as advantage 
to detect the accessory type before hs insertion, but this logic could 
break if we are inserting headphone slowly, which is not a normal usecase.

Any solution to this issue can be always break if we are doing slow 
insertion of hs or we endup making the driver complex. I will think 
about this once again and see how other drivers handle this usecase.

> 
> evtest logs:
> 
> 	Plugging headset:
> Event: time 10181.936746, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1
> Event: time 10181.936746, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 1
> Event: time 10181.936746, -------------- SYN_REPORT ------------
> 	Unplugging headset:
> Event: time 10183.432797, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 1
> Event: time 10183.432797, -------------- SYN_REPORT ------------
> Event: time 10183.871029, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 0
> Event: time 10183.871029, -------------- SYN_REPORT ------------
> Event: time 10183.971521, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 1
> Event: time 10183.971521, -------------- SYN_REPORT ------------
> Event: time 10184.249429, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0
> Event: time 10184.249429, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 0
> Event: time 10184.249429, -------------- SYN_REPORT ------------
> 	Plugin headphone:
> Event: time 10190.033748, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1
> Event: time 10190.033748, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 1
> Event: time 10190.033748, -------------- SYN_REPORT ------------
> 	Unplugging headphone:
> Event: time 10191.267473, type 1 (EV_KEY), code 226 (KEY_MEDIA), value 0
> Event: time 10191.267473, -------------- SYN_REPORT ------------
> Event: time 10191.548238, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0
> Event: time 10191.548238, type 5 (EV_SW), code 4 (SW_MICROPHONE_INSERT), value 0
> Event: time 10191.548238, -------------- SYN_REPORT ------------
> 	Plugging headphone:
> Event: time 10194.342217, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1
> Event: time 10194.342217, -------------- SYN_REPORT ------------
> 	Unplugging headphone:
> Event: time 10195.446049, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0
> Event: time 10195.446049, -------------- SYN_REPORT -----------
> 
> 
> Also, the microphone is not reported when cable is connected and a
> button is pressed down. That might be a bit of a corner case, so I don't
> think it's worth complexifying the driver to handle that.
> 
> A few additional comments inline.
> 
> =
>     ^
>     git am complains about the leading space
> 
>> +			      by internal current source, this is a low power.
>> + - qcom,mbhc-vthreshold-high: Array of 5 thresold voltages in mV for 5 buttons
>> + 			       detection on headset when mbhc is powered up
>     ^
>     same here

Yep, I will fix this in next version.
> 
>> diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
>> index dec4978..04a2112 100644
>> --- a/sound/soc/codecs/msm8916-wcd-analog.c
>> +++ b/sound/soc/codecs/msm8916-wcd-analog.c
> 
> [...]
> 
>> +int btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 |
>> +	       SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4;
>> +int hs_jack_mask = SND_JACK_HEADPHONE | SND_JACK_HEADSET;
>> +
> 
> I think they're missing a 'static' (or should they be macros?).
> 
Yep.
>> +#define MBHC_MAX_BUTTONS	(5)
>> +
>>   struct pm8916_wcd_analog_priv {
>>   	u16 pmic_rev;
>>   	u16 codec_version;
>> +	int	mbhc_sw_irq;
>> +	int	mbhc_btn_press_irq;
>> +	int	mbhc_btn_release_irq;
> 
> Is is worth storing these irqs as these values are not used outside of
> pm8916_wcd_analog_spmi_probe?
Will clean this up in next version.
> 
>> +	bool	mbhc_btn_enabled;
>> +	/* special event to detect accessory type */
>> +	bool	mbhc_btn0_pressed;
>> +	bool	detect_accessory_type;
>>   	struct clk *mclk;
>> +	struct snd_soc_codec *codec;
>>   	struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
>> +	struct snd_soc_jack *jack;
>> +	bool hphl_jack_type_normally_open;
>> +	bool gnd_jack_type_normally_open;
>> +	/* Voltage threshold when internal current source of 100uA is used */
>> +	u32 vref_btn_cs[MBHC_MAX_BUTTONS];
>> +	/* Voltage threshold when microphone bias is ON */
>> +	u32 vref_btn_micb[MBHC_MAX_BUTTONS];
>>   	unsigned int micbias1_cap_mode;
>>   	unsigned int micbias2_cap_mode;
>>   	unsigned int micbias_mv;
> 
> [...]
> 
>> +static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv,
>> +				      bool micbias2_enabled)
>> +{
>> +	struct snd_soc_codec *codec = priv->codec;
>> +	u32 coarse, fine, reg_val, reg_addr;
>> +	int *vrefs, i;
>> +
>> +	if (!micbias2_enabled) { /* use internal 100uA Current source */
>> +		/* Enable internal 2.2k Internal Rbias Resistor */
>> +		snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS,
>> +				    MICB_1_INT_TX2_INT_RBIAS_EN_MASK,
>> +				    MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE);
>> +		/* Remove pull down on MIC BIAS2 */
>> +		snd_soc_update_bits(codec, CDC_A_MICB_2_EN,
>> +				   CDC_A_MICB_2_PULL_DOWN_EN_MASK,
>> +				   0);
>> +		/* enable 100uA internal current source */
>> +		snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
>> +				    CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK,
>> +				    CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA);
>> +	}
>> +	snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
>> +			CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK,
>> +			CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN);
> 
> Maybe test the value of priv->mbhc_btn_enabled here, otherwise it will
> use erroneous values.
This should be harmless, as we are not handling any of those interrupts, 
but I will fix this in next version.
> 
>> +
>> +	if (micbias2_enabled)
>> +		vrefs = &priv->vref_btn_micb[0];
>> +	else
>> +		vrefs = &priv->vref_btn_cs[0];
>> +
>> +	/* program vref ranges for all the buttons */
>> +	reg_addr = CDC_A_MBHC_BTN0_ZDET_CTL_0;
>> +	for (i = 0; i <  MBHC_MAX_BUTTONS; i++) {
>> +		/* split mv in to coarse parts of 100mv & fine parts of 12mv */
>> +		coarse = (vrefs[i] / 100);
>> +		fine = ((vrefs[i] % 100) / 12);
>> +		reg_val = (coarse << CDC_A_MBHC_BTN_VREF_COARSE_SHIFT) |
>> +			 (fine << CDC_A_MBHC_BTN_VREF_FINE_SHIFT);
>> +		snd_soc_update_bits(codec, reg_addr,
>> +			       CDC_A_MBHC_BTN_VREF_MASK,
>> +			       reg_val);
>> +		reg_addr++;
>> +	}
>> +
>> +	return 0;
>> +}
> 
> [...]
> 
>> +static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
>> +{
>> +	struct pm8916_wcd_analog_priv *priv = arg;
>> +	struct snd_soc_codec *codec = priv->codec;
>> +	bool micbias_enabled = false;
> 
> micbias_enabled can be moved to the ins case as it's only used there.
> 
okay..

>> +	bool ins = false;
>> +
>> +					  &priv->vref_btn_cs[0],
>> +					  MBHC_MAX_BUTTONS);
>> +	if (rval < 0)
>> +		priv->mbhc_btn_enabled = false;
> 
> You should add brackets and maybe move the error message here (see my
> comment at the end).

Yes, if it make it more readable.
> 
>> +	else {
>> +		rval = of_property_read_u32_array(dev->of_node,
>> +						  "qcom,mbhc-vthreshold-high",
>> +						  &priv->vref_btn_micb[0],
>> +						  MBHC_MAX_BUTTONS);
>> +		if (rval < 0)
>> +			priv->mbhc_btn_enabled = false;
>> +	}
>> +
>>   	return 0;
>>   }

>> +		dev_err(dev, "MBHC button detection disabled\n");
> 
> imho, if you want to print an error message if this feature is
> unavailable, you should do that when parsing properties and let the
> users know which missing property caused the error.
Okay will do that..
> 
> 
> Regards,
> 

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 4/6] ASoC: codecs: msm8916-wcd-analog: add MBHC support
  2017-08-03 11:11       ` Srinivas Kandagatla
  (?)
@ 2017-08-03 12:53       ` Damien Riegel
  2017-08-03 13:10           ` Srinivas Kandagatla
  -1 siblings, 1 reply; 22+ messages in thread
From: Damien Riegel @ 2017-08-03 12:53 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Banajit Goswami, alsa-devel, Jaroslav Kysela,
	Takashi Iwai, Patrick Lai, linux-kernel

On Thu, Aug 03, 2017 at 12:11:04PM +0100, Srinivas Kandagatla wrote:
> 
> 
> On 03/08/17 00:33, Damien Riegel wrote:
> > Hi Srinivas,
> > 
> > 
> > On Wed, Aug 02, 2017 at 07:09:28PM +0200, srinivas.kandagatla@linaro.org wrote:
> > > From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> > > 
> > > MBHC (MultiButton Headset Control) support is available in pm8921 in two
> > > blocks, one to detect mechanical headset insertion and removal and other
> > > block to support headset type detection and 5 button detection and othe
> > > features like impedance calculation.
> > > 
> > > This patch adds support to:
> > > 1> Support to NC and NO type of headset Jacks.
> > > 2> Mechanical insertion and detection of headset jack.
> > > 3> Detect a 3 pole Headphone and a 4 pole Headset.
> > > 4> Detect 5 buttons.
> > > 
> > > Tested it on DB410c with Audio Mezz board with 4 pole and 3 pole
> > > headset/headphones.
> > 
> 
> > Good news! This version has better results. I still have an issue where
> > a headphone is reported as a headset when I do this sequence:
> 
> Hi Damien,
> 
> Thanks for testing..
> 
> 
> > 	1. connect headset
> > 	2. disconnect headset
> > 	3. connect headphone
> 
> > Following headphone connections/disconnections are reported correctly.
> > Note that I don't press the media key, it's wrongly detected when I pull
> > off the cable.
> 
> If you do step 3 slowly there is a chance that we get KEY_MEDIA events, as
> headphone is a 3 pole, during hp inserting we would end up shorting the MIC
> and GND lines on the connector, as 3-pole connectors have longer GND
> pole/connector length, this action is same as pressing MEDIA KEY.
> 
> The logic in the driver uses this MEDIA KEY press release as advantage to
> detect the accessory type before hs insertion, but this logic could break if
> we are inserting headphone slowly, which is not a normal usecase.
> 
> Any solution to this issue can be always break if we are doing slow
> insertion of hs or we endup making the driver complex. I will think about
> this once again and see how other drivers handle this usecase.

Understood, and you're right we should keep this driver as simple as
possible.

I get the KEY_MEDIA event when doing step 2 (disconnecting headset), and
I understand why it's doing that (and I think it's okay if we don't
filter out that spurious event), but we should at least get a KEY_MEDIA
release event at the same time as SW_{MICROPHONE,HEADPHONE}_INSERT
removal. It doesn't really matter if I disconnect it fast or slowly,
sometimes I'll get the KEY_MEDIA, some other times I won't.

Shouldn't mbhc_btn0_pressed be reset to false when physical removal is
detected? I think that would solve the issue of headphone being detected
as headset.


Regards,
-- 
Damien

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 4/6] ASoC: codecs: msm8916-wcd-analog: add MBHC support
  2017-08-03 12:53       ` Damien Riegel
@ 2017-08-03 13:10           ` Srinivas Kandagatla
  0 siblings, 0 replies; 22+ messages in thread
From: Srinivas Kandagatla @ 2017-08-03 13:10 UTC (permalink / raw)
  To: Damien Riegel
  Cc: Mark Brown, Banajit Goswami, alsa-devel, Jaroslav Kysela,
	Takashi Iwai, Patrick Lai, linux-kernel



On 03/08/17 13:53, Damien Riegel wrote:
> On Thu, Aug 03, 2017 at 12:11:04PM +0100, Srinivas Kandagatla wrote:
>>
>>
>> On 03/08/17 00:33, Damien Riegel wrote:
>>> Hi Srinivas,
>>>
>>>
>>> On Wed, Aug 02, 2017 at 07:09:28PM +0200, srinivas.kandagatla@linaro.org wrote:
>>>> From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>>>>
>>>> MBHC (MultiButton Headset Control) support is available in pm8921 in two
>>>> blocks, one to detect mechanical headset insertion and removal and other
>>>> block to support headset type detection and 5 button detection and othe
>>>> features like impedance calculation.
>>>>
>>>> This patch adds support to:
>>>> 1> Support to NC and NO type of headset Jacks.
>>>> 2> Mechanical insertion and detection of headset jack.
>>>> 3> Detect a 3 pole Headphone and a 4 pole Headset.
>>>> 4> Detect 5 buttons.
>>>>
>>>> Tested it on DB410c with Audio Mezz board with 4 pole and 3 pole
>>>> headset/headphones.
>>>
>>
>>> Good news! This version has better results. I still have an issue where
>>> a headphone is reported as a headset when I do this sequence:
>>
>> Hi Damien,
>>
>> Thanks for testing..
>>
>>
>>> 	1. connect headset
>>> 	2. disconnect headset
>>> 	3. connect headphone
>>
>>> Following headphone connections/disconnections are reported correctly.
>>> Note that I don't press the media key, it's wrongly detected when I pull
>>> off the cable.
>>
>> If you do step 3 slowly there is a chance that we get KEY_MEDIA events, as
>> headphone is a 3 pole, during hp inserting we would end up shorting the MIC
>> and GND lines on the connector, as 3-pole connectors have longer GND
>> pole/connector length, this action is same as pressing MEDIA KEY.
>>
>> The logic in the driver uses this MEDIA KEY press release as advantage to
>> detect the accessory type before hs insertion, but this logic could break if
>> we are inserting headphone slowly, which is not a normal usecase.
>>
>> Any solution to this issue can be always break if we are doing slow
>> insertion of hs or we endup making the driver complex. I will think about
>> this once again and see how other drivers handle this usecase.
> 
> Understood, and you're right we should keep this driver as simple as
> possible.
> 
> I get the KEY_MEDIA event when doing step 2 (disconnecting headset), and
> I understand why it's doing that (and I think it's okay if we don't
> filter out that spurious event), but we should at least get a KEY_MEDIA
> release event at the same time as SW_{MICROPHONE,HEADPHONE}_INSERT
> removal. It doesn't really matter if I disconnect it fast or slowly,
> sometimes I'll get the KEY_MEDIA, some other times I won't.

I will see if I can fix this in a much better way and let you know.

> 
> Shouldn't mbhc_btn0_pressed be reset to false when physical removal is
> detected? I think that would solve the issue of headphone being detected
> as headset.
> 
Yes, I have considered that change to my next version.

> 
> Regards,
> 

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v2 4/6] ASoC: codecs: msm8916-wcd-analog: add MBHC support
@ 2017-08-03 13:10           ` Srinivas Kandagatla
  0 siblings, 0 replies; 22+ messages in thread
From: Srinivas Kandagatla @ 2017-08-03 13:10 UTC (permalink / raw)
  To: Damien Riegel
  Cc: alsa-devel, Banajit Goswami, linux-kernel, Patrick Lai,
	Takashi Iwai, Mark Brown



On 03/08/17 13:53, Damien Riegel wrote:
> On Thu, Aug 03, 2017 at 12:11:04PM +0100, Srinivas Kandagatla wrote:
>>
>>
>> On 03/08/17 00:33, Damien Riegel wrote:
>>> Hi Srinivas,
>>>
>>>
>>> On Wed, Aug 02, 2017 at 07:09:28PM +0200, srinivas.kandagatla@linaro.org wrote:
>>>> From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>>>>
>>>> MBHC (MultiButton Headset Control) support is available in pm8921 in two
>>>> blocks, one to detect mechanical headset insertion and removal and other
>>>> block to support headset type detection and 5 button detection and othe
>>>> features like impedance calculation.
>>>>
>>>> This patch adds support to:
>>>> 1> Support to NC and NO type of headset Jacks.
>>>> 2> Mechanical insertion and detection of headset jack.
>>>> 3> Detect a 3 pole Headphone and a 4 pole Headset.
>>>> 4> Detect 5 buttons.
>>>>
>>>> Tested it on DB410c with Audio Mezz board with 4 pole and 3 pole
>>>> headset/headphones.
>>>
>>
>>> Good news! This version has better results. I still have an issue where
>>> a headphone is reported as a headset when I do this sequence:
>>
>> Hi Damien,
>>
>> Thanks for testing..
>>
>>
>>> 	1. connect headset
>>> 	2. disconnect headset
>>> 	3. connect headphone
>>
>>> Following headphone connections/disconnections are reported correctly.
>>> Note that I don't press the media key, it's wrongly detected when I pull
>>> off the cable.
>>
>> If you do step 3 slowly there is a chance that we get KEY_MEDIA events, as
>> headphone is a 3 pole, during hp inserting we would end up shorting the MIC
>> and GND lines on the connector, as 3-pole connectors have longer GND
>> pole/connector length, this action is same as pressing MEDIA KEY.
>>
>> The logic in the driver uses this MEDIA KEY press release as advantage to
>> detect the accessory type before hs insertion, but this logic could break if
>> we are inserting headphone slowly, which is not a normal usecase.
>>
>> Any solution to this issue can be always break if we are doing slow
>> insertion of hs or we endup making the driver complex. I will think about
>> this once again and see how other drivers handle this usecase.
> 
> Understood, and you're right we should keep this driver as simple as
> possible.
> 
> I get the KEY_MEDIA event when doing step 2 (disconnecting headset), and
> I understand why it's doing that (and I think it's okay if we don't
> filter out that spurious event), but we should at least get a KEY_MEDIA
> release event at the same time as SW_{MICROPHONE,HEADPHONE}_INSERT
> removal. It doesn't really matter if I disconnect it fast or slowly,
> sometimes I'll get the KEY_MEDIA, some other times I won't.

I will see if I can fix this in a much better way and let you know.

> 
> Shouldn't mbhc_btn0_pressed be reset to false when physical removal is
> detected? I think that would solve the issue of headphone being detected
> as headset.
> 
Yes, I have considered that change to my next version.

> 
> Regards,
> 

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Applied "ASoC: qcom: apq8016-sbc: Add support to Headset JACK" to the asoc tree
  2017-08-02 17:09   ` srinivas.kandagatla
@ 2017-08-21 18:33     ` Mark Brown
  -1 siblings, 0 replies; 22+ messages in thread
From: Mark Brown @ 2017-08-21 18:33 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Brown, Mark Brown, Banajit Goswami, alsa-devel,
	damien.riegel, Patrick Lai, linux-kernel, Takashi Iwai,
	alsa-devel

The patch

   ASoC: qcom: apq8016-sbc: Add support to Headset JACK

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From b47b91c8504c62f95ddff2876620d68697927bd4 Mon Sep 17 00:00:00 2001
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Date: Thu, 17 Aug 2017 10:02:11 +0200
Subject: [PATCH] ASoC: qcom: apq8016-sbc: Add support to Headset JACK

This patch adds support to Headset JACK, also provides board specific
vref ranges for mbhc buttons to be detected.

This headset supports both 3 pole and 4 pole headset type and 5 buttons.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 sound/soc/qcom/apq8016_sbc.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c
index 96a079d9f697..d49adc822a11 100644
--- a/sound/soc/qcom/apq8016_sbc.c
+++ b/sound/soc/qcom/apq8016_sbc.c
@@ -21,12 +21,16 @@
 #include <linux/platform_device.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
+#include <sound/jack.h>
 #include <sound/soc.h>
+#include <uapi/linux/input-event-codes.h>
 #include <dt-bindings/sound/apq8016-lpass.h>
 
 struct apq8016_sbc_data {
 	void __iomem *mic_iomux;
 	void __iomem *spkr_iomux;
+	struct snd_soc_jack jack;
+	bool jack_setup;
 	struct snd_soc_dai_link dai_link[];	/* dynamically allocated */
 };
 
@@ -70,6 +74,31 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
 
 	}
 
+	if (!pdata->jack_setup) {
+		struct snd_jack *jack;
+
+		rval = snd_soc_card_jack_new(card, "Headset Jack",
+					     SND_JACK_HEADSET |
+					     SND_JACK_HEADPHONE |
+					     SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+					     SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+					     SND_JACK_BTN_4,
+					     &pdata->jack, NULL, 0);
+
+		if (rval < 0) {
+			dev_err(card->dev, "Unable to add Headphone Jack\n");
+			return rval;
+		}
+
+		jack = pdata->jack.jack;
+
+		snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_MEDIA);
+		snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+		snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+		snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+		pdata->jack_setup = true;
+	}
+
 	for (i = 0 ; i < dai_link->num_codecs; i++) {
 		struct snd_soc_dai *dai = rtd->codec_dais[i];
 
@@ -81,6 +110,11 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
 			dev_warn(card->dev, "Failed to set mclk: %d\n", rval);
 			return rval;
 		}
+		rval = snd_soc_codec_set_jack(codec, &pdata->jack, NULL);
+		if (rval != 0 && rval != -ENOTSUPP) {
+			dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+			return rval;
+		}
 	}
 
 	return 0;
-- 
2.13.3

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Applied "ASoC: qcom: apq8016-sbc: Add support to Headset JACK" to the asoc tree
@ 2017-08-21 18:33     ` Mark Brown
  0 siblings, 0 replies; 22+ messages in thread
From: Mark Brown @ 2017-08-21 18:33 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: alsa-devel, Banajit Goswami, damien.riegel, Patrick Lai,
	linux-kernel, Takashi Iwai, Mark Brown

The patch

   ASoC: qcom: apq8016-sbc: Add support to Headset JACK

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From b47b91c8504c62f95ddff2876620d68697927bd4 Mon Sep 17 00:00:00 2001
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Date: Thu, 17 Aug 2017 10:02:11 +0200
Subject: [PATCH] ASoC: qcom: apq8016-sbc: Add support to Headset JACK

This patch adds support to Headset JACK, also provides board specific
vref ranges for mbhc buttons to be detected.

This headset supports both 3 pole and 4 pole headset type and 5 buttons.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 sound/soc/qcom/apq8016_sbc.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c
index 96a079d9f697..d49adc822a11 100644
--- a/sound/soc/qcom/apq8016_sbc.c
+++ b/sound/soc/qcom/apq8016_sbc.c
@@ -21,12 +21,16 @@
 #include <linux/platform_device.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
+#include <sound/jack.h>
 #include <sound/soc.h>
+#include <uapi/linux/input-event-codes.h>
 #include <dt-bindings/sound/apq8016-lpass.h>
 
 struct apq8016_sbc_data {
 	void __iomem *mic_iomux;
 	void __iomem *spkr_iomux;
+	struct snd_soc_jack jack;
+	bool jack_setup;
 	struct snd_soc_dai_link dai_link[];	/* dynamically allocated */
 };
 
@@ -70,6 +74,31 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
 
 	}
 
+	if (!pdata->jack_setup) {
+		struct snd_jack *jack;
+
+		rval = snd_soc_card_jack_new(card, "Headset Jack",
+					     SND_JACK_HEADSET |
+					     SND_JACK_HEADPHONE |
+					     SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+					     SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+					     SND_JACK_BTN_4,
+					     &pdata->jack, NULL, 0);
+
+		if (rval < 0) {
+			dev_err(card->dev, "Unable to add Headphone Jack\n");
+			return rval;
+		}
+
+		jack = pdata->jack.jack;
+
+		snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_MEDIA);
+		snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+		snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+		snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+		pdata->jack_setup = true;
+	}
+
 	for (i = 0 ; i < dai_link->num_codecs; i++) {
 		struct snd_soc_dai *dai = rtd->codec_dais[i];
 
@@ -81,6 +110,11 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
 			dev_warn(card->dev, "Failed to set mclk: %d\n", rval);
 			return rval;
 		}
+		rval = snd_soc_codec_set_jack(codec, &pdata->jack, NULL);
+		if (rval != 0 && rval != -ENOTSUPP) {
+			dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+			return rval;
+		}
 	}
 
 	return 0;
-- 
2.13.3

^ permalink raw reply related	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2017-08-21 18:34 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-02 17:09 [PATCH v2 0/6] ASoC: codecs: msm8916-wcd-analog: Add support to MBHC srinivas.kandagatla
2017-08-02 17:09 ` srinivas.kandagatla
2017-08-02 17:09 ` [PATCH v2 1/6] ASoC: jack: fix snd_soc_codec_set_jack return error srinivas.kandagatla
2017-08-02 17:09   ` srinivas.kandagatla
2017-08-02 17:09 ` [PATCH v2 2/6] ASoC: codecs: msm8916-wcd-analog: move codec reset to probe srinivas.kandagatla
2017-08-02 17:09   ` srinivas.kandagatla
2017-08-02 17:09 ` [PATCH v2 3/6] ASoC: codecs: msm8916-wcd-analog: get micbias voltage from dt srinivas.kandagatla
2017-08-02 17:09 ` [PATCH v2 4/6] ASoC: codecs: msm8916-wcd-analog: add MBHC support srinivas.kandagatla
2017-08-02 17:09   ` srinivas.kandagatla
2017-08-02 23:33   ` Damien Riegel
2017-08-02 23:33     ` Damien Riegel
2017-08-03 11:11     ` Srinivas Kandagatla
2017-08-03 11:11       ` Srinivas Kandagatla
2017-08-03 12:53       ` Damien Riegel
2017-08-03 13:10         ` Srinivas Kandagatla
2017-08-03 13:10           ` Srinivas Kandagatla
2017-08-02 17:09 ` [PATCH v2 5/6] ASoC: qcom: apq8016-sbc: Add support to Headset JACK srinivas.kandagatla
2017-08-02 17:09   ` srinivas.kandagatla
2017-08-21 18:33   ` Applied "ASoC: qcom: apq8016-sbc: Add support to Headset JACK" to the asoc tree Mark Brown
2017-08-21 18:33     ` Mark Brown
2017-08-02 17:09 ` [PATCH v2 6/6] arm64: dts: apq8016-sbc: add mbhc buttons support srinivas.kandagatla
2017-08-02 17:09   ` srinivas.kandagatla

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.