转自:http://blog.csdn.net/luckywang1103/article/details/49095655
接下来准备写几篇和asoc dapm相关的文章
1. kcontrol宏
2. kcontrol的注册与使用
3. dapm widgets & dapm kcontrol & dapm route
4. dapm_widget & dapm_route注册
5. dapm route的更新
struct snd_kcontrol_new
<code class="hljs objectivec has-numbering"><span class="hljs-keyword">struct</span> snd_kcontrol_new { snd_ctl_elem_iface_t iface; <span class="hljs-comment">/* interface identifier */</span> <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> device; <span class="hljs-comment">/* device/client number */</span> <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> subdevice; <span class="hljs-comment">/* subdevice (substream) number */</span> <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">char</span> *name; <span class="hljs-comment">/* ASCII name of item */</span> <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> index; <span class="hljs-comment">/* index of item */</span> <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> access; <span class="hljs-comment">/* access rights */</span> <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> count; <span class="hljs-comment">/* count of same elements */</span> snd_kcontrol_info_t *info; snd_kcontrol_get_t *get; snd_kcontrol_put_t *put; <span class="hljs-keyword">union</span> { snd_kcontrol_tlv_rw_t *c; <span class="hljs-keyword">const</span> <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> *p; } tlv; <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> private_value; };</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li></ul>
iface:control类型,通常是SNDRV_CTL_ELEM_IFACE_MIXER
name:kcontrol的名字,名字的命名规则遵循”源-方向-功能”
源可理解为control的输入端,如Master, PCM, CD, line等
方向代表kcontrol的数据流向,如Playback, Capture, Bypass, 也可以不定义,这时是双向的
功能,如Switch, Volume, Route等
通常会借助include/sound/soc.h文件中的宏来定义这个结构体,比如SOC_SINGLE等。
另外info, get, put三个字段是一些回调函数,常常使用sound/soc/soc-core.c文件提供的函数,而不用自己去实现。
kcontrol与dapm kcontrol区别
kcontrol通常用于控件的音量等控制,而dapm kcontrol相关的kcontrol则是用于widget电源管理的开关。
一些构造snd_kcontrol_new结构体的宏
SOC_SINGLE
<code class="hljs avrasm has-numbering"><span class="hljs-preprocessor">#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \</span> ((unsigned long)&(struct soc_mixer_control) \ {<span class="hljs-preprocessor">.reg</span> = xreg, <span class="hljs-preprocessor">.shift</span> = xshift, <span class="hljs-preprocessor">.rshift</span> = xshift, <span class="hljs-preprocessor">.max</span> = xmax, \ <span class="hljs-preprocessor">.invert</span> = xinvert}) <span class="hljs-preprocessor">#define SOC_SINGLE(xname, reg, shift, max, invert) \</span> { <span class="hljs-preprocessor">.iface</span> = SNDRV_CTL_ELEM_IFACE_MIXER, <span class="hljs-preprocessor">.name</span> = xname, \ <span class="hljs-preprocessor">.info</span> = snd_soc_info_volsw, <span class="hljs-preprocessor">.get</span> = snd_soc_get_volsw,\ <span class="hljs-preprocessor">.put</span> = snd_soc_put_volsw, \ <span class="hljs-preprocessor">.private</span>_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>
SOC_SINGLE定义最简单的控件,这种控件只有一个控制量,比如一个开关,或者是数值的变化(比如codec中的某个频率,FIFO大小等)
参数:xname(该控件的名字),reg(该控件对应的寄存器的地址),shift(控制位在寄存器中的位移),max(控件可设置的最大值),invert(设定值是否取反)
SOC_SINGLE_VALUE宏定义private_value字段,目的主要是为了填充struct soc_mixer_control结构体
当上层调用info, get, put函数的时候可以将kcontrol->private_value强制转换为struct soc_mixer_control类型,然后使用这个结构体中的reg, shift, max等数据。
SOC_SINGLE_TLV
<code class="hljs avrasm has-numbering"><span class="hljs-preprocessor">#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \</span> ((unsigned long)&(struct soc_mixer_control) \ {<span class="hljs-preprocessor">.reg</span> = xreg, <span class="hljs-preprocessor">.shift</span> = xshift, <span class="hljs-preprocessor">.rshift</span> = xshift, <span class="hljs-preprocessor">.max</span> = xmax, \ <span class="hljs-preprocessor">.invert</span> = xinvert}) <span class="hljs-preprocessor">#define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \</span> { <span class="hljs-preprocessor">.iface</span> = SNDRV_CTL_ELEM_IFACE_MIXER, <span class="hljs-preprocessor">.name</span> = xname, \ <span class="hljs-preprocessor">.access</span> = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ SNDRV_CTL_ELEM_ACCESS_READWRITE,\ <span class="hljs-preprocessor">.tlv</span><span class="hljs-preprocessor">.p</span> = (tlv_array), \ <span class="hljs-preprocessor">.info</span> = snd_soc_info_volsw, <span class="hljs-preprocessor">.get</span> = snd_soc_get_volsw,\ <span class="hljs-preprocessor">.put</span> = snd_soc_put_volsw, \ <span class="hljs-preprocessor">.private</span>_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li></ul>
该宏和SOC_SINGLE类似,唯一不同的是增加了tlv.p字段,适用于那些以dB为单位的kcontrol。
DECLARE_TLV_DB_SCALE(name, min, step, mute)宏来构造变量tlv_array。
参数name是变量的名字,min是最小值,step是步进值,如果mute=1,当该kcontrol处于最小值时会mute。
来看一个实例:
<code class="hljs r has-numbering">/* from <span class="hljs-number">0</span> to <span class="hljs-number">30</span> dB <span class="hljs-keyword">in</span> <span class="hljs-number">2</span> dB steps */ static DECLARE_TLV_DB_SCALE(vga_tlv, <span class="hljs-number">0</span>, <span class="hljs-number">200</span>, <span class="hljs-number">0</span>); static const struct snd_kcontrol_new uda1380_snd_controls[] = { <span class="hljs-keyword">...</span> SOC_SINGLE_TLV(<span class="hljs-string">"Mic Capture Volume"</span>, UDA1380_ADC, <span class="hljs-number">8</span>, <span class="hljs-number">15</span>, <span class="hljs-number">0</span>, vga_tlv), /* VGA_CTRL */ }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul>
寄存器UDA1380_ADC的偏移8bit处定义了”Mic Capture Volume”,寄存器最大值为15,对应到dB的最小值是0dB,步进值是200*0.01dB=2dB,最大值是15*2dB=30dB
如上这样,寄存器的值与实际增益控制就有一个映射关系了。
SOC_ENUM
<code class="hljs avrasm has-numbering"><span class="hljs-preprocessor">#define SOC_ENUM(xname, xenum) \</span> { <span class="hljs-preprocessor">.iface</span> = SNDRV_CTL_ELEM_IFACE_MIXER, <span class="hljs-preprocessor">.name</span> = xname,\ <span class="hljs-preprocessor">.info</span> = snd_soc_info_enum_double, \ <span class="hljs-preprocessor">.get</span> = snd_soc_get_enum_double, <span class="hljs-preprocessor">.put</span> = snd_soc_put_enum_double, \ <span class="hljs-preprocessor">.private</span>_value = (unsigned long)&xenum } <span class="hljs-preprocessor">#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmax, xtexts) \</span> { <span class="hljs-preprocessor">.reg</span> = xreg, <span class="hljs-preprocessor">.shift</span>_l = xshift_l, <span class="hljs-preprocessor">.shift</span>_r = xshift_r, \ <span class="hljs-preprocessor">.max</span> = xmax, <span class="hljs-preprocessor">.texts</span> = xtexts } <span class="hljs-preprocessor">#define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) \</span> SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts) //xtexts就是mux或者mixer多个输入源的名字,是字符串数组</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li></ul>
SOC_ENUM可以用来定义mux, mixer等有多个输入的控件。
SOC_ENUM_SINGLE或SOC_ENUM_DOUBLE用来构造SOC_ENUM中的private_value字段。当info、get、put函数被调用时,会将这个private_value转化成struct soc_enum结构体类型来对数据进行处理。
对于mixer
<code class="hljs cs has-numbering"><span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *uda134x_dsp_setting[] = {<span class="hljs-string">"Flat"</span>, <span class="hljs-string">"Minimum1"</span>, <span class="hljs-string">"Minimum2"</span>, <span class="hljs-string">"Maximum"</span>}; <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *uda134x_deemph[] = {<span class="hljs-string">"None"</span>, <span class="hljs-string">"32Khz"</span>, <span class="hljs-string">"44.1Khz"</span>, <span class="hljs-string">"48Khz"</span>}; <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *uda134x_mixmode[] = {<span class="hljs-string">"Differential"</span>, <span class="hljs-string">"Analog1"</span>, <span class="hljs-string">"Analog2"</span>, <span class="hljs-string">"Both"</span>}; <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">struct</span> soc_enum uda134x_mixer_enum[] = { SOC_ENUM_SINGLE(UDA134X_DATA010, <span class="hljs-number">0</span>, <span class="hljs-number">0x04</span>, uda134x_dsp_setting), SOC_ENUM_SINGLE(UDA134X_DATA010, <span class="hljs-number">3</span>, <span class="hljs-number">0x04</span>, uda134x_deemph), SOC_ENUM_SINGLE(UDA134X_EA010, <span class="hljs-number">0</span>, <span class="hljs-number">0x04</span>, uda134x_mixmode), }; <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">struct</span> snd_kcontrol_new uda1341_snd_controls[] = { ... SOC_ENUM(<span class="hljs-string">"Sound Processing Filter"</span>, uda134x_mixer_enum[<span class="hljs-number">0</span>]), SOC_ENUM(<span class="hljs-string">"PCM Playback De-emphasis"</span>, uda134x_mixer_enum[<span class="hljs-number">1</span>]), SOC_ENUM(<span class="hljs-string">"Input Mux"</span>, uda134x_mixer_enum[<span class="hljs-number">2</span>]), }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul>
通过SOC_ENUM(“Sound Processing Filter”, uda134x_mixer_enum[0])定义了名为“Sound processing Filter”的mixer控件
从SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting)看出通过配置寄存器UDA134X_DATA010可以控制这个mixer控件的四个输入源”Flat”, “Minimum1”, “Minimum2”, “Maximum”,输入源可以选择其中的一个或者多个。
对于mux
对于mux也类似,如SOC_ENUM(“Input Mux”, uda134x_mixer_enum[2]),只不过mux一个时候只能有一个输入源,即”Differential”, “Analog1”, “Analog2”, “Both”中只能选择一个。
SOC_ENUM_EXT
<code class="hljs avrasm has-numbering"><span class="hljs-preprocessor">#define SOC_ENUM_SINGLE_EXT(xmax, xtexts) \</span> { <span class="hljs-preprocessor">.max</span> = xmax, <span class="hljs-preprocessor">.texts</span> = xtexts } <span class="hljs-preprocessor">#define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \</span> { <span class="hljs-preprocessor">.iface</span> = SNDRV_CTL_ELEM_IFACE_MIXER, <span class="hljs-preprocessor">.name</span> = xname, \ <span class="hljs-preprocessor">.info</span> = snd_soc_info_enum_ext, \ <span class="hljs-preprocessor">.get</span> = xhandler_get, <span class="hljs-preprocessor">.put</span> = xhandler_put, \ <span class="hljs-preprocessor">.private</span>_value = (unsigned long)&xenum }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul>
SOC_ENUM_EXT与SOC_ENUM宏很类似,但是,SOC_ENUM中构造private_value字段使用SOC_ENUM_SINGLE或者SOC_ENUM_DOUBLE,这两个宏构造的时候都和具体寄存器的某个或者某两个bit相关,而SOC_ENUM_EXT中构造private_value字段使用SOC_ENUM_SINGLE_EXT,这个宏构造的时候只要初始化字符串数组就行了。
<code class="hljs cs has-numbering"><span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *rt5659_micbias2_power_mode[] = { <span class="hljs-string">"Disable"</span>, <span class="hljs-string">"Enable"</span> }; <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">struct</span> soc_enum rt5659_micbias2_power_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rt5659_micbias2_power_mode), rt5659_micbias2_power_mode); <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> rt5659_micbias2_power_get(<span class="hljs-keyword">struct</span> snd_kcontrol *kcontrol, <span class="hljs-keyword">struct</span> snd_ctl_elem_value *ucontrol) { <span class="hljs-keyword">struct</span> snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); <span class="hljs-keyword">int</span> pwr = snd_soc_read(codec, RT5659_PWR_ANLG_2); <span class="hljs-keyword">if</span> (pwr & <span class="hljs-number">0x0400</span>) ucontrol-><span class="hljs-keyword">value</span>.integer.<span class="hljs-keyword">value</span>[<span class="hljs-number">0</span>] = <span class="hljs-number">1</span>; <span class="hljs-keyword">else</span> ucontrol-><span class="hljs-keyword">value</span>.integer.<span class="hljs-keyword">value</span>[<span class="hljs-number">0</span>] = <span class="hljs-number">0</span>; <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; } <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> rt5659_micbias2_power_put(<span class="hljs-keyword">struct</span> snd_kcontrol *kcontrol, <span class="hljs-keyword">struct</span> snd_ctl_elem_value *ucontrol) { <span class="hljs-keyword">struct</span> snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); <span class="hljs-keyword">int</span> pwr = ucontrol-><span class="hljs-keyword">value</span>.integer.<span class="hljs-keyword">value</span>[<span class="hljs-number">0</span>]; <span class="hljs-keyword">if</span> (pwr == <span class="hljs-number">1</span>) { snd_soc_update_bits(codec, RT5659_PWR_ANLG_3, <span class="hljs-number">0x0002</span>, <span class="hljs-number">0x0002</span>); snd_soc_update_bits(codec, RT5659_PWR_ANLG_1, <span class="hljs-number">0x0200</span>, <span class="hljs-number">0x0200</span>); snd_soc_update_bits(codec, RT5659_PWR_ANLG_2, <span class="hljs-number">0x0400</span>, <span class="hljs-number">0x0400</span>); dev_info(codec->dev, <span class="hljs-string">"enable micbias2 power\n"</span>); } <span class="hljs-keyword">else</span> { snd_soc_update_bits(codec, RT5659_PWR_ANLG_3, <span class="hljs-number">0x0002</span>, <span class="hljs-number">0x0000</span>); snd_soc_update_bits(codec, RT5659_PWR_ANLG_1, <span class="hljs-number">0x0200</span>, <span class="hljs-number">0x0000</span>); snd_soc_update_bits(codec, RT5659_PWR_ANLG_2, <span class="hljs-number">0x0400</span>, <span class="hljs-number">0x0000</span>); dev_info(codec->dev, <span class="hljs-string">"disable micbias2 power\n"</span>); } <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; } SOC_ENUM_EXT(<span class="hljs-string">"micbias2 power"</span>, rt5659_micbias2_power_enum, rt5659_micbias2_power_get, rt5659_micbias2_power_put),</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li></ul>
SOC_VALUE_ENUM
SOC_VALUE_ENUM 用于定义带values字段的snd_kcontrol_new结构体
SOC_VALUE_ENUM_SINGLE和SOC_VALUE_ENUM_DOUBLE 用于定义带values字段的soc_enum结构体
SOC_DOUBLE
SOC_SINGLE在寄存器中只控制一个变量,通常用于单声道;而SOC_DOUBLE可以同时在一个寄存器中控制两个相似的变量,通常是对左右声道的控制,用于立体声。
SOC_DOUBLE_R
与SOC_DOUBLE类似,用于左右声道的控制不在一个寄存器中的情况,参数中指定两个寄存器地址
SOC_DOUBLE_TLV
SOC_DOUBLE_R_TLV
其他
下面这些带EXT的宏需要我们自己定义get, put函数
SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert, xhandler_get, xhandler_put)
SOC_DOUBLE_EXT(xname, xreg, shift_left, shift_right, xmax, xinvert, xhandler_get, xhandler_put)
SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert, xhandler_get, xhandler_put, tlv_array)
SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert, xhandler_get, xhandler_put, tlv_array)
SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, xhandler_get, xhandler_put, tlv_array)
SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put)