Patchwork [RFC,08/13] sound: sam9x5_wm8731: machine driver for at91sam9x5 wm8731 boards

login
register
mail settings
Submitter Richard Genoud
Date July 1, 2013, 8:39 a.m.
Message ID <1372667978-4718-9-git-send-email-richard.genoud@gmail.com>
Download mbox | patch
Permalink /patch/256041/
State New
Headers show

Comments

Richard Genoud - July 1, 2013, 8:39 a.m.
From: Nicolas Ferre <nicolas.ferre@atmel.com>

Description of the Asoc machine driver for an at91sam9x5 based board
with a wm8731 audio DAC. Wm8731 is clocked by a crystal and used as a
master on the SSC/I2S interface. Its connections are a headphone jack
and an Line input jack.

[Richard: this is based on an old patch from Nicolas that I forward
ported and reworked to use only device tree]

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
---
 sound/soc/atmel/Kconfig         |   12 ++
 sound/soc/atmel/Makefile        |    2 +
 sound/soc/atmel/sam9x5_wm8731.c |  232 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 246 insertions(+)
 create mode 100644 sound/soc/atmel/sam9x5_wm8731.c
Bo Shen - July 2, 2013, 3:21 a.m.
Hi Richard,

   Will move this patch before 5, 6, 7?

On 7/1/2013 16:39, Richard Genoud wrote:
> From: Nicolas Ferre <nicolas.ferre@atmel.com>
>
> Description of the Asoc machine driver for an at91sam9x5 based board
> with a wm8731 audio DAC. Wm8731 is clocked by a crystal and used as a
> master on the SSC/I2S interface. Its connections are a headphone jack
> and an Line input jack.
>
> [Richard: this is based on an old patch from Nicolas that I forward
> ported and reworked to use only device tree]
>
> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
> ---
>   sound/soc/atmel/Kconfig         |   12 ++
>   sound/soc/atmel/Makefile        |    2 +
>   sound/soc/atmel/sam9x5_wm8731.c |  232 +++++++++++++++++++++++++++++++++++++++
>   3 files changed, 246 insertions(+)
>   create mode 100644 sound/soc/atmel/sam9x5_wm8731.c
>
> diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
> index 3fdd87f..f24d601 100644
> --- a/sound/soc/atmel/Kconfig
> +++ b/sound/soc/atmel/Kconfig
> @@ -13,6 +13,7 @@ config SND_ATMEL_SOC_PDC
>   config SND_ATMEL_SOC_DMA
>   	tristate
>   	depends on SND_ATMEL_SOC
> +	select SND_SOC_DMAENGINE_PCM
>
>   config SND_ATMEL_SOC_SSC
>   	tristate
> @@ -32,6 +33,17 @@ config SND_AT91_SOC_SAM9G20_WM8731
>   	  Say Y if you want to add support for SoC audio on WM8731-based
>   	  AT91sam9g20 evaluation board.
>
> +config SND_AT91_SOC_SAM9X5_WM8731
> +	tristate "SoC Audio support for WM8731-based at91sam9x5 board"
> +	depends on ATMEL_SSC && SND_ATMEL_SOC && SOC_AT91SAM9X5
> +	select SND_ATMEL_SOC_SSC
> +	select SND_ATMEL_SOC_DMA
> +	select SND_ATMEL_SOC_PDC

Not need to select SND_ATMEL_SOC_PDC

