[alsa-devel] [PATCH V2 1/2] ALSA: Alchemy AC97C/I2SC audio support

Lars-Peter Clausen lars at metafoo.de
Fri Jul 22 01:54:07 CEST 2011


You should always Cc Mark and Liam on ASoC patch. Also the proper commit title
prefix is ASoC.

On 07/21/2011 06:34 PM, Manuel Lauss wrote:
> This patch adds ASoC support for the AC97 and I2S controllers
> on the old Au1000/Au1500/Au1100 chips and a universal machine
> driver for the Db1000/Db1500/Db1100 boards.
> 
> AC97 Tested on a Db1500.  I2S untested since none of the boards
> actually have and I2S codec wired up.
> 
> Signed-off-by: Manuel Lauss <manuel.lauss at googlemail.com>
> ---
> V2: added untested I2S controller driver for completeness, removed the audio
>     defines from the au1000 header as well.
> 
>  arch/mips/alchemy/devboards/db1x00/platform.c |   37 ++
>  arch/mips/include/asm/mach-au1x00/au1000.h    |   61 ----
>  sound/soc/au1x/Kconfig                        |   28 ++
>  sound/soc/au1x/Makefile                       |   10 +
>  sound/soc/au1x/ac97c.c                        |  398 +++++++++++++++++++++
>  sound/soc/au1x/db1000.c                       |   75 ++++
>  sound/soc/au1x/dma.c                          |  470 +++++++++++++++++++++++++
>  sound/soc/au1x/i2sc.c                         |  353 +++++++++++++++++++
>  sound/soc/au1x/psc.h                          |   31 ++-
>  9 files changed, 1393 insertions(+), 70 deletions(-)
>  create mode 100644 sound/soc/au1x/ac97c.c
>  create mode 100644 sound/soc/au1x/db1000.c
>  create mode 100644 sound/soc/au1x/dma.c
>  create mode 100644 sound/soc/au1x/i2sc.c

It might make sense to split this into multiple patches. Especially the
platform part should be put in a seperate patch, since there isn't really any
compile time dependency to the other parts it could go via the MIPS tree.

> diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c
> new file mode 100644
> index 0000000..8fc25d0
> --- /dev/null
> +++ b/sound/soc/au1x/ac97c.c
> @@ -0,0 +1,398 @@
> +/*
> + * Au1000/Au1500/Au1100 AC97C controller driver for ASoC
> + *
> + * (c) 2011 Manuel Lauss <manuel.lauss at googlemail.com>
> + *
> + * based on the old ALSA driver by Charles Eidsness.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/suspend.h>
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/initval.h>
> +#include <sound/soc.h>
> +#include <asm/mach-au1x00/au1000.h>
> +#include <asm/mach-au1x00/au1xxx_psc.h>
> +
> +#include "psc.h"
> +
> +/*#define AC_DEBUG*/
> +
> +#define MSG(x...)	printk(KERN_ERR "ac97c: " x)

dev_err or pr_err

> +#ifdef AC_DEBUG
> +#define DBG(x...)	MSG(x)
> +#else
> +#define DBG(x...)	do {} while (0)
> +#endif

dev_dbg or pr_dbg

> [...]
> +
> +/* how often to retry failed codec register reads/writes */
> +#define AC97_RW_RETRIES	5
> +
> +#define AC97_DIR	\
> +	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
Unused

>[...]
> +
> +static int au1xac97c_hw_params(struct snd_pcm_substream *substream,
> +			       struct snd_pcm_hw_params *params,
> +			       struct snd_soc_dai *dai)
> +{
> +	return 0;
> +}
> +
> +static int au1xac97c_trigger(struct snd_pcm_substream *substream,
> +			     int cmd, struct snd_soc_dai *dai)
> +{
> +	return 0;
> +}
> +

If you don't want to do anything in the callbacks just leave them out.

> +static int au1xac97c_dai_probe(struct snd_soc_dai *dai)
> +{
> +	return ac97c_workdata ? 0 : -ENODEV;
> +}
> +
> +static struct snd_soc_dai_ops au1xac97c_dai_ops = {
const

> +	.trigger	= au1xac97c_trigger,
> +	.hw_params	= au1xac97c_hw_params,
> +};
> +
> [...]
> diff --git a/sound/soc/au1x/db1000.c b/sound/soc/au1x/db1000.c
> new file mode 100644
> index 0000000..9368b5d
> [...]
> +static int __init db1000_audio_load(void)
> +{
> +	int ret, id;
> +
> +	/* impostor check */
> +	id = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI));
> +
> +	ret = -ENOMEM;
> +	db1000_asoc97_dev = platform_device_alloc("soc-audio", 0);

New drivers shouldn't user soc-audio anymore, just register a normal platform
device driver.

