Hi,
Here is some work I've done to make the sound work on sam9x5ek.
It's based on Nicolas Ferre's and Uwe Kleine-König's work.
(Atmel patch on 2.6.39 if I recall)
I'd like to have some advice, mainly on the 1st patch
"misc: atmel_ssc: add device tree DMA support".
I do not clearly see what is the right way to add DMA DT support for
this device, since it doesn't request a channel directly.
patches applies on next-20130701
[I let the original signed-off from Nicolas and Uwe in place, I don't
know if I should replace them by something like "originaly-signed-off-by"
since the code has been changed.]
Best regards,
Richard.
Nicolas Ferre (3):
sound: sam9x5_wm8731: machine driver for at91sam9x5 wm8731 boards
sound: atmel_ssc_dai: PM: actually stopping clock on suspend/resume
sound: wm8731: rework power management
Richard Genoud (10):
misc: atmel_ssc: add device tree DMA support
misc: atmel_ssc: keep the count of pdev->id
ARM: at91: DTS: sam9x5: add clock for SSC DT entry
ARM: at91: DTS: sam9x5: add SSC DMA parameters
ARM: AT91: DTS: sam9x5ek: add WM8731 codec
ARM: AT91: DTS: sam9x5ek: add sound configuration
ARM: AT91: DTS: sam9x5ek: enable SSC
sound: atmel-pcm: don't return ok if pcm-dma is not implemented
sound: atmel-pcm-dma: check pointer before dereference
sound: codec: wm8371: correct capture line/mic
arch/arm/boot/dts/at91sam9x5.dtsi | 3 +
arch/arm/boot/dts/at91sam9x5ek.dtsi | 27 ++++
arch/arm/mach-at91/at91sam9x5.c | 1 +
drivers/misc/atmel-ssc.c | 63 +++++++++
include/linux/atmel-ssc.h | 2 +
include/linux/platform_data/dma-atmel.h | 2 +
sound/soc/atmel/Kconfig | 12 ++
sound/soc/atmel/Makefile | 2 +
sound/soc/atmel/atmel-pcm-dma.c | 3 +
sound/soc/atmel/atmel-pcm.h | 2 +-
sound/soc/atmel/atmel_ssc_dai.c | 20 ++-
sound/soc/atmel/sam9x5_wm8731.c | 232 +++++++++++++++++++++++++++++++
sound/soc/codecs/wm8731.c | 10 +-
13 files changed, 368 insertions(+), 11 deletions(-)
create mode 100644 sound/soc/atmel/sam9x5_wm8731.c
--
1.7.10.4
The ssc device has to fill the at_dma_slave structure with the
device tree informations.
Doing a of_dma_request_slave_channel()+dma_release_channel() for that
seems wrong (or at least not very clean).
Signed-off-by: Richard Genoud <[email protected]>
---
drivers/misc/atmel-ssc.c | 56 +++++++++++++++++++++++++++++++
include/linux/atmel-ssc.h | 2 ++
include/linux/platform_data/dma-atmel.h | 2 ++
3 files changed, 60 insertions(+)
diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c
index f7b90661..3afbd82 100644
--- a/drivers/misc/atmel-ssc.c
+++ b/drivers/misc/atmel-ssc.c
@@ -19,7 +19,9 @@
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_dma.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/platform_data/dma-atmel.h>
/* Serialize access to ssc_list and user count */
static DEFINE_SPINLOCK(user_lock);
@@ -127,12 +129,57 @@ static inline const struct atmel_ssc_platform_data * __init
platform_get_device_id(pdev)->driver_data;
}
+static int atmel_ssc_get_dma_data(struct device_node *np,
+ struct at_dma_slave *sdata)
+{
+ struct dma_chan *chan;
+ struct at_dma_slave *rx_data;
+ int err = -1;
+
+ /*
+ * FIXME: this is clearly not the right way to do it.
+ * In order to fill struct at_dma_slave with both rx and tx data,
+ * we request and release both channels.
+ * Et voila ! We've got all the whole structure !
+ * upside: it works(R)
+ * downside: feels wrong, dirty, not optimized...
+ */
+ chan = of_dma_request_slave_channel(np, "tx");
+ if (!chan)
+ return err;
+
+ if (chan->private)
+ memcpy(sdata, chan->private, sizeof(*sdata));
+ else
+ goto out;
+
+ dma_release_channel(chan);
+
+ chan = of_dma_request_slave_channel(np, "rx");
+ if (!chan)
+ goto out;
+
+ if (chan->private) {
+ rx_data = chan->private;
+ sdata->cfg &= ~(ATC_SRC_PER_MSB(0xff) | ATC_SRC_PER(0xff));
+ sdata->cfg |= ATC_GET_SRC_ID(rx_data->cfg);
+ err = 0;
+ }
+
+out:
+ if (chan)
+ dma_release_channel(chan);
+
+ return err;
+}
+
static int ssc_probe(struct platform_device *pdev)
{
struct resource *regs;
struct ssc_device *ssc;
const struct atmel_ssc_platform_data *plat_dat;
struct pinctrl *pinctrl;
+ struct device_node *of = pdev->dev.of_node;
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
@@ -166,6 +213,15 @@ static int ssc_probe(struct platform_device *pdev)
return -ENXIO;
}
+ /* populate platform_data from device tree */
+ if (ssc->pdata && ssc->pdata->use_dma && of) {
+ if (atmel_ssc_get_dma_data(of, &ssc->pdata->dma_slave)) {
+ dev_err(&pdev->dev, "could not get DMA\n");
+ return -EINVAL;
+ }
+ }
+ ssc->pdev->dev.platform_data = &ssc->pdata->dma_slave;
+
/* disable all interrupts */
clk_prepare_enable(ssc->clk);
ssc_writel(ssc->regs, IDR, -1);
diff --git a/include/linux/atmel-ssc.h b/include/linux/atmel-ssc.h
index deb0ae5..80c83ee 100644
--- a/include/linux/atmel-ssc.h
+++ b/include/linux/atmel-ssc.h
@@ -1,12 +1,14 @@
#ifndef __INCLUDE_ATMEL_SSC_H
#define __INCLUDE_ATMEL_SSC_H
+#include <linux/platform_data/dma-atmel.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/io.h>
struct atmel_ssc_platform_data {
int use_dma;
+ struct at_dma_slave dma_slave;
};
struct ssc_device {
diff --git a/include/linux/platform_data/dma-atmel.h b/include/linux/platform_data/dma-atmel.h
index e95f19c..0d8b9d6 100644
--- a/include/linux/platform_data/dma-atmel.h
+++ b/include/linux/platform_data/dma-atmel.h
@@ -44,11 +44,13 @@ struct at_dma_slave {
#define ATC_SRC_H2SEL_SW (0x0 << 9)
#define ATC_SRC_H2SEL_HW (0x1 << 9)
#define ATC_SRC_PER_MSB(h) (ATC_PER_MSB(h) << 10) /* Channel src rq (most significant bits) */
+#define ATC_GET_SRC_ID(h) ((((h) >> 6) & 0x3U) | ((h) & 0xFU)) /* Retrieve channel src id */
#define ATC_DST_REP (0x1 << 12) /* Destination Replay Mod */
#define ATC_DST_H2SEL (0x1 << 13) /* Destination Handshaking Mod */
#define ATC_DST_H2SEL_SW (0x0 << 13)
#define ATC_DST_H2SEL_HW (0x1 << 13)
#define ATC_DST_PER_MSB(h) (ATC_PER_MSB(h) << 14) /* Channel dst rq (most significant bits) */
+#define ATC_GET_DST_ID(h) ((((h) >> 10) & 0x3U) | (((h) >> 4) & 0xFU)) /* Retrieve channel dst id */
#define ATC_SOD (0x1 << 16) /* Stop On Done */
#define ATC_LOCK_IF (0x1 << 20) /* Interface Lock */
#define ATC_LOCK_B (0x1 << 21) /* AHB Bus Lock */
--
1.7.10.4
Signed-off-by: Richard Genoud <[email protected]>
---
arch/arm/mach-at91/at91sam9x5.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c
index 2abee66..191eb4b 100644
--- a/arch/arm/mach-at91/at91sam9x5.c
+++ b/arch/arm/mach-at91/at91sam9x5.c
@@ -233,6 +233,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("mci_clk", "f000c000.mmc", &mmc1_clk),
CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma0_clk),
CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller", &dma1_clk),
+ CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.0", &ssc_clk),
CLKDEV_CON_DEV_ID("pclk", "f0010000.ssc", &ssc_clk),
CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk),
CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk),
--
1.7.10.4
The WM8731 codec on sam9x5ek board is on i2c, address 1A
Signed-off-by: Richard Genoud <[email protected]>
---
arch/arm/boot/dts/at91sam9x5ek.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/at91sam9x5ek.dtsi b/arch/arm/boot/dts/at91sam9x5ek.dtsi
index b753855..a81a1a6 100644
--- a/arch/arm/boot/dts/at91sam9x5ek.dtsi
+++ b/arch/arm/boot/dts/at91sam9x5ek.dtsi
@@ -59,6 +59,11 @@
i2c0: i2c@f8010000 {
status = "okay";
+
+ wm8731: wm8731@1a {
+ compatible = "wm8731";
+ reg = <0x1a>;
+ };
};
pinctrl@fffff400 {
--
1.7.10.4
From: Nicolas Ferre <[email protected]>
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 <[email protected]>
Signed-off-by: Uwe Kleine-König <[email protected]>
Signed-off-by: Richard Genoud <[email protected]>
---
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
+ 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 <[email protected]>
+ *
+ * Based on sam9g20_wm8731.c by:
+ * Sedji Gaouaou <[email protected]>
+ *
+ * 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 <[email protected]>");
+MODULE_AUTHOR("Richard Genoud <[email protected]>");
+MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAM9x5 - WM8731");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
--
1.7.10.4
If platform_data is NULL, filter() is called with a NULL slave
parameter.
Signed-off-by: Richard Genoud <[email protected]>
---
sound/soc/atmel/atmel-pcm-dma.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c
index 1d38fd0..b20dbba 100644
--- a/sound/soc/atmel/atmel-pcm-dma.c
+++ b/sound/soc/atmel/atmel-pcm-dma.c
@@ -96,6 +96,9 @@ static bool filter(struct dma_chan *chan, void *slave)
{
struct at_dma_slave *sl = slave;
+ if (!sl)
+ return false;
+
if (sl->dma_dev == chan->device->dev) {
chan->private = sl;
return true;
--
1.7.10.4
From: Nicolas Ferre <[email protected]>
Stop SSC clock on suspend/resume cycle checking if the controller is actually
initialized. This will save power while sleeping.
Signed-off-by: Nicolas Ferre <[email protected]>
Signed-off-by: Uwe Kleine-König <[email protected]>
---
sound/soc/atmel/atmel_ssc_dai.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index f3fdfa0..14da27a 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -659,12 +659,10 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
#ifdef CONFIG_PM
static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
{
- struct atmel_ssc_info *ssc_p;
+ struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
if (!cpu_dai->active)
- return 0;
-
- ssc_p = &ssc_info[cpu_dai->id];
+ goto out;
/* Save the status register before disabling transmit and receive */
ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
@@ -680,6 +678,11 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
+out:
+ if (ssc_p->initialized) {
+ pr_debug("atmel_ssc_dai: suspend - stop clock\n");
+ clk_disable(ssc_p->ssc->clk);
+ }
return 0;
}
@@ -687,14 +690,17 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
{
- struct atmel_ssc_info *ssc_p;
+ struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
u32 cr;
+ if (ssc_p->initialized) {
+ pr_debug("atmel_ssc_dai: resume - restart clock\n");
+ clk_enable(ssc_p->ssc->clk);
+ }
+
if (!cpu_dai->active)
return 0;
- ssc_p = &ssc_info[cpu_dai->id];
-
/* restore SSC register settings */
ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
--
1.7.10.4
With the "switch" text, we can't enable the line and mic capture in
alsamixer.
Signed-off-by: Richard Genoud <[email protected]>
---
sound/soc/codecs/wm8731.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 3cf6b20..dae1403 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -164,10 +164,10 @@ SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V,
SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0,
in_tlv),
-SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),
+SOC_DOUBLE_R("Line Capture", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),
SOC_SINGLE_TLV("Mic Boost Volume", WM8731_APANA, 0, 1, 0, mic_tlv),
-SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1),
+SOC_SINGLE("Mic Capture", WM8731_APANA, 1, 1, 1),
SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1,
sidetone_tlv),
--
1.7.10.4
From: Nicolas Ferre <[email protected]>
- preserve crystal oscillator across suspend/resume sequence:
enabled by default,it should be kept enabled on resume.
- if codec is in active state: set the active bit at resume time.
Signed-off-by: Nicolas Ferre <[email protected]>
Signed-off-by: Uwe Kleine-König <[email protected]>
---
sound/soc/codecs/wm8731.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 5276062..3cf6b20 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -465,7 +465,9 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, WM8731_PWR, reg | 0x0040);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, WM8731_PWR, 0xffff);
+ snd_soc_write(codec, WM8731_ACTIVE, 0x0);
+ /* standby: keep crystal oscillator enabled */
+ snd_soc_write(codec, WM8731_PWR, 0x00df);
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
wm8731->supplies);
regcache_mark_dirty(wm8731->regmap);
@@ -516,6 +518,8 @@ static int wm8731_suspend(struct snd_soc_codec *codec)
static int wm8731_resume(struct snd_soc_codec *codec)
{
wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ if (codec->active)
+ snd_soc_write(codec, WM8731_ACTIVE, 0x0001);
return 0;
}
--
1.7.10.4
If DMA is not selected, atmel_pcm_dma_platform_register() should fail.
Like that, the driver knows it can't use it.
Signed-off-by: Richard Genoud <[email protected]>
---
sound/soc/atmel/atmel-pcm.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h
index 12ae814..c0a01fa 100644
--- a/sound/soc/atmel/atmel-pcm.h
+++ b/sound/soc/atmel/atmel-pcm.h
@@ -109,7 +109,7 @@ void atmel_pcm_dma_platform_unregister(struct device *dev);
#else
static inline int atmel_pcm_dma_platform_register(struct device *dev)
{
- return 0;
+ return -1;
}
static inline void atmel_pcm_dma_platform_unregister(struct device *dev)
{
--
1.7.10.4
Enable the SSC needed for the WM8731 codec
Signed-off-by: Richard Genoud <[email protected]>
---
arch/arm/boot/dts/at91sam9x5ek.dtsi | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm/boot/dts/at91sam9x5ek.dtsi b/arch/arm/boot/dts/at91sam9x5ek.dtsi
index 77e4073..4f3584c 100644
--- a/arch/arm/boot/dts/at91sam9x5ek.dtsi
+++ b/arch/arm/boot/dts/at91sam9x5ek.dtsi
@@ -95,6 +95,10 @@
watchdog@fffffe40 {
status = "okay";
};
+
+ ssc0: ssc@f0010000 {
+ status = "okay";
+ };
};
usb0: ohci@00600000 {
--
1.7.10.4
The sam9x5ek board has 2 jacks:
headphone wired on RHPOUT/LHPOUT of the wm8731
line in wired on LLINEIN/RLINEIN of the wm8731
Signed-off-by: Richard Genoud <[email protected]>
---
arch/arm/boot/dts/at91sam9x5ek.dtsi | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/arch/arm/boot/dts/at91sam9x5ek.dtsi b/arch/arm/boot/dts/at91sam9x5ek.dtsi
index a81a1a6..77e4073 100644
--- a/arch/arm/boot/dts/at91sam9x5ek.dtsi
+++ b/arch/arm/boot/dts/at91sam9x5ek.dtsi
@@ -109,4 +109,22 @@
status = "okay";
};
};
+
+ sound {
+ compatible = "atmel,sam9x5-audio-wm8731";
+
+ atmel,model = "wm8731 @ AT91SAM9X5EK";
+
+ atmel,audio-routing =
+ "Headphone Jack", "RHPOUT",
+ "Headphone Jack", "LHPOUT",
+ "LLINEIN", "Line In Jack",
+ "RLINEIN", "Line In Jack";
+
+ atmel,ssc-controller = <&ssc0>;
+ atmel,audio-codec = <&wm8731>;
+ atmel,format = "i2s";
+ atmel,bitclock-master;
+ atmel,frame-master;
+ };
};
--
1.7.10.4
Signed-off-by: Richard Genoud <[email protected]>
---
arch/arm/boot/dts/at91sam9x5.dtsi | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi
index 57d45f5..cf78ac0 100644
--- a/arch/arm/boot/dts/at91sam9x5.dtsi
+++ b/arch/arm/boot/dts/at91sam9x5.dtsi
@@ -542,6 +542,9 @@
compatible = "atmel,at91sam9g45-ssc";
reg = <0xf0010000 0x4000>;
interrupts = <28 IRQ_TYPE_LEVEL_HIGH 5>;
+ dmas = <&dma0 1 AT91_DMA_CFG_PER_ID(13)>,
+ <&dma0 1 AT91_DMA_CFG_PER_ID(14)>;
+ dma-names = "tx", "rx";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>;
status = "disabled";
--
1.7.10.4
With device tree, pdev->id is always -1, so we introduce a local
counter.
Signed-off-by: Richard Genoud <[email protected]>
---
drivers/misc/atmel-ssc.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c
index 3afbd82..d1ec5ab 100644
--- a/drivers/misc/atmel-ssc.c
+++ b/drivers/misc/atmel-ssc.c
@@ -173,6 +173,12 @@ out:
return err;
}
+/* counter of ssc devive instances.
+ * With device tree pdev->id is always -1, so we have to keep the
+ * count ourselves
+ */
+static int ssc_device_id;
+
static int ssc_probe(struct platform_device *pdev)
{
struct resource *regs;
@@ -235,6 +241,7 @@ static int ssc_probe(struct platform_device *pdev)
}
spin_lock(&user_lock);
+ pdev->id = ssc_device_id++;
list_add_tail(&ssc->list, &ssc_list);
spin_unlock(&user_lock);
--
1.7.10.4
Hi Richard,
On 7/1/2013 16:39, Richard Genoud wrote:
> The ssc device has to fill the at_dma_slave structure with the
> device tree informations.
> Doing a of_dma_request_slave_channel()+dma_release_channel() for that
> seems wrong (or at least not very clean).
Please hold on of this, as to the ASoC dmaengine will deal with this.
So, we not need do it manually.
Now, I am working on it. And will send out the patch soon after testing OK.
> Signed-off-by: Richard Genoud<[email protected]>
> ---
> drivers/misc/atmel-ssc.c | 56 +++++++++++++++++++++++++++++++
> include/linux/atmel-ssc.h | 2 ++
> include/linux/platform_data/dma-atmel.h | 2 ++
> 3 files changed, 60 insertions(+)
Best Regards,
Bo Shen
Hi Richard,
On 7/1/2013 16:39, Richard Genoud wrote:
> With device tree, pdev->id is always -1, so we introduce a local
> counter.
>
> Signed-off-by: Richard Genoud <[email protected]>
> ---
> drivers/misc/atmel-ssc.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c
> index 3afbd82..d1ec5ab 100644
> --- a/drivers/misc/atmel-ssc.c
> +++ b/drivers/misc/atmel-ssc.c
> @@ -173,6 +173,12 @@ out:
> return err;
> }
>
> +/* counter of ssc devive instances.
> + * With device tree pdev->id is always -1, so we have to keep the
> + * count ourselves
> + */
> +static int ssc_device_id;
Do we really need this? If Yes, would it better to get from device
through of_alias_get_id?
> +
> static int ssc_probe(struct platform_device *pdev)
> {
> struct resource *regs;
> @@ -235,6 +241,7 @@ static int ssc_probe(struct platform_device *pdev)
> }
>
> spin_lock(&user_lock);
> + pdev->id = ssc_device_id++;
> list_add_tail(&ssc->list, &ssc_list);
> spin_unlock(&user_lock);
>
>
Best Regards,
Bo Shen
Hi Richard,
On 7/1/2013 16:39, Richard Genoud wrote:
> Signed-off-by: Richard Genoud <[email protected]>
> ---
> arch/arm/mach-at91/at91sam9x5.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c
> index 2abee66..191eb4b 100644
> --- a/arch/arm/mach-at91/at91sam9x5.c
> +++ b/arch/arm/mach-at91/at91sam9x5.c
> @@ -233,6 +233,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
> CLKDEV_CON_DEV_ID("mci_clk", "f000c000.mmc", &mmc1_clk),
> CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma0_clk),
> CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller", &dma1_clk),
> + CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.0", &ssc_clk),
Actually, we don't use this anymore. Am I right?
> CLKDEV_CON_DEV_ID("pclk", "f0010000.ssc", &ssc_clk),
> CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk),
> CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk),
>
Best Regards,
Bo Shen
Hi Richard,
Will move this patch before 5, 6, 7?
On 7/1/2013 16:39, Richard Genoud wrote:
> From: Nicolas Ferre <[email protected]>
>
> 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 <[email protected]>
> Signed-off-by: Uwe Kleine-König <[email protected]>
> Signed-off-by: Richard Genoud <[email protected]>
> ---
> 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 <[email protected]>
> + *
> + * Based on sam9g20_wm8731.c by:
> + * Sedji Gaouaou <[email protected]>
> + *
> + * 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 <[email protected]>");
> +MODULE_AUTHOR("Richard Genoud <[email protected]>");
> +MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAM9x5 - WM8731");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:" DRV_NAME);
>
Best Regards,
Bo Shen
2013/7/2 Bo Shen <[email protected]>:
> Hi Richard,
>
>
> On 7/1/2013 16:39, Richard Genoud wrote:
>>
>> The ssc device has to fill the at_dma_slave structure with the
>> device tree informations.
>> Doing a of_dma_request_slave_channel()+dma_release_channel() for that
>> seems wrong (or at least not very clean).
>
>
> Please hold on of this, as to the ASoC dmaengine will deal with this. So, we
> not need do it manually.
>
> Now, I am working on it. And will send out the patch soon after testing OK.
Ok, I stay tuned !
Thanks !
Best regards,
Richard
Hi Richard,
On 7/2/2013 16:13, Richard Genoud wrote:
>> Please hold on of this, as to the ASoC dmaengine will deal with this. So, we
>> >not need do it manually.
>> >
>> >Now, I am working on it. And will send out the patch soon after testing OK.
> Ok, I stay tuned !
I think you can go on working with the two patches I send just now.
[1]: ASoC: atmel_ssc_dai: move set dma data to startup callback
https://patchwork.kernel.org/patch/2811241/
[2]: ASoC: atmel-pcm: use generic dmaengine framework
https://patchwork.kernel.org/patch/2811261/
I use your patches 4 ~ 8 do a quick testing, it works. Please note the
name of asound.state under /etc in your rootfs should the same with
atmel,model in patch 6.
> Thanks !
>
> Best regards,
> Richard
Best Regards,
Bo Shen
2013/7/2 Bo Shen <[email protected]>:
> Hi Richard,
>
>
> On 7/2/2013 16:13, Richard Genoud wrote:
>>>
>>> Please hold on of this, as to the ASoC dmaengine will deal with this. So,
>>> we
>>> >not need do it manually.
>>> >
>>> >Now, I am working on it. And will send out the patch soon after testing
>>> > OK.
>>
>> Ok, I stay tuned !
>
>
> I think you can go on working with the two patches I send just now.
> [1]: ASoC: atmel_ssc_dai: move set dma data to startup callback
> https://patchwork.kernel.org/patch/2811241/
>
> [2]: ASoC: atmel-pcm: use generic dmaengine framework
> https://patchwork.kernel.org/patch/2811261/
>
> I use your patches 4 ~ 8 do a quick testing, it works. Please note the name
> of asound.state under /etc in your rootfs should the same with atmel,model
> in patch 6.
Thanks for testing !
I was just doing the same test, it works with
aplay -Dplug:default good_music.wav
but there's a violent hang (kernel stops, no trace) when I try the record :
arecord -v -V stereo -Dplug:default -f cd -t wav -c 2 /tmp/toto.wav
last thing I see is :
dma dma0chan3: atc_control (3)
I'll try to trace that.
Best regards,
Richard.
2013/7/3 Richard Genoud <[email protected]>:
> 2013/7/2 Bo Shen <[email protected]>:
>> Hi Richard,
>>
>>
>> On 7/2/2013 16:13, Richard Genoud wrote:
>>>>
>>>> Please hold on of this, as to the ASoC dmaengine will deal with this. So,
>>>> we
>>>> >not need do it manually.
>>>> >
>>>> >Now, I am working on it. And will send out the patch soon after testing
>>>> > OK.
>>>
>>> Ok, I stay tuned !
>>
>>
>> I think you can go on working with the two patches I send just now.
>> [1]: ASoC: atmel_ssc_dai: move set dma data to startup callback
>> https://patchwork.kernel.org/patch/2811241/
>>
>> [2]: ASoC: atmel-pcm: use generic dmaengine framework
>> https://patchwork.kernel.org/patch/2811261/
>>
>> I use your patches 4 ~ 8 do a quick testing, it works. Please note the name
>> of asound.state under /etc in your rootfs should the same with atmel,model
>> in patch 6.
>
> Thanks for testing !
> I was just doing the same test, it works with
> aplay -Dplug:default good_music.wav
>
> but there's a violent hang (kernel stops, no trace) when I try the record :
> arecord -v -V stereo -Dplug:default -f cd -t wav -c 2 /tmp/toto.wav
> last thing I see is :
> dma dma0chan3: atc_control (3)
>
> I'll try to trace that.
I think it's DMA related.
the last thing done by the kernel is:
i2c i2c-0: i2c_outb: 0x34 A
i2c i2c-0: i2c_outb: 0x0c A
i2c i2c-0: i2c_outb: 0x5a A
meaning: enable power on, LINE IN, ADC, OSC, on the WM8731
so, after that, data is comming from the codec to the SSC and then is
handled by the DMA.
there must be something nasty on the DMA bus to hang everything like that...
Richard.
Hi Richard,
On 7/3/2013 23:51, Richard Genoud wrote:
>> >but there's a violent hang (kernel stops, no trace) when I try the record :
>> >arecord -v -V stereo -Dplug:default -f cd -t wav -c 2 /tmp/toto.wav
>> >last thing I see is :
>> >dma dma0chan3: atc_control (3)
I don't meet this issue. Playback and recording works well on my side on
at91sam9g35ek board.
>> >I'll try to trace that.
> I think it's DMA related.
> the last thing done by the kernel is:
> i2c i2c-0: i2c_outb: 0x34 A
> i2c i2c-0: i2c_outb: 0x0c A
> i2c i2c-0: i2c_outb: 0x5a A
> meaning: enable power on, LINE IN, ADC, OSC, on the WM8731
> so, after that, data is comming from the codec to the SSC and then is
> handled by the DMA.
> there must be something nasty on the DMA bus to hang everything like that...
Will you try i2c without DMA support to test this issue?
> Richard.
Best Regards,
Bo Shen
2013/7/4 Bo Shen <[email protected]>:
> Hi Richard,
>
>
> On 7/3/2013 23:51, Richard Genoud wrote:
>>>
>>> >but there's a violent hang (kernel stops, no trace) when I try the
>>> > record :
>>> >arecord -v -V stereo -Dplug:default -f cd -t wav -c 2 /tmp/toto.wav
>>> >last thing I see is :
>>> >dma dma0chan3: atc_control (3)
>
>
> I don't meet this issue. Playback and recording works well on my side on
> at91sam9g35ek board.
>
>
>>> >I'll try to trace that.
>>
>> I think it's DMA related.
>> the last thing done by the kernel is:
>> i2c i2c-0: i2c_outb: 0x34 A
>> i2c i2c-0: i2c_outb: 0x0c A
>> i2c i2c-0: i2c_outb: 0x5a A
>> meaning: enable power on, LINE IN, ADC, OSC, on the WM8731
>> so, after that, data is comming from the codec to the SSC and then is
>> handled by the DMA.
>> there must be something nasty on the DMA bus to hang everything like
>> that...
>
>
> Will you try i2c without DMA support to test this issue?
Ok, I nailed it !
To be sure we are on the same base, here is what I have done:
onto next-20130704:
- your 5 patches:
ASoC: atmel_ssc_dai: move set dma data to startup callback
ASoC: atmel_ssc_dai: add error mask define
ASoC: atmel-pcm-dma: move prepare for dma to dai prepare
ARM: atmel-ssc: change phybase type to dma_addr_t
ASoC: atmel-pcm: use generic dmaengine framework
- my patches 4-8:
ARM: at91: DTS: sam9x5: add SSC DMA parameters
ARM: AT91: DTS: sam9x5ek: add WM8731 codec
ARM: AT91: DTS: sam9x5ek: add sound configuration
ARM: AT91: DTS: sam9x5ek: enable SSC
sound: sam9x5_wm8731: machine driver for at91sam9x5 wm8731 boards
To be sure that dma-I2c doesn't disturb something, I use i2c gpio bitbang:
diff --git a/arch/arm/boot/dts/at91sam9x5ek.dtsi
b/arch/arm/boot/dts/at91sam9x5ek.dtsi
index 6684d4b..53a991e 100644
--- a/arch/arm/boot/dts/at91sam9x5ek.dtsi
+++ b/arch/arm/boot/dts/at91sam9x5ek.dtsi
@@ -57,15 +57,6 @@
status = "okay";
};
- i2c0: i2c@f8010000 {
- status = "okay";
-
- wm8731: wm8731@1a {
- compatible = "wm8731";
- reg = <0x1a>;
- };
- };
-
pinctrl@fffff400 {
mmc0 {
pinctrl_board_mmc0: mmc0-board {
@@ -114,6 +105,15 @@
};
};
+ i2c@0 {
+ status = "okay";
+
+ wm8731: wm8731@1a {
+ compatible = "wm8731";
+ reg = <0x1a>;
+ };
+ };
+
sound {
compatible = "atmel,sam9x5-audio-wm8731";
with that configuration, it hangs when I do a:
arecord -v -V stereo -Dplug:default -f cd -t wav -c 2 /tmp/toto.wav
Now, if I remove the overrun error on ssc in rx_mask:
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 0ecf356..c04e825 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -83,7 +83,6 @@ static struct atmel_ssc_mask ssc_rx_mask = {
.ssc_disable = SSC_BIT(CR_RXDIS),
.ssc_endx = SSC_BIT(SR_ENDRX),
.ssc_endbuf = SSC_BIT(SR_RXBUFF),
- .ssc_error = SSC_BIT(SR_OVRUN),
.pdc_enable = ATMEL_PDC_RXTEN,
.pdc_disable = ATMEL_PDC_RXTDIS,
};
It doesn't hang any more doing a simple record.
BUT it still hangs if we do record and play at the same time.
Removing the overrun on tx_mask prevent the hang:
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 0ecf356..41e15c2 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -73,7 +73,6 @@ static struct atmel_ssc_mask ssc_tx_mask = {
.ssc_disable = SSC_BIT(CR_TXDIS),
.ssc_endx = SSC_BIT(SR_ENDTX),
.ssc_endbuf = SSC_BIT(SR_TXBUFE),
- .ssc_error = SSC_BIT(SR_OVRUN),
.pdc_enable = ATMEL_PDC_TXTEN,
.pdc_disable = ATMEL_PDC_TXTDIS,
};
i.e. when I revert "ASoC: atmel_ssc_dai: add error mask define", I
don't see any hang.
Could you test and confirm that behaviour please ?
I attached a the (simple) .config I used for the tests.
PS: I hope the patches won't be mangled by gmail...
Best regards,
Richard.
Hi Richard,
On 7/4/2013 21:44, Richard Genoud wrote:
> 2013/7/4 Bo Shen <[email protected]>:
>> Hi Richard,
>>
>>
>> On 7/3/2013 23:51, Richard Genoud wrote:
>>>>
>>>>> but there's a violent hang (kernel stops, no trace) when I try the
>>>>> record :
>>>>> arecord -v -V stereo -Dplug:default -f cd -t wav -c 2 /tmp/toto.wav
>>>>> last thing I see is :
>>>>> dma dma0chan3: atc_control (3)
>>
>>
>> I don't meet this issue. Playback and recording works well on my side on
>> at91sam9g35ek board.
>>
>>
>>>>> I'll try to trace that.
>>>
>>> I think it's DMA related.
>>> the last thing done by the kernel is:
>>> i2c i2c-0: i2c_outb: 0x34 A
>>> i2c i2c-0: i2c_outb: 0x0c A
>>> i2c i2c-0: i2c_outb: 0x5a A
>>> meaning: enable power on, LINE IN, ADC, OSC, on the WM8731
>>> so, after that, data is comming from the codec to the SSC and then is
>>> handled by the DMA.
>>> there must be something nasty on the DMA bus to hang everything like
>>> that...
>>
>>
>> Will you try i2c without DMA support to test this issue?
>
> Ok, I nailed it !
>
> To be sure we are on the same base, here is what I have done:
> onto next-20130704:
> - your 5 patches:
> ASoC: atmel_ssc_dai: move set dma data to startup callback
> ASoC: atmel_ssc_dai: add error mask define
> ASoC: atmel-pcm-dma: move prepare for dma to dai prepare
> ARM: atmel-ssc: change phybase type to dma_addr_t
> ASoC: atmel-pcm: use generic dmaengine framework
> - my patches 4-8:
> ARM: at91: DTS: sam9x5: add SSC DMA parameters
> ARM: AT91: DTS: sam9x5ek: add WM8731 codec
> ARM: AT91: DTS: sam9x5ek: add sound configuration
> ARM: AT91: DTS: sam9x5ek: enable SSC
> sound: sam9x5_wm8731: machine driver for at91sam9x5 wm8731 boards
>
> To be sure that dma-I2c doesn't disturb something, I use i2c gpio bitbang:
> diff --git a/arch/arm/boot/dts/at91sam9x5ek.dtsi
> b/arch/arm/boot/dts/at91sam9x5ek.dtsi
> index 6684d4b..53a991e 100644
> --- a/arch/arm/boot/dts/at91sam9x5ek.dtsi
> +++ b/arch/arm/boot/dts/at91sam9x5ek.dtsi
> @@ -57,15 +57,6 @@
> status = "okay";
> };
>
> - i2c0: i2c@f8010000 {
> - status = "okay";
> -
> - wm8731: wm8731@1a {
> - compatible = "wm8731";
> - reg = <0x1a>;
> - };
> - };
> -
> pinctrl@fffff400 {
> mmc0 {
> pinctrl_board_mmc0: mmc0-board {
> @@ -114,6 +105,15 @@
> };
> };
>
> + i2c@0 {
> + status = "okay";
> +
> + wm8731: wm8731@1a {
> + compatible = "wm8731";
> + reg = <0x1a>;
> + };
> + };
> +
> sound {
> compatible = "atmel,sam9x5-audio-wm8731";
>
> with that configuration, it hangs when I do a:
> arecord -v -V stereo -Dplug:default -f cd -t wav -c 2 /tmp/toto.wav
>
> Now, if I remove the overrun error on ssc in rx_mask:
> diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
> index 0ecf356..c04e825 100644
> --- a/sound/soc/atmel/atmel_ssc_dai.c
> +++ b/sound/soc/atmel/atmel_ssc_dai.c
> @@ -83,7 +83,6 @@ static struct atmel_ssc_mask ssc_rx_mask = {
> .ssc_disable = SSC_BIT(CR_RXDIS),
> .ssc_endx = SSC_BIT(SR_ENDRX),
> .ssc_endbuf = SSC_BIT(SR_RXBUFF),
> - .ssc_error = SSC_BIT(SR_OVRUN),
> .pdc_enable = ATMEL_PDC_RXTEN,
> .pdc_disable = ATMEL_PDC_RXTDIS,
> };
>
> It doesn't hang any more doing a simple record.
> BUT it still hangs if we do record and play at the same time.
>
> Removing the overrun on tx_mask prevent the hang:
> diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
> index 0ecf356..41e15c2 100644
> --- a/sound/soc/atmel/atmel_ssc_dai.c
> +++ b/sound/soc/atmel/atmel_ssc_dai.c
> @@ -73,7 +73,6 @@ static struct atmel_ssc_mask ssc_tx_mask = {
> .ssc_disable = SSC_BIT(CR_TXDIS),
> .ssc_endx = SSC_BIT(SR_ENDTX),
> .ssc_endbuf = SSC_BIT(SR_TXBUFE),
> - .ssc_error = SSC_BIT(SR_OVRUN),
> .pdc_enable = ATMEL_PDC_TXTEN,
> .pdc_disable = ATMEL_PDC_TXTDIS,
> };
>
> i.e. when I revert "ASoC: atmel_ssc_dai: add error mask define", I
> don't see any hang.
>
> Could you test and confirm that behaviour please ?
Yes, I aware this issue.
Actually the system not hang, the resource all are occupied by the
interrupt. This because, we enable the interrupt, when once interrupt
occur, I try many methods to clear it, however we can not clear it. So,
it generates the interrupt all the time. It seems the system hang.
Temp solution: not enable the interrupt. use the following patch to
disable the interrupt.
---8>---
diff --git a/sound/soc/atmel/atmel_ssc_dai.c
b/sound/soc/atmel/atmel_ssc_dai.c
index 0ecf356..bb53dea 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -649,7 +649,7 @@ static int atmel_ssc_prepare(struct
snd_pcm_substream *substream,
dma_params = ssc_p->dma_params[dir];
ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable);
- ssc_writel(ssc_p->ssc->regs, IER, dma_params->mask->ssc_error);
+ ssc_writel(ssc_p->ssc->regs, IDR, dma_params->mask->ssc_error);
pr_debug("%s enabled SSC_SR=0x%08x\n",
dir ? "receive" : "transmit",
---<8---
BTW, I am checking this with our IP team, if find the real solution, I
will fix it.
> I attached a the (simple) .config I used for the tests.
>
> PS: I hope the patches won't be mangled by gmail...
>
> Best regards,
> Richard.
Best Regards,
Bo Shen
2013/7/5 Bo Shen <[email protected]>:
> Yes, I aware this issue.
> Actually the system not hang, the resource all are occupied by the
> interrupt. This because, we enable the interrupt, when once interrupt occur,
> I try many methods to clear it, however we can not clear it. So, it
> generates the interrupt all the time. It seems the system hang.
>
> Temp solution: not enable the interrupt. use the following patch to disable
> the interrupt.
> ---8>---
> diff --git a/sound/soc/atmel/atmel_ssc_dai.c
> b/sound/soc/atmel/atmel_ssc_dai.c
> index 0ecf356..bb53dea 100644
> --- a/sound/soc/atmel/atmel_ssc_dai.c
> +++ b/sound/soc/atmel/atmel_ssc_dai.c
> @@ -649,7 +649,7 @@ static int atmel_ssc_prepare(struct snd_pcm_substream
> *substream,
> dma_params = ssc_p->dma_params[dir];
>
> ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable);
> - ssc_writel(ssc_p->ssc->regs, IER, dma_params->mask->ssc_error);
> + ssc_writel(ssc_p->ssc->regs, IDR, dma_params->mask->ssc_error);
>
> pr_debug("%s enabled SSC_SR=0x%08x\n",
> dir ? "receive" : "transmit",
> ---<8---
>
> BTW, I am checking this with our IP team, if find the real solution, I will
> fix it.
ok, I'll rebase my patches on that.
Thanks !
Best Regards,
Richard
2013/7/2 Bo Shen <[email protected]>:
> 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 <[email protected]>
>>
>> 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 <[email protected]>
>> Signed-off-by: Uwe Kleine-König <[email protected]>
>> Signed-off-by: Richard Genoud <[email protected]>
>> ---
>> 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 <[email protected]>
>> + *
>> + * Based on sam9g20_wm8731.c by:
>> + * Sedji Gaouaou <[email protected]>
>> + *
>> + * 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 <[email protected]>");
>> +MODULE_AUTHOR("Richard Genoud <[email protected]>");
>> +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.
--
for me, ck means con kolivas and not calvin klein... does it mean I'm a geek ?
2013/7/2 Bo Shen <[email protected]>:
> Hi Richard,
>
>
> On 7/1/2013 16:39, Richard Genoud wrote:
>>
>> With device tree, pdev->id is always -1, so we introduce a local
>> counter.
>>
>> Signed-off-by: Richard Genoud <[email protected]>
>> ---
>> drivers/misc/atmel-ssc.c | 7 +++++++
>> 1 file changed, 7 insertions(+)
>>
>> diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c
>> index 3afbd82..d1ec5ab 100644
>> --- a/drivers/misc/atmel-ssc.c
>> +++ b/drivers/misc/atmel-ssc.c
>> @@ -173,6 +173,12 @@ out:
>> return err;
>> }
>>
>> +/* counter of ssc devive instances.
>> + * With device tree pdev->id is always -1, so we have to keep the
>> + * count ourselves
>> + */
>> +static int ssc_device_id;
>
>
> Do we really need this? If Yes, would it better to get from device through
> of_alias_get_id?
you're right, it seems that we don't need that anymore.
thanks !
Richard.
2013/7/2 Bo Shen <[email protected]>:
> Hi Richard,
>
>
> On 7/1/2013 16:39, Richard Genoud wrote:
>>
>> Signed-off-by: Richard Genoud <[email protected]>
>> ---
>> arch/arm/mach-at91/at91sam9x5.c | 1 +
>> 1 file changed, 1 insertion(+)
>>
>> diff --git a/arch/arm/mach-at91/at91sam9x5.c
>> b/arch/arm/mach-at91/at91sam9x5.c
>> index 2abee66..191eb4b 100644
>> --- a/arch/arm/mach-at91/at91sam9x5.c
>> +++ b/arch/arm/mach-at91/at91sam9x5.c
>> @@ -233,6 +233,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
>> CLKDEV_CON_DEV_ID("mci_clk", "f000c000.mmc", &mmc1_clk),
>> CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller",
>> &dma0_clk),
>> CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller",
>> &dma1_clk),
>> + CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.0", &ssc_clk),
>
>
> Actually, we don't use this anymore. Am I right?
correct !
I'll drop it.
Thanks,
Richard.
On Fri, Jul 05, 2013 at 05:15:05PM +0200, Richard Genoud wrote:
> 2013/7/2 Bo Shen <[email protected]>:
> >> From: Nicolas Ferre <[email protected]>
> >>
> >> 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...
2013/7/5 Mark Brown <[email protected]>:
> On Fri, Jul 05, 2013 at 05:15:05PM +0200, Richard Genoud wrote:
>> 2013/7/2 Bo Shen <[email protected]>:
>
>> >> From: Nicolas Ferre <[email protected]>
>> >>
>> >> 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.
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
2013/7/8 Bo Shen <[email protected]>:
> 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.