[Linux Audio Driver] 高通平台内部MIC_BIAS简介

#更新 2020.05.10

我觉得我这个标题取的不是很妥当,为了表达对技术的敬畏之心,我将原标题
《一文搞懂内部MIC_BIAS》修改为《高通平台内部MIC_BIAS简介》

#更新2020.05.10

#更新 2021.10.28
这篇文章说的麦克偏置都是老平台的东西了,新平台比如一些5G平台是没有啥内部偏置的,高通给的硬件参考设计也没有。即无论是硅麦还是驻极体麦克,都是用的外部偏置。
#更新2021.10.28

1. 问题背景

代码路径:

 kernel/msm-3.18/sound/soc/codecs/msm8x16-wcd.c

在这里插入图片描述

今天一个学长,问我这个这个修改几个意思,我为啥要改这个;我随口就说,两个都是关闭主、副麦克内部供电,因为是MEMS麦克(硅麦),如果是驻极体麦克的话,就要打开这个两个偏置,分别写

(codec,micb_int_reg,0x80,0x80);
(codec,micb_int_reg,0x02,0x02);

然后学长问是MIC_BIAS,我说应该是吧…(有点没底气…),我给他截了两个寄存器介绍:

在这里插入图片描述
在这里插入图片描述
( 第7位表明 主麦克的内部供电的开关,第1位表明副麦克的内部供电开关)

然后学长问我有没有硬件原理图;我掏出来两张珍藏许久的原理图。。。。

在这里插入图片描述
在这里插入图片描述
第一张是驻极体麦克(ECM),第二张是模拟硅麦(MEMS);然后学长问…你在逗我吗,驻极体麦克的
供电我都找不到,什么关闭内部偏置。。。

在这里插入图片描述

2. 先解释硬件

在这里插入图片描述

首先高通芯片 PMU内部有 MIC_BIAS,PMU外部也有 MIC_BIAS,取决于你是怎么上拉的;
MIC1_P(主麦)和MIC3_P(副麦)内部上拉到MICBIAS1,MIC2_P(耳机麦)内部上拉到MICBIAS2;

在这里插入图片描述
在这里插入图片描述
(灵魂画手。。希望大佬们不要介意…)

对于ECM麦克来说,它需要打开内部MIC_BIAS,而对于MEMS麦克来说,它需要关闭内部MIAS,使用外部的MIC_BIAS.

在这里插入图片描述
具体到代码上就是设备树里面这样配置,表明都是使用的PMU内部的 MIC_BIAS,而若你要使用PMU外部的
MIC_BIAS,应该这样写:

 "MIC BIAS External1", "Handset Mic",
 "MIC BIAS External2", "Headset Mic",
 "MIC BIAS External3", "Secondary Mic",

update on 2020.03.12
/**********************************************************************/
经大佬提醒,我这个地方写的有点片面;就是如果设备树里面是配置的是内部mic bias的话,以上
的修改都OK,但是如果配置的外部mic bias,也是可以通过寄存器来 开关mic bias的,就在原修改的
下面一点点:

代码路径:kernel/msm-3.18/sound/soc/codecs/msm8x16-wcd.c
char *external2_text = "External2";
char *external_text = "External";

if (!strnstr(w->name, external_text, strlen(w->name)))
	snd_soc_update_bits(codec,
		MSM8X16_WCD_A_ANALOG_MICB_1_EN, 0x05, 0x04);
if (w->reg == MSM8X16_WCD_A_ANALOG_MICB_1_EN)
	msm8x16_wcd_configure_cap(codec, true, micbias2);

/**********************************************************************/

OK,其实我们已经知道了,我之前回答学长的问题是不准确的;正确答案应该是: 基于当前的电路设计,驻极体(ECM)使用的是PMU内部的 MIC_BIAS,所以我们要把那两个bit打开(置1),而对于模拟硅麦(MEMS),由于是使用的PMU外部的MIC_BIAS,所以我们要把PMU内部的MIC_BIAS关闭,这样才能让MEMS麦克正常工作。