> +	if (!db1000_asoc97_dev)
> +		goto out;
> +
> +	platform_set_drvdata(db1000_asoc97_dev, &db1000_ac97_machine);
> +	ret = platform_device_add(db1000_asoc97_dev);
> +
> +	if (ret) {
> +		platform_device_put(db1000_asoc97_dev);
> +		db1000_asoc97_dev = NULL;
> +	}
> +out:
> +	return ret;
> +}
> +
> +static void __exit db1000_audio_unload(void)
> +{
> +	platform_device_unregister(db1000_asoc97_dev);
> +}
> +
> +module_init(db1000_audio_load);
> +module_exit(db1000_audio_unload);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("DB1000/DB1500/DB1100 ASoC audio support");
> +MODULE_AUTHOR("Manuel Lauss");
> diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c
> new file mode 100644
> index 0000000..0f7d90a
> --- /dev/null
> +++ b/sound/soc/au1x/dma.c
> @@ -0,0 +1,470 @@
> [...]
> +
> +static struct platform_driver alchemy_ac97pcm_driver = {
> +	.driver	= {
> +		.name	= AC97C_DMANAME,
> +		.owner	= THIS_MODULE,
> +	},
> +	.probe		= alchemy_pcm_drvprobe,
> +	.remove		= __devexit_p(alchemy_pcm_drvremove),
> +};
> +
> +static struct platform_driver alchemy_i2spcm_driver = {
> +	.driver	= {
> +		.name	= I2SC_DMANAME,
> +		.owner	= THIS_MODULE,
> +	},
> +	.probe		= alchemy_pcm_drvprobe,
> +	.remove		= __devexit_p(alchemy_pcm_drvremove),
> +};

You shouldn't really have to register two identical drivers for this. If you
really want to be able to instantiate the driver with two different names use
platform_device_id. But in my opinion it should be enough to just have one
generic name, since there is nothing AC97 or I2S specific in this driver.

> [...]
> +
> +struct platform_device *alchemy_pcm_add(struct platform_device *pdev, int type)
> +{
+	struct resource *res, *r;
+	struct platform_device *pd;
+	char *pdevname;
+	int id[2];
+	int ret;
+
+	r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!r)
+		return NULL;
+	id[0] = r->start;
+
+	r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (!r)
+		return NULL;
+	id[1] = r->start;
+
+	res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL);
+	if (!res)
+		return NULL;
+
+	res[0].start = res[0].end = id[0];
+	res[1].start = res[1].end = id[1];
+	res[0].flags = res[1].flags = IORESOURCE_DMA;
+
+	/* "alchemy-pcm-ac97" or "alchemy-pcm-i2s" */
+	pdevname = (type == 0) ? AC97C_DMANAME : I2SC_DMANAME;
+	pd = platform_device_alloc(pdevname, -1);
+	if (!pd)
+		goto out;
+
+	pd->resource = res;
+	pd->num_resources = 2;
+
+	ret = platform_device_add(pd);
+	if (!ret)
+		return pd;
+
+	platform_device_put(pd);
+out:
+	kfree(res);
+	return NULL;
> +}

This function looks a bit fishy. The pcm driver should be registered by the
platform code file as well. If you need different DMA regions for I2C and AC97
use snd_soc_dai_set_dma_data and snd_soc_dai_get_dma_data to pass them to the
PCM driver from the I2S or AC97 driver.

> [...]
> diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c
> new file mode 100644
> index 0000000..1a31d51
> --- /dev/null
> +++ b/sound/soc/au1x/i2sc.c
> @@ -0,0 +1,353 @@
> [...]
> +
> +/* supported I2S DAI hardware formats */
> +#define AU1XI2SC_DAIFMT \
> +	(SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J |	\
> +	 SND_SOC_DAIFMT_NB_NF)
Unused


> +
> +/* supported I2S direction */
> +#define AU1XI2SC_DIR \
> +	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
Unused

> +
> +#define AU1XI2SC_RATES \
> +	SNDRV_PCM_RATE_8000_192000
> +
> +#define AU1XI2SC_FMTS \
> +	SNDRV_PCM_FMTBIT_S16_LE
> +
> +struct i2sc_ctx {
> +	void __iomem *mmio;
> +	unsigned long cfg, rate;
> +	struct platform_device *dmapd;
> +};
> +
> [...]
> +
> +static int au1xi2s_hw_params(struct snd_pcm_substream *substream,
> +			     struct snd_pcm_hw_params *params,
> +			     struct snd_soc_dai *dai)
> +{
> +	struct i2sc_ctx *ctx = snd_soc_dai_get_drvdata(dai);
> +	unsigned long stat, v;
> +
> +	v = msbits_to_reg(params->msbits);
> +	/* check if the PSC is already streaming data */

Use .symmetric_rates = 1 in your dai_driver struct for this.

> +	stat = RD(ctx, I2S_CONFIG);
> +	if (stat & (CFG_TN | CFG_RN)) {
> +		/* reject parameters not currently set up in hardware */
> +		if ((ctx->rate != params_rate(params)) ||
> +		    ((stat & CFG_SZ_MASK) != v))
> +			return -EINVAL;
> +	} else {
> +		/* set sample bitdepth */
> +		ctx->cfg &= ~CFG_SZ_MASK;
> +		if (v)
> +			ctx->cfg |= v;
> +		else
> +			return -EINVAL;
> +		/* remember current rate for other stream */
> +		ctx->rate = params_rate(params);
> +	}
> +	return 0;
> +}
> +
> +static struct snd_soc_dai_ops au1xi2s_dai_ops = {
const

> +	.trigger	= au1xi2s_trigger,
> +	.hw_params	= au1xi2s_hw_params,
> +	.set_fmt	= au1xi2s_set_fmt,
> +};
> +
> [...]
--
To unsubscribe from this list: send the line "unsubscribe alsa-devel" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the Alsa-devel mailing list