ASoC Codec Driver
The codec driver is generic and hardwareindependent code that configures the
codec to provide audio capture and playback. It should contain no code that is
specific to the target platform or machine. All platform and machine specific
code should be added to the platform and machine drivers respectively.
ASoC解码器驱动
=================
解码器驱动是通用、硬件无关的代码,它配置解码器以支持音频捕获与回放。它不应包含任何与目标平台或机器相关的代码。平台或机器相关代码应该分别加入到平台和机器驱动中去。
Each codec driver *must* provide the following features:-
1) Codec DAI and PCM configuration
2) Codec control IO - using I2C, 3 Wire(SPI) or both APIs
3) Mixers and audio controls
4) Codec audio operations
Optionally, codec drivers can alsoprovide:-
5) DAPM description.
6) DAPM event handler.
7) DAC Digital mute control.
各解码器驱动必须提供如下特性:
1)解码器数字音频接口和PCM配置。
2)解码器控制IO-使用I2C,3总线(SPI)或两个都有。
3)混音器和音频控制。
4)解码器音频操作。
解码器驱动可以选择性提供:
5)动态音频电源管理描述。
6)动态音频电源管理事件控制。
7)数模转换数字消音控制。
Its probably best to use this guide in conjunction with the existing codec
driver code in sound/soc/codecs/
大家也许最好联同己存在于sound/soc/codecs/中的驱动代码一起来使用这个指导书。
ASoC Codec driver breakdown
1 - Codec DAI and PCM configuration
Each codec driver must have a structsnd_soc_codec_dai to define its DAI and
PCM capabilities and operations. This struct is exported so that it can be
registered with the core by your machine driver.
ASoC 解码器故障
===========================
1-解码器数字音频接口和PCM配置
-----------------------------------
各解码器驱动必须有一个snd_soc_codec_dai数据结构,它用来定义DAI和PCM提供的功能和操作。这个数据结构要导出,好让你的机器驱动程序将它注册到ALSA核心中去。
e.g.
例如:
struct snd_soc_codec_dai wm8731_dai = {
.name = "WM8731",
/* playback capabilities */
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM8731_RATES,
.formats = WM8731_FORMATS,},
/* capture capabilities */
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8731_RATES,
.formats = WM8731_FORMATS,},
/* pcm operations - see section 4 below */
.ops = {
.prepare = wm8731_pcm_prepare,
.hw_params = wm8731_hw_params,
.shutdown = wm8731_shutdown,
},
/* DAI operations - see DAI.txt */
.dai_ops = {
.digital_mute = wm8731_mute,
.set_sysclk = wm8731_set_dai_sysclk,
.set_fmt = wm8731_set_dai_fmt,
}
};
EXPORT_SYMBOL_GPL(wm8731_dai);
2 - Codec control IO
--------------------
The codec can usually be controlled via an I2C or SPI style interface
(AC97 combines control with data in the DAI). The codec drivers provide
functions to read and write the codec registers along with supplying a
register cache:-
/* IO control data and register cache */
void *control_data; /* codec control (i2c/3wire) data */
void *reg_cache;
Codec read/write should do any data formatting and call the hardware
read write below to perform the IO. These functions are called by the
core and ALSA when performing DAPM or changing the mixer:-
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
Codec hardware IO functions - usually points to either the I2C, SPI or AC97
read/write:-
2-解码器控制IO
解码器通常可以通过I2C或SPI类型的接器进行控制(AC97 的数字音频接口中把数据和控制结合在了一起)。解码器驱动提供读写解码器寄存器和供应寄存器缓存的功能。
/* IO control data and register cache */
void *control_data; /* codec control (i2c/3wire) data */
void *reg_cache;
解码器的读写可以作用于任何格式的数据,最终调用底层硬件的读写功能操作IO。ALSA核或ALSA在动态音频电源管理或改变混音器时会调用这些函数。
unsigned int(*read)(struct snd_soc_codec *, unsigned int);
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
解码器硬件IO函数-通常指向I2C,SPI或AC97的读写:
hw_write_t hw_write;
hw_read_t hw_read;
3 - Mixers and audio controls
All the codecmixers and audio controls can be defined using the convenience
macros defined in soc.h.
#define SOC_SINGLE(xname, reg, shift, mask, invert)
Defines a single control as follows:-
xname= Control name e.g. "Playback Volume"
reg = codec register
shift = control bit(s) offset in register
mask = control bit size(s) e.g. mask of 7 = 3 bits
invert = the control is inverted
Other macros include:-
#define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert)
A stereo control
#defineSOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert)
A stereo control spanning 2 registers
#defineSOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts)
Defines an single enumerated control asfollows:-
xreg = register
xshift = control bit(s) offset in register
xmask = control bit(s) size
xtexts = pointer to array of strings that describe each setting
#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts)
Defines a stereo enumerated control
3-混音器和音频控制
-----------------------------
所有解码器混音器和音频控制都可以通过使用soc.h中定义的宏而带来方便。
#define SOC_SINGLE(xname, reg, shift, mask, invert)
这个宏可以定义一个单次操作:
xname = Control name e.g. "Playback Volume"
reg = codec register
shift = control bit(s) offset in register
mask = control bit size(s) e.g. mask of 7 = 3 bits
invert = the control is inverted
其它的宏还有:
#define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask,invert)
下面是一个立体声控制:
#define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask,invert)
扩用两个寄存器的立体声控制如下:
#define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts)
定义一个枚举控制如下:
xreg = register
xshift = control bit(s) offset in register
xmask = control bit(s) size
xtexts = pointer to array of strings that describe each setting
#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts)
上面的宏定义一个立体声枚举控制。
4 - Codec Audio Operations
The codec driver also supports thefollowing ALSA operations:-
/* SoC audio ops */
struct snd_soc_ops {
int (*startup)(struct snd_pcm_substream *);
void (*shutdown)(struct snd_pcm_substream *);
int (*hw_params)(struct snd_pcm_substream *, structsnd_pcm_hw_params *);
int (*hw_free)(struct snd_pcm_substream *);
int (*prepare)(struct snd_pcm_substream *);
};
Please refer to the ALSA driver PCMdocumentation for details.
http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c436.htm
4-解码器音频操作
--------------------------
解码器驱动还支持下面的ALSA操作:
/* SoC audio ops */
struct snd_soc_ops {
int (*startup)(struct snd_pcm_substream *);
void (*shutdown)(struct snd_pcm_substream *);
int (*hw_params)(struct snd_pcm_substream *, structsnd_pcm_hw_params *);
int (*hw_free)(struct snd_pcm_substream *);
int (*prepare)(struct snd_pcm_substream *);
};
详情请参考ALSA驱动PCM文档:
http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c436.htm
5 - DAPM description.
The Dynamic Audio Power Managementdescription describes the codec power
components and their relationships and registers to the ASoC core.
Please read dapm.txt for details of building the description.
Please also see the examples in other codecdrivers.
5-动态音频电源管理
---------------------
动态音频电源管理描述的是解码器电源组件和它们的关系,和向ASoC核注册的方法。详情请阅dapm.txt。
也请参照其它解码器的例子。
6 - DAPM event handler
This function is a callback that handlescodec domain PM calls and system
domain PM calls (e.g. suspend and resume). It is used to put the codec
to sleep when not in use.
Power states:-
SNDRV_CTL_POWER_D0: /* full On */
/* vref/mid, clk and osc on, active */
SNDRV_CTL_POWER_D1: /* partial On */
SNDRV_CTL_POWER_D2: /* partial On */
SNDRV_CTL_POWER_D3hot: /* Off, with power */
/* everything off except vref/vmid, inactive */
SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */
6-动态音频电源管理事件句柄
----------------------
该功能是一个回调函数,用来处理解码器域的电源管理调用和系统域的电源管理调用(如挂起和恢复)。它用来在不用时使解码器进入休眠状态。
电源状态:
SNDRV_CTL_POWER_D0: /* full On */
/* vref/mid, clk and osc on, active */
SNDRV_CTL_POWER_D1: /* partial On */
SNDRV_CTL_POWER_D2: /* partial On */
SNDRV_CTL_POWER_D3hot: /* Off, with power */
/* everything off except vref/vmid, inactive */
SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */
7 - Codec DAC digital mute control
----------------------------------
Most codecs have a digital mute before the DACs that can be used to
minimise any system noise. The mute stops any digital data from
entering the DAC.
A callback can be created that is called bythe core for each codec DAI
when the mute is applied or freed.
7-解码器数模转换消音控制
多数解码器都有一个数字消音装置放在数模转换器前面。它可以用来最小化系统躁声。消音器不让任何数字数据进入DAC。
消音器使用或释放时会创造一个回调。ASoC核会为每个解码器数字音频接口调用这个回调函数。
i.e.
static int wm8974_mute(struct snd_soc_codec *codec,
struct snd_soc_codec_dai *dai, int mute)
{
u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) &0xffbf;
if(mute)
wm8974_write(codec, WM8974_DAC, mute_reg| 0x40);
else
wm8974_write(codec, WM8974_DAC,mute_reg);
return 0;
}