add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
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.
config SND_DAVINCI_SOC_I2S
tristate
config SND_DAVINCI_SOC_MCASP
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
select SND_SOC_TLV320AIC3X
help
Say Y if you want to add support for SoC audio on TI
DaVinci DM6446 or DM355 EVM platforms.
config SND_DM6467_SOC_EVM
tristate "SoC Audio support for DaVinci DM6467 EVM"
depends on SND_DAVINCI_SOC && MACH_DAVINCI_DM6467_EVM
select SND_DAVINCI_SOC_MCASP
select SND_SOC_TLV320AIC3X
select SND_SOC_SPDIF
help
Say Y if you want to add support for SoC audio on TI
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
help
Say Y if you want to add support for SoC audio on
Lyrtech SFFSDR board.
config SND_DA830_SOC_EVM
tristate "SoC Audio support for DA830/OMAP-L137 EVM"
depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA830_EVM
select SND_DAVINCI_SOC_MCASP
select SND_SOC_TLV320AIC3X
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
select SND_DAVINCI_SOC_MCASP
select SND_SOC_TLV320AIC3X
help
Say Y if you want to add support for SoC audio on TI
DA850/OMAP-L138 EVM

View File

@@ -0,0 +1,18 @@
# 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
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
# DAVINCI 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_DM6467_SOC_EVM) += snd-soc-evm.o
obj-$(CONFIG_SND_DA830_SOC_EVM) += snd-soc-evm.o
obj-$(CONFIG_SND_DA850_SOC_EVM) += snd-soc-evm.o
obj-$(CONFIG_SND_DAVINCI_SOC_SFFSDR) += snd-soc-sffsdr.o

View File

