ASOC 各部分框图示意

Platform

一般由 SOC 芯片原厂负责编写,主要涉及到 SOC 内部数字音频接口DAI(I2S)和 DMA 的寄存器配置。

Codec

一般由硬件方案的驱动工程师或者 Codec 芯片原厂负责编写,主要涉及到 Codec 芯片相关的寄存器配置。

Machine

一般由硬件方案的驱动工程师编写,根据项目所选型的 Codec 来选择对应的 DAI,进行关联。

ASOC 代码关联关键点

--------------------------------------------------------------------------------------------
Machine:关联 Codec 和 Platform ,完成声卡的创建,并设置 Codec 和 Platform 对齐格式和主从模式等。
sunxi-sndi2s1.c --> snd_soc_register_card() <-- ac108_machine.c sunxi-snddaudio.c Codec:针对音频CODEC的驱动,主要是进行AD、DA转换,对音频通路的控制,音量控制、EQ控制等等
sndi2s1.c --> snd_soc_register_codec() <-- AC108.c Platform:针对CPU端的驱动,主要包括数据音频接口的配置,时钟频率、数据格式,DMA的设置等等
sunxi-i2s1.c --> snd_soc_register_dai() <-- sun3iw1_daudio.c
sunxi-i2s1dma.c --> snd_soc_register_platform() <-- sunxi_dma.c
--------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
sunxi-sndi2s1.c (Machine) snd_soc_register_card()
--------------------------------------------------------------------------------------------
static struct snd_soc_ops sunxi_sndi2s1_ops = {
.hw_params = sunxi_sndi2s1_hw_params, // 硬件参数设定
}; static struct snd_soc_dai_link sunxi_sndi2s1_dai_link = {
.name = "I2S1",
.stream_name = "SUNXI-I2S1",
.init = sunxi_i2s1_init, //Platform的数字音频接口(DAI)的名称,系统根据这个匹配Platform_dai驱动
//sunxi-i2s1.c --> platform_driver_register()
.cpu_dai_name = "i2s1", //Platform的名称,用来匹配Platform驱动的
//sunxi-i2s1dma.c --> snd_soc_register_platform()
.platform_name = "sunxi-i2s1-pcm-audio.0", //codec的数字音频接口(DAI)的名称,系统根据这个匹配codec_dai驱动
//sndi2s1.c --> snd_soc_register_codec()
.codec_dai_name = "sndi2s1", //codec的名称,系统将根据这个名字匹配相应的Codec驱动
//sndi2s1.c --> platform_device_register()/i2c_add_driver()
.codec_name = "sunxi-i2s1-codec.0", .ops = &sunxi_sndi2s1_ops,
}; static struct snd_soc_card snd_soc_sunxi_sndi2s1 = {
.name = "sndi2s1", // 为我们的声卡定义一个名字
.owner = THIS_MODULE,
.dai_link = &sunxi_sndi2s1_dai_link,
.num_links = 1,
};
snd_soc_register_card(&snd_soc_sunxi_sndi2s1) --------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
sndi2s1.c (Codec) snd_soc_register_codec()
--------------------------------------------------------------------------------------------
struct snd_soc_dai_ops sndi2s1_dai_ops = {
.startup = sndi2s1_startup, // 打开设备,设备开始工作的时候回调
.shutdown = sndi2s1_shutdown, // 关闭设备前的回调
.hw_params = sndi2s1_hw_params, // 设置Codec硬件相关的参数(数据位宽等)
.digital_mute = sndi2s1_mute, // Codec静音操作
.set_sysclk = sndi2s1_set_dai_sysclk, // 设置 Codec 的主时钟(SYSCLK_SRC_MCLK、SYSCLK_SRC_PLL)
.set_clkdiv = sndi2s1_set_dai_clkdiv, // 设置Codec的分频系数
.set_fmt = sndi2s1_set_dai_fmt // 设置Codec传输的数据格式(主从、对齐格式、时钟极性)
}; //.hw_params\.set_sysclk\.set_clkdiv\.set_fmt 由Machine驱动设置 struct snd_soc_dai_driver sndi2s1_dai = {
.name = "sndi2s1", // 用于被snd_soc_dai_link.codec_dai_name 匹配
.playback = { // 用于描述播放时Codec支持的声道数,码率,数据格式等能力
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = sndi2s1_RATES,
.formats = sndi2s1_FORMATS,
},
.capture = { // 用于描述录音时Codec支持的声道数,码率,数据格式等能力
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = sndi2s1_RATES,
.formats = sndi2s1_FORMATS,
},
.ops = &sndi2s1_dai_ops,
};
EXPORT_SYMBOL(sndi2s1_dai);
snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sndi2s1, &sndi2s1_dai, 1); static struct platform_device sndi2s1_codec_device = {
.name = "sunxi-i2s1-codec",
}; static struct platform_driver sndi2s1_codec_driver = {
.driver = {
.name = "sunxi-i2s1-codec", // 用于被snd_soc_dai_link.codec_name 匹配
.owner = THIS_MODULE,
},
.probe = sndi2s1_codec_probe,
.remove = __exit_p(sndi2s1_codec_remove),
};
platform_device_register(&sndi2s1_codec_device)
platform_driver_register(&sndi2s1_codec_driver) --------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
sunxi-i2s1.c (Platform - dai:I2S 通信接口) snd_soc_register_dai()
--------------------------------------------------------------------------------------------
static struct snd_soc_dai_ops sunxi_i2s1_dai_ops = {
.trigger = sunxi_i2s1_trigger, // I2S 启动、暂停、恢复、停止时的操作
.hw_params = sunxi_i2s1_hw_params, // 设置I2S数据位宽等硬件参数
.set_fmt = sunxi_i2s1_set_fmt, // 设置I2S数据格式(主从、对齐格式、时钟极性)
.set_clkdiv = sunxi_i2s1_set_clkdiv, // 设置I2S的时钟分频
.set_sysclk = sunxi_i2s1_set_sysclk, // 设置系统时钟
}; //.hw_params.\set_sysclk\.set_clkdiv\.set_fmt 由Machine驱动设置 static struct snd_soc_dai_driver sunxi_i2s1_dai = {
.probe = sunxi_i2s1_dai_probe,
.suspend = sunxi_i2s1_suspend,
.resume = sunxi_i2s1_resume,
.remove = sunxi_i2s1_dai_remove,
.playback = { //用于描述播放时I2S支持的声道数,码率,数据格式等能力
.channels_min = 1,
.channels_max = 2,
.rates = SUNXI_I2S1_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
},
.capture = { //用于描述录音时I2S支持的声道数,码率,数据格式等能力
.channels_min = 1,
.channels_max = 2,
.rates = SUNXI_I2S1_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
},
.ops = &sunxi_i2s1_dai_ops,
};
snd_soc_register_dai(&pdev->dev, &sunxi_i2s1_dai); static struct platform_device sunxi_i2s1_device = {
.name = "i2s1",
.id = PLATFORM_DEVID_NONE,
}; static struct platform_driver sunxi_i2s1_driver = {
.probe = sunxi_i2s1_dev_probe,
.remove = __exit_p(sunxi_i2s1_dev_remove),
.driver = {
.name = "i2s1", // 用于被snd_soc_dai_link.cpu_dai_name 匹配
.owner = THIS_MODULE,
},
};
platform_device_register(&sunxi_i2s1_device)
platform_driver_register(&sunxi_i2s1_driver) --------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------
sunxi-i2s1dma.c (Platform - dai:音频 DMA 配置) snd_soc_register_platform()
--------------------------------------------------------------------------------------------
static struct snd_pcm_ops sunxi_pcm_ops = {
.open = sunxi_pcm_open, // 打开设备,准备开始播放时调用,会打开 DMA 引擎
.close = sunxi_pcm_close, // 关闭播放,关闭DMA引擎
.ioctl = snd_pcm_lib_ioctl, // 应用层调用的 ioctl 回调
// 应用设置播放参数的时候调用,根据设置的参数,设置DMA,例如数据宽度,传输块大小,DMA地址
.hw_params = sunxi_pcm_hw_params,
.hw_free = sunxi_pcm_hw_free,
.trigger = sunxi_pcm_trigger, // DMA 开始、暂停、恢复、结束传输的回调
.pointer = snd_dmaengine_pcm_pointer, // 返回DMA缓冲的当前指针
.mmap = sunxi_pcm_mmap, // 建立内存映射
}; static struct snd_soc_platform_driver sunxi_soc_platform = {
.ops = &sunxi_pcm_ops,
.pcm_new = sunxi_pcm_new,
.pcm_free = sunxi_pcm_free_dma_buffers,
};
snd_soc_register_platform(&pdev->dev, &sunxi_soc_platform); static struct platform_device sunxi_i2s1_pcm_device = {
.name = "sunxi-i2s1-pcm-audio",
}; static struct platform_driver sunxi_i2s1_pcm_driver = {
.probe = sunxi_i2s1_pcm_probe,
.remove = __exit_p(sunxi_i2s1_pcm_remove),
.driver = {
.name = "sunxi-i2s1-pcm-audio", // 用于被snd_soc_dai_link.platform_name 匹配
.owner = THIS_MODULE,
},
};
platform_device_register(&sunxi_i2s1_pcm_device);
platform_driver_register(&sunxi_i2s1_pcm_driver);
--------------------------------------------------------------------------------------------

