Skip to contentSkip to conversation

Enhance imx-wm8962 machine driver

PATCH Superseded Nicolin Chen 2 participants 10 messages Dec 6, 2013

Subsystems (6)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover Enhance imx-wm8962 machine driver

Although there's no direct relationship between any of these three patches, I recommend to apply them in order so as to circumvent merging conflicts.

Nicolin Chen (3):
ASoC: fsl: imx-wm8962: Don't update bias_level in machine driver
ASoC: fsl: imx-wm8962: Grant hw_params/free() permission to control
FLL
ASoC: fsl: imx-wm8962: Add headphone and microphone jack plugin/out
detection

 .../devicetree/bindings/sound/imx-audio-wm8962.txt |   8 +
 Documentation/devicetree/bindings/sound/wm8962.txt |   8 +
 sound/soc/fsl/imx-wm8962.c                         | 361 ++++++++++++++++++---
 3 files changed, 326 insertions(+), 51 deletions(-)
Signature · 1 line
1.8.4
#1 lore raw

PATCH 1/3 ASoC: fsl: imx-wm8962: Don't update bias_level in machine driver

If we update it here, the set_bias_level() of Codec driver won't be normally called and we will then miss some essential procedures in set_bias_level() of the Codec driver. Thus drop it.

---
 sound/soc/fsl/imx-wm8962.c | 2 --
 1 file changed, 2 deletions(-)
sound/soc/fsl/imx-wm8962.c +0 -2
index 61e4885..3fd76bc 100644
@@ -130,8 +130,6 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
 		break;
 	}
 
-	dapm->bias_level = level;
-
 	return 0;
 }
 
Signature · 1 line
1.8.4
#2 lore raw

PATCH 2/3 ASoC: fsl: imx-wm8962: Grant hw_params/free() permission to control FLL

In reply to Nicolin Chen

Previously, we couldn't use hw_params() and hw_free() to open and close FLL becuase there might be a race between two simmultaneous substreams so the FLL configuration would be changed and accordingly mulfunction. Also it wouldn't make sense for bypass path feature of WM8962. So we adopted DAPM way to control it. However, if we want to playback a different sample rate file, we need to wait for DAPM to change its bias_level and reconfigure FLL.

But after we introduced full symmetry protection in the soc-pcm, we don't need to worry about the race any more. And the instance by using hw_params() and hw_free() to control FLL will allow us to support flexible use cases, 'aplay -Dhw:0 44k16bit.wav 48k24bit.wav 32k16bit.wav' for example.

Thus this patch mainly adds FLL configuration code to hw_params/hw_free() so as to enchance the sound card's capability. Meanwhile in order not to break the bypass path feature, we make both set_bias_level() and hw_xxx() ways coexist.

---
 sound/soc/fsl/imx-wm8962.c | 136 ++++++++++++++++++++++++++++-----------------
 1 file changed, 86 insertions(+), 50 deletions(-)
sound/soc/fsl/imx-wm8962.c +86 -50
index 3fd76bc..8baf5d4 100644
@@ -35,6 +35,7 @@ struct imx_wm8962_data {
 	char platform_name[DAI_NAME_SIZE];
 	struct clk *codec_clk;
 	unsigned int clk_frequency;
+	bool fll_lock;
 };
 
 struct imx_priv {
@@ -49,20 +50,93 @@ static const struct snd_soc_dapm_widget imx_wm8962_dapm_widgets[] = {
 	SND_SOC_DAPM_MIC("DMIC", NULL),
 };
 
-static int sample_rate = 44100;
-static snd_pcm_format_t sample_format = SNDRV_PCM_FORMAT_S16_LE;
+static int imx_wm8962_enable_fll(struct snd_soc_dai *codec_dai, u32 sample_rate,
+				snd_pcm_format_t sample_format)
+{
+	struct imx_priv *priv = &card_priv;
+	struct device *dev = &priv->pdev->dev;
+	struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev);
+	u32 freq, ret;
+
+	if (data->fll_lock)
+		return 0;
+
+	data->fll_lock = 1;
+
+	if (sample_format == SNDRV_PCM_FORMAT_S24_LE)
+		freq = sample_rate * 384;
+	else
+		freq = sample_rate * 256;
+
+	ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL, WM8962_FLL_MCLK,
+				  data->clk_frequency, freq);
+	if (ret) {
+		dev_err(dev, "failed to start FLL: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_FLL,
+				     freq, SND_SOC_CLOCK_IN);
+	if (ret)
+		dev_err(dev, "failed to set SYSCLK: %d\n", ret);
+
+	return ret;
+}
+
+static int imx_wm8962_disable_fll(struct snd_soc_dai *codec_dai)
+{
+	struct imx_priv *priv = &card_priv;
+	struct device *dev = &priv->pdev->dev;
+	struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev);
+	int ret;
+
+	if (!data->fll_lock)
+		return 0;
+
+	data->fll_lock = 0;
+
+	/* Switch to MCLK as sysclk once so as to disable FLL */
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
+				     0, SND_SOC_CLOCK_IN);
+	if (ret) {
+		dev_err(dev, "failed to switch away from FLL: %d\n", ret);
+		return ret;
+	}
+
+	/* Disable FLL so that we can reset its output freq later */
+	ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
+				  WM8962_FLL_MCLK, 0, 0);
+	if (ret)
+		dev_err(dev, "failed to stop FLL: %d\n", ret);
+
+	return ret;
+}
 
 static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
 		struct snd_pcm_hw_params *params)
 {
-	sample_rate = params_rate(params);
-	sample_format = params_format(params);
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
 
-	return 0;
+	return imx_wm8962_enable_fll(codec_dai, params_rate(params),
+				     params_format(params));
+}
+
+static int imx_hifi_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	/* Don't diable FLL if still having multiple substreams running */
+	if (codec_dai->active != 1)
+		return 0;
+
+	return imx_wm8962_disable_fll(codec_dai);
 }
 
 static struct snd_soc_ops imx_hifi_ops = {
 	.hw_params = imx_hifi_hw_params,
+	.hw_free = imx_hifi_hw_free,
 };
 
 static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