3. 再解释软件

作为社会主义的接班人,尤其是接受了九年义务教育的人,咱不能满足于此,还需要继续分析…

在这里插入图片描述

micbias2 = (snd_soc_read(codec, MSM8X16_WCD_A_ANALOG_MICB_2_EN) & 0x80);
	switch (event) {
	case SND_SOC_DAPM_PRE_PMU:
		if (strnstr(w->name, internal1_text, strlen(w->name))) {
			if (get_codec_version(msm8x16_wcd) >= CAJON)
				snd_soc_update_bits(codec,
					MSM8X16_WCD_A_ANALOG_TX_1_2_ATEST_CTL_2,
					0x02, 0x02);
			
			snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x00);
		} else if (strnstr(w->name, internal2_text, strlen(w->name))) {
			snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x10);
			snd_soc_update_bits(codec, w->reg, 0x60, 0x00);
		} else if (strnstr(w->name, internal3_text, strlen(w->name))) {
			snd_soc_update_bits(codec, micb_int_reg, 0x02, 0x00);

灵魂拷问: snd_soc_update_bits函数是怎么把寄存器写进去的???

./kernel/msm-3.18/include/sound/soc.h

int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg,
				unsigned int mask, unsigned int value);

这里定义了函数的原型,但是没有初始化;

./kernel/msm-3.18/sound/soc/soc-io.c

int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg,
				unsigned int mask, unsigned int value)
{
	return snd_soc_component_update_bits(&codec->component, reg, mask,
		value);
}

我们继续追踪代码,看下snd_soc_component_update_bits函数;

int snd_soc_component_update_bits(struct snd_soc_component *component,
	unsigned int reg, unsigned int mask, unsigned int val)
{
	bool change;
	int ret;

	if (component->regmap)
		ret = regmap_update_bits_check(component->regmap, reg, mask,
			val, &change);
	else
		ret = snd_soc_component_update_bits_legacy(component, reg,
			mask, val, &change);

	if (ret < 0)
		return ret;
	return change;
}

component->regmap这个是一个虚拟的内存映射,表明如果这里进行了虚拟内存映射的话,
代码就走if后面的,否则就走else下面的,
这个是玩Linux kernel那帮大佬调试用的,具体我没用过;

其实两者都是一个意思啊,如果用SI追代码的话,从regmap_update_bits_check函数进去,到_regmap_update_bits函数;

static int _regmap_update_bits(struct regmap *map, unsigned int reg,
			       unsigned int mask, unsigned int val,
			       bool *change)
{
	int ret;
	unsigned int tmp, orig;

	ret = _regmap_read(map, reg, &orig);
	if (ret != 0)
		return ret;

	tmp = orig & ~mask;
	tmp |= val & mask;

	if (tmp != orig) {
		ret = _regmap_write(map, reg, tmp);
		if (change)
			*change = true;
	} else {
		if (change)
			*change = false;
	}

	return ret;
}

或者直接追snd_soc_component_update_bits_legacy函数,

static int snd_soc_component_update_bits_legacy(
	struct snd_soc_component *component, unsigned int reg,
	unsigned int mask, unsigned int val, bool *change)
{
	unsigned int old, new;
	int ret;

	if (!component->read || !component->write)
		return -EIO;

	mutex_lock(&component->io_mutex);

	ret = component->read(component, reg, &old);
	if (ret < 0)
		goto out_unlock;

	new = (old & ~mask) | (val & mask);
	*change = old != new;
	if (*change)
		ret = component->write(component, reg, new);
out_unlock:
	mutex_unlock(&component->io_mutex);

	return ret;
}

解释下核心代码:

new = (old & ~mask) | (val & mask);

(old & ~mask) //原来寄存器mask标志位上的值清0,
(val & mask) // val是你要写入的值,这个是把mask位的值修改成val的值

