Patchwork [v2,3/3] ASoC: tegra: add tegra machine driver using wm9712 codec

login
register
mail settings
Submitter Lucas Stach
Date Jan. 5, 2013, 1:18 a.m.
Message ID <1357348725-32139-4-git-send-email-dev@lynxeye.de>
Download mbox | patch
Permalink /patch/209590/
State Not Applicable, archived
Headers show

Comments

Lucas Stach - Jan. 5, 2013, 1:18 a.m.
This adds a very simple machine driver using the Wolfson wm9712 AC97
codec.

Signed-off-by: Lucas Stach <dev@lynxeye.de>
---
v2:
- manually probe codec driver without using DT
- fixup Kconfig for select and depends
---
 .../bindings/sound/nvidia,tegra-audio-wm9712.txt   |  51 ++++++
 sound/soc/tegra/Kconfig                            |   9 ++
 sound/soc/tegra/Makefile                           |   2 +
 sound/soc/tegra/tegra_wm9712.c                     | 179 +++++++++++++++++++++
 4 files changed, 241 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt
 create mode 100644 sound/soc/tegra/tegra_wm9712.c
Stephen Warren - Jan. 8, 2013, 10:17 p.m.
On 01/04/2013 06:18 PM, Lucas Stach wrote:
> This adds a very simple machine driver using the Wolfson wm9712 AC97
> codec.

> diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c


> +static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd)

> +	err = snd_soc_dapm_sync(dapm);
> +	if (err)
> +		return err;
> +
> +	return 0;

That block is just "return snd_soc_dapm_sync(dapm);"

> +static int tegra_wm9712_driver_probe(struct platform_device *pdev)

> +	machine->codec = platform_device_alloc("wm9712-codec", -1);

Hmmm. I thought that could be auto-instantiated based on probing the
AC'97 bus for whatever was there?

> +	ret = platform_device_add(machine->codec);
> +	if (ret)
> +		goto codec_put;

After this point, ...

> +	ret = snd_soc_of_parse_card_name(card, "nvidia,model");
> +	if (ret)
> +		goto codec_put;

don't the error paths need to undo platform_device_add()? Or does
platform_device_put() do that automatically?

> +codec_put:
> +	platform_device_put(machine->codec);
> +	return ret;
> +}

> +static int tegra_wm9712_driver_remove(struct platform_device *pdev)

> +	platform_device_put(machine->codec);

Same comment here.

--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Lucas Stach - Jan. 8, 2013, 10:52 p.m.
Am Dienstag, den 08.01.2013, 15:17 -0700 schrieb Stephen Warren:
> On 01/04/2013 06:18 PM, Lucas Stach wrote:
> > This adds a very simple machine driver using the Wolfson wm9712 AC97
> > codec.
> 
> > diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c
> 
> 
> > +static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd)
> 
> > +	err = snd_soc_dapm_sync(dapm);
> > +	if (err)
> > +		return err;
> > +
> > +	return 0;
> 
> That block is just "return snd_soc_dapm_sync(dapm);"
> 
> > +static int tegra_wm9712_driver_probe(struct platform_device *pdev)
> 
> > +	machine->codec = platform_device_alloc("wm9712-codec", -1);
> 
> Hmmm. I thought that could be auto-instantiated based on probing the
> AC'97 bus for whatever was there?
AC97 is in principle probeable by reading the first regs of the codec,
but all the infrastructure to do proper hotplugging on the AC97 bus is
missing in the kernel. As I didn't want to be the one to add all this
infrastructure to a dying audio platform, I opted to do the same thing
as all the other platform using AC97 and instantiate the codec from the
machine driver.

This binds machine driver and codec together, but after all the machine
driver is already both pretty small and codec specific. So considering
the number of Tegra platform out there using AC97 I figured it's not
worth the hassle trying to write a generic machine driver.
> 
> > +	ret = platform_device_add(machine->codec);
> > +	if (ret)
> > +		goto codec_put;
> 
> After this point, ...
> 
> > +	ret = snd_soc_of_parse_card_name(card, "nvidia,model");
> > +	if (ret)
> > +		goto codec_put;
> 
> don't the error paths need to undo platform_device_add()? Or does
> platform_device_put() do that automatically?
> 
Yep, I've just gone through the implementation of those platform
functions and at some point we reverse the device_add. I'll fix this up.
> > +codec_put:
> > +	platform_device_put(machine->codec);
> > +	return ret;
> > +}
> 
> > +static int tegra_wm9712_driver_remove(struct platform_device *pdev)
> 
> > +	platform_device_put(machine->codec);
> 
> Same comment here.
> 