Machine 实例(sunxi_sndi2s1.c)

/*
* sound\soc\sunxi\i2s1\sunxi_sndi2s1.c
* (C) Copyright 2010-2016
* Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
* huangxin <huangxin@Reuuimllatech.com>
*
* some simple description for this code
*
* 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.
*
*/ #include <linux/module.h>
#include <linux/clk.h>
#include <linux/mutex.h> #include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <sound/soc-dapm.h>
#include <linux/io.h>
#include <mach/sys_config.h> #include "sunxi-i2s1.h"
#include "sunxi-i2s1dma.h" static bool i2s1_pcm_select = 0; static int i2s1_used = 0;
static int i2s1_master = 0;
static int audio_format = 0;
static int signal_inversion = 0; /*
* i2s1_pcm_select == 0:--> pcm
* i2s1_pcm_select == 1:--> i2s
*/
static int sunxi_i2s1_set_audio_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
i2s1_pcm_select = ucontrol->value.integer.value[0]; if (i2s1_pcm_select) {
audio_format = 1;
signal_inversion = 1;
i2s1_master = 4;
} else {
audio_format = 4;
signal_inversion = 3;
i2s1_master = 1;
} return 0;
} static int sunxi_i2s1_get_audio_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = i2s1_pcm_select;
return 0;
} /* I2s Or Pcm Audio Mode Select */
static const struct snd_kcontrol_new sunxi_i2s1_controls[] = {
SOC_SINGLE_BOOL_EXT("I2s Or Pcm Audio Mode Select format", 0,
sunxi_i2s1_get_audio_mode, sunxi_i2s1_set_audio_mode),
}; static int sunxi_sndi2s1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int ret = 0;
u32 freq = 22579200; 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;
unsigned long sample_rate = params_rate(params); switch (sample_rate) {
case 8000:
case 16000:
case 32000:
case 64000:
case 128000:
case 12000:
case 24000:
case 48000:
case 96000:
case 192000:
freq = 24576000;
break;
} /*set system clock source freq and set the mode as i2s1 or pcm*/
ret = snd_soc_dai_set_sysclk(cpu_dai, 0 , freq, i2s1_pcm_select);
if (ret < 0) {
return ret;
} ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/*
* codec clk & FRM master. AP as slave
*/
ret = snd_soc_dai_set_fmt(cpu_dai, (audio_format | (signal_inversion<<8) | (i2s1_master<<12)));
if (ret < 0) {
return ret;
} ret = snd_soc_dai_set_clkdiv(cpu_dai, 0, sample_rate);
if (ret < 0) {
return ret;
} /*
* audio_format == SND_SOC_DAIFMT_DSP_A
* signal_inversion<<8 == SND_SOC_DAIFMT_IB_NF
* i2s1_master<<12 == SND_SOC_DAIFMT_CBM_CFM
*/
I2S1_DBG("%s,line:%d,audio_format:%d,SND_SOC_DAIFMT_DSP_A:%d\n",\
__func__, __LINE__, audio_format, SND_SOC_DAIFMT_DSP_A);
I2S1_DBG("%s,line:%d,signal_inversion:%d,signal_inversion<<8:%d,SND_SOC_DAIFMT_IB_NF:%d\n",\
__func__, __LINE__, signal_inversion, signal_inversion<<8, SND_SOC_DAIFMT_IB_NF);
I2S1_DBG("%s,line:%d,i2s1_master:%d,i2s1_master<<12:%d,SND_SOC_DAIFMT_CBM_CFM:%d\n",\
__func__, __LINE__, i2s1_master, i2s1_master<<12, SND_SOC_DAIFMT_CBM_CFM); return 0;
} /*
* Card initialization
*/
static int sunxi_i2s1_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_card *card = rtd->card;
int ret; /* Add virtual switch */
ret = snd_soc_add_codec_controls(codec, sunxi_i2s1_controls,
ARRAY_SIZE(sunxi_i2s1_controls));
if (ret) {
dev_warn(card->dev,
"Failed to register audio mode control, "
"will continue without it.\n");
}
return 0;
} static struct snd_soc_ops sunxi_sndi2s1_ops = {
.hw_params = sunxi_sndi2s1_hw_params,
}; static struct snd_soc_dai_link sunxi_sndi2s1_dai_link = {
.name = "I2S1",
.stream_name = "SUNXI-I2S1",
.cpu_dai_name = "i2s1",
.codec_dai_name = "sndi2s1",
.init = sunxi_i2s1_init,
.platform_name = "sunxi-i2s1-pcm-audio.0",
.codec_name = "sunxi-i2s1-codec.0",
.ops = &sunxi_sndi2s1_ops,
}; static struct snd_soc_card snd_soc_sunxi_sndi2s1 = {
.name = "sndi2s1",
.owner = THIS_MODULE,
.dai_link = &sunxi_sndi2s1_dai_link,
.num_links = 1,
}; static int __devinit sunxi_sndi2s1_dev_probe(struct platform_device *pdev)
{
int ret = 0;
script_item_u val;
script_item_value_type_e type;
struct snd_soc_card *card = &snd_soc_sunxi_sndi2s1; type = script_get_item("i2s1", "i2s1_select", &val);
if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
pr_err("[I2S1] i2s1_select type err!\n");
}
i2s1_pcm_select = val.val; type = script_get_item("i2s1", "i2s1_master", &val);
if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
pr_err("[I2S1] i2s1_master type err!\n");
}
i2s1_master = val.val; type = script_get_item("i2s1", "audio_format", &val);
if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
pr_err("[I2S1] audio_format type err!\n");
}
audio_format = val.val; type = script_get_item("i2s1", "signal_inversion", &val);
if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
pr_err("[I2S1] signal_inversion type err!\n");
}
signal_inversion = val.val;
card->dev = &pdev->dev;
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret);
}
return ret;
} static int __devexit sunxi_sndi2s1_dev_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev); snd_soc_unregister_card(card);
return 0;
} /*data relating*/
static struct platform_device sunxi_i2s1_device = {
.name = "sndi2s1",
.id = PLATFORM_DEVID_NONE,
}; /*method relating*/
static struct platform_driver sunxi_i2s1_driver = {
.probe = sunxi_sndi2s1_dev_probe,
.remove = __exit_p(sunxi_sndi2s1_dev_remove),
.driver = {
.name = "sndi2s1",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
},
}; static int __init sunxi_sndi2s1_init(void)
{
int err = 0;
script_item_u val;
script_item_value_type_e type;
type = script_get_item("i2s1", "i2s1_used", &val);
if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
pr_err("[I2S] type err!\n");
}
i2s1_used = val.val;
if (i2s1_used) {
if((err = platform_device_register(&sunxi_i2s1_device)) < 0)
return err; if ((err = platform_driver_register(&sunxi_i2s1_driver)) < 0)
return err;
} else {
pr_warning("I2S1 driver not init,just return.\n");
}
return 0;
}
module_init(sunxi_sndi2s1_init); static void __exit sunxi_sndi2s1_exit(void)
{
if (i2s1_used) {
i2s1_used = 0;
platform_driver_unregister(&sunxi_i2s1_driver);
platform_device_unregister(&sunxi_i2s1_device);
}
}
module_exit(sunxi_sndi2s1_exit);
MODULE_AUTHOR("huangxin");
MODULE_DESCRIPTION("SUNXI_sndi2s1 ALSA SoC audio driver");
MODULE_LICENSE("GPL");

