diff options
Diffstat (limited to 'sound/soc/davinci')
| -rw-r--r-- | sound/soc/davinci/Kconfig | 94 | ||||
| -rw-r--r-- | sound/soc/davinci/Makefile | 10 | ||||
| -rw-r--r-- | sound/soc/davinci/davinci-evm.c | 487 | ||||
| -rw-r--r-- | sound/soc/davinci/davinci-i2s.c | 647 | ||||
| -rw-r--r-- | sound/soc/davinci/davinci-i2s.h | 5 | ||||
| -rw-r--r-- | sound/soc/davinci/davinci-mcasp.c | 1276 | ||||
| -rw-r--r-- | sound/soc/davinci/davinci-mcasp.h | 289 | ||||
| -rw-r--r-- | sound/soc/davinci/davinci-pcm.c | 661 | ||||
| -rw-r--r-- | sound/soc/davinci/davinci-pcm.h | 32 | ||||
| -rw-r--r-- | sound/soc/davinci/davinci-sffsdr.c | 178 | ||||
| -rw-r--r-- | sound/soc/davinci/davinci-vcif.c | 278 | ||||
| -rw-r--r-- | sound/soc/davinci/edma-pcm.c | 57 | ||||
| -rw-r--r-- | sound/soc/davinci/edma-pcm.h | 25 |
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__ */ |
