kcontol为何?写过alsa codec驱动的人都很熟悉了。
droidphone前辈的这篇文章也非常详细:
通俗一点讲,kcontol控件提供了很方便的在线调试手段,比如linux下使用amixer,android下使用timymix,也方便我们封装接口供上层使用,比较静音,设置codec音量等等。通常这些控制都比较简单,基本上就是操作codec寄存器的某一个bit而已,例如TI 5707芯片设置音量和静音的kcontol控制为:
static const struct snd_kcontrol_new tas5707_snd_controls[] = {SOC_SINGLE_TLV("Master Volume", DDX_MASTER_VOLUME, 0,0xff, 1, mvol_tlv),SOC_SINGLE_TLV("Ch1 Volume", DDX_CHANNEL1_VOL, 0,0xff, 1, chvol_tlv),SOC_SINGLE_TLV("Ch2 Volume", DDX_CHANNEL2_VOL, 0,0xff, 1, chvol_tlv),SOC_SINGLE("Ch1 Switch", DDX_SOFT_MUTE, 0, 1, 1),SOC_SINGLE("Ch2 Switch", DDX_SOFT_MUTE, 1, 1, 1),SOC_SINGLE_RANGE("Fine Master Volume", DDX_CHANNEL3_VOL, 0,0x80, 0x83, 0),
};
alsa定义的宏很方便我们对这些寄存器进行操作。在老的kernel版本中,kcontol并没有好的方式处理批量寄存器的设置,比如codec的EQDRC设置,用户层在切换音效时需要设置多组EQ相关的寄存器,这个时候就需要自己想办法实现了,比如增加一个节点等。
最近在看android P的code时发现,最新的linux kernel,增加了SND_SOC_BYTES_EXT这样一个宏,可以设置多组寄存器,先来看看alsa关于这个宏的定义:
#define SND_SOC_BYTES_EXT(xname, xcount, xhandler_get, xhandler_put)
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_soc_bytes_info_ext, .get = xhandler_get, .put = xhandler_put, .private_value = (unsigned long)&(struct soc_bytes_ext) {.max = xcount} }
xname为控件的名字
xcount为设置寄存器的最大个数
xhandler_get和xhandler_put分别为获取和设置参数的接口,具体的实现由我们来编写
private_value可以作为我们传下来的寄存器值数组
还是拿ti 5707设置EQ参数举例,看看set和get方法的实现:
SND_SOC_BYTES_EXT("EQ table", TAS5707_EQ_LENGTH,tas5707_get_EQ_param, tas5707_set_EQ_param)
上面是kcontol宏的定义
static int tas5707_set_EQ_param(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol)
{struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);struct snd_soc_codec *codec = snd_soc_component_to_codec(component);struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);struct soc_bytes_ext *params = (void *)kcontrol->private_value;void *data;u8 *val, *p = &tas5707_EQ_table[0];unsigned int i = 0, addr;data = kmemdup(ucontrol->value.bytes.data,params->max, GFP_KERNEL | GFP_DMA);if (!data)return -ENOMEM;val = (u8 *)data;memcpy(p, val, params->max / sizeof(u8));for (i = 0; i < 14; i++) {addr = DDX_CH1_BQ_0 + i;regmap_raw_write(tas5707->regmap,addr, p, TAS5707_EQ_PARAM_LENGTH);p += TAS5707_EQ_PARAM_LENGTH;}kfree(data);return 0;
}static int tas5707_get_EQ_param(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol)
{/*struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);*struct snd_soc_codec *codec = snd_soc_component_to_codec(component);*struct tas5707_priv *tas5707 = snd_soc_codec_get_drvdata(codec);*/unsigned int i, addr;u8 *val = (u8 *)ucontrol->value.bytes.data;u8 *p = &tas5707_EQ_table[0];for (i = 0; i < 14; i++) {addr = DDX_CH1_BQ_0 + i;/*regmap_raw_read(tas5707->regmap,* addr, p, TAS5707_EQ_PARAM_LENGTH);*/memcpy(val, p, TAS5707_EQ_PARAM_LENGTH);p += TAS5707_EQ_PARAM_LENGTH;val += TAS5707_EQ_PARAM_LENGTH;}return 0;
}
写完了kernel的kcontol接口,即通过tinyalsa封装接口往kernel传递
本文发布于:2024-02-02 23:16:16,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170688697547097.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |