RK3568调试记:为RK809添加音量调节功能

最近在搞一个RK3568的项目,用到了codec的录音放音功能。使用aplay来播放wav,差点把我耳朵震聋。于是我打算使用alsamixer来调小音量,结果给我整了这么一出:

没有音量条也就无法调节音量,使用amixer命令看一下:

仅有两个控件,而这两个控件也不是调音量的,难道说RK3568的codec不支持音量调节吗?
RK3568使用的codec芯片是rk809,同时也是PMIC(就很神奇)。带着问题百度了下,发现可以通过修改设备树来调节音量:

rk809_codec: codec {
			#sound-dai-cells = <0>;
			compatible = "rockchip,rk809-codec", "rockchip,rk817-codec";
			clocks = <&cru I2S1_MCLKOUT>;
			clock-names = "mclk";
			assigned-clocks = <&cru I2S1_MCLKOUT>, <&cru I2S1_MCLK_TX_IOE>;
			assigned-clock-rates = <12288000>;
			assigned-clock-parents = <&cru I2S1_MCLKOUT_TX>, <&cru I2S1_MCLKOUT_TX>;
			pinctrl-names = "default";
			pinctrl-0 = <&i2s1m0_mclk>;
			hp-volume = <20>;  //此处修改耳机音量,3~255即1.125db~-95db,0.375db/step
			spk-volume = <3>;  //此处修改喇叭音量,3~255即1.125db~-95db,0.375db/step
			mic-in-differential;
			status = "okay";
		};

修改后再播放果然生效,证明rk809是具备调节音量的功能的。

但问题还是没有解决,总不能听个音乐想调下音量,于是修改设备树,重新编译,烧写到产品中…正确的做法是把音量调节控件添加到codec驱动中使其支持音量调节功能。

根据设备树rk809的compatible属性找到它的驱动源码sound/soc/codecs/rk817_codec.c。找到它的控件注册位置:

static int rk817_probe(struct snd_soc_component *component)
{
	/* 省略 */
	snd_soc_add_component_controls(component, rk817_snd_path_controls,
				       ARRAY_SIZE(rk817_snd_path_controls));
	return 0;
}

rk817_snd_path_controls就是控件对象了,它的定义也在本文件中:

static struct snd_kcontrol_new rk817_snd_path_controls[] = {
	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),
};

这两个控件与amixer命令查找出了两个控件相吻合。需要添加第三个控件用于音量调节。

这里我的做法是参考一个比较熟悉的codec的驱动wm8960(sound/soc/codecs/wm8960.c),它是这样添加音量调节控件的:

这个宏的原型:

include/sound/soc.h
SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array)
参数:
xname: 控件件名称,
reg_left: 左声道寄存器地址
reg_right: 右声道寄存器地址
xshift: 控件功能在寄存器中需要修改第几个bit
xmax: 寄存器可写入的最大值
xinvert: 写入寄存器的值是否需要反转(0: 写0就是0; 1: 写0为xmax-0)
tlv_array: 控制元数据(就是一个容器),alsa以此来定义dB范围/比例

宏展开SOC_DOUBLE_R_TLV(“Playback Volume”, WM8960_LDAC, WM8960_RDAC,
0, 255, 0, dac_tlv),

SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC, 0, 255, 0, dac_tlv),
{
	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
	.name = ("Playback Volume"),
	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.tlv.p = (dac_tlv),  //控制元数据
	.info = snd_soc_info_volsw,
	.get = snd_soc_get_volsw,  //amixer的sget或cget的回调
	.put = snd_soc_put_volsw,  //amixer的sset或cset的回调
	.private_value = SOC_DOUBLE_R_VALUE( WM8960_LDAC,   WM8960_RDAC,             0,        255,                0)
  //.private_value = SOC_DOUBLE_R_VALUE(左声道音量reg,  右声道音量reg, 寄存器位offset, 寄存器最大值, 设置的值是否反转)
},

再看它的元数据dac_tlv的定义:

static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
dac_tlv: 元数据变量
-12750: 最小增益(单位0.01dB),即最小音量为-127.5dB,需要参考codec datasheet
50: 调节音量的步长(单位0.01dB),0.5dB 0~255即-127.5dB~0dB,127.5/256约为0.5
1: 音量最小是是否需要静音(0: 否; 1: 是)

rk809的道理基本一样,只是需要确定音量调节的寄存器地址,查看rk809的datasheet:

二者在sound/soc/codecs/rk817_codec.h中有定义:

#define RK817_CODEC_DDAC_VOLL		(RK817_CODEC_BASE + 0x31)
#define RK817_CODEC_DDAC_VOLR		(RK817_CODEC_BASE + 0x32)

最后照猫画虎,添加音量调节控件:

#include <sound/tlv.h>  //+ 头文件。否则编译找不到宏DECLARE_TLV_DB_SCALE
/* 省略 */
static const DECLARE_TLV_DB_SCALE(dac_tlv, -9500, 37, 1);  //+ 音量调节控件元数据,根据rk809datasheet -1.125db~-95db,0.375db/step

static struct snd_kcontrol_new rk817_snd_path_controls[] = {
	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_DOUBLE_R_TLV("Playback Volume", RK817_CODEC_DDAC_VOLL,  RK817_CODEC_DDAC_VOLR,
		 0, 255, 0, dac_tlv),  //+ 音量调节控件
};

编译烧写内核,alsamixer成功出现音量条:

amixer查看,出现音量控件’Playback’:

使用amixer contents查看详情:

分别用amixer sset Playback 0,0以及amixer sset Playback 255,255测试最大和最小音量:

然后使用aplay放歌出现了一个小bug,使用amixer sset Playback 0,0原本表示静音却放出了最大音量,而amixer sset Playback 255,255是最小音量,这与标定值相反。控件的配置是照搬wm8960的,对比wm8960和rk809的datasheet得到答案:
rk809:

wm8960:

wm8960音量与寄存器值对应关系为255对应最大值0dB,而rk809 255对应最小值-95dB,正好相反!因此需要修改控件参数:

static struct snd_kcontrol_new rk817_snd_path_controls[] = {
	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_DOUBLE_R_TLV("Playback Volume", RK817_CODEC_DDAC_VOLL,  RK817_CODEC_DDAC_VOLR,
		 0, 255, 1/*这里把0修改为1,表示寄存器写入x,实际值为255-x*/, dac_tlv),
};

成功修复bug!

物联沃分享整理
物联沃-IOTWORD物联网 » RK3568调试记:为RK809添加音量调节功能

发表评论