[V2,1/2] ASoC: samsung: Add machine driver for Odroid X2/U3
diff mbox

Message ID 1403108551-25058-2-git-send-email-s.nawrocki@samsung.com
State New, archived
Headers show

Commit Message

Sylwester Nawrocki June 18, 2014, 4:22 p.m. UTC
This patch adds the sound subsystem driver for Odroid-X2 and
Odroid-U3 boards. The codec works in I2S master mode; there are
2 separate audio routing paths defined as there are differences
in the signal routing between the X2 and U3 boards, i.e. U3 uses
single jack for headphones and microphone.

Signed-off-by: Chen Zhen <zhen1.chen@samsung.com>
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
---
 sound/soc/samsung/Kconfig             |    8 ++
 sound/soc/samsung/Makefile            |    2 +
 sound/soc/samsung/odroidx2_max98090.c |  191 +++++++++++++++++++++++++++++++++
 3 files changed, 201 insertions(+)
 create mode 100644 sound/soc/samsung/odroidx2_max98090.c

Comments

Tushar Behera June 25, 2014, 3:31 a.m. UTC | #1
On 06/18/2014 09:52 PM, Sylwester Nawrocki wrote:
> This patch adds the sound subsystem driver for Odroid-X2 and
> Odroid-U3 boards. The codec works in I2S master mode; there are
> 2 separate audio routing paths defined as there are differences
> in the signal routing between the X2 and U3 boards, i.e. U3 uses
> single jack for headphones and microphone.
> 
> Signed-off-by: Chen Zhen <zhen1.chen@samsung.com>
> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
> ---
>  sound/soc/samsung/Kconfig             |    8 ++
>  sound/soc/samsung/Makefile            |    2 +
>  sound/soc/samsung/odroidx2_max98090.c |  191 +++++++++++++++++++++++++++++++++
>  3 files changed, 201 insertions(+)
>  create mode 100644 sound/soc/samsung/odroidx2_max98090.c
> 

[ ... ]

> +static int odroidx2_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 *cpu_dai = rtd->cpu_dai;
> +	struct snd_soc_dai *codec_dai = rtd->codec_dai;
> +	int ret;
> +
> +	ret = snd_soc_dai_set_sysclk(codec_dai, 0, MAX98090_MCLK,
> +						SND_SOC_CLOCK_IN);
> +	if (ret < 0) {
> +		dev_err(codec_dai->dev,
> +			"Unable to switch to FLL1: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Set the cpu DAI configuration in order to use CDCLK */
> +	ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
> +					0, SND_SOC_CLOCK_OUT);
> +	if (ret < 0)
> +		return ret;
> +

While upstreaming sound-card driver for Snow board, I had a comment from
Mark to move this to probe, if possible. That way, the clock operations
would be done only once and this function and odroidx2_ops can be
removed altogether.

> +	dev_dbg(codec_dai->dev, "HiFi DAI %s params: channels: %d, rate: %d\n",
> +		snd_pcm_stream_str(substream), params_channels(params),
> +		params_rate(params));
> +
> +	return 0;
> +}
> +
> +static struct snd_soc_ops odroidx2_ops = {
> +	.hw_params	= odroidx2_hw_params,
> +};
> +

[ ... ]

> +
> +	ret = snd_soc_register_card(card);

devm_snd_soc_register_card ?

