[alsa-devel] [PATCH 2/2] ALSA: ASoc: putting together AT91SAM9260 and TLV320AIC3X

Prchal Jiří jiri.prchal at aksignal.cz
Mon Apr 4 10:57:01 CEST 2011


Hi,
this is separeted PATCH for glue AT91SAM9260 and TLV320AIC3X on CDU board.

Depend on: [PATCH 1/2] ALSA: ASoc: TLV320AIC3X: ad SPI and clock on GPIO2 or BCLK
	[PATCH] ALSA: ASoc: new functions snd_soc_7_8_*
	[PATCH] ARCH arm: adding new board: CDU
Kernel version: 2.6.38
Signed-off-by: Jiri Prchal <jiri.prchal at aksignal.cz>

Dne 24.3.2011 21:07, Ryan Mallon napsal(a):
> On 03/24/2011 11:43 PM, Prchal Jiří wrote:
>> Hi,
>> this patch is for example how to put together AT91SAM9260 and TLV320AIC3106 controlled via SPI.
>> It tooks me a lot of time to make it working, so I think it could be helpfull for other people.
>>
>> In original source "snd-soc-afeb9260.c" which I use as example is BIG ERROR:
>> In function "afeb9260_soc_init" is missing call of "atmel_ssc_set_audio(0);". It cause bug "PROBLEM: Asoc driver in
>> 2.6.37.3 for AT91SAM9260 / TLV320AIC3X is broken" which I post some time ago.
> 
> Hi Jiri,
> 
> This patch is actually doing two things: adding board support for a new
> at91sam9260 device and adding the audio glue for the new board. It
> should be split into two patches. Quick review below.
> 
> ~Ryan
> 
I separate it.

>>
>> Depend on: [PATCH 1/2] ALSA: ASoc: TLV320AIC3X: ad SPI and clock on GPIO2 or BCLK
>> Kernel version: 2.6.38
>> Signed-off-by: Jiri Prchal <jiri.prchal at aksignal.cz>
>> ---
>>
>> diff -uprN -X linux-2.6.38-vanilla/Documentation/dontdiff linux-2.6.38-vanilla/sound/soc/atmel/Kconfig
>> /home/prchal/arm/fw-cdu/linux/linux-2.6.38/sound/soc/atmel/Kconfig
>> --- linux-2.6.38-vanilla/sound/soc/atmel/Kconfig	2011-03-15 02:20:32.000000000 +0100
>> +++ /home/prchal/arm/fw-cdu/linux/linux-2.6.38/sound/soc/atmel/Kconfig	2011-03-22 09:46:46.751566158 +0100
>> @@ -50,3 +50,11 @@ config SND_AT91_SOC_AFEB9260
>>  	select SND_SOC_TLV320AIC23
>>  	help
>>  	  Say Y here to support sound on AFEB9260 board.
>> +
>> +config SND_AT91_SOC_CDU
>> +	tristate "SoC Audio support for CDU board"
>> +	depends on ARCH_AT91 && MACH_CDU && SND_ATMEL_SOC
>> +	select SND_ATMEL_SOC_SSC
>> +	select SND_SOC_TLV320AIC3X
>> +	help
>> +	  Say Y here to support sound on CDU board.
>> diff -uprN -X linux-2.6.38-vanilla/Documentation/dontdiff linux-2.6.38-vanilla/sound/soc/atmel/Makefile
>> /home/prchal/arm/fw-cdu/linux/linux-2.6.38/sound/soc/atmel/Makefile
>> --- linux-2.6.38-vanilla/sound/soc/atmel/Makefile	2011-03-15 02:20:32.000000000 +0100
>> +++ /home/prchal/arm/fw-cdu/linux/linux-2.6.38/sound/soc/atmel/Makefile	2011-03-16 09:26:17.000000000 +0100
>> @@ -14,3 +14,4 @@ snd-soc-playpaq-objs := playpaq_wm8510.o
>>  obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
>>  obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
>>  obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
>> +obj-$(CONFIG_SND_AT91_SOC_CDU) += snd-soc-cdu.o
>> diff -uprN -X linux-2.6.38-vanilla/Documentation/dontdiff linux-2.6.38-vanilla/sound/soc/atmel/snd-soc-cdu.c
>> /home/prchal/arm/fw-cdu/linux/linux-2.6.38/sound/soc/atmel/snd-soc-cdu.c
>> --- linux-2.6.38-vanilla/sound/soc/atmel/snd-soc-cdu.c	1970-01-01 01:00:00.000000000 +0100
>> +++ /home/prchal/arm/fw-cdu/linux/linux-2.6.38/sound/soc/atmel/snd-soc-cdu.c	2011-03-24 09:27:55.404367652 +0100
>> @@ -0,0 +1,264 @@
>> +/*
>> + * snd-soc-cdu  --  SoC audio for AT91SAM9260-based
>> + * 			AKsignal CDU board.
>> + *
>> + *  Copyright (C) 2005 SAN People
>> + *  Copyright (C) 2008 Atmel
>> + *  Copyright (C) 2011 AK signal Brno
>> + *
>> + * Authors: Sedji Gaouaou <sedji.gaouaou at atmel.com>
>> + *          Jiri Prchal <jiri.prchal at aksignal.cz>
>> + *
>> + * Based on ati_b1_wm8731.c by:
>> + * Frank Mandarino <fmandarino at endrelia.com>
>> + * Copyright 2006 Endrelia Technologies Inc.
>> + * Based on corgi.c by:
>> + * Copyright 2005 Wolfson Microelectronics PLC.
>> + * Copyright 2005 Openedhand Ltd.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
>> + */
>> +
>> +#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>
> 
> Note sure you need all of these includes. linux/interrupt.h,
> linux/moduleparam.h, linux/timer.h and mach/gpio.h at least appear to be
> uneccessary.
Cleaned up.

