APS2 Mainlining Linux笔记
本文最后更新于 2025年10月7日 下午
本人不对因参考这篇笔记产生的任何硬件、软件问题负责,任何问题
应由使用者个人承担。使用本文章中的方法或资源即代表你同意本条款。
前言
感谢ayaneo公司提供的APS2样机和支持。APS2是Pocket S的二代产品,采用高通G3 Gen3平台,根据现有资料推测为SM8650换皮版本,因此可以共享SM8650主线Linux平台驱动。
可以参考Linaro的主线驱动适配情况列表Qualcomm mainline status了解其他平台的驱动情况。
适配笔记
记录一下开发过程,反正闲着也是闲着,写点内容说不定有人看呢。
UEFI 适配
前些年已经在本地为Aloha UEFI适配了SM8650平台可从ABL启动的二阶段UEFI,相同平台的话,不同设备只需要手动给驱动应用一些patch即可使用,因此只花了不到一天的时间就为APS2适配好了UEFI。
但是由于Aloha UEFI目前不考虑合并SM8650平台,所以相关代码不会合并到主仓库中,目前来看UEFI基本功能正常,USB(UFP)、UFS均可用。
由于UEFI开发不是重点,且ABL也可以直接启动Linux,所以这里就不详细展开说移植过程了。这两个我都用过,我觉得不存在孰好孰坏一说,有的平台UEFI方便,有的平台ABL方便。
安卓/Linux多启动
关于安卓和Linux的多启动问题,由于DualbootKernelPatcher(简称DBKP)基本上只能检测接SOC的GPIO状态从而区分不同系统启动,例如OP7系列和XiaomiPad5, 一个是检测TriKey的GPIO电平状态,一个是检测保护套盖子的霍尔传感器的GPIO状态。
之前APS1的Turbo按键也是挂在GPIO上的,所以可以通过读取状态来区分启动安卓还是Linux,但是APS1之后的设备这个Turbo按键(还有除了开关音量键的所有按键)都是接到手柄控制器上面的,走USB->PCIe->SOC被系统读取,那我肯定不会就为了读取一个GPIO状态在裸机环境把又是USB又是PCIe的好几套协议栈和驱动搭起来,所以基于DBKP的多启动在APS2/DMG/ACE上不可用。那多启动就真的不行了?
- 一个思路是完善DBKP,添加GUI和音量键选择功能,驱动SPMI相对简单。
- 一个思路是完全从UEFI启动,安卓内核和Linux内核直接在Grub中选择启动,更简单和容易实现。
- 一个思路是,即然设备是Unfused,那直接改个ABL在里面加上一个启动到Linux选项不就完事了。
众所周知,高通的ABL是开放源代码的,直接编译签名一下就可以运行了,我这里打算结合高通最近开源的abl2esp,替换原来的ABL,启动ESP分区的GRUB之后,通过切换启动项选择启动Linux or LinuxLoader,efi。
abl2esp是一个基于RUST实现的引导程序加载器(应该是干了之前BDS干的活),从所有可能的ESP分区找bootaa64.efi加载,利用这个程序可以加载grub或者linux,或者其他efi,甚至补上驱动直接启动Windows?
目前已经完成对abl2esp的测试,参考博文ABL2ESP测试。
ABL2ESP 配置
在刷入ABL2ESP之前,需要首先配置ESP分区,不然不出意料的话会直接变砖。
ESP分区可以选择ufs上的任意fat/fat32/exfat分区,或者放SD卡上的分区也行。
放在UFS的分区上可能会因为系统升级导致引导丢失,如果不慎重启之后机器就砖了,因为abl2esp无法找到引导。所以在安卓里面升级之后、重启之前请务必检查存放引导的分区。推荐将grub和LinuxLoader放在logfs分区(只有8MB),可以塞得下,然后把linux的Image/initrd/dtb等文件放在格式化成ext4的rawdump分区,因为这俩分区都是不会干扰系统升级的,也不会被系统升级覆盖,算比较安全。或者推荐直接把ESP分区放在SD卡。
把patch过按键的GRUB2放在里面,并且配置/boot/grub/grub.cfg,给出一个基础grub.cfg的例子,假设linux内核和grub放在esp分区:
1 | |
LinuxLoader.efi是从原机的abl.elf提取的,用来启动安卓;第二个是Linux的启动项,存放在esp分区的/for_boot/目录下。
1 | |
Linux 启动
上一节,UEFI已经正常启动了,只需要编写编译设备树,在机器上配置ESP分区、rootfs就可以启动系统了。
设备树编写
编写已有平台的设备树通常需要参考其他参考平台的设备。例如SM8650的手机通常需要参考SM8650 qrd/mtp的设备树进行编写,本案例中采用QRD的设备树进行参考。复制arch/arm64/boot/dts/qcom/sm8650-qrd.dts到arch/arm64/boot/dts/qcom/sm8650-ayaneo-ps2.dts,然后根据具体硬件对其进行删改。
检查PMIC配置
PMIC是电源管理IC,高通平台上通常以PM8xxx, PM8xxxA/B类似的规则命名,打开device info hw,即可查看本机有哪些在线的pmic/ldo,如下图所示:
通过结合安卓设备树和主线设备树,humu是ldob,对应主线设备树中的pm8550,pm_v8对应ldoi,pm_v6x对应pm8550vs,id有c、d、e、g,检查主线设备树中的对应配置,发现多出了m和n(pm8010),这些都没用到,可以直接去掉。
保留如下pmic进行测试,同时删去apps_rsc中的regulator配置。
1 | |
大多数情况下,参考设备设备树里面的ldo默认电压配置和大多数设备的电压配置是相同的,不过处于安全考虑,还是需要核查一遍。
如下代码块中的regulator-min/max-microvolt需要和安卓中的进行对比,数值差不多就行。
1 | |
如果发现主线设备树的apps_rsc节点中缺少某个ldo(例如ldod2),大概率是因为压根没用到那一路的LDO,可以通过搜索安卓设备树里面这路ldo的phandle数值查找是否存在引用该ldo的设备。不过现阶段不急着自己补上,可以等到后面写设备节点的时候再补。
检查GPIO保留
一些特殊设备可能会在devcfg中保留gpio用作特殊用途,这些gpio在主线设备树如果添加保留属性,可能会导致系统崩溃。
安卓设备树中,该参数的属性名称是qcom,gpios-reserved。
1 | |
主线中的该参数属性名称为:gpio-reserved-ranges。代码块如下,其中32代表从32开始,8代表保留32~39八个gpio。
1 | |
对比安卓和主线设备树,可以发现gpio保留有缺失,修正后的代码如下:
1 | |
配置固件路径
默认Linux的固件路径通常/usr/lib/firmware/,设备树中的路径是基于此路径的相对路径。
我这里使用mdt,Linux主线在6.几,具体是几我也忘了,就合并了加载mdt格式的固件的支持了,而且pil-squasher貌似还不支持合并新版本的mdt固件。最好在路径中加上vendor名称和设备代号/设备名称,用来区分不同设备的固件(这些常常不一样)。
下面是一个ipa固件的例子,按照该配置为其他dsp/协处理器也配置固件路径。
1 | |
配置完成之后,将安卓中/vendor/firmware/,/vendor/firmware_mnt/image,/vendor/bt_firmware/中用到的固件复制到Linux Rootfs分区里面,设备树中配置的位置。
添加i2c等外设设备
管他用得到用不到,先把三个gpi_dma打开:
1 | |
搜索安卓设备树中的i2c@xxxx,查找i2c master设备下的子节点,如下案例中的nq@64和redriver@1c,此时可以去主线内核源码中搜索是否存在对应驱动,例如搜索rtc6226,没有任何结果,搜索rtc6,也没相关结果,基本上代表主线并不支持该芯片。redriver在qrd的配置中默认已经存在,不用去修改。
1 | |
触摸驱动通常也挂在i2c下面,aps2采用了汇顶科技的gt9xx驱动,在i2cdump中可以验证:
1 | |
对应安卓设备树中的:
1 | |
推测该芯片大概率是gt911,在主线的Documentation目录中搜索gt911,通过Documentation/devicetree/bindings/input/touchscreen/goodix.yaml文件参考编写设备树。
1 | |
通过查找安卓设备树中的属性值,对应到主线驱动的属性,编写主线设备树中的设备节点,下面是编写完成的:
1 | |
按照上面的逻辑编写其余子设备节点即可。
检查已存在的gpio值
参考设备的谁倍数中常常已经定义了部分芯片使用gpio,但是OEM生产的设备上,这些值常常会被改变,因此需要检查已经存在gpio配置是否正确,包括gpio序号,中断配置类型,gpio电平配置,gpio模式配置等。
检查reserved-memory
OEM可能会定义不一样的保留内存区域,该区域一般定义在soc级dtsi文件中,可以在dts文件中对include进来的节点进行修改:
1 | |
面板配置
通过以下命令查找设备面板型号
1 | |
使用msm8916-mainline项目组的linux-mdss-dsi-panel-driver-generator和/sys/firmware/fdt生成面板驱动框架:
1 | |
如何遇到报错ValueError: 'bl_ctrl_external' is not a valid BacklightControl,可以尝试将mdss_dsi_wt0630_60hz_video节点下面的属性qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_external";改成qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";,重新反编译成dtb,再运行lmdpdg,即可正常导出。
把生成的驱动放在drivers/gpu/drm/panel/panel-wt0630-2k.c,编辑该目录下面的Makefile和Kconfig,添加构建选项。
由于APS2面板依然采用的是Dual DSI面板, panel driver generator导出的驱动并不能直接使用,需要手动添加dsi1之后才能使用。
之后调试的发现,wt0630模式和属性都和wt0600一致,直接沿用上代驱动即可。
声音
麻了,花了两周时间debug声音问题,最后找到是audioreach.c中active channel配置问题,我写死了个正确的值就可以放音乐了。但是目前还没有正确的修复方法,写了封邮件给代码作者,问他有什么意见。
重命名声卡model,这个很重要。
1 | |
高通新平台上使用Audioreach架构,具体audio bring up思路如下:
- 启动ADSP/LPASS/LPAIDSP, 高通的Hexagon DSP负责音频数据处理和DMA输出配置,所以需要先起来.
- Audioreach topology, 是alsatplg,需要自己根据平台的具体硬件进行编译,可以参考其他设备的,名称必须为
<model>-tplg.bin,必须被放在/usr/lib/firmware/qcom/sm8650/。 - Alsa UCM, Use case manager, 需要根据驱动和具体硬件编写,可以参考其他设备的配置,名称需要和
model一致,且必须创建一个conf下的软连接。
扬声器:
添加路由:1
2audio-routing = "SpkrLeft IN", "WSA_SPK1 OUT",
"SpkrRight IN", "WSA_SPK2 OUT",APS2使用WSA2/SWR3双路Speaker,由于audioreach驱动中按照num_channels生成active channels mask,导致DSP一致尝试给swr0/wsa1发送数据,但是实际上发不出去,就一直卡请求dsp返回音频处理结果那里,通过强制设置为
0xc/0b1100即可修复问题,但是会给其他设备引入问题,所以之后考虑其他修复方法:1
2-intf_cfg->cfg.active_channels_mask = (1 << cfg->num_channels) - 1;
+intf_cfg->cfg.active_channels_mask = 0b1100;麦克风
APS2使用WCD939x Codec,麦克风挂在WCD TX上面的DMIC1链路,是通过安卓termux下面使用alsactl monitor监控,打开录音机和关闭录音机获取到的状态可以判断出来。如果你对着一个孔说话但是Linux下面没声音,可能是那个孔不是麦克风,建议在安卓测试一下洞是不是麦克风。UCM2配置
根据实际硬件编写就可,可以参考其他设备的。
结语
[2025/10/7]
前前后后弄了也有个把月了,基本功能除了mic也算正常了,之后可能考虑手搓一下指纹驱动,这篇笔记就先完结了,之后会在博客发完整系统。
相关链接
- 官方的Linux内核仓库:https://kernel.org/
- AudioReach仓库:linux-msm/audioreach-topology
- UCM2仓库:alsa-project/alsa-ucm-conf
- Kancy维护的Linux分支:sunflower2333/linux
- Kancy Patch过的abl2esp:sunflower2333/abl2esp
- Kancy Patch过的Grub2:sunflower2333/grub2
- 另外我修改过的ucm2和audioreach配置也在同名github账号下。