@@ -0,0 +1,287 @@
/*
* 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/i2c.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 "../codecs/tlv320aic3x.h"
#include "../codecs/spdif_transciever.h"
#include "davinci-pcm.h"
#include "davinci-i2s.h"
#include "davinci-mcasp.h"
#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)
{
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;
/* ASP1 on DM355 EVM is clocked by an external oscillator */
if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm())
sysclk = 27000000;
/* 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;
else if (machine_is_davinci_da830_evm() ||
machine_is_davinci_da850_evm())
sysclk = 24576000;
else
return -EINVAL;
/* set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
if (ret < 0)
return ret;
/* set the codec system clock */
ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops evm_ops = {
.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 2V"},
{"MIC3R", NULL, "Mic Bias 2V"},
{"Mic Bias 2V", NULL, "Mic Jack"},
/* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */
{"LINE1L", NULL, "Line In"},
{"LINE2L", NULL, "Line In"},
{"LINE1R", NULL, "Line In"},
{"LINE2R", NULL, "Line In"},
};
/* Logic for a aic3x as connected on a davinci-evm */
static int evm_aic3x_init(struct snd_soc_codec *codec)
{
/* Add davinci-evm specific widgets */
snd_soc_dapm_new_controls(codec, 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));
/* 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);
return 0;
}
/* davinci-evm digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link evm_dai = {
.name = "TLV320AIC3X",
.stream_name = "AIC3X",
.cpu_dai = &davinci_i2s_dai,
.codec_dai = &aic3x_dai,
.init = evm_aic3x_init,
.ops = &evm_ops,
};
static struct snd_soc_dai_link dm6467_evm_dai[] = {
{
.name = "TLV320AIC3X",
.stream_name = "AIC3X",
.cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_I2S_DAI],
.codec_dai = &aic3x_dai,
.init = evm_aic3x_init,
.ops = &evm_ops,
},
{
.name = "McASP",
.stream_name = "spdif",
.cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_DIT_DAI],
.codec_dai = &dit_stub_dai,
.ops = &evm_ops,
},
};
static struct snd_soc_dai_link da8xx_evm_dai = {
.name = "TLV320AIC3X",
.stream_name = "AIC3X",
.cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_I2S_DAI],
.codec_dai = &aic3x_dai,
.init = evm_aic3x_init,
.ops = &evm_ops,
};
/* 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,
.num_links = 1,
};
/* davinci dm6467 evm audio machine driver */
static struct snd_soc_card dm6467_snd_soc_card_evm = {
.name = "DaVinci DM6467 EVM",
.platform = &davinci_soc_platform,
.dai_link = dm6467_evm_dai,
.num_links = ARRAY_SIZE(dm6467_evm_dai),
};
static struct snd_soc_card da830_snd_soc_card = {
.name = "DA830/OMAP-L137 EVM",
.dai_link = &da8xx_evm_dai,
.platform = &davinci_soc_platform,
.num_links = 1,
};
static struct snd_soc_card da850_snd_soc_card = {
.name = "DA850/OMAP-L138 EVM",
.dai_link = &da8xx_evm_dai,
.platform = &davinci_soc_platform,
.num_links = 1,
};
static struct aic3x_setup_data aic3x_setup;
/* evm audio subsystem */
static struct snd_soc_device evm_snd_devdata = {
.card = &snd_soc_card_evm,
.codec_dev = &soc_codec_dev_aic3x,
.codec_data = &aic3x_setup,
};
/* evm audio subsystem */
static struct snd_soc_device dm6467_evm_snd_devdata = {
.card = &dm6467_snd_soc_card_evm,
.codec_dev = &soc_codec_dev_aic3x,
.codec_data = &aic3x_setup,
};
/* evm audio subsystem */
static struct snd_soc_device da830_evm_snd_devdata = {
.card = &da830_snd_soc_card,
.codec_dev = &soc_codec_dev_aic3x,
.codec_data = &aic3x_setup,
};
static struct snd_soc_device da850_evm_snd_devdata = {
.card = &da850_snd_soc_card,
.codec_dev = &soc_codec_dev_aic3x,
.codec_data = &aic3x_setup,
};
static struct platform_device *evm_snd_device;
static int __init evm_init(void)
{
struct snd_soc_device *evm_snd_dev_data;
int index;
int ret;
if (machine_is_davinci_evm()) {
evm_snd_dev_data = &evm_snd_devdata;
index = 0;
} else if (machine_is_davinci_dm355_evm()) {
evm_snd_dev_data = &evm_snd_devdata;
index = 1;
} else if (machine_is_davinci_dm6467_evm()) {
evm_snd_dev_data = &dm6467_evm_snd_devdata;
index = 0;
} else if (machine_is_davinci_da830_evm()) {
evm_snd_dev_data = &da830_evm_snd_devdata;
index = 1;
} else if (machine_is_davinci_da850_evm()) {
evm_snd_dev_data = &da850_evm_snd_devdata;
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);
evm_snd_dev_data->dev = &evm_snd_device->dev;
ret = platform_device_add(evm_snd_device);
if (ret)
platform_device_put(evm_snd_device);
return ret;
}
static void __exit evm_exit(void)
{
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,603 @@
/*
* 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/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <mach/asp.h>
#include "davinci-pcm.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_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_SRGR_FWID(v) ((v) << 8)
#define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16)
#define DAVINCI_MCBSP_SRGR_FSGM (1 << 28)
#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,
};
struct davinci_mcbsp_dev {
/*
* dma_params must be first because rtd->dai->cpu_dai->private_data
* is cast to a pointer of an array of struct davinci_pcm_dma_params in
* davinci_pcm_open.
*/
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;
};
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_device *socdev = rtd->socdev;
struct snd_soc_platform *platform = socdev->card->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->pcm_ops->trigger) {
int ret = platform->pcm_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->pcm_ops->trigger) {
int ret = platform->pcm_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 = cpu_dai->private_data;
unsigned int pcr;
unsigned int srgr;
srgr = DAVINCI_MCBSP_SRGR_FSGM |
DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
/* 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:
/* 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;
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.
*/
fmt ^= SND_SOC_DAIFMT_NB_IF;
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;
}
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_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct davinci_mcbsp_dev *dev = dai->private_data;
struct davinci_pcm_dma_params *dma_params =
&dev->dma_params[substream->stream];
struct snd_interval *i = NULL;
int mcbsp_word_length;
unsigned int rcr, xcr, srgr;
u32 spcr;
/* 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);
}
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);
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
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 */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
dma_params->data_type = 1;
mcbsp_word_length = DAVINCI_MCBSP_WORD_8;
break;
case SNDRV_PCM_FORMAT_S16_LE:
dma_params->data_type = 2;
mcbsp_word_length = DAVINCI_MCBSP_WORD_16;
break;
case SNDRV_PCM_FORMAT_S32_LE:
dma_params->data_type = 4;
mcbsp_word_length = DAVINCI_MCBSP_WORD_32;
break;
default:
printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
return -EINVAL;
}
dma_params->acnt = dma_params->data_type;
rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1);
xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1);
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);
return 0;
}
static int davinci_i2s_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct davinci_mcbsp_dev *dev = dai->private_data;
int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
davinci_mcbsp_stop(dev, playback);
if ((dev->pcr & DAVINCI_MCBSP_PCR_FSXM) == 0) {
/* codec is master */
davinci_mcbsp_start(dev, substream);
}
return 0;
}
static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct davinci_mcbsp_dev *dev = dai->private_data;
int ret = 0;
int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
if ((dev->pcr & DAVINCI_MCBSP_PCR_FSXM) == 0)
return 0; /* return if codec is master */
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 void davinci_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct davinci_mcbsp_dev *dev = dai->private_data;
int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
davinci_mcbsp_stop(dev, playback);
}
#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
.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,
};
struct snd_soc_dai davinci_i2s_dai = {
.name = "davinci-i2s",
.id = 0,
.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,
};
EXPORT_SYMBOL_GPL(davinci_i2s_dai);
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;
int ret;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(&pdev->dev, "no mem resource?\n");
return -ENODEV;
}
ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
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->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
ret = -ENODEV;
goto err_free_mem;
}
clk_enable(dev->clk);
dev->base = (void __iomem *)IO_ADDRESS(mem->start);
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_CAPTURE].dma_addr =
(dma_addr_t)(io_v2p(dev->base) + 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_free_mem;
}
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_free_mem;
}
dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start;
davinci_i2s_dai.private_data = dev;
ret = snd_soc_register_dai(&davinci_i2s_dai);
if (ret != 0)
goto err_free_mem;
return 0;
err_free_mem:
kfree(dev);
err_release_region:
release_mem_region(mem->start, (mem->end - mem->start) + 1);
return ret;
}
static int davinci_i2s_remove(struct platform_device *pdev)
{
struct davinci_mcbsp_dev *dev = davinci_i2s_dai.private_data;
struct resource *mem;
snd_soc_unregister_dai(&davinci_i2s_dai);
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;
}
static struct platform_driver davinci_mcbsp_driver = {
.probe = davinci_i2s_probe,
.remove = davinci_i2s_remove,
.driver = {
.name = "davinci-asp",
.owner = THIS_MODULE,
},
};
static int __init davinci_i2s_init(void)
{
return platform_driver_register(&davinci_mcbsp_driver);
}
module_init(davinci_i2s_init);
static void __exit davinci_i2s_exit(void)
{
platform_driver_unregister(&davinci_mcbsp_driver);
}
module_exit(davinci_i2s_exit);
MODULE_AUTHOR("Vladimir Barinov");
MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,17 @@
/*
* 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
extern struct snd_soc_dai davinci_i2s_dai;
#endif

View File

@@ -0,0 +1,969 @@
/*
* 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/delay.h>
#include <linux/io.h>
#include <linux/clk.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-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_WFIFOCTL (0x1010)
#define DAVINCI_MCASP_WFIFOSTS (0x1014)
#define DAVINCI_MCASP_RFIFOCTL (0x1018)
#define DAVINCI_MCASP_RFIFOSTS (0x101C)
/*
* 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)
/*
* 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)
/*
* DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control
* Register Bits
*/
#define AHCLKXDIV(val) (val)
#define AHCLKXPOL BIT(14)
#define AHCLKXE BIT(15)
/*
* DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control
* Register Bits
*/
#define AHCLKRDIV(val) (val)
#define AHCLKRPOL BIT(14)
#define AHCLKRE BIT(15)
/*
* 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)
/*
* 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 NUMDMA_MASK (0xFF)
#define DAVINCI_MCASP_NUM_SERIALIZER 16
static inline void mcasp_set_bits(void __iomem *reg, u32 val)
{
__raw_writel(__raw_readl(reg) | val, reg);
}
static inline void mcasp_clr_bits(void __iomem *reg, u32 val)
{
__raw_writel((__raw_readl(reg) & ~(val)), reg);
}
static inline void mcasp_mod_bits(void __iomem *reg, u32 val, u32 mask)
{
__raw_writel((__raw_readl(reg) & ~mask) | val, reg);
}
static inline void mcasp_set_reg(void __iomem *reg, u32 val)
{
__raw_writel(val, reg);
}
static inline u32 mcasp_get_reg(void __iomem *reg)
{
return (unsigned int)__raw_readl(reg);
}
static inline void mcasp_set_ctl_reg(void __iomem *regs, u32 val)
{
int i = 0;
mcasp_set_bits(regs, 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(regs) & val) == val)
break;
}
if (i == 1000 && ((mcasp_get_reg(regs) & val) != val))
printk(KERN_ERR "GBLCTL write error\n");
}
static void mcasp_start_rx(struct davinci_audio_dev *dev)
{
mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0);
mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0);
mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
}
static void mcasp_start_tx(struct davinci_audio_dev *dev)
{
u8 offset = 0, i;
u32 cnt;
mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
for (i = 0; i < dev->num_serializer; i++) {
if (dev->serial_dir[i] == TX_MODE) {
offset = i;
break;
}
}
/* wait for TX ready */
cnt = 0;
while (!(mcasp_get_reg(dev->base + DAVINCI_MCASP_XRSRCTL_REG(offset)) &
TXSTATE) && (cnt < 100000))
cnt++;
mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
}
static void davinci_mcasp_start(struct davinci_audio_dev *dev, int stream)
{
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (dev->txnumevt) /* enable FIFO */
mcasp_set_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
FIFO_ENABLE);
mcasp_start_tx(dev);
} else {
if (dev->rxnumevt) /* enable FIFO */
mcasp_set_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
FIFO_ENABLE);
mcasp_start_rx(dev);
}
}
static void mcasp_stop_rx(struct davinci_audio_dev *dev)
{
mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, 0);
mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
}
static void mcasp_stop_tx(struct davinci_audio_dev *dev)
{
mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, 0);
mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
}
static void davinci_mcasp_stop(struct davinci_audio_dev *dev, int stream)
{
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (dev->txnumevt) /* disable FIFO */
mcasp_clr_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
FIFO_ENABLE);
mcasp_stop_tx(dev);
} else {
if (dev->rxnumevt) /* disable FIFO */
mcasp_clr_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
FIFO_ENABLE);
mcasp_stop_rx(dev);
}
}
static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct davinci_audio_dev *dev = cpu_dai->private_data;
void __iomem *base = dev->base;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
/* codec is clock and frame slave */
mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x7 << 26));
break;
case SND_SOC_DAIFMT_CBM_CFS:
/* codec is clock master and frame slave */
mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x2d << 26));
break;
case SND_SOC_DAIFMT_CBM_CFM:
/* codec is clock and frame master */
mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, (0x3f << 26));
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_IB_NF:
mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
break;
case SND_SOC_DAIFMT_NB_IF:
mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
break;
case SND_SOC_DAIFMT_IB_IF:
mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
break;
case SND_SOC_DAIFMT_NB_NF:
mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
break;
default:
return -EINVAL;
}
return 0;
}
static int davinci_config_channel_size(struct davinci_audio_dev *dev,
int channel_size)
{
u32 fmt = 0;
u32 mask, rotate;
switch (channel_size) {
case DAVINCI_AUDIO_WORD_8:
fmt = 0x03;
rotate = 6;
mask = 0x000000ff;
break;
case DAVINCI_AUDIO_WORD_12:
fmt = 0x05;
rotate = 5;
mask = 0x00000fff;
break;
case DAVINCI_AUDIO_WORD_16:
fmt = 0x07;
rotate = 4;
mask = 0x0000ffff;
break;
case DAVINCI_AUDIO_WORD_20:
fmt = 0x09;
rotate = 3;
mask = 0x000fffff;
break;
case DAVINCI_AUDIO_WORD_24:
fmt = 0x0B;
rotate = 2;
mask = 0x00ffffff;
break;
case DAVINCI_AUDIO_WORD_28:
fmt = 0x0D;
rotate = 1;
mask = 0x0fffffff;
break;
case DAVINCI_AUDIO_WORD_32:
fmt = 0x0F;
rotate = 0;
mask = 0xffffffff;
break;
default:
return -EINVAL;
}
mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG,
RXSSZ(fmt), RXSSZ(0x0F));
mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
TXSSZ(fmt), TXSSZ(0x0F));
mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXROT(rotate),
TXROT(7));
mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, RXROT(rotate),
RXROT(7));
mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, mask);
mcasp_set_reg(dev->base + DAVINCI_MCASP_RXMASK_REG, mask);
return 0;
}
static void davinci_hw_common_param(struct davinci_audio_dev *dev, int stream)
{
int i;
u8 tx_ser = 0;
u8 rx_ser = 0;
/* Default configuration */
mcasp_set_bits(dev->base + DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
/* All PINS as McASP */
mcasp_set_reg(dev->base + DAVINCI_MCASP_PFUNC_REG, 0x00000000);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG,
TXDATADMADIS);
} else {
mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
mcasp_clr_bits(dev->base + DAVINCI_MCASP_REVTCTL_REG,
RXDATADMADIS);
}
for (i = 0; i < dev->num_serializer; i++) {
mcasp_set_bits(dev->base + DAVINCI_MCASP_XRSRCTL_REG(i),
dev->serial_dir[i]);
if (dev->serial_dir[i] == TX_MODE) {
mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG,
AXR(i));
tx_ser++;
} else if (dev->serial_dir[i] == RX_MODE) {
mcasp_clr_bits(dev->base + DAVINCI_MCASP_PDIR_REG,
AXR(i));
rx_ser++;
}
}
if (dev->txnumevt && stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (dev->txnumevt * tx_ser > 64)
dev->txnumevt = 1;
mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, tx_ser,
NUMDMA_MASK);
mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
((dev->txnumevt * tx_ser) << 8), NUMEVT_MASK);
mcasp_set_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE);
}
if (dev->rxnumevt && stream == SNDRV_PCM_STREAM_CAPTURE) {
if (dev->rxnumevt * rx_ser > 64)
dev->rxnumevt = 1;
mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, rx_ser,
NUMDMA_MASK);
mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
((dev->rxnumevt * rx_ser) << 8), NUMEVT_MASK);
mcasp_set_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE);
}
}
static void davinci_hw_param(struct davinci_audio_dev *dev, int stream)
{
int i, active_slots;
u32 mask = 0;
active_slots = (dev->tdm_slots > 31) ? 32 : dev->tdm_slots;
for (i = 0; i < active_slots; i++)
mask |= (1 << i);
mcasp_clr_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* bit stream is MSB first with no delay */
/* DSP_B mode */
mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG,
AHCLKXE);
mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, mask);
mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXORD);
if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32))
mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG,
FSXMOD(dev->tdm_slots), FSXMOD(0x1FF));
else
printk(KERN_ERR "playback tdm slot %d not supported\n",
dev->tdm_slots);
mcasp_clr_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
} else {
/* bit stream is MSB first with no delay */
/* DSP_B mode */
mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, RXORD);
mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG,
AHCLKRE);
mcasp_set_reg(dev->base + DAVINCI_MCASP_RXTDM_REG, mask);
if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32))
mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG,
FSRMOD(dev->tdm_slots), FSRMOD(0x1FF));
else
printk(KERN_ERR "capture tdm slot %d not supported\n",
dev->tdm_slots);
mcasp_clr_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
}
}
/* S/PDIF */
static void davinci_hw_dit_param(struct davinci_audio_dev *dev)
{
/* Set the PDIR for Serialiser as output */
mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG, AFSX);
/* TXMASK for 24 bits */
mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, 0x00FFFFFF);
/* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0
and LSB first */
mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
TXROT(6) | TXSSZ(15));
/* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */
mcasp_set_reg(dev->base + DAVINCI_MCASP_TXFMCTL_REG,
AFSXE | FSXMOD(0x180));
/* Set the TX tdm : for all the slots */
mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF);
/* Set the TX clock controls : div = 1 and internal */
mcasp_set_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG,
ACLKXE | TX_ASYNC);
mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
/* Only 44100 and 48000 are valid, both have the same setting */
mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3));
/* Enable the DIT */
mcasp_set_bits(dev->base + DAVINCI_MCASP_TXDITCTL_REG, DITEN);
}
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_audio_dev *dev = cpu_dai->private_data;
struct davinci_pcm_dma_params *dma_params =
&dev->dma_params[substream->stream];
int word_length;
u8 numevt;
davinci_hw_common_param(dev, substream->stream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
numevt = dev->txnumevt;
else
numevt = dev->rxnumevt;
if (!numevt)
numevt = 1;
if (dev->op_mode == DAVINCI_MCASP_DIT_MODE)
davinci_hw_dit_param(dev);
else
davinci_hw_param(dev, substream->stream);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
dma_params->data_type = 1;
word_length = DAVINCI_AUDIO_WORD_8;
break;
case SNDRV_PCM_FORMAT_S16_LE:
dma_params->data_type = 2;
word_length = DAVINCI_AUDIO_WORD_16;
break;
case SNDRV_PCM_FORMAT_S32_LE:
dma_params->data_type = 4;
word_length = DAVINCI_AUDIO_WORD_32;
break;
default:
printk(KERN_WARNING "davinci-mcasp: unsupported PCM format");
return -EINVAL;
}
if (dev->version == MCASP_VERSION_2) {
dma_params->data_type *= numevt;
dma_params->acnt = 4 * numevt;
} else
dma_params->acnt = dma_params->data_type;
davinci_config_channel_size(dev, word_length);
return 0;
}
static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *cpu_dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct davinci_audio_dev *dev = rtd->dai->cpu_dai->private_data;
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
davinci_mcasp_start(dev, substream->stream);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
davinci_mcasp_stop(dev, substream->stream);
break;
default:
ret = -EINVAL;
}
return ret;
}
static 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,
};
struct snd_soc_dai davinci_mcasp_dai[] = {
{
.name = "davinci-i2s",
.id = 0,
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = DAVINCI_MCASP_RATES,
.formats = SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = DAVINCI_MCASP_RATES,
.formats = SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &davinci_mcasp_dai_ops,
},
{
.name = "davinci-dit",
.id = 1,
.playback = {
.channels_min = 1,
.channels_max = 384,
.rates = DAVINCI_MCASP_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &davinci_mcasp_dai_ops,
},
};
EXPORT_SYMBOL_GPL(davinci_mcasp_dai);
static int davinci_mcasp_probe(struct platform_device *pdev)
{
struct davinci_pcm_dma_params *dma_data;
struct resource *mem, *ioarea, *res;
struct snd_platform_data *pdata;
struct davinci_audio_dev *dev;
int ret = 0;
dev = kzalloc(sizeof(struct davinci_audio_dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(&pdev->dev, "no mem resource?\n");
ret = -ENODEV;
goto err_release_data;
}
ioarea = request_mem_region(mem->start,
(mem->end - mem->start) + 1, pdev->name);
if (!ioarea) {
dev_err(&pdev->dev, "Audio region already claimed\n");
ret = -EBUSY;
goto err_release_data;
}
pdata = pdev->dev.platform_data;
dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
ret = -ENODEV;
goto err_release_region;
}
clk_enable(dev->clk);
dev->base = (void __iomem *)IO_ADDRESS(mem->start);
dev->op_mode = pdata->op_mode;
dev->tdm_slots = pdata->tdm_slots;
dev->num_serializer = pdata->num_serializer;
dev->serial_dir = pdata->serial_dir;
dev->codec_fmt = pdata->codec_fmt;
dev->version = pdata->version;
dev->txnumevt = pdata->txnumevt;
dev->rxnumevt = pdata->rxnumevt;
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
dma_data->eventq_no = pdata->eventq_no;
dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
io_v2p(dev->base));
/* first TX, then RX */
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
goto err_release_region;
}
dma_data->channel = res->start;
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE];
dma_data->eventq_no = pdata->eventq_no;
dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
io_v2p(dev->base));
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
goto err_release_region;
}
dma_data->channel = res->start;
davinci_mcasp_dai[pdata->op_mode].private_data = dev;
davinci_mcasp_dai[pdata->op_mode].dev = &pdev->dev;
ret = snd_soc_register_dai(&davinci_mcasp_dai[pdata->op_mode]);
if (ret != 0)
goto err_release_region;
return 0;
err_release_region:
release_mem_region(mem->start, (mem->end - mem->start) + 1);
err_release_data:
kfree(dev);
return ret;
}
static int davinci_mcasp_remove(struct platform_device *pdev)
{
struct snd_platform_data *pdata = pdev->dev.platform_data;
struct davinci_audio_dev *dev;
struct resource *mem;
snd_soc_unregister_dai(&davinci_mcasp_dai[pdata->op_mode]);
dev = davinci_mcasp_dai[pdata->op_mode].private_data;
clk_disable(dev->clk);
clk_put(dev->clk);
dev->clk = NULL;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(mem->start, (mem->end - mem->start) + 1);
kfree(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,
},
};
static int __init davinci_mcasp_init(void)
{
return platform_driver_register(&davinci_mcasp_driver);
}
module_init(davinci_mcasp_init);
static void __exit davinci_mcasp_exit(void)
{
platform_driver_unregister(&davinci_mcasp_driver);
}
module_exit(davinci_mcasp_exit);
MODULE_AUTHOR("Steve Chen");
MODULE_DESCRIPTION("TI DAVINCI McASP SoC Interface");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,65 @@
/*
* 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
#include <linux/io.h>
#include <mach/asp.h>
#include "davinci-pcm.h"
extern struct snd_soc_dai davinci_mcasp_dai[];
#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_96000
#define DAVINCI_MCASP_I2S_DAI 0
#define DAVINCI_MCASP_DIT_DAI 1
enum {
DAVINCI_AUDIO_WORD_8 = 0,
DAVINCI_AUDIO_WORD_12,
DAVINCI_AUDIO_WORD_16,
DAVINCI_AUDIO_WORD_20,
DAVINCI_AUDIO_WORD_24,
DAVINCI_AUDIO_WORD_32,
DAVINCI_AUDIO_WORD_28, /* This is only valid for McASP */
};
struct davinci_audio_dev {
/*
* dma_params must be first because rtd->dai->cpu_dai->private_data
* is cast to a pointer of an array of struct davinci_pcm_dma_params in
* davinci_pcm_open.
*/
struct davinci_pcm_dma_params dma_params[2];
void __iomem *base;
int sample_rate;
struct clk *clk;
unsigned int codec_fmt;
/* McASP specific data */
int tdm_slots;
u8 op_mode;
u8 num_serializer;
u8 *serial_dir;
u8 version;
/* McASP FIFO related */
u8 txnumevt;
u8 rxnumevt;
};
#endif /* DAVINCI_MCASP_H */

