Linux alsa kcontrol

  • Post author:
  • Post category:linux




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 版权协议,转载请附上原文出处链接和本声明。