--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mark Brown - Jan. 13, 2013, 11:26 p.m.
On Tue, Jan 08, 2013 at 11:52:33PM +0100, Lucas Stach wrote:
> Am Dienstag, den 08.01.2013, 15:17 -0700 schrieb Stephen Warren:

> > Hmmm. I thought that could be auto-instantiated based on probing the
> > AC'97 bus for whatever was there?

> AC97 is in principle probeable by reading the first regs of the codec,
> but all the infrastructure to do proper hotplugging on the AC97 bus is
> missing in the kernel. As I didn't want to be the one to add all this
> infrastructure to a dying audio platform, I opted to do the same thing
> as all the other platform using AC97 and instantiate the codec from the
> machine driver.

That's about the size of it, yes.  We should write the device tree as
though we'd got better infrastructure here since this isn't something
that ought to be required but right now this is how you do AC'97 in
ASoC.  It'd be really nice if someone had the time to work on the AC'97
infrastructure here but I'd not hold my breath given that it's rarely
used in new designs.

Patch

diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt
new file mode 100644
index 0000000..be35d34
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt
@@ -0,0 +1,51 @@ 
+NVIDIA Tegra audio complex
+
+Required properties:
+- compatible : "nvidia,tegra-audio-wm9712"
+- nvidia,model : The user-visible name of this sound complex.
+- nvidia,audio-routing : A list of the connections between audio components.
+  Each entry is a pair of strings, the first being the connection's sink,
+  the second being the connection's source. Valid names for sources and
+  sinks are the WM9712's pins, and the jacks on the board:
+
+  WM9712 pins:
+
+  * MONOOUT
+  * HPOUTL
+  * HPOUTR
+  * LOUT2
+  * ROUT2
+  * OUT3
+  * LINEINL
+  * LINEINR
+  * PHONE
+  * PCBEEP
+  * MIC1
+  * MIC2
+  * Mic Bias
+
+  Board connectors:
+
+  * Headphone
+  * LineIn
+  * Mic
+
+- nvidia,ac97-controller : The phandle of the Tegra AC97 controller
+
+
+Example:
+
+sound {
+	compatible = "nvidia,tegra-audio-wm9712-colibri_t20",
+		         "nvidia,tegra-audio-wm9712";
+	nvidia,model = "Toradex Colibri T20";
+
+	nvidia,audio-routing =
+		"Headphone", "HPOUTL",
+		"Headphone", "HPOUTR",
+		"LineIn", "LINEINL",
+		"LineIn", "LINEINR",
+		"Mic", "MIC1";
+
+	nvidia,ac97-controller = <&ac97>;
+};
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 4b3a2b8..dbc27ce 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -80,6 +80,15 @@  config SND_SOC_TEGRA_WM8903
 	  boards using the WM8093 codec. Currently, the supported boards are
 	  Harmony, Ventana, Seaboard, Kaen, and Aebl.
 
+config SND_SOC_TEGRA_WM9712
+	tristate "SoC Audio support for Tegra boards using a WM9712 codec"
+	depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
+	select SND_SOC_TEGRA20_AC97
+	select SND_SOC_WM9712
+	help
+	  Say Y or M here if you want to add support for SoC audio on Tegra
+	  boards using the WM9712 (or compatible) codec.
+
 config SND_SOC_TEGRA_TRIMSLICE
 	tristate "SoC Audio support for TrimSlice board"
 	depends on SND_SOC_TEGRA && I2C
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index 02513d9..416a14b 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -20,10 +20,12 @@  obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o
 # Tegra machine Support
 snd-soc-tegra-wm8753-objs := tegra_wm8753.o
 snd-soc-tegra-wm8903-objs := tegra_wm8903.o
+snd-soc-tegra-wm9712-objs := tegra_wm9712.o
 snd-soc-tegra-trimslice-objs := trimslice.o
 snd-soc-tegra-alc5632-objs := tegra_alc5632.o
 
 obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o
 obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
+obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.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_wm9712.c b/sound/soc/tegra/tegra_wm9712.c
new file mode 100644
index 0000000..9268621
--- /dev/null
+++ b/sound/soc/tegra/tegra_wm9712.c
@@ -0,0 +1,179 @@ 
+/*
+ * tegra20_wm9712.c - Tegra machine ASoC driver for boards using WM9712 codec.
+ *
+ * Copyright 2012 Lucas Stach <dev@lynxeye.de>
+ *
+ * Partly based on code copyright/by:
+ * Copyright 2011,2012 Toradex Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "tegra-snd-wm9712"
+
+struct tegra_wm9712 {
+	struct platform_device *codec;
+};
+
+static const struct snd_soc_dapm_widget tegra_wm9712_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	SND_SOC_DAPM_LINE("LineIn", NULL),
+	SND_SOC_DAPM_MIC("Mic", NULL),
+};
+
+static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	int err;
+
+	snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+
+	err = snd_soc_dapm_sync(dapm);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static struct snd_soc_dai_link tegra_wm9712_dai = {
+	.name = "AC97 HiFi",
+	.stream_name = "AC97 HiFi",
+	.cpu_dai_name = "tegra-ac97-pcm",
+	.codec_dai_name = "wm9712-hifi",
+	.codec_name = "wm9712-codec",
+	.init = tegra_wm9712_init,
+};
+
+static struct snd_soc_card snd_soc_tegra_wm9712 = {
+	.name = "tegra-wm9712",
+	.owner = THIS_MODULE,
+	.dai_link = &tegra_wm9712_dai,
+	.num_links = 1,
+
+	.dapm_widgets = tegra_wm9712_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(tegra_wm9712_dapm_widgets),
+	.fully_routed = true,
+};
+
+static int tegra_wm9712_driver_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct snd_soc_card *card = &snd_soc_tegra_wm9712;
+	struct tegra_wm9712 *machine;
+	int ret;
+
+	if (!pdev->dev.of_node) {
+		dev_err(&pdev->dev, "No platform data supplied\n");
+		return -EINVAL;
+	}
+
+	machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm9712),
+			       GFP_KERNEL);
+	if (!machine) {
+		dev_err(&pdev->dev, "Can't allocate tegra_wm9712 struct\n");
+		return -ENOMEM;
+	}
+
+	card->dev = &pdev->dev;
+	platform_set_drvdata(pdev, card);
+	snd_soc_card_set_drvdata(card, machine);
+
+	machine->codec = platform_device_alloc("wm9712-codec", -1);
+	if (!machine->codec) {
+		dev_err(&pdev->dev, "Can't allocate wm9712 platform device\n");
+		return -ENOMEM;
+	}
+
+	ret = platform_device_add(machine->codec);
+	if (ret)
+		goto codec_put;
+
+	ret = snd_soc_of_parse_card_name(card, "nvidia,model");
+	if (ret)
+		goto codec_put;
+
+	ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
+	if (ret)
+		goto codec_put;
+
+	tegra_wm9712_dai.cpu_of_node = of_parse_phandle(np,
+				       "nvidia,ac97-controller", 0);
+	if (!tegra_wm9712_dai.cpu_of_node) {
+		dev_err(&pdev->dev,
+			"Property 'nvidia,ac97-controller' missing or invalid\n");
+		ret = -EINVAL;
+		goto codec_put;
+	}
+
+	tegra_wm9712_dai.platform_of_node = tegra_wm9712_dai.cpu_of_node;
+
+	ret = snd_soc_register_card(card);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+			ret);
+		goto codec_put;
+	}
+
+	return 0;
+
+codec_put:
+	platform_device_put(machine->codec);
+	return ret;
+}
+
+static int tegra_wm9712_driver_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct tegra_wm9712 *machine = snd_soc_card_get_drvdata(card);
+
+	snd_soc_unregister_card(card);
+
+	platform_device_put(machine->codec);
+
+	return 0;
+}
+
+static const struct of_device_id tegra_wm9712_of_match[] __devinitconst = {
+	{ .compatible = "nvidia,tegra-audio-wm9712", },
+	{},
+};
+
+static struct platform_driver tegra_wm9712_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+		.of_match_table = tegra_wm9712_of_match,
+	},
+	.probe = tegra_wm9712_driver_probe,
+	.remove = tegra_wm9712_driver_remove,
+};
+module_platform_driver(tegra_wm9712_driver);
+
+MODULE_AUTHOR("Lucas Stach");
+MODULE_DESCRIPTION("Tegra+WM9712 machine ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra_wm9712_of_match);