> +	select SND_SOC_WM8731
> +	help
> +	  Say Y if you want to add support for audio SoC on an
> +	  at91sam9x5 based board that is using WM8731 codec.
> +
>   config SND_AT91_SOC_AFEB9260
>   	tristate "SoC Audio support for AFEB9260 board"
>   	depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
> diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
> index 41967cc..7784c09 100644
> --- a/sound/soc/atmel/Makefile
> +++ b/sound/soc/atmel/Makefile
> @@ -11,6 +11,8 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
>
>   # AT91 Machine Support
>   snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
> +snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
>
>   obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
> +obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
>   obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
> diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c
> new file mode 100644
> index 0000000..83ca457
> --- /dev/null
> +++ b/sound/soc/atmel/sam9x5_wm8731.c
> @@ -0,0 +1,232 @@
> +/*
> + * sam9x5_wm8731   --	SoC audio for AT91SAM9X5-based boards
> + *			that are using WM8731 as codec.
> + *
> + *  Copyright (C) 2011 Atmel,
> + *		  Nicolas Ferre <nicolas.ferre@atmel.com>
> + *
> + * Based on sam9g20_wm8731.c by:
> + * Sedji Gaouaou <sedji.gaouaou@atmel.com>
> + *
> + * GPL
> + */
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/kernel.h>
> +#include <linux/clk.h>
> +#include <linux/timer.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/i2c.h>
> +
> +#include <linux/atmel-ssc.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +
> +#include <asm/mach-types.h>
> +#include <mach/hardware.h>
> +#include <mach/gpio.h>
> +
> +#include "../codecs/wm8731.h"
> +#include "atmel-pcm.h"
> +#include "atmel_ssc_dai.h"
> +
> +#define MCLK_RATE 12288000
> +
> +#define DRV_NAME "sam9x5-snd-wm8731"
> +
> +/*
> + * Audio paths on at91sam9x5ek board:
> + *
> + *  |A| ------------> |      | ---R----> Headphone Jack
> + *  |T| <----\        |  WM  | ---L--/
> + *  |9| ---> CLK <--> | 8751 | <--R----- Line In Jack
> + *  |1| <------------ |      | <--L--/
> + */
> +static const struct snd_soc_dapm_widget at91sam9x5ek_dapm_widgets[] = {
> +	SND_SOC_DAPM_HP("Headphone Jack", NULL),
> +	SND_SOC_DAPM_LINE("Line In Jack", NULL),
> +};
> +
> +/*
> + * Logic for a wm8731 as connected on a at91sam9x5 based board.
> + */
> +static int at91sam9x5ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
> +{
> +	struct snd_soc_codec *codec = rtd->codec;
> +	struct snd_soc_dai *codec_dai = rtd->codec_dai;
> +	struct snd_soc_dapm_context *dapm = &codec->dapm;
> +	struct device *dev = rtd->dev;
> +	int ret;
> +
> +	dev_dbg(dev, "ASoC: at91sam9x5ek_wm8731_init() called\n");
> +
> +	/*
> +	 * remove some not supported rates in relation with clock
> +	 * provided to the wm8731 codec
> +	 */
> +	switch (MCLK_RATE) {
> +	case 12288000:
> +		codec_dai->driver->playback.rates &= SNDRV_PCM_RATE_8000 |
> +						     SNDRV_PCM_RATE_32000 |
> +						     SNDRV_PCM_RATE_48000 |
> +						     SNDRV_PCM_RATE_96000;
> +		codec_dai->driver->capture.rates &= SNDRV_PCM_RATE_8000 |
> +						    SNDRV_PCM_RATE_32000 |
> +						    SNDRV_PCM_RATE_48000 |
> +						    SNDRV_PCM_RATE_96000;
> +		break;
> +	case 12000000:
> +		/* all wm8731 rates supported */
> +		break;
> +	default:
> +		dev_err(dev, "ASoC: Codec Master clock rate not defined\n");
> +		return -EINVAL;
> +	}

Here, I think we not need to use switch ... case, as the MCLK_RATE is 
hard code as 12288000.

-----------------------------------------------

> +	/* set not connected pins */
> +	snd_soc_dapm_nc_pin(dapm, "Mic Bias");
> +	snd_soc_dapm_nc_pin(dapm, "MICIN");
> +	snd_soc_dapm_nc_pin(dapm, "LOUT");
> +	snd_soc_dapm_nc_pin(dapm, "ROUT");
> +
> +	/* always connected */
> +	snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
> +	snd_soc_dapm_enable_pin(dapm, "Line In Jack");

------------------------------------------------
This part, not needed, as to the ASoC framework will deal with it.

> +	/* set the codec system clock for DAC and ADC */
> +	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
> +				     MCLK_RATE, SND_SOC_CLOCK_IN);
> +	if (ret < 0) {
> +		dev_err(dev, "ASoC: Failed to set WM8731 SYSCLK: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* signal a DAPM event */
> +	snd_soc_dapm_sync(dapm);
> +	return 0;
> +}
> +
> +static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct device_node *codec_np, *cpu_np;
> +	struct snd_soc_card *card;
> +	struct snd_soc_dai_link *dai;
> +	int ret;
> +
> +	if (!np) {
> +		dev_err(&pdev->dev, "No device node supplied\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = atmel_ssc_set_audio(0);
> +	if (ret != 0) {
> +		dev_err(&pdev->dev,
> +			"ASoC: Failed to set SSC 0 for audio: %d\n", ret);
> +		return ret;
> +	}
> +
> +	card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
> +	dai = devm_kzalloc(&pdev->dev, sizeof(*dai), GFP_KERNEL);
> +	if (!dai || !card) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	card->dev = &pdev->dev;
> +	card->owner = THIS_MODULE;
> +	card->dai_link = dai;
> +	card->num_links = 1;
> +	dai->name = "WM8731";
> +	dai->stream_name = "WM8731 PCM";
> +	dai->codec_dai_name = "wm8731-hifi";
> +	dai->init = at91sam9x5ek_wm8731_init;
> +	card->dapm_widgets = at91sam9x5ek_dapm_widgets;
> +	card->num_dapm_widgets = ARRAY_SIZE(at91sam9x5ek_dapm_widgets);

Will keep these as snd_soc_card and snd_soc_dai_link structure separately?

> +	ret = snd_soc_of_parse_card_name(card, "atmel,model");
> +	if (ret)
> +		goto out;
> +
> +	ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing");
> +	if (ret)
> +		goto out;
> +
> +	codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
> +	if (!codec_np) {
> +		dev_err(&pdev->dev, "codec info missing\n");
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	dai->codec_of_node = codec_np;
> +
> +	cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
> +	if (!cpu_np) {
> +		dev_err(&pdev->dev, "ssc controller node missing\n");
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +	dai->cpu_of_node = cpu_np;
> +	dai->platform_of_node = cpu_np;
> +
> +	of_node_put(codec_np);
> +	of_node_put(cpu_np);
> +
> +	dai->dai_fmt = snd_soc_of_parse_daifmt(np, "atmel,");
> +
> +	ret = snd_soc_register_card(card);
> +	if (ret) {
> +		dev_err(&pdev->dev,
> +			"ASoC: Platform device allocation failed\n");
> +		goto out;
> +	}
> +
> +	platform_set_drvdata(pdev, card);
> +
> +	dev_info(&pdev->dev, "ASoC: at91sam9x5ek_init ok\n");
> +
> +	return ret;
> +
> +out:
> +	atmel_ssc_put_audio(0);
> +	return ret;
> +}
> +
> +static int sam9x5_wm8731_driver_remove(struct platform_device *pdev)
> +{
> +	struct snd_soc_card *card = platform_get_drvdata(pdev);
> +
> +	snd_soc_unregister_card(card);
> +	atmel_ssc_put_audio(0);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id sam9x5_wm8731_of_match[] = {
> +	{ .compatible = "atmel,sam9x5-audio-wm8731", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, sam9x5_wm8731_of_match);
> +
> +static struct platform_driver sam9x5_wm8731_driver = {
> +	.driver = {
> +		.name = DRV_NAME,
> +		.owner = THIS_MODULE,
> +		.of_match_table = of_match_ptr(sam9x5_wm8731_of_match),
> +	},
> +	.probe = sam9x5_wm8731_driver_probe,
> +	.remove = sam9x5_wm8731_driver_remove,
> +};
> +module_platform_driver(sam9x5_wm8731_driver);
> +
> +/* Module information */
> +MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>");
> +MODULE_AUTHOR("Richard Genoud <richard.genoud@gmail.com>");
> +MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAM9x5 - WM8731");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:" DRV_NAME);
>

Best Regards,
Bo Shen
Richard Genoud - July 5, 2013, 3:15 p.m.
2013/7/2 Bo Shen <voice.shen@atmel.com>:
> Hi Richard,
>
>   Will move this patch before 5, 6, 7?
yes, you're right.

>
>
> On 7/1/2013 16:39, Richard Genoud wrote:
>>
>> From: Nicolas Ferre <nicolas.ferre@atmel.com>
>>
>> Description of the Asoc machine driver for an at91sam9x5 based board
>> with a wm8731 audio DAC. Wm8731 is clocked by a crystal and used as a
>> master on the SSC/I2S interface. Its connections are a headphone jack
>> and an Line input jack.
>>
>> [Richard: this is based on an old patch from Nicolas that I forward
>> ported and reworked to use only device tree]
>>
>> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
>> Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
>> ---
>>   sound/soc/atmel/Kconfig         |   12 ++
>>   sound/soc/atmel/Makefile        |    2 +
>>   sound/soc/atmel/sam9x5_wm8731.c |  232
>> +++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 246 insertions(+)
>>   create mode 100644 sound/soc/atmel/sam9x5_wm8731.c
>>
>> diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
>> index 3fdd87f..f24d601 100644
>> --- a/sound/soc/atmel/Kconfig
>> +++ b/sound/soc/atmel/Kconfig
>> @@ -13,6 +13,7 @@ config SND_ATMEL_SOC_PDC
>>   config SND_ATMEL_SOC_DMA
>>         tristate
>>         depends on SND_ATMEL_SOC
>> +       select SND_SOC_DMAENGINE_PCM
>>
>>   config SND_ATMEL_SOC_SSC
>>         tristate
>> @@ -32,6 +33,17 @@ config SND_AT91_SOC_SAM9G20_WM8731
>>           Say Y if you want to add support for SoC audio on WM8731-based
>>           AT91sam9g20 evaluation board.
>>
>> +config SND_AT91_SOC_SAM9X5_WM8731
>> +       tristate "SoC Audio support for WM8731-based at91sam9x5 board"
>> +       depends on ATMEL_SSC && SND_ATMEL_SOC && SOC_AT91SAM9X5
>> +       select SND_ATMEL_SOC_SSC
>> +       select SND_ATMEL_SOC_DMA
>> +       select SND_ATMEL_SOC_PDC
>
>
> Not need to select SND_ATMEL_SOC_PDC
ok, I'll drop this

>> +       select SND_SOC_WM8731
>> +       help
>> +         Say Y if you want to add support for audio SoC on an
>> +         at91sam9x5 based board that is using WM8731 codec.
>> +
>>   config SND_AT91_SOC_AFEB9260
>>         tristate "SoC Audio support for AFEB9260 board"
>>         depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 &&
>> SND_ATMEL_SOC
>> diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
>> index 41967cc..7784c09 100644
>> --- a/sound/soc/atmel/Makefile
>> +++ b/sound/soc/atmel/Makefile
>> @@ -11,6 +11,8 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) +=
>> snd-soc-atmel_ssc_dai.o
>>
>>   # AT91 Machine Support
>>   snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
>> +snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
>>
>>   obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
>> +obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
>>   obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
>> diff --git a/sound/soc/atmel/sam9x5_wm8731.c
>> b/sound/soc/atmel/sam9x5_wm8731.c
>> new file mode 100644
>> index 0000000..83ca457
>> --- /dev/null
>> +++ b/sound/soc/atmel/sam9x5_wm8731.c
>> @@ -0,0 +1,232 @@
>> +/*
>> + * sam9x5_wm8731   --  SoC audio for AT91SAM9X5-based boards
>> + *                     that are using WM8731 as codec.
>> + *
>> + *  Copyright (C) 2011 Atmel,
>> + *               Nicolas Ferre <nicolas.ferre@atmel.com>
>> + *
>> + * Based on sam9g20_wm8731.c by:
>> + * Sedji Gaouaou <sedji.gaouaou@atmel.com>
>> + *
>> + * GPL
>> + */
>> +#include <linux/module.h>
>> +#include <linux/moduleparam.h>
>> +#include <linux/kernel.h>
>> +#include <linux/clk.h>
>> +#include <linux/timer.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/i2c.h>
>> +
>> +#include <linux/atmel-ssc.h>
>> +
>> +#include <sound/core.h>
>> +#include <sound/pcm.h>
>> +#include <sound/pcm_params.h>
>> +#include <sound/soc.h>
>> +
>> +#include <asm/mach-types.h>
>> +#include <mach/hardware.h>
>> +#include <mach/gpio.h>
>> +
>> +#include "../codecs/wm8731.h"
>> +#include "atmel-pcm.h"
>> +#include "atmel_ssc_dai.h"
>> +
>> +#define MCLK_RATE 12288000
>> +
>> +#define DRV_NAME "sam9x5-snd-wm8731"
>> +
>> +/*
>> + * Audio paths on at91sam9x5ek board:
>> + *
>> + *  |A| ------------> |      | ---R----> Headphone Jack
>> + *  |T| <----\        |  WM  | ---L--/
>> + *  |9| ---> CLK <--> | 8751 | <--R----- Line In Jack
>> + *  |1| <------------ |      | <--L--/
>> + */
>> +static const struct snd_soc_dapm_widget at91sam9x5ek_dapm_widgets[] = {
>> +       SND_SOC_DAPM_HP("Headphone Jack", NULL),
>> +       SND_SOC_DAPM_LINE("Line In Jack", NULL),
>> +};
>> +
>> +/*
>> + * Logic for a wm8731 as connected on a at91sam9x5 based board.
>> + */
>> +static int at91sam9x5ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
>> +{
>> +       struct snd_soc_codec *codec = rtd->codec;
>> +       struct snd_soc_dai *codec_dai = rtd->codec_dai;
>> +       struct snd_soc_dapm_context *dapm = &codec->dapm;
>> +       struct device *dev = rtd->dev;
>> +       int ret;
>> +
>> +       dev_dbg(dev, "ASoC: at91sam9x5ek_wm8731_init() called\n");
>> +
>> +       /*
>> +        * remove some not supported rates in relation with clock
>> +        * provided to the wm8731 codec
>> +        */
>> +       switch (MCLK_RATE) {
>> +       case 12288000:
>> +               codec_dai->driver->playback.rates &= SNDRV_PCM_RATE_8000 |
>> +                                                    SNDRV_PCM_RATE_32000
>> |
>> +                                                    SNDRV_PCM_RATE_48000
>> |
>> +                                                    SNDRV_PCM_RATE_96000;
>> +               codec_dai->driver->capture.rates &= SNDRV_PCM_RATE_8000 |
>> +                                                   SNDRV_PCM_RATE_32000 |
>> +                                                   SNDRV_PCM_RATE_48000 |
>> +                                                   SNDRV_PCM_RATE_96000;
>> +               break;
>> +       case 12000000:
>> +               /* all wm8731 rates supported */
>> +               break;
>> +       default:
>> +               dev_err(dev, "ASoC: Codec Master clock rate not
>> defined\n");
>> +               return -EINVAL;
>> +       }
>
>
> Here, I think we not need to use switch ... case, as the MCLK_RATE is hard
> code as 12288000.
yes, I'll drop the code for != 12288000

>
> -----------------------------------------------
>
>
>> +       /* set not connected pins */
>> +       snd_soc_dapm_nc_pin(dapm, "Mic Bias");
>> +       snd_soc_dapm_nc_pin(dapm, "MICIN");
>> +       snd_soc_dapm_nc_pin(dapm, "LOUT");
>> +       snd_soc_dapm_nc_pin(dapm, "ROUT");
>> +
>> +       /* always connected */
>> +       snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
>> +       snd_soc_dapm_enable_pin(dapm, "Line In Jack");
>
>
> ------------------------------------------------
> This part, not needed, as to the ASoC framework will deal with it.
ok !

>
>
>> +       /* set the codec system clock for DAC and ADC */
>> +       ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
>> +                                    MCLK_RATE, SND_SOC_CLOCK_IN);
>> +       if (ret < 0) {
>> +               dev_err(dev, "ASoC: Failed to set WM8731 SYSCLK: %d\n",
>> ret);
>> +               return ret;
>> +       }
>> +
>> +       /* signal a DAPM event */
>> +       snd_soc_dapm_sync(dapm);
>> +       return 0;
>> +}
>> +
>> +static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
>> +{
>> +       struct device_node *np = pdev->dev.of_node;
>> +       struct device_node *codec_np, *cpu_np;
>> +       struct snd_soc_card *card;
>> +       struct snd_soc_dai_link *dai;
>> +       int ret;
>> +
>> +       if (!np) {
>> +               dev_err(&pdev->dev, "No device node supplied\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       ret = atmel_ssc_set_audio(0);
>> +       if (ret != 0) {
>> +               dev_err(&pdev->dev,
>> +                       "ASoC: Failed to set SSC 0 for audio: %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
>> +       dai = devm_kzalloc(&pdev->dev, sizeof(*dai), GFP_KERNEL);
>> +       if (!dai || !card) {
>> +               ret = -ENOMEM;
>> +               goto out;
>> +       }
>> +
>> +       card->dev = &pdev->dev;
>> +       card->owner = THIS_MODULE;
>> +       card->dai_link = dai;
>> +       card->num_links = 1;
>> +       dai->name = "WM8731";
>> +       dai->stream_name = "WM8731 PCM";
>> +       dai->codec_dai_name = "wm8731-hifi";
>> +       dai->init = at91sam9x5ek_wm8731_init;
>> +       card->dapm_widgets = at91sam9x5ek_dapm_widgets;
>> +       card->num_dapm_widgets = ARRAY_SIZE(at91sam9x5ek_dapm_widgets);
>
>
> Will keep these as snd_soc_card and snd_soc_dai_link structure separately?
I don't really understand what you mean here.
do you mean that something like that will more explicit ? :
+       card->dai_link[0].name = "WM8731";
+       card->dai_link[0].stream_name = "WM8731 PCM";
+       card->dai_link[0].codec_dai_name = "wm8731-hifi";
+       card->dai_link[0].init = at91sam9x5ek_wm8731_init;

>
>
>> +       ret = snd_soc_of_parse_card_name(card, "atmel,model");
>> +       if (ret)
>> +               goto out;
>> +
>> +       ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing");
>> +       if (ret)
>> +               goto out;
>> +
>> +       codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
>> +       if (!codec_np) {
>> +               dev_err(&pdev->dev, "codec info missing\n");
>> +               ret = -EINVAL;
>> +               goto out;
>> +       }
>> +
>> +       dai->codec_of_node = codec_np;
>> +
>> +       cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
>> +       if (!cpu_np) {
>> +               dev_err(&pdev->dev, "ssc controller node missing\n");
>> +               ret = -EINVAL;
>> +               goto out;
>> +       }
>> +       dai->cpu_of_node = cpu_np;
>> +       dai->platform_of_node = cpu_np;
>> +
>> +       of_node_put(codec_np);
>> +       of_node_put(cpu_np);
>> +
>> +       dai->dai_fmt = snd_soc_of_parse_daifmt(np, "atmel,");
>> +
>> +       ret = snd_soc_register_card(card);
>> +       if (ret) {
>> +               dev_err(&pdev->dev,
>> +                       "ASoC: Platform device allocation failed\n");
>> +               goto out;
>> +       }
>> +
>> +       platform_set_drvdata(pdev, card);
>> +
>> +       dev_info(&pdev->dev, "ASoC: at91sam9x5ek_init ok\n");
>> +
>> +       return ret;
>> +
>> +out:
>> +       atmel_ssc_put_audio(0);
>> +       return ret;
>> +}
>> +
>> +static int sam9x5_wm8731_driver_remove(struct platform_device *pdev)
>> +{
>> +       struct snd_soc_card *card = platform_get_drvdata(pdev);
>> +
>> +       snd_soc_unregister_card(card);
>> +       atmel_ssc_put_audio(0);
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id sam9x5_wm8731_of_match[] = {
>> +       { .compatible = "atmel,sam9x5-audio-wm8731", },
>> +       {},
>> +};
>> +MODULE_DEVICE_TABLE(of, sam9x5_wm8731_of_match);
>> +
>> +static struct platform_driver sam9x5_wm8731_driver = {
>> +       .driver = {
>> +               .name = DRV_NAME,
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = of_match_ptr(sam9x5_wm8731_of_match),
>> +       },
>> +       .probe = sam9x5_wm8731_driver_probe,
>> +       .remove = sam9x5_wm8731_driver_remove,
>> +};
>> +module_platform_driver(sam9x5_wm8731_driver);
>> +
>> +/* Module information */
>> +MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>");
>> +MODULE_AUTHOR("Richard Genoud <richard.genoud@gmail.com>");
>> +MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAM9x5 - WM8731");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:" DRV_NAME);
>>
>
> Best Regards,
> Bo Shen

Thanks for reviewing !


Best regards
Richard.
Mark Brown - July 5, 2013, 6:06 p.m.
On Fri, Jul 05, 2013 at 05:15:05PM +0200, Richard Genoud wrote:
> 2013/7/2 Bo Shen <voice.shen@atmel.com>:

> >> From: Nicolas Ferre <nicolas.ferre@atmel.com>
> >>
> >> Description of the Asoc machine driver for an at91sam9x5 based board
> >> with a wm8731 audio DAC. Wm8731 is clocked by a crystal and used as a
> >> master on the SSC/I2S interface. Its connections are a headphone jack
> >> and an Line input jack.

Always CC relevant maintainers and mailing lists on patches...
Richard Genoud - July 5, 2013, 6:12 p.m.
2013/7/5 Mark Brown <broonie@kernel.org>:
> On Fri, Jul 05, 2013 at 05:15:05PM +0200, Richard Genoud wrote:
>> 2013/7/2 Bo Shen <voice.shen@atmel.com>:
>
>> >> From: Nicolas Ferre <nicolas.ferre@atmel.com>
>> >>
>> >> Description of the Asoc machine driver for an at91sam9x5 based board
>> >> with a wm8731 audio DAC. Wm8731 is clocked by a crystal and used as a
>> >> master on the SSC/I2S interface. Its connections are a headphone jack
>> >> and an Line input jack.
>
> Always CC relevant maintainers and mailing lists on patches...
Sorry Mark, I'll double check that on the next version.

Thanks !

Best Regards,
Richard.
Bo Shen - July 8, 2013, 2:01 a.m.
Hi Richard,

On 7/5/2013 23:15, Richard Genoud wrote:
>>> +       card->dev = &pdev->dev;
>>> >>+       card->owner = THIS_MODULE;
>>> >>+       card->dai_link = dai;
>>> >>+       card->num_links = 1;
>>> >>+       dai->name = "WM8731";
>>> >>+       dai->stream_name = "WM8731 PCM";
>>> >>+       dai->codec_dai_name = "wm8731-hifi";
>>> >>+       dai->init = at91sam9x5ek_wm8731_init;
>>> >>+       card->dapm_widgets = at91sam9x5ek_dapm_widgets;
>>> >>+       card->num_dapm_widgets = ARRAY_SIZE(at91sam9x5ek_dapm_widgets);
>> >
>> >
>> >Will keep these as snd_soc_card and snd_soc_dai_link structure separately?
> I don't really understand what you mean here.
> do you mean that something like that will more explicit ? :
> +       card->dai_link[0].name = "WM8731";
> +       card->dai_link[0].stream_name = "WM8731 PCM";
> +       card->dai_link[0].codec_dai_name = "wm8731-hifi";
> +       card->dai_link[0].init = at91sam9x5ek_wm8731_init;
>

I mean using structure to define snd_soc_card and snd_soc_dai_link, look 
like:
struct snd_soc_dai_link dai_link_name = {
	.name = xxx,
	.stream_name = xxx,
	...
};

struct snd_soc_card card_name = {
	.name = xxxx
	.ower = THIS_MODULE,
	.dai_link = &dai_link_name,
	...
};

In this way, I think it will be more clear than put them in code.

Best Regards,
Bo Shen
Richard Genoud - July 8, 2013, 7:55 a.m.
2013/7/8 Bo Shen <voice.shen@atmel.com>:
> Hi Richard,
>
>
> On 7/5/2013 23:15, Richard Genoud wrote:
>>>>
>>>> +       card->dev = &pdev->dev;
>>>> >>+       card->owner = THIS_MODULE;
>>>> >>+       card->dai_link = dai;
>>>> >>+       card->num_links = 1;
>>>> >>+       dai->name = "WM8731";
>>>> >>+       dai->stream_name = "WM8731 PCM";
>>>> >>+       dai->codec_dai_name = "wm8731-hifi";
>>>> >>+       dai->init = at91sam9x5ek_wm8731_init;
>>>> >>+       card->dapm_widgets = at91sam9x5ek_dapm_widgets;
>>>> >>+       card->num_dapm_widgets =
>>>> >> ARRAY_SIZE(at91sam9x5ek_dapm_widgets);
>>>
>>> >
>>> >
>>> >Will keep these as snd_soc_card and snd_soc_dai_link structure
>>> > separately?
>>
>> I don't really understand what you mean here.
>> do you mean that something like that will more explicit ? :
>> +       card->dai_link[0].name = "WM8731";
>> +       card->dai_link[0].stream_name = "WM8731 PCM";
>> +       card->dai_link[0].codec_dai_name = "wm8731-hifi";
>> +       card->dai_link[0].init = at91sam9x5ek_wm8731_init;
>>
>
> I mean using structure to define snd_soc_card and snd_soc_dai_link, look
> like:
> struct snd_soc_dai_link dai_link_name = {
>         .name = xxx,
>         .stream_name = xxx,
>         ...
> };
>
> struct snd_soc_card card_name = {
>         .name = xxxx
>         .ower = THIS_MODULE,
>         .dai_link = &dai_link_name,
>         ...
> };
>
> In this way, I think it will be more clear than put them in code.

ok, got it !

Thanks !

Richard.

Patch

diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 3fdd87f..f24d601 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -13,6 +13,7 @@  config SND_ATMEL_SOC_PDC
 config SND_ATMEL_SOC_DMA
 	tristate
 	depends on SND_ATMEL_SOC
+	select SND_SOC_DMAENGINE_PCM
 
 config SND_ATMEL_SOC_SSC
 	tristate
@@ -32,6 +33,17 @@  config SND_AT91_SOC_SAM9G20_WM8731
 	  Say Y if you want to add support for SoC audio on WM8731-based
 	  AT91sam9g20 evaluation board.
 
+config SND_AT91_SOC_SAM9X5_WM8731
+	tristate "SoC Audio support for WM8731-based at91sam9x5 board"
+	depends on ATMEL_SSC && SND_ATMEL_SOC && SOC_AT91SAM9X5
+	select SND_ATMEL_SOC_SSC
+	select SND_ATMEL_SOC_DMA
+	select SND_ATMEL_SOC_PDC
+	select SND_SOC_WM8731
+	help
+	  Say Y if you want to add support for audio SoC on an
+	  at91sam9x5 based board that is using WM8731 codec.
+
 config SND_AT91_SOC_AFEB9260
 	tristate "SoC Audio support for AFEB9260 board"
 	depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index 41967cc..7784c09 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -11,6 +11,8 @@  obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
 
 # AT91 Machine Support
 snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
+snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
 
 obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
+obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
 obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c
new file mode 100644
index 0000000..83ca457
--- /dev/null
+++ b/sound/soc/atmel/sam9x5_wm8731.c
@@ -0,0 +1,232 @@ 
+/*
+ * sam9x5_wm8731   --	SoC audio for AT91SAM9X5-based boards
+ *			that are using WM8731 as codec.
+ *
+ *  Copyright (C) 2011 Atmel,
+ *		  Nicolas Ferre <nicolas.ferre@atmel.com>
+ *
+ * Based on sam9g20_wm8731.c by:
+ * Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *
+ * GPL
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+#include <linux/atmel-ssc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+
+#include "../codecs/wm8731.h"
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
+
+#define MCLK_RATE 12288000
+
+#define DRV_NAME "sam9x5-snd-wm8731"
+
+/*
+ * Audio paths on at91sam9x5ek board:
+ *
+ *  |A| ------------> |      | ---R----> Headphone Jack
+ *  |T| <----\        |  WM  | ---L--/
+ *  |9| ---> CLK <--> | 8751 | <--R----- Line In Jack
+ *  |1| <------------ |      | <--L--/
+ */
+static const struct snd_soc_dapm_widget at91sam9x5ek_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_LINE("Line In Jack", NULL),
+};
+
+/*
+ * Logic for a wm8731 as connected on a at91sam9x5 based board.
+ */
+static int at91sam9x5ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct device *dev = rtd->dev;
+	int ret;
+
+	dev_dbg(dev, "ASoC: at91sam9x5ek_wm8731_init() called\n");
+
+	/*
+	 * remove some not supported rates in relation with clock
+	 * provided to the wm8731 codec
+	 */
+	switch (MCLK_RATE) {
+	case 12288000:
+		codec_dai->driver->playback.rates &= SNDRV_PCM_RATE_8000 |
+						     SNDRV_PCM_RATE_32000 |
+						     SNDRV_PCM_RATE_48000 |
+						     SNDRV_PCM_RATE_96000;
+		codec_dai->driver->capture.rates &= SNDRV_PCM_RATE_8000 |
+						    SNDRV_PCM_RATE_32000 |
+						    SNDRV_PCM_RATE_48000 |
+						    SNDRV_PCM_RATE_96000;
+		break;
+	case 12000000:
+		/* all wm8731 rates supported */
+		break;
+	default:
+		dev_err(dev, "ASoC: Codec Master clock rate not defined\n");
+		return -EINVAL;
+	}
+
+	/* set not connected pins */
+	snd_soc_dapm_nc_pin(dapm, "Mic Bias");
+	snd_soc_dapm_nc_pin(dapm, "MICIN");
+	snd_soc_dapm_nc_pin(dapm, "LOUT");
+	snd_soc_dapm_nc_pin(dapm, "ROUT");
+
+	/* always connected */
+	snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
+	snd_soc_dapm_enable_pin(dapm, "Line In Jack");
+
+	/* set the codec system clock for DAC and ADC */
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
+				     MCLK_RATE, SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		dev_err(dev, "ASoC: Failed to set WM8731 SYSCLK: %d\n", ret);
+		return ret;
+	}
+
+	/* signal a DAPM event */
+	snd_soc_dapm_sync(dapm);
+	return 0;
+}
+
+static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *codec_np, *cpu_np;
+	struct snd_soc_card *card;
+	struct snd_soc_dai_link *dai;
+	int ret;
+
+	if (!np) {
+		dev_err(&pdev->dev, "No device node supplied\n");
+		return -EINVAL;
+	}
+
+	ret = atmel_ssc_set_audio(0);
+	if (ret != 0) {
+		dev_err(&pdev->dev,
+			"ASoC: Failed to set SSC 0 for audio: %d\n", ret);
+		return ret;
+	}
+
+	card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
+	dai = devm_kzalloc(&pdev->dev, sizeof(*dai), GFP_KERNEL);
+	if (!dai || !card) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	card->dev = &pdev->dev;
+	card->owner = THIS_MODULE;
+	card->dai_link = dai;
+	card->num_links = 1;
+	dai->name = "WM8731";
+	dai->stream_name = "WM8731 PCM";
+	dai->codec_dai_name = "wm8731-hifi";
+	dai->init = at91sam9x5ek_wm8731_init;
+	card->dapm_widgets = at91sam9x5ek_dapm_widgets;
+	card->num_dapm_widgets = ARRAY_SIZE(at91sam9x5ek_dapm_widgets);
+
+	ret = snd_soc_of_parse_card_name(card, "atmel,model");
+	if (ret)
+		goto out;
+
+	ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing");
+	if (ret)
+		goto out;
+
+	codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
+	if (!codec_np) {
+		dev_err(&pdev->dev, "codec info missing\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	dai->codec_of_node = codec_np;
+
+	cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
+	if (!cpu_np) {
+		dev_err(&pdev->dev, "ssc controller node missing\n");
+		ret = -EINVAL;
+		goto out;
+	}
+	dai->cpu_of_node = cpu_np;
+	dai->platform_of_node = cpu_np;
+
+	of_node_put(codec_np);
+	of_node_put(cpu_np);
+
+	dai->dai_fmt = snd_soc_of_parse_daifmt(np, "atmel,");
+
+	ret = snd_soc_register_card(card);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"ASoC: Platform device allocation failed\n");
+		goto out;
+	}
+
+	platform_set_drvdata(pdev, card);
+
+	dev_info(&pdev->dev, "ASoC: at91sam9x5ek_init ok\n");
+
+	return ret;
+
+out:
+	atmel_ssc_put_audio(0);
+	return ret;
+}
+
+static int sam9x5_wm8731_driver_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_card(card);
+	atmel_ssc_put_audio(0);
+
+	return 0;
+}
+
+static const struct of_device_id sam9x5_wm8731_of_match[] = {
+	{ .compatible = "atmel,sam9x5-audio-wm8731", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, sam9x5_wm8731_of_match);
+
+static struct platform_driver sam9x5_wm8731_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(sam9x5_wm8731_of_match),
+	},
+	.probe = sam9x5_wm8731_driver_probe,
+	.remove = sam9x5_wm8731_driver_remove,
+};
+module_platform_driver(sam9x5_wm8731_driver);
+
+/* Module information */
+MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>");
+MODULE_AUTHOR("Richard Genoud <richard.genoud@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAM9x5 - WM8731");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);