[alsa-devel] SAIF configuration on imx28
George Stefan
stefan.george87 at gmail.com
Tue Aug 21 14:39:25 CEST 2012
2012/8/21 Fabio Estevam <festevam at gmail.com>
> Hi George,
>
> On Tue, Aug 21, 2012 at 8:24 AM, George Stefan
> <stefan.george87 at gmail.com> wrote:
>
> > My problem is, although i am receiving dma irqs when i record ( arecord
> -f
> > S16_LE -r 8000 -c 1 <file>.wav), when i play the resulting file its just
> > silence.
> >
> > [ 5295.890000] dump @ mxs_saif_trigger
> > [ 5295.890000] scr 101 , 107
> > [ 5295.890000] stat 80010041 , 80010071
> > [ 5295.890000] data 0 , ffffffff
> > [ 5295.890000] version 1010000 , 1010000
> > This is a dump for arecord!
> >
> > Do you have any ideeas?
>
> Can you please to record as:
> arecord -D hw:0,1 -d 5 -f S16_LE -r 44100 -c 2 1.wav
>
> ,as suggested here: http://www.spinics.net/lists/arm-kernel/msg186015.html
>
> Regards,
>
> Fabio Estevam
>
I don't think thats the issue.
In order to be more clear i will attach the code:
/* This is the codec code */
static struct snd_soc_codec *cinterion_codec;
/*
* Line discpline related code
*
* Any of the callback functions below can be used in two ways:
* 1) registerd by a machine driver as one of line discipline operations,
* 2) called from a machine's provided line discipline callback function
* in case when extra machine specific code must be run as well.
*/
static const char *init_audio_mode = "AT^SNFS=6/r";
/* Please see pg 422 of AHS3-W AT Command Set */
static const char *init_pcm_audio = "AT^SAIC=1,1,1,1,0,0,1/r";
/* Line discipline .open() */
static int cinterion_tty_open(struct tty_struct *tty)
{
int len = strlen(init_audio_mode);
int ret;
struct snd_soc_codec *codec = cinterion_codec;
/* Doesn't make sense without write callback */
if (!tty->ops->write)
return -EINVAL;
/* Pass the codec structure address for use by other ldisc callbacks */
tty->disc_data = codec;
if (tty->ops->write(tty, init_audio_mode, len) != len) {
ret = -EIO;
return ret;
}
len = strlen(init_pcm_audio);
if (tty->ops->write(tty, init_pcm_audio, len) != len) {
ret = -EIO;
return ret;
}
/* Actual setup will be performed after the modem responds. */
return 0;
err:
tty->disc_data = NULL;
return ret;
}
/* Line discipline .receive_buf() */
static void cinterion_tty_receive(struct tty_struct *tty,
const unsigned char *cp, char *fp, int count)
{
struct snd_soc_codec *codec = tty->disc_data;
if (!codec)
return;
if (!codec->control_data) {
/* First modem response, complete setup procedure */
/* Set up codec driver access to modem controls */
codec->control_data = tty;
codec->hw_write = (hw_write_t)tty->ops->write;
codec->pop_time = 1;
}
}
/* Line discipline .close() */
static void cinterion_tty_close(struct tty_struct *tty)
{
struct snd_soc_codec *codec = tty->disc_data;
tty->disc_data = NULL;
if (!codec)
return;
/* Prevent the codec driver from further accessing the modem */
codec->hw_write = NULL;
codec->control_data = NULL;
codec->pop_time = 0;
}
struct tty_ldisc_ops cinterion_tty_ops = {
.magic = TTY_LDISC_MAGIC,
.name = "cinterion-ahs3w",
.owner = THIS_MODULE,
.open = cinterion_tty_open,
.close = cinterion_tty_close,
.receive_buf = cinterion_tty_receive,
};
EXPORT_SYMBOL_GPL(cinterion_tty_ops);
struct snd_soc_dai cinterion_codec_dai = {
.name = "cinterion",
.playback = {
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
};
EXPORT_SYMBOL_GPL(cinterion_codec_dai);
static int cinterion_soc_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
if (!cinterion_codec) {
dev_err(&pdev->dev, "cinterion codec not yet discovered\n");
return -ENODEV;
}
codec = cinterion_codec;
socdev->card->codec = codec;
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "cinterion: failed to create pcms\n");
goto pcm_err;
}
return ret;
pcm_err:
return ret;
}
static int cinterion_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_soc_free_pcms(socdev);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_cinterion = {
.probe = cinterion_soc_probe,
.remove = cinterion_soc_remove,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_cinterion);
static __devinit int cinterion_codec_probe(struct platform_device *pdev)
{
struct snd_soc_codec *codec;
int ret;
if (cinterion_codec) {
printk(KERN_ERR "Cinterion is allready registered\n");
return -EBUSY;
}
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if(codec == NULL)
return -ENOMEM;
codec->control_data = NULL;
codec->hw_write = NULL;
codec->pop_time = 0;
cinterion_codec = codec;
platform_set_drvdata(pdev, codec);
mutex_init(&codec->mutex);
codec->dev = &pdev->dev;
codec->name = "cinterion";
codec->owner = THIS_MODULE;
codec->dai = &cinterion_codec_dai;
codec->num_dai = 1;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
cinterion_codec_dai.dev = &pdev->dev;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to register CODEC: %d\n", ret);
goto err;
}
ret = snd_soc_register_dai(&cinterion_codec_dai);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret);
goto err_codec;
}
return 0;
err_codec:
snd_soc_unregister_codec(codec);
err:
kfree(codec);
return ret;
}
static __devexit int cinterion_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_dai(&cinterion_codec_dai);
snd_soc_unregister_codec(platform_get_drvdata(pdev));
return 0;
}
static struct platform_driver cinterion_codec_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
.probe = cinterion_codec_probe,
.remove = __devexit_p(cinterion_codec_remove),
};
static int __init cinterion_codec_modinit(void)
{
return platform_driver_register(&cinterion_codec_driver);
}
static void __exit cinterion_codec_exit(void)
{
platform_driver_unregister(&cinterion_codec_driver);
}
module_init(cinterion_codec_modinit);
module_exit(cinterion_codec_exit);
mxs-devb.c code
#include "../codecs/cinterion-ahs3w.h"
struct mxs_evk_priv {
int sysclk;
int hw;
struct platform_device *pdev;
};
static struct mxs_evk_priv card_priv;
/* this should be implemented */
static int mxs_acu2_audio_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_link *machine = rtd->dai;
struct snd_soc_dai *cpu_dai = machine->cpu_dai;
struct mxs_evk_priv *priv = &card_priv;
unsigned int rate = params_rate(params);
int ret = 0;
u32 dai_format;
/* only need to do this once as capture and playback are sync */
#if 0
if (priv->hw)
return 0;
priv->hw = 1;
priv->sysclk = 512 * rate;
printk("sysclk %lu\n", priv->sysclk);
snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSP_SYS_MCLK, 256);
#endif
/* set cpu_dai to master mode for playback, slave mode for record */
// dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
// SND_SOC_DAIFMT_CBM_CFM;
dai_format = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_IB_NF |
SND_SOC_DAIFMT_CBM_CFM;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
if (ret < 0)
return ret;
#if 0
/* set the SAIF system clock as output */
snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, priv->sysclk, \
SND_SOC_CLOCK_OUT);
snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 256000,
SND_SOC_CLOCK_OUT);
#endif
snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
SND_SOC_CLOCK_IN);
return 0;
}
/*
* mxs_acu2 Cinterion audio DAI operations.
*/
static struct snd_soc_ops mxs_acu2_ops = {
.hw_params = mxs_acu2_audio_hw_params,
};
/* Line discipline .open() */
static int cinterion_ahs3w_open(struct tty_struct *tty)
{
return cinterion_tty_ops.open(tty);
}
/* Line discipline .receive_buf() */
static void cinterion_ahs3w_receive(struct tty_struct *tty,
const unsigned char *cp, char *fp, int count)
{
struct snd_soc_codec *codec = tty->disc_data;
const unsigned char *c;
int apply, ret;
/* First modem response...must complete setup */
if (!codec->control_data) {
cinterion_tty_ops.receive_buf(tty, cp, fp, count);
return ;
}
cinterion_tty_ops.receive_buf(tty, cp, fp, count);
printk("count %d\n", count);
}
/* Line discipline .close() */
static void cinterion_ahs3w_close(struct tty_struct *tty)
{
struct snd_soc_codec *codec = tty->disc_data;
cinterion_tty_ops.close(tty);
}
static struct tty_ldisc_ops cinterion_ahs3w_ops = {
.magic = TTY_LDISC_MAGIC,
.name = "cinterion_ahs3w",
.owner = THIS_MODULE,
.open = cinterion_ahs3w_open,
.close = cinterion_ahs3w_close,
.receive_buf = cinterion_ahs3w_receive,
};
/*
* Card initialization
*/
static int mxs_cinterion_init(struct snd_soc_codec *codec)
{
struct snd_soc_dai *codec_dai = codec->dai;
int ret;
/* Register optional line discipline for over the modem control
*/
ret = tty_register_ldisc(N_V253, &cinterion_ahs3w_ops);
if (ret)
printk(KERN_ERR,
"Failed to register line discipline, "
"will continue without any controls.\n");
return ret;
}
/* mxs_evk digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link acu2_dai = {
.name = "cinterion",
.stream_name = "cinterion pcm",
.codec_dai = &cinterion_codec_dai,
.init = mxs_cinterion_init,
.ops = &mxs_acu2_ops,
};
/* SoC machine structure */
static struct snd_soc_card acu2_snd_soc_card = {
.name = "mxs-evk",
.platform = &mxs_soc_platform,
.dai_link = &acu2_dai,
.num_links = 1,
};
static struct snd_soc_device acu2_snd_soc_device = {
.card = &acu2_snd_soc_card,
.codec_dev = &soc_codec_dev_cinterion,
};
static int __devinit mxs_evk_cinterion_probe(struct platform_device *pdev)
{
struct mxs_audio_platform_data *plat = pdev->dev.platform_data;
struct mxs_saif *saif_select;
int ret = -EINVAL;
/* saif 0 and 1 clocks and stuff */
if (plat->init && plat->init())
goto err_plat_init;
acu2_dai.cpu_dai = &mxs_saif_dai[0]; /* adds the cpu dai */
acu2_dai.cpu_dai->name = "imx28-dai";
saif_select = (struct mxs_saif *)acu2_dai.cpu_dai->private_data;
saif_select->stream_mapping = PLAYBACK_SAIF0_CAPTURE_SAIF1;
saif_select->saif_mclk = plat->saif_mclock;
saif_select->saif_clk = SAIF0;
return 0;
err_plat_init:
if (plat->finit)
plat->finit();
return ret;
}
static int mxs_evk_cinterion_remove(struct platform_device *pdev)
{
struct mxs_audio_platform_data *plat = pdev->dev.platform_data;
if (plat->finit)
plat->finit();
return 0;
}
/* audio platform driver */
static struct platform_driver acu2_cinterion_audio_driver = {
.probe = mxs_evk_cinterion_probe,
.remove = mxs_evk_cinterion_remove,
.driver = {
.name = "mxs-sgtl5000",
},
};
static struct platform_device *acu2_snd_platform_device;
static struct platform_device *cinterion_codec_platform_device;
static int __init mxs_acu2_init(void)
{
int ret;
ret = platform_driver_register(&acu2_cinterion_audio_driver);
if (ret)
return -ENOMEM;
acu2_snd_platform_device = platform_device_alloc("soc-audio", 1);
if (!acu2_snd_platform_device) {
printk(KERN_ERR "Platform device allocation failed\n");
return -ENOMEM;
}
platform_set_drvdata(acu2_snd_platform_device, &acu2_snd_soc_device);
acu2_snd_soc_device.dev = &acu2_snd_platform_device->dev;
ret = platform_device_add(acu2_snd_platform_device);
if (ret)
platform_device_put(acu2_snd_platform_device);
cinterion_codec_platform_device =
platform_device_register_simple("cinterion",
-1, NULL,
0);
return ret;
}
static void __exit mxs_acu2_exit(void)
{
platform_device_unregister(acu2_snd_platform_device);
}
module_init(mxs_acu2_init);
module_exit(mxs_acu2_exit);
Also the dump of arecord -d 5 -f S16_LE -r 8000 -c 2 1.wav
hexdump -C test.wav
00000000 52 49 46 46 04 ca 55 00 57 41 56 45 66 6d 74 20
|RIFF..U.WAVEfmt |
00000010 10 00 00 00 01 00 02 00 44 ac 00 00 10 b1 02 00
|........D.......|
00000020 04 00 10 00 64 61 74 61 e0 c9 55 00 00 00 00 00
|....data..U.....|
00000030 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
|................|
*
00562020
More information about the Alsa-devel
mailing list