2024-05-08 11:02:11

by Artur Weber

[permalink] [raw]
Subject: [PATCH v2 0/7] ASoC: samsung: midas-audio: Add GPIO-based headset jack detection

Many of Samsung's Exynos 4 devices share the same midas-audio driver
to handle the codec setup. While most of these devices, including the
Midas itself, use the jack detection provided by the WM8994 driver,
other devices such as the Samsung Galaxy Tab 3 8.0 (lt01) use two GPIOs
and an ADC channel to determine jack insertion, the jack's type, and
button presses (for headsets with volume up/down/play buttons).

In the downstream kernel, this behavior is implemented in the sec-jack
driver[1], and the per-device settings are configured in *-jack.c files
in the mach folder (see e.g. the Tab 3's implementation[2]).

This patchset implements this mechanism in the midas_wm1811.c driver,
and adds new DTS options to allow for its configuration. It also
enables jack detection for the Samsung Galaxy Tab 3 8.0.

A very similar mechanism was already present in the aries_wm8994.c
driver[3]; this implementation heavily borrows from it, though there
are a few extra cleanups as well.

Signed-off-by: Artur Weber <[email protected]>

[1] https://github.com/gr8nole/android_kernel_samsung_smdk4x12/blob/lineage-14.1/drivers/misc/sec_jack.c
[2] https://github.com/gr8nole/android_kernel_samsung_smdk4x12/blob/lineage-14.1/arch/arm/mach-exynos/tab3-jack.c
[3] https://github.com/torvalds/linux/blob/master/sound/soc/samsung/aries_wm8994.c

---
Changes in v2:
- Added vendor prefix to threshold properties
- Added separate headset mic bias regulator
- Changed some cases of dev_err + return with return dev_err_probe
- Added an extra patch to replace some previous dev_err + return cases
with dev_err_probe
- Moved tab3 DTS wm1811 codec config changes to separate commit

---
Artur Weber (7):
ASoC: dt-bindings: samsung,midas-audio: Add headset mic bias supply
ASoC: dt-bindings: samsung,midas-audio: Add GPIO-based headset jack detection
ASoC: samsung: midas_wm1811: Add headset mic bias supply support
ASoC: samsung: midas_wm1811: Add GPIO-based headset jack detection
ASoC: samsung: midas_wm1811: Use dev_err_probe where appropriate
ARM: dts: samsung: exynos4212-tab3: Fix headset mic, add jack detection
ARM: dts: samsung: exynos4212-tab3: Fix up wm1811 codec config

.../bindings/sound/samsung,midas-audio.yaml | 33 ++
arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi | 31 +-
sound/soc/samsung/Kconfig | 2 +-
sound/soc/samsung/midas_wm1811.c | 343 +++++++++++++++++++--
4 files changed, 379 insertions(+), 30 deletions(-)
---
base-commit: e67572cd2204894179d89bd7b984072f19313b03
change-id: 20240502-midas-wm1811-gpio-jack-b10226b17ecc

Best regards,
--
Artur Weber <[email protected]>



2024-05-08 11:02:11

by Artur Weber

[permalink] [raw]
Subject: [PATCH v2 1/7] ASoC: dt-bindings: samsung,midas-audio: Add headset mic bias supply

Some devices use a separate mic bias supply (also referred to as
"ear mic bias") to enable/disable the headset mic.

Add the DT property headset-mic-bias-supply to allow for specifying
this supply.

Signed-off-by: Artur Weber <[email protected]>
---
Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml | 3 +++
1 file changed, 3 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml
index 6ec80f529d84..6ed53dd0bb53 100644
--- a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml
+++ b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml
@@ -53,6 +53,9 @@ properties:
submic-bias-supply:
description: Supply for the micbias on the Sub microphone

+ headset-mic-bias-supply:
+ description: Supply for the micbias on the Headset microphone
+
fm-sel-gpios:
maxItems: 1
description: GPIO pin for FM selection

--
2.45.0


2024-05-08 11:02:27

by Artur Weber

[permalink] [raw]
Subject: [PATCH v2 3/7] ASoC: samsung: midas_wm1811: Add headset mic bias supply support

Some devices use a headset mic bias supply (sometimes referred to as
"ear mic bias") to enable/disable the headset mic.

Add support for getting the supply from DT and setting it up
accordingly to the value of the Headset Mic switch.

Signed-off-by: Artur Weber <[email protected]>
---
Changes in v2:
- Added this commit
---
sound/soc/samsung/midas_wm1811.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c
index f31244156ff6..ab0a4804b45e 100644
--- a/sound/soc/samsung/midas_wm1811.c
+++ b/sound/soc/samsung/midas_wm1811.c
@@ -29,6 +29,7 @@
struct midas_priv {
struct regulator *reg_mic_bias;
struct regulator *reg_submic_bias;
+ struct regulator *reg_headset_mic_bias;
struct gpio_desc *gpio_fm_sel;
struct gpio_desc *gpio_lineout_sel;
unsigned int fll1_rate;
@@ -201,6 +202,25 @@ static int midas_submic_bias(struct snd_soc_dapm_widget *w,
return 0;
}

+static int midas_headset_mic_bias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_card *card = w->dapm->card;
+ struct midas_priv *priv = snd_soc_card_get_drvdata(card);
+
+ if (!priv->reg_headset_mic_bias)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return regulator_enable(priv->reg_headset_mic_bias);
+ case SND_SOC_DAPM_POST_PMD:
+ return regulator_disable(priv->reg_headset_mic_bias);
+ }
+
+ return 0;
+}
+
static int midas_fm_set(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -271,7 +291,7 @@ static const struct snd_soc_dapm_widget midas_dapm_widgets[] = {
SND_SOC_DAPM_LINE("FM In", midas_fm_set),

SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", midas_headset_mic_bias),
SND_SOC_DAPM_MIC("Main Mic", midas_mic_bias),
SND_SOC_DAPM_MIC("Sub Mic", midas_submic_bias),
};
@@ -455,6 +475,17 @@ static int midas_probe(struct platform_device *pdev)
return PTR_ERR(priv->reg_submic_bias);
}

