【总结笔记】全志平台 Linux ASOC 框架浅析
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 框架浅析的更多相关文章
- 驱动开发学习笔记. 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 ...
 - 驱动开发学习笔记. 0.04  linux 2.6 platform device register 平台设备注册  1/2 共2篇
		
驱动开发读书笔记. 0.04 linux 2.6 platform device register 平台设备注册 1/2 共2篇下面这段摘自 linux源码里面的文档 : Documentatio ...
 - .NET平台常用的框架整理
		
基于.NET平台常用的框架整理 DotNet | 2016-03-31 17:13 (点击上方蓝字,可快速关注我们) 来源:天使不哭 链接:http://www.cnblogs.com/hgmyz/p ...
 - .NET平台下开源框架
		
一.AOP框架Encase 是C#编写开发的为.NET平台提供的AOP框架.Encase 独特的提供了把方面(aspects)部署到运行时代码,而其它AOP框架依赖配置文件的方式.这种部署方面(asp ...
 - linux驱动程序框架基础
		
============================ 指引 ============================= 第一节是最基础的驱动程序: 第二节是/dev应用层接口的使 ...
 - 关于Linux主流框架运维工作剖析
		
LINUX是开源的,这也是最主要的原因,想学Windows,Unix对不起,没有源代码.也正是因为这样,LINUX才能够像雪球一样越滚越大,发展到现在这种规模.今天将为大家带来关于Linux主流框架运 ...
 - ASoC框架
		
ASoC框架分为3部分: 1. platform(用来描述芯片的DAI接口,负责数据传输): DAI:snd_soc_dai_driver, 用来表示支持哪些格式数据, 提供设置格式的函数, 启动数据 ...
 - 《Linux就该这么学》培训笔记_ch00_认识Linux系统和红帽认证
		
<Linux就该这么学>培训笔记_ch00_认识Linux系统和红帽认证 文章最后会post上书本的笔记照片. 文章主要内容: 认识开源 Linux系统的种类及优势特性 认识红帽系统及红帽 ...
 - Linux 驱动框架---input子系统框架
		
前面从具体(Linux 驱动框架---input子系统)的工作过程学习了Linux的input子系统相关的架构知识,但是前面的学习比较实际缺少总结,所以今天就来总结一下输入子系统的架构分层,站到远处来 ...
 - Linux 驱动框架---input子系统
		
input 子系统也是作为内核的一个字符设备模块存在的,所以他也是字符设备自然也会有字符设备的文件接口.input子系统的注册过程主要分为两步,先注册了一个input class然后再注册一个字符设备 ...
 
随机推荐
- MQTT+esp32+nodered+springboot  智能家居项目  -- 项目准备
			
1.后台系环境:idea jdk8.0以上 maven tomcat spring boot 2.前端环境 nodejs nodered 3.硬件环境: audrion esp32 ...
 - mitmproxy抓包工具
			
中文官网 https://ptorch.com/docs/10/mitmproxy-concepts-options mitmproxy抓包工具 1. mitmproxy 介绍与安装 需要安装pyth ...
 - zk系列一:zookeeper基础介绍
			
聊完kafka必不可少的需要再聊一聊zk了,下面开始 一.ZK是什么 ZooKeeper是分布式应用程序的高性能协调服务.它可以实现分布式的选主.统一配置管理,命名,分布式节点同步,分布式锁等分布式常 ...
 - C#实践炸飞机socket通信
			
一.前言 最近老师要求做课设,实现一个 "炸飞机" 游戏,我是负责UI界面实现和Socket通信实现的,在这里想总结一下我实现Socket的具体过程,对其中的产生的问题和实现的方法 ...
 - 嵌入式-C语言基础:字符串结束标识符
			
#include<stdio.h> int main() { char cdata[]={'h','e','l','l','o'}; char cdata2[]="hello&q ...
 - 新零售SaaS架构:多租户系统架构设计
			
什么是多租户? 多租户是SaaS领域的特有产物,在SaaS服务中,租户是指使用SaaS系统的客户,租户不同于用户,例如,B端SaaS产品,用户可能是某个组织下的员工,但整个企业组织是SaaS系统的租户 ...
 - WebApi实现Token验证
			
为什么要实现Token呢.在我们客户端发送请求时,如果没有校验数据是否合法那么就有可能造成非法请求泄露我们的数据 实现Token的思路 1.客户端通过用户名和密码来获取Token 通过自己的账号和密码 ...
 - FCOS论文复现:通用物体检测算法
			
摘要:本案例代码是FCOS论文复现的体验案例,此模型为FCOS论文中所提出算法在ModelArts + PyTorch框架下的实现.本代码支持FCOS + ResNet-101在MS-COCO数据集上 ...
 - Fastjsonfan反序列化(1)
			
前言 之前只是对FastJson漏洞有简单的一个认知,虽然由于网上fastjson漏洞调试的文章很多,但是真正有着自己的理解并能清楚的讲述出来的文章少之又少.大多文章都是对已知的漏洞调用流程做了大量分 ...
 - MISC相关刷题记录迁移