View File

@@ -0,0 +1,415 @@
/*
* 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.
*/
#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 <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <asm/dma.h>
#include <mach/edma.h>
#include "davinci-pcm.h"
static struct snd_pcm_hardware davinci_pcm_hardware = {
.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,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
.periods_min = 16,
.periods_max = 255,
.fifo_size = 0,
};
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 */
struct davinci_pcm_dma_params *params; /* DMA params */
};
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 int data_type;
unsigned short acnt;
unsigned int count;
period_size = snd_pcm_lib_period_bytes(substream);
dma_offset = prtd->period * period_size;
dma_pos = runtime->dma_addr + dma_offset;
pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
"dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
data_type = prtd->params->data_type;
count = period_size / data_type;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
src = dma_pos;
dst = prtd->params->dma_addr;
src_bidx = data_type;
dst_bidx = 0;
} else {
src = prtd->params->dma_addr;
dst = dma_pos;
src_bidx = 0;
dst_bidx = data_type;
}
acnt = prtd->params->acnt;
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, acnt, count, 1, 0, ASYNC);
prtd->period++;
if (unlikely(prtd->period >= runtime->periods))
prtd->period = 0;
}
static void davinci_pcm_dma_irq(unsigned lch, 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);
if (unlikely(ch_status != DMA_COMPLETE))
return;
if (snd_pcm_running(substream)) {
snd_pcm_period_elapsed(substream);
spin_lock(&prtd->lock);
davinci_pcm_enqueue_dma(substream);
spin_unlock(&prtd->lock);
}
}
static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
{
struct davinci_runtime_data *prtd = substream->runtime->private_data;
struct edmacc_param p_ram;
int ret;
/* Request master DMA channel */
ret = edma_alloc_channel(prtd->params->channel,
davinci_pcm_dma_irq, substream,
EVENTQ_0);
if (ret < 0)
return ret;
prtd->master_lch = ret;
/* Request parameter RAM reload slot */
ret = edma_alloc_slot(EDMA_CTLR(prtd->master_lch), EDMA_SLOT_ANY);
if (ret < 0) {
edma_free_channel(prtd->master_lch);
return ret;
}
prtd->slave_lch = ret;
/* 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->slave_lch, &p_ram);
p_ram.opt |= TCINTEN | EDMA_TCC(EDMA_CHAN_SLOT(prtd->master_lch));
p_ram.link_bcntrld = EDMA_CHAN_SLOT(prtd->slave_lch) << 5;
edma_write_slot(prtd->slave_lch, &p_ram);
return 0;
}
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:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
edma_start(prtd->master_lch);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
edma_stop(prtd->master_lch);
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;
struct edmacc_param temp;
prtd->period = 0;
davinci_pcm_enqueue_dma(substream);
/* Copy self-linked parameter RAM entry into master channel */
edma_read_slot(prtd->slave_lch, &temp);
edma_write_slot(prtd->master_lch, &temp);
davinci_pcm_enqueue_dma(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;
dma_addr_t count;
dma_addr_t src, dst;
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;
spin_unlock(&prtd->lock);
offset = bytes_to_frames(runtime, 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;
int ret = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct davinci_pcm_dma_params *pa = rtd->dai->cpu_dai->private_data;
struct davinci_pcm_dma_params *params = &pa[substream->stream];
if (!params)
return -ENODEV;
snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware);
/* 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;
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;
edma_unlink(prtd->slave_lch);
edma_free_slot(prtd->slave_lch);
edma_free_channel(prtd->master_lch);
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)
{
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;
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++) {
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;
}
}
static u64 davinci_pcm_dmamask = 0xffffffff;
static int davinci_pcm_new(struct snd_card *card,
struct snd_soc_dai *dai, struct snd_pcm *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;
if (dai->playback.channels_min) {
ret = davinci_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
return ret;
}
if (dai->capture.channels_min) {
ret = davinci_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
return ret;
}
return 0;
}
struct snd_soc_platform davinci_soc_platform = {
.name = "davinci-audio",
.pcm_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)
{
snd_soc_unregister_platform(&davinci_soc_platform);
}
module_exit(davinci_soc_platform_exit);
MODULE_AUTHOR("Vladimir Barinov");
MODULE_DESCRIPTION("TI DAVINCI PCM DMA module");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,31 @@
/*
* 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 <mach/edma.h>
#include <mach/asp.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 */
enum dma_event_q eventq_no; /* event queue number */
unsigned char data_type; /* xfer data type */
unsigned char convert_mono_stereo;
};
extern struct snd_soc_platform davinci_soc_platform;
#endif

View File

@@ -0,0 +1,178 @@
/*
* 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");