回到我们最开始:

(codec,micb_int_reg,0x02,0x02); // 第一个0x02指的就是第一位,即mask位; 第二个0x02,是
指的是把这个值修改为1,这个0x02就是val的值。

mutex_lock(&component->io_mutex);

特别提一下这里有个mutex互斥锁,说明这个该函数很重要,不能被中断打断;

其实早期的kernel里面有注解;

 * snd_soc_component_update_bits() - Perform read/modify/write cycle
 * @component: Component to update
 * @reg: Register to update
 * @mask: Mask that specifies which bits to update
 * @val: New value for the bits specified by mask
 *
 * Return: 1 if the operation was successful and the value of the register
 * changed, 0 if the operation was successful, but the value did not change.
 * Returns a negative error code otherwise.

int snd_soc_component_update_bits(struct snd_soc_component *component,
	unsigned int reg, unsigned int mask, unsigned int val)
....

很清晰,已经指出了mask以及val的意思;

4. 作者注

/******
@article{Linux Audio Driver,
Author = { 1byte ≠ 8bit},
Year = { 2020},
}
******/

希望对大家有帮助.

OVER.

  • 7
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
### 回答1: linux_android_audio_customization_a.pdf是关于Linux和Android音频定制的资料。在这个文件中,介绍了Linux和Android操作系统中的音频定制的方法和过程。 首先,文档列出了音频定制的背景和原因。在现代移动设备中,音频在娱乐、通信和其他功能中扮演着至关重要的角色。因此,为了满足不同用户的需求,定制化音频配置具有很大的意义。 接着,文档详细解释了在Linux和Android系统中进行音频定制的步骤和工具。对于Linux系统,文档提到了常见的音频框架,如Alsa和PulseAudio,以及它们的应用和配置。对于Android系统,文档提供了关于HAL(硬件抽象层)的概述,以及如何在设备上进行音频配置的具体步骤。 此外,文档还讨论了一些常见音频定制的实例和解决方案。例如,如何配置系统音频参数,如音量、均衡和混音。文档还提供了与外部音频设备集成的相关信息,如蓝牙音箱和耳机。 最后,文档对音频测试和调试进行了简要介绍。它列出了一些常见的音频问题,并提供了一些调试和分析工具,以帮助开发人员解决这些问题。 总之,linux_android_audio_customization_a.pdf 提供了关于Linux和Android音频定制的综合指南。无论是对于开发者还是对于希望进行系统定制的用户来说,这个文档都非常有用。它详细解释了音频定制的步骤、工具和实际案例,帮助读者理解和应用这些知识。 ### 回答2: 《linux_android_audio_customization_a.pdf》是一份关于Linux和Android音频定制的文档。这份文档探讨了在Linux和Android系统中如何定制音频功能的方法和技巧。 首先,文档介绍了Linux和Android音频框架的基本概念和组成部分。它解释了音频硬件、驱动程序、HAL(硬件抽象层)、AudioFlinger等之间的关系。读者可以了解到音频在系统中的流程和工作原理。 接下来,文档讨论了音频硬件的配置和驱动程序的定制。它解释了如何选择和配置适合系统需求的音频硬件,以及如何定制和优化音频驱动程序以实现更好的音频性能。 文档还涵盖了音频HAL的定制方法。它介绍了HAL的作用以及如何实现音频功能的定制和扩展。读者可以学习到如何添加新的音频特性、处理音频事件和数据传输等。 此外,文档还包括了关于Android的音频策略和音频焦点管理的内容。它讲解了如何定义和管理音频策略,以及在多个应用程序同时请求音频焦点时如何处理。 总之,《linux_android_audio_customization_a.pdf》是一份关于Linux和Android音频定制的详细文档。它提供了针对音频硬件、驱动程序和HAL的定制方法,以及关于音频策略和焦点管理的指导。无论是对于音频开发人员还是系统定制者来说,这份文档都是一份宝贵的参考资料。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值