lkml.org 
[lkml]   [2011]   [Apr]   [4]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    SubjectRe: [PATCH 2/2] ALSA: ASoc: putting together AT91SAM9260 and TLV320AIC3X
    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@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@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@atmel.com>
    >> + * Jiri Prchal <jiri.prchal@aksignal.cz>
    >> + *
    >> + * Based on ati_b1_wm8731.c by:
    >> + * Frank Mandarino <fmandarino@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@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@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@atmel.com>
    + * Jiri Prchal <jiri.prchal@aksignal.cz>
    + *
    + * Based on ati_b1_wm8731.c by:
    + * Frank Mandarino <fmandarino@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@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@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/
    \
     
     \ /
      Last update: 2011-04-04 10:59    [W:0.070 / U:30.528 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site