Codec 实例(sndi2s1.c)

/*
* sound\soc\sunxi\i2s1\sndi2s1.c
* (C) Copyright 2010-2016
* Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
* huangxin <huangxin@Reuuimllatech.com>
*
* some simple description for this code
*
* 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.
*
*/ #include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <linux/io.h>
#include <mach/sys_config.h> struct sndi2s1_priv {
int sysclk;
int dai_fmt; struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream;
}; static int i2s1_used = 0;
#define sndi2s1_RATES (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT)
#define sndi2s1_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) static int sndi2s1_mute(struct snd_soc_dai *dai, int mute)
{
return 0;
} static int sndi2s1_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
return 0;
} static void sndi2s1_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{ } static int sndi2s1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
return 0;
} static int sndi2s1_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
return 0;
} static int sndi2s1_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
{
return 0;
} static int sndi2s1_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
return 0;
} struct snd_soc_dai_ops sndi2s1_dai_ops = {
.startup = sndi2s1_startup,
.shutdown = sndi2s1_shutdown,
.hw_params = sndi2s1_hw_params,
.digital_mute = sndi2s1_mute,
.set_sysclk = sndi2s1_set_dai_sysclk,
.set_clkdiv = sndi2s1_set_dai_clkdiv,
.set_fmt = sndi2s1_set_dai_fmt,
}; struct snd_soc_dai_driver sndi2s1_dai = {
.name = "sndi2s1",
/* playback capabilities */
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 8,
.rates = sndi2s1_RATES,
.formats = sndi2s1_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 8,
.rates = sndi2s1_RATES,
.formats = sndi2s1_FORMATS,
},
/* pcm operations */
.ops = &sndi2s1_dai_ops,
};
EXPORT_SYMBOL(sndi2s1_dai); static int sndi2s1_soc_probe(struct snd_soc_codec *codec)
{
struct sndi2s1_priv *sndi2s1; sndi2s1 = kzalloc(sizeof(struct sndi2s1_priv), GFP_KERNEL);
if(sndi2s1 == NULL){
return -ENOMEM;
}
snd_soc_codec_set_drvdata(codec, sndi2s1); return 0;
} /* power down chip */
static int sndi2s1_soc_remove(struct snd_soc_codec *codec)
{
struct sndi2s1_priv *sndi2s1 = snd_soc_codec_get_drvdata(codec); kfree(sndi2s1); return 0;
} static struct snd_soc_codec_driver soc_codec_dev_sndi2s1 = {
.probe = sndi2s1_soc_probe,
.remove = sndi2s1_soc_remove,
}; static int __devinit sndi2s1_codec_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sndi2s1, &sndi2s1_dai, 1);
} static int __devexit sndi2s1_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
} /*data relating*/
static struct platform_device sndi2s1_codec_device = {
.name = "sunxi-i2s1-codec",
}; /*method relating*/
static struct platform_driver sndi2s1_codec_driver = {
.driver = {
.name = "sunxi-i2s1-codec",
.owner = THIS_MODULE,
},
.probe = sndi2s1_codec_probe,
.remove = __exit_p(sndi2s1_codec_remove),
}; static int __init sndi2s1_codec_init(void)
{
int err = 0;
script_item_u val;
script_item_value_type_e type; type = script_get_item("i2s1", "i2s1_used", &val);
if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
pr_err("[I2S] type err!\n");
} i2s1_used = val.val; if (i2s1_used) {
if((err = platform_device_register(&sndi2s1_codec_device)) < 0)
return err; if ((err = platform_driver_register(&sndi2s1_codec_driver)) < 0)
return err;
} else {
pr_err("[I2S]sndi2s1 cannot find any using configuration for controllers, return directly!\n");
return 0;
} return 0;
}
module_init(sndi2s1_codec_init); static void __exit sndi2s1_codec_exit(void)
{
if (i2s1_used) {
i2s1_used = 0;
platform_driver_unregister(&sndi2s1_codec_driver);
}
}
module_exit(sndi2s1_codec_exit); MODULE_DESCRIPTION("SNDI2S1 ALSA soc codec driver");
MODULE_AUTHOR("huangxin");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:sunxi-i2s1-codec");

【总结笔记】全志平台 Linux ASOC 框架浅析的更多相关文章

  1. 驱动开发学习笔记. 0.05 linux 2.6 platform device register 平台设备注册 2/2 共2篇

    驱动开发读书笔记. 0.05 linux 2.6 platform device register 平台设备注册 2/2 共2篇 下面这段摘自 linux源码里面的文档 : 内核版本2.6.22Doc ...

  2. 驱动开发学习笔记. 0.04 linux 2.6 platform device register 平台设备注册 1/2 共2篇

    驱动开发读书笔记. 0.04  linux 2.6 platform device register 平台设备注册  1/2 共2篇下面这段摘自 linux源码里面的文档 : Documentatio ...

  3. .NET平台常用的框架整理

    基于.NET平台常用的框架整理 DotNet | 2016-03-31 17:13 (点击上方蓝字,可快速关注我们) 来源:天使不哭 链接:http://www.cnblogs.com/hgmyz/p ...

  4. .NET平台下开源框架

    一.AOP框架Encase 是C#编写开发的为.NET平台提供的AOP框架.Encase 独特的提供了把方面(aspects)部署到运行时代码,而其它AOP框架依赖配置文件的方式.这种部署方面(asp ...

  5. linux驱动程序框架基础

    ============================      指引     ============================= 第一节是最基础的驱动程序: 第二节是/dev应用层接口的使 ...

  6. 关于Linux主流框架运维工作剖析

    LINUX是开源的,这也是最主要的原因,想学Windows,Unix对不起,没有源代码.也正是因为这样,LINUX才能够像雪球一样越滚越大,发展到现在这种规模.今天将为大家带来关于Linux主流框架运 ...

  7. ASoC框架

    ASoC框架分为3部分: 1. platform(用来描述芯片的DAI接口,负责数据传输): DAI:snd_soc_dai_driver, 用来表示支持哪些格式数据, 提供设置格式的函数, 启动数据 ...

  8. 《Linux就该这么学》培训笔记_ch00_认识Linux系统和红帽认证

    <Linux就该这么学>培训笔记_ch00_认识Linux系统和红帽认证 文章最后会post上书本的笔记照片. 文章主要内容: 认识开源 Linux系统的种类及优势特性 认识红帽系统及红帽 ...

  9. Linux 驱动框架---input子系统框架

    前面从具体(Linux 驱动框架---input子系统)的工作过程学习了Linux的input子系统相关的架构知识,但是前面的学习比较实际缺少总结,所以今天就来总结一下输入子系统的架构分层,站到远处来 ...

  10. Linux 驱动框架---input子系统

    input 子系统也是作为内核的一个字符设备模块存在的,所以他也是字符设备自然也会有字符设备的文件接口.input子系统的注册过程主要分为两步,先注册了一个input class然后再注册一个字符设备 ...

