Linux 君正平台ASOC音频驱动架构分析

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。

 

 

你可能感兴趣的:(C语言)