[alsa-devel] [PATCH v2] ASoC: Tegra machine ASoC driver for boards using ALC5332 codec
At this stage only Toshiba AC100/Dynabook supported.
Signed-off-by: Leon Romanovsky leon@leon.nu Signed-off-by: Andrey Danin danindrey@mail.ru
-- Changes since v1: * Use module_platform_driver. * Move controls, dapm_widgets and dapm_routes to snd_soc_card structure. * Set dai_fmt in snd_soc_dai_link structure instead probe function. * Remove check board in probe function * Fix warning about unused codec_dai. * Use devm_kzalloc to manage memory allocation. --- sound/soc/tegra/Kconfig | 9 ++ sound/soc/tegra/Makefile | 2 + sound/soc/tegra/tegra_alc5632.c | 233 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+), 0 deletions(-) create mode 100644 sound/soc/tegra/tegra_alc5632.c
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index c6af1fd..ce1b773 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -47,3 +47,12 @@ config SND_SOC_TEGRA_TRIMSLICE help Say Y or M here if you want to add support for SoC audio on the TrimSlice platform. + +config SND_SOC_TEGRA_ALC5632 + tristate "SoC Audio support for Tegra boards using an ALC5632 codec" + depends on SND_SOC_TEGRA && I2C + select SND_SOC_TEGRA_I2S + select SND_SOC_ALC5632 + help + Say Y or M here if you want to add support for SoC audio on the + Toshiba AC100 netbook. diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 4d943b3..8e584b8 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -14,6 +14,8 @@ obj-$(CONFIG_SND_SOC_TEGRA_SPDIF) += snd-soc-tegra-spdif.o # Tegra machine Support snd-soc-tegra-wm8903-objs := tegra_wm8903.o snd-soc-tegra-trimslice-objs := trimslice.o +snd-soc-tegra-alc5632-objs := tegra_alc5632.o
obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o +obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c new file mode 100644 index 0000000..89697c6 --- /dev/null +++ b/sound/soc/tegra/tegra_alc5632.c @@ -0,0 +1,233 @@ +/* +* tegra_alc5632.c -- Toshiba AC100(PAZ00) machine ASoC driver +* +* Copyright (C) 2011 The AC100 Kernel Team ac100@lists.lauchpad.net +* +* Authors: Leon Romanovsky leon@leon.nu +* Andrey Danin danindrey@mail.ru +* Marc Dietrich marvin24@gmx.de +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include <asm/mach-types.h> + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/gpio.h> + +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "../codecs/alc5632.h" + +#include "tegra_das.h" +#include "tegra_i2s.h" +#include "tegra_pcm.h" +#include "tegra_asoc_utils.h" + +#define DRV_NAME "tegra-alc5632" + +struct tegra_alc5632 { + struct tegra_asoc_utils_data util_data; +}; + +static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *card = codec->card; + struct tegra_alc5632 *alc5632 = snd_soc_card_get_drvdata(card); + int srate, mclk; + int err; + + srate = params_rate(params); + switch (srate) { + case 64000: + case 88200: + case 96000: + mclk = 128 * srate; + break; + default: + mclk = 512 * srate; + break; + } + /* FIXME: Codec only requires >= 3MHz if OSR==0 */ + while (mclk < 6000000) + mclk *= 2; + + err = tegra_asoc_utils_set_rate(&alc5632->util_data, srate, mclk); + if (err < 0) { + dev_err(card->dev, "Can't configure clocks\n"); + return err; + } + + err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (err < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return err; + } + + return 0; +} + +static struct snd_soc_ops tegra_alc5632_asoc_ops = { + .hw_params = tegra_alc5632_asoc_hw_params, +}; + +static struct snd_soc_jack tegra_alc5632_hs_jack; + +static struct snd_soc_jack_pin tegra_alc5632_hs_jack_pins[] = { + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headset Stereophone", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static struct snd_soc_jack_gpio tegra_alc5632_hs_jack_gpio = { + .name = "Headset Detect", + .report = SND_JACK_HEADSET, + .debounce_time = 150, +}; + +static const struct snd_soc_dapm_widget tegra_alc5632_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Int Spk", NULL), + SND_SOC_DAPM_HP("Headset Stereophone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route tegra_alc5632_audio_map[] = { + /* Internal Speaker */ + {"Int Spk", NULL, "SPKOUT"}, + {"Int Spk", NULL, "SPKOUTN"}, + + /* Headset Mic */ + {"MIC1", NULL, "MICBIAS1"}, + {"MICBIAS1", NULL, "Headset Mic"}, + + /* Headset Stereophone */ + {"Headset Stereophone", NULL, "HPR"}, + {"Headset Stereophone", NULL, "HPL"}, +}; + +static const struct snd_kcontrol_new tegra_alc5632_controls[] = { + SOC_DAPM_PIN_SWITCH("Int Spk"), +}; + +static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, + &tegra_alc5632_hs_jack); + snd_soc_jack_add_pins(&tegra_alc5632_hs_jack, + ARRAY_SIZE(tegra_alc5632_hs_jack_pins), + tegra_alc5632_hs_jack_pins); + snd_soc_jack_add_gpios(&tegra_alc5632_hs_jack, + 1, &tegra_alc5632_hs_jack_gpio); + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + + return 0; +} + +static struct snd_soc_dai_link tegra_alc5632_dai = { + .name = "ALC5632", + .stream_name = "ALC5632 PCM", + .codec_name = "alc5632.0-001e", + .platform_name = "tegra-pcm-audio", + .cpu_dai_name = "tegra-i2s.0", + .codec_dai_name = "alc5632-hifi", + .init = tegra_alc5632_asoc_init, + .ops = &tegra_alc5632_asoc_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS, +}; + +static struct snd_soc_card snd_soc_tegra_alc5632 = { + .name = "tegra-alc5632", + .dai_link = &tegra_alc5632_dai, + .num_links = 1, + .controls = tegra_alc5632_controls, + .num_controls = ARRAY_SIZE(tegra_alc5632_controls), + .dapm_widgets = tegra_alc5632_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra_alc5632_dapm_widgets), + .dapm_routes = tegra_alc5632_audio_map, + .num_dapm_routes = ARRAY_SIZE(tegra_alc5632_audio_map), + .fully_routed = true, +}; + +static __devinit int tegra_alc5632_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &snd_soc_tegra_alc5632; + struct tegra_alc5632 *alc5632; + int ret; + + alc5632 = devm_kzalloc(&pdev->dev, + sizeof(struct tegra_alc5632), GFP_KERNEL); + if (!alc5632) { + dev_err(&pdev->dev, "Can't allocate tegra_alc5632\n"); + return -ENOMEM; + } + + ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev); + if (ret) + return ret; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, alc5632); + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + tegra_asoc_utils_fini(&alc5632->util_data); + return ret; + } + + return 0; +} + +static int __devexit tegra_alc5632_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_alc5632 *alc5632 = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + + tegra_asoc_utils_fini(&alc5632->util_data); + + return 0; +} + +static struct platform_driver tegra_alc5632_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = tegra_alc5632_probe, + .remove = __devexit_p(tegra_alc5632_remove), +}; +module_platform_driver(tegra_alc5632_driver); + +MODULE_AUTHOR("Leon Romanovsky leon@leon.nu"); +MODULE_DESCRIPTION("Tegra+ALC5632 machine ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME);
Leon Romanovsky wrote at Monday, December 12, 2011 12:46 PM:
At this stage only Toshiba AC100/Dynabook supported.
+static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
...
- srate = params_rate(params);
- switch (srate) {
- case 64000:
- case 88200:
- case 96000:
mclk = 128 * srate;
break;
- default:
mclk = 512 * srate;
break;
- }
- /* FIXME: Codec only requires >= 3MHz if OSR==0 */
- while (mclk < 6000000)
mclk *= 2;
I think you should replace all that with:
mclk = 512 * srate;
The reason being:
* The codec only supports up to 48KHz as far as I can tell, thus making the special-case in the switch statement never fire, and irrelevant.
* The codec wants 512Fs, not min(6MHz, 512Fs) as far as I can tell, so the extra while loop at the end is going to set the wrong rate for 8KHz and 11.025KHz.
+static struct snd_soc_jack_gpio tegra_alc5632_hs_jack_gpio = {
- .name = "Headset Detect",
- .report = SND_JACK_HEADSET,
- .debounce_time = 150,
+};
...
+static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) +{
...
- snd_soc_jack_add_gpios(&tegra_alc5632_hs_jack,
1, &tegra_alc5632_hs_jack_gpio);
Since tegra_alc5632_hs_jack_gpio is never set, doesn't this end up using GPIO 0 as the headset detect, which most likely is something completely unrelated to audio.
Other than those two things, this looks fine to me.
On Mon, Dec 12, 2011 at 22:20, Stephen Warren swarren@nvidia.com wrote:
Leon Romanovsky wrote at Monday, December 12, 2011 12:46 PM:
At this stage only Toshiba AC100/Dynabook supported.
+static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
...
- srate = params_rate(params);
- switch (srate) {
- case 64000:
- case 88200:
- case 96000:
- mclk = 128 * srate;
- break;
- default:
- mclk = 512 * srate;
- break;
- }
- /* FIXME: Codec only requires >= 3MHz if OSR==0 */
- while (mclk < 6000000)
- mclk *= 2;
I think you should replace all that with:
mclk = 512 * srate;
The reason being:
- The codec only supports up to 48KHz as far as I can tell, thus making
the special-case in the switch statement never fire, and irrelevant.
- The codec wants 512Fs, not min(6MHz, 512Fs) as far as I can tell, so
the extra while loop at the end is going to set the wrong rate for 8KHz and 11.025KHz.
You are right, I'll check it at the spec.
+static struct snd_soc_jack_gpio tegra_alc5632_hs_jack_gpio = {
- .name = "Headset Detect",
- .report = SND_JACK_HEADSET,
- .debounce_time = 150,
+};
...
+static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) +{
...
- snd_soc_jack_add_gpios(&tegra_alc5632_hs_jack,
- 1, &tegra_alc5632_hs_jack_gpio);
Since tegra_alc5632_hs_jack_gpio is never set, doesn't this end up using GPIO 0 as the headset detect, which most likely is something completely unrelated to audio.
This is initial support of board file, at the coming patch set, I'll update board-paz00-pinmux.c [1] with audio related pins and I'll add platform device support. I think It is easy for review in this way.
[1] http://git.kernel.org/?p=linux/kernel/git/olof/tegra.git;a=blob;f=arch/arm/m...
Other than those two things, this looks fine to me.
-- nvpublic
Leon Romanovsky wrote at Monday, December 12, 2011 11:36 PM:
On Mon, Dec 12, 2011 at 22:20, Stephen Warren swarren@nvidia.com wrote:
Leon Romanovsky wrote at Monday, December 12, 2011 12:46 PM:
At this stage only Toshiba AC100/Dynabook supported.
...
+static struct snd_soc_jack_gpio tegra_alc5632_hs_jack_gpio = {
- .name = "Headset Detect",
- .report = SND_JACK_HEADSET,
- .debounce_time = 150,
+};
...
+static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) +{
...
- snd_soc_jack_add_gpios(&tegra_alc5632_hs_jack,
- 1, &tegra_alc5632_hs_jack_gpio);
Since tegra_alc5632_hs_jack_gpio is never set, doesn't this end up using GPIO 0 as the headset detect, which most likely is something completely unrelated to audio.
This is initial support of board file, at the coming patch set, I'll update board-paz00-pinmux.c [1] with audio related pins and I'll add platform device support. I think It is easy for review in this way.
I'm fine adding the feature later. But, I think you shouldn't call snd_soc_jack_add_gpios() until the platform data is there, or you'll end up enabling the feature already, but using the wrong GPIO.
On Tue, Dec 13, 2011 at 19:23, Stephen Warren swarren@nvidia.com wrote:
Leon Romanovsky wrote at Monday, December 12, 2011 11:36 PM:
On Mon, Dec 12, 2011 at 22:20, Stephen Warren swarren@nvidia.com wrote:
Leon Romanovsky wrote at Monday, December 12, 2011 12:46 PM:
At this stage only Toshiba AC100/Dynabook supported.
...
+static struct snd_soc_jack_gpio tegra_alc5632_hs_jack_gpio = {
- .name = "Headset Detect",
- .report = SND_JACK_HEADSET,
- .debounce_time = 150,
+};
...
+static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) +{
...
- snd_soc_jack_add_gpios(&tegra_alc5632_hs_jack,
- 1, &tegra_alc5632_hs_jack_gpio);
Since tegra_alc5632_hs_jack_gpio is never set, doesn't this end up using GPIO 0 as the headset detect, which most likely is something completely unrelated to audio.
This is initial support of board file, at the coming patch set, I'll update board-paz00-pinmux.c [1] with audio related pins and I'll add platform device support. I think It is easy for review in this way.
I'm fine adding the feature later. But, I think you shouldn't call snd_soc_jack_add_gpios() until the platform data is there, or you'll end up enabling the feature already, but using the wrong GPIO.
Does it matter ? Anyway, this file is useless without proper audio support, and currently only paz00 will be using this file.
-- Leon Romanovsky | Independent Linux Consultant www.leon.nu | leon@leon.nu
Leon Romanovsky wrote at Tuesday, December 13, 2011 11:27 AM:
On Tue, Dec 13, 2011 at 19:23, Stephen Warren swarren@nvidia.com wrote:
Leon Romanovsky wrote at Monday, December 12, 2011 11:36 PM:
On Mon, Dec 12, 2011 at 22:20, Stephen Warren swarren@nvidia.com wrote:
Leon Romanovsky wrote at Monday, December 12, 2011 12:46 PM:
At this stage only Toshiba AC100/Dynabook supported.
...
+static struct snd_soc_jack_gpio tegra_alc5632_hs_jack_gpio = {
- .name = "Headset Detect",
- .report = SND_JACK_HEADSET,
- .debounce_time = 150,
+};
...
+static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) +{
...
- snd_soc_jack_add_gpios(&tegra_alc5632_hs_jack,
- 1, &tegra_alc5632_hs_jack_gpio);
Since tegra_alc5632_hs_jack_gpio is never set, doesn't this end up using GPIO 0 as the headset detect, which most likely is something completely unrelated to audio.
This is initial support of board file, at the coming patch set, I'll update board-paz00-pinmux.c [1] with audio related pins and I'll add platform device support. I think It is easy for review in this way.
I'm fine adding the feature later. But, I think you shouldn't call snd_soc_jack_add_gpios() until the platform data is there, or you'll end up enabling the feature already, but using the wrong GPIO.
Does it matter ? Anyway, this file is useless without proper audio support, and currently only paz00 will be using this file.
Yes, I think so.
The issue is that by calling snd_soc_jack_add_gpios(), ASoC will internally call gpio_request() and request_any_context_irq() for GPIO 0, which:
a) Will prevent it from being used as a GPIO by any other code.
b) Will configure the GPIO HW to make this pin an input, such that if the pin is enabled as a GPIO, might stop Tegra driving the pin if it was previously configured as an output.
c) If the GPIO is attached to some random piece of HW that toggles the line, this could cause an interrupt storm, or at least bogus jack detect events.
Just removing that one call and the unused GPIO structure would solve all this; it can easily be added back when adding the platform data support.
On Tue, Dec 13, 2011 at 21:03, Stephen Warren swarren@nvidia.com wrote:
Leon Romanovsky wrote at Tuesday, December 13, 2011 11:27 AM:
On Tue, Dec 13, 2011 at 19:23, Stephen Warren swarren@nvidia.com wrote:
Leon Romanovsky wrote at Monday, December 12, 2011 11:36 PM:
On Mon, Dec 12, 2011 at 22:20, Stephen Warren swarren@nvidia.com wrote:
Leon Romanovsky wrote at Monday, December 12, 2011 12:46 PM:
At this stage only Toshiba AC100/Dynabook supported.
...
+static struct snd_soc_jack_gpio tegra_alc5632_hs_jack_gpio = {
- .name = "Headset Detect",
- .report = SND_JACK_HEADSET,
- .debounce_time = 150,
+};
...
+static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) +{
...
- snd_soc_jack_add_gpios(&tegra_alc5632_hs_jack,
- 1, &tegra_alc5632_hs_jack_gpio);
Since tegra_alc5632_hs_jack_gpio is never set, doesn't this end up using GPIO 0 as the headset detect, which most likely is something completely unrelated to audio.
This is initial support of board file, at the coming patch set, I'll update board-paz00-pinmux.c [1] with audio related pins and I'll add platform device support. I think It is easy for review in this way.
I'm fine adding the feature later. But, I think you shouldn't call snd_soc_jack_add_gpios() until the platform data is there, or you'll end up enabling the feature already, but using the wrong GPIO.
Does it matter ? Anyway, this file is useless without proper audio support, and currently only paz00 will be using this file.
Yes, I think so.
The issue is that by calling snd_soc_jack_add_gpios(), ASoC will internally call gpio_request() and request_any_context_irq() for GPIO 0, which:
a) Will prevent it from being used as a GPIO by any other code.
b) Will configure the GPIO HW to make this pin an input, such that if the pin is enabled as a GPIO, might stop Tegra driving the pin if it was previously configured as an output.
c) If the GPIO is attached to some random piece of HW that toggles the line, this could cause an interrupt storm, or at least bogus jack detect events.
Just removing that one call and the unused GPIO structure would solve all this; it can easily be added back when adding the platform data support.
Thanks for explanation, I'll try to resend the patch till the weekends.
-- nvpublic
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
participants (2)
-
Leon Romanovsky
-
Stephen Warren