08.音频系统:第003课_Linux音频驱动程序:第004节_声卡控制之kcontrol

一个芯片之中有多个寄存器,一个寄存器里,某些位用来表示某个功能。如下,那我们要分析的kcontrol是表示一个功能,还是表示一个寄存器呢?当然使用kcontrol表示一个功能比较好,如下:
在这里插入图片描述
在打开某个功能的时候,我们去操作某个kcontrol就可以了。显然我们找到了kcontrol的核心,寄存器的某些位来表示一个功能,其可能是一个位,也可能是多个位,显然这些位的读写函数都是各不相同。kcontrol中有自己的读写函数。如上图我们做些简单的总接:
1.一个声卡有多个kcontrol
2.一个kcontrol对应一个功能,如:音量,开关,录音。
3.kcontrol中有函数设置功能
下面我们开始分析代码,打开rt5651.c文件,找到:

static const struct snd_kcontrol_new rt5651_snd_controls[] = {
	//后续进行粘贴
	......
	......
};

可以发现该结构体rt5651_snd_controls,被snd_soc_codec_driver结构体的controls成员所指向:

static struct snd_soc_codec_driver soc_codec_dev_rt5651 = {
	.controls = rt5651_snd_controls,
	.num_controls = ARRAY_SIZE(rt5651_snd_controls),
}
snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5651,rt5651_dai, ARRAY_SIZE(rt5651_dai));
	/*通过machine最终导致snd_soc_register_card被调用*/
	ret = snd_soc_register_card(card);
		/*在该函数内,会做很多工作,如绑定dai等等*/
		ret = snd_soc_instantiate_card(card);
			snd_soc_add_card_controls(card, card->controls, card->num_controls);
				snd_soc_add_controls(card, soc_card->dev, controls, num_controls,NULL, soc_card);
					const struct snd_kcontrol_new *control = &controls[i];
					snd_ctl_add(card, snd_soc_cnew(control, data,control->name, prefix));
						list_add_tail(&kcontrol->list, &card->controls);
						

可以知道soc_codec_dev_rt5651会被snd_soc_register_codec函数注册。最终被添加到&kcontrol->list列表之中,这个列表挂载着一堆snd_kcontrol *kcontrol,如下:
在这里插入图片描述
我们看看snd_kcontrol 结构体存在什么东西:

struct snd_ctl_elem_id {
	unsigned int numid;		/* numeric identifier, zero = invalid */
	snd_ctl_elem_iface_t iface;	/* interface identifier */
	unsigned int device;		/* device/client number */
	unsigned int subdevice;		/* subdevice (substream) number */
	unsigned char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];		/* ASCII name of item */
	unsigned int index;		/* index of item */
};
struct snd_kcontrol {
	struct snd_ctl_elem_id id;
	unsigned int count;		/* count of same elements:相同元素个数 */
	snd_kcontrol_info_t *info;//获得kcontrol的信息
	snd_kcontrol_get_t *get//获得kcontrol的值(寄存器的一些值)
	snd_kcontrol_put_t *put;//设置kcontrol的值(寄存器的值)
	unsigned long private_value;//该值为给上面3个函数使用
	void *private_data;//该值为给上面3个函数使用(如指定寄存器的那些未等等)
}

那么以上的值,如struct snd_ctl_elem_id id,寄存器等等都是由我们芯片的驱动程序提供:
在这里插入图片描述
芯片驱动中有一系列的snd_kcontrol_new,用他们构造snd_kcontrol,然后添加到声卡之中。每个snd_kcontrol_new都存在snd_kcontrol_info_t *info,snd_kcontrol_get_t *get,snd_kcontrol_put_t *put;等等。
我了我们编写的方便,其提供了多个类似于SOC_DOUBLE_TLV,SOC_DOUBLE,SOC_DOUBLE等宏。

