ASoC驱动的组成
ASoC(ALSA System on Chip)是ALSA在SoC方面的发展和演变,它在本质上仍然属于ALSA,但是在ALSA架构的基础上对CPU相关的代码和CODEC相关的代码进行了分离。其原因是,采用传统ALSA架构的情况下,同一型号的CODEC工作于不同的CPU时,需要不同的驱动,这不符合代码重用的要求。
ASoC主要由3部分组成。
(1)CODEC驱动。这一部分只关心CODEC本身,与CPU平台相关的特性不由此部分操作。
(2)平台驱动。这一部分只关心CPU本身,不关心CODEC。它主要处理两个问题:DMA引擎和SoC集成的PCM、I2S或AC’97数字接口的控制。
(3)板驱动。也称为machine驱动,这一部分将平台驱动和CODEC驱动绑定在一起,描述了板一级的硬件特征。
i2s 总线:
I2S有3个主要信号:1.串行时钟SCLK,也叫位时钟(BCLK),即对应数字音频的每一位数据,SCLK都有1个脉冲。SCLK的频率=2×采样频率×采样位数 2. 帧时钟LRCK,用于切换左右声道的数据。LRCK为“1”表示正在传输的是左声道的数据,为“0”则表示正在传输的是右声道的数据。LRCK的频率等于采样频率。3.串行数据SDATA,就是用二进制补码表示的音频数据。
有时为了使系统间能够更好地同步,还需要另外传输一个信号MCLK,称为主时钟,也叫系统时钟(Sys Clock)(在君正平台上这个主时钟可要可不要)
写一个简单codec驱动实例:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include "icdc_d3.h"
#include
#define XFM10231_RATES SNDRV_PCM_RATE_16000 // 采样频率
#define XFM10231_FORMATS SNDRV_PCM_FMTBIT_S16_LE //采样格式
#define DLV4780_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
SNDRV_PCM_FMTBIT_S20_3LE |SNDRV_PCM_FMTBIT_S24_LE)
/*
static int xfm10231_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
return 0;
}
static void xfm10231_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
return 0;
}
*/
static struct snd_soc_dai_driver xfm10231_dai = { // 只采集录音 ,没有play
.name = "xfm10231-dai",
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
};
static struct snd_soc_codec_driver soc_codec_dev_xfm10231;
static int xfm10231_probe(struct platform_device *pdev)
{
int ret = 0;
printk("xfm10231 register_codec \n");
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_xfm10231, &xfm10231_dai, 1); //注册codec解码器
if(ret < 0){
printk("xfm10231 register_codec failed\n");
}
return ret;
}
static int xfm10231_remove(struct platform_device *pdev)
{
int ret = 0;
snd_soc_unregister_codec(&pdev->dev);
//kfree(g_xfm10231_i2c_data);
return ret;
}
static struct platform_driver xfm10231_driver = {
.driver = {
.name = "xfm10231",
.owner = THIS_MODULE,
},
.probe = xfm10231_probe,
.remove = xfm10231_remove,
};
module_platform_driver(xfm10231_driver);
MODULE_DESCRIPTION("Soc xfm10231 driver");
MODULE_AUTHOR("tjin
MODULE_LICENSE("GPL");
然后在创建一个声卡:
#include
#include
#include
#include
#include
#include
#include
#include "../icodec/icdc_d3.h"
void imxfm_i2s_startup(struct snd_pcm_substream *sps){
printk("phoenix_i2s_startup\n");
return;
}
int imxfm_i2s_shutdown(struct snd_pcm_substream *sps){
printk("phoenix_i2s_shutdown\n");
return 0;
}
int imxfm_i2s_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->cpu_dai;
int ret;
printk("phoenix_i2s_hw_params\n");
/*FIXME snd_soc_dai_set_pll*/
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_MSB|SND_SOC_DAIFMT_CBM_CFM); //设置主模式 ,位时钟由 //xfm10231 提供,所以是 //主模式
if (ret)
return ret;
ret = snd_soc_dai_set_sysclk(cpu_dai, JZ_I2S_EX_CODEC, 16000, SND_SOC_CLOCK_IN); //设置外部解码器,时钟输入
if (ret)
return ret;
return 0;
};
int imxfm_i2s_hw_free(struct snd_pcm_substream *substream)
{
/*notify release pll*/
printk("phoenix_i2s_hw_free\n");
return 0;
}
static struct snd_soc_ops imxfm_i2s_ops = {
.startup = imxfm_i2s_startup,
.shutdown = imxfm_i2s_shutdown,
.hw_params = imxfm_i2s_hw_params,
.hw_free = imxfm_i2s_hw_free,
};
static struct snd_soc_dai_link imxfm_dais[] = {
{
.name = "xfm10231 icdc",
.stream_name = "xfm10231 icdc",
.platform_name = "jz-asoc-aic-dma",
.cpu_dai_name = "jz-asoc-aic-i2s", //i2s总线驱动名字
.codec_dai_name = "xfm10231-dai",
.codec_name = "xfm10231", //上面的codec 名字 。总线 、codec 、声卡 三者匹配
.ops = &imxfm_i2s_ops,
},
};
static struct snd_soc_card imxfm = {
.name = "imxfm",
.owner = THIS_MODULE,
.dai_link = imxfm_dais,
.num_links = ARRAY_SIZE(imxfm_dais),
};
static int snd_imxfm_probe(struct platform_device *pdev)
{
int ret = 0;
imxfm.dev = &pdev->dev;
ret = snd_soc_register_card(&imxfm);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
return ret;
}
static int snd_imxfm_remove(struct platform_device *pdev)
{
snd_soc_unregister_card(&imxfm);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver snd_imxfm_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "ingenic-imxfm",
.pm = &snd_soc_pm_ops,
},
.probe = snd_imxfm_probe,
.remove = snd_imxfm_remove,
};
module_platform_driver(snd_imxfm_driver);
MODULE_AUTHOR("sccheng
MODULE_DESCRIPTION("ALSA SoC imxfm Snd Card");
MODULE_LICENSE("GPL");
以上两个示例只有platform_driver,没有注册platform_device。