+ priv->reg_headset_mic_bias = devm_regulator_get_optional(dev,
+ "headset-mic-bias");
+ if (IS_ERR(priv->reg_headset_mic_bias)) {
+ ret = PTR_ERR(priv->reg_headset_mic_bias);
+ if (ret == -ENODEV)
+ priv->reg_headset_mic_bias = NULL;
+ else
+ return dev_err_probe(dev, ret,
+ "Failed to get headset mic bias regulator\n");
+ }
+
priv->gpio_fm_sel = devm_gpiod_get_optional(dev, "fm-sel", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpio_fm_sel)) {
dev_err(dev, "Failed to get FM selection GPIO\n");

--
2.45.0


2024-05-08 11:02:46

by Artur Weber

[permalink] [raw]
Subject: [PATCH v2 2/7] ASoC: dt-bindings: samsung,midas-audio: Add GPIO-based headset jack detection

Some Samsung devices that share the midas-audio driver use a GPIO-based
approach to headset jack detection, as opposed to using the built-in
jack detection provided by the wm8994 driver. This setup uses two GPIOs
(one for jack detection and another for key detection) and an ADC
channel for determining the jack type or button pressed.

Add DT configuration values that allow for describing these setups.

Signed-off-by: Artur Weber <[email protected]>
---
Changes in v2:
- Added vendor prefix to threshold properties
- Dropped pipe (|) character from description: field
---
.../bindings/sound/samsung,midas-audio.yaml | 30 ++++++++++++++++++++++
1 file changed, 30 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml
index 6ed53dd0bb53..6b760a73e8bf 100644
--- a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml
+++ b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml
@@ -64,6 +64,36 @@ properties:
maxItems: 1
description: GPIO pin for line out selection

+ headset-detect-gpios:
+ maxItems: 1
+ description: GPIO for detection of headset insertion
+
+ headset-key-gpios:
+ maxItems: 1
+ description: GPIO for detection of headset key press
+
+ io-channels:
+ maxItems: 1
+ description: IO channel to read micbias voltage for headset detection
+
+ io-channel-names:
+ const: headset-detect
+
+ samsung,headset-4pole-threshold-microvolt:
+ minItems: 2
+ maxItems: 2
+ description:
+ Array containing minimum and maximum IO channel value for 4-pole
+ (with microphone/button) headsets. If the IO channel value is
+ outside of this range, a 3-pole headset is assumed.
+
+ samsung,headset-button-threshold-microvolt:
+ minItems: 3
+ maxItems: 3
+ description:
+ Array of minimum (inclusive) IO channel values for headset button
+ detection, in order: "Media", "Volume Up" and "Volume Down".
+
required:
- compatible
- cpu

--
2.45.0


2024-05-08 11:02:55

by Artur Weber

[permalink] [raw]
Subject: [PATCH v2 6/7] ARM: dts: samsung: exynos4212-tab3: Fix headset mic, add jack detection

Set up headset mic bias regulator and add the necessary properties to
the samsung,midas-audio node to allow for headset jack detection.

Signed-off-by: Artur Weber <[email protected]>
---
Changes in v2:
- Added headset mic bias regulator
- Added samsung prefix to threshold properties
- Dropped wm1811 config changes
---
arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)