@@ -70,60 +144,20 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
 					enum snd_soc_bias_level level)
 {
 	struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
-	struct imx_priv *priv = &card_priv;
-	struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev);
-	struct device *dev = &priv->pdev->dev;
-	unsigned int pll_out;
-	int ret;
 
 	if (dapm->dev != codec_dai->dev)
 		return 0;
 
 	switch (level) {
 	case SND_SOC_BIAS_PREPARE:
-		if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
-			if (sample_format == SNDRV_PCM_FORMAT_S24_LE)
-				pll_out = sample_rate * 384;
-			else
-				pll_out = sample_rate * 256;
-
-			ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
-					WM8962_FLL_MCLK, data->clk_frequency,
-					pll_out);
-			if (ret < 0) {
-				dev_err(dev, "failed to start FLL: %d\n", ret);
-				return ret;
-			}
-
-			ret = snd_soc_dai_set_sysclk(codec_dai,
-					WM8962_SYSCLK_FLL, pll_out,
-					SND_SOC_CLOCK_IN);
-			if (ret < 0) {
-				dev_err(dev, "failed to set SYSCLK: %d\n", ret);
-				return ret;
-			}
-		}
+		if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
+			return imx_wm8962_enable_fll(codec_dai, 44100,
+						SNDRV_PCM_FORMAT_S16_LE);
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
-		if (dapm->bias_level == SND_SOC_BIAS_PREPARE) {
-			ret = snd_soc_dai_set_sysclk(codec_dai,
-					WM8962_SYSCLK_MCLK, data->clk_frequency,
-					SND_SOC_CLOCK_IN);
-			if (ret < 0) {
-				dev_err(dev,
-					"failed to switch away from FLL: %d\n",
-					ret);
-				return ret;
-			}
-
-			ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
-					0, 0, 0);
-			if (ret < 0) {
-				dev_err(dev, "failed to stop FLL: %d\n", ret);
-				return ret;
-			}
-		}
+		if (dapm->bias_level == SND_SOC_BIAS_PREPARE)
+			return imx_wm8962_disable_fll(codec_dai);
 		break;
 
 	default:
@@ -141,6 +175,8 @@ static int imx_wm8962_late_probe(struct snd_soc_card *card)
 	struct device *dev = &priv->pdev->dev;
 	int ret;
 
+	data->fll_lock = 0;
+
 	ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
 			data->clk_frequency, SND_SOC_CLOCK_IN);
 	if (ret < 0)
Signature · 1 line
1.8.4
maintainer
#3 lore raw

Re: [PATCH 2/3] ASoC: fsl: imx-wm8962: Grant hw_params/free() permission to control FLL