static const struct snd_kcontrol_new rt5651_snd_controls[] = {
	/* Headphone Output Volume */
	SOC_DOUBLE_TLV("HP Playback Volume", RT5651_HP_VOL,
		RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 39, 1, out_vol_tlv),
	/* OUTPUT Control */
	SOC_DOUBLE_TLV("OUT Playback Volume", RT5651_LOUT_CTRL1,
		RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 39, 1, out_vol_tlv),

	/* DAC Digital Volume */
	SOC_DOUBLE("DAC2 Playback Switch", RT5651_DAC2_CTRL,
		RT5651_M_DAC_L2_VOL_SFT, RT5651_M_DAC_R2_VOL_SFT, 1, 1),
	SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5651_DAC1_DIG_VOL,
			RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
			175, 0, dac_vol_tlv),
	SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5651_DAC2_DIG_VOL,
			RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
			175, 0, dac_vol_tlv),
	/* IN1/IN2 Control */
	SOC_SINGLE_TLV("IN1 Boost", RT5651_IN1_IN2,
		RT5651_BST_SFT1, 8, 0, bst_tlv),
	SOC_SINGLE_TLV("IN2 Boost", RT5651_IN1_IN2,
		RT5651_BST_SFT2, 8, 0, bst_tlv),
	/* INL/INR Volume Control */
	SOC_DOUBLE_TLV("IN Capture Volume", RT5651_INL1_INR1_VOL,
			RT5651_INL_VOL_SFT, RT5651_INR_VOL_SFT,
			31, 1, in_vol_tlv),
	/* ADC Digital Volume Control */
	SOC_DOUBLE("ADC Capture Switch", RT5651_ADC_DIG_VOL,
		RT5651_L_MUTE_SFT, RT5651_R_MUTE_SFT, 1, 1),
	SOC_DOUBLE_TLV("ADC Capture Volume", RT5651_ADC_DIG_VOL,
			RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
			127, 0, adc_vol_tlv),
	SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5651_ADC_DATA,
			RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
			127, 0, adc_vol_tlv),
	/* ADC Boost Volume Control */
	SOC_DOUBLE_TLV("ADC Boost Gain", RT5651_ADC_BST_VOL,
			RT5651_ADC_L_BST_SFT, RT5651_ADC_R_BST_SFT,
			3, 0, adc_bst_tlv),

	/* RT5651 ASRC Switch */
	SOC_ENUM_EXT("RT5651 ASRC Switch", rt5651_asrc_enum,
		     rt5651_asrc_get, rt5651_asrc_put),
	/* ASRC */
	SOC_SINGLE("IF1 ASRC Switch", RT5651_PLL_MODE_1,
		RT5651_STO1_T_SFT, 1, 0),
	SOC_SINGLE("IF2 ASRC Switch", RT5651_PLL_MODE_1,
		RT5651_STO2_T_SFT, 1, 0),
	SOC_SINGLE("DMIC ASRC Switch", RT5651_PLL_MODE_1,
		RT5651_DMIC_1_M_SFT, 1, 0),

	SOC_ENUM("ADC IF2 Data Switch", rt5651_if2_adc_enum),
	SOC_ENUM("DAC IF2 Data Switch", rt5651_if2_dac_enum),
};

我们进入SOC_DOUBLE_TLV宏:

#define SOC_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, tlv_array) \
{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
	.tlv.p = (tlv_array), \
	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
	.put = snd_soc_put_volsw, \
	.private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \
					  max, invert, 0) }

这个宏会帮我们实现info,put ,以及private_value函数,以及私有数据的构造,其私有数据存放的是SOC_DOUBLE_VALUE,其为一个结构体,如下:

#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert, xautodisable) \
	((unsigned long)&(struct soc_mixer_control) \
	{.reg = xreg, .rreg = xreg, .shift = shift_left, \
	.rshift = shift_right, .max = xmax, .platform_max = xmax, \
	.invert = xinvert, .autodisable = xautodisable})

其指定了:
reg :寄存开始,,rreg :寄存器结束,,shift_left:从哪一位开始shift_right:从那一位结束,xmax:用几位来表示
等等信息,这是一个简单的例子。
在开发板上执行tinymix,其会列举出所有的:
在这里插入图片描述

可以看到第一个"HP Playback Volume"与驱动程序的

SOC_DOUBLE_TLV("HP Playback Volume", RT5651_HP_VOL,RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 39, 1, out_vol_tlv),

相对应,其他的也是一样,依次如此。我们可以用过其序列,也可以通过其名字,其做控制。如:
tinymix “OUT Playback Volume”
得到:
OUT Playback Volume: 31 31 (range 0->39)
执行:
tinymix “OUT Playback Volume” 10
可以把其设置为10,然后再执行
tinymix “OUT Playback Volume”
可以把名字换成序列,即1,2,3,4,5等等。

前面提到snd_kcontrol 中存成员unsigned int count,代表有多少个元素,也就是说可以存在多个snd_ctl_elem_id,如果第一个snd_kcontrol 的snd_ctl_elem_id为1,count为3,name第二个snd_kcontrol 的snd_ctl_elem_id就要从3开始。

