打开/关闭搜索
搜索
打开/关闭菜单
通知
打开/关闭个人菜单
查看“高通Windows平台音频驱动适配”的源代码
来自Uotan Wiki · 刷机百科
查看
阅读
查看源代码
查看历史
associated-pages
页面
讨论
更多操作
←
高通Windows平台音频驱动适配
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
''<u>声明:本篇中可能存在大量错误,欢迎指正。</u>'' '''<big>前言</big>''' 在高通平台上,常见的扬声器解决方案主要有: # WCD -- SWR --> WSA --> Speaker(例如WCD9340 + WSA8815) # WCD -- I2S --> 3rd-party Amplifier --> Speaker(例如WCD9340 + TFA9874) 大多数OEM机器采用三方功放,部分厂商采用高通的WSA方案,高通工程机绝大部分采用WCD + WSA方案。 本文讨论方案2,也就是WCD + 三方功放方案。 '''<big>基础知识</big>''' <u>常见英文简写/名词说明:</u> * AP: Application Processor,应用处理器,这里指处理器 * WCD: 高通的Audio CodeC * [[wikipedia:I²C|I2C]]: 也常写作I²C/IIC, 只需要两根线就可以双向传输数据 * [[wikipedia:I²S|I2S]]: 集成电路内置音频总线,传输音频数据 * SWR: SoundWire,MIPI 2014年定义的音频接口 * SLIMBus: MIPI 2007年定义的音频接口 * DSDT: Differentiated System Description Table,ACPI表中的一个,用以描述平台的外围设备和系统硬件 * ASL: ACPI源语言 * Microsoft asl: 微软的ASL编译器 * iasl: Intel的ASL语言编译器 * AML: ACPI计算机语言,即ASL编译后产物 * dsl: iasl反编译AML之后产出的反编译源代码文件 * ACDB: Audio Calibration Database 高通平台音频配置 * amp: 下文对Amplifier即功放芯片的简称 * LA: Linux Android平台 * WP: WIndows平台 <u>硬件相关:</u> * WCD通过SPI, SLIMBus 与AP进行通信。 * 功放芯片通常通过I2C与AP进行通信,即我们可以通过i2c传输命令配置和控制功放芯片。 * AP通过I2S向功放芯片传输音频数据。 * I2S一般连接四根线,即BCK, WCK, DATAO, DATAI。 * 在Linux内核或者安卓内核源码中可以找到平台GPIO的功能配置等信息,例如[https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/drivers/pinctrl/qcom/pinctrl-sm8150.c#n1313 SM8150]。 * 高通的I2S不只一组,其命名编号通常为Primary,Secondary,Tertiary,Quaternary或其简写。 * 在Linux内核或者安卓内核中的设备树源码中可以寻找到I2C相关的属性,例如[https://github.com/OnePlusOSS/android_kernel_oneplus_sm8150/blob/oneplus/SM8150_SDX50M_Q_10.0/arch/arm64/boot/dts/qcom/sm8150-qupv3.dtsi SM8150] <u>软件相关:</u> 高通Windows平台已经提供了大部分音频相关的DSDT配置和驱动,即: # 基础的DSDT # 音频相关驱动以及inf中的默认配置 # 默认的ACDB 我们能够在Windows Update,或者[[WOA-Project]]的[https://github.com/WOA-Project/Qualcomm-Reference-Drivers Qualcomm-Reference-Drivers]上下载到的参考平台驱动和固件,其中对应关系: {| class="wikitable" |+ !移动PC平台 !移动平台 |- |SD850 |SD845 |- |7c Gen1/Gen2 |SD720 |- |7c Gen3 |SD778 |- |SD835 |SD835 |} 对于 * 如何从firmware cab中解包出ACPI表 * 如何反编译DSDT * 如何反编译dtb * 如何解决回编译.dsl中遇到的error * 如何反编译AeoB * 如何回编译AeoBSL 均不在本文探讨范围之内。 '''<u>所以,在阅读下述内容时,假设你已经拥有DSDT表和Audio相关驱动,并且说实在的,这是最常见的情况。</u>''' '''<big>配置DSDT表</big>''' 对于使用三方功放的平台,我们需要在DSDT中定义: * 功放设备,其中包含了功放的硬件连接的资源描述,主要为I2C和Gpio中断(如果你的驱动不处理中断,可以不定义) * 功放设备的电源管理配置,其中包含功放的Reset脚以及电源配置(如果你的功放在dts中未定义电源,此处也不需要定义) * I2S Gpio功能配置 * I2C Master设备定义 * I2C Master设备资源定义 下面我们以一加7T Pro为例,具体说说如何定义。 '''<u>定义功放设备:</u>''' 要在DSDT中定义我们的功放设备,首先我们需要了解其基本的连接属性,即挂在哪个i2c master下面,中断gpio是多少,reset gpio是多少,reset gpio是高电平使能还是低电平使能等等,这些通常在设备树中有所定义。 <u>''一:在设备树中寻找属性:''</u> 在安卓里面,打开<code>Device Info HW</code>软件,点击右上角<code>设置</code>,打开<code>显示i2c地址</code>和<code>使用Root</code>在首页,我们可以看到设备的音频那一栏写着诸如<code>tfa98xx (i2c 0-0034)</code>、<code>tas2557 (i2c 1-004c)</code> 、cs35l41 <code>(i2c 0-0040)</code>之类的字样,此时我们打开反编译之后的dts,搜索相关字段,例如我在一加7T Pro的dts中搜索tfaxx,可以找到相关节点:<blockquote> i2c@890000 { ...... tfa98xx_right@34 { compatible = "nxp,tfa98xx"; reg = <0x34>; reset-gpio = <0x38 0x25 0x00>; status = "ok"; phandle = <0x761>; }; tfa98xx_left@35 { compatible = "nxp,tfa98xx"; reg = <0x35>; reset-gpio = <0x38 0x64 0x00>; status = "ok"; phandle = <0x762>; }; ...... };</blockquote>一加7T Pro拥有双扬声器,故此处定义了左右两个tfa98xx设备。观察上下文即可得出tfa98xx设备位于<code>i2c@890000</code>下,reset 脚分别为<code>gpio37</code>(hex: 0x25)和<code>gpio100</code> (hex: 0x64),并且这两个slave设备在i2c上地址/ID分别为<code>0x34</code>和<code>0x35</code>。 <u>''二:使用asl创建设备:''</u> 新建文本文件cust_spkr.asl并粘贴如以下内容:<blockquote> Device (SPK1) { Name (_HID, "PLHD0001") Name (_UID, 0) Alias(\_SB.PSUB, _SUB) Name (_DEP, Package() { \_SB.GIO0, \_SB.I2CX }) Method (_CRS, 0x0, NotSerialized) { Name (RBUF, ResourceTemplate() { I2CSerialBus(0xAA, , 400000, AddressingMode7Bit, "\\_SB.I2CX",,,,) }) Return (RBUF) } }</blockquote>粘贴进去之后,我们需要修改几个地方, # <code>Device (SPK1)</code> 中的1修改为实际数值,例如有两个amp,则第二个设备名应该为<code>SPK2</code>,以此类推。 # 将<code>Name (_HID, "PLHD0001")</code>将此处的<code>PLHD0001</code>改成你的设备的名称,例如对于一加7TPro, 此处为<code>GTFA9874</code>,请记住你定义的_HID,下文驱动中还需要用到。 # <code>Name (_UID, 0)</code> 中的0修改为实际数值,例如有两个amp,则第二个设备的_UID应该为<code>1</code>,以此类推。 # <code>\_SB.I2CX</code> 中的X修改为实际数值,例如本文中amp挂在<code>i2c@890000</code>下,通过观察[https://github.com/OnePlusOSS/android_kernel_oneplus_sm8150/blob/31bebb6ffc476f51986bd04502a76ffbd9e2fa7a/arch/arm64/boot/dts/qcom/sm8150-qupv3.dtsi#L111 xxx-qupv3.dtsi源码]可知其属于qupv3_se4_i2c,故此处的<code>X</code>为 <code>5</code>(se号+1)。 # <code>I2CSerialBus(0xAA, , 400000, AddressingMode7Bit, "\\_SB.I2CX",,,,)</code> 中的<code>0xAA</code>修改为实际值,例如本文中第一个tfa98xx地址为<code>0x34</code>,则此处修改为<code>0x34</code>,<code>400000</code>为频率,通常为<code>400khz</code>或者<code>100khz,"\\_SB.I2CX"</code> 中的<code>X</code>需要修改为实际值,参考上条。 自此,我们已经在asl中完成了amp设备的定义。接下来,我们需要在DSDT.dsl中添加<code>Include("cust_spkr.asl")</code> 将我们的文件包含在DSDT.dsl中。 需要注意的是,这条Include语句必须位于 <code>Scope (\_SB){ ... }</code>内。 <u>'''定义I2C Master设备:'''</u> 上文中我们使用了<code>\_SB.I2CX</code>这个设备,但是大部分情况下这个设备都是没有被定义的,我们需要手动在dsdt中添加这个设备。 打开的DSDT.dsl,搜索<code>Device (I2C</code>,此时应当有较多和候选结果,如图: [[文件:Search I2C Device.png|缩略图|1077x1077像素]] 此时我们将<code>Device (I2CX) {</code> 到对应的 <code>}</code> 复制并粘贴一份,依照我们的需求做相应修改: # 修改粘贴后的文本中的<code>I2CX</code>的<code>X</code>为我们需要的se号+1。由于上文我们已经在dts中找到了i2c对应的se号,此处不在赘述。在一加7T Pro例子中,我们得到的是<code>5</code> 。注意,如果这里的<code>X</code>是两位数,你应当写作<code>ICX</code>,例如<code>IC17</code>。 # 修改<code>_UID</code>为实际值。此处与上文'''<code>X</code>'''的值保持相同,在一加7T Pro例子中,此处是<code>5</code>。 # 修改<code>Memory32Fixed</code>中的Address Base为实际值。在一加7T Pro的例子中,我们找到的<code>i2c@890000</code> 里面的<code>890000</code>即为此处的值,注意此数字为16精致数字,我们补上0x后,此处填写<code>0x890000</code>。 # 修改<code>Interrupt</code>里面的中断号。在上文中提到的[https://github.com/OnePlusOSS/android_kernel_oneplus_sm8150/blob/31bebb6ffc476f51986bd04502a76ffbd9e2fa7a/arch/arm64/boot/dts/qcom/sm8150-qupv3.dtsi#L114 xxxx-quiv3.dtsi]中有此中断号的定义,需要注意的是,对于GIC_SPI,此处的值需要+32,即 <code>605 + 32= 637</code>。 <u>'''配置I2CMaster设备的硬件资源'''</u> 在DSDT.dsl中,我们再次搜索<code>\\_SB.I2C</code>,我们会找到一个类似如下结构的地方(注意,此处我将缩进和可省略代码整理了一下以便能够放在这里): [[文件:Sorted I2C resource.png|缩略图|1099x1099像素]] 将你的搜索结果中的这一个<code>Package{...}</code>复制并粘贴这个<code>Package{...}</code>下方,然后修改这里的一些字符串和Gpio配置为我们需要的实际值: # 修改此处的<code>"\\_SB.I2CX"</code>为实际值,此处<code>X</code>应当与你在上方所得的<code>X</code>值保持一致。 # 修改上下两方的<code>QUP_Y、wrap_Y</code>和<code>sZ_clk</code>中的<code>Y</code>与<code>Z</code>为实际值。你仍然需要参考安卓[https://github.com/OnePlusOSS/android_kernel_oneplus_sm8150/blob/31bebb6ffc476f51986bd04502a76ffbd9e2fa7a/arch/arm64/boot/dts/qcom/sm8150-qupv3.dtsi#L118 xxxx-qupv3.dts中的一个宏]来确定这里的值,例如此处的<code>GCC_QUPV3_WRAP0_S4_CLK</code>中的<code>WARP0</code>中的0即为Y的值,<code>S4</code>中的<code>4</code>为此处的<code>Z</code>。 # 修改TLMM Gpio的值为正确的值,你可以在安卓内核源码中的[https://github.com/OnePlusOSS/android_kernel_oneplus_sm8150/blob/31bebb6ffc476f51986bd04502a76ffbd9e2fa7a/arch/arm64/boot/dts/qcom/sm8150-pinctrl.dtsi#L782 xxxx-pinctrl.dtsi]和[https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/drivers/pinctrl/qcom/pinctrl-sm8150.c#n1365 linux内核源码]中找到对应的gpio以及当其配置为qup时对应的func编号,在本文一加7T Pro的例子中,i2c_se4在pinctrl.dtsi中对应的gpio编号为51和52,gpio51、gpio52在内核源码中qup4功能对应的func为1。注意,PINGROUP(...)从左往右数依次为:(PIN编号,块,func1,func2,func3 ..... )。 # 此处TLMMGPIO内的Package释义如下:{PIN编号,电平,func,方向,上下拉,驱动电流强度},具体使用参考DSDT.dsl内其他TLMMGPIO Package在DSTATE0和DSTATE3的区别即可知。 <u>'''配置Amp设备的硬件资源'''</u> 通过上述操作,我们已经成功定义了I2C从设备和I2C主设备,并且掌握了如何在Package中定义一个gpio状态。 如果我们此时进入系统并安装上I2C驱动,应当会在设备管理器中出现一个未知设备,且通过查看其详细信息可知此设备为SPK1。 假如此时使用SPBTool能够获取到Amp的回传数据,则此条目可忽略。当然,为了追求更高效率的电源管理,还是建议配置。 通常参考设备的dsdt的中PEP会保留OPMD方法,我们可以尝试在DSDT.dsl中搜索此OPMD,如果没有搜索结果,你可以新建一个文件,并且将他Include到你的DSDT中,文件内容大致如下:<blockquote>Scope(\_SB_.PEP0){ Method(OPMD){ Return(OPCC) } Name(OPCC, Package () { Package(){ "DEVICE", "\\_SB.SPK1", Package(){ "DSTATE", 0, // Set your Reset gpio here, IC should be enabled in this DSTATE. <nowiki> // Package(){"TLMMGPIO",Package(){123, 1, 0, 1, 0, 0}},</nowiki> // Delay in ms if you need it, in most time not needed. //Package() { "DELAY", Package() {2} }, } Package(){ "DSTATE", 3, // Set your Reset gpio here, IC should be shutdown in this DSTATE. <nowiki> // Package(){"TLMMGPIO",Package(){123, 0, 0, 0, 0, 0}},</nowiki> // Delay in ms if you need it, in most time not needed. //Package() { "DELAY", Package() {2} }, } } } }</blockquote>根据上文中所述的TLMMGPIO Package配置方法,并且按照你从dts中获得的Reset Gpio编号,你可以很轻松的按照这里的提示填写对应的数值。 在本文一加7T Pro的例子中,Reset Gpio编号为gpio37,我们将其TLMMGPIO的package前面的//删掉以取消注释,并将pin编号(第一个数字)改为37,由于此例中配置Reset脚为高电平时IC关闭(注意,通常情况下是Reset高电平时IC正常工作,此例是特殊),故将第二个数据改为0,在D3状态下,则将第二个数字改为1,第四个数字改为1。 '''<u>添加I2S配置</u>''' 恭喜你,已经成功完成了大部分配置了。现在我们即将完成最后一步,配置I2S所需的硬件资源。 相信根据前面的操作,你现在能过熟练使用TLMMGPIO这个Package来配置gpio状态了。 <u>''一:在设备树中寻找属性:''</u> 要配置正确的I2S,我们首先需要在源码中dts中,或者反编译后的dts中寻找到[https://github.com/OnePlusOSS/android_kernel_oneplus_sm8150/blob/1dd473abda05a72f6978c47b2a7d80828db6b426/arch/arm64/boot/dts/qcom/sm8150-oem.dtsi#L212 codec对应的节点],此处为<code>quat</code>,故使用了<code>Quaternary</code>组的I2S,通过观察可知<code>mi2s_quat</code>在active时<code>137、138</code>均启用,但是由于缺乏原理图DATA线无法确定是使用SD0、SD1、SD2还是SD3(详见pinctrl dts里面的描述),可以先考虑将几个gpio都写进去,日后再acdb中进行尝试。 当然如果你有原理图,直接搜索amp的名称,观察其BCK、WCK、DO、DI走线对应的gpio编号即可。 <u>''二:在DSDT中的合适位置添加资源:''</u> 在DSDT.dsl中搜索<code>\\_SB.ADSP.SLM1.ADCM.AUDD</code>,找到对应的资源配置Package。 [[文件:Search Resource of AUDD.png|缩略图|1102x1102像素]] 在此Package中寻找正确的Component ID,在本例中,<code>Quaternary</code>对应<code>Component ID</code>为<code>0xD。</code>你可以通过查阅<code>auddev_ext.inf</code>中的<code>COMPONENT_GPIOUID</code>与其对应的<code>GroupID_DeviceID</code>来确定其对应关系,具体方法在之后说到'''ACDB配置'''时会详细阐述。 [[文件:Quaternary RX I2S Component.png|缩略图|666x666像素]] 通常的对应关系为: {| class="wikitable" |+ !RX I2S ID !Component ID |- |Quaternary |0xD |- |Tertiary |0xB |- |Secondary |0x9 |- |Primary |0x7 |} 右侧图示为整理并添加完I2S相关的GPIO之后的Component 0xD,之前已经介绍过TLMMGPIO Package内部各个参数的含义,故不在赘述。 请'''务必'''将'''dts'''和'''源码'''中定义的'''对应的'''I2S的GPIO添加到这里,并且指定正确的Function,否则声音信号无法传输到Amp。 <big>''<u>自此,我们已经完成了对DSDT方面的修改,下面我们介绍驱动所需要的修改,以及如何书写一个驱动控制Amplifier。</u>''</big> '''<big>配置ACDB</big>''' WP平台的ACDB与LA平台的ACDB并不兼容,即便是同样的SOC。 旧平台的ACDB通常可以在'''QACT v7.4'''中进行编辑。 你可以在[https://developer.qualcomm.com/software/qact-platform 这里下载QACT]。 [[文件:QACT.png|缩略图|1085x1085像素]] 这是'''QACT 7.4'''正常打开时候的精神面貌,点击<code>Open ACDB From Disk</code>, 再选择解压后的<code>qcacsp_xxxx.cab</code>包里面的<code>workspaceFile.qwsp</code>。如果没问题的话你会看到这个页面: [[文件:QACT OPEN DEVICE MGR.png|缩略图|1080x1080像素]] 选择上方的Tools -> Device Designer 进入设备设计器中。 打开qcaudminiport.inf,搜索TopologySpeaker, 找到 <code>HKR,QCAUD\TopologySpeaker\Device0\,DeviceID,0x00010001,0x000000FF</code> 这里写的Device ID即为扬声器设备的Device ID,同时也就串联起了之前我们提到的<code>auddev_ext</code>里面的Device ID,一个device中包含了其使用的I2S,以及Data Line是SD0还是SD1、SD2、SD3。Device 存储在ACDB中,通过QACT的Device Designer中我们可以修改原来设备的属性或者添加删除设备。 推荐尽量不要修改qcaudminiport.inf中的Device ID,通过默认配置的方式达到我们的目的,这些DeviceID和ComponentID之类的如果一处配置出错都会导致驱动异常(表现为音量条自动调到最大,且无法控制) <blockquote></blockquote>
返回
高通Windows平台音频驱动适配
。