關於alsa架構已經啃了好久好久,但是也卡了好久好久。難說皮毛到底有看懂多少,不管,我們先來啃wm8960 codec的驅動代碼:
必要相關函數說明:
////////////////////////////////////////////////////////////////////////////
1.#define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) \
SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts)
#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmax, xtexts) \
{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
.max = xmax, .texts = xtexts, \
.mask = xmax ? roundup_pow_of_two(xmax) - 1 : 0}
ex:
#define WM8960_ALC3 0x13
static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode)
這個宏定義的作用:reg 0x13的bit8是的功能是select alc mode,
設置0:alc mode 1:limite mode.
1.大概猜測:
SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts)
Defines an single enumerated control as follows:-
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去填充某個特殊的結構體,從而實現相應的初始化設定:
/* enumerated kcontrol */
struct soc_enum {
unsigned short reg;
unsigned short reg2;
unsigned char shift_l;
unsigned char shift_r;
unsigned int max;
unsigned int mask;
const char * const *texts;
const unsigned int *values;
};
根據理解,我們發現SOC_ENUM_SINGLE()這個宏用來填充soc_enum的結構體,如果將它展開:
SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode)
--->
{
.reg = WM8960_ALC3,
.shift_l = 8,
.shift_r = 8,
.max = 2,
.texts = {"ALC", "Limiter"},
.mask = xmax ? roundup_pow_of_two(xmax) - 1 : 0//mask=?
}
////////////////////////////////////////////////////////////////////////////
2.#define SOC_ENUM(xname, xenum) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
.info = snd_soc_info_enum_double, \
.get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \
.private_value = (unsigned long)&xenum }
ex:
static const struct soc_enum wm8960_enum0 = SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode);
SOC_ENUM(“ALC Function”, wm8960_enum0),
同樣,展開後:
--->
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "ADC Polarity",
.info = snd_soc_info_enum_double,
.get = snd_soc_get_enum_double,
.put = snd_soc_put_enum_double,
.private_value = (unsigned long)&wm8960_enum0,
}
所以,我們發現,這個宏實際上還是對某個結構體的填充.用來填充snd_kcontrol_new這個結構體
這邊傳入的snd_soc_info_enum_double這個函數:
int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
uinfo->value.enumerated.items = e->max;
if (uinfo->value.enumerated.item > e->max - 1)
uinfo->value.enumerated.item = e->max - 1;
strcpy(uinfo->value.enumerated.name,
e->texts[uinfo->value.enumerated.item]);
return 0;
}
是爲了獲得kcontrol->private_value的某些參數
int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val;
val = snd_soc_read(codec, e->reg);
ucontrol->value.enumerated.item[0]
= (val >> e->shift_l) & e->mask;
if (e->shift_l != e->shift_r)
ucontrol->value.enumerated.item[1] =
(val >> e->shift_r) & e->mask;
return 0;
}
是爲了讀取對應的kcontrol的reg的某些bit的狀態
int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val;
unsigned int mask;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
val = ucontrol->value.enumerated.item[0] << e->shift_l;
mask = e->mask << e->shift_l;
if (e->shift_l != e->shift_r) {
if (ucontrol->value.enumerated.item[1] > e->max - 1)
return -EINVAL;
val |= ucontrol->value.enumerated.item[1] << e->shift_r;
mask |= e->mask << e->shift_r;
}
return snd_soc_update_bits_locked(codec, e->reg, mask, val);
}
同理,這邊是設置對應kcontrol的reg的某些bit的狀態.
struct snd_kcontrol_new {
snd_ctl_elem_iface_t iface; /* interface identifier */
unsigned int device; /* device/client number */
unsigned int subdevice; /* subdevice (substream) number */
const unsigned char *name; /* ASCII name of item */
unsigned int index; /* index of item */
unsigned int access; /* access rights */
unsigned int count; /* count of same elements */
snd_kcontrol_info_t *info;
snd_kcontrol_get_t *get;
snd_kcontrol_put_t *put;
union {
snd_kcontrol_tlv_rw_t *c;
const unsigned int *p;
} tlv;
unsigned long private_value;
};
所以,這個宏同樣是完成初始化動作,而且我們可以看到,
SOC_ENUM_SINGLE()
SOC_ENUM()—->需要先完成SOC_ENUM_XXX(),當然上面的例子是通過SOC_ENUM_SINGLE()來實現private data的初始化的
////////////////////////////////////////////////////////////////////////////
本質上,所有的宏基本上都是爲了實現snd_kcontrol_new的結構體而服務,用來初始化snd_kcontrol_new的宏有:
SOC_ENUM(xname, xenum)
SOC_DAPM_SINGLE(xname, reg, shift, max, invert)
SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array)
SOC_DOUBLE_R(xname, reg_left, reg_right, xshift, xmax, xinvert)
SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array)
SOC_SINGLE(xname, reg, shift, max, invert)
SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put)
//DAPM
SOC_DAPM_SINGLE(xname, reg, shift, max, invert)
所以,我們可以知道,這些宏都是用來初始化snd_kcontrol_new結構體的,每個宏對應某個寄存器的特殊操作,可以實現讀寫控制。其中DAPM(digital audio power maniger)
也算snd_kcontrol_new的一種實例,snd_kcontrol_new結構是一種很重要的數據結構。它實現了許多寄存器的實例化的操作
////////////////////////////////////////////////////////////////////////////
#define SND_SOC_DAPM_INPUT(wname) \
{ .id = snd_soc_dapm_input, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM }
ex:
SND_SOC_DAPM_INPUT("LINPUT1")
同樣展開
--->
{
.id = snd_soc_dapm_input,
.name = "LINPUT1",
.kcontrol_news = NULL,
.num_kcontrols = 0,
.reg = SND_SOC_NOPM
}
#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
wcontrols, wncontrols)\
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
ex:
SND_SOC_DAPM_MIXER(“Left Input Mixer”, WM8960_POWER3, 5, 0,
wm8960_lin, ARRAY_SIZE(wm8960_lin)),
展開:
--->
{
.id = snd_soc_dapm_mixer,
.name = "Left Input Mixer",
.reg = WM8960_POWER3,
.shift = 5,
.invert = 0,
.kcontrol_news = wm8960_lin,
.num_kcontrols = ARRAY_SIZE(wm8960_lin)
}
我們發現這兩個宏,同樣是實現了對某個結構體的初始化.
只是因宏的不同,填充的結構體的成員變量會有差異.
這個結構體是:
/* dapm widget */
struct snd_soc_dapm_widget {
enum snd_soc_dapm_type id;
const char *name; /* widget name */
const char *sname; /* stream name */
struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct list_head list;
struct snd_soc_dapm_context *dapm;
void *priv; /* widget specific data */
struct regulator *regulator; /* attached regulator */
const struct snd_soc_pcm_stream *params; /* params for dai links */
/* dapm control */
int reg; /* negative reg = no direct dapm */
unsigned char shift; /* bits to shift */
unsigned int value; /* widget current value */
unsigned int mask; /* non-shifted mask */
unsigned int on_val; /* on state value */
unsigned int off_val; /* off state value */
unsigned char power:1; /* block power status */
unsigned char invert:1; /* invert the power bit */
unsigned char active:1; /* active stream on DAC, ADC's */
unsigned char connected:1; /* connected codec pin */
unsigned char new:1; /* cnew complete */
unsigned char ext:1; /* has external widgets */
unsigned char force:1; /* force state */
unsigned char ignore_suspend:1; /* kept enabled over suspend */
unsigned char new_power:1; /* power from this run */
unsigned char power_checked:1; /* power checked this run */
int subseq; /* sort within widget type */
int (*power_check)(struct snd_soc_dapm_widget *w);
/* external events */
unsigned short event_flags; /* flags to specify event types */
int (*event)(struct snd_soc_dapm_widget*, struct snd_kcontrol *, int);
/* kcontrols that relate to this widget */
int num_kcontrols;
const struct snd_kcontrol_new *kcontrol_news;
struct snd_kcontrol **kcontrols;
/* widget input and outputs */
struct list_head sources;
struct list_head sinks;
/* used during DAPM updates */
struct list_head power_list;
struct list_head dirty;
int inputs;
int outputs;
struct clk *clk;
};
首先這個是DAPM相關的東西,大概可以理解爲 數位聲音電源管理小部件專用的結構體.
這個結構體裏面有包含const struct snd_kcontrol_new *kcontrol_news;
也就是說在某些情況下,爲了實現DAPM-widget,有時需要先初始化snd_kcontrol_new數據結構.
通常用來實例化DAPM widget的宏有:
SND_SOC_DAPM_INPUT(wname)
SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags)
SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert,wcontrols, wncontrols)
SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert)
SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert)
SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,wcontrols, wncontrols)
SND_SOC_DAPM_OUTPUT(wname)
DAPM widget的實例化後,我們便可以控制某個小部件的電源模塊的開關.前面有說過,因爲省電的關係,需要單獨增加DAPM widget的電源小部件,以方便獨立對某個模塊
進行電源控制.
////////////////////////////////////////////////////////////////////////////
/*
* DAPM audio route definition.
*
* Defines an audio route originating at source via control and finishing
* at sink.
*/
struct snd_soc_dapm_route {
const char *sink;
const char *control;
const char *source;
/* Note: currently only supported for links where source is a supply */
int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink);
};
根據字面理解,這邊的作用是實現一條audio路由通路。
將sink端的DAPM和source的DAPM連接起來,這樣便可以進行聲音的錄製/播放
ex:
{ “Left Boost Mixer”, “LINPUT1 Switch”, “LINPUT1” }
意義:將 “Left Boost Mixer”(sink端)的DAPM與 “LINPUT1”(source端)的DAPM進行一次connect. “LINPUT1 Switch”用來提供control的操作函數
當wm8960_probe(struct snd_soc_codec *codec)時,它做的事情:
{
/*kcontrol: 控件*/
snd_soc_add_codec_controls(codec, wm8960_snd_controls,
ARRAY_SIZE(wm8960_snd_controls));
/*widgets: 組件*/
wm8960_add_widgets(codec);
}
在static int wm8960_add_widgets(struct snd_soc_codec *codec)裏面
{
snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,
ARRAY_SIZE(wm8960_dapm_widgets));
snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
}
在wm8960_i2c_probe裏面
會有:
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8960, &wm8960_dai, 1);// dai num=1
大致上可以這樣認爲:
首先出冊IIC驅動,當match到對應設備時,執行iic_probe函數,同時最後再使用snd_soc_register_codec函數註冊codec。
當match到對應的設備時,進行它自己的probe函數,此函數會向codec中添加一些控制小模塊和DAPM的組件,同時加載DAPM路由表.
整個codec的驅動函數大致流程就是這樣