下面我们做一些总结:
在这里插入图片描述

  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个示例的ls_stk3332.c驱动程序,用于实现RK3588与STK3332之间的音频输入和输出功能: ``` #include <linux/module.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/slab.h> #include <linux/regmap.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/control.h> #define STK3332_REG_GAIN 0x00 #define STK3332_REG_VOLUME 0x01 #define STK3332_REG_MUTE 0x02 struct stk3332_data { struct regmap *regmap; unsigned int gain; unsigned int volume; bool mute; }; static int stk3332_set_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct stk3332_data *data = snd_kcontrol_chip(kcontrol); unsigned int gain = ucontrol->value.integer.value[0]; if (gain > 255) gain = 255; data->gain = gain; regmap_write(data->regmap, STK3332_REG_GAIN, gain); return 0; } static int stk3332_get_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct stk3332_data *data = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = data->gain; return 0; } static int stk3332_set_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct stk3332_data *data = snd_kcontrol_chip(kcontrol); unsigned int volume = ucontrol->value.integer.value[0]; if (volume > 255) volume = 255; data->volume = volume; regmap_write(data->regmap, STK3332_REG_VOLUME, volume); return 0; } static int stk3332_get_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct stk3332_data *data = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = data->volume; return 0; } static int stk3332_set_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct stk3332_data *data = snd_kcontrol_chip(kcontrol); bool mute = ucontrol->value.integer.value[0]; data->mute = mute; regmap_write(data->regmap, STK3332_REG_MUTE, mute ? 1 : 0); return 0; } static int stk3332_get_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct stk3332_data *data = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = data->mute ? 1 : 0; return 0; } static const struct snd_kcontrol_new stk3332_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Gain", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = snd_ctl_boolean_mono_info, .get = stk3332_get_gain, .put = stk3332_set_gain, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Volume", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = snd_ctl_boolean_mono_info, .get = stk3332_get_volume, .put = stk3332_set_volume, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mute", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = snd_ctl_boolean_mono_info, .get = stk3332_get_mute, .put = stk3332_set_mute, }, { } /* end */ }; static int stk3332_pcm_open(struct snd_pcm_substream *substream) { return 0; } static int stk3332_pcm_close(struct snd_pcm_substream *substream) { return 0; } static int stk3332_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { return 0; } static int stk3332_pcm_hw_free(struct snd_pcm_substream *substream) { return 0; } static struct snd_pcm_ops stk3332_pcm_ops = { .open = stk3332_pcm_open, .close = stk3332_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = stk3332_pcm_hw_params, .hw_free = stk3332_pcm_hw_free, }; static int stk3332_pcm_new(struct snd_card *card, struct snd_soc_pcm_runtime *rtd) { struct snd_pcm *pcm; int ret; ret = snd_pcm_new(card, "STK3332 PCM", 0, 1, 0, &pcm); if (ret < 0) return ret; pcm->private_data = rtd; pcm->info_flags = 0; strcpy(pcm->name, "STK3332 PCM"); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &stk3332_pcm_ops); return 0; } static int stk3332_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct stk3332_data *data; struct regmap *regmap; regmap = dev_get_regmap(dev->parent, NULL); if (!regmap) { dev_err(dev, "Failed to get regmap\n"); return -ENODEV; } data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->regmap = regmap; data->gain = 128; data->volume = 128; data->mute = false; platform_set_drvdata(pdev, data); // 注册音频控制器 snd_soc_register_codec(&pdev->dev, &codec_stk3332, &stk3332_dai, 1); // 注册PCM设备 return snd_soc_new_pcm_runtime(&pdev->dev, &stk3332_pcm_dai, &stk3332_pcm_platform, 0); } static int stk3332_remove(struct platform_device *pdev) { snd_soc_unregister_codec(&pdev->dev); return 0; } static const struct of_device_id stk3332_of_match[] = { { .compatible = "st,stk3332", }, { }, }; MODULE_DEVICE_TABLE(of, stk3332_of_match); static struct platform_driver stk3332_driver = { .probe = stk3332_probe, .remove = stk3332_remove, .driver = { .name = "stk3332", .of_match_table = stk3332_of_match, }, }; module_platform_driver(stk3332_driver); MODULE_DESCRIPTION("STK3332 audio driver"); MODULE_AUTHOR("Your Name"); MODULE_LICENSE("GPL"); ``` 以上驱动程序仅供参考,具体的驱动程序要根据实际的硬件连接和应用需求进行调整和修改。同时,需要根据实际情况进行dts配置和驱动程序的加载,以实现音频输入和输出的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江南才尽,年少无知!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值