> 
>> +
>> +#include "../codecs/tlv320aic3x.h"
>> +#include "atmel-pcm.h"
>> +#include "atmel_ssc_dai.h"
>> +
>> +struct {
>> +	unsigned int channels;
>> +	snd_pcm_format_t format;
>> +	unsigned int rate;
>> +	unsigned int codecclk;
>> +	unsigned int cmrdiv;
>> +	unsigned int period;
>> +} cdu_audio[] = {
>> +	/* 16 bit stereo modes */
>> +	{2, SNDRV_PCM_FORMAT_S16_LE, 8000, 2096000, 25, 130,},
>> +	{2, SNDRV_PCM_FORMAT_S16_LE, 16000, 2496000, 21, 77,},
>> +	{2, SNDRV_PCM_FORMAT_S16_LE, 32000, 2496000, 21, 38,},
>> +	{2, SNDRV_PCM_FORMAT_S16_LE, 48000, 2016000, 26, 20,},
>> +	{2, SNDRV_PCM_FORMAT_S16_LE, 96000, 4032000, 13, 20,},
>> +
>> +	{2, SNDRV_PCM_FORMAT_S16_LE, 11025, 2381400, 22, 107,},
>> +	{2, SNDRV_PCM_FORMAT_S16_LE, 22050, 2381400, 22, 53,},
>> +	{2, SNDRV_PCM_FORMAT_S16_LE, 44100, 2381400, 22, 26,},
>> +	{2, SNDRV_PCM_FORMAT_S16_LE, 88200, 4762800, 11, 26,},
>> +
>> +	{2, SNDRV_PCM_FORMAT_S16_LE, 11520, 2626560, 20, 113,},
>> +	{2, SNDRV_PCM_FORMAT_S16_LE, 23040, 2626560, 20, 56,},
> 
> Can these be calculated rather than using a table? The first two values
> probably don't need to be in the table since they are always the same.
Explained in new comment why it is table. I made the table smaller.

> 
>> +
>> +};
>> +
>> +static int cdu_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
>> +{
>> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> +	struct snd_soc_dai *codec_dai = rtd->codec_dai;
>> +	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
>> +	int ret;
>> +	int i, found = 0;
>> +	snd_pcm_format_t format = params_format(params);
>> +	unsigned int rate = params_rate(params);
>> +	unsigned int channels = params_channels(params);
>> +
>> +	/* set codec DAI configuration */
>> +	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
> 
> Long lines should be broken to fit inside 80 characters.
OK.

> 
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* set cpu DAI configuration */
>> +	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* find the correct audio parameters */
>> +	for (i = 0; i < ARRAY_SIZE(cdu_audio); i++) {
>> +		if (rate == cdu_audio[i].rate &&
>> +		    format == cdu_audio[i].format &&
>> +		    channels == cdu_audio[i].channels) {
>> +			found = 1;
>> +			break;
>> +		}
>> +	}
>> +	if (!found)
>> +		return -EINVAL;
> 
> Should maybe do this before doing the dai_set_fmt's since we only
> support some modes. Also:
> 
> 	/* Only support 2 channel S16_LE */
> 	if (channels != 2 || format != SNDRV_PCM_FORMAT_S16_LE)
> 		return -EINVAL;
> 
> 	/* Check rate support */
> 	for (i = 0; i < ARRAY_SIZE(cdc_audio); i++)
> 		if (rate == cdc_audio[i].rate) {
> 			found = 1;
> 			break;
> 		}
> 	if (!found)
> 		return -EINVAL;
> 
OK.

>> +
>> +	/* Set the codec system clock for DAC and ADC */
>> +	ret = snd_soc_dai_set_sysclk(codec_dai, CLKIN_BCLK, cdu_audio[i].codecclk, SND_SOC_CLOCK_IN);
>> +	if (ret < 0) {
>> +		printk(KERN_ERR "can't set codec system clock\n");
> 
> You should, I think, be able to do:
> 
> 	struct device *dev = rtd->card->dev;
> 
> 	dev_err(dev, "can't set codec system clock\n");
> 
> Same goes for other printks.
> 
OK.

>> +		return ret;
>> +	}
>> +
>> +	/* Set the cpu clock dividers to BCLK */
>> +	ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cdu_audio[i].cmrdiv);
>> +	ret |= snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_TCMR_PERIOD, cdu_audio[i].period);
>> +	ret |= snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_RCMR_PERIOD, cdu_audio[i].period);
>> +	if (ret < 0) {
>> +		printk(KERN_ERR "can't set cpu system clock\n");
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static struct snd_soc_ops cdu_ops = {
>> +	.hw_params = cdu_hw_params,
>> +};
>> +
>> +static const struct snd_soc_dapm_widget cdu_dapm_widgets[] = {
>> +	SND_SOC_DAPM_HP("Headphone Jack", NULL),
>> +	SND_SOC_DAPM_LINE("Line Out", NULL),
>> +	SND_SOC_DAPM_MIC("Mic Jack", NULL),
>> +	SND_SOC_DAPM_LINE("Line In", NULL),
>> +};
>> +
>> +static const struct snd_soc_dapm_route intercon[] = {
>> +	/* Headphone connected to HPLOUT, HPROUT */
>> +	{"Headphone Jack", NULL, "HPLOUT"},
>> +	{"Headphone Jack", NULL, "HPROUT"},
>> +
>> +	/* Line Out connected to LLOUT, RLOUT */
>> +	{"Line Out", NULL, "LLOUT"},
>> +	{"Line Out", NULL, "RLOUT"},
>> +
>> +	/* Mic connected to (MIC3L | MIC3R) */
>> +	{"MIC3L", NULL, "Mic Bias 2V"},
>> +	{"MIC3R", NULL, "Mic Bias 2V"},
>> +	{"Mic Bias 2V", NULL, "Mic Jack"},
>> +
>> +	/* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */
>> +	{"LINE1L", NULL, "Line In"},
>> +	{"LINE2L", NULL, "Line In"},
>> +	{"LINE1R", NULL, "Line In"},
>> +	{"LINE2R", NULL, "Line In"},
>> +};
>> +
>> +/*
>> + * Logic for a aic3x as connected on a cdu board.
>> + */
>> +static int cdu_aic3x_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;
>> +	int ret;
>> +
>> +	printk(KERN_DEBUG "cdu_aic3x: cdu_aic3x_init called\n");
> 
> Is this line really needed?
> 
NO, removed.

>> +
>> +	/* Add specific widgets */
>> +	snd_soc_dapm_new_controls(dapm, cdu_dapm_widgets,
>> +				  ARRAY_SIZE(cdu_dapm_widgets));
>> +	/* Set up specific audio path interconnects */
>> +	snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
>> +
>> +	/* always connected */
>> +	snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
>> +	snd_soc_dapm_enable_pin(dapm, "Line Out");
>> +	snd_soc_dapm_enable_pin(dapm, "Mic Jack");
>> +	snd_soc_dapm_enable_pin(dapm, "Line In");
>> +
>> +	snd_soc_dapm_sync(dapm);
> 
> IIRC, you no longer need to explicitly call snd_soc_dapm_enable_pin and
> snd_soc_dapm_sync. Somebody else can probably shed more light on this.
> 
Removed.

>> +
>> +	return 0;
>> +}
>> +
>> +static struct snd_soc_dai_link cdu_dai = {
>> +	.name		= "TLV320AIC3106",
>> +	.stream_name	= "PCM",
>> +	.cpu_dai_name	= "atmel-ssc-dai.0",
>> +	.codec_dai_name	= "tlv320aic3x-hifi",
>> +	.init = cdu_aic3x_init,
> 
> Tab-delimiting got messed here.
> 
OK.

>> +	.platform_name	= "atmel-pcm-audio",
>> +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
>> +	.codec_name	= "tlv320aic3x-codec.0-001b",
>> +#endif
>> +#if defined(CONFIG_SPI_MASTER)
>> +	.codec_name	= "spi1.3",
>> +#endif
> 
> If both CONFIG_I2C and CONFIG_SPI_MASTER are set then you will have a
> broken build. Which one does the CDU board use?
> 
Since CDU use only SPI the I2C option removed.

>> +	.ops		= &cdu_ops,
>> +};
>> +
>> +static struct snd_soc_card snd_soc_cdu = {
>> +	.name		= "TLV320AIC3106",
>> +	.dai_link	= &cdu_dai,
>> +	.num_links	= 1,
>> +};
>> +
>> +static struct platform_device *cdu_snd_device;
>> +
>> +static int __init cdu_init(void)
>> +{
>> +	struct clk *pllb;
> 
> Remove this line, pllb is never used in this function.
> 
OK.

>> +	int ret;
>> +
>> +	ret = atmel_ssc_set_audio(0);
>> +	if (ret != 0) {
> 
> Nitpick. Just if (ret) is fine.
> 
OK.

>> +		pr_err("Failed to set SSC 0 for audio: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	cdu_snd_device = platform_device_alloc("soc-audio", -1);
>> +	if (!cdu_snd_device) {
>> +		printk(KERN_ERR "ASoC: Platform device allocation failed\n");
>> +		ret = -ENOMEM;
>> +	}
>> +
>> +	platform_set_drvdata(cdu_snd_device, &snd_soc_cdu);
>> +
>> +	ret = platform_device_add(cdu_snd_device);
>> +	if (ret) {
>> +		printk(KERN_ERR "ASoC: Platform device allocation failed\n");
> 
> Use pr_err to be consistent.
> 
OK.

>> +		goto err_device_add;
>> +	}
>> +
>> +	return ret;
>> +
>> +err_device_add:
>> +	platform_device_put(cdu_snd_device);
>> +err:
>> +	return ret;
>> +}
>> +
>> +static void __exit cdu_exit(void)
>> +{
>> +	platform_device_unregister(cdu_snd_device);
>> +	cdu_snd_device = NULL;
> 
> I don't think you need to set cdu_snd_device to NULL here.
> 
OK.

>> +}
>> +
>> +module_init(cdu_init);
>> +module_exit(cdu_exit);
>> +
>> +/* Module information */
>> +MODULE_AUTHOR("Jiri Prchal <jiri.prchal at aksignal.cz>");
>> +MODULE_DESCRIPTION("ALSA SoC CDU_AIC3X");
>> +MODULE_LICENSE("GPL");
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at  http://www.tux.org/lkml/
> 
> 

diff -uprN -X linux-2.6.38-vanilla/Documentation/dontdiff linux-2.6.38-vanilla/sound/soc/atmel/Kconfig
linux-2.6.38-patch/sound/soc/atmel/Kconfig
--- linux-2.6.38-vanilla/sound/soc/atmel/Kconfig	2011-03-15 02:20:32.000000000 +0100
+++ linux-2.6.38-patch/sound/soc/atmel/Kconfig	2011-03-22 09:46:46.751566158 +0100
@@ -50,3 +50,11 @@ config SND_AT91_SOC_AFEB9260
 	select SND_SOC_TLV320AIC23
 	help
 	  Say Y here to support sound on AFEB9260 board.
+
+config SND_AT91_SOC_CDU
+	tristate "SoC Audio support for CDU board"
+	depends on ARCH_AT91 && MACH_CDU && SND_ATMEL_SOC
+	select SND_ATMEL_SOC_SSC
+	select SND_SOC_TLV320AIC3X
+	help
+	  Say Y here to support sound on CDU board.
diff -uprN -X linux-2.6.38-vanilla/Documentation/dontdiff linux-2.6.38-vanilla/sound/soc/atmel/Makefile
linux-2.6.38-patch/sound/soc/atmel/Makefile
--- linux-2.6.38-vanilla/sound/soc/atmel/Makefile	2011-03-15 02:20:32.000000000 +0100
+++ linux-2.6.38-patch/sound/soc/atmel/Makefile	2011-03-16 09:26:17.000000000 +0100
@@ -14,3 +14,4 @@ snd-soc-playpaq-objs := playpaq_wm8510.o
 obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
 obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
 obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
+obj-$(CONFIG_SND_AT91_SOC_CDU) += snd-soc-cdu.o
diff -uprN -X linux-2.6.38-vanilla/Documentation/dontdiff linux-2.6.38-vanilla/sound/soc/atmel/snd-soc-cdu.c
linux-2.6.38-patch/sound/soc/atmel/snd-soc-cdu.c
--- linux-2.6.38-vanilla/sound/soc/atmel/snd-soc-cdu.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.38-patch/sound/soc/atmel/snd-soc-cdu.c	2011-03-25 13:42:07.545051043 +0100
@@ -0,0 +1,259 @@
+/*
+ * snd-soc-cdu  --  SoC audio for AT91SAM9260-based
+ * 			AKsignal CDU board.
+ *
+ *  Copyright (C) 2005 SAN People
+ *  Copyright (C) 2008 Atmel
+ *  Copyright (C) 2011 AK signal Brno
+ *
+ * Authors: Sedji Gaouaou <sedji.gaouaou at atmel.com>
+ *          Jiri Prchal <jiri.prchal at aksignal.cz>
+ *
+ * Based on ati_b1_wm8731.c by:
+ * Frank Mandarino <fmandarino at endrelia.com>
+ * Copyright 2006 Endrelia Technologies Inc.
+ * Based on corgi.c by:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/platform_device.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 "../codecs/tlv320aic3x.h"
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
+
+/*
+ * Table of supported rates
+ * with their clock divider (cmrdiv) and number clock in frame (period + 1 * 2),
+ * codecclk sould be integer multiple of sample rate even if it is not exact true
+ * to avoid data on SSC underrun / overrun -
+ * CPU can not generate clock as fine as CODEC.
+ */
+struct {
+	unsigned int rate;
+	unsigned int codecclk;
+	unsigned int cmrdiv;
+	unsigned int period;
+} cdu_audio[] = {
+	{8000, 2096000, 25, 130,},
+	{16000, 2496000, 21, 77,},
+	{32000, 2496000, 21, 38,},
+	{48000, 2016000, 26, 20,},
+	{96000, 4032000, 13, 20,},
+
+	{11025, 2381400, 22, 107,},
+	{22050, 2381400, 22, 53,},
+	{44100, 2381400, 22, 26,},
+	{88200, 4762800, 11, 26,},
+
+	/* special rates for serial line transfer */
+	{11520, 2626560, 20, 113,},
+	{23040, 2626560, 20, 56,},
+	{46080, 2764800, 19, 29,},
+};
+
+static int cdu_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct device *dev = rtd->card->dev;
+	int ret;
+	int i, found = 0;
+
+	/* set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai,
+				  SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai,
+				  SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	/* Only support 1 or 2 channel S16_LE */
+	if ((params_channels(params) != 1 && params_channels(params) != 2) ||
+	   params_format(params) != SNDRV_PCM_FORMAT_S16_LE)
+		return -EINVAL;
+
+	/* Check rate support */
+	for (i = 0; i < ARRAY_SIZE(cdu_audio); i++)
+		if (params_rate(params) == cdu_audio[i].rate) {
+			found = 1;
+			break;
+		}
+	if (!found)
+		return -EINVAL;
+
+	/* Set the codec system clock for DAC and ADC */
+	ret = snd_soc_dai_set_sysclk(codec_dai,
+				     CLKIN_BCLK, cdu_audio[i].codecclk,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		dev_err(dev, "can't set codec system clock\n");
+		return ret;
+	}
+
+	/* Set the cpu clock dividers to BCLK */
+	ret = snd_soc_dai_set_clkdiv(cpu_dai,
+				     ATMEL_SSC_CMR_DIV, cdu_audio[i].cmrdiv);
+	ret |= snd_soc_dai_set_clkdiv(cpu_dai,
+				      ATMEL_SSC_TCMR_PERIOD,
+				      cdu_audio[i].period);
+	ret |= snd_soc_dai_set_clkdiv(cpu_dai,
+				      ATMEL_SSC_RCMR_PERIOD,
+				      cdu_audio[i].period);
+	if (ret < 0) {
+		dev_err(dev, "can't set cpu system clock\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops cdu_ops = {
+	.hw_params = cdu_hw_params,
+};
+
+static const struct snd_soc_dapm_widget cdu_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_LINE("Line Out", NULL),
+	SND_SOC_DAPM_MIC("Mic Jack", NULL),
+	SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+	/* Headphone connected to HPLOUT, HPROUT */
+	{"Headphone Jack", NULL, "HPLOUT"},
+	{"Headphone Jack", NULL, "HPROUT"},
+
+	/* Line Out connected to LLOUT, RLOUT */
+	{"Line Out", NULL, "LLOUT"},
+	{"Line Out", NULL, "RLOUT"},
+
+	/* Mic connected to (MIC3L | MIC3R) */
+	{"MIC3L", NULL, "Mic Bias 2V"},
+	{"MIC3R", NULL, "Mic Bias 2V"},
+	{"Mic Bias 2V", NULL, "Mic Jack"},
+
+	/* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */
+	{"LINE1L", NULL, "Line In"},
+	{"LINE2L", NULL, "Line In"},
+	{"LINE1R", NULL, "Line In"},
+	{"LINE2R", NULL, "Line In"},
+};
+
+/*
+ * Logic for a aic3x as connected on a cdu board.
+ */
+static int cdu_aic3x_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;
+	int ret;
+
+	/* Add specific widgets */
+	snd_soc_dapm_new_controls(dapm, cdu_dapm_widgets,
+				  ARRAY_SIZE(cdu_dapm_widgets));
+	/* Set up specific audio path interconnects */
+	snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
+
+	return 0;
+}
+
+static struct snd_soc_dai_link cdu_dai = {
+	.name		= "TLV320AIC3106",
+	.stream_name	= "PCM",
+	.cpu_dai_name	= "atmel-ssc-dai.0",
+	.codec_dai_name	= "tlv320aic3x-hifi",
+	.init		= cdu_aic3x_init,
+	.platform_name	= "atmel-pcm-audio",
+	.codec_name	= "spi1.3",
+	.ops		= &cdu_ops,
+};
+
+static struct snd_soc_card snd_soc_cdu = {
+	.name		= "TLV320AIC3106",
+	.dai_link	= &cdu_dai,
+	.num_links	= 1,
+};
+
+static struct platform_device *cdu_snd_device;
+
+static int __init cdu_init(void)
+{
+	int ret;
+
+	ret = atmel_ssc_set_audio(0);
+	if (ret) {
+		pr_err("Failed to set SSC 0 for audio: %d\n", ret);
+		return ret;
+	}
+
+	cdu_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!cdu_snd_device) {
+		pr_err("ASoC: Platform device allocation failed: %d\n", ret);
+		ret = -ENOMEM;
+	}
+
+	platform_set_drvdata(cdu_snd_device, &snd_soc_cdu);
+
+	ret = platform_device_add(cdu_snd_device);
+	if (ret) {
+		pr_err("ASoC: Platform device adding failed: %d\n", ret);
+		goto err_device_add;
+	}
+
+	return ret;
+
+err_device_add:
+	platform_device_put(cdu_snd_device);
+err:
+	return ret;
+}
+
+static void __exit cdu_exit(void)
+{
+	platform_device_unregister(cdu_snd_device);
+}
+
+module_init(cdu_init);
+module_exit(cdu_exit);
+
+/* Module information */
+MODULE_AUTHOR("Jiri Prchal <jiri.prchal at aksignal.cz>");
+MODULE_DESCRIPTION("ALSA SoC CDU_AIC3X");
+MODULE_LICENSE("GPL");
--
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