diff --git a/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi b/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi
index e5254e32aa8f..8dc81112172c 100644
--- a/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi
+++ b/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi
@@ -294,11 +294,30 @@ submic_bias_reg: voltage-regulator-5 {
regulator-max-microvolt = <2800000>;
};

+ earmic_bias_reg: voltage-regulator-6 {
+ compatible = "regulator-fixed";
+ regulator-name = "EAR_MICBIAS_LDO_2.8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ gpio = <&gpm0 0 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
sound: sound {
compatible = "samsung,midas-audio";
model = "TAB3";
mic-bias-supply = <&mic_bias_reg>;
submic-bias-supply = <&submic_bias_reg>;
+ headset-mic-bias-supply = <&earmic_bias_reg>;
+
+ lineout-sel-gpios = <&gpj1 2 GPIO_ACTIVE_HIGH>;
+
+ headset-detect-gpios = <&gpx0 4 GPIO_ACTIVE_LOW>;
+ headset-key-gpios = <&gpx3 6 GPIO_ACTIVE_LOW>;
+ samsung,headset-4pole-threshold-microvolt = <710 2000>;
+ samsung,headset-button-threshold-microvolt = <0 130 260>;
+ io-channel-names = "headset-detect";
+ io-channels = <&adc 0>;

audio-routing = "HP", "HPOUT1L",
"HP", "HPOUT1R",
@@ -345,6 +364,11 @@ wlan_pwrseq: sdhci3-pwrseq {
};
};

+&adc {
+ vdd-supply = <&ldo3_reg>;
+ status = "okay";
+};
+
&bus_acp {
devfreq = <&bus_dmc>;
status = "okay";

--
2.45.0


2024-05-08 11:03:01

by Artur Weber

[permalink] [raw]
Subject: [PATCH v2 7/7] ARM: dts: samsung: exynos4212-tab3: Fix up wm1811 codec config

Drop incorrect interrupt parent and add MCLK2 clock.

Signed-off-by: Artur Weber <[email protected]>
---
Changes in v2:
- Split out wm1811 changes from midas-audio config change patch
---
arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi b/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi
index 8dc81112172c..20e5e7ba6b92 100644
--- a/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi
+++ b/arch/arm/boot/dts/samsung/exynos4212-tab3.dtsi
@@ -529,12 +529,11 @@ &i2c_4 {
wm1811: audio-codec@1a {
compatible = "wlf,wm1811";
reg = <0x1a>;
- clocks = <&pmu_system_controller 0>;
- clock-names = "MCLK1";
+ clocks = <&pmu_system_controller 0>,
+ <&s5m8767_osc S2MPS11_CLK_BT>;
+ clock-names = "MCLK1", "MCLK2";
interrupt-controller;
#interrupt-cells = <2>;
- interrupt-parent = <&gpx3>;
- interrupts = <6 IRQ_TYPE_LEVEL_HIGH>;

gpio-controller;
#gpio-cells = <2>;

--
2.45.0


2024-05-08 11:09:47

by Artur Weber

[permalink] [raw]
Subject: [PATCH v2 4/7] ASoC: samsung: midas_wm1811: Add GPIO-based headset jack detection

Some Samsung devices that use the midas_wm1811 driver use a GPIO-based
method for detecting whether the headset jack is plugged in, as well as
detecting which headset buttons are pressed. There are two GPIOs:
a "headset detect" GPIO responsible for detecting jack insertion, and
a "headset key" GPIO which triggers when a button on the headset is
pressed. The plug type and the button pressed are determined based
on information from an ADC channel. The headset mic is enabled by a
headset mic bias regulator.

Add support for the GPIO-based headset jack detection mechanism,
and make it configurable from the device tree.

This implementation borrows somewhat from the aries_wm8994.c driver,
though there are a few changes to make the code cleaner, and to add
support for DT-based configuration.

Notably, a dependency on IIO is introduced, to accommodate the ADC
reading requirement.

Signed-off-by: Artur Weber <[email protected]>
---
Changes in v2:
- Added separate headset mic bias regulator
- Added samsung preset to threshold properties
- Replaced dev_err+return with return dev_err_probe where needed
---
sound/soc/samsung/Kconfig | 2 +-
sound/soc/samsung/midas_wm1811.c | 286 +++++++++++++++++++++++++++++++++++++--
2 files changed, 277 insertions(+), 11 deletions(-)

diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 93c2b1b08d0a..4b1ea7b2c796 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -140,7 +140,7 @@ config SND_SOC_SAMSUNG_ARIES_WM8994

config SND_SOC_SAMSUNG_MIDAS_WM1811
tristate "SoC I2S Audio support for Midas boards"
- depends on SND_SOC_SAMSUNG
+ depends on SND_SOC_SAMSUNG && IIO
select SND_SAMSUNG_I2S
select SND_SOC_WM8994
help
diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c
index ab0a4804b45e..ce94550ee32e 100644
--- a/sound/soc/samsung/midas_wm1811.c
+++ b/sound/soc/samsung/midas_wm1811.c
@@ -7,7 +7,9 @@

#include <linux/clk.h>
#include <linux/gpio/consumer.h>
+#include <linux/iio/consumer.h>
#include <linux/mfd/wm8994/registers.h>
+#include <linux/input-event-codes.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
@@ -32,6 +34,9 @@ struct midas_priv {
struct regulator *reg_headset_mic_bias;
struct gpio_desc *gpio_fm_sel;
struct gpio_desc *gpio_lineout_sel;
+ struct gpio_desc *gpio_headset_detect;
+ struct gpio_desc *gpio_headset_key;
+ struct iio_channel *adc_headset_detect;
unsigned int fll1_rate;

struct snd_soc_jack headset_jack;
@@ -48,6 +53,131 @@ static struct snd_soc_jack_pin headset_jack_pins[] = {
},
};

+/*
+ * min_mv/max_mv values in this struct are set up based on DT values.
+ */
+static struct snd_soc_jack_zone headset_jack_zones[] = {
+ { .jack_type = SND_JACK_HEADPHONE, },
+ { .jack_type = SND_JACK_HEADSET, },
+ { .jack_type = SND_JACK_HEADPHONE, },
+};
+
+/*
+ * This is used for manual detection in headset_key_check, we reuse the
+ * structure since it's convenient.
+ *
+ * min_mv/max_mv values in this struct are set up based on DT values.
+ */
+static struct snd_soc_jack_zone headset_key_zones[] = {
+ { .jack_type = SND_JACK_BTN_0, }, /* Media */
+ { .jack_type = SND_JACK_BTN_1, }, /* Volume Up */
+ { .jack_type = SND_JACK_BTN_2, }, /* Volume Down */
+};
+
+static int headset_jack_check(void *data)
+{
+ struct midas_priv *priv = (struct midas_priv *) data;
+ int adc, jack_type, ret;
+ int bias_already_on = 0;
+
+ if (!gpiod_get_value_cansleep(priv->gpio_headset_detect))
+ return 0;
+
+ if (priv->reg_headset_mic_bias) {
+ /*
+ * Get state of Headset Mic switch by checking the headset mic
+ * bias regulator
+ */
+ bias_already_on = \
+ regulator_is_enabled(priv->reg_headset_mic_bias);
+
+ /*
+ * If it's not enabled yet, temporarily enable headset mic bias
+ * for ADC measurement
+ */
+ if (bias_already_on < 0)
+ pr_err("%s: Failed to get headset mic bias state: %d",
+ __func__, ret);
+ else if (!bias_already_on) {
+ ret = regulator_enable(priv->reg_headset_mic_bias);
+ if (ret)
+ pr_err("%s: Failed to enable micbias: %d\n",
+ __func__, ret);
+ }
+ }
+
+ /* Sleep for a small amount of time to get the value to stabilize */
+ msleep(20);
+
+ ret = iio_read_channel_processed(priv->adc_headset_detect, &adc);
+ if (ret) {
+ pr_err("%s: Failed to read ADC (%d), assuming headphones\n",
+ __func__, ret);
+ return SND_JACK_HEADPHONE;
+ }
+ pr_debug("%s: ADC value is %d\n", __func__, adc);
+
+ jack_type = snd_soc_jack_get_type(&priv->headset_jack, adc);
+
+ /*
+ * Revert the headset mic bias supply to its previous state
+ * (i.e. if it was disabled before the check, disable it again)
+ */
+ if (priv->reg_headset_mic_bias && bias_already_on == 0) {
+ ret = regulator_disable(priv->reg_headset_mic_bias);
+ if (ret)
+ pr_err("%s: Failed to disable micbias: %d\n",
+ __func__, ret);
+ }
+
+ return jack_type;
+}
+
+static int headset_key_check(void *data)
+{
+ struct midas_priv *priv = (struct midas_priv *) data;
+ int adc, i, ret;
+
+ if (!gpiod_get_value_cansleep(priv->gpio_headset_key))
+ return 0;
+
+ /* Filter out keypresses when 4 pole jack not detected */
+ if (!(priv->headset_jack.status & SND_JACK_MICROPHONE))
+ return 0;
+
+ ret = iio_read_channel_processed(priv->adc_headset_detect, &adc);
+ if (ret) {
+ pr_err("%s: Failed to read ADC (%d), can't detect key type\n",
+ __func__, ret);
+ return 0;
+ }
+ pr_debug("%s: ADC value is %d\n", __func__, adc);
+
+ for (i = 0; i < ARRAY_SIZE(headset_key_zones); i++) {
+ if (adc >= headset_key_zones[i].min_mv &&
+ adc <= headset_key_zones[i].max_mv) {
+ return headset_key_zones[i].jack_type;
+ }
+ }
+
+ return 0;
+}
+
+static struct snd_soc_jack_gpio headset_gpio[] = {
+ {
+ .name = "Headset Jack",
+ .report = SND_JACK_HEADSET,
+ .debounce_time = 150,
+ .jack_status_check = headset_jack_check,
+ },
+ {
+ .name = "Headset Key",
+ .report = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
+ .debounce_time = 30,
+ .jack_status_check = headset_key_check,
+ },
+};
+
static int midas_start_fll1(struct snd_soc_pcm_runtime *rtd, unsigned int rate)
{
struct snd_soc_card *card = rtd->card;
@@ -335,18 +465,67 @@ static int midas_late_probe(struct snd_soc_card *card)
return ret;
}

- ret = snd_soc_card_jack_new_pins(card, "Headset",
- SND_JACK_HEADSET | SND_JACK_MECHANICAL |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 |
- SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5,
- &priv->headset_jack,
- headset_jack_pins,
- ARRAY_SIZE(headset_jack_pins));
- if (ret)
+ if (!priv->gpio_headset_detect) {
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_MECHANICAL |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_BTN_4 | SND_JACK_BTN_5,
+ &priv->headset_jack,
+ headset_jack_pins,
+ ARRAY_SIZE(headset_jack_pins));
+ if (ret)
+ return ret;
+
+ wm8958_mic_detect(aif1_dai->component, &priv->headset_jack,
+ NULL, NULL, NULL, NULL);
+ } else {
+ /* Some devices (n8000, t310) use a GPIO to detect the jack. */
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2,
+ &priv->headset_jack,
+ headset_jack_pins,
+ ARRAY_SIZE(headset_jack_pins));
+ if (ret) {
+ dev_err(card->dev,
+ "Failed to set up headset pins: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_jack_add_zones(&priv->headset_jack,
+ ARRAY_SIZE(headset_jack_zones),
+ headset_jack_zones);
+ if (ret) {
+ dev_err(card->dev,
+ "Failed to set up headset zones: %d\n", ret);
+ return ret;
+ }
+
+ headset_gpio[0].data = priv;
+ headset_gpio[0].desc = priv->gpio_headset_detect;
+
+ headset_gpio[1].data = priv;
+ headset_gpio[1].desc = priv->gpio_headset_key;
+
+ snd_jack_set_key(priv->headset_jack.jack,
+ SND_JACK_BTN_0, KEY_MEDIA);
+ snd_jack_set_key(priv->headset_jack.jack,
+ SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(priv->headset_jack.jack,
+ SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_jack_add_gpios(&priv->headset_jack,
+ ARRAY_SIZE(headset_gpio),
+ headset_gpio);
+ if (ret)
+ dev_err(card->dev,
+ "Failed to set up headset jack GPIOs: %d\n",
+ ret);
+
return ret;
+ }

- wm8958_mic_detect(aif1_dai->component, &priv->headset_jack,
- NULL, NULL, NULL, NULL);
return 0;
}

@@ -453,6 +632,9 @@ static int midas_probe(struct platform_device *pdev)
struct snd_soc_card *card = &midas_card;
struct device *dev = &pdev->dev;
static struct snd_soc_dai_link *dai_link;
+ enum iio_chan_type channel_type;
+ u32 fourpole_threshold[2];
+ u32 button_threshold[3];
struct midas_priv *priv;
int ret, i;

@@ -499,6 +681,90 @@ static int midas_probe(struct platform_device *pdev)
return PTR_ERR(priv->gpio_lineout_sel);
}

+ priv->gpio_headset_detect = devm_gpiod_get_optional(dev,
+ "headset-detect", GPIOD_IN);
+ if (IS_ERR(priv->gpio_headset_detect))
+ return dev_err_probe(dev, PTR_ERR(priv->gpio_headset_detect),
+ "Failed to get headset jack detect GPIO\n");
+
+ if (priv->gpio_headset_detect) {
+ priv->adc_headset_detect = devm_iio_channel_get(dev,
+ "headset-detect");
+ if (IS_ERR(priv->adc_headset_detect))
+ return dev_err_probe(dev,
+ PTR_ERR(priv->adc_headset_detect),
+ "Failed to get ADC channel\n");
+
+ ret = iio_get_channel_type(priv->adc_headset_detect,
+ &channel_type);
+ if (ret) {
+ dev_err(dev, "Failed to get ADC channel type\n");
+ return ret;
+ }
+
+ if (channel_type != IIO_VOLTAGE) {
+ dev_err(dev, "ADC channel is not voltage\n");
+ return ret;
+ }
+
+ priv->gpio_headset_key = devm_gpiod_get(dev, "headset-key",
+ GPIOD_IN);
+ if (IS_ERR(priv->gpio_headset_key))
+ return dev_err_probe(dev,
+ PTR_ERR(priv->gpio_headset_key),
+ "Failed to get headset key GPIO\n");
+
+ ret = of_property_read_u32_array(dev->of_node,
+ "samsung,headset-4pole-threshold-microvolt",
+ fourpole_threshold,
+ ARRAY_SIZE(fourpole_threshold));
+ if (ret) {
+ dev_err(dev, "Failed to get 4-pole jack detection threshold\n");
+ return ret;
+ }
+
+ if (fourpole_threshold[0] > fourpole_threshold[1]) {
+ dev_err(dev, "Invalid 4-pole jack detection threshold value\n");
+ return -EINVAL;
+ }
+
+ headset_jack_zones[0].max_mv = (fourpole_threshold[0]);
+ headset_jack_zones[1].min_mv = (fourpole_threshold[0] + 1);
+
+ headset_jack_zones[1].max_mv = (fourpole_threshold[1]);
+ headset_jack_zones[2].min_mv = (fourpole_threshold[1] + 1);
+
+ ret = of_property_read_u32_array(dev->of_node,
+ "samsung,headset-button-threshold-microvolt",
+ button_threshold,
+ ARRAY_SIZE(button_threshold));
+ if (ret) {
+ dev_err(dev, "Failed to get headset button detection threshold\n");
+ return ret;
+ }
+
+ if (button_threshold[0] > button_threshold[1] ||
+ button_threshold[1] > button_threshold[2]) {
+ dev_err(dev, "Invalid headset button detection threshold value\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (i != 0 && button_threshold[i] <= 0) {
+ dev_err(dev, "Invalid headset button detection threshold value\n");
+ return -EINVAL;
+ }
+
+ headset_key_zones[i].min_mv = button_threshold[i];
+
+ if (i == 2)
+ headset_key_zones[i].max_mv = UINT_MAX;
+ else
+ headset_key_zones[i].max_mv = \
+ (button_threshold[i+1] - 1);
+ }
+ }
+
ret = snd_soc_of_parse_card_name(card, "model");
if (ret < 0) {
dev_err(dev, "Card name is not specified\n");

--
2.45.0


2024-05-08 11:09:49

by Artur Weber

[permalink] [raw]
Subject: [PATCH v2 5/7] ASoC: samsung: midas_wm1811: Use dev_err_probe where appropriate

Since we're already using it in the newly-added GPIO requests for
jack detection, extend it to the previous checks as well.

Signed-off-by: Artur Weber <[email protected]>
---
Changes in v2:
- Added this commit
---
sound/soc/samsung/midas_wm1811.c | 28 ++++++++++++----------------
1 file changed, 12 insertions(+), 16 deletions(-)

diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c
index ce94550ee32e..91f4be98723c 100644
--- a/sound/soc/samsung/midas_wm1811.c
+++ b/sound/soc/samsung/midas_wm1811.c
@@ -646,16 +646,14 @@ static int midas_probe(struct platform_device *pdev)
card->dev = dev;

priv->reg_mic_bias = devm_regulator_get(dev, "mic-bias");
- if (IS_ERR(priv->reg_mic_bias)) {
- dev_err(dev, "Failed to get mic bias regulator\n");
- return PTR_ERR(priv->reg_mic_bias);
- }
+ if (IS_ERR(priv->reg_mic_bias))
+ return dev_err_probe(dev, PTR_ERR(priv->reg_mic_bias),
+ "Failed to get mic bias regulator\n");

priv->reg_submic_bias = devm_regulator_get(dev, "submic-bias");
- if (IS_ERR(priv->reg_submic_bias)) {
- dev_err(dev, "Failed to get submic bias regulator\n");
- return PTR_ERR(priv->reg_submic_bias);
- }
+ if (IS_ERR(priv->reg_submic_bias))
+ return dev_err_probe(dev, PTR_ERR(priv->reg_submic_bias),
+ "Failed to get submic bias regulator\n");

priv->reg_headset_mic_bias = devm_regulator_get_optional(dev,
"headset-mic-bias");
@@ -669,17 +667,15 @@ static int midas_probe(struct platform_device *pdev)
}

priv->gpio_fm_sel = devm_gpiod_get_optional(dev, "fm-sel", GPIOD_OUT_HIGH);
- if (IS_ERR(priv->gpio_fm_sel)) {
- dev_err(dev, "Failed to get FM selection GPIO\n");
- return PTR_ERR(priv->gpio_fm_sel);
- }
+ if (IS_ERR(priv->gpio_fm_sel))
+ return dev_err_probe(dev, PTR_ERR(priv->gpio_fm_sel),
+ "Failed to get FM selection GPIO\n");

priv->gpio_lineout_sel = devm_gpiod_get_optional(dev, "lineout-sel",
GPIOD_OUT_HIGH);
- if (IS_ERR(priv->gpio_lineout_sel)) {
- dev_err(dev, "Failed to get line out selection GPIO\n");
- return PTR_ERR(priv->gpio_lineout_sel);
- }
+ if (IS_ERR(priv->gpio_lineout_sel))
+ return dev_err_probe(dev, PTR_ERR(priv->gpio_lineout_sel),
+ "Failed to get line out selection GPIO\n");

priv->gpio_headset_detect = devm_gpiod_get_optional(dev,
"headset-detect", GPIOD_IN);

--
2.45.0


2024-05-08 12:52:26

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v2 2/7] ASoC: dt-bindings: samsung,midas-audio: Add GPIO-based headset jack detection


On Wed, 08 May 2024 12:58:50 +0200, Artur Weber wrote:
> Some Samsung devices that share the midas-audio driver use a GPIO-based
> approach to headset jack detection, as opposed to using the built-in
> jack detection provided by the wm8994 driver. This setup uses two GPIOs
> (one for jack detection and another for key detection) and an ADC
> channel for determining the jack type or button pressed.
>
> Add DT configuration values that allow for describing these setups.
>
> Signed-off-by: Artur Weber <[email protected]>
> ---
> Changes in v2:
> - Added vendor prefix to threshold properties
> - Dropped pipe (|) character from description: field
> ---
> .../bindings/sound/samsung,midas-audio.yaml | 30 ++++++++++++++++++++++
> 1 file changed, 30 insertions(+)
>

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:
/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml:95:26: [error] syntax error: mapping values are not allowed here (syntax)

dtschema/dtc warnings/errors:
make[2]: *** Deleting file 'Documentation/devicetree/bindings/sound/samsung,midas-audio.example.dts'
Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml:95:26: mapping values are not allowed in this context
make[2]: *** [Documentation/devicetree/bindings/Makefile:26: Documentation/devicetree/bindings/sound/samsung,midas-audio.example.dts] Error 1
make[2]: *** Waiting for unfinished jobs....
/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml:95:26: mapping values are not allowed in this context
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml: ignoring, error parsing file
make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1430: dt_binding_check] Error 2
make: *** [Makefile:240: __sub-make] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/[email protected]

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


2024-05-09 07:13:31

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v2 7/7] ARM: dts: samsung: exynos4212-tab3: Fix up wm1811 codec config

On 08/05/2024 12:58, Artur Weber wrote:
> Drop incorrect interrupt parent and add MCLK2 clock.

1. Separate patches.
2. Missing explanation why. "Incorrect" says a bit, but not too much.
Also, imprecise - you remove all interrupts, not just incorrect parent.

Please provide proper rationale, why this is not correct and information
that bluetooth 32 kHz clock feeds both bluetooth module and audio codec.

>

Best regards,
Krzysztof