> +	if (ret) {
> +		dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret);
> +		goto err_put_cpu_n;
> +	}
> +
> +	return 0;
> +
> +err_put_cpu_n:
> +	of_node_put(odroidx2_dai[0].cpu_of_node);
> +err_put_cod_n:
> +	of_node_put(odroidx2_dai[0].codec_of_node);
> +	return ret;
> +}
> +
> +static int odroidx2_audio_remove(struct platform_device *pdev)
> +{
> +	struct snd_soc_card *card = platform_get_drvdata(pdev);
> +
> +	snd_soc_unregister_card(card);

This can be removed when devm_snd_soc_register_card is used.
Sylwester Nawrocki June 26, 2014, 8:47 a.m. UTC | #2
On 25/06/14 05:31, Tushar Behera wrote:
> On 06/18/2014 09:52 PM, Sylwester Nawrocki wrote:
>> This patch adds the sound subsystem driver for Odroid-X2 and
>> Odroid-U3 boards. The codec works in I2S master mode; there are
>> 2 separate audio routing paths defined as there are differences
>> in the signal routing between the X2 and U3 boards, i.e. U3 uses
>> single jack for headphones and microphone.
>>
>> Signed-off-by: Chen Zhen <zhen1.chen@samsung.com>
>> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
>> ---
>>  sound/soc/samsung/Kconfig             |    8 ++
>>  sound/soc/samsung/Makefile            |    2 +
>>  sound/soc/samsung/odroidx2_max98090.c |  191 +++++++++++++++++++++++++++++++++
>>  3 files changed, 201 insertions(+)
>>  create mode 100644 sound/soc/samsung/odroidx2_max98090.c
>>
> 
> [ ... ]
> 
>> +static int odroidx2_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 *cpu_dai = rtd->cpu_dai;
>> +	struct snd_soc_dai *codec_dai = rtd->codec_dai;
>> +	int ret;
>> +
>> +	ret = snd_soc_dai_set_sysclk(codec_dai, 0, MAX98090_MCLK,
>> +						SND_SOC_CLOCK_IN);
>> +	if (ret < 0) {
>> +		dev_err(codec_dai->dev,
>> +			"Unable to switch to FLL1: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	/* Set the cpu DAI configuration in order to use CDCLK */
>> +	ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
>> +					0, SND_SOC_CLOCK_OUT);
>> +	if (ret < 0)
>> +		return ret;
>> +
> 
> While upstreaming sound-card driver for Snow board, I had a comment from
> Mark to move this to probe, if possible. That way, the clock operations
> would be done only once and this function and odroidx2_ops can be
> removed altogether.

Thanks for the suggestion, I'll change it for the next iteration.

>> +	dev_dbg(codec_dai->dev, "HiFi DAI %s params: channels: %d, rate: %d\n",
>> +		snd_pcm_stream_str(substream), params_channels(params),
>> +		params_rate(params));
>> +
>> +	return 0;
>> +}
>> +
>> +static struct snd_soc_ops odroidx2_ops = {
>> +	.hw_params	= odroidx2_hw_params,
>> +};
>> +
> 
> [ ... ]
> 
>> +
>> +	ret = snd_soc_register_card(card);
> 
> devm_snd_soc_register_card ?

Could be, although snd_soc_unregister_card() wouldn't release references
to the OF nodes, i.e. there would be no related of_node_put() calls.
And for correctness, the of_node_put() calls should be made _after_
a snd_soc_unregister_card() call, and I can't see how it could be possible
if I left only of_node_put() calls in the odroidx2_audio_remove() callback.

>> +	if (ret) {
>> +		dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret);
>> +		goto err_put_cpu_n;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_put_cpu_n:
>> +	of_node_put(odroidx2_dai[0].cpu_of_node);
>> +err_put_cod_n:
>> +	of_node_put(odroidx2_dai[0].codec_of_node);
>> +	return ret;
>> +}
>> +
>> +static int odroidx2_audio_remove(struct platform_device *pdev)
>> +{
>> +	struct snd_soc_card *card = platform_get_drvdata(pdev);
>> +
>> +	snd_soc_unregister_card(card);
> 
> This can be removed when devm_snd_soc_register_card is used.

--
Regards,
Sylwester
Mark Brown June 30, 2014, 6:46 p.m. UTC | #3
On Wed, Jun 18, 2014 at 06:22:30PM +0200, Sylwester Nawrocki wrote:

> +struct odroidx2_drv_data odroidx2_drvdata = {
> +	.dapm_widgets		= odroidx2_dapm_widgets,
> +	.num_dapm_widgets	= ARRAY_SIZE(odroidx2_dapm_widgets),
> +};
> +
> +struct odroidx2_drv_data odroidu3_drvdata = {
> +	.dapm_widgets		= odroidu3_dapm_widgets,
> +	.num_dapm_widgets	= ARRAY_SIZE(odroidu3_dapm_widgets),
> +};

> +	ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
> +	if (ret < 0)
> +		return ret;

Given that the widgets don't have any actions defined and the routing is
all done by DT it might be easier and more flexible to just define all
the widgets all the time and let them hang there if they're not in use,
that way you don't need multiple compatible strings.  If you do that
you're then very close to being able to use simple-card...
Sylwester Nawrocki July 14, 2014, 11:27 a.m. UTC | #4
On 30/06/14 20:46, Mark Brown wrote:
> On Wed, Jun 18, 2014 at 06:22:30PM +0200, Sylwester Nawrocki wrote:
> 
>> +struct odroidx2_drv_data odroidx2_drvdata = {
>> +	.dapm_widgets		= odroidx2_dapm_widgets,
>> +	.num_dapm_widgets	= ARRAY_SIZE(odroidx2_dapm_widgets),
>> +};
>> +
>> +struct odroidx2_drv_data odroidu3_drvdata = {
>> +	.dapm_widgets		= odroidu3_dapm_widgets,
>> +	.num_dapm_widgets	= ARRAY_SIZE(odroidu3_dapm_widgets),
>> +};
> 
>> +	ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
>> +	if (ret < 0)
>> +		return ret;
> 
> Given that the widgets don't have any actions defined and the routing is
> all done by DT it might be easier and more flexible to just define all
> the widgets all the time and let them hang there if they're not in use,
> that way you don't need multiple compatible strings.  If you do that
> you're then very close to being able to use simple-card...

Too bad, I noticed this comment only just now.  I'll consider this and
will try again and see how simple-card could be used.  There is also the
samsung-i2s-sec secondary 'overlay' CPU DAI that would need to be handled,
and we have it now internally specified in the machine driver through
cpu_dai_name, rather than cpu_of_node.
There might be some more work in the driver needed to support the
secondary I2S interface generically with DT.

--
Regards,
Sylwester
--
To unsubscribe from this list: send the line "unsubscribe alsa-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mark Brown July 14, 2014, 11:31 a.m. UTC | #5
On Mon, Jul 14, 2014 at 01:27:53PM +0200, Sylwester Nawrocki wrote:

> Too bad, I noticed this comment only just now.  I'll consider this and
> will try again and see how simple-card could be used.  There is also the
> samsung-i2s-sec secondary 'overlay' CPU DAI that would need to be handled,
> and we have it now internally specified in the machine driver through
> cpu_dai_name, rather than cpu_of_node.
> There might be some more work in the driver needed to support the
> secondary I2S interface generically with DT.

You should be converting the device to DPCM for both that and the
compressed audio path.

Patch
diff mbox

diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 7745629..bd2645c 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -243,3 +243,11 @@  config SND_SOC_SNOW
 	help
 	  Say Y if you want to add audio support for various Snow
 	  boards based on Exynos5 series of SoCs.
+
+config SND_SOC_ODROIDX2
+	tristate "Audio support for Odroid-X2 and Odroid-U3"
+	depends on SND_SOC_SAMSUNG
+	select SND_SOC_MAX98090
+	select SND_SAMSUNG_I2S
+	help
+	  Say Y here to enable audio support for the Odroid-X2/U3.
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index 6d0212b..8b1cfbd 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -46,6 +46,7 @@  snd-soc-tobermory-objs := tobermory.o
 snd-soc-lowland-objs := lowland.o
 snd-soc-littlemill-objs := littlemill.o
 snd-soc-bells-objs := bells.o
+snd-soc-odroidx2-max98090-objs := odroidx2_max98090.o
 
 obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -71,3 +72,4 @@  obj-$(CONFIG_SND_SOC_TOBERMORY) += snd-soc-tobermory.o
 obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o
 obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o
 obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o
+obj-$(CONFIG_SND_SOC_ODROIDX2) += snd-soc-odroidx2-max98090.o
diff --git a/sound/soc/samsung/odroidx2_max98090.c b/sound/soc/samsung/odroidx2_max98090.c
new file mode 100644
index 0000000..11c0952
--- /dev/null
+++ b/sound/soc/samsung/odroidx2_max98090.c
@@ -0,0 +1,191 @@ 
+/*
+ * Copyright (C) 2014 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/of.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include "i2s.h"
+
+struct odroidx2_drv_data {
+	const struct snd_soc_dapm_widget *dapm_widgets;
+	unsigned int num_dapm_widgets;
+};
+
+/* The I2S CDCLK output clock frequency to the MAX98090 codec. */
+#define MAX98090_MCLK 19200000
+
+static int odroidx2_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 *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0, MAX98090_MCLK,
+						SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		dev_err(codec_dai->dev,
+			"Unable to switch to FLL1: %d\n", ret);
+		return ret;
+	}
+
+	/* Set the cpu DAI configuration in order to use CDCLK */
+	ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
+					0, SND_SOC_CLOCK_OUT);
+	if (ret < 0)
+		return ret;
+
+	dev_dbg(codec_dai->dev, "HiFi DAI %s params: channels: %d, rate: %d\n",
+		snd_pcm_stream_str(substream), params_channels(params),
+		params_rate(params));
+
+	return 0;
+}
+
+static struct snd_soc_ops odroidx2_ops = {
+	.hw_params	= odroidx2_hw_params,
+};
+
+static const struct snd_soc_dapm_widget odroidx2_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_MIC("Mic Jack", NULL),
+	SND_SOC_DAPM_MIC("DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_widget odroidu3_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_SPK("Speakers", NULL),
+};
+
+static struct snd_soc_dai_link odroidx2_dai[] = {
+	{
+		.name		= "MAX98090",
+		.stream_name	= "MAX98090 PCM",
+		.codec_dai_name	= "HiFi",
+		.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBM_CFM,
+		.ops		= &odroidx2_ops,
+	}
+};
+
+static struct snd_soc_card odroidx2 = {
+	.owner		= THIS_MODULE,
+	.dai_link	= odroidx2_dai,
+	.num_links	= ARRAY_SIZE(odroidx2_dai),
+	.fully_routed	= true,
+};
+
+struct odroidx2_drv_data odroidx2_drvdata = {
+	.dapm_widgets		= odroidx2_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(odroidx2_dapm_widgets),
+};
+
+struct odroidx2_drv_data odroidu3_drvdata = {
+	.dapm_widgets		= odroidu3_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(odroidu3_dapm_widgets),
+};
+
+static const struct of_device_id odroidx2_audio_of_match[] = {
+	{
+		.compatible	= "samsung,odroidx2-audio",
+		.data		= &odroidx2_drvdata,
+	}, {
+		.compatible	= "samsung,odroidu3-audio",
+		.data		= &odroidu3_drvdata,
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, odroidx2_audio_of_match);
+
+static int odroidx2_audio_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct snd_soc_card *card = &odroidx2;
+	struct odroidx2_drv_data *dd;
+	const struct of_device_id *of_id;
+	int ret;
+
+	card->dev = &pdev->dev;
+
+	of_id = of_match_node(odroidx2_audio_of_match, np);
+	dd = (struct odroidx2_drv_data *)of_id->data;
+
+	card->dapm_widgets = dd->dapm_widgets;
+	card->num_dapm_widgets = dd->num_dapm_widgets;
+
+	ret = snd_soc_of_parse_card_name(card, "samsung,model");
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
+	if (ret < 0)
+		return ret;
+
+	odroidx2_dai[0].codec_of_node = of_parse_phandle(np,
+						"samsung,audio-codec", 0);
+	if (!odroidx2_dai[0].codec_of_node) {
+		dev_err(&pdev->dev,
+			"Property 'samsung,audio-codec' missing or invalid\n");
+		return -EINVAL;
+	}
+
+	odroidx2_dai[0].cpu_of_node = of_parse_phandle(np,
+						"samsung,i2s-controller", 0);
+	if (!odroidx2_dai[0].cpu_of_node) {
+		dev_err(&pdev->dev,
+			"Property 'samsung,i2s-controller' missing or invalid\n");
+		ret = -EINVAL;
+		goto err_put_cod_n;
+	}
+
+	odroidx2_dai[0].platform_of_node = odroidx2_dai[0].cpu_of_node;
+
+	ret = snd_soc_register_card(card);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret);
+		goto err_put_cpu_n;
+	}
+
+	return 0;
+
+err_put_cpu_n:
+	of_node_put(odroidx2_dai[0].cpu_of_node);
+err_put_cod_n:
+	of_node_put(odroidx2_dai[0].codec_of_node);
+	return ret;
+}
+
+static int odroidx2_audio_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_card(card);
+
+	of_node_put(odroidx2_dai[0].cpu_of_node);
+	of_node_put(odroidx2_dai[0].codec_of_node);
+
+	return 0;
+}
+
+static struct platform_driver odroidx2_audio_driver = {
+	.driver = {
+		.name		= "odroidx2-audio",
+		.owner		= THIS_MODULE,
+		.of_match_table	= odroidx2_audio_of_match,
+	},
+	.probe	= odroidx2_audio_probe,
+	.remove	= odroidx2_audio_remove,
+};
+module_platform_driver(odroidx2_audio_driver);
+
+MODULE_AUTHOR("Chen Zhen <zhen1.chen@samsung.com>");
+MODULE_DESCRIPTION("ALSA SoC Odroid X2/U3 Audio Support");
+MODULE_LICENSE("GPL v2");