随机推荐

  1. clip-path属性深入理解与使用

    clip-path CSS 属性可以创建一个只有元素的部分区域可以显示的剪切区域.区域内的部分显示,区域外的隐藏. clip-path的属性值可以是以下几种: 1.inset: 将元素剪裁为一个矩形, ...

  2. Docker容器化技术

    1. 初始Docker 1.1 Docker概念 Docker概念:Docker是一个开源的应用容器引擎 诞生于2013年初,基于Go实现,dotCloud公司出品(后改名为Docker Inc) D ...

  3. 从0到1搭建redis6.0.7

    redis集群搭建 一.安装redis 源码安装: 1.下载源码包: wget http://download.redis.io/releases/redis-6.0.7.tar.gz 2.解压到指定 ...

  4. el-select实现下拉框触底加载更多

    当下拉框需要展示的数据有很多时,几千甚至上万条,一次性全部请求回来再按照特定格式比如 id-name 去处理数据的话,不论是从接口还是前端,这个性能都不是很好,会造成下拉框初次打开时响应很慢,影响用户 ...

  5. Enum.Parse的使用

    Enum的转换,用Enum.Parse() Enum.Parse()方法.这个方法带3个参数,第一个参数是要使用的枚举类型.其语法是关键字typeof后跟放在括号中的枚举类名.第二个参数是要转换的字符 ...

  6. UWSGI 安装出现 ModuleNotFoundError: No module named '_ctypes'

    原因:Python3中有个内置模块叫ctypes,它是Python3的外部函数库模块,它提供兼容C语言的数据类型,并通过它调用Linux系统下的共享库(Shared library),此模块需要使用C ...

  7. 前端工程化与webpack的介绍

    前端工程化 概念:在企业级的前端项目开发中,把前端开发所需的工具.技术.流程.经验等进行规范化.标准化. 模块化 js的模块化,css的模块化,资源的模块化 组件化 复用现有的UI结构,样式,行为 规 ...

  8. Dijkstra 算法说明与实现

    Dijkstra 算法说明与实现 作者:Grey 原文地址: 博客园:Dijkstra 算法说明与实现 CSDN:Dijkstra 算法说明与实现 问题描述 问题:给定出发点,出发点到所有点的距离之和 ...

  9. Kubernetes(k8s)存储管理之数据卷volumes(五):动态制备-存储类StorageClass

    目录 一.系统环境 二.前言 三.静态制备和动态制备 四.存储类StorageClass 4.1 存储类StorageClass概览 4.2 StorageClass 资源 五.创建存储类Storag ...

  10. Django框架三板斧本质-jsonResponse对象-form表单上传文件request对象方法-FBV与CBV区别

    目录 一:视图层 2.三板斧(HttpResponse对象) 4.HttpResponse() 5.render() 6.redirect() 7.也可以是一个完整的URL 二:三板斧本质 1.Dja ...