sound/soc/fsl/imx-wm8962.c static const struct snd_soc_dapm_widget imx_wm8962_dapm_widgets[] = { · line 62

Quoted 10 lines · Nicolin Chen
sound/soc/fsl/imx-wm8962.c +10 -0
+static int imx_wm8962_enable_fll(struct snd_soc_dai *codec_dai, u32 sample_rate,
+				snd_pcm_format_t sample_format)
+{
+	struct imx_priv *priv = &card_priv;
+	struct device *dev = &priv->pdev->dev;
+	struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev);
+	u32 freq, ret;
+
+	if (data->fll_lock)
+		return 0;

Don't we need an actual lock to protect this?

#4 lore raw

Re: [PATCH 2/3] ASoC: fsl: imx-wm8962: Grant hw_params/free() permission to control FLL

Quoted 14 lines · Mark Brown

sound/soc/fsl/imx-wm8962.c static const struct snd_soc_dapm_widget imx_wm8962_dapm_widgets[] = { · line 62

Quoted 10 lines · Nicolin Chen
sound/soc/fsl/imx-wm8962.c +10 -0
+static int imx_wm8962_enable_fll(struct snd_soc_dai *codec_dai, u32 sample_rate,
+				snd_pcm_format_t sample_format)
+{
+	struct imx_priv *priv = &card_priv;
+	struct device *dev = &priv->pdev->dev;
+	struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev);
+	u32 freq, ret;
+
+	if (data->fll_lock)
+		return 0;

Don't we need an actual lock to protect this?

Thank you for the advice, I will revise it.

Best regards,
Nicolin Chen

#5 lore raw

PATCH 3/3 ASoC: fsl: imx-wm8962: Add headphone and microphone jack plugin/out detection

In reply to Nicolin Chen

Some platforms of i.MX series place two GPIOs to detect jack pluging events of headphone and microphone so as to let software switch the output (input) route between headphone (analog microphone) and external speakers (on-board digital microphone).

Therefore, this patch mainly adds jack event detection feature and switchs I/O routes accordingly. Meanwhile, for those monaural input cases, we also turn on the ADC monomix features of WM8962 if needed.

---
 .../devicetree/bindings/sound/imx-audio-wm8962.txt |   8 +
 Documentation/devicetree/bindings/sound/wm8962.txt |   8 +
 sound/soc/fsl/imx-wm8962.c                         | 225 +++++++++++++++++++++
 3 files changed, 241 insertions(+)
Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt +8 -0
index f49450a..5d4222e 100644
@@ -24,6 +24,12 @@ Required properties:
 Note: The AUDMUX port numbering should start at 1, which is consistent with
 hardware manual.
 
+Optional properties:
+- hp-det-gpios : The gpio pin to detect plug in/out event that happens to
+  Headphone jack.
+- mic-det-gpios: The gpio pin to detect plug in/out event that happens to
+  Microphone jack.
+
 Example:
 
 sound {
@@ -43,4 +49,6 @@ sound {
 		"DMICDAT", "DMIC";
 	mux-int-port = <2>;
 	mux-ext-port = <3>;
+	hp-det-gpios = <&gpio7 8 1>;
+	mic-det-gpios = <&gpio1 9 1>;
 };
Documentation/devicetree/bindings/sound/wm8962.txt +8 -0
index 7f82b59..744d148 100644
@@ -13,6 +13,14 @@ Optional properties:
     of R51 (Class D Control 2) gets set, indicating that the speaker is
     in mono mode.
 
+  - amic-mono: This is a boolean property. If present, indicating that the
+    analog micphone is hardware monaural input, the driver would enable the
+    monomix for it.
+
+  - dmic-mono: This is a boolean property. If present, indicating that the
+    digital micphone is hardware monaural input, the driver would enable the
+    monomix for it.
+
   - mic-cfg : Default register value for R48 (Additional Control 4).
     If absent, the default should be the register default.
 
sound/soc/fsl/imx-wm8962.c +225 -0

280 lines not on this page · raw message

Signature · 7 lines
1.8.4


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
maintainer
#6 lore raw

Re: [PATCH 3/3] ASoC: fsl: imx-wm8962: Add headphone and microphone jack plugin/out detection

Quoted 3 lines · Nicolin Chen

Therefore, this patch mainly adds jack event detection feature and switchs I/O routes accordingly. Meanwhile, for those monaural input cases, we also turn on the ADC monomix features of WM8962 if needed.

No, the kernel shouldn't be doing any route switching or other configuration - that should be done by userspace. This is a policy decision, there are situations where for example audio should go to both headphones and speaker when headphones are connected in order to make sure notifications are audible even if headpones have been removed.

Quoted 4 lines
sound/soc/fsl/imx-wm8962.c +4 -0
+	if (hp_status != priv->hp_active_low) {
+		snprintf(buf, 32, "STATE=%d", 2);
+		snd_soc_dapm_disable_pin(&priv->codec->dapm, "Ext Spk");
+		ret = imx_hp_jack_gpio.report;

sound/soc/fsl/imx-wm8962.c struct imx_wm8962_data { · line 109

Quoted 10 lines
sound/soc/fsl/imx-wm8962.c +10 -0
+	} else {
+		snprintf(buf, 32, "STATE=%d", 0);
+		snd_soc_dapm_enable_pin(&priv->codec->dapm, "Ext Spk");
+		ret = 0;
+	}
+
+	envp[0] = "NAME=headphone";
+	envp[1] = buf;
+	envp[2] = NULL;
+	kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp);

What's this uevent trying to do and why is it open coded in the driver?

#7 lore raw

Re: [PATCH 3/3] ASoC: fsl: imx-wm8962: Add headphone and microphone jack plugin/out detection

Quoted 11 lines · Mark Brown
Quoted 3 lines · Nicolin Chen

Therefore, this patch mainly adds jack event detection feature and switchs I/O routes accordingly. Meanwhile, for those monaural input cases, we also turn on the ADC monomix features of WM8962 if needed.

No, the kernel shouldn't be doing any route switching or other configuration - that should be done by userspace. This is a policy decision, there are situations where for example audio should go to both headphones and speaker when headphones are connected in order to make sure notifications are audible even if headpones have been removed.

I see...It seems my solution is too specific. I will re-think about it.

Quoted 8 lines
Quoted 8 lines
sound/soc/fsl/imx-wm8962.c +8 -0
+	if (hp_status != priv->hp_active_low) {
+		snprintf(buf, 32, "STATE=%d", 2);
+		snd_soc_dapm_disable_pin(&priv->codec->dapm, "Ext Spk");
+		ret = imx_hp_jack_gpio.report;
+	} else {
+		snprintf(buf, 32, "STATE=%d", 0);
+		snd_soc_dapm_enable_pin(&priv->codec->dapm, "Ext Spk");
+		ret = 0;
Quoted 8 lines

sound/soc/fsl/imx-wm8962.c struct imx_wm8962_data { · line 109

Quoted 6 lines
sound/soc/fsl/imx-wm8962.c +6 -0
+	}
+
+	envp[0] = "NAME=headphone";
+	envp[1] = buf;
+	envp[2] = NULL;
+	kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp);

What's this uevent trying to do and why is it open coded in the driver?

That's a requirement from Android team of Freescale. By checking this event they can determine whether current output is speaker or headphone so as to place an icon on the notification area.

Sir, if you think this patch is not common enough or non-community style, I can try to cut it down to an common one or entirely drop it.

Looking forward to your reply,
Nicolin Chen

maintainer
#8 lore raw

Re: [PATCH 3/3] ASoC: fsl: imx-wm8962: Add headphone and microphone jack plugin/out detection

Quoted 10 lines · Nicolin Chen
Quoted 1 line · Mark Brown

What's this uevent trying to do and why is it open coded in the driver?

That's a requirement from Android team of Freescale. By checking this event they can determine whether current output is speaker or headphone so as to place an icon on the notification area.

Sir, if you think this patch is not common enough or non-community style, I can try to cut it down to an common one or entirely drop it.

This isn't mainline stuff, this is the Android standard switch class. Someone should really contribute something to Android which backs it onto the back of ALSA jacks. The mainline equivalent is extcon which does have switch class emulation but it's a bit broken in its generation of uevents and is also not yet integrated into the ALSA jack stuff.

The thing to do is ideally integrate extcon with the ALSA jack code and update the Android headset observer to work with extcon (or write a separate extcon based observer).

#9 lore raw

Re: [PATCH 3/3] ASoC: fsl: imx-wm8962: Add headphone and microphone jack plugin/out detection

Quoted 18 lines · Mark Brown
Quoted 10 lines · Nicolin Chen
Quoted 1 line · Mark Brown

What's this uevent trying to do and why is it open coded in the driver?

That's a requirement from Android team of Freescale. By checking this event they can determine whether current output is speaker or headphone so as to place an icon on the notification area.

Sir, if you think this patch is not common enough or non-community style, I can try to cut it down to an common one or entirely drop it.

This isn't mainline stuff, this is the Android standard switch class. Someone should really contribute something to Android which backs it onto the back of ALSA jacks. The mainline equivalent is extcon which does have switch class emulation but it's a bit broken in its generation of uevents and is also not yet integrated into the ALSA jack stuff.

Quoted 3 lines

The thing to do is ideally integrate extcon with the ALSA jack code and update the Android headset observer to work with extcon (or write a separate extcon based observer).

Thank you for the detailed explain. It looks more complicated to me with current code base. I think I should just extract the mainline-style part so it'll make things easy if there's gonna be some plausible integrations afterward.

Thank you indeed,
Nicolin Chen