Linux alsa kcontrol
定义
Control接口主要作用是应用程序(alsa-lib)可以访问kcontrol 来控制
音频
codec芯片中的多路开关,滑动控件等。对于Mixer(混音)来说,所有的mixer工作都是通过control接口的API来实现的。一个kcontrol可以代表一个
mixer
(混音器),或者是一个
mux
(多路开关),又或者是一个
Volume
(音量控制器)等等。
kcontrol相关定义
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 kcontrol name*/
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;
};
iface的设备分类
typedef int __bitwise snd_ctl_elem_iface_t;
#define SNDRV_CTL_ELEM_IFACE_CARD ((__force snd_ctl_elem_iface_t) 0) /* global control */
#define SNDRV_CTL_ELEM_IFACE_HWDEP ((__force snd_ctl_elem_iface_t) 1) /* hardware dependent device */
#define SNDRV_CTL_ELEM_IFACE_MIXER ((__force snd_ctl_elem_iface_t) 2) /* virtual mixer device */
#define SNDRV_CTL_ELEM_IFACE_PCM ((__force snd_ctl_elem_iface_t) 3) /* PCM device */
#define SNDRV_CTL_ELEM_IFACE_RAWMIDI ((__force snd_ctl_elem_iface_t) 4) /* RawMidi device */
#define SNDRV_CTL_ELEM_IFACE_TIMER ((__force snd_ctl_elem_iface_t) 5) /* timer device */
#define SNDRV_CTL_ELEM_IFACE_SEQUENCER ((__force snd_ctl_elem_iface_t) 6) /* sequencer client */
access 类型
#define SNDRV_CTL_ELEM_ACCESS_READ (1<<0)
#define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1)
#define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE)
#define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) /* control value may be changed without a notification */
#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<3) /* when was control changed */
#define SNDRV_CTL_ELEM_ACCESS_TLV_READ (1<<4) /* TLV read is possible */
#define SNDRV_CTL_ELEM_ACCESS_TLV_WRITE (1<<5) /* TLV write is possible */
#define SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE (SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
#define SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND (1<<6) /* TLV command is possible */
#define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) /* control does actually nothing, but may be updated */
#define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) /* write lock */
#define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) /* write lock owner */
#define SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK (1<<28) /* kernel use a TLV callback */
#define SNDRV_CTL_ELEM_ACCESS_USER (1<<29) /* user space element */
kcontrol实例
我们来看一下rk817的codec的kcontrol定义
static struct snd_kcontrol_new rk817_snd_path_controls[] = { //定义一个kcontrol数组
SOC_ENUM_EXT("Playback Path", rk817_playback_path_type,
rk817_playback_path_get, rk817_playback_path_put),
SOC_ENUM_EXT("Capture MIC Path", rk817_capture_path_type,
rk817_capture_path_get, rk817_capture_path_put),
};
看一下SOC_ENUM_EXT的定义,kcontrol定义成mixer类型,name分别为playback和capture,相关的get和put函数接着往下看
#define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_enum_double, \
.get = xhandler_get, .put = xhandler_put, \
.private_value = (unsigned long)&xenum }
rk817_playback_path_put函数
static int rk817_playback_path_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component);
long int pre_path;
if (rk817->playback_path == ucontrol->value.integer.value[0]) {
DBG("%s : playback_path is not changed!\n",
__func__);
return 0;
}
pre_path = rk817->playback_path;
rk817->playback_path = ucontrol->value.integer.value[0];
DBG("%s : set playback_path %ld, pre_path %ld\n",
__func__, rk817->playback_path, pre_path);
if (rk817->playback_path != OFF)
clk_prepare_enable(rk817->mclk);
else
clk_disable_unprepare(rk817->mclk);
switch (rk817->playback_path) {
case OFF:
if (pre_path != OFF && (pre_path != HP_PATH &&
pre_path != HP_NO_MIC && pre_path != RING_HP &&
pre_path != RING_HP_NO_MIC)) {
rk817_codec_power_down(component, RK817_CODEC_PLAYBACK);
if (rk817->capture_path == 0)
rk817_codec_power_down(component, RK817_CODEC_ALL);
}
break;
case RCV:
case SPK_PATH:
case RING_SPK:
if (pre_path == OFF)
rk817_codec_power_up(component, RK817_CODEC_PLAYBACK);
if (rk817->out_l2spk_r2hp) {
/* for costdown: ldac -> ClassD rdac -> Hp */
/* HP_CP_EN , CP 2.3V */
snd_soc_component_write(component, RK817_CODEC_AHP_CP,
0x11);
/* power on HP two stage opamp ,HP amplitude 0db */
snd_soc_component_write(component, RK817_CODEC_AHP_CFG0,
0x80);
/* power on dac ibias/l/r */
snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1,
PWD_DACBIAS_ON | PWD_DACD_ON |
PWD_DACL_ON | PWD_DACR_ON);
/* CLASS D mode */
snd_soc_component_write(component,
RK817_CODEC_DDAC_MUTE_MIXCTL,
0x18);
/* CLASS D enable */
snd_soc_component_write(component,
RK817_CODEC_ACLASSD_CFG1,
0xa5);
/* restart CLASS D, OCPP/N */
snd_soc_component_write(component,
RK817_CODEC_ACLASSD_CFG2,
0xf7);
} else if (!rk817->use_ext_amplifier) {
/* power on dac ibias/l/r */
snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1,
PWD_DACBIAS_ON | PWD_DACD_ON |
PWD_DACL_DOWN | PWD_DACR_DOWN);
/* CLASS D mode */
snd_soc_component_write(component,
RK817_CODEC_DDAC_MUTE_MIXCTL,
0x10);
/* CLASS D enable */
snd_soc_component_write(component,
RK817_CODEC_ACLASSD_CFG1,
0xa5);
/* restart CLASS D, OCPP/N */
snd_soc_component_write(component,
RK817_CODEC_ACLASSD_CFG2,
0xf7);
} else {
/* HP_CP_EN , CP 2.3V */
snd_soc_component_write(component, RK817_CODEC_AHP_CP,
0x11);
/* power on HP two stage opamp ,HP amplitude 0db */
snd_soc_component_write(component, RK817_CODEC_AHP_CFG0,
0x80);
/* power on dac ibias/l/r */
snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1,
PWD_DACBIAS_ON | PWD_DACD_DOWN |
PWD_DACL_ON | PWD_DACR_ON);
snd_soc_component_update_bits(component,
RK817_CODEC_DDAC_MUTE_MIXCTL,
DACMT_ENABLE, DACMT_DISABLE);
}
snd_soc_component_write(component, RK817_CODEC_DDAC_VOLL,
rk817->spk_volume);
snd_soc_component_write(component, RK817_CODEC_DDAC_VOLR,
rk817->spk_volume);
break;
case HP_PATH:
case HP_NO_MIC:
case RING_HP:
case RING_HP_NO_MIC:
if (pre_path == OFF)
rk817_codec_power_up(component, RK817_CODEC_PLAYBACK);
/* HP_CP_EN , CP 2.3V */
snd_soc_component_write(component, RK817_CODEC_AHP_CP, 0x11);
/* power on HP two stage opamp ,HP amplitude 0db */
snd_soc_component_write(component, RK817_CODEC_AHP_CFG0, 0x80);
/* power on dac ibias/l/r */
snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1,
PWD_DACBIAS_ON | PWD_DACD_DOWN |
PWD_DACL_ON | PWD_DACR_ON);
snd_soc_component_update_bits(component,
RK817_CODEC_DDAC_MUTE_MIXCTL,
DACMT_ENABLE, DACMT_DISABLE);
snd_soc_component_write(component, RK817_CODEC_DDAC_VOLL,
rk817->hp_volume);
snd_soc_component_write(component, RK817_CODEC_DDAC_VOLR,
rk817->hp_volume);
break;
case BT:
break;
case SPK_HP:
case RING_SPK_HP:
if (pre_path == OFF)
rk817_codec_power_up(component, RK817_CODEC_PLAYBACK);
/* HP_CP_EN , CP 2.3V */
snd_soc_component_write(component, RK817_CODEC_AHP_CP, 0x11);
/* power on HP two stage opamp ,HP amplitude 0db */
snd_soc_component_write(component, RK817_CODEC_AHP_CFG0, 0x80);
/* power on dac ibias/l/r */
snd_soc_component_write(component, RK817_CODEC_ADAC_CFG1,
PWD_DACBIAS_ON | PWD_DACD_ON |
PWD_DACL_ON | PWD_DACR_ON);
if (!rk817->use_ext_amplifier) {
/* CLASS D mode */
snd_soc_component_write(component,
RK817_CODEC_DDAC_MUTE_MIXCTL,
0x10);
/* CLASS D enable */
snd_soc_component_write(component,
RK817_CODEC_ACLASSD_CFG1,
0xa5);
/* restart CLASS D, OCPP/N */
snd_soc_component_write(component,
RK817_CODEC_ACLASSD_CFG2,
0xf7);
}
snd_soc_component_write(component, RK817_CODEC_DDAC_VOLL,
rk817->hp_volume);
snd_soc_component_write(component, RK817_CODEC_DDAC_VOLR,
rk817->hp_volume);
break;
default:
return -EINVAL;
}
return 0;
}
注册kcontrol实例
-->rk817_probe
-->rk817_reset(component)
-->snd_soc_add_component_controls(component, rk817_snd_path_controls, //把control添加到component组件中
ARRAY_SIZE(rk817_snd_path_controls));
-->snd_soc_add_controls(card, component->dev, controls,
num_controls, component->name_prefix, component)
--> for (i = 0; i < num_controls; i++) {
const struct snd_kcontrol_new *control = &controls[i];
err = snd_ctl_add(card, snd_soc_cnew(control, data,
control->name, prefix));
-->-->snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
-->__snd_ctl_add(card, kcontrol)
__snd_ctl_add具体实现,每个control都添加到cards中controls链表中,然后snd_ctl_notify通知用户有新的control变化
/* add a new kcontrol object; call with card->controls_rwsem locked */
static int __snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
struct snd_ctl_elem_id id;
unsigned int idx;
unsigned int count;
id = kcontrol->id;
if (id.index > UINT_MAX - kcontrol->count)
return -EINVAL;
if (snd_ctl_find_id(card, &id)) {
dev_err(card->dev,
"control %i:%i:%i:%s:%i is already present\n",
id.iface, id.device, id.subdevice, id.name, id.index);
return -EBUSY;
}
if (snd_ctl_find_hole(card, kcontrol->count) < 0)
return -ENOMEM;
list_add_tail(&kcontrol->list, &card->controls);
card->controls_count += kcontrol->count;
kcontrol->id.numid = card->last_numid + 1;
card->last_numid += kcontrol->count;
id = kcontrol->id;
count = kcontrol->count;
for (idx = 0; idx < count; idx++, id.index++, id.numid++)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
return 0;
}
应用对应的control
使用amixer contents 我们可以看到4个mxier control ,numid = 1 和numid = 2 是codec rk817_snd_path_controls生成的,两个音量根据
配置生成的softvol 控件
[root@:/]# amixer contents
numid=4,iface=MIXER,name='Master Playback Volume'
; type=INTEGER,access=rw---RW-,values=2,min=0,max=255,step=0
: values=255,255
| dBscale-min=-20.00dB,step=0.03dB,mute=0
numid=2,iface=MIXER,name='Capture MIC Path'
; type=ENUMERATED,access=rw------,values=1,items=5
; Item #0 'MIC OFF'
; Item #1 'Main Mic'
; Item #2 'Hands Free Mic'
; Item #3 'BT Sco Mic'
; Item #4 'Main Mic To Fm23'
: values=0
numid=1,iface=MIXER,name='Playback Path'
; type=ENUMERATED,access=rw------,values=1,items=11
; Item #0 'OFF'
; Item #1 'RCV'
; Item #2 'SPK'
; Item #3 'HP'
; Item #4 'HP_NO_MIC'
; Item #5 'BT'
; Item #6 'SPK_HP'
; Item #7 'RING_SPK'
; Item #8 'RING_HP'
; Item #9 'RING_HP_NO_MIC'
; Item #10 'RING_SPK_HP'
: values=0
numid=3,iface=MIXER,name='Digital Capture Volume'
; type=INTEGER,access=rw---RW-,values=2,min=0,max=120,step=0
: values=72,72
| dBscale-min=-30.00dB,step=0.41dB,mute=0
版权声明:本文为qq_39678541原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。