Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

99
sound/soc/davinci/Kconfig Normal file
View file

@ -0,0 +1,99 @@
config SND_DAVINCI_SOC
tristate "SoC Audio for TI DAVINCI"
depends on ARCH_DAVINCI
config SND_EDMA_SOC
tristate "SoC Audio for Texas Instruments chips using eDMA (AM33XX/43XX)"
depends on SOC_AM33XX || SOC_AM43XX
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M here if you want audio support for TI SoC which uses eDMA.
The following line of SoCs are supported by this platform driver:
- AM335x
- AM437x/AM438x
config SND_DAVINCI_SOC_I2S
tristate
config SND_DAVINCI_SOC_MCASP
tristate "Multichannel Audio Serial Port (McASP) support"
depends on SND_DAVINCI_SOC || SND_OMAP_SOC || SND_EDMA_SOC
help
Say Y or M here if you want to have support for McASP IP found in
various Texas Instruments SoCs like:
- daVinci devices
- Sitara line of SoCs (AM335x, AM438x, etc)
- DRA7x devices
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_EDMA_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, 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
tristate "Audio Codec - AIC3101"
help
Say Y if you want to add support for AIC3101 audio codec
config SND_DM365_VOICE_CODEC
tristate "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

View file

@ -0,0 +1,17 @@
# DAVINCI Platform Support
snd-soc-davinci-objs := davinci-pcm.o
snd-soc-edma-objs := edma-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_EDMA_SOC) += snd-soc-edma.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
# Generic DAVINCI/AM33xx Machine Support
snd-soc-evm-objs := davinci-evm.o
obj-$(CONFIG_SND_DAVINCI_SOC_GENERIC_EVM) += snd-soc-evm.o

View file

@ -0,0 +1,512 @@
/*
* ASoC driver for TI DAVINCI EVM platform
*
* Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* 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/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 <asm/dma.h>
#include <asm/mach-types.h>
#include <linux/edma.h>
#include "davinci-pcm.h"
#include "davinci-i2s.h"
struct snd_soc_card_drvdata_davinci {
struct clk *mclk;
unsigned sysclk;
};
static int evm_startup(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);
if (drvdata->mclk)
return clk_prepare_enable(drvdata->mclk);
return 0;
}
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);
if (drvdata->mclk)
clk_disable_unprepare(drvdata->mclk);
}
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,
};
/* davinci-evm machine dapm widgets */
static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_LINE("Line Out", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_LINE("Line In", NULL),
};
/* davinci-evm machine audio_mapnections to the codec pins */
static const struct snd_soc_dapm_route audio_map[] = {
/* Headphone connected to HPLOUT, HPROUT */
{"Headphone Jack", NULL, "HPLOUT"},
{"Headphone Jack", NULL, "HPROUT"},
/* Line Out connected to LLOUT, RLOUT */
{"Line Out", NULL, "LLOUT"},
{"Line Out", NULL, "RLOUT"},
/* Mic connected to (MIC3L | MIC3R) */
{"MIC3L", NULL, "Mic Bias"},
{"MIC3R", NULL, "Mic Bias"},
{"Mic Bias", NULL, "Mic Jack"},
/* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */
{"LINE1L", NULL, "Line In"},
{"LINE2L", NULL, "Line In"},
{"LINE1R", NULL, "Line In"},
{"LINE2R", NULL, "Line In"},
};
/* Logic for a aic3x as connected on a davinci-evm */
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(&card->dapm, aic3x_dapm_widgets,
ARRAY_SIZE(aic3x_dapm_widgets));
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_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 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_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 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,
};
/* 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,
};
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,
};
/* 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,
};
/* 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,
};
static const struct of_device_id davinci_evm_dt_ids[] = {
{
.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 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 struct platform_driver davinci_evm_driver = {
.probe = davinci_evm_probe,
.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 snd_soc_card *evm_snd_dev_data;
int index;
int ret;
/*
* 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
if (machine_is_davinci_evm()) {
evm_snd_dev_data = &dm6446_snd_soc_card_evm;
index = 0;
} else if (machine_is_davinci_dm355_evm()) {
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;
evm_snd_device = platform_device_alloc("soc-audio", index);
if (!evm_snd_device)
return -ENOMEM;
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);
return ret;
}
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);
}
module_init(evm_init);
module_exit(evm_exit);
MODULE_AUTHOR("Vladimir Barinov");
MODULE_DESCRIPTION("TI DAVINCI EVM ASoC driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,781 @@
/*
* ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
*
* Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* 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/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>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include "davinci-pcm.h"
#include "davinci-i2s.h"
/*
* NOTE: terminology here is confusing.
*
* - This driver supports the "Audio Serial Port" (ASP),
* found on dm6446, dm355, and other DaVinci chips.
*
* - But it labels it a "Multi-channel Buffered Serial Port"
* (McBSP) as on older chips like the dm642 ... which was
* backward-compatible, possibly explaining that confusion.
*
* - OMAP chips have a controller called McBSP, which is
* incompatible with the DaVinci flavor of McBSP.
*
* - Newer DaVinci chips have a controller called McASP,
* incompatible with ASP and with either McBSP.
*
* In short: this uses ASP to implement I2S, not McBSP.
* And it won't be the only DaVinci implemention of I2S.
*/
#define DAVINCI_MCBSP_DRR_REG 0x00
#define DAVINCI_MCBSP_DXR_REG 0x04
#define DAVINCI_MCBSP_SPCR_REG 0x08
#define DAVINCI_MCBSP_RCR_REG 0x0c
#define DAVINCI_MCBSP_XCR_REG 0x10
#define DAVINCI_MCBSP_SRGR_REG 0x14
#define DAVINCI_MCBSP_PCR_REG 0x24
#define DAVINCI_MCBSP_SPCR_RRST (1 << 0)
#define DAVINCI_MCBSP_SPCR_RINTM(v) ((v) << 4)
#define DAVINCI_MCBSP_SPCR_XRST (1 << 16)
#define DAVINCI_MCBSP_SPCR_XINTM(v) ((v) << 20)
#define DAVINCI_MCBSP_SPCR_GRST (1 << 22)
#define DAVINCI_MCBSP_SPCR_FRST (1 << 23)
#define DAVINCI_MCBSP_SPCR_FREE (1 << 25)
#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)
#define DAVINCI_MCBSP_PCR_FSRP (1 << 2)
#define DAVINCI_MCBSP_PCR_FSXP (1 << 3)
#define DAVINCI_MCBSP_PCR_SCLKME (1 << 7)
#define DAVINCI_MCBSP_PCR_CLKRM (1 << 8)
#define DAVINCI_MCBSP_PCR_CLKXM (1 << 9)
#define DAVINCI_MCBSP_PCR_FSRM (1 << 10)
#define DAVINCI_MCBSP_PCR_FSXM (1 << 11)
enum {
DAVINCI_MCBSP_WORD_8 = 0,
DAVINCI_MCBSP_WORD_12,
DAVINCI_MCBSP_WORD_16,
DAVINCI_MCBSP_WORD_20,
DAVINCI_MCBSP_WORD_24,
DAVINCI_MCBSP_WORD_32,
};
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 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;
/*
* 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,
int reg, u32 val)
{
__raw_writel(val, dev->base + reg);
}
static inline u32 davinci_mcbsp_read_reg(struct davinci_mcbsp_dev *dev, int reg)
{
return __raw_readl(dev->base + reg);
}
static void toggle_clock(struct davinci_mcbsp_dev *dev, int playback)
{
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);
}
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 (playback) {
/* Stop the DMA to avoid data loss */
/* while the transmitter is out of reset to handle XSYNCERR */
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 */
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 */
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->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 transmitter or receiver */
spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
spcr |= mask;
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 davinci_mcbsp_dev *dev, int playback)
{
u32 spcr;
/* Reset transmitter/receiver and sample rate/frame sync generators */
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
static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
unsigned int pcr;
unsigned int srgr;
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 */
pcr = DAVINCI_MCBSP_PCR_FSXM |
DAVINCI_MCBSP_PCR_FSRM |
DAVINCI_MCBSP_PCR_CLKXM |
DAVINCI_MCBSP_PCR_CLKRM;
break;
case SND_SOC_DAIFMT_CBM_CFS:
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 */
pcr = 0;
break;
default:
printk(KERN_ERR "%s:bad master\n", __func__);
return -EINVAL;
}
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
/* Davinci doesn't support TRUE I2S, but some codecs will have
* the left and right channels contiguous. This allows
* dsp_a mode to be used with an inverted normal frame clk.
* If your codec is master and does not have contiguous
* channels, then you will have sound on only one channel.
* Try using a different mode, or codec as slave.
*
* The TLV320AIC33 is an example of a codec where this works.
* It has a variable bit clock frequency allowing it to have
* valid data on every bit clock.
*
* The TLV320AIC23 is an example of a codec where this does not
* work. It has a fixed bit clock frequency with progressively
* more empty bit clock slots between channels as the sample
* rate is lowered.
*/
inv_fs = true;
case SND_SOC_DAIFMT_DSP_A:
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__);
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
/* CLKRP Receive clock polarity,
* 1 - sampled on rising edge of CLKR
* valid on rising edge
* CLKXP Transmit clock polarity,
* 1 - clocked on falling edge of CLKX
* valid on rising edge
* FSRP Receive frame sync pol, 0 - active high
* FSXP Transmit frame sync pol, 0 - active high
*/
pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP);
break;
case SND_SOC_DAIFMT_IB_IF:
/* CLKRP Receive clock polarity,
* 0 - sampled on falling edge of CLKR
* valid on falling edge
* CLKXP Transmit clock polarity,
* 0 - clocked on rising edge of CLKX
* valid on falling edge
* FSRP Receive frame sync pol, 1 - active low
* FSXP Transmit frame sync pol, 1 - active low
*/
pcr |= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
break;
case SND_SOC_DAIFMT_NB_IF:
/* CLKRP Receive clock polarity,
* 1 - sampled on rising edge of CLKR
* valid on rising edge
* CLKXP Transmit clock polarity,
* 1 - clocked on falling edge of CLKX
* valid on rising edge
* FSRP Receive frame sync pol, 1 - active low
* FSXP Transmit frame sync pol, 1 - active low
*/
pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP |
DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
break;
case SND_SOC_DAIFMT_IB_NF:
/* CLKRP Receive clock polarity,
* 0 - sampled on falling edge of CLKR
* valid on falling edge
* CLKXP Transmit clock polarity,
* 0 - clocked on rising edge of CLKX
* valid on falling edge
* FSRP Receive frame sync pol, 0 - active high
* FSXP Transmit frame sync pol, 0 - active high
*/
break;
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);
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;
}
static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
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, master;
unsigned int rcr, xcr, srgr, clk_div, freq, framesize;
u32 spcr;
snd_pcm_format_t fmt;
unsigned element_cnt = 1;
/* general line settings */
spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
spcr |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
} else {
spcr |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
}
master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
fmt = params_format(params);
mcbsp_word_length = asp_word_length[fmt];
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 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 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:
return -EINVAL;
}
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
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 {
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(dev, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
davinci_mcbsp_stop(dev, playback);
break;
default:
ret = -EINVAL;
}
return ret;
}
static int davinci_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *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, *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);
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, "McBSP region already claimed\n");
return -EBUSY;
}
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;
}
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))
return -ENODEV;
clk_enable(dev->clk);
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].dma_addr =
(dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr =
(dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG);
/* 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);
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 int davinci_i2s_remove(struct platform_device *pdev)
{
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;
return 0;
}
static struct platform_driver davinci_mcbsp_driver = {
.probe = davinci_i2s_probe,
.remove = davinci_i2s_remove,
.driver = {
.name = "davinci-mcbsp",
.owner = THIS_MODULE,
},
};
module_platform_driver(davinci_mcbsp_driver);
MODULE_AUTHOR("Vladimir Barinov");
MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,20 @@
/*
* ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
*
* Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* 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.
*/
#ifndef _DAVINCI_I2S_H
#define _DAVINCI_I2S_H
/* McBSP dividers */
enum davinci_mcbsp_div {
DAVINCI_MCBSP_CLKGDV, /* Sample rate generator divider */
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -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 */

View file

@ -0,0 +1,861 @@
/*
* ALSA PCM interface for the TI DAVINCI processor
*
* 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
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#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>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <asm/dma.h>
#include "davinci-pcm.h"
#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 |
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,
};
/*
* 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 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;
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", 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 = 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;
}
acnt = prtd->params->acnt;
edma_set_src(prtd->asp_link[0], src, INCR, W8BIT);
edma_set_dest(prtd->asp_link[0], dst, INCR, W8BIT);
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 link, u16 ch_status, void *data)
{
struct snd_pcm_substream *substream = data;
struct davinci_runtime_data *prtd = substream->runtime->private_data;
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 != EDMA_DMA_COMPLETE))
return;
if (snd_pcm_running(substream)) {
spin_lock(&prtd->lock);
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 davinci_pcm_dma_params *params = prtd->params;
int ret;
if (!params)
return -ENODEV;
/* 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)
goto exit1;
/* 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__);
}
/* Issue transfer completion IRQ when the channel completes a
* transfer, then always reload from the same slot (by a kind
* of loopback link). The completion IRQ handler will update
* the reload slot with a new buffer.
*
* REVISIT save p_ram here after setting up everything except
* 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->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)
{
struct davinci_runtime_data *prtd = substream->runtime->private_data;
int ret = 0;
spin_lock(&prtd->lock);
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_resume(prtd->asp_channel);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
edma_pause(prtd->asp_channel);
break;
default:
ret = -EINVAL;
break;
}
spin_unlock(&prtd->lock);
return ret;
}
static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
{
struct davinci_runtime_data *prtd = substream->runtime->private_data;
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->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;
}
static snd_pcm_uframes_t
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;
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);
asp_count = prtd->period - 2;
spin_unlock(&prtd->lock);
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;
return offset;
}
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;
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;
ret = davinci_pcm_dma_request(substream);
if (ret) {
printk(KERN_ERR "davinci_pcm: Failed to get dma channels\n");
kfree(prtd);
}
return ret;
}
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;
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);
return 0;
}
static int davinci_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
}
static int davinci_pcm_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
static int davinci_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
return dma_mmap_writecombine(substream->pcm->card->dev, vma,
runtime->dma_area,
runtime->dma_addr,
runtime->dma_bytes);
}
static struct snd_pcm_ops davinci_pcm_ops = {
.open = davinci_pcm_open,
.close = davinci_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = davinci_pcm_hw_params,
.hw_free = davinci_pcm_hw_free,
.prepare = davinci_pcm_prepare,
.trigger = davinci_pcm_trigger,
.pointer = davinci_pcm_pointer,
.mmap = davinci_pcm_mmap,
};
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;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
buf->area = dma_alloc_writecombine(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
pr_debug("davinci_pcm: preallocate_dma_buffer: area=%p, addr=%p, "
"size=%d\n", (void *) buf->area, (void *) buf->addr, size);
if (!buf->area)
return -ENOMEM;
buf->bytes = size;
return 0;
}
static void davinci_pcm_free(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
int stream;
for (stream = 0; stream < 2; stream++) {
struct snd_dma_buffer *iram_dma;
substream = pcm->streams[stream].substream;
if (!substream)
continue;
buf = &substream->dma_buffer;
if (!buf->area)
continue;
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 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;
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
ret = davinci_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK,
pcm_hardware_playback.buffer_bytes_max);
if (ret)
return ret;
}
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
ret = davinci_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE,
pcm_hardware_capture.buffer_bytes_max);
if (ret)
return ret;
}
return 0;
}
static struct snd_soc_platform_driver davinci_soc_platform = {
.ops = &davinci_pcm_ops,
.pcm_new = davinci_pcm_new,
.pcm_free = davinci_pcm_free,
};
int davinci_soc_platform_register(struct device *dev)
{
return devm_snd_soc_register_platform(dev, &davinci_soc_platform);
}
EXPORT_SYMBOL_GPL(davinci_soc_platform_register);
MODULE_AUTHOR("Vladimir Barinov");
MODULE_DESCRIPTION("TI DAVINCI PCM DMA module");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,41 @@
/*
* ALSA PCM interface for the TI DAVINCI processor
*
* Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
* 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.
*/
#ifndef _DAVINCI_PCM_H
#define _DAVINCI_PCM_H
#include <linux/genalloc.h>
#include <linux/platform_data/davinci_asp.h>
#include <linux/platform_data/edma.h>
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;
};
#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

View file

@ -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");

View file

@ -0,0 +1,59 @@
/*
* 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>
#include "edma-pcm.h"
static const struct snd_pcm_hardware edma_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
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");

View file

@ -0,0 +1,32 @@
/*
* 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__
#if IS_ENABLED(CONFIG_SND_EDMA_SOC)
int edma_pcm_platform_register(struct device *dev);
#else
static inline int edma_pcm_platform_register(struct device *dev)
{
return 0;
}
#endif /* CONFIG_SND_EDMA_SOC */
#endif /* __EDMA_PCM_H__ */