[alsa-devel] [PATCH 0/2]blackfin TDM dai and blackfin-ad1938 machine driver
Hi Mark, The following two patches are the 2nd version for the "new support for ad1938 codec" patch sequence. Since you have applied the codec driver patch([PATCH 1/3] new ad1938 codec driver based on asoc), here I only send the DAI and Board driver patches again according to your feedback. Patch 1: blackfin I2S(TDM mode) CPU DAI driver Patch 2: board driver to connect bf5xx with ad1938
Detailed description about every patch and changes is included in the corresponding patch. Thanks Barry
Signed-off-by: Barry Song 21cnbao@gmail.com
Even though TDM mode can be a part of I2S mode, we still split bf5xx i2s and bf5xx tdm into individual files sharing same I2S DAI type in SOC core. We don't define a new DAI type, but give two instances for standard I2S and TDM, both in I2S-family DAI type. On the one hand, I2S and TDM means two completely different interface configuration to blackfin, results in completely different hardware configuration, DMA description and data transfer flow. Merging them as a whole will be very ugly and difficult to maintain. On the other hand, hardware board connection decides the I2S/TDM mode directly. It's needless to switch between I2S and TDM dynamically. For TDM mode, board driver can refer to the TDM instance, for I2S mode, board driver can refer to the I2S instance. It will not cause trouble to descript the hardware interface with two instances and the board driver is simple and easy.
Changes from the last patch version include: 1. Fix checkpatch issues and code-style issues 2. Add codes for restoring the bf5xx_tdm.configured while all streams are closed 3. Change snd_soc_register_dai in bfin_tdm_init to platform driver and move resource related codes to platform probe --- sound/soc/blackfin/Kconfig | 15 ++- sound/soc/blackfin/Makefile | 4 + sound/soc/blackfin/bf5xx-tdm-pcm.c | 332 ++++++++++++++++++++++++++++++++++ sound/soc/blackfin/bf5xx-tdm-pcm.h | 21 +++ sound/soc/blackfin/bf5xx-tdm.c | 342 ++++++++++++++++++++++++++++++++++++ sound/soc/blackfin/bf5xx-tdm.h | 14 ++ 6 files changed, 727 insertions(+), 1 deletions(-) create mode 100644 sound/soc/blackfin/bf5xx-tdm-pcm.c create mode 100644 sound/soc/blackfin/bf5xx-tdm-pcm.h create mode 100644 sound/soc/blackfin/bf5xx-tdm.c create mode 100644 sound/soc/blackfin/bf5xx-tdm.h
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index 811596f..2ab6d2f 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -7,6 +7,15 @@ config SND_BF5XX_I2S mode (supports single stereo In/Out). You will also need to select the audio interfaces to support below.
+config SND_BF5XX_TDM + tristate "SoC I2S(TDM mode) Audio for the ADI BF5xx chip" + depends on (BLACKFIN && SND_SOC) + help + Say Y or M if you want to add support for codecs attached to + the Blackfin SPORT (synchronous serial ports) interface in TDM + mode. + You will also need to select the audio interfaces to support below. + config SND_BF5XX_SOC_SSM2602 tristate "SoC SSM2602 Audio support for BF52x ezkit" depends on SND_BF5XX_I2S @@ -69,6 +78,10 @@ config SND_BF5XX_SOC_I2S tristate select SND_BF5XX_SOC_SPORT
+config SND_BF5XX_SOC_TDM + tristate + select SND_BF5XX_SOC_SPORT + config SND_BF5XX_SOC_AC97 tristate select AC97_BUS @@ -85,7 +98,7 @@ config SND_BF5XX_SOC_AD1980
config SND_BF5XX_SPORT_NUM int "Set a SPORT for Sound chip" - depends on (SND_BF5XX_I2S || SND_BF5XX_AC97) + depends on (SND_BF5XX_I2S || SND_BF5XX_AC97 || SND_BF5XX_TDM) range 0 3 if BF54x range 0 1 if !BF54x default 0 diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile index 97bb37a..f861926 100644 --- a/sound/soc/blackfin/Makefile +++ b/sound/soc/blackfin/Makefile @@ -1,15 +1,19 @@ # Blackfin Platform Support snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o +snd-bf5xx-tdm-objs := bf5xx-tdm-pcm.o snd-soc-bf5xx-sport-objs := bf5xx-sport.o snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o +snd-soc-bf5xx-tdm-objs := bf5xx-tdm.o
obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o +obj-$(CONFIG_SND_BF5XX_TDM) += snd-bf5xx-tdm.o obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o +obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o
# Blackfin Machine Support snd-ad1980-objs := bf5xx-ad1980.o diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c new file mode 100644 index 0000000..0e791d3 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c @@ -0,0 +1,332 @@ +/* + * File: sound/soc/blackfin/bf5xx-tdm-pcm.c + * Author: Barry Song Barry.Song@analog.com + * + * Created: Tue June 06 2009 + * Description: DMA driver for tdm codec + * + * Modified: + * Copyright 2009 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include <asm/dma.h> + +#include "bf5xx-tdm-pcm.h" +#include "bf5xx-tdm.h" +#include "bf5xx-sport.h" + +#define PCM_BUFFER_MAX 0x10000 +#define FRAGMENT_SIZE_MIN (4*1024) +#define FRAGMENTS_MIN 2 +#define FRAGMENTS_MAX 32 + +static void bf5xx_dma_irq(void *data) +{ + struct snd_pcm_substream *pcm = data; + snd_pcm_period_elapsed(pcm); +} + +static const struct snd_pcm_hardware bf5xx_pcm_hardware = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_48000, + .channels_min = 2, + .channels_max = 8, + .buffer_bytes_max = PCM_BUFFER_MAX, + .period_bytes_min = FRAGMENT_SIZE_MIN, + .period_bytes_max = PCM_BUFFER_MAX/2, + .periods_min = FRAGMENTS_MIN, + .periods_max = FRAGMENTS_MAX, +}; + +static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + size_t size = bf5xx_pcm_hardware.buffer_bytes_max; + snd_pcm_lib_malloc_pages(substream, size * 4); + + return 0; +} + +static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_lib_free_pages(substream); + + return 0; +} + +static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sport_device *sport = runtime->private_data; + int fragsize_bytes = frames_to_bytes(runtime, runtime->period_size); + + fragsize_bytes /= runtime->channels; + /* inflate the fragsize to match the dma width of SPORT */ + fragsize_bytes *= 8; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + sport_set_tx_callback(sport, bf5xx_dma_irq, substream); + sport_config_tx_dma(sport, runtime->dma_area, + runtime->periods, fragsize_bytes); + } else { + sport_set_rx_callback(sport, bf5xx_dma_irq, substream); + sport_config_rx_dma(sport, runtime->dma_area, + runtime->periods, fragsize_bytes); + } + + return 0; +} + +static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sport_device *sport = runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + sport_tx_start(sport); + else + sport_rx_start(sport); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + sport_tx_stop(sport); + else + sport_rx_stop(sport); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sport_device *sport = runtime->private_data; + unsigned int diff; + snd_pcm_uframes_t frames; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + diff = sport_curr_offset_tx(sport); + frames = diff / (8*4); /* 32 bytes per frame */ + } else { + diff = sport_curr_offset_rx(sport); + frames = diff / (8*4); + } + return frames; +} + +static int bf5xx_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret; + + snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + goto out; + + if (sport_handle != NULL) + runtime->private_data = sport_handle; + else { + pr_err("sport_handle is NULL\n"); + return -1; + } + return 0; + +out: + return ret; +} + +static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count) +{ + unsigned int *src; + unsigned int *dst; + int i; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + src = buf; + dst = (unsigned int *)substream->runtime->dma_area; + + dst += pos * 8; + while (count--) { + for (i = 0; i < substream->runtime->channels; i++) + *(dst + i) = *src++; + dst += 8; + } + } else { + src = (unsigned int *)substream->runtime->dma_area; + dst = buf; + + src += pos * 8; + while (count--) { + for (i = 0; i < substream->runtime->channels; i++) + *dst++ = *(src+i); + src += 8; + } + } + + return 0; +} + +static int bf5xx_pcm_silence(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +{ + unsigned char *buf = substream->runtime->dma_area; + buf += pos * 8 * 4; + memset(buf, '\0', count * 8 * 4); + + return 0; +} + + +struct snd_pcm_ops bf5xx_pcm_tdm_ops = { + .open = bf5xx_pcm_open, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = bf5xx_pcm_hw_params, + .hw_free = bf5xx_pcm_hw_free, + .prepare = bf5xx_pcm_prepare, + .trigger = bf5xx_pcm_trigger, + .pointer = bf5xx_pcm_pointer, + .copy = bf5xx_pcm_copy, + .silence = bf5xx_pcm_silence, +}; + +static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = bf5xx_pcm_hardware.buffer_bytes_max; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_coherent(pcm->card->dev, size * 4, + &buf->addr, GFP_KERNEL); + if (!buf->area) { + pr_err("Failed to allocate dma memory \ + Please increase uncached DMA memory region\n"); + return -ENOMEM; + } + buf->bytes = size; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + sport_handle->tx_buf = buf->area; + else + sport_handle->rx_buf = buf->area; + + return 0; +} + +static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + dma_free_coherent(NULL, buf->bytes, buf->area, 0); + buf->area = NULL; + } + if (sport_handle) + sport_done(sport_handle); +} + +static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK; + +int bf5xx_pcm_tdm_new(struct snd_card *card, struct snd_soc_dai *dai, + struct snd_pcm *pcm) +{ + int ret = 0; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &bf5xx_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_32BIT_MASK; + + if (dai->playback.channels_min) { + ret = bf5xx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (dai->capture.channels_min) { + ret = bf5xx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } +out: + return ret; +} + +struct snd_soc_platform bf5xx_tdm_soc_platform = { + .name = "bf5xx-audio", + .pcm_ops = &bf5xx_pcm_tdm_ops, + .pcm_new = bf5xx_pcm_tdm_new, + .pcm_free = bf5xx_pcm_free_dma_buffers, +}; +EXPORT_SYMBOL_GPL(bf5xx_tdm_soc_platform); + +static int __init bfin_pcm_tdm_init(void) +{ + return snd_soc_register_platform(&bf5xx_tdm_soc_platform); +} +module_init(bfin_pcm_tdm_init); + +static void __exit bfin_pcm_tdm_exit(void) +{ + snd_soc_unregister_platform(&bf5xx_tdm_soc_platform); +} +module_exit(bfin_pcm_tdm_exit); + +MODULE_AUTHOR("Barry Song"); +MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.h b/sound/soc/blackfin/bf5xx-tdm-pcm.h new file mode 100644 index 0000000..9e8473d --- /dev/null +++ b/sound/soc/blackfin/bf5xx-tdm-pcm.h @@ -0,0 +1,21 @@ +/* + * linux/sound/arm/bf5xx-tdm-pcm.h -- ALSA PCM interface for the Blackfin + * + * Copyright 2009 Analog Device Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _BF5XX_TDM_PCM_H +#define _BF5XX_TDM_PCM_H + +struct bf5xx_pcm_dma_params { + char *name; /* stream identifier */ +}; + +/* platform data */ +extern struct snd_soc_platform bf5xx_tdm_soc_platform; + +#endif diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c new file mode 100644 index 0000000..9afe4d6 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-tdm.c @@ -0,0 +1,342 @@ +/* + * File: sound/soc/blackfin/bf5xx-tdm.c + * Author: Barry Song Barry.Song@analog.com + * + * Created: Thurs June 04 2009 + * Description: Blackfin I2S(TDM) CPU DAI driver + * Even though TDM mode can be as part of I2S DAI, but there + * are so much difference in configuration and data flow, + * it's very ugly to integrate I2S and TDM into a module + * + * Modified: + * Copyright 2009 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include <asm/irq.h> +#include <asm/portmux.h> +#include <linux/mutex.h> +#include <linux/gpio.h> + +#include "bf5xx-sport.h" +#include "bf5xx-tdm.h" + +struct bf5xx_tdm_port { + u16 tcr1; + u16 rcr1; + u16 tcr2; + u16 rcr2; + int configured; +}; + +static struct bf5xx_tdm_port bf5xx_tdm; +static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; + +static struct sport_param sport_params[2] = { + { + .dma_rx_chan = CH_SPORT0_RX, + .dma_tx_chan = CH_SPORT0_TX, + .err_irq = IRQ_SPORT0_ERROR, + .regs = (struct sport_register *)SPORT0_TCR1, + }, + { + .dma_rx_chan = CH_SPORT1_RX, + .dma_tx_chan = CH_SPORT1_TX, + .err_irq = IRQ_SPORT1_ERROR, + .regs = (struct sport_register *)SPORT1_TCR1, + } +}; + +/* + * Setting the TFS pin selector for SPORT 0 based on whether the selected + * port id F or G. If the port is F then no conflict should exist for the + * TFS. When Port G is selected and EMAC then there is a conflict between + * the PHY interrupt line and TFS. Current settings prevent the conflict + * by ignoring the TFS pin when Port G is selected. This allows both + * ssm2602 using Port G and EMAC concurrently. + */ +#ifdef CONFIG_BF527_SPORT0_PORTF +#define LOCAL_SPORT0_TFS (P_SPORT0_TFS) +#else +#define LOCAL_SPORT0_TFS (0) +#endif + +static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, + P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0}, + {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI, + P_SPORT1_RSCLK, P_SPORT1_TFS, 0} }; + +static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + int ret = 0; + + /* interface format:support TDM,slave mode */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + break; + default: + printk(KERN_ERR "%s: Unknown DAI format type\n", __func__); + ret = -EINVAL; + break; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + break; + case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + ret = -EINVAL; + break; + default: + printk(KERN_ERR "%s: Unknown DAI master type\n", __func__); + ret = -EINVAL; + break; + } + + return ret; +} + +static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int ret = 0; + + bf5xx_tdm.tcr2 &= ~0x1f; + bf5xx_tdm.rcr2 &= ~0x1f; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + bf5xx_tdm.tcr2 |= 31; + bf5xx_tdm.rcr2 |= 31; + sport_handle->wdsize = 4; + break; + /* at present, we only support 32bit transfer */ + default: + pr_err("not supported PCM format yet\n"); + break; + } + + if (!bf5xx_tdm.configured) { + /* + * TX and RX are not independent,they are enabled at the + * same time, even if only one side is running. So, we + * need to configure both of them at the time when the first + * stream is opened. + * + * CPU DAI:slave mode. + */ + ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1, + bf5xx_tdm.rcr2, 0, 0); + if (ret) { + pr_err("SPORT is busy!\n"); + return -EBUSY; + } + + ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1, + bf5xx_tdm.tcr2, 0, 0); + if (ret) { + pr_err("SPORT is busy!\n"); + return -EBUSY; + } + + bf5xx_tdm.configured = 1; + } + + return 0; +} + +static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* No active stream, SPORT is allowed to be configured again. */ + if (!dai->active) + bf5xx_tdm.configured = 0; +} + +#ifdef CONFIG_PM +static int bf5xx_tdm_suspend(struct snd_soc_dai *dai) +{ + struct sport_device *sport = + (struct sport_device *)dai->private_data; + + if (!dai->active) + return 0; + if (dai->capture.active) + sport_rx_stop(sport); + if (dai->playback.active) + sport_tx_stop(sport); + return 0; +} + +static int bf5xx_tdm_resume(struct snd_soc_dai *dai) +{ + int ret; + struct sport_device *sport = + (struct sport_device *)dai->private_data; + + if (!dai->active) + return 0; + + ret = sport_set_multichannel(sport, 8, 0xFF, 1); + if (ret) { + pr_err("SPORT is busy!\n"); + ret = -EBUSY; + } + + ret = sport_config_rx(sport, IRFS, 0x1F, 0, 0); + if (ret) { + pr_err("SPORT is busy!\n"); + ret = -EBUSY; + } + + ret = sport_config_tx(sport, ITFS, 0x1F, 0, 0); + if (ret) { + pr_err("SPORT is busy!\n"); + ret = -EBUSY; + } + + return 0; +} + +#else +#define bf5xx_tdm_suspend NULL +#define bf5xx_tdm_resume NULL +#endif + +static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = { + .hw_params = bf5xx_tdm_hw_params, + .set_fmt = bf5xx_tdm_set_dai_fmt, + .shutdown = bf5xx_tdm_shutdown, +}; + +struct snd_soc_dai bf5xx_tdm_dai = { + .name = "bf5xx-tdm", + .id = 0, + .suspend = bf5xx_tdm_suspend, + .resume = bf5xx_tdm_resume, + .playback = { + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE,}, + .capture = { + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE,}, + .ops = &bf5xx_tdm_dai_ops, +}; +EXPORT_SYMBOL_GPL(bf5xx_tdm_dai); + +static int __devinit bfin_tdm_probe(struct platform_device *pdev) +{ + int ret = 0; + + if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { + pr_err("Requesting Peripherals failed\n"); + return -EFAULT; + } + + /* request DMA for SPORT */ + sport_handle = sport_init(&sport_params[sport_num], 4, \ + 8 * sizeof(u32), NULL); + if (!sport_handle) { + peripheral_free_list(&sport_req[sport_num][0]); + return -ENODEV; + } + + /* SPORT works in TDM mode */ + ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1); + if (ret) { + pr_err("SPORT is busy!\n"); + ret = -EBUSY; + goto sport_config_err; + } + + ret = sport_config_rx(sport_handle, IRFS, 0x1F, 0, 0); + if (ret) { + pr_err("SPORT is busy!\n"); + ret = -EBUSY; + goto sport_config_err; + } + + ret = sport_config_tx(sport_handle, ITFS, 0x1F, 0, 0); + if (ret) { + pr_err("SPORT is busy!\n"); + ret = -EBUSY; + goto sport_config_err; + } + + ret = snd_soc_register_dai(&bf5xx_tdm_dai); + if (ret) { + pr_err("Failed to register DAI: %d\n", ret); + goto sport_config_err; + } + return 0; + +sport_config_err: + peripheral_free_list(&sport_req[sport_num][0]); + return ret; +} + +static int __devexit bfin_tdm_remove(struct platform_device *pdev) +{ + peripheral_free_list(&sport_req[sport_num][0]); + snd_soc_unregister_dai(&bf5xx_tdm_dai); + + return 0; +} + +static struct platform_driver bfin_tdm_driver = { + .probe = bfin_tdm_probe, + .remove = __devexit_p(bfin_tdm_remove), + .driver = { + .name = "bfin-sport", + .owner = THIS_MODULE, + }, +}; + +static int __init bfin_tdm_init(void) +{ + return platform_driver_register(&bfin_tdm_driver); +} +module_init(bfin_tdm_init); + +static void __exit bfin_tdm_exit(void) +{ + platform_driver_unregister(&bfin_tdm_driver); +} +module_exit(bfin_tdm_exit); + +/* Module information */ +MODULE_AUTHOR("Barry Song"); +MODULE_DESCRIPTION("TDM driver for ADI Blackfin"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/blackfin/bf5xx-tdm.h b/sound/soc/blackfin/bf5xx-tdm.h new file mode 100644 index 0000000..e5157cd --- /dev/null +++ b/sound/soc/blackfin/bf5xx-tdm.h @@ -0,0 +1,14 @@ +/* + * linux/sound/arm/bf5xx-i2s.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _BF5XX_TDM_H +#define _BF5XX_TDM_H + +extern struct snd_soc_dai bf5xx_tdm_dai; + +#endif
On Thu, Jul 23, 2009 at 02:10:33AM +0800, Barry Song wrote:
On the one hand, I2S and TDM means two completely different interface configuration to blackfin, results in completely different hardware configuration, DMA description and data transfer flow. Merging them as a whole will be very ugly and difficult to maintain.
I'm still a bit confused about why this is required - I'm not sure where the requirement for the changes in the DMA layout come from.
Looking at the code it appears that the main difference is that your TDM mode always configures the DMA layout for 8 channel blocks. This appears to be because the SPORT code requires that you specify the block size statically when you call sport_init(). Looking at the code it seems like the SPORT hardware is capable of changing the memory layout on the fly (at least, all this stuff appears to be written down by software at various points during setup so I'd expect it to be changable at runtime) and that the restrictions here are due to the SPORT API rather than the hardware itself?
If that is the case then would it not be better to refactor the SPORT API so that it is capable of changing the configuration on the fly and then update the existing I2S driver to be able to take advantage of that? This would reduce the amount of code duplication here and I'd expect it to also make things run more efficiently when less than 8 channels are in use in TDM mode.
On the other hand, hardware board connection decides the I2S/TDM mode directly. It's needless to switch between I2S and TDM dynamically. For TDM mode, board driver can refer to the TDM instance, for I2S mode,
Are you saying that a physically different SPORT or other hardware configuration on the CPU is required to use TDM mode? The impression given by the drivers has always been that this is some kind of generic synchronous serial port and the way the SPORT number is set up seems to support that. Could you go into more detail here, please - this would be a very unusual implementation decision for a CPU.
- if (sport_handle != NULL)
runtime->private_data = sport_handle;
- else {
pr_err("sport_handle is NULL\n");
return -1;
- }
Should return a real error code here.
+int bf5xx_pcm_tdm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+{
This should be static; any functions that are exported via operations structures should be static.
+static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+{
- int ret = 0;
- bf5xx_tdm.tcr2 &= ~0x1f;
- bf5xx_tdm.rcr2 &= ~0x1f;
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S32_LE:
bf5xx_tdm.tcr2 |= 31;
bf5xx_tdm.rcr2 |= 31;
sport_handle->wdsize = 4;
break;
/* at present, we only support 32bit transfer */
- default:
pr_err("not supported PCM format yet\n");
break;
This should return an error.
+static int __devinit bfin_tdm_probe(struct platform_device *pdev) +{
- int ret = 0;
- if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
pr_err("Requesting Peripherals failed\n");
return -EFAULT;
- }
+static struct platform_driver bfin_tdm_driver = {
- .probe = bfin_tdm_probe,
- .remove = __devexit_p(bfin_tdm_remove),
- .driver = {
.name = "bfin-sport",
.owner = THIS_MODULE,
- },
+};
This isn't a good name for the driver - all of the Blackfin CPU audio drivers and probably some others use a SPORT so the same driver name would apply for each of them. If more than one of these drivers chose the same name then you'd not be able to have both drivers in the kernel at once. The driver name should be something that's specific to the device you're trying to register, such as bfin-tdm.
Hi Mark,
2009/7/25 Mark Brown broonie@opensource.wolfsonmicro.com:
On Thu, Jul 23, 2009 at 02:10:33AM +0800, Barry Song wrote:
On the one hand, I2S and TDM means two completely different interface configuration to blackfin, results in completely different hardware configuration, DMA description and data transfer flow. Merging them as a whole will be very ugly and difficult to maintain.
I'm still a bit confused about why this is required - I'm not sure where the requirement for the changes in the DMA layout come from.
Looking at the code it appears that the main difference is that your TDM mode always configures the DMA layout for 8 channel blocks. This
I2S is not a special case of TDM with only left and right two slots for SPORT interface. I2S coordinates with TDM in SPORT, but not a part of TDM. TDM require different hardware configuration with I2S, not only different slot number. One is "Stereo Serial Operation" mode of SPORT, the other one is "Multichannel Operation" mode. They are incompatible at the same time.
appears to be because the SPORT code requires that you specify the block size statically when you call sport_init(). Looking at the code it seems like the SPORT hardware is capable of changing the memory layout on the fly (at least, all this stuff appears to be written down by software at various points during setup so I'd expect it to be changable at runtime) and that the restrictions here are due to the SPORT API rather than the hardware itself?
If that is the case then would it not be better to refactor the SPORT API so that it is capable of changing the configuration on the fly and then update the existing I2S driver to be able to take advantage of that? This would reduce the amount of code duplication here and I'd expect it to also make things run more efficiently when less than 8 channels are in use in TDM mode.
Mainly due to hardware, it's really difficult for us to control the memory layout on the fly. That causes much trouble in hardware and driver reliability. Datasheet claims DMA buffer should be fixed while SPORT is enabled, but also claims the layout may be changed while SPORT is disabled. So it's maybe possible for us to change the SPORT api to support the configuration on the fly with a SPORT shutdown, then i2s, tdm even ac97 can all use them to reduce the amount of code duplication. But it need much work on debugging and testing to make it work and reliable. So how about to patch it later as an individual project since the changes will involve sport, i2s, ac97 and tdm?
On the other hand, hardware board connection decides the I2S/TDM mode directly. It's needless to switch between I2S and TDM dynamically. For TDM mode, board driver can refer to the TDM instance, for I2S mode,
Are you saying that a physically different SPORT or other hardware configuration on the CPU is required to use TDM mode? The impression given by the drivers has always been that this is some kind of generic synchronous serial port and the way the SPORT number is set up seems to support that. Could you go into more detail here, please - this would be a very unusual implementation decision for a CPU.
Here I means the different hardware configuration, and the semantic of pins has some differences too for I2S and TDM.
- if (sport_handle != NULL)
- runtime->private_data = sport_handle;
- else {
- pr_err("sport_handle is NULL\n");
- return -1;
- }
Should return a real error code here.
Fixed
+int bf5xx_pcm_tdm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
+{
This should be static; any functions that are exported via operations structures should be static.
Fixed
+static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+{
- int ret = 0;
- bf5xx_tdm.tcr2 &= ~0x1f;
- bf5xx_tdm.rcr2 &= ~0x1f;
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S32_LE:
- bf5xx_tdm.tcr2 |= 31;
- bf5xx_tdm.rcr2 |= 31;
- sport_handle->wdsize = 4;
- break;
- /* at present, we only support 32bit transfer */
- default:
- pr_err("not supported PCM format yet\n");
- break;
This should return an error.
Fixed
+static int __devinit bfin_tdm_probe(struct platform_device *pdev) +{
- int ret = 0;
- if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
- pr_err("Requesting Peripherals failed\n");
- return -EFAULT;
- }
+static struct platform_driver bfin_tdm_driver = {
- .probe = bfin_tdm_probe,
- .remove = __devexit_p(bfin_tdm_remove),
- .driver = {
- .name = "bfin-sport",
- .owner = THIS_MODULE,
- },
+};
This isn't a good name for the driver - all of the Blackfin CPU audio drivers and probably some others use a SPORT so the same driver name would apply for each of them. If more than one of these drivers chose the same name then you'd not be able to have both drivers in the kernel at once. The driver name should be something that's specific to the device you're trying to register, such as bfin-tdm.
Fixed
Thanks Barry
On Mon, Jul 27, 2009 at 06:00:48PM +0800, Barry Song wrote:
2009/7/25 Mark Brown broonie@opensource.wolfsonmicro.com:
Looking at the code it appears that the main difference is that your TDM mode always configures the DMA layout for 8 channel blocks. This
I2S is not a special case of TDM with only left and right two slots for SPORT interface. I2S coordinates with TDM in SPORT, but not a part of TDM. TDM require different hardware configuration with I2S, not only different slot number. One is "Stereo Serial Operation" mode of SPORT, the other one is "Multichannel Operation" mode. They are incompatible at the same time.
Right, but that's something I'd expect to be handleable at run time - and in any case, the multi-channel varaint can drop down to only two channels.
Mainly due to hardware, it's really difficult for us to control the memory layout on the fly. That causes much trouble in hardware and driver reliability. Datasheet claims DMA buffer should be fixed while SPORT is enabled, but also claims the layout may be changed while SPORT is disabled. So it's maybe possible for us to change the SPORT api to support the configuration on the fly with a SPORT shutdown, then i2s, tdm even ac97 can all use them to reduce the amount of code duplication. But it need much work on debugging and testing to make it work and reliable. So how about to patch it later as an individual project since the changes will involve sport, i2s, ac97 and tdm?
OK, if the hardware is as difficult to use as that I guess the current situation is adequate. I'll check over your patch later but the code was almost ready last time so I don't anticipate any problems. However...
On the other hand, hardware board connection decides the I2S/TDM mode directly. It's needless to switch between I2S and TDM dynamically. For TDM mode, board driver can refer to the TDM instance, for I2S mode,
Are you saying that a physically different SPORT or other hardware configuration on the CPU is required to use TDM mode? The impression given by the drivers has always been that this is some kind of generic synchronous serial port and the way the SPORT number is set up seems to support that. Could you go into more detail here, please - this would be a very unusual implementation decision for a CPU.
Here I means the different hardware configuration, and the semantic of pins has some differences too for I2S and TDM.
...are you sure there's a different meaning for the external pins - what are these differences? If they're not just related to the number of bit clock cycles per frame clock then are you sure it will interoperate with other DSP mode devices?
2009/7/27 Mark Brown broonie@opensource.wolfsonmicro.com:
On Mon, Jul 27, 2009 at 06:00:48PM +0800, Barry Song wrote:
2009/7/25 Mark Brown broonie@opensource.wolfsonmicro.com:
Looking at the code it appears that the main difference is that your TDM mode always configures the DMA layout for 8 channel blocks. This
I2S is not a special case of TDM with only left and right two slots for SPORT interface. I2S coordinates with TDM in SPORT, but not a part of TDM. TDM require different hardware configuration with I2S, not only different slot number. One is "Stereo Serial Operation" mode of SPORT, the other one is "Multichannel Operation" mode. They are incompatible at the same time.
Right, but that's something I'd expect to be handleable at run time - and in any case, the multi-channel varaint can drop down to only two channels.
Mainly due to hardware, it's really difficult for us to control the memory layout on the fly. That causes much trouble in hardware and driver reliability. Datasheet claims DMA buffer should be fixed while SPORT is enabled, but also claims the layout may be changed while SPORT is disabled. So it's maybe possible for us to change the SPORT api to support the configuration on the fly with a SPORT shutdown, then i2s, tdm even ac97 can all use them to reduce the amount of code duplication. But it need much work on debugging and testing to make it work and reliable. So how about to patch it later as an individual project since the changes will involve sport, i2s, ac97 and tdm?
OK, if the hardware is as difficult to use as that I guess the current situation is adequate. I'll check over your patch later but the code was almost ready last time so I don't anticipate any problems. However...
On the other hand, hardware board connection decides the I2S/TDM mode directly. It's needless to switch between I2S and TDM dynamically. For TDM mode, board driver can refer to the TDM instance, for I2S mode,
Are you saying that a physically different SPORT or other hardware configuration on the CPU is required to use TDM mode? The impression given by the drivers has always been that this is some kind of generic synchronous serial port and the way the SPORT number is set up seems to support that. Could you go into more detail here, please - this would be a very unusual implementation decision for a CPU.
Here I means the different hardware configuration, and the semantic of pins has some differences too for I2S and TDM.
...are you sure there's a different meaning for the external pins - what are these differences? If they're not just related to the number of bit clock cycles per frame clock then are you sure it will interoperate with other DSP mode devices?
The main differences are the usages. For I2S mode, each SPORT has two sets of independent transmit and receive pins. Every set of pins support a stereo receive or transmit. So a typical hardware connection to support multi-channels in I2S mode is like attached "I2S.gif". Many pins are connected, and the main characteristic is using multi-sets of pins concurrently. And in the codec side, DSDATA1-n get the semantic as "DACx Data In", ASDATA1-n are "ADCx Data Out". For TDM mode, the main characteristic is using time-sharing to a set of pins. A typical connection for TDM is like "tdm.gif". Only tx/rx/clk/sync are connected to codec. And in codec side, DSDATA1 gets the semantic as "DAC TDM Data In", DSDATA2-n are not "DACx Data In" again. ASDATA1 becomes "ADC TDM Data Out", ASDATA2-n are not "ADCx Data Out" again. And another difference is when multichannel mode is enabled on a SPORT, both the transmitter and the receiver use RFS, which is only a receive sync in I2S mode, as a frame sync. The RFS signal is used to synchronize the channels and restart each multichannel sequence. TFS, which is "Transmit Frame Sync" in I2S mode, becomes TDV "transmit data valid" for external logic in TDM. In TDM mode, the SPORT can interoperate with other DSP mode devices considering they are using "tx/rx/clk/sync" pins. That's very common.
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
On Tue, Jul 28, 2009 at 12:32:01AM +0800, Barry Song wrote:
2009/7/27 Mark Brown broonie@opensource.wolfsonmicro.com:
...are you sure there's a different meaning for the external pins - what are these differences? If they're not just related to the number of bit clock cycles per frame clock then are you sure it will interoperate with other DSP mode devices?
For I2S mode, each SPORT has two sets of independent transmit and receive pins. Every set of pins support a stereo receive or transmit.
For TDM mode, the main characteristic is using time-sharing to a set of pins. A typical connection for TDM is like "tdm.gif". Only tx/rx/clk/sync are connected to codec. And in codec side, DSDATA1
OK, that's not really a difference as far as ASoC is concerned - all the pins have their normal meanings, it's just that your I2S mode permits additional data lines (presumably your TDM mode could also be set up to use multiple data lines). None of the pins change meaning.
Signed-off-by: Barry Song 21cnbao@gmail.com
Changes from the last patch version: Fix the wrong mask in set tdm slot --- sound/soc/blackfin/Kconfig | 8 ++ sound/soc/blackfin/Makefile | 2 + sound/soc/blackfin/bf5xx-ad1938.c | 142 +++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 0 deletions(-) create mode 100644 sound/soc/blackfin/bf5xx-ad1938.c
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index 2ab6d2f..8a4de4d 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -96,6 +96,14 @@ config SND_BF5XX_SOC_AD1980 help Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
+config SND_BF5XX_SOC_AD1938 + tristate "SoC AD1938 Audio support for Blackfin" + depends on SND_BF5XX_TDM + select SND_BF5XX_SOC_TDM + select SND_SOC_AD1938 + help + Say Y if you want to add support for AD1938 codec on Blackfin. + config SND_BF5XX_SPORT_NUM int "Set a SPORT for Sound chip" depends on (SND_BF5XX_I2S || SND_BF5XX_AC97 || SND_BF5XX_TDM) diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile index f861926..f4d7607 100644 --- a/sound/soc/blackfin/Makefile +++ b/sound/soc/blackfin/Makefile @@ -19,7 +19,9 @@ obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o snd-ad1980-objs := bf5xx-ad1980.o snd-ssm2602-objs := bf5xx-ssm2602.o snd-ad73311-objs := bf5xx-ad73311.o +snd-ad1938-objs := bf5xx-ad1938.o
obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o +obj-$(CONFIG_SND_BF5XX_SOC_AD1938) += snd-ad1938.o diff --git a/sound/soc/blackfin/bf5xx-ad1938.c b/sound/soc/blackfin/bf5xx-ad1938.c new file mode 100644 index 0000000..08269e9 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ad1938.c @@ -0,0 +1,142 @@ +/* + * File: sound/soc/blackfin/bf5xx-ad1938.c + * Author: Barry Song Barry.Song@analog.com + * + * Created: Thur June 4 2009 + * Description: Board driver for ad1938 sound chip + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/pcm_params.h> + +#include <asm/blackfin.h> +#include <asm/cacheflush.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +#include "../codecs/ad1938.h" +#include "bf5xx-sport.h" + +#include "bf5xx-tdm-pcm.h" +#include "bf5xx-tdm.h" + +static struct snd_soc_card bf5xx_ad1938; + +static int bf5xx_ad1938_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + + cpu_dai->private_data = sport_handle; + return 0; +} + +static int bf5xx_ad1938_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 *cpu_dai = rtd->dai->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + int ret = 0; + /* set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + /* set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + /* set codec DAI slots, 8 channels, all channels are enabled */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 8); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops bf5xx_ad1938_ops = { + .startup = bf5xx_ad1938_startup, + .hw_params = bf5xx_ad1938_hw_params, +}; + +static struct snd_soc_dai_link bf5xx_ad1938_dai = { + .name = "ad1938", + .stream_name = "AD1938", + .cpu_dai = &bf5xx_tdm_dai, + .codec_dai = &ad1938_dai, + .ops = &bf5xx_ad1938_ops, +}; + +static struct snd_soc_card bf5xx_ad1938 = { + .name = "bf5xx_ad1938", + .platform = &bf5xx_tdm_soc_platform, + .dai_link = &bf5xx_ad1938_dai, + .num_links = 1, +}; + +static struct snd_soc_device bf5xx_ad1938_snd_devdata = { + .card = &bf5xx_ad1938, + .codec_dev = &soc_codec_dev_ad1938, +}; + +static struct platform_device *bfxx_ad1938_snd_device; + +static int __init bf5xx_ad1938_init(void) +{ + int ret; + + bfxx_ad1938_snd_device = platform_device_alloc("soc-audio", -1); + if (!bfxx_ad1938_snd_device) + return -ENOMEM; + + platform_set_drvdata(bfxx_ad1938_snd_device, &bf5xx_ad1938_snd_devdata); + bf5xx_ad1938_snd_devdata.dev = &bfxx_ad1938_snd_device->dev; + ret = platform_device_add(bfxx_ad1938_snd_device); + + if (ret) + platform_device_put(bfxx_ad1938_snd_device); + + return ret; +} + +static void __exit bf5xx_ad1938_exit(void) +{ + platform_device_unregister(bfxx_ad1938_snd_device); +} + +module_init(bf5xx_ad1938_init); +module_exit(bf5xx_ad1938_exit); + +/* Module information */ +MODULE_AUTHOR("Barry Song"); +MODULE_DESCRIPTION("ALSA SoC AD1938 board driver"); +MODULE_LICENSE("GPL"); +
On Thu, Jul 23, 2009 at 02:10:34AM +0800, Barry Song wrote:
Signed-off-by: Barry Song 21cnbao@gmail.com
Changes from the last patch version: Fix the wrong mask in set tdm slot
This is all OK.
On Thu, Jul 23, 2009 at 02:10:34AM +0800, Barry Song wrote:
Signed-off-by: Barry Song 21cnbao@gmail.com
Changes from the last patch version: Fix the wrong mask in set tdm slot
Applied, thanks.
participants (2)
-
Barry Song
-
Mark Brown