aboutsummaryrefslogtreecommitdiff
path: root/sound/soc/davinci
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/davinci')
-rw-r--r--sound/soc/davinci/Kconfig94
-rw-r--r--sound/soc/davinci/Makefile10
-rw-r--r--sound/soc/davinci/davinci-evm.c487
-rw-r--r--sound/soc/davinci/davinci-i2s.c647
-rw-r--r--sound/soc/davinci/davinci-i2s.h5
-rw-r--r--sound/soc/davinci/davinci-mcasp.c1276
-rw-r--r--sound/soc/davinci/davinci-mcasp.h289
-rw-r--r--sound/soc/davinci/davinci-pcm.c661
-rw-r--r--sound/soc/davinci/davinci-pcm.h32
-rw-r--r--sound/soc/davinci/davinci-sffsdr.c178
-rw-r--r--sound/soc/davinci/davinci-vcif.c278
-rw-r--r--sound/soc/davinci/edma-pcm.c57
-rw-r--r--sound/soc/davinci/edma-pcm.h25
13 files changed, 3396 insertions, 643 deletions
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 411a710be66..50a098749b9 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -1,30 +1,82 @@
config SND_DAVINCI_SOC
- tristate "SoC Audio for the TI DAVINCI chip"
- depends on ARCH_DAVINCI
- help
- Say Y or M if you want to add support for codecs attached to
- the DAVINCI AC97 or I2S interface. You will also need
- to select the audio interfaces to support below.
+ tristate "SoC Audio for TI DAVINCI or AM33XX/AM43XX chips"
+ depends on ARCH_DAVINCI || SOC_AM33XX || SOC_AM43XX
config SND_DAVINCI_SOC_I2S
tristate
-config SND_DAVINCI_SOC_EVM
- tristate "SoC Audio support for DaVinci DM6446 or DM355 EVM"
- depends on SND_DAVINCI_SOC
- depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM
- select SND_DAVINCI_SOC_I2S
+config SND_DAVINCI_SOC_MCASP
+ tristate
+
+config SND_DAVINCI_SOC_VCIF
+ tristate
+
+config SND_DAVINCI_SOC_GENERIC_EVM
+ tristate
select SND_SOC_TLV320AIC3X
+ select SND_DAVINCI_SOC_MCASP
+
+config SND_AM33XX_SOC_EVM
+ tristate "SoC Audio for the AM33XX chip based boards"
+ depends on SND_DAVINCI_SOC && SOC_AM33XX && I2C
+ select SND_DAVINCI_SOC_GENERIC_EVM
+ help
+ Say Y or M if you want to add support for SoC audio on AM33XX
+ boards using McASP and TLV320AIC3X codec. For example AM335X-EVM,
+ AM335X-EVMSK, and BeagelBone with AudioCape boards have this
+ setup.
+
+config SND_DAVINCI_SOC_EVM
+ tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
+ depends on SND_DAVINCI_SOC && I2C
+ depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM
+ select SND_DAVINCI_SOC_GENERIC_EVM
help
Say Y if you want to add support for SoC audio on TI
- DaVinci DM6446 or DM355 EVM platforms.
-
-config SND_DAVINCI_SOC_SFFSDR
- tristate "SoC Audio support for SFFSDR"
- depends on SND_DAVINCI_SOC && MACH_SFFSDR
- select SND_DAVINCI_SOC_I2S
- select SND_SOC_PCM3008
- select SFFSDR_FPGA
+ DaVinci DM6446, DM355 or DM365 EVM platforms.
+
+choice
+ prompt "DM365 codec select"
+ depends on SND_DAVINCI_SOC_EVM
+ depends on MACH_DAVINCI_DM365_EVM
+
+config SND_DM365_AIC3X_CODEC
+ bool "Audio Codec - AIC3101"
help
- Say Y if you want to add support for SoC audio on
- Lyrtech SFFSDR board.
+ Say Y if you want to add support for AIC3101 audio codec
+
+config SND_DM365_VOICE_CODEC
+ bool "Voice Codec - CQ93VC"
+ select MFD_DAVINCI_VOICECODEC
+ select SND_DAVINCI_SOC_VCIF
+ select SND_SOC_CQ0093VC
+ help
+ Say Y if you want to add support for SoC On-chip voice codec
+endchoice
+
+config SND_DM6467_SOC_EVM
+ tristate "SoC Audio support for DaVinci DM6467 EVM"
+ depends on SND_DAVINCI_SOC && MACH_DAVINCI_DM6467_EVM && I2C
+ select SND_DAVINCI_SOC_GENERIC_EVM
+ select SND_SOC_SPDIF
+
+ help
+ Say Y if you want to add support for SoC audio on TI
+
+config SND_DA830_SOC_EVM
+ tristate "SoC Audio support for DA830/OMAP-L137 EVM"
+ depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA830_EVM && I2C
+ select SND_DAVINCI_SOC_GENERIC_EVM
+
+ help
+ Say Y if you want to add support for SoC audio on TI
+ DA830/OMAP-L137 EVM
+
+config SND_DA850_SOC_EVM
+ tristate "SoC Audio support for DA850/OMAP-L138 EVM"
+ depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM && I2C
+ select SND_DAVINCI_SOC_GENERIC_EVM
+ help
+ Say Y if you want to add support for SoC audio on TI
+ DA850/OMAP-L138 EVM
+
diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile
index ca8bae1fc3f..744d4d9a018 100644
--- a/sound/soc/davinci/Makefile
+++ b/sound/soc/davinci/Makefile
@@ -1,13 +1,15 @@
# DAVINCI Platform Support
snd-soc-davinci-objs := davinci-pcm.o
snd-soc-davinci-i2s-objs := davinci-i2s.o
+snd-soc-davinci-mcasp-objs:= davinci-mcasp.o
+snd-soc-davinci-vcif-objs:= davinci-vcif.o
obj-$(CONFIG_SND_DAVINCI_SOC) += snd-soc-davinci.o
obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
+obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o
+obj-$(CONFIG_SND_DAVINCI_SOC_VCIF) += snd-soc-davinci-vcif.o
-# DAVINCI Machine Support
+# Generic DAVINCI/AM33xx Machine Support
snd-soc-evm-objs := davinci-evm.o
-snd-soc-sffsdr-objs := davinci-sffsdr.o
-obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o
-obj-$(CONFIG_SND_DAVINCI_SOC_SFFSDR) += snd-soc-sffsdr.o
+obj-$(CONFIG_SND_DAVINCI_SOC_GENERIC_EVM) += snd-soc-evm.o
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index 58fd1cbedd8..a50010e2891 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -14,67 +14,78 @@
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/platform_data/edma.h>
+#include <linux/i2c.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
-#include <sound/soc-dapm.h>
#include <asm/dma.h>
#include <asm/mach-types.h>
-#include <mach/asp.h>
-#include <mach/edma.h>
-#include <mach/mux.h>
+#include <linux/edma.h>
-#include "../codecs/tlv320aic3x.h"
#include "davinci-pcm.h"
#include "davinci-i2s.h"
+struct snd_soc_card_drvdata_davinci {
+ struct clk *mclk;
+ unsigned sysclk;
+};
-#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
- SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF)
-static int evm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int evm_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- int ret = 0;
- unsigned sysclk;
+ struct snd_soc_card *soc_card = rtd->card;
+ struct snd_soc_card_drvdata_davinci *drvdata =
+ snd_soc_card_get_drvdata(soc_card);
- /* ASP1 on DM355 EVM is clocked by an external oscillator */
- if (machine_is_davinci_dm355_evm())
- sysclk = 27000000;
+ if (drvdata->mclk)
+ return clk_prepare_enable(drvdata->mclk);
- /* ASP0 in DM6446 EVM is clocked by U55, as configured by
- * board-dm644x-evm.c using GPIOs from U18. There are six
- * options; here we "know" we use a 48 KHz sample rate.
- */
- else if (machine_is_davinci_evm())
- sysclk = 12288000;
+ return 0;
+}
- else
- return -EINVAL;
+static void evm_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *soc_card = rtd->card;
+ struct snd_soc_card_drvdata_davinci *drvdata =
+ snd_soc_card_get_drvdata(soc_card);
- /* set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT);
- if (ret < 0)
- return ret;
+ if (drvdata->mclk)
+ clk_disable_unprepare(drvdata->mclk);
+}
- /* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
- if (ret < 0)
- return ret;
+static int evm_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 snd_soc_card *soc_card = rtd->card;
+ int ret = 0;
+ unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *)
+ snd_soc_card_get_drvdata(soc_card))->sysclk;
/* set the codec system clock */
ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
+ /* set the CPU system clock */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
return 0;
}
static struct snd_soc_ops evm_ops = {
+ .startup = evm_startup,
+ .shutdown = evm_shutdown,
.hw_params = evm_hw_params,
};
@@ -97,9 +108,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"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"},
+ {"MIC3L", NULL, "Mic Bias"},
+ {"MIC3R", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Mic Jack"},
/* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */
{"LINE1L", NULL, "Line In"},
@@ -109,116 +120,373 @@ static const struct snd_soc_dapm_route audio_map[] = {
};
/* Logic for a aic3x as connected on a davinci-evm */
-static int evm_aic3x_init(struct snd_soc_codec *codec)
+static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct device_node *np = card->dev->of_node;
+ int ret;
+
/* Add davinci-evm specific widgets */
- snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
+ snd_soc_dapm_new_controls(&card->dapm, aic3x_dapm_widgets,
ARRAY_SIZE(aic3x_dapm_widgets));
- /* Set up davinci-evm specific audio path audio_map */
- snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+ if (np) {
+ ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing");
+ if (ret)
+ return ret;
+ } else {
+ /* Set up davinci-evm specific audio path audio_map */
+ snd_soc_dapm_add_routes(&card->dapm, audio_map,
+ ARRAY_SIZE(audio_map));
+ }
/* not connected */
- snd_soc_dapm_disable_pin(codec, "MONO_LOUT");
- snd_soc_dapm_disable_pin(codec, "HPLCOM");
- snd_soc_dapm_disable_pin(codec, "HPRCOM");
-
- /* always connected */
- snd_soc_dapm_enable_pin(codec, "Headphone Jack");
- snd_soc_dapm_enable_pin(codec, "Line Out");
- snd_soc_dapm_enable_pin(codec, "Mic Jack");
- snd_soc_dapm_enable_pin(codec, "Line In");
-
- snd_soc_dapm_sync(codec);
+ snd_soc_dapm_nc_pin(&codec->dapm, "MONO_LOUT");
+ snd_soc_dapm_nc_pin(&codec->dapm, "HPLCOM");
+ snd_soc_dapm_nc_pin(&codec->dapm, "HPRCOM");
return 0;
}
/* davinci-evm digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link evm_dai = {
+static struct snd_soc_dai_link dm6446_evm_dai = {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai_name = "davinci-mcbsp",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .codec_name = "tlv320aic3x-codec.1-001b",
+ .platform_name = "davinci-mcbsp",
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+};
+
+static struct snd_soc_dai_link dm355_evm_dai = {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai_name = "davinci-mcbsp.1",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .codec_name = "tlv320aic3x-codec.1-001b",
+ .platform_name = "davinci-mcbsp.1",
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+};
+
+static struct snd_soc_dai_link dm365_evm_dai = {
+#ifdef CONFIG_SND_DM365_AIC3X_CODEC
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai_name = "davinci-mcbsp",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .codec_name = "tlv320aic3x-codec.1-0018",
+ .platform_name = "davinci-mcbsp",
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+#elif defined(CONFIG_SND_DM365_VOICE_CODEC)
+ .name = "Voice Codec - CQ93VC",
+ .stream_name = "CQ93",
+ .cpu_dai_name = "davinci-vcif",
+ .codec_dai_name = "cq93vc-hifi",
+ .codec_name = "cq93vc-codec",
+ .platform_name = "davinci-vcif",
+#endif
+};
+
+static struct snd_soc_dai_link dm6467_evm_dai[] = {
+ {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai_name= "davinci-mcasp.0",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .platform_name = "davinci-mcasp.0",
+ .codec_name = "tlv320aic3x-codec.0-001a",
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+ },
+ {
+ .name = "McASP",
+ .stream_name = "spdif",
+ .cpu_dai_name= "davinci-mcasp.1",
+ .codec_dai_name = "dit-hifi",
+ .codec_name = "spdif_dit",
+ .platform_name = "davinci-mcasp.1",
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+ },
+};
+
+static struct snd_soc_dai_link da830_evm_dai = {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai_name = "davinci-mcasp.1",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .codec_name = "tlv320aic3x-codec.1-0018",
+ .platform_name = "davinci-mcasp.1",
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+};
+
+static struct snd_soc_dai_link da850_evm_dai = {
.name = "TLV320AIC3X",
.stream_name = "AIC3X",
- .cpu_dai = &davinci_i2s_dai,
- .codec_dai = &aic3x_dai,
+ .cpu_dai_name= "davinci-mcasp.0",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .codec_name = "tlv320aic3x-codec.1-0018",
+ .platform_name = "davinci-mcasp.0",
.init = evm_aic3x_init,
.ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
};
-/* davinci-evm audio machine driver */
-static struct snd_soc_card snd_soc_card_evm = {
- .name = "DaVinci EVM",
- .platform = &davinci_soc_platform,
- .dai_link = &evm_dai,
+/* davinci dm6446 evm audio machine driver */
+/*
+ * ASP0 in DM6446 EVM is clocked by U55, as configured by
+ * board-dm644x-evm.c using GPIOs from U18. There are six
+ * options; here we "know" we use a 48 KHz sample rate.
+ */
+static struct snd_soc_card_drvdata_davinci dm6446_snd_soc_card_drvdata = {
+ .sysclk = 12288000,
+};
+
+static struct snd_soc_card dm6446_snd_soc_card_evm = {
+ .name = "DaVinci DM6446 EVM",
+ .owner = THIS_MODULE,
+ .dai_link = &dm6446_evm_dai,
.num_links = 1,
+ .drvdata = &dm6446_snd_soc_card_drvdata,
};
-/* evm audio private data */
-static struct aic3x_setup_data evm_aic3x_setup = {
- .i2c_bus = 1,
- .i2c_address = 0x1b,
+/* davinci dm355 evm audio machine driver */
+/* ASP1 on DM355 EVM is clocked by an external oscillator */
+static struct snd_soc_card_drvdata_davinci dm355_snd_soc_card_drvdata = {
+ .sysclk = 27000000,
};
-/* evm audio subsystem */
-static struct snd_soc_device evm_snd_devdata = {
- .card = &snd_soc_card_evm,
- .codec_dev = &soc_codec_dev_aic3x,
- .codec_data = &evm_aic3x_setup,
+static struct snd_soc_card dm355_snd_soc_card_evm = {
+ .name = "DaVinci DM355 EVM",
+ .owner = THIS_MODULE,
+ .dai_link = &dm355_evm_dai,
+ .num_links = 1,
+ .drvdata = &dm355_snd_soc_card_drvdata,
};
-/* DM6446 EVM uses ASP0; line-out is a pair of RCA jacks */
-static struct resource evm_snd_resources[] = {
- {
- .start = DAVINCI_ASP0_BASE,
- .end = DAVINCI_ASP0_BASE + SZ_8K - 1,
- .flags = IORESOURCE_MEM,
- },
+/* davinci dm365 evm audio machine driver */
+static struct snd_soc_card_drvdata_davinci dm365_snd_soc_card_drvdata = {
+ .sysclk = 27000000,
+};
+
+static struct snd_soc_card dm365_snd_soc_card_evm = {
+ .name = "DaVinci DM365 EVM",
+ .owner = THIS_MODULE,
+ .dai_link = &dm365_evm_dai,
+ .num_links = 1,
+ .drvdata = &dm365_snd_soc_card_drvdata,
};
-static struct evm_snd_platform_data evm_snd_data = {
- .tx_dma_ch = DAVINCI_DMA_ASP0_TX,
- .rx_dma_ch = DAVINCI_DMA_ASP0_RX,
+/* davinci dm6467 evm audio machine driver */
+static struct snd_soc_card_drvdata_davinci dm6467_snd_soc_card_drvdata = {
+ .sysclk = 27000000,
+};
+
+static struct snd_soc_card dm6467_snd_soc_card_evm = {
+ .name = "DaVinci DM6467 EVM",
+ .owner = THIS_MODULE,
+ .dai_link = dm6467_evm_dai,
+ .num_links = ARRAY_SIZE(dm6467_evm_dai),
+ .drvdata = &dm6467_snd_soc_card_drvdata,
+};
+
+static struct snd_soc_card_drvdata_davinci da830_snd_soc_card_drvdata = {
+ .sysclk = 24576000,
+};
+
+static struct snd_soc_card da830_snd_soc_card = {
+ .name = "DA830/OMAP-L137 EVM",
+ .owner = THIS_MODULE,
+ .dai_link = &da830_evm_dai,
+ .num_links = 1,
+ .drvdata = &da830_snd_soc_card_drvdata,
+};
+
+static struct snd_soc_card_drvdata_davinci da850_snd_soc_card_drvdata = {
+ .sysclk = 24576000,
+};
+
+static struct snd_soc_card da850_snd_soc_card = {
+ .name = "DA850/OMAP-L138 EVM",
+ .owner = THIS_MODULE,
+ .dai_link = &da850_evm_dai,
+ .num_links = 1,
+ .drvdata = &da850_snd_soc_card_drvdata,
+};
+
+#if defined(CONFIG_OF)
+
+/*
+ * The struct is used as place holder. It will be completely
+ * filled with data from dt node.
+ */
+static struct snd_soc_dai_link evm_dai_tlv320aic3x = {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .ops = &evm_ops,
+ .init = evm_aic3x_init,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
};
-/* DM335 EVM uses ASP1; line-out is a stereo mini-jack */
-static struct resource dm335evm_snd_resources[] = {
+static const struct of_device_id davinci_evm_dt_ids[] = {
{
- .start = DAVINCI_ASP1_BASE,
- .end = DAVINCI_ASP1_BASE + SZ_8K - 1,
- .flags = IORESOURCE_MEM,
+ .compatible = "ti,da830-evm-audio",
+ .data = (void *) &evm_dai_tlv320aic3x,
},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, davinci_evm_dt_ids);
+
+/* davinci evm audio machine driver */
+static struct snd_soc_card evm_soc_card = {
+ .owner = THIS_MODULE,
+ .num_links = 1,
};
-static struct evm_snd_platform_data dm335evm_snd_data = {
- .tx_dma_ch = DAVINCI_DMA_ASP1_TX,
- .rx_dma_ch = DAVINCI_DMA_ASP1_RX,
+static int davinci_evm_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *match =
+ of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev);
+ struct snd_soc_dai_link *dai = (struct snd_soc_dai_link *) match->data;
+ struct snd_soc_card_drvdata_davinci *drvdata = NULL;
+ struct clk *mclk;
+ int ret = 0;
+
+ evm_soc_card.dai_link = dai;
+
+ dai->codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0);
+ if (!dai->codec_of_node)
+ return -EINVAL;
+
+ dai->cpu_of_node = of_parse_phandle(np, "ti,mcasp-controller", 0);
+ if (!dai->cpu_of_node)
+ return -EINVAL;
+
+ dai->platform_of_node = dai->cpu_of_node;
+
+ evm_soc_card.dev = &pdev->dev;
+ ret = snd_soc_of_parse_card_name(&evm_soc_card, "ti,model");
+ if (ret)
+ return ret;
+
+ mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (PTR_ERR(mclk) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (IS_ERR(mclk)) {
+ dev_dbg(&pdev->dev, "mclk not found.\n");
+ mclk = NULL;
+ }
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ drvdata->mclk = mclk;
+
+ ret = of_property_read_u32(np, "ti,codec-clock-rate", &drvdata->sysclk);
+
+ if (ret < 0) {
+ if (!drvdata->mclk) {
+ dev_err(&pdev->dev,
+ "No clock or clock rate defined.\n");
+ return -EINVAL;
+ }
+ drvdata->sysclk = clk_get_rate(drvdata->mclk);
+ } else if (drvdata->mclk) {
+ unsigned int requestd_rate = drvdata->sysclk;
+ clk_set_rate(drvdata->mclk, drvdata->sysclk);
+ drvdata->sysclk = clk_get_rate(drvdata->mclk);
+ if (drvdata->sysclk != requestd_rate)
+ dev_warn(&pdev->dev,
+ "Could not get requested rate %u using %u.\n",
+ requestd_rate, drvdata->sysclk);
+ }
+
+ snd_soc_card_set_drvdata(&evm_soc_card, drvdata);
+ ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card);
+
+ if (ret)
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+
+ return ret;
+}
+
+static int davinci_evm_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static struct platform_driver davinci_evm_driver = {
+ .probe = davinci_evm_probe,
+ .remove = davinci_evm_remove,
+ .driver = {
+ .name = "davinci_evm",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = of_match_ptr(davinci_evm_dt_ids),
+ },
};
+#endif
static struct platform_device *evm_snd_device;
static int __init evm_init(void)
{
- struct resource *resources;
- unsigned num_resources;
- struct evm_snd_platform_data *data;
+ struct snd_soc_card *evm_snd_dev_data;
int index;
int ret;
- if (machine_is_davinci_evm()) {
- davinci_cfg_reg(DM644X_MCBSP);
+ /*
+ * If dtb is there, the devices will be created dynamically.
+ * Only register platfrom driver structure.
+ */
+#if defined(CONFIG_OF)
+ if (of_have_populated_dt())
+ return platform_driver_register(&davinci_evm_driver);
+#endif
- resources = evm_snd_resources;
- num_resources = ARRAY_SIZE(evm_snd_resources);
- data = &evm_snd_data;
+ if (machine_is_davinci_evm()) {
+ evm_snd_dev_data = &dm6446_snd_soc_card_evm;
index = 0;
} else if (machine_is_davinci_dm355_evm()) {
- /* we don't use ASP1 IRQs, or we'd need to mux them ... */
- davinci_cfg_reg(DM355_EVT8_ASP1_TX);
- davinci_cfg_reg(DM355_EVT9_ASP1_RX);
-
- resources = dm335evm_snd_resources;
- num_resources = ARRAY_SIZE(dm335evm_snd_resources);
- data = &dm335evm_snd_data;
+ evm_snd_dev_data = &dm355_snd_soc_card_evm;
+ index = 1;
+ } else if (machine_is_davinci_dm365_evm()) {
+ evm_snd_dev_data = &dm365_snd_soc_card_evm;
+ index = 0;
+ } else if (machine_is_davinci_dm6467_evm()) {
+ evm_snd_dev_data = &dm6467_snd_soc_card_evm;
+ index = 0;
+ } else if (machine_is_davinci_da830_evm()) {
+ evm_snd_dev_data = &da830_snd_soc_card;
index = 1;
+ } else if (machine_is_davinci_da850_evm()) {
+ evm_snd_dev_data = &da850_snd_soc_card;
+ index = 0;
} else
return -EINVAL;
@@ -226,17 +494,7 @@ static int __init evm_init(void)
if (!evm_snd_device)
return -ENOMEM;
- platform_set_drvdata(evm_snd_device, &evm_snd_devdata);
- evm_snd_devdata.dev = &evm_snd_device->dev;
- platform_device_add_data(evm_snd_device, data, sizeof(*data));
-
- ret = platform_device_add_resources(evm_snd_device, resources,
- num_resources);
- if (ret) {
- platform_device_put(evm_snd_device);
- return ret;
- }
-
+ platform_set_drvdata(evm_snd_device, evm_snd_dev_data);
ret = platform_device_add(evm_snd_device);
if (ret)
platform_device_put(evm_snd_device);
@@ -246,6 +504,13 @@ static int __init evm_init(void)
static void __exit evm_exit(void)
{
+#if defined(CONFIG_OF)
+ if (of_have_populated_dt()) {
+ platform_driver_unregister(&davinci_evm_driver);
+ return;
+ }
+#endif
+
platform_device_unregister(evm_snd_device);
}
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index b1ea52fc83c..7682af31d6e 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -12,9 +12,11 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
+#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
+#include <linux/platform_data/davinci_asp.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -23,6 +25,7 @@
#include <sound/soc.h>
#include "davinci-pcm.h"
+#include "davinci-i2s.h"
/*
@@ -63,17 +66,23 @@
#define DAVINCI_MCBSP_RCR_RWDLEN1(v) ((v) << 5)
#define DAVINCI_MCBSP_RCR_RFRLEN1(v) ((v) << 8)
#define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16)
+#define DAVINCI_MCBSP_RCR_RFIG (1 << 18)
#define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21)
+#define DAVINCI_MCBSP_RCR_RFRLEN2(v) ((v) << 24)
+#define DAVINCI_MCBSP_RCR_RPHASE BIT(31)
#define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5)
#define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8)
#define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16)
#define DAVINCI_MCBSP_XCR_XFIG (1 << 18)
#define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21)
+#define DAVINCI_MCBSP_XCR_XFRLEN2(v) ((v) << 24)
+#define DAVINCI_MCBSP_XCR_XPHASE BIT(31)
#define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8)
#define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16)
#define DAVINCI_MCBSP_SRGR_FSGM (1 << 28)
+#define DAVINCI_MCBSP_SRGR_CLKSM BIT(29)
#define DAVINCI_MCBSP_PCR_CLKRP (1 << 0)
#define DAVINCI_MCBSP_PCR_CLKXP (1 << 1)
@@ -85,14 +94,6 @@
#define DAVINCI_MCBSP_PCR_FSRM (1 << 10)
#define DAVINCI_MCBSP_PCR_FSXM (1 << 11)
-#define MOD_REG_BIT(val, mask, set) do { \
- if (set) { \
- val |= mask; \
- } else { \
- val &= ~mask; \
- } \
-} while (0)
-
enum {
DAVINCI_MCBSP_WORD_8 = 0,
DAVINCI_MCBSP_WORD_12,
@@ -102,18 +103,58 @@ enum {
DAVINCI_MCBSP_WORD_32,
};
-static struct davinci_pcm_dma_params davinci_i2s_pcm_out = {
- .name = "I2S PCM Stereo out",
+static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+ [SNDRV_PCM_FORMAT_S8] = 1,
+ [SNDRV_PCM_FORMAT_S16_LE] = 2,
+ [SNDRV_PCM_FORMAT_S32_LE] = 4,
+};
+
+static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+ [SNDRV_PCM_FORMAT_S8] = DAVINCI_MCBSP_WORD_8,
+ [SNDRV_PCM_FORMAT_S16_LE] = DAVINCI_MCBSP_WORD_16,
+ [SNDRV_PCM_FORMAT_S32_LE] = DAVINCI_MCBSP_WORD_32,
};
-static struct davinci_pcm_dma_params davinci_i2s_pcm_in = {
- .name = "I2S PCM Stereo in",
+static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+ [SNDRV_PCM_FORMAT_S8] = SNDRV_PCM_FORMAT_S16_LE,
+ [SNDRV_PCM_FORMAT_S16_LE] = SNDRV_PCM_FORMAT_S32_LE,
};
struct davinci_mcbsp_dev {
+ struct device *dev;
+ struct davinci_pcm_dma_params dma_params[2];
void __iomem *base;
+#define MOD_DSP_A 0
+#define MOD_DSP_B 1
+ int mode;
+ u32 pcr;
struct clk *clk;
- struct davinci_pcm_dma_params *dma_params[2];
+ /*
+ * Combining both channels into 1 element will at least double the
+ * amount of time between servicing the dma channel, increase
+ * effiency, and reduce the chance of overrun/underrun. But,
+ * it will result in the left & right channels being swapped.
+ *
+ * If relabeling the left and right channels is not possible,
+ * you may want to let the codec know to swap them back.
+ *
+ * It may allow x10 the amount of time to service dma requests,
+ * if the codec is master and is using an unnecessarily fast bit clock
+ * (ie. tlvaic23b), independent of the sample rate. So, having an
+ * entire frame at once means it can be serviced at the sample rate
+ * instead of the bit clock rate.
+ *
+ * In the now unlikely case that an underrun still
+ * occurs, both the left and right samples will be repeated
+ * so that no pops are heard, and the left and right channels
+ * won't end up being swapped because of the underrun.
+ */
+ unsigned enable_channel_combine:1;
+
+ unsigned int fmt;
+ int clk_div;
+ int clk_input_pin;
+ bool i2s_accurate_sck;
};
static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
@@ -127,97 +168,92 @@ static inline u32 davinci_mcbsp_read_reg(struct davinci_mcbsp_dev *dev, int reg)
return __raw_readl(dev->base + reg);
}
-static void davinci_mcbsp_start(struct snd_pcm_substream *substream)
+static void toggle_clock(struct davinci_mcbsp_dev *dev, int playback)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
- struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_platform *platform = socdev->card->platform;
- u32 w;
- int ret;
+ u32 m = playback ? DAVINCI_MCBSP_PCR_CLKXP : DAVINCI_MCBSP_PCR_CLKRP;
+ /* The clock needs to toggle to complete reset.
+ * So, fake it by toggling the clk polarity.
+ */
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr ^ m);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr);
+}
- /* Start the sample generator and enable transmitter/receiver */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+static void davinci_mcbsp_start(struct davinci_mcbsp_dev *dev,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ u32 spcr;
+ u32 mask = playback ? DAVINCI_MCBSP_SPCR_XRST : DAVINCI_MCBSP_SPCR_RRST;
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ if (spcr & mask) {
+ /* start off disabled */
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG,
+ spcr & ~mask);
+ toggle_clock(dev, playback);
+ }
+ if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM |
+ DAVINCI_MCBSP_PCR_CLKXM | DAVINCI_MCBSP_PCR_CLKRM)) {
+ /* Start the sample generator */
+ spcr |= DAVINCI_MCBSP_SPCR_GRST;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+ }
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (playback) {
/* Stop the DMA to avoid data loss */
/* while the transmitter is out of reset to handle XSYNCERR */
- if (platform->pcm_ops->trigger) {
- ret = platform->pcm_ops->trigger(substream,
+ if (platform->driver->ops->trigger) {
+ int ret = platform->driver->ops->trigger(substream,
SNDRV_PCM_TRIGGER_STOP);
if (ret < 0)
printk(KERN_DEBUG "Playback DMA stop failed\n");
}
/* Enable the transmitter */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr |= DAVINCI_MCBSP_SPCR_XRST;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
/* wait for any unexpected frame sync error to occur */
udelay(100);
/* Disable the transmitter to clear any outstanding XSYNCERR */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr &= ~DAVINCI_MCBSP_SPCR_XRST;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+ toggle_clock(dev, playback);
/* Restart the DMA */
- if (platform->pcm_ops->trigger) {
- ret = platform->pcm_ops->trigger(substream,
+ if (platform->driver->ops->trigger) {
+ int ret = platform->driver->ops->trigger(substream,
SNDRV_PCM_TRIGGER_START);
if (ret < 0)
printk(KERN_DEBUG "Playback DMA start failed\n");
}
- /* Enable the transmitter */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
-
- } else {
-
- /* Enable the reciever */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
}
+ /* Enable transmitter or receiver */
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr |= mask;
- /* Start frame sync */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_FRST, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM)) {
+ /* Start frame sync */
+ spcr |= DAVINCI_MCBSP_SPCR_FRST;
+ }
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
}
-static void davinci_mcbsp_stop(struct snd_pcm_substream *substream)
+static void davinci_mcbsp_stop(struct davinci_mcbsp_dev *dev, int playback)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
- u32 w;
+ u32 spcr;
/* Reset transmitter/receiver and sample rate/frame sync generators */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST |
- DAVINCI_MCBSP_SPCR_FRST, 0);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
- else
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 0);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
-}
-
-static int davinci_i2s_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
-
- cpu_dai->dma_data = dev->dma_params[substream->stream];
-
- return 0;
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr &= ~(DAVINCI_MCBSP_SPCR_GRST | DAVINCI_MCBSP_SPCR_FRST);
+ spcr &= playback ? ~DAVINCI_MCBSP_SPCR_XRST : ~DAVINCI_MCBSP_SPCR_RRST;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+ toggle_clock(dev, playback);
}
#define DEFAULT_BITPERSAMPLE 16
@@ -225,15 +261,17 @@ static int davinci_i2s_startup(struct snd_pcm_substream *substream,
static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
- struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
unsigned int pcr;
unsigned int srgr;
- unsigned int rcr;
- unsigned int xcr;
+ bool inv_fs = false;
+ /* Attention srgr is updated by hw_params! */
srgr = DAVINCI_MCBSP_SRGR_FSGM |
DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
+ dev->fmt = fmt;
+ /* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
/* cpu is master */
@@ -243,11 +281,26 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
DAVINCI_MCBSP_PCR_CLKRM;
break;
case SND_SOC_DAIFMT_CBM_CFS:
- /* McBSP CLKR pin is the input for the Sample Rate Generator.
- * McBSP FSR and FSX are driven by the Sample Rate Generator. */
- pcr = DAVINCI_MCBSP_PCR_SCLKME |
- DAVINCI_MCBSP_PCR_FSXM |
- DAVINCI_MCBSP_PCR_FSRM;
+ pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM;
+ /*
+ * Selection of the clock input pin that is the
+ * input for the Sample Rate Generator.
+ * McBSP FSR and FSX are driven by the Sample Rate
+ * Generator.
+ */
+ switch (dev->clk_input_pin) {
+ case MCBSP_CLKS:
+ pcr |= DAVINCI_MCBSP_PCR_CLKXM |
+ DAVINCI_MCBSP_PCR_CLKRM;
+ break;
+ case MCBSP_CLKR:
+ pcr |= DAVINCI_MCBSP_PCR_SCLKME;
+ break;
+ default:
+ dev_err(dev->dev, "bad clk_input_pin\n");
+ return -EINVAL;
+ }
+
break;
case SND_SOC_DAIFMT_CBM_CFM:
/* codec is master */
@@ -258,11 +311,8 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return -EINVAL;
}
- rcr = DAVINCI_MCBSP_RCR_RFRLEN1(1);
- xcr = DAVINCI_MCBSP_XCR_XFIG | DAVINCI_MCBSP_XCR_XFRLEN1(1);
+ /* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_DSP_B:
- break;
case SND_SOC_DAIFMT_I2S:
/* Davinci doesn't support TRUE I2S, but some codecs will have
* the left and right channels contiguous. This allows
@@ -280,10 +330,12 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
* more empty bit clock slots between channels as the sample
* rate is lowered.
*/
- fmt ^= SND_SOC_DAIFMT_NB_IF;
+ inv_fs = true;
case SND_SOC_DAIFMT_DSP_A:
- rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
- xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
+ dev->mode = MOD_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ dev->mode = MOD_DSP_B;
break;
default:
printk(KERN_ERR "%s:bad format\n", __func__);
@@ -342,10 +394,23 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
default:
return -EINVAL;
}
+ if (inv_fs == true)
+ pcr ^= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
+ dev->pcr = pcr;
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
+ return 0;
+}
+
+static int davinci_i2s_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if (div_id != DAVINCI_MCBSP_CLKGDV)
+ return -ENODEV;
+
+ dev->clk_div = div;
return 0;
}
@@ -353,98 +418,244 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct davinci_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
- struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct davinci_pcm_dma_params *dma_params =
+ &dev->dma_params[substream->stream];
struct snd_interval *i = NULL;
- int mcbsp_word_length;
- u32 w;
+ int mcbsp_word_length, master;
+ unsigned int rcr, xcr, srgr, clk_div, freq, framesize;
+ u32 spcr;
+ snd_pcm_format_t fmt;
+ unsigned element_cnt = 1;
/* general line settings */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- w |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ spcr |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
} else {
- w |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ spcr |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
}
- i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
- w = DAVINCI_MCBSP_SRGR_FSGM;
- MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1), 1);
+ master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
+ fmt = params_format(params);
+ mcbsp_word_length = asp_word_length[fmt];
- i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1), 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
-
- /* Determine xfer data type */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S8:
- dma_params->data_type = 1;
- mcbsp_word_length = DAVINCI_MCBSP_WORD_8;
+ switch (master) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ freq = clk_get_rate(dev->clk);
+ srgr = DAVINCI_MCBSP_SRGR_FSGM |
+ DAVINCI_MCBSP_SRGR_CLKSM;
+ srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length *
+ 8 - 1);
+ if (dev->i2s_accurate_sck) {
+ clk_div = 256;
+ do {
+ framesize = (freq / (--clk_div)) /
+ params->rate_num *
+ params->rate_den;
+ } while (((framesize < 33) || (framesize > 4095)) &&
+ (clk_div));
+ clk_div--;
+ srgr |= DAVINCI_MCBSP_SRGR_FPER(framesize - 1);
+ } else {
+ /* symmetric waveforms */
+ clk_div = freq / (mcbsp_word_length * 16) /
+ params->rate_num * params->rate_den;
+ srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length *
+ 16 - 1);
+ }
+ clk_div &= 0xFF;
+ srgr |= clk_div;
break;
- case SNDRV_PCM_FORMAT_S16_LE:
- dma_params->data_type = 2;
- mcbsp_word_length = DAVINCI_MCBSP_WORD_16;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ srgr = DAVINCI_MCBSP_SRGR_FSGM;
+ clk_div = dev->clk_div - 1;
+ srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * 8 - 1);
+ srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * 16 - 1);
+ clk_div &= 0xFF;
+ srgr |= clk_div;
break;
- case SNDRV_PCM_FORMAT_S32_LE:
- dma_params->data_type = 4;
- mcbsp_word_length = DAVINCI_MCBSP_WORD_32;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Clock and frame sync given from external sources */
+ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+ srgr = DAVINCI_MCBSP_SRGR_FSGM;
+ srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
+ pr_debug("%s - %d FWID set: re-read srgr = %X\n",
+ __func__, __LINE__, snd_interval_value(i) - 1);
+
+ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
+ srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
break;
default:
- printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
return -EINVAL;
}
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
- DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
-
+ rcr = DAVINCI_MCBSP_RCR_RFIG;
+ xcr = DAVINCI_MCBSP_XCR_XFIG;
+ if (dev->mode == MOD_DSP_B) {
+ rcr |= DAVINCI_MCBSP_RCR_RDATDLY(0);
+ xcr |= DAVINCI_MCBSP_XCR_XDATDLY(0);
} else {
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
- DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
+ rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
+ xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
+ }
+ /* Determine xfer data type */
+ fmt = params_format(params);
+ if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) {
+ printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
+ return -EINVAL;
+ }
+ if (params_channels(params) == 2) {
+ element_cnt = 2;
+ if (double_fmt[fmt] && dev->enable_channel_combine) {
+ element_cnt = 1;
+ fmt = double_fmt[fmt];
+ }
+ switch (master) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+ rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(0);
+ xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(0);
+ rcr |= DAVINCI_MCBSP_RCR_RPHASE;
+ xcr |= DAVINCI_MCBSP_XCR_XPHASE;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(element_cnt - 1);
+ xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(element_cnt - 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ dma_params->acnt = dma_params->data_type = data_type[fmt];
+ dma_params->fifo_level = 0;
+ mcbsp_word_length = asp_word_length[fmt];
+
+ switch (master) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+ rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0);
+ xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
+ xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
+ break;
+ default:
+ return -EINVAL;
}
+
+ rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
+ DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
+ xcr |= DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
+ DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
+ else
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
+
+ pr_debug("%s - %d srgr=%X\n", __func__, __LINE__, srgr);
+ pr_debug("%s - %d xcr=%X\n", __func__, __LINE__, xcr);
+ pr_debug("%s - %d rcr=%X\n", __func__, __LINE__, rcr);
+ return 0;
+}
+
+static int davinci_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
+ int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ davinci_mcbsp_stop(dev, playback);
return 0;
}
static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
int ret = 0;
+ int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- davinci_mcbsp_start(substream);
+ davinci_mcbsp_start(dev, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- davinci_mcbsp_stop(substream);
+ davinci_mcbsp_stop(dev, playback);
break;
default:
ret = -EINVAL;
}
-
return ret;
}
-static int davinci_i2s_probe(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static int davinci_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_card *card = socdev->card;
- struct snd_soc_dai *cpu_dai = card->dai_link->cpu_dai;
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_set_dma_data(dai, substream, dev->dma_params);
+ return 0;
+}
+
+static void davinci_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
+ int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ davinci_mcbsp_stop(dev, playback);
+}
+
+#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
+
+static const struct snd_soc_dai_ops davinci_i2s_dai_ops = {
+ .startup = davinci_i2s_startup,
+ .shutdown = davinci_i2s_shutdown,
+ .prepare = davinci_i2s_prepare,
+ .trigger = davinci_i2s_trigger,
+ .hw_params = davinci_i2s_hw_params,
+ .set_fmt = davinci_i2s_set_dai_fmt,
+ .set_clkdiv = davinci_i2s_dai_set_clkdiv,
+
+};
+
+static struct snd_soc_dai_driver davinci_i2s_dai = {
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = DAVINCI_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = DAVINCI_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &davinci_i2s_dai_ops,
+
+};
+
+static const struct snd_soc_component_driver davinci_i2s_component = {
+ .name = "davinci-i2s",
+};
+
+static int davinci_i2s_probe(struct platform_device *pdev)
+{
+ struct snd_platform_data *pdata = pdev->dev.platform_data;
struct davinci_mcbsp_dev *dev;
- struct resource *mem, *ioarea;
- struct evm_snd_platform_data *pdata;
+ struct resource *mem, *ioarea, *res;
+ enum dma_event_q asp_chan_q = EVENTQ_0;
+ enum dma_event_q ram_chan_q = EVENTQ_1;
int ret;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -453,109 +664,117 @@ static int davinci_i2s_probe(struct platform_device *pdev,
return -ENODEV;
}
- ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
- pdev->name);
+ ioarea = devm_request_mem_region(&pdev->dev, mem->start,
+ resource_size(mem),
+ pdev->name);
if (!ioarea) {
dev_err(&pdev->dev, "McBSP region already claimed\n");
return -EBUSY;
}
- dev = kzalloc(sizeof(struct davinci_mcbsp_dev), GFP_KERNEL);
- if (!dev) {
- ret = -ENOMEM;
- goto err_release_region;
+ dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcbsp_dev),
+ GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ if (pdata) {
+ dev->enable_channel_combine = pdata->enable_channel_combine;
+ dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].sram_size =
+ pdata->sram_size_playback;
+ dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size =
+ pdata->sram_size_capture;
+ dev->clk_input_pin = pdata->clk_input_pin;
+ dev->i2s_accurate_sck = pdata->i2s_accurate_sck;
+ asp_chan_q = pdata->asp_chan_q;
+ ram_chan_q = pdata->ram_chan_q;
}
- cpu_dai->private_data = dev;
+ dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].asp_chan_q = asp_chan_q;
+ dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].ram_chan_q = ram_chan_q;
+ dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].asp_chan_q = asp_chan_q;
+ dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].ram_chan_q = ram_chan_q;
dev->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(dev->clk)) {
- ret = -ENODEV;
- goto err_free_mem;
- }
+ if (IS_ERR(dev->clk))
+ return -ENODEV;
clk_enable(dev->clk);
- dev->base = (void __iomem *)IO_ADDRESS(mem->start);
- pdata = pdev->dev.platform_data;
+ dev->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!dev->base) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto err_release_clk;
+ }
- dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &davinci_i2s_pcm_out;
- dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->channel = pdata->tx_dma_ch;
- dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->dma_addr =
- (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG);
+ dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr =
+ (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
- dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &davinci_i2s_pcm_in;
- dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->channel = pdata->rx_dma_ch;
- dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->dma_addr =
- (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG);
+ dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr =
+ (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG);
- return 0;
+ /* first TX, then RX */
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no DMA resource\n");
+ ret = -ENXIO;
+ goto err_release_clk;
+ }
+ dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "no DMA resource\n");
+ ret = -ENXIO;
+ goto err_release_clk;
+ }
+ dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start;
+ dev->dev = &pdev->dev;
+
+ dev_set_drvdata(&pdev->dev, dev);
-err_free_mem:
- kfree(dev);
-err_release_region:
- release_mem_region(mem->start, (mem->end - mem->start) + 1);
+ ret = snd_soc_register_component(&pdev->dev, &davinci_i2s_component,
+ &davinci_i2s_dai, 1);
+ if (ret != 0)
+ goto err_release_clk;
+ ret = davinci_soc_platform_register(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+ goto err_unregister_component;
+ }
+
+ return 0;
+
+err_unregister_component:
+ snd_soc_unregister_component(&pdev->dev);
+err_release_clk:
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
return ret;
}
-static void davinci_i2s_remove(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static int davinci_i2s_remove(struct platform_device *pdev)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_card *card = socdev->card;
- struct snd_soc_dai *cpu_dai = card->dai_link->cpu_dai;
- struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
- struct resource *mem;
+ struct davinci_mcbsp_dev *dev = dev_get_drvdata(&pdev->dev);
+
+ snd_soc_unregister_component(&pdev->dev);
clk_disable(dev->clk);
clk_put(dev->clk);
dev->clk = NULL;
- kfree(dev);
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(mem->start, (mem->end - mem->start) + 1);
+ return 0;
}
-#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
-
-static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
- .startup = davinci_i2s_startup,
- .trigger = davinci_i2s_trigger,
- .hw_params = davinci_i2s_hw_params,
- .set_fmt = davinci_i2s_set_dai_fmt,
-};
-
-struct snd_soc_dai davinci_i2s_dai = {
- .name = "davinci-i2s",
- .id = 0,
- .probe = davinci_i2s_probe,
- .remove = davinci_i2s_remove,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = DAVINCI_I2S_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = DAVINCI_I2S_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = &davinci_i2s_dai_ops,
+static struct platform_driver davinci_mcbsp_driver = {
+ .probe = davinci_i2s_probe,
+ .remove = davinci_i2s_remove,
+ .driver = {
+ .name = "davinci-mcbsp",
+ .owner = THIS_MODULE,
+ },
};
-EXPORT_SYMBOL_GPL(davinci_i2s_dai);
-static int __init davinci_i2s_init(void)
-{
- return snd_soc_register_dai(&davinci_i2s_dai);
-}
-module_init(davinci_i2s_init);
-
-static void __exit davinci_i2s_exit(void)
-{
- snd_soc_unregister_dai(&davinci_i2s_dai);
-}
-module_exit(davinci_i2s_exit);
+module_platform_driver(davinci_mcbsp_driver);
MODULE_AUTHOR("Vladimir Barinov");
MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface");
diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h
index 241648ce887..48dac3e2521 100644
--- a/sound/soc/davinci/davinci-i2s.h
+++ b/sound/soc/davinci/davinci-i2s.h
@@ -12,6 +12,9 @@
#ifndef _DAVINCI_I2S_H
#define _DAVINCI_I2S_H
-extern struct snd_soc_dai davinci_i2s_dai;
+/* McBSP dividers */
+enum davinci_mcbsp_div {
+ DAVINCI_MCBSP_CLKGDV, /* Sample rate generator divider */
+};
#endif
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
new file mode 100644
index 00000000000..9afb14629a1
--- /dev/null
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -0,0 +1,1276 @@
+/*
+ * ALSA SoC McASP Audio Layer for TI DAVINCI processor
+ *
+ * Multi-channel Audio Serial Port Driver
+ *
+ * Author: Nirmal Pandey <n-pandey@ti.com>,
+ * Suresh Rajashekara <suresh.r@ti.com>
+ * Steve Chen <schen@.mvista.com>
+ *
+ * Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright: (C) 2009 Texas Instruments, India
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/omap-pcm.h>
+
+#include "davinci-pcm.h"
+#include "davinci-mcasp.h"
+
+#define MCASP_MAX_AFIFO_DEPTH 64
+
+struct davinci_mcasp_context {
+ u32 txfmtctl;
+ u32 rxfmtctl;
+ u32 txfmt;
+ u32 rxfmt;
+ u32 aclkxctl;
+ u32 aclkrctl;
+ u32 pdir;
+};
+
+struct davinci_mcasp {
+ struct davinci_pcm_dma_params dma_params[2];
+ struct snd_dmaengine_dai_dma_data dma_data[2];
+ void __iomem *base;
+ u32 fifo_base;
+ struct device *dev;
+
+ /* McASP specific data */
+ int tdm_slots;
+ u8 op_mode;
+ u8 num_serializer;
+ u8 *serial_dir;
+ u8 version;
+ u16 bclk_lrclk_ratio;
+ int streams;
+
+ int sysclk_freq;
+ bool bclk_master;
+
+ /* McASP FIFO related */
+ u8 txnumevt;
+ u8 rxnumevt;
+
+ bool dat_port;
+
+#ifdef CONFIG_PM_SLEEP
+ struct davinci_mcasp_context context;
+#endif
+};
+
+static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset,
+ u32 val)
+{
+ void __iomem *reg = mcasp->base + offset;
+ __raw_writel(__raw_readl(reg) | val, reg);
+}
+
+static inline void mcasp_clr_bits(struct davinci_mcasp *mcasp, u32 offset,
+ u32 val)
+{
+ void __iomem *reg = mcasp->base + offset;
+ __raw_writel((__raw_readl(reg) & ~(val)), reg);
+}
+
+static inline void mcasp_mod_bits(struct davinci_mcasp *mcasp, u32 offset,
+ u32 val, u32 mask)
+{
+ void __iomem *reg = mcasp->base + offset;
+ __raw_writel((__raw_readl(reg) & ~mask) | val, reg);
+}
+
+static inline void mcasp_set_reg(struct davinci_mcasp *mcasp, u32 offset,
+ u32 val)
+{
+ __raw_writel(val, mcasp->base + offset);
+}
+
+static inline u32 mcasp_get_reg(struct davinci_mcasp *mcasp, u32 offset)
+{
+ return (u32)__raw_readl(mcasp->base + offset);
+}
+
+static void mcasp_set_ctl_reg(struct davinci_mcasp *mcasp, u32 ctl_reg, u32 val)
+{
+ int i = 0;
+
+ mcasp_set_bits(mcasp, ctl_reg, val);
+
+ /* programming GBLCTL needs to read back from GBLCTL and verfiy */
+ /* loop count is to avoid the lock-up */
+ for (i = 0; i < 1000; i++) {
+ if ((mcasp_get_reg(mcasp, ctl_reg) & val) == val)
+ break;
+ }
+
+ if (i == 1000 && ((mcasp_get_reg(mcasp, ctl_reg) & val) != val))
+ printk(KERN_ERR "GBLCTL write error\n");
+}
+
+static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp)
+{
+ u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
+ u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
+
+ return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE;
+}
+
+static void mcasp_start_rx(struct davinci_mcasp *mcasp)
+{
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
+
+ /*
+ * When ASYNC == 0 the transmit and receive sections operate
+ * synchronously from the transmit clock and frame sync. We need to make
+ * sure that the TX signlas are enabled when starting reception.
+ */
+ if (mcasp_is_synchronous(mcasp)) {
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+ }
+
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
+
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
+
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
+
+ if (mcasp_is_synchronous(mcasp))
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+}
+
+static void mcasp_start_tx(struct davinci_mcasp *mcasp)
+{
+ u8 offset = 0, i;
+ u32 cnt;
+
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
+
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
+ for (i = 0; i < mcasp->num_serializer; i++) {
+ if (mcasp->serial_dir[i] == TX_MODE) {
+ offset = i;
+ break;
+ }
+ }
+
+ /* wait for TX ready */
+ cnt = 0;
+ while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(offset)) &
+ TXSTATE) && (cnt < 100000))
+ cnt++;
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
+}
+
+static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream)
+{
+ u32 reg;
+
+ mcasp->streams++;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (mcasp->txnumevt) { /* enable FIFO */
+ reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+ }
+ mcasp_start_tx(mcasp);
+ } else {
+ if (mcasp->rxnumevt) { /* enable FIFO */
+ reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+ }
+ mcasp_start_rx(mcasp);
+ }
+}
+
+static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
+{
+ /*
+ * In synchronous mode stop the TX clocks if no other stream is
+ * running
+ */
+ if (mcasp_is_synchronous(mcasp) && !mcasp->streams)
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0);
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+}
+
+static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
+{
+ u32 val = 0;
+
+ /*
+ * In synchronous mode keep TX clocks running if the capture stream is
+ * still running.
+ */
+ if (mcasp_is_synchronous(mcasp) && mcasp->streams)
+ val = TXHCLKRST | TXCLKRST | TXFSRST;
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+}
+
+static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream)
+{
+ u32 reg;
+
+ mcasp->streams--;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (mcasp->txnumevt) { /* disable FIFO */
+ reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ }
+ mcasp_stop_tx(mcasp);
+ } else {
+ if (mcasp->rxnumevt) { /* disable FIFO */
+ reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ }
+ mcasp_stop_rx(mcasp);
+ }
+}
+
+static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret = 0;
+ u32 data_delay;
+ bool fs_pol_rising;
+ bool inv_fs = false;
+
+ pm_runtime_get_sync(mcasp->dev);
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+ /* 1st data bit occur one ACLK cycle after the frame sync */
+ data_delay = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ case SND_SOC_DAIFMT_AC97:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+ /* No delay after FS */
+ data_delay = 0;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ /* configure a full-word SYNC pulse (LRCLK) */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+ /* 1st data bit occur one ACLK cycle after the frame sync */
+ data_delay = 1;
+ /* FS need to be inverted */
+ inv_fs = true;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ /* configure a full-word SYNC pulse (LRCLK) */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+ /* No delay after FS */
+ data_delay = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, FSXDLY(data_delay),
+ FSXDLY(3));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, FSRDLY(data_delay),
+ FSRDLY(3));
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* codec is clock and frame slave */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR);
+ mcasp->bclk_master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ /* codec is clock master and frame slave */
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR);
+ mcasp->bclk_master = 0;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* codec is clock and frame master */
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG,
+ ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR);
+ mcasp->bclk_master = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ fs_pol_rising = true;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ fs_pol_rising = false;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ fs_pol_rising = false;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ fs_pol_rising = true;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (inv_fs)
+ fs_pol_rising = !fs_pol_rising;
+
+ if (fs_pol_rising) {
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+ } else {
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+ }
+out:
+ pm_runtime_put_sync(mcasp->dev);
+ return ret;
+}
+
+static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+ switch (div_id) {
+ case 0: /* MCLK divider */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXDIV(div - 1), AHCLKXDIV_MASK);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
+ break;
+
+ case 1: /* BCLK divider */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
+ ACLKXDIV(div - 1), ACLKXDIV_MASK);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
+ ACLKRDIV(div - 1), ACLKRDIV_MASK);
+ break;
+
+ case 2: /* BCLK/LRCLK ratio */
+ mcasp->bclk_lrclk_ratio = div;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+ if (dir == SND_SOC_CLOCK_OUT) {
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AHCLKX);
+ } else {
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AHCLKX);
+ }
+
+ mcasp->sysclk_freq = freq;
+
+ return 0;
+}
+
+static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
+ int word_length)
+{
+ u32 fmt;
+ u32 tx_rotate = (word_length / 4) & 0x7;
+ u32 rx_rotate = (32 - word_length) / 4;
+ u32 mask = (1ULL << word_length) - 1;
+
+ /*
+ * if s BCLK-to-LRCLK ratio has been configured via the set_clkdiv()
+ * callback, take it into account here. That allows us to for example
+ * send 32 bits per channel to the codec, while only 16 of them carry
+ * audio payload.
+ * The clock ratio is given for a full period of data (for I2S format
+ * both left and right channels), so it has to be divided by number of
+ * tdm-slots (for I2S - divided by 2).
+ */
+ if (mcasp->bclk_lrclk_ratio)
+ word_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
+
+ /* mapping of the XSSZ bit-field as described in the datasheet */
+ fmt = (word_length >> 1) - 1;
+
+ if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
+ RXSSZ(0x0F));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt),
+ TXSSZ(0x0F));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate),
+ TXROT(7));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate),
+ RXROT(7));
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask);
+ }
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
+
+ return 0;
+}
+
+static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
+ int period_words, int channels)
+{
+ struct davinci_pcm_dma_params *dma_params = &mcasp->dma_params[stream];
+ struct snd_dmaengine_dai_dma_data *dma_data = &mcasp->dma_data[stream];
+ int i;
+ u8 tx_ser = 0;
+ u8 rx_ser = 0;
+ u8 slots = mcasp->tdm_slots;
+ u8 max_active_serializers = (channels + slots - 1) / slots;
+ int active_serializers, numevt, n;
+ u32 reg;
+ /* Default configuration */
+ if (mcasp->version < MCASP_VERSION_3)
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
+
+ /* All PINS as McASP */
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
+ } else {
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_REVTCTL_REG, RXDATADMADIS);
+ }
+
+ for (i = 0; i < mcasp->num_serializer; i++) {
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+ mcasp->serial_dir[i]);
+ if (mcasp->serial_dir[i] == TX_MODE &&
+ tx_ser < max_active_serializers) {
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i));
+ tx_ser++;
+ } else if (mcasp->serial_dir[i] == RX_MODE &&
+ rx_ser < max_active_serializers) {
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i));
+ rx_ser++;
+ } else {
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+ SRMOD_INACTIVE, SRMOD_MASK);
+ }
+ }
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ active_serializers = tx_ser;
+ numevt = mcasp->txnumevt;
+ reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+ } else {
+ active_serializers = rx_ser;
+ numevt = mcasp->rxnumevt;
+ reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+ }
+
+ if (active_serializers < max_active_serializers) {
+ dev_warn(mcasp->dev, "stream has more channels (%d) than are "
+ "enabled in mcasp (%d)\n", channels,
+ active_serializers * slots);
+ return -EINVAL;
+ }
+
+ /* AFIFO is not in use */
+ if (!numevt) {
+ /* Configure the burst size for platform drivers */
+ if (active_serializers > 1) {
+ /*
+ * If more than one serializers are in use we have one
+ * DMA request to provide data for all serializers.
+ * For example if three serializers are enabled the DMA
+ * need to transfer three words per DMA request.
+ */
+ dma_params->fifo_level = active_serializers;
+ dma_data->maxburst = active_serializers;
+ } else {
+ dma_params->fifo_level = 0;
+ dma_data->maxburst = 0;
+ }
+ return 0;
+ }
+
+ if (period_words % active_serializers) {
+ dev_err(mcasp->dev, "Invalid combination of period words and "
+ "active serializers: %d, %d\n", period_words,
+ active_serializers);
+ return -EINVAL;
+ }
+
+ /*
+ * Calculate the optimal AFIFO depth for platform side:
+ * The number of words for numevt need to be in steps of active
+ * serializers.
+ */
+ n = numevt % active_serializers;
+ if (n)
+ numevt += (active_serializers - n);
+ while (period_words % numevt && numevt > 0)
+ numevt -= active_serializers;
+ if (numevt <= 0)
+ numevt = active_serializers;
+
+ mcasp_mod_bits(mcasp, reg, active_serializers, NUMDMA_MASK);
+ mcasp_mod_bits(mcasp, reg, NUMEVT(numevt), NUMEVT_MASK);
+
+ /* Configure the burst size for platform drivers */
+ if (numevt == 1)
+ numevt = 0;
+ dma_params->fifo_level = numevt;
+ dma_data->maxburst = numevt;
+
+ return 0;
+}
+
+static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream)
+{
+ int i, active_slots;
+ u32 mask = 0;
+ u32 busel = 0;
+
+ if ((mcasp->tdm_slots < 2) || (mcasp->tdm_slots > 32)) {
+ dev_err(mcasp->dev, "tdm slot %d not supported\n",
+ mcasp->tdm_slots);
+ return -EINVAL;
+ }
+
+ active_slots = (mcasp->tdm_slots > 31) ? 32 : mcasp->tdm_slots;
+ for (i = 0; i < active_slots; i++)
+ mask |= (1 << i);
+
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
+
+ if (!mcasp->dat_port)
+ busel = TXSEL;
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
+ FSXMOD(mcasp->tdm_slots), FSXMOD(0x1FF));
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
+ FSRMOD(mcasp->tdm_slots), FSRMOD(0x1FF));
+
+ return 0;
+}
+
+/* S/PDIF */
+static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp)
+{
+ /* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0
+ and LSB first */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(6) | TXSSZ(15));
+
+ /* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE | FSXMOD(0x180));
+
+ /* Set the TX tdm : for all the slots */
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF);
+
+ /* Set the TX clock controls : div = 1 and internal */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE | TX_ASYNC);
+
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
+
+ /* Only 44100 and 48000 are valid, both have the same setting */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3));
+
+ /* Enable the DIT */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXDITCTL_REG, DITEN);
+
+ return 0;
+}
+
+static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+ struct davinci_pcm_dma_params *dma_params =
+ &mcasp->dma_params[substream->stream];
+ int word_length;
+ int channels = params_channels(params);
+ int period_size = params_period_size(params);
+ int ret;
+
+ /* If mcasp is BCLK master we need to set BCLK divider */
+ if (mcasp->bclk_master) {
+ unsigned int bclk_freq = snd_soc_params_to_bclk(params);
+ if (mcasp->sysclk_freq % bclk_freq != 0) {
+ dev_err(mcasp->dev, "Can't produce required BCLK\n");
+ return -EINVAL;
+ }
+ davinci_mcasp_set_clkdiv(
+ cpu_dai, 1, mcasp->sysclk_freq / bclk_freq);
+ }
+
+ ret = mcasp_common_hw_param(mcasp, substream->stream,
+ period_size * channels, channels);
+ if (ret)
+ return ret;
+
+ if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+ ret = mcasp_dit_hw_param(mcasp);
+ else
+ ret = mcasp_i2s_hw_param(mcasp, substream->stream);
+
+ if (ret)
+ return ret;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ dma_params->data_type = 1;
+ word_length = 8;
+ break;
+
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dma_params->data_type = 2;
+ word_length = 16;
+ break;
+
+ case SNDRV_PCM_FORMAT_U24_3LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ dma_params->data_type = 3;
+ word_length = 24;
+ break;
+
+ case SNDRV_PCM_FORMAT_U24_LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_U32_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dma_params->data_type = 4;
+ word_length = 32;
+ break;
+
+ default:
+ printk(KERN_WARNING "davinci-mcasp: unsupported PCM format");
+ return -EINVAL;
+ }
+
+ if (mcasp->version == MCASP_VERSION_2 && !dma_params->fifo_level)
+ dma_params->acnt = 4;
+ else
+ dma_params->acnt = dma_params->data_type;
+
+ davinci_config_channel_size(mcasp, word_length);
+
+ return 0;
+}
+
+static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *cpu_dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ davinci_mcasp_start(mcasp, substream->stream);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ davinci_mcasp_stop(mcasp, substream->stream);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
+ .trigger = davinci_mcasp_trigger,
+ .hw_params = davinci_mcasp_hw_params,
+ .set_fmt = davinci_mcasp_set_dai_fmt,
+ .set_clkdiv = davinci_mcasp_set_clkdiv,
+ .set_sysclk = davinci_mcasp_set_sysclk,
+};
+
+static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+ if (mcasp->version == MCASP_VERSION_4) {
+ /* Using dmaengine PCM */
+ dai->playback_dma_data =
+ &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+ dai->capture_dma_data =
+ &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+ } else {
+ /* Using davinci-pcm */
+ dai->playback_dma_data = mcasp->dma_params;
+ dai->capture_dma_data = mcasp->dma_params;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int davinci_mcasp_suspend(struct snd_soc_dai *dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+ struct davinci_mcasp_context *context = &mcasp->context;
+
+ context->txfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG);
+ context->rxfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
+ context->txfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMT_REG);
+ context->rxfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMT_REG);
+ context->aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
+ context->aclkrctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG);
+ context->pdir = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG);
+
+ return 0;
+}
+
+static int davinci_mcasp_resume(struct snd_soc_dai *dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+ struct davinci_mcasp_context *context = &mcasp->context;
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, context->txfmtctl);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG, context->rxfmtctl);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMT_REG, context->txfmt);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMT_REG, context->rxfmt);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, context->aclkxctl);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, context->aclkrctl);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_PDIR_REG, context->pdir);
+
+ return 0;
+}
+#else
+#define davinci_mcasp_suspend NULL
+#define davinci_mcasp_resume NULL
+#endif
+
+#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000
+
+#define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_U8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_U24_3LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
+ {
+ .name = "davinci-mcasp.0",
+ .probe = davinci_mcasp_dai_probe,
+ .suspend = davinci_mcasp_suspend,
+ .resume = davinci_mcasp_resume,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 32 * 16,
+ .rates = DAVINCI_MCASP_RATES,
+ .formats = DAVINCI_MCASP_PCM_FMTS,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 32 * 16,
+ .rates = DAVINCI_MCASP_RATES,
+ .formats = DAVINCI_MCASP_PCM_FMTS,
+ },
+ .ops = &davinci_mcasp_dai_ops,
+
+ },
+ {
+ .name = "davinci-mcasp.1",
+ .probe = davinci_mcasp_dai_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 384,
+ .rates = DAVINCI_MCASP_RATES,
+ .formats = DAVINCI_MCASP_PCM_FMTS,
+ },
+ .ops = &davinci_mcasp_dai_ops,
+ },
+
+};
+
+static const struct snd_soc_component_driver davinci_mcasp_component = {
+ .name = "davinci-mcasp",
+};
+
+/* Some HW specific values and defaults. The rest is filled in from DT. */
+static struct davinci_mcasp_pdata dm646x_mcasp_pdata = {
+ .tx_dma_offset = 0x400,
+ .rx_dma_offset = 0x400,
+ .asp_chan_q = EVENTQ_0,
+ .version = MCASP_VERSION_1,
+};
+
+static struct davinci_mcasp_pdata da830_mcasp_pdata = {
+ .tx_dma_offset = 0x2000,
+ .rx_dma_offset = 0x2000,
+ .asp_chan_q = EVENTQ_0,
+ .version = MCASP_VERSION_2,
+};
+
+static struct davinci_mcasp_pdata am33xx_mcasp_pdata = {
+ .tx_dma_offset = 0,
+ .rx_dma_offset = 0,
+ .asp_chan_q = EVENTQ_0,
+ .version = MCASP_VERSION_3,
+};
+
+static struct davinci_mcasp_pdata dra7_mcasp_pdata = {
+ .tx_dma_offset = 0x200,
+ .rx_dma_offset = 0x284,
+ .asp_chan_q = EVENTQ_0,
+ .version = MCASP_VERSION_4,
+};
+
+static const struct of_device_id mcasp_dt_ids[] = {
+ {
+ .compatible = "ti,dm646x-mcasp-audio",
+ .data = &dm646x_mcasp_pdata,
+ },
+ {
+ .compatible = "ti,da830-mcasp-audio",
+ .data = &da830_mcasp_pdata,
+ },
+ {
+ .compatible = "ti,am33xx-mcasp-audio",
+ .data = &am33xx_mcasp_pdata,
+ },
+ {
+ .compatible = "ti,dra7-mcasp-audio",
+ .data = &dra7_mcasp_pdata,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mcasp_dt_ids);
+
+static int mcasp_reparent_fck(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk *gfclk, *parent_clk;
+ const char *parent_name;
+ int ret;
+
+ if (!node)
+ return 0;
+
+ parent_name = of_get_property(node, "fck_parent", NULL);
+ if (!parent_name)
+ return 0;
+
+ gfclk = clk_get(&pdev->dev, "fck");
+ if (IS_ERR(gfclk)) {
+ dev_err(&pdev->dev, "failed to get fck\n");
+ return PTR_ERR(gfclk);
+ }
+
+ parent_clk = clk_get(NULL, parent_name);
+ if (IS_ERR(parent_clk)) {
+ dev_err(&pdev->dev, "failed to get parent clock\n");
+ ret = PTR_ERR(parent_clk);
+ goto err1;
+ }
+
+ ret = clk_set_parent(gfclk, parent_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to reparent fck\n");
+ goto err2;
+ }
+
+err2:
+ clk_put(parent_clk);
+err1:
+ clk_put(gfclk);
+ return ret;
+}
+
+static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of(
+ struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct davinci_mcasp_pdata *pdata = NULL;
+ const struct of_device_id *match =
+ of_match_device(mcasp_dt_ids, &pdev->dev);
+ struct of_phandle_args dma_spec;
+
+ const u32 *of_serial_dir32;
+ u32 val;
+ int i, ret = 0;
+
+ if (pdev->dev.platform_data) {
+ pdata = pdev->dev.platform_data;
+ return pdata;
+ } else if (match) {
+ pdata = (struct davinci_mcasp_pdata*) match->data;
+ } else {
+ /* control shouldn't reach here. something is wrong */
+ ret = -EINVAL;
+ goto nodata;
+ }
+
+ ret = of_property_read_u32(np, "op-mode", &val);
+ if (ret >= 0)
+ pdata->op_mode = val;
+
+ ret = of_property_read_u32(np, "tdm-slots", &val);
+ if (ret >= 0) {
+ if (val < 2 || val > 32) {
+ dev_err(&pdev->dev,
+ "tdm-slots must be in rage [2-32]\n");
+ ret = -EINVAL;
+ goto nodata;
+ }
+
+ pdata->tdm_slots = val;
+ }
+
+ of_serial_dir32 = of_get_property(np, "serial-dir", &val);
+ val /= sizeof(u32);
+ if (of_serial_dir32) {
+ u8 *of_serial_dir = devm_kzalloc(&pdev->dev,
+ (sizeof(*of_serial_dir) * val),
+ GFP_KERNEL);
+ if (!of_serial_dir) {
+ ret = -ENOMEM;
+ goto nodata;
+ }
+
+ for (i = 0; i < val; i++)
+ of_serial_dir[i] = be32_to_cpup(&of_serial_dir32[i]);
+
+ pdata->num_serializer = val;
+ pdata->serial_dir = of_serial_dir;
+ }
+
+ ret = of_property_match_string(np, "dma-names", "tx");
+ if (ret < 0)
+ goto nodata;
+
+ ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret,
+ &dma_spec);
+ if (ret < 0)
+ goto nodata;
+
+ pdata->tx_dma_channel = dma_spec.args[0];
+
+ ret = of_property_match_string(np, "dma-names", "rx");
+ if (ret < 0)
+ goto nodata;
+
+ ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret,
+ &dma_spec);
+ if (ret < 0)
+ goto nodata;
+
+ pdata->rx_dma_channel = dma_spec.args[0];
+
+ ret = of_property_read_u32(np, "tx-num-evt", &val);
+ if (ret >= 0)
+ pdata->txnumevt = val;
+
+ ret = of_property_read_u32(np, "rx-num-evt", &val);
+ if (ret >= 0)
+ pdata->rxnumevt = val;
+
+ ret = of_property_read_u32(np, "sram-size-playback", &val);
+ if (ret >= 0)
+ pdata->sram_size_playback = val;
+
+ ret = of_property_read_u32(np, "sram-size-capture", &val);
+ if (ret >= 0)
+ pdata->sram_size_capture = val;
+
+ return pdata;
+
+nodata:
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error populating platform data, err %d\n",
+ ret);
+ pdata = NULL;
+ }
+ return pdata;
+}
+
+static int davinci_mcasp_probe(struct platform_device *pdev)
+{
+ struct davinci_pcm_dma_params *dma_params;
+ struct snd_dmaengine_dai_dma_data *dma_data;
+ struct resource *mem, *ioarea, *res, *dat;
+ struct davinci_mcasp_pdata *pdata;
+ struct davinci_mcasp *mcasp;
+ int ret;
+
+ if (!pdev->dev.platform_data && !pdev->dev.of_node) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+
+ mcasp = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcasp),
+ GFP_KERNEL);
+ if (!mcasp)
+ return -ENOMEM;
+
+ pdata = davinci_mcasp_set_pdata_from_of(pdev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
+ if (!mem) {
+ dev_warn(mcasp->dev,
+ "\"mpu\" mem resource not found, using index 0\n");
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ return -ENODEV;
+ }
+ }
+
+ ioarea = devm_request_mem_region(&pdev->dev, mem->start,
+ resource_size(mem), pdev->name);
+ if (!ioarea) {
+ dev_err(&pdev->dev, "Audio region already claimed\n");
+ return -EBUSY;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
+ return ret;
+ }
+
+ mcasp->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!mcasp->base) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ mcasp->op_mode = pdata->op_mode;
+ mcasp->tdm_slots = pdata->tdm_slots;
+ mcasp->num_serializer = pdata->num_serializer;
+ mcasp->serial_dir = pdata->serial_dir;
+ mcasp->version = pdata->version;
+ mcasp->txnumevt = pdata->txnumevt;
+ mcasp->rxnumevt = pdata->rxnumevt;
+
+ mcasp->dev = &pdev->dev;
+
+ dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
+ if (dat)
+ mcasp->dat_port = true;
+
+ dma_params = &mcasp->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
+ dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+ dma_params->asp_chan_q = pdata->asp_chan_q;
+ dma_params->ram_chan_q = pdata->ram_chan_q;
+ dma_params->sram_pool = pdata->sram_pool;
+ dma_params->sram_size = pdata->sram_size_playback;
+ if (dat)
+ dma_params->dma_addr = dat->start;
+ else
+ dma_params->dma_addr = mem->start + pdata->tx_dma_offset;
+
+ /* Unconditional dmaengine stuff */
+ dma_data->addr = dma_params->dma_addr;
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (res)
+ dma_params->channel = res->start;
+ else
+ dma_params->channel = pdata->tx_dma_channel;
+
+ /* dmaengine filter data for DT and non-DT boot */
+ if (pdev->dev.of_node)
+ dma_data->filter_data = "tx";
+ else
+ dma_data->filter_data = &dma_params->channel;
+
+ dma_params = &mcasp->dma_params[SNDRV_PCM_STREAM_CAPTURE];
+ dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+ dma_params->asp_chan_q = pdata->asp_chan_q;
+ dma_params->ram_chan_q = pdata->ram_chan_q;
+ dma_params->sram_pool = pdata->sram_pool;
+ dma_params->sram_size = pdata->sram_size_capture;
+ if (dat)
+ dma_params->dma_addr = dat->start;
+ else
+ dma_params->dma_addr = mem->start + pdata->rx_dma_offset;
+
+ /* Unconditional dmaengine stuff */
+ dma_data->addr = dma_params->dma_addr;
+
+ if (mcasp->version < MCASP_VERSION_3) {
+ mcasp->fifo_base = DAVINCI_MCASP_V2_AFIFO_BASE;
+ /* dma_params->dma_addr is pointing to the data port address */
+ mcasp->dat_port = true;
+ } else {
+ mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (res)
+ dma_params->channel = res->start;
+ else
+ dma_params->channel = pdata->rx_dma_channel;
+
+ /* dmaengine filter data for DT and non-DT boot */
+ if (pdev->dev.of_node)
+ dma_data->filter_data = "rx";
+ else
+ dma_data->filter_data = &dma_params->channel;
+
+ dev_set_drvdata(&pdev->dev, mcasp);
+
+ mcasp_reparent_fck(pdev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &davinci_mcasp_component,
+ &davinci_mcasp_dai[pdata->op_mode], 1);
+
+ if (ret != 0)
+ goto err;
+
+ switch (mcasp->version) {
+ case MCASP_VERSION_1:
+ case MCASP_VERSION_2:
+ case MCASP_VERSION_3:
+ ret = davinci_soc_platform_register(&pdev->dev);
+ break;
+ case MCASP_VERSION_4:
+ ret = omap_pcm_platform_register(&pdev->dev);
+ break;
+ default:
+ dev_err(&pdev->dev, "Invalid McASP version: %d\n",
+ mcasp->version);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret) {
+ dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+}
+
+static int davinci_mcasp_remove(struct platform_device *pdev)
+{
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver davinci_mcasp_driver = {
+ .probe = davinci_mcasp_probe,
+ .remove = davinci_mcasp_remove,
+ .driver = {
+ .name = "davinci-mcasp",
+ .owner = THIS_MODULE,
+ .of_match_table = mcasp_dt_ids,
+ },
+};
+
+module_platform_driver(davinci_mcasp_driver);
+
+MODULE_AUTHOR("Steve Chen");
+MODULE_DESCRIPTION("TI DAVINCI McASP SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
new file mode 100644
index 00000000000..98fbc451892
--- /dev/null
+++ b/sound/soc/davinci/davinci-mcasp.h
@@ -0,0 +1,289 @@
+/*
+ * ALSA SoC McASP Audio Layer for TI DAVINCI processor
+ *
+ * MCASP related definitions
+ *
+ * Author: Nirmal Pandey <n-pandey@ti.com>,
+ * Suresh Rajashekara <suresh.r@ti.com>
+ * Steve Chen <schen@.mvista.com>
+ *
+ * Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright: (C) 2009 Texas Instruments, India
+ *
+ * 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 DAVINCI_MCASP_H
+#define DAVINCI_MCASP_H
+
+/*
+ * McASP register definitions
+ */
+#define DAVINCI_MCASP_PID_REG 0x00
+#define DAVINCI_MCASP_PWREMUMGT_REG 0x04
+
+#define DAVINCI_MCASP_PFUNC_REG 0x10
+#define DAVINCI_MCASP_PDIR_REG 0x14
+#define DAVINCI_MCASP_PDOUT_REG 0x18
+#define DAVINCI_MCASP_PDSET_REG 0x1c
+
+#define DAVINCI_MCASP_PDCLR_REG 0x20
+
+#define DAVINCI_MCASP_TLGC_REG 0x30
+#define DAVINCI_MCASP_TLMR_REG 0x34
+
+#define DAVINCI_MCASP_GBLCTL_REG 0x44
+#define DAVINCI_MCASP_AMUTE_REG 0x48
+#define DAVINCI_MCASP_LBCTL_REG 0x4c
+
+#define DAVINCI_MCASP_TXDITCTL_REG 0x50
+
+#define DAVINCI_MCASP_GBLCTLR_REG 0x60
+#define DAVINCI_MCASP_RXMASK_REG 0x64
+#define DAVINCI_MCASP_RXFMT_REG 0x68
+#define DAVINCI_MCASP_RXFMCTL_REG 0x6c
+
+#define DAVINCI_MCASP_ACLKRCTL_REG 0x70
+#define DAVINCI_MCASP_AHCLKRCTL_REG 0x74
+#define DAVINCI_MCASP_RXTDM_REG 0x78
+#define DAVINCI_MCASP_EVTCTLR_REG 0x7c
+
+#define DAVINCI_MCASP_RXSTAT_REG 0x80
+#define DAVINCI_MCASP_RXTDMSLOT_REG 0x84
+#define DAVINCI_MCASP_RXCLKCHK_REG 0x88
+#define DAVINCI_MCASP_REVTCTL_REG 0x8c
+
+#define DAVINCI_MCASP_GBLCTLX_REG 0xa0
+#define DAVINCI_MCASP_TXMASK_REG 0xa4
+#define DAVINCI_MCASP_TXFMT_REG 0xa8
+#define DAVINCI_MCASP_TXFMCTL_REG 0xac
+
+#define DAVINCI_MCASP_ACLKXCTL_REG 0xb0
+#define DAVINCI_MCASP_AHCLKXCTL_REG 0xb4
+#define DAVINCI_MCASP_TXTDM_REG 0xb8
+#define DAVINCI_MCASP_EVTCTLX_REG 0xbc
+
+#define DAVINCI_MCASP_TXSTAT_REG 0xc0
+#define DAVINCI_MCASP_TXTDMSLOT_REG 0xc4
+#define DAVINCI_MCASP_TXCLKCHK_REG 0xc8
+#define DAVINCI_MCASP_XEVTCTL_REG 0xcc
+
+/* Left(even TDM Slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRA_REG 0x100
+/* Right(odd TDM slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRB_REG 0x118
+/* Left(even TDM slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRA_REG 0x130
+/* Right(odd TDM Slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRB_REG 0x148
+
+/* Serializer n Control Register */
+#define DAVINCI_MCASP_XRSRCTL_BASE_REG 0x180
+#define DAVINCI_MCASP_XRSRCTL_REG(n) (DAVINCI_MCASP_XRSRCTL_BASE_REG + \
+ (n << 2))
+
+/* Transmit Buffer for Serializer n */
+#define DAVINCI_MCASP_TXBUF_REG 0x200
+/* Receive Buffer for Serializer n */
+#define DAVINCI_MCASP_RXBUF_REG 0x280
+
+/* McASP FIFO Registers */
+#define DAVINCI_MCASP_V2_AFIFO_BASE (0x1010)
+#define DAVINCI_MCASP_V3_AFIFO_BASE (0x1000)
+
+/* FIFO register offsets from AFIFO base */
+#define MCASP_WFIFOCTL_OFFSET (0x0)
+#define MCASP_WFIFOSTS_OFFSET (0x4)
+#define MCASP_RFIFOCTL_OFFSET (0x8)
+#define MCASP_RFIFOSTS_OFFSET (0xc)
+
+/*
+ * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management
+ * Register Bits
+ */
+#define MCASP_FREE BIT(0)
+#define MCASP_SOFT BIT(1)
+
+/*
+ * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits
+ */
+#define AXR(n) (1<<n)
+#define PFUNC_AMUTE BIT(25)
+#define ACLKX BIT(26)
+#define AHCLKX BIT(27)
+#define AFSX BIT(28)
+#define ACLKR BIT(29)
+#define AHCLKR BIT(30)
+#define AFSR BIT(31)
+
+/*
+ * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits
+ */
+#define AXR(n) (1<<n)
+#define PDIR_AMUTE BIT(25)
+#define ACLKX BIT(26)
+#define AHCLKX BIT(27)
+#define AFSX BIT(28)
+#define ACLKR BIT(29)
+#define AHCLKR BIT(30)
+#define AFSR BIT(31)
+
+/*
+ * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits
+ */
+#define DITEN BIT(0) /* Transmit DIT mode enable/disable */
+#define VA BIT(2)
+#define VB BIT(3)
+
+/*
+ * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits
+ */
+#define TXROT(val) (val)
+#define TXSEL BIT(3)
+#define TXSSZ(val) (val<<4)
+#define TXPBIT(val) (val<<8)
+#define TXPAD(val) (val<<13)
+#define TXORD BIT(15)
+#define FSXDLY(val) (val<<16)
+
+/*
+ * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits
+ */
+#define RXROT(val) (val)
+#define RXSEL BIT(3)
+#define RXSSZ(val) (val<<4)
+#define RXPBIT(val) (val<<8)
+#define RXPAD(val) (val<<13)
+#define RXORD BIT(15)
+#define FSRDLY(val) (val<<16)
+
+/*
+ * DAVINCI_MCASP_TXFMCTL_REG - Transmit Frame Control Register Bits
+ */
+#define FSXPOL BIT(0)
+#define AFSXE BIT(1)
+#define FSXDUR BIT(4)
+#define FSXMOD(val) (val<<7)
+
+/*
+ * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits
+ */
+#define FSRPOL BIT(0)
+#define AFSRE BIT(1)
+#define FSRDUR BIT(4)
+#define FSRMOD(val) (val<<7)
+
+/*
+ * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits
+ */
+#define ACLKXDIV(val) (val)
+#define ACLKXE BIT(5)
+#define TX_ASYNC BIT(6)
+#define ACLKXPOL BIT(7)
+#define ACLKXDIV_MASK 0x1f
+
+/*
+ * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits
+ */
+#define ACLKRDIV(val) (val)
+#define ACLKRE BIT(5)
+#define RX_ASYNC BIT(6)
+#define ACLKRPOL BIT(7)
+#define ACLKRDIV_MASK 0x1f
+
+/*
+ * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control
+ * Register Bits
+ */
+#define AHCLKXDIV(val) (val)
+#define AHCLKXPOL BIT(14)
+#define AHCLKXE BIT(15)
+#define AHCLKXDIV_MASK 0xfff
+
+/*
+ * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control
+ * Register Bits
+ */
+#define AHCLKRDIV(val) (val)
+#define AHCLKRPOL BIT(14)
+#define AHCLKRE BIT(15)
+#define AHCLKRDIV_MASK 0xfff
+
+/*
+ * DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits
+ */
+#define MODE(val) (val)
+#define DISMOD (val)(val<<2)
+#define TXSTATE BIT(4)
+#define RXSTATE BIT(5)
+#define SRMOD_MASK 3
+#define SRMOD_INACTIVE 0
+
+/*
+ * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits
+ */
+#define LBEN BIT(0)
+#define LBORD BIT(1)
+#define LBGENMODE(val) (val<<2)
+
+/*
+ * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration
+ */
+#define TXTDMS(n) (1<<n)
+
+/*
+ * DAVINCI_MCASP_RXTDMSLOT_REG - Receive TDM Slot Register configuration
+ */
+#define RXTDMS(n) (1<<n)
+
+/*
+ * DAVINCI_MCASP_GBLCTL_REG - Global Control Register Bits
+ */
+#define RXCLKRST BIT(0) /* Receiver Clock Divider Reset */
+#define RXHCLKRST BIT(1) /* Receiver High Frequency Clock Divider */
+#define RXSERCLR BIT(2) /* Receiver Serializer Clear */
+#define RXSMRST BIT(3) /* Receiver State Machine Reset */
+#define RXFSRST BIT(4) /* Frame Sync Generator Reset */
+#define TXCLKRST BIT(8) /* Transmitter Clock Divider Reset */
+#define TXHCLKRST BIT(9) /* Transmitter High Frequency Clock Divider*/
+#define TXSERCLR BIT(10) /* Transmit Serializer Clear */
+#define TXSMRST BIT(11) /* Transmitter State Machine Reset */
+#define TXFSRST BIT(12) /* Frame Sync Generator Reset */
+
+/*
+ * DAVINCI_MCASP_AMUTE_REG - Mute Control Register Bits
+ */
+#define MUTENA(val) (val)
+#define MUTEINPOL BIT(2)
+#define MUTEINENA BIT(3)
+#define MUTEIN BIT(4)
+#define MUTER BIT(5)
+#define MUTEX BIT(6)
+#define MUTEFSR BIT(7)
+#define MUTEFSX BIT(8)
+#define MUTEBADCLKR BIT(9)
+#define MUTEBADCLKX BIT(10)
+#define MUTERXDMAERR BIT(11)
+#define MUTETXDMAERR BIT(12)
+
+/*
+ * DAVINCI_MCASP_REVTCTL_REG - Receiver DMA Event Control Register bits
+ */
+#define RXDATADMADIS BIT(0)
+
+/*
+ * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits
+ */
+#define TXDATADMADIS BIT(0)
+
+/*
+ * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits
+ */
+#define FIFO_ENABLE BIT(16)
+#define NUMEVT_MASK (0xFF << 8)
+#define NUMEVT(x) (((x) & 0xFF) << 8)
+#define NUMDMA_MASK (0xFF)
+
+#endif /* DAVINCI_MCASP_H */
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index a0599658848..7809e9d935f 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -3,6 +3,7 @@
*
* Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ * added SRAM ping/pong (C) 2008 Troy Kisky <troy.kisky@boundarydevices.com>
*
* 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
@@ -15,6 +16,8 @@
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/kernel.h>
+#include <linux/genalloc.h>
+#include <linux/platform_data/edma.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -22,24 +25,45 @@
#include <sound/soc.h>
#include <asm/dma.h>
-#include <mach/edma.h>
#include "davinci-pcm.h"
-static struct snd_pcm_hardware davinci_pcm_hardware = {
+#ifdef DEBUG
+static void print_buf_info(int slot, char *name)
+{
+ struct edmacc_param p;
+ if (slot < 0)
+ return;
+ edma_read_slot(slot, &p);
+ printk(KERN_DEBUG "%s: 0x%x, opt=%x, src=%x, a_b_cnt=%x dst=%x\n",
+ name, slot, p.opt, p.src, p.a_b_cnt, p.dst);
+ printk(KERN_DEBUG " src_dst_bidx=%x link_bcntrld=%x src_dst_cidx=%x ccnt=%x\n",
+ p.src_dst_bidx, p.link_bcntrld, p.src_dst_cidx, p.ccnt);
+}
+#else
+static void print_buf_info(int slot, char *name)
+{
+}
+#endif
+
+static struct snd_pcm_hardware pcm_hardware_playback = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME|
+ SNDRV_PCM_INFO_BATCH),
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8 * 1024,
+ .periods_min = 16,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware pcm_hardware_capture = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE),
- .formats = (SNDRV_PCM_FMTBIT_S16_LE),
- .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_KNOT),
- .rate_min = 8000,
- .rate_max = 96000,
- .channels_min = 2,
- .channels_max = 2,
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_BATCH),
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
@@ -48,107 +72,445 @@ static struct snd_pcm_hardware davinci_pcm_hardware = {
.fifo_size = 0,
};
+/*
+ * How ping/pong works....
+ *
+ * Playback:
+ * ram_params - copys 2*ping_size from start of SDRAM to iram,
+ * links to ram_link2
+ * ram_link2 - copys rest of SDRAM to iram in ping_size units,
+ * links to ram_link
+ * ram_link - copys entire SDRAM to iram in ping_size uints,
+ * links to self
+ *
+ * asp_params - same as asp_link[0]
+ * asp_link[0] - copys from lower half of iram to asp port
+ * links to asp_link[1], triggers iram copy event on completion
+ * asp_link[1] - copys from upper half of iram to asp port
+ * links to asp_link[0], triggers iram copy event on completion
+ * triggers interrupt only needed to let upper SOC levels update position
+ * in stream on completion
+ *
+ * When playback is started:
+ * ram_params started
+ * asp_params started
+ *
+ * Capture:
+ * ram_params - same as ram_link,
+ * links to ram_link
+ * ram_link - same as playback
+ * links to self
+ *
+ * asp_params - same as playback
+ * asp_link[0] - same as playback
+ * asp_link[1] - same as playback
+ *
+ * When capture is started:
+ * asp_params started
+ */
struct davinci_runtime_data {
spinlock_t lock;
int period; /* current DMA period */
- int master_lch; /* Master DMA channel */
- int slave_lch; /* linked parameter RAM reload slot */
+ int asp_channel; /* Master DMA channel */
+ int asp_link[2]; /* asp parameter link channel, ping/pong */
struct davinci_pcm_dma_params *params; /* DMA params */
+ int ram_channel;
+ int ram_link;
+ int ram_link2;
+ struct edmacc_param asp_params;
+ struct edmacc_param ram_params;
};
+static void davinci_pcm_period_elapsed(struct snd_pcm_substream *substream)
+{
+ struct davinci_runtime_data *prtd = substream->runtime->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ prtd->period++;
+ if (unlikely(prtd->period >= runtime->periods))
+ prtd->period = 0;
+}
+
+static void davinci_pcm_period_reset(struct snd_pcm_substream *substream)
+{
+ struct davinci_runtime_data *prtd = substream->runtime->private_data;
+
+ prtd->period = 0;
+}
+/*
+ * Not used with ping/pong
+ */
static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
{
struct davinci_runtime_data *prtd = substream->runtime->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
- int lch = prtd->slave_lch;
unsigned int period_size;
unsigned int dma_offset;
dma_addr_t dma_pos;
dma_addr_t src, dst;
unsigned short src_bidx, dst_bidx;
+ unsigned short src_cidx, dst_cidx;
unsigned int data_type;
+ unsigned short acnt;
unsigned int count;
+ unsigned int fifo_level;
period_size = snd_pcm_lib_period_bytes(substream);
dma_offset = prtd->period * period_size;
dma_pos = runtime->dma_addr + dma_offset;
+ fifo_level = prtd->params->fifo_level;
pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
- "dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
+ "dma_ptr = %x period_size=%x\n", prtd->asp_link[0], dma_pos,
+ period_size);
data_type = prtd->params->data_type;
count = period_size / data_type;
+ if (fifo_level)
+ count /= fifo_level;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
src = dma_pos;
dst = prtd->params->dma_addr;
src_bidx = data_type;
- dst_bidx = 0;
+ dst_bidx = 4;
+ src_cidx = data_type * fifo_level;
+ dst_cidx = 0;
} else {
src = prtd->params->dma_addr;
dst = dma_pos;
src_bidx = 0;
dst_bidx = data_type;
+ src_cidx = 0;
+ dst_cidx = data_type * fifo_level;
}
- edma_set_src(lch, src, INCR, W8BIT);
- edma_set_dest(lch, dst, INCR, W8BIT);
- edma_set_src_index(lch, src_bidx, 0);
- edma_set_dest_index(lch, dst_bidx, 0);
- edma_set_transfer_params(lch, data_type, count, 1, 0, ASYNC);
+ acnt = prtd->params->acnt;
+ edma_set_src(prtd->asp_link[0], src, INCR, W8BIT);
+ edma_set_dest(prtd->asp_link[0], dst, INCR, W8BIT);
- prtd->period++;
- if (unlikely(prtd->period >= runtime->periods))
- prtd->period = 0;
+ edma_set_src_index(prtd->asp_link[0], src_bidx, src_cidx);
+ edma_set_dest_index(prtd->asp_link[0], dst_bidx, dst_cidx);
+
+ if (!fifo_level)
+ edma_set_transfer_params(prtd->asp_link[0], acnt, count, 1, 0,
+ ASYNC);
+ else
+ edma_set_transfer_params(prtd->asp_link[0], acnt,
+ fifo_level,
+ count, fifo_level,
+ ABSYNC);
}
-static void davinci_pcm_dma_irq(unsigned lch, u16 ch_status, void *data)
+static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
{
struct snd_pcm_substream *substream = data;
struct davinci_runtime_data *prtd = substream->runtime->private_data;
- pr_debug("davinci_pcm: lch=%d, status=0x%x\n", lch, ch_status);
+ print_buf_info(prtd->ram_channel, "i ram_channel");
+ pr_debug("davinci_pcm: link=%d, status=0x%x\n", link, ch_status);
- if (unlikely(ch_status != DMA_COMPLETE))
+ if (unlikely(ch_status != EDMA_DMA_COMPLETE))
return;
if (snd_pcm_running(substream)) {
- snd_pcm_period_elapsed(substream);
-
spin_lock(&prtd->lock);
- davinci_pcm_enqueue_dma(substream);
+ if (prtd->ram_channel < 0) {
+ /* No ping/pong must fix up link dma data*/
+ davinci_pcm_enqueue_dma(substream);
+ }
+ davinci_pcm_period_elapsed(substream);
spin_unlock(&prtd->lock);
+ snd_pcm_period_elapsed(substream);
+ }
+}
+
+#ifdef CONFIG_GENERIC_ALLOCATOR
+static int allocate_sram(struct snd_pcm_substream *substream,
+ struct gen_pool *sram_pool, unsigned size,
+ struct snd_pcm_hardware *ppcm)
+{
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ struct snd_dma_buffer *iram_dma = NULL;
+ dma_addr_t iram_phys = 0;
+ void *iram_virt = NULL;
+
+ if (buf->private_data || !size)
+ return 0;
+
+ ppcm->period_bytes_max = size;
+ iram_virt = gen_pool_dma_alloc(sram_pool, size, &iram_phys);
+ if (!iram_virt)
+ goto exit1;
+ iram_dma = kzalloc(sizeof(*iram_dma), GFP_KERNEL);
+ if (!iram_dma)
+ goto exit2;
+ iram_dma->area = iram_virt;
+ iram_dma->addr = iram_phys;
+ memset(iram_dma->area, 0, size);
+ iram_dma->bytes = size;
+ buf->private_data = iram_dma;
+ return 0;
+exit2:
+ if (iram_virt)
+ gen_pool_free(sram_pool, (unsigned)iram_virt, size);
+exit1:
+ return -ENOMEM;
+}
+
+static void davinci_free_sram(struct snd_pcm_substream *substream,
+ struct snd_dma_buffer *iram_dma)
+{
+ struct davinci_runtime_data *prtd = substream->runtime->private_data;
+ struct gen_pool *sram_pool = prtd->params->sram_pool;
+
+ gen_pool_free(sram_pool, (unsigned) iram_dma->area, iram_dma->bytes);
+}
+#else
+static int allocate_sram(struct snd_pcm_substream *substream,
+ struct gen_pool *sram_pool, unsigned size,
+ struct snd_pcm_hardware *ppcm)
+{
+ return 0;
+}
+
+static void davinci_free_sram(struct snd_pcm_substream *substream,
+ struct snd_dma_buffer *iram_dma)
+{
+}
+#endif
+
+/*
+ * Only used with ping/pong.
+ * This is called after runtime->dma_addr, period_bytes and data_type are valid
+ */
+static int ping_pong_dma_setup(struct snd_pcm_substream *substream)
+{
+ unsigned short ram_src_cidx, ram_dst_cidx;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct davinci_runtime_data *prtd = runtime->private_data;
+ struct snd_dma_buffer *iram_dma =
+ (struct snd_dma_buffer *)substream->dma_buffer.private_data;
+ struct davinci_pcm_dma_params *params = prtd->params;
+ unsigned int data_type = params->data_type;
+ unsigned int acnt = params->acnt;
+ /* divide by 2 for ping/pong */
+ unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1;
+ unsigned int fifo_level = prtd->params->fifo_level;
+ unsigned int count;
+ if ((data_type == 0) || (data_type > 4)) {
+ printk(KERN_ERR "%s: data_type=%i\n", __func__, data_type);
+ return -EINVAL;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_addr_t asp_src_pong = iram_dma->addr + ping_size;
+ ram_src_cidx = ping_size;
+ ram_dst_cidx = -ping_size;
+ edma_set_src(prtd->asp_link[1], asp_src_pong, INCR, W8BIT);
+
+ edma_set_src_index(prtd->asp_link[0], data_type,
+ data_type * fifo_level);
+ edma_set_src_index(prtd->asp_link[1], data_type,
+ data_type * fifo_level);
+
+ edma_set_src(prtd->ram_link, runtime->dma_addr, INCR, W32BIT);
+ } else {
+ dma_addr_t asp_dst_pong = iram_dma->addr + ping_size;
+ ram_src_cidx = -ping_size;
+ ram_dst_cidx = ping_size;
+ edma_set_dest(prtd->asp_link[1], asp_dst_pong, INCR, W8BIT);
+
+ edma_set_dest_index(prtd->asp_link[0], data_type,
+ data_type * fifo_level);
+ edma_set_dest_index(prtd->asp_link[1], data_type,
+ data_type * fifo_level);
+
+ edma_set_dest(prtd->ram_link, runtime->dma_addr, INCR, W32BIT);
}
+
+ if (!fifo_level) {
+ count = ping_size / data_type;
+ edma_set_transfer_params(prtd->asp_link[0], acnt, count,
+ 1, 0, ASYNC);
+ edma_set_transfer_params(prtd->asp_link[1], acnt, count,
+ 1, 0, ASYNC);
+ } else {
+ count = ping_size / (data_type * fifo_level);
+ edma_set_transfer_params(prtd->asp_link[0], acnt, fifo_level,
+ count, fifo_level, ABSYNC);
+ edma_set_transfer_params(prtd->asp_link[1], acnt, fifo_level,
+ count, fifo_level, ABSYNC);
+ }
+
+ edma_set_src_index(prtd->ram_link, ping_size, ram_src_cidx);
+ edma_set_dest_index(prtd->ram_link, ping_size, ram_dst_cidx);
+ edma_set_transfer_params(prtd->ram_link, ping_size, 2,
+ runtime->periods, 2, ASYNC);
+
+ /* init master params */
+ edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
+ edma_read_slot(prtd->ram_link, &prtd->ram_params);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ struct edmacc_param p_ram;
+ /* Copy entire iram buffer before playback started */
+ prtd->ram_params.a_b_cnt = (1 << 16) | (ping_size << 1);
+ /* 0 dst_bidx */
+ prtd->ram_params.src_dst_bidx = (ping_size << 1);
+ /* 0 dst_cidx */
+ prtd->ram_params.src_dst_cidx = (ping_size << 1);
+ prtd->ram_params.ccnt = 1;
+
+ /* Skip 1st period */
+ edma_read_slot(prtd->ram_link, &p_ram);
+ p_ram.src += (ping_size << 1);
+ p_ram.ccnt -= 1;
+ edma_write_slot(prtd->ram_link2, &p_ram);
+ /*
+ * When 1st started, ram -> iram dma channel will fill the
+ * entire iram. Then, whenever a ping/pong asp buffer finishes,
+ * 1/2 iram will be filled.
+ */
+ prtd->ram_params.link_bcntrld =
+ EDMA_CHAN_SLOT(prtd->ram_link2) << 5;
+ }
+ return 0;
+}
+
+/* 1 asp tx or rx channel using 2 parameter channels
+ * 1 ram to/from iram channel using 1 parameter channel
+ *
+ * Playback
+ * ram copy channel kicks off first,
+ * 1st ram copy of entire iram buffer completion kicks off asp channel
+ * asp tcc always kicks off ram copy of 1/2 iram buffer
+ *
+ * Record
+ * asp channel starts, tcc kicks off ram copy
+ */
+static int request_ping_pong(struct snd_pcm_substream *substream,
+ struct davinci_runtime_data *prtd,
+ struct snd_dma_buffer *iram_dma)
+{
+ dma_addr_t asp_src_ping;
+ dma_addr_t asp_dst_ping;
+ int ret;
+ struct davinci_pcm_dma_params *params = prtd->params;
+
+ /* Request ram master channel */
+ ret = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY,
+ davinci_pcm_dma_irq, substream,
+ prtd->params->ram_chan_q);
+ if (ret < 0)
+ goto exit1;
+
+ /* Request ram link channel */
+ ret = prtd->ram_link = edma_alloc_slot(
+ EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
+ if (ret < 0)
+ goto exit2;
+
+ ret = prtd->asp_link[1] = edma_alloc_slot(
+ EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
+ if (ret < 0)
+ goto exit3;
+
+ prtd->ram_link2 = -1;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = prtd->ram_link2 = edma_alloc_slot(
+ EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
+ if (ret < 0)
+ goto exit4;
+ }
+ /* circle ping-pong buffers */
+ edma_link(prtd->asp_link[0], prtd->asp_link[1]);
+ edma_link(prtd->asp_link[1], prtd->asp_link[0]);
+ /* circle ram buffers */
+ edma_link(prtd->ram_link, prtd->ram_link);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ asp_src_ping = iram_dma->addr;
+ asp_dst_ping = params->dma_addr; /* fifo */
+ } else {
+ asp_src_ping = params->dma_addr; /* fifo */
+ asp_dst_ping = iram_dma->addr;
+ }
+ /* ping */
+ edma_set_src(prtd->asp_link[0], asp_src_ping, INCR, W16BIT);
+ edma_set_dest(prtd->asp_link[0], asp_dst_ping, INCR, W16BIT);
+ edma_set_src_index(prtd->asp_link[0], 0, 0);
+ edma_set_dest_index(prtd->asp_link[0], 0, 0);
+
+ edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
+ prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN);
+ prtd->asp_params.opt |= TCCHEN |
+ EDMA_TCC(prtd->ram_channel & 0x3f);
+ edma_write_slot(prtd->asp_link[0], &prtd->asp_params);
+
+ /* pong */
+ edma_set_src(prtd->asp_link[1], asp_src_ping, INCR, W16BIT);
+ edma_set_dest(prtd->asp_link[1], asp_dst_ping, INCR, W16BIT);
+ edma_set_src_index(prtd->asp_link[1], 0, 0);
+ edma_set_dest_index(prtd->asp_link[1], 0, 0);
+
+ edma_read_slot(prtd->asp_link[1], &prtd->asp_params);
+ prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f));
+ /* interrupt after every pong completion */
+ prtd->asp_params.opt |= TCINTEN | TCCHEN |
+ EDMA_TCC(prtd->ram_channel & 0x3f);
+ edma_write_slot(prtd->asp_link[1], &prtd->asp_params);
+
+ /* ram */
+ edma_set_src(prtd->ram_link, iram_dma->addr, INCR, W32BIT);
+ edma_set_dest(prtd->ram_link, iram_dma->addr, INCR, W32BIT);
+ pr_debug("%s: audio dma channels/slots in use for ram:%u %u %u,"
+ "for asp:%u %u %u\n", __func__,
+ prtd->ram_channel, prtd->ram_link, prtd->ram_link2,
+ prtd->asp_channel, prtd->asp_link[0],
+ prtd->asp_link[1]);
+ return 0;
+exit4:
+ edma_free_channel(prtd->asp_link[1]);
+ prtd->asp_link[1] = -1;
+exit3:
+ edma_free_channel(prtd->ram_link);
+ prtd->ram_link = -1;
+exit2:
+ edma_free_channel(prtd->ram_channel);
+ prtd->ram_channel = -1;
+exit1:
+ return ret;
}
static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
{
+ struct snd_dma_buffer *iram_dma;
struct davinci_runtime_data *prtd = substream->runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct davinci_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data;
- struct edmacc_param p_ram;
+ struct davinci_pcm_dma_params *params = prtd->params;
int ret;
- if (!dma_data)
+ if (!params)
return -ENODEV;
- prtd->params = dma_data;
-
- /* Request master DMA channel */
- ret = edma_alloc_channel(prtd->params->channel,
- davinci_pcm_dma_irq, substream,
- EVENTQ_0);
+ /* Request asp master DMA channel */
+ ret = prtd->asp_channel = edma_alloc_channel(params->channel,
+ davinci_pcm_dma_irq, substream,
+ prtd->params->asp_chan_q);
if (ret < 0)
- return ret;
- prtd->master_lch = ret;
+ goto exit1;
- /* Request parameter RAM reload slot */
- ret = edma_alloc_slot(EDMA_SLOT_ANY);
- if (ret < 0) {
- edma_free_channel(prtd->master_lch);
- return ret;
+ /* Request asp link channels */
+ ret = prtd->asp_link[0] = edma_alloc_slot(
+ EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
+ if (ret < 0)
+ goto exit2;
+
+ iram_dma = (struct snd_dma_buffer *)substream->dma_buffer.private_data;
+ if (iram_dma) {
+ if (request_ping_pong(substream, prtd, iram_dma) == 0)
+ return 0;
+ printk(KERN_WARNING "%s: dma channel allocation failed,"
+ "not using sram\n", __func__);
}
- prtd->slave_lch = ret;
/* Issue transfer completion IRQ when the channel completes a
* transfer, then always reload from the same slot (by a kind
@@ -159,12 +521,17 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
* the buffer and its length (ccnt) ... use it as a template
* so davinci_pcm_enqueue_dma() takes less time in IRQ.
*/
- edma_read_slot(prtd->slave_lch, &p_ram);
- p_ram.opt |= TCINTEN | EDMA_TCC(prtd->master_lch);
- p_ram.link_bcntrld = prtd->slave_lch << 5;
- edma_write_slot(prtd->slave_lch, &p_ram);
-
+ edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
+ prtd->asp_params.opt |= TCINTEN |
+ EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel));
+ prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(prtd->asp_link[0]) << 5;
+ edma_write_slot(prtd->asp_link[0], &prtd->asp_params);
return 0;
+exit2:
+ edma_free_channel(prtd->asp_channel);
+ prtd->asp_channel = -1;
+exit1:
+ return ret;
}
static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
@@ -176,14 +543,21 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ edma_start(prtd->asp_channel);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ prtd->ram_channel >= 0) {
+ /* copy 1st iram buffer */
+ edma_start(prtd->ram_channel);
+ }
+ break;
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- edma_start(prtd->master_lch);
+ edma_resume(prtd->asp_channel);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- edma_stop(prtd->master_lch);
+ edma_pause(prtd->asp_channel);
break;
default:
ret = -EINVAL;
@@ -198,14 +572,48 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
{
struct davinci_runtime_data *prtd = substream->runtime->private_data;
- struct edmacc_param temp;
- prtd->period = 0;
+ davinci_pcm_period_reset(substream);
+ if (prtd->ram_channel >= 0) {
+ int ret = ping_pong_dma_setup(substream);
+ if (ret < 0)
+ return ret;
+
+ edma_write_slot(prtd->ram_channel, &prtd->ram_params);
+ edma_write_slot(prtd->asp_channel, &prtd->asp_params);
+
+ print_buf_info(prtd->ram_channel, "ram_channel");
+ print_buf_info(prtd->ram_link, "ram_link");
+ print_buf_info(prtd->ram_link2, "ram_link2");
+ print_buf_info(prtd->asp_channel, "asp_channel");
+ print_buf_info(prtd->asp_link[0], "asp_link[0]");
+ print_buf_info(prtd->asp_link[1], "asp_link[1]");
+
+ /*
+ * There is a phase offset of 2 periods between the position
+ * used by dma setup and the position reported in the pointer
+ * function.
+ *
+ * The phase offset, when not using ping-pong buffers, is due to
+ * the two consecutive calls to davinci_pcm_enqueue_dma() below.
+ *
+ * Whereas here, with ping-pong buffers, the phase is due to
+ * there being an entire buffer transfer complete before the
+ * first dma completion event triggers davinci_pcm_dma_irq().
+ */
+ davinci_pcm_period_elapsed(substream);
+ davinci_pcm_period_elapsed(substream);
+
+ return 0;
+ }
davinci_pcm_enqueue_dma(substream);
+ davinci_pcm_period_elapsed(substream);
/* Copy self-linked parameter RAM entry into master channel */
- edma_read_slot(prtd->slave_lch, &temp);
- edma_write_slot(prtd->master_lch, &temp);
+ edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
+ edma_write_slot(prtd->asp_channel, &prtd->asp_params);
+ davinci_pcm_enqueue_dma(substream);
+ davinci_pcm_period_elapsed(substream);
return 0;
}
@@ -216,20 +624,25 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct davinci_runtime_data *prtd = runtime->private_data;
unsigned int offset;
- dma_addr_t count;
- dma_addr_t src, dst;
-
+ int asp_count;
+ unsigned int period_size = snd_pcm_lib_period_bytes(substream);
+
+ /*
+ * There is a phase offset of 2 periods between the position used by dma
+ * setup and the position reported in the pointer function. Either +2 in
+ * the dma setup or -2 here in the pointer function (with wrapping,
+ * both) accounts for this offset -- choose the latter since it makes
+ * the first-time setup clearer.
+ */
spin_lock(&prtd->lock);
-
- edma_get_position(prtd->master_lch, &src, &dst);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- count = src - runtime->dma_addr;
- else
- count = dst - runtime->dma_addr;
-
+ asp_count = prtd->period - 2;
spin_unlock(&prtd->lock);
- offset = bytes_to_frames(runtime, count);
+ if (asp_count < 0)
+ asp_count += runtime->periods;
+ asp_count *= period_size;
+
+ offset = bytes_to_frames(runtime, asp_count);
if (offset >= runtime->buffer_size)
offset = 0;
@@ -240,15 +653,38 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct davinci_runtime_data *prtd;
+ struct snd_pcm_hardware *ppcm;
int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct davinci_pcm_dma_params *pa;
+ struct davinci_pcm_dma_params *params;
- snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware);
+ pa = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ if (!pa)
+ return -ENODEV;
+ params = &pa[substream->stream];
+
+ ppcm = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ &pcm_hardware_playback : &pcm_hardware_capture;
+ allocate_sram(substream, params->sram_pool, params->sram_size, ppcm);
+ snd_soc_set_runtime_hwparams(substream, ppcm);
+ /* ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
prtd = kzalloc(sizeof(struct davinci_runtime_data), GFP_KERNEL);
if (prtd == NULL)
return -ENOMEM;
spin_lock_init(&prtd->lock);
+ prtd->params = params;
+ prtd->asp_channel = -1;
+ prtd->asp_link[0] = prtd->asp_link[1] = -1;
+ prtd->ram_channel = -1;
+ prtd->ram_link = -1;
+ prtd->ram_link2 = -1;
runtime->private_data = prtd;
@@ -266,10 +702,29 @@ static int davinci_pcm_close(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct davinci_runtime_data *prtd = runtime->private_data;
- edma_unlink(prtd->slave_lch);
-
- edma_free_slot(prtd->slave_lch);
- edma_free_channel(prtd->master_lch);
+ if (prtd->ram_channel >= 0)
+ edma_stop(prtd->ram_channel);
+ if (prtd->asp_channel >= 0)
+ edma_stop(prtd->asp_channel);
+ if (prtd->asp_link[0] >= 0)
+ edma_unlink(prtd->asp_link[0]);
+ if (prtd->asp_link[1] >= 0)
+ edma_unlink(prtd->asp_link[1]);
+ if (prtd->ram_link >= 0)
+ edma_unlink(prtd->ram_link);
+
+ if (prtd->asp_link[0] >= 0)
+ edma_free_slot(prtd->asp_link[0]);
+ if (prtd->asp_link[1] >= 0)
+ edma_free_slot(prtd->asp_link[1]);
+ if (prtd->asp_channel >= 0)
+ edma_free_channel(prtd->asp_channel);
+ if (prtd->ram_link >= 0)
+ edma_free_slot(prtd->ram_link);
+ if (prtd->ram_link2 >= 0)
+ edma_free_slot(prtd->ram_link2);
+ if (prtd->ram_channel >= 0)
+ edma_free_channel(prtd->ram_channel);
kfree(prtd);
@@ -311,11 +766,11 @@ static struct snd_pcm_ops davinci_pcm_ops = {
.mmap = davinci_pcm_mmap,
};
-static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream,
+ size_t size)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = davinci_pcm_hardware.buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
@@ -340,6 +795,7 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
int stream;
for (stream = 0; stream < 2; stream++) {
+ struct snd_dma_buffer *iram_dma;
substream = pcm->streams[stream].substream;
if (!substream)
continue;
@@ -351,31 +807,36 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
dma_free_writecombine(pcm->card->dev, buf->bytes,
buf->area, buf->addr);
buf->area = NULL;
+ iram_dma = buf->private_data;
+ if (iram_dma) {
+ davinci_free_sram(substream, iram_dma);
+ kfree(iram_dma);
+ }
}
}
-static u64 davinci_pcm_dmamask = 0xffffffff;
-
-static int davinci_pcm_new(struct snd_card *card,
- struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int davinci_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
int ret;
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &davinci_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = 0xffffffff;
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
- if (dai->playback.channels_min) {
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
ret = davinci_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
+ SNDRV_PCM_STREAM_PLAYBACK,
+ pcm_hardware_playback.buffer_bytes_max);
if (ret)
return ret;
}
- if (dai->capture.channels_min) {
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
ret = davinci_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
+ SNDRV_PCM_STREAM_CAPTURE,
+ pcm_hardware_capture.buffer_bytes_max);
if (ret)
return ret;
}
@@ -383,25 +844,17 @@ static int davinci_pcm_new(struct snd_card *card,
return 0;
}
-struct snd_soc_platform davinci_soc_platform = {
- .name = "davinci-audio",
- .pcm_ops = &davinci_pcm_ops,
+static struct snd_soc_platform_driver davinci_soc_platform = {
+ .ops = &davinci_pcm_ops,
.pcm_new = davinci_pcm_new,
.pcm_free = davinci_pcm_free,
};
-EXPORT_SYMBOL_GPL(davinci_soc_platform);
-
-static int __init davinci_soc_platform_init(void)
-{
- return snd_soc_register_platform(&davinci_soc_platform);
-}
-module_init(davinci_soc_platform_init);
-static void __exit davinci_soc_platform_exit(void)
+int davinci_soc_platform_register(struct device *dev)
{
- snd_soc_unregister_platform(&davinci_soc_platform);
+ return devm_snd_soc_register_platform(dev, &davinci_soc_platform);
}
-module_exit(davinci_soc_platform_exit);
+EXPORT_SYMBOL_GPL(davinci_soc_platform_register);
MODULE_AUTHOR("Vladimir Barinov");
MODULE_DESCRIPTION("TI DAVINCI PCM DMA module");
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h
index 62cb4eb07e3..0fe2346a9aa 100644
--- a/sound/soc/davinci/davinci-pcm.h
+++ b/sound/soc/davinci/davinci-pcm.h
@@ -12,18 +12,30 @@
#ifndef _DAVINCI_PCM_H
#define _DAVINCI_PCM_H
-struct davinci_pcm_dma_params {
- char *name; /* stream identifier */
- int channel; /* sync dma channel ID */
- dma_addr_t dma_addr; /* device physical address for DMA */
- unsigned int data_type; /* xfer data type */
-};
+#include <linux/genalloc.h>
+#include <linux/platform_data/davinci_asp.h>
+#include <linux/platform_data/edma.h>
-struct evm_snd_platform_data {
- int tx_dma_ch;
- int rx_dma_ch;
+struct davinci_pcm_dma_params {
+ int channel; /* sync dma channel ID */
+ unsigned short acnt;
+ dma_addr_t dma_addr; /* device physical address for DMA */
+ unsigned sram_size;
+ struct gen_pool *sram_pool; /* SRAM gen_pool for ping pong */
+ enum dma_event_q asp_chan_q; /* event queue number for ASP channel */
+ enum dma_event_q ram_chan_q; /* event queue number for RAM channel */
+ unsigned char data_type; /* xfer data type */
+ unsigned char convert_mono_stereo;
+ unsigned int fifo_level;
};
-extern struct snd_soc_platform davinci_soc_platform;
+#if IS_ENABLED(CONFIG_SND_DAVINCI_SOC)
+int davinci_soc_platform_register(struct device *dev);
+#else
+static inline int davinci_soc_platform_register(struct device *dev)
+{
+ return 0;
+}
+#endif /* CONFIG_SND_DAVINCI_SOC */
#endif
diff --git a/sound/soc/davinci/davinci-sffsdr.c b/sound/soc/davinci/davinci-sffsdr.c
deleted file mode 100644
index 40eccfe9e35..00000000000
--- a/sound/soc/davinci/davinci-sffsdr.c
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * ASoC driver for Lyrtech SFFSDR board.
- *
- * Author: Hugo Villeneuve
- * Copyright (C) 2008 Lyrtech inc
- *
- * Based on ASoC driver for TI DAVINCI EVM platform, original copyright follow:
- * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-
-#include <asm/dma.h>
-#include <asm/mach-types.h>
-#ifdef CONFIG_SFFSDR_FPGA
-#include <asm/plat-sffsdr/sffsdr-fpga.h>
-#endif
-
-#include <mach/mcbsp.h>
-#include <mach/edma.h>
-
-#include "../codecs/pcm3008.h"
-#include "davinci-pcm.h"
-#include "davinci-i2s.h"
-
-/*
- * CLKX and CLKR are the inputs for the Sample Rate Generator.
- * FSX and FSR are outputs, driven by the sample Rate Generator.
- */
-#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
- SND_SOC_DAIFMT_CBM_CFS | \
- SND_SOC_DAIFMT_IB_NF)
-
-static int sffsdr_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;
- int fs;
- int ret = 0;
-
- /* Fsref can be 32000, 44100 or 48000. */
- fs = params_rate(params);
-
-#ifndef CONFIG_SFFSDR_FPGA
- /* Without the FPGA module, the Fs is fixed at 44100 Hz */
- if (fs != 44100) {
- pr_debug("warning: only 44.1 kHz is supported without SFFSDR FPGA module\n");
- return -EINVAL;
- }
-#endif
-
- /* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
- if (ret < 0)
- return ret;
-
- pr_debug("sffsdr_hw_params: rate = %d Hz\n", fs);
-
-#ifndef CONFIG_SFFSDR_FPGA
- return 0;
-#else
- return sffsdr_fpga_set_codec_fs(fs);
-#endif
-}
-
-static struct snd_soc_ops sffsdr_ops = {
- .hw_params = sffsdr_hw_params,
-};
-
-/* davinci-sffsdr digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link sffsdr_dai = {
- .name = "PCM3008", /* Codec name */
- .stream_name = "PCM3008 HiFi",
- .cpu_dai = &davinci_i2s_dai,
- .codec_dai = &pcm3008_dai,
- .ops = &sffsdr_ops,
-};
-
-/* davinci-sffsdr audio machine driver */
-static struct snd_soc_card snd_soc_sffsdr = {
- .name = "DaVinci SFFSDR",
- .platform = &davinci_soc_platform,
- .dai_link = &sffsdr_dai,
- .num_links = 1,
-};
-
-/* sffsdr audio private data */
-static struct pcm3008_setup_data sffsdr_pcm3008_setup = {
- .dem0_pin = GPIO(45),
- .dem1_pin = GPIO(46),
- .pdad_pin = GPIO(47),
- .pdda_pin = GPIO(38),
-};
-
-/* sffsdr audio subsystem */
-static struct snd_soc_device sffsdr_snd_devdata = {
- .card = &snd_soc_sffsdr,
- .codec_dev = &soc_codec_dev_pcm3008,
- .codec_data = &sffsdr_pcm3008_setup,
-};
-
-static struct resource sffsdr_snd_resources[] = {
- {
- .start = DAVINCI_MCBSP_BASE,
- .end = DAVINCI_MCBSP_BASE + SZ_8K - 1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct evm_snd_platform_data sffsdr_snd_data = {
- .tx_dma_ch = DAVINCI_DMA_MCBSP_TX,
- .rx_dma_ch = DAVINCI_DMA_MCBSP_RX,
-};
-
-static struct platform_device *sffsdr_snd_device;
-
-static int __init sffsdr_init(void)
-{
- int ret;
-
- if (!machine_is_sffsdr())
- return -EINVAL;
-
- sffsdr_snd_device = platform_device_alloc("soc-audio", 0);
- if (!sffsdr_snd_device) {
- printk(KERN_ERR "platform device allocation failed\n");
- return -ENOMEM;
- }
-
- platform_set_drvdata(sffsdr_snd_device, &sffsdr_snd_devdata);
- sffsdr_snd_devdata.dev = &sffsdr_snd_device->dev;
- platform_device_add_data(sffsdr_snd_device, &sffsdr_snd_data,
- sizeof(sffsdr_snd_data));
-
- ret = platform_device_add_resources(sffsdr_snd_device,
- sffsdr_snd_resources,
- ARRAY_SIZE(sffsdr_snd_resources));
- if (ret) {
- printk(KERN_ERR "platform device add ressources failed\n");
- goto error;
- }
-
- ret = platform_device_add(sffsdr_snd_device);
- if (ret)
- goto error;
-
- return ret;
-
-error:
- platform_device_put(sffsdr_snd_device);
- return ret;
-}
-
-static void __exit sffsdr_exit(void)
-{
- platform_device_unregister(sffsdr_snd_device);
-}
-
-module_init(sffsdr_init);
-module_exit(sffsdr_exit);
-
-MODULE_AUTHOR("Hugo Villeneuve");
-MODULE_DESCRIPTION("Lyrtech SFFSDR ASoC driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c
new file mode 100644
index 00000000000..77aef05588c
--- /dev/null
+++ b/sound/soc/davinci/davinci-vcif.c
@@ -0,0 +1,278 @@
+/*
+ * ALSA SoC Voice Codec Interface for TI DAVINCI processor
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/mfd/davinci_voicecodec.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "davinci-pcm.h"
+#include "davinci-i2s.h"
+
+#define MOD_REG_BIT(val, mask, set) do { \
+ if (set) { \
+ val |= mask; \
+ } else { \
+ val &= ~mask; \
+ } \
+} while (0)
+
+struct davinci_vcif_dev {
+ struct davinci_vc *davinci_vc;
+ struct davinci_pcm_dma_params dma_params[2];
+};
+
+static void davinci_vcif_start(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct davinci_vcif_dev *davinci_vcif_dev =
+ snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
+ u32 w;
+
+ /* Start the sample generator and enable transmitter/receiver */
+ w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0);
+ else
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0);
+
+ writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
+}
+
+static void davinci_vcif_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct davinci_vcif_dev *davinci_vcif_dev =
+ snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
+ u32 w;
+
+ /* Reset transmitter/receiver and sample rate/frame sync generators */
+ w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1);
+ else
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1);
+
+ writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
+}
+
+static int davinci_vcif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct davinci_vcif_dev *davinci_vcif_dev = snd_soc_dai_get_drvdata(dai);
+ struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
+ struct davinci_pcm_dma_params *dma_params =
+ &davinci_vcif_dev->dma_params[substream->stream];
+ u32 w;
+
+ /* Restart the codec before setup */
+ davinci_vcif_stop(substream);
+ davinci_vcif_start(substream);
+
+ /* General line settings */
+ writel(DAVINCI_VC_CTRL_MASK, davinci_vc->base + DAVINCI_VC_CTRL);
+
+ writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTCLR);
+
+ writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTEN);
+
+ w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
+
+ /* Determine xfer data type */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ dma_params->data_type = 0;
+
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
+ DAVINCI_VC_CTRL_RD_UNSIGNED |
+ DAVINCI_VC_CTRL_WD_BITS_8 |
+ DAVINCI_VC_CTRL_WD_UNSIGNED, 1);
+ break;
+ case SNDRV_PCM_FORMAT_S8:
+ dma_params->data_type = 1;
+
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
+ DAVINCI_VC_CTRL_WD_BITS_8, 1);
+
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_UNSIGNED |
+ DAVINCI_VC_CTRL_WD_UNSIGNED, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dma_params->data_type = 2;
+
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
+ DAVINCI_VC_CTRL_RD_UNSIGNED |
+ DAVINCI_VC_CTRL_WD_BITS_8 |
+ DAVINCI_VC_CTRL_WD_UNSIGNED, 0);
+ break;
+ default:
+ printk(KERN_WARNING "davinci-vcif: unsupported PCM format");
+ return -EINVAL;
+ }
+
+ dma_params->acnt = dma_params->data_type;
+
+ writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
+
+ return 0;
+}
+
+static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ davinci_vcif_start(substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ davinci_vcif_stop(substream);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int davinci_vcif_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_set_dma_data(dai, substream, dev->dma_params);
+ return 0;
+}
+
+#define DAVINCI_VCIF_RATES SNDRV_PCM_RATE_8000_48000
+
+static const struct snd_soc_dai_ops davinci_vcif_dai_ops = {
+ .startup = davinci_vcif_startup,
+ .trigger = davinci_vcif_trigger,
+ .hw_params = davinci_vcif_hw_params,
+};
+
+static struct snd_soc_dai_driver davinci_vcif_dai = {
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = DAVINCI_VCIF_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = DAVINCI_VCIF_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &davinci_vcif_dai_ops,
+
+};
+
+static const struct snd_soc_component_driver davinci_vcif_component = {
+ .name = "davinci-vcif",
+};
+
+static int davinci_vcif_probe(struct platform_device *pdev)
+{
+ struct davinci_vc *davinci_vc = pdev->dev.platform_data;
+ struct davinci_vcif_dev *davinci_vcif_dev;
+ int ret;
+
+ davinci_vcif_dev = devm_kzalloc(&pdev->dev,
+ sizeof(struct davinci_vcif_dev),
+ GFP_KERNEL);
+ if (!davinci_vcif_dev) {
+ dev_dbg(&pdev->dev,
+ "could not allocate memory for private data\n");
+ return -ENOMEM;
+ }
+
+ /* DMA tx params */
+ davinci_vcif_dev->davinci_vc = davinci_vc;
+ davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel =
+ davinci_vc->davinci_vcif.dma_tx_channel;
+ davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr =
+ davinci_vc->davinci_vcif.dma_tx_addr;
+
+ /* DMA rx params */
+ davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel =
+ davinci_vc->davinci_vcif.dma_rx_channel;
+ davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr =
+ davinci_vc->davinci_vcif.dma_rx_addr;
+
+ dev_set_drvdata(&pdev->dev, davinci_vcif_dev);
+
+ ret = snd_soc_register_component(&pdev->dev, &davinci_vcif_component,
+ &davinci_vcif_dai, 1);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "could not register dai\n");
+ return ret;
+ }
+
+ ret = davinci_soc_platform_register(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+ snd_soc_unregister_component(&pdev->dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int davinci_vcif_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver davinci_vcif_driver = {
+ .probe = davinci_vcif_probe,
+ .remove = davinci_vcif_remove,
+ .driver = {
+ .name = "davinci-vcif",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(davinci_vcif_driver);
+
+MODULE_AUTHOR("Miguel Aguilar");
+MODULE_DESCRIPTION("Texas Instruments DaVinci ASoC Voice Codec Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/davinci/edma-pcm.c b/sound/soc/davinci/edma-pcm.c
new file mode 100644
index 00000000000..d38afb1c61a
--- /dev/null
+++ b/sound/soc/davinci/edma-pcm.c
@@ -0,0 +1,57 @@
+/*
+ * edma-pcm.c - eDMA PCM driver using dmaengine for AM3xxx, AM4xxx
+ *
+ * Copyright (C) 2014 Texas Instruments, Inc.
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Based on: sound/soc/tegra/tegra_pcm.c
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+#include <linux/edma.h>
+
+static const struct snd_pcm_hardware edma_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 64 * 1024,
+ .periods_min = 2,
+ .periods_max = 19, /* Limit by edma dmaengine driver */
+};
+
+static const struct snd_dmaengine_pcm_config edma_dmaengine_pcm_config = {
+ .pcm_hardware = &edma_pcm_hardware,
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ .compat_filter_fn = edma_filter_fn,
+ .prealloc_buffer_size = 128 * 1024,
+};
+
+int edma_pcm_platform_register(struct device *dev)
+{
+ return devm_snd_dmaengine_pcm_register(dev, &edma_dmaengine_pcm_config,
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
+}
+EXPORT_SYMBOL_GPL(edma_pcm_platform_register);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_DESCRIPTION("eDMA PCM ASoC platform driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/davinci/edma-pcm.h b/sound/soc/davinci/edma-pcm.h
new file mode 100644
index 00000000000..894c378c0f7
--- /dev/null
+++ b/sound/soc/davinci/edma-pcm.h
@@ -0,0 +1,25 @@
+/*
+ * edma-pcm.h - eDMA PCM driver using dmaengine for AM3xxx, AM4xxx
+ *
+ * Copyright (C) 2014 Texas Instruments, Inc.
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Based on: sound/soc/tegra/tegra_pcm.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.
+ *
+ * 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.
+ */
+
+#ifndef __EDMA_PCM_H__
+#define __EDMA_PCM_H__
+
+int edma_pcm_platform_register(struct device *dev);
+
+#endif /* __EDMA_PCM_H__ */