RK3568开源GPU驱动实践:从内核到Mesa的Panfrost适配指南
2026/9/17 6:55:41 网站建设 项目流程

算起来,我从拿到RK3568开发板到彻底放弃原厂闭源GPU栈,中间只隔了一个星期。原因很简单:在某次把内核从5.10切到6.1做外设适配时,闭源libmali用户态库和内核模块版本对不上,GLES直接起不来,而厂商社区那边的回复基本等于“你等我们下一版 release”。也就是那时候,我决定把主线内核自带的Panfrost驱动当作主力方案来推。今天这篇就是完整记录,写给那些想在rk3568平台上用开源GPU驱动(panfrost)干活的人,说说我从内核配置、设备树、用户态Mesa一路踩到性能调优和崩溃排查的全过程。

RK3568这颗芯片在很多板子上都能看到,四核A55,GPU部分是Mali-G52 2EE,不算强,但做工业HMI、边缘盒子、轻量图形界面完全够用。Panfrost是Linux主线内核里的开源Mali GPU驱动,配合Mesa社区的Gallium驱动,能在没有厂商闭源组件的情况下把GLES、Vulkan这些跑起来。这篇文章适合的对象是:已经能编译内核、会用设备树、手上有一块RK3568开发板的人。如果你只是刚接触嵌入式Linux,我会尽量把背景和原理也讲明白,但建议先熟悉一下基础编译流程再上手。

1. 为什么要在RK3568上折腾Panfrost:不仅是情怀

1.1 Mali-G52 2EE是什么来头

要理解Panfrost,先得知道RK3568的GPU具体是哪一代架构。Mali-G52属于Arm Bifrost架构,相比更老的Midgard架构,Bifrost在调度和执行模型上有明显变化,编译器、指令缓冲、作业提交方式都是另一套逻辑。RK3568用的这个G52 2EE版本,相当于双执行引擎配置,定位是中低端GPU,但支持Vulkan、GLES 3.2这些现代图形接口的硬件特性。

很多人会把Mali-G52和Mali-G52 2EE搞混。区别在于EE(Execution Engine)数量,2EE意味着同一时刻能并行的执行引擎少一些,在着色器密集型任务里性能跟高配版G52有差距。这块GPU通常运行在800MHz左右的频率,不过具体频率由芯片的OPP表决定,不同板子默认不一样。

了解这一点对适配Panfrost很重要。因为Panfrost对Bifrost架构的支持是分代推进的,早期只支持Midgard,后来才把Bifrost的支持合入主线。G52在Bifrost里面算是比较早被覆盖的型号,社区测试也多,所以相对好适配。如果你想在RK3568上跑Wayland合成器、做Qt界面、播放视频叠加特效,Panfrost完全能接得住。

1.2 不用闭源mali驱动到底损失了什么,又换回了什么

原厂方案通常包含两个部分:一个是内核态的mali驱动(类似kbase),一个是用户态的libmali,里面打包了OpenGL ES、OpenCL、Vulkan的接口实现。这玩意儿的毛病在于“版本耦合太死”。不同内核版本、不同GPU频率、不同安卓或Linux BSP版本,都需要对应版本的libmali。你一旦自己改了内核配置、换了编译器版本或者升级了DRM子系统,闭源库可能就直接罢工。

我遇到过的最典型情况是:把内核从Rockchip的5.10分支换成主线6.1之后,原厂libmali根本没有适配主线内核的版本,因为厂商的闭源内核模块通常跟着自己内核分支走。这时候你能选择的路线只有三条:一是继续停留在厂商内核,放弃新内核带来的新特性;二是自己尝试移植闭源mali内核模块到主线内核,难度极大且没有源码可查;三是切到Panfrost,内核态用主线自带的开源驱动,用户态用Mesa,彻底绕开厂商锁定。

换回什么好处?最直接的好处是“可升级”。内核可以跟着主线走,Mesa可以跟着麒麟/Ubuntu/Debian的软件源走,出了问题能自己读代码、查bug报告。社区里有人遇到问题会报告给Panfrost的issue tracker,修复后会合入主线,这种透明度是闭源方案做不到的。另外,Panfrost不需要厂商额外提供固件文件,驱动自己处理Mali的作业调度,少了“固件版本不匹配”这一层坑。

代价也有。首先在OpenCL上,Panfrost至今没有完整的计算方案,如果你依赖GPU计算,那得继续用闭源或者另想办法。其次,Vulkan方面panvk驱动虽然能用,但成熟度不如GLES。所以这个选择适合的是做显示、合成、2D界面、轻量3D的人,而不是重度计算用户。

1.3 开源路线的支持边界

Panfrost的官方支持范围大致是:Mali 400/450(Lima驱动负责)、Mali T860这类Midgard、以及Mali G31/G52/G57这种Bifrost。RK3568的G52刚好落在Bifrost支持区间内。主线内核里drivers/gpu/drm/panfrost/目录就是它的实现,从内核5.4之后开始合入,到6.x版本已经相当稳定。

用户态方面,Mesa中的Panfrost Gallium驱动支持GLES 2.0/3.0/3.1甚至部分3.2,配合KMS/DRM的GBM接口,可以跑weston、GNOME/ KDE这类现代合成器。Mesa中还有panvk这个Vulkan驱动,基本功能可跑,但扩展覆盖不全,复杂场景容易崩。我的经验是:做常规GUI和媒体播放走EGL/GLES没问题,不要指望它去打游戏跑benchmark。

2. 内核侧适配:从Kconfig到设备树节点

2.1 开启Panfrost必需的Kconfig

主线内核默认不会把Panfrost编进去,需要手动打开Kconfig。注意,这里指的是CONFIG_DRM_PANFROST,不是Rockchip BSP里常见的CONFIG_MALI或者CONFIG_GPU_ARMBIFROST。打开方式很简单,在menuconfig里搜索Panfrost,路径是:

Device Drivers -> Graphics support -> Panfrost (EXPERIMENTAL) GPU driver

它依赖DRM框架和ARM64架构。建议同时打开这些项:

CONFIG_DRM=y CONFIG_DRM_PANFROST=y CONFIG_DRM_SCHED=y CONFIG_IOMMU_SUPPORT=y CONFIG_ROCKCHIP_IOMMU=y CONFIG_CMA=y

Panfrost用DRM的调度器管理GPU作业,所以CONFIG_DRM_SCHED必须开着。CONFIG_ROCKCHIP_IOMMU这个跟RK3568的GPU内存管理强相关,后面会讲。CMA默认一般开着,但大小建议在cmdline里显式指定,否则默认值可能偏小。

如果你用的内核版本比较老,比如5.10或者更早的官方BSP内核,Panfrost的代码可能没有完全包含G52的支持,或者缺少后面修复的bug。我的建议是直接用主线6.1 LTS或更新的内核,不要在旧内核上强行backport,省得踩一些已经修掉的坑。

2.2 设备树中的GPU节点到底在描述什么

设备树是RK3568适配Panfrost最容易出问题的地方。RK3568原厂设备树里通常已经有一个mali节点,但如果你的板子是从某个BSP拷贝出来的,这个节点的内容不一定符合主线Panfrost驱动的解析规则。

一个典型的GPU节点长这样:

&gpu { status = "okay"; interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "job", "mmu", "gpu"; power-domains = <&power RK3568_PD_GPU>; clocks = <&cru CLK_GPU>, <&cru CLK_GPU_PVTM>; clock-names = "gpu", "pvtm"; operating-points-v2 = <&gpu_opp_table>; };

这里的interrupt-names顺序很关键。Panfrost驱动要求三个中断按jobmmugpu的顺序,对应Mali的Job中断、MMU中断和GPU中断。如果原厂设备树里顺序不对,或者只用了一个中断号,启动时驱动可能初始化失败。热词里有人搜“rk3568设备树”,这个节点正是重点。

power-domains指向GPU电源域,RK3568的PD_GPU编号是由电源管理框架定义好的。如果这个属性缺失,GPU可能根本不会被上电,驱动初始化会卡在等待电源稳定。另外,operating-points-v2指向GPU动态调频的OPP表,没有这张表的话Panfrost只能按默认频率跑,性能可能发挥不出来。

2.3 频率表与devfreq:让GPU真正跑起来

GPU频率通常由devfreq框架控制。OPP表一般在rk3568.dtsi里维护,大家常见的档位类似:

gpu_opp_table: opp-table { compatible = "operating-points-v2"; opp-200000000 { opp-hz = /bits/ 64 <200000000>; opp-microvolt = <900000>; }; opp-400000000 { opp-hz = /bits/ 64 <400000000>; opp-microvolt = <950000>; }; opp-800000000 { opp-hz = /bits/ 64 <800000000>; opp-microvolt = <1000000>; }; };

具体电压要根据你的板子电源设计来,贸然抄别的板子的微电压值有风险。确认方法是在某个固定频率下跑一轮glmark2,观察是否出现GPU hang或者系统重启。如果频率能稳定跑,就说明电压够用。

在系统运行时,可以通过devfreq接口查看当前GPU频率:

ls /sys/class/devfreq/ cat /sys/class/devfreq/fdab0000.gpu/cur_freq cat /sys/class/devfreq/fdab0000.gpu/load

设备名不一定是fdab0000.gpu,以实际设备树基地址为准,用ls看一下再拼路径。load文件显示的是GPU负载百分比,能用来判断调频策略是否合理。

2.4 编译、烧录与内核参数的坑

编译Panfrost没有特殊要求,正常交叉编译内核即可:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image dtbs

烧录方式取决于你的bootloader实现。RK3568一般用rkdeveloptool烧写boot分区,或者直接用SD卡启动。这里提醒一个容易踩的坑:设备树一定要和内核配套编译,别用厂商BSP编译的dtb去配主线内核,很多莫名其妙的问题都出在“核树分离”。

cmdline里建议加cma=512M。Panfrost在分配GPU内存、纹理缓冲区时,IOMMU可以把分散物理页映射给GPU,但是有些驱动路径仍然需要连续内存。512M对于1080p合成和常见3D应用足够,如果你的板子内存只有2G,可以缩到256M。观察内存是否够用的方法是:

cat /proc/meminfo | grep Cma

CmaFree太小的话,渲染大纹理时会出现分配失败。

3. 用户态渲染栈:让Mesa替GPU干活

3.1 Mesa交叉编译需要注意的依赖

内核态的Panfrost只是提供作业提交和内存管理的通道,真正把GLES/Vulkan API翻译成Mali指令的是用户态驱动,也就是Mesa里的Panfrost Gallium驱动和panvk Vulkan驱动。这里我推荐用Mesa 23.x以上的版本,对Bifrost的支持已经比较完整。

交叉编译Mesa最常见的痛苦是依赖链长。Mesa本身就依赖libdrm、libexpat、zlib、libxml2,如果还要编GLX,就得拉X11那一堆。在嵌入式目标机上跑的通常没有X11,所以可以显式声明平台:

meson setup build \ -Dplatforms=drm,gbm,wayland,surfaceless \ -Dgallium-drivers=panfrost \ -Dvulkan-drivers=panfrost \ -Dglx=disabled \ -Dbuildtype=release \ -Dprefix=/usr ninja -C build DESTDIR=$ROOTFS ninja -C build install

-Dplatforms=drm,gbm,wayland,surfaceless的意思是让Mesa支持KMS/DRM直接输出、GBM缓冲区管理、Wayland和surfaceless上下文,正好覆盖嵌入式Linux最常见的场景。-Dglx=disabled可以省掉X11依赖,大幅降低编译复杂度。

还有一个容易忽略的点:libdrm的版本要尽量新。Panfrost的GBM接口对libdrm版本有要求,如果libdrm太老,Mesa编译时可能提示找不到某个头文件。我习惯先交叉编译一份libdrm装到roofs,再编Mesa。

3.2 与libmali共存还是替换

很多RK3568板子的出厂rootfs里已经装好了官方libmali,路径通常在/usr/lib/aarch64-linux-gnu/libmali.so或者/usr/lib/libmali.so。如果你不处理它,直接用Mesa装出来的libEGL、libGLESv2、libgbm,动态链接器会优先找到系统已有的libmali。结果是你以为用的Panfrost,实际还是在走闭源库。

最简单的做法是做一次干净的切换:先备份原有libmali,然后卸载或者移走它,确认ldconfig之后系统里只剩Mesa的库:

mv /usr/lib/aarch64-linux-gnu/libmali.so /usr/lib/aarch64-linux-gnu/libmali.so.bak ldconfig

这里要特别提醒,不要同时让两个库抢占同一个so文件名。Mesa安装后libEGL.so.1libGLESv2.so.2libgbm.so.1这些名字和libmali是冲突的,强行共存会遇到“symbol lookup error”或者EGL初始化失败。

如果你的应用使用了OpenCL,那Panfrost这边暂时无解,闭源libmali得留着。这种情况下可以通过LD_LIBRARY_PATH或者修改链接脚本的方式,让GLES相关调用走Mesa,OpenCL继续走libmali。但操作起来非常麻烦,我试过几次后认为,除非你有硬性需求,否则直接二选一更省心。

3.3 用kmscube做无桌面验证

安装完Mesa之后,最怕的就是“看起来装好了但不知道跑没跑起来”。我建议跳过桌面,直接上kmscube这种极简测试程序验证。

EGL_PLATFORM=gbm kmscube

kmscube会无视显示服务器,直接用KMS/DRM创建全屏窗口渲染一个旋转立方体。如果屏幕上能看到立方体在转,说明从Panfrost内核驱动到Mesa用户态已经打通了。如果报错,先看dmesg | grep -i panfrost,后续在调试章节细说。

比kmscube更进一步的是glmark2-es2

glmark2-es2 --fullscreen

这个能测出大致的GLES性能指标,包括纹理填充率、光照、阴影等场景。我一般拿它作为基础回归测试,每次改设备树或Mesa版本后先跑一轮,确保性能没倒退。

对于Wayland场景,可以启动weston,然后把一个带GPU渲染的客户端窗口跑起来。weston自带的weston-simple-egl就能验证合成器是否真正调用了GPU。}

3.4 Vulkan的坑:panvk的成熟度

很多人看到Panfrost支持Vulkan,就想在RK3568上跑vkcube。确实,Mesa的panvk可以跑起来,但我的体会是“能用,但别指望太多”。在我的测试里,vkcube能正常出现,几个基础测试用例也能通过,但一旦涉及复杂的descriptor set、compute shader、多队列并发,就可能触发驱动里的bug或者直接VK_ERROR_DEVICE_LOST。

所以项目上如果只是做界面,优先走GLES。Vulkan先当作“可以尝试”的辅助能力,不要作为交付依赖。如果非要使用Vulkan,关注Mesa的更新日志,panvk几乎每个版本都在修bug,过三个月再回来看可能就稳很多。

4. 性能摸底与调优:GPU到底跑没跑起来

4.1 devfreq:从频率和负载看GPU工作状态

“GPU卡顿”不一定真的是图形能力不够,很多时候是驱动压根没让GPU跑上去。我在RK3568上遇过一种情况:跑glmark2时画面一顿一顿,CPU和内存占用都不高,怎么看怎么像软件渲染。最后查了GPU频率,发现它一直锁在200MHz。原因就是devfreq的governor没有正常工作,或者OPP表里的频率档位和电源管理策略不匹配。

排查方法用两个文件就够了:

watch -n 1 cat /sys/class/devfreq/$(ls /sys/class/devfreq | grep gpu)/load watch -n 1 cat /sys/class/devfreq/$(ls /sys/class/devfreq | grep gpu)/cur_freq

load显示的是负载百分比,如果在跑图形应用时这个值一直不高,那说明GPU没有被充分利用,卡顿来自别处(比如纹理上传带宽不够、CPU提交过慢)。如果load高但频率低,那就是调频策略问题,可以手动切到performance或者userspace验证:

echo performance > /sys/class/devfreq/$(ls /sys/class/devfreq | grep gpu)/governor

切到performance后卡顿消失,基本可以确定是动态调频响应慢。长期方案是修OPP表和电源域配置,或者换一个更激进的governor,比如把simple_ondemand的上限阈值调低。

4.2 CMA与IOMMU:分配内存的第一道坎

Panfrost处理GPU内存有两种路径:一是通过IOMMU映射分散页,二是依赖CMA分配连续内存。RK3568的设备树里GPU节点通常会关联一个rockchip,iommu节点,例如gpu_mmu。如果IOMMU正常工作,大部分缓冲区都能通过IOMMU映射。

但总有些路径绕不开CMA。比如某些显示合成、显存导入操作,或者Mesa里没有走DMA-BUF堆的代码路径,会调用连续内存分配。CMA太小的话,dmesg会看到类似cma_alloc: failed的错误。这时候加大cmdline里的cma参数是最快的解决方法:

cma=512M

另外需要注意,不要把设备树里的GPU节点标记为dma-coherent。Mali GPU需要软件维护CPU与GPU之间的缓存一致性,Panfrost驱动有自己的一套cache操作逻辑。如果你从某些板卡文档里抄了dma-coherent进去,反而可能因为缓存一致性问题导致贴图花屏、随机崩溃。我见过有人排查了半天渲染错乱,最后发现就是多加了这一个属性。

4.3 性能对比表:开源驱动与闭源方案的差距

我把同一块RK3568板子上跑glmark2的数据整理过一份对比,闭源libmali在部分场景下确实更强,但差距没有想象中那么大。下面是我当时记录的参考数据,不构成严谨benchmark,只做大致量级参考:

测试项闭源libmaliPanfrost + Mesa备注
glmark2 default综合分210左右180左右相差15%上下
纹理填充场景fill-rate偏高略低与内存带宽分配有关
复杂光照/着色器场景稳定偶见shader编译卡顿首次编译着色器时会卡一下
Wayland合成流畅流畅打开GBM后无明显区别
Vulkan基础可用可用但受限panvk仍不够成熟

这个对比基于Mesa 23.1和同版本内核。换个Mesa版本结果可能会变化,Panfrost的性能优化一直在推进,新版本通常只会更快更稳。

如果你觉得开源驱动性能不够,可以从两个方向优化:一个是增大GPU频率档位并确保供电稳定,另一个是检查DDR频率。GPU性能不只取决于GPU核心频率,显存带宽一样是瓶颈。RK3568的DMC频率可以动态调整,但有些板子默认把DDR频率调得比较保守,导致GPU频率再高也喂不饱数据。可以用/sys/class/devfreq/dmc/cur_freq查一下。

5. 踩坑实录:三次典型故障的完整排查链路

5.1 中断顺序不对:启动过程GPU模块直接卡死

第一次把主线内核跑在RK3568上时,我没仔细看设备树,直接沿用了某个板卡SDK里的GPU节点。启动日志里能加载Panfrost驱动,但跑到初始化某个阶段时整个系统直接hang住,串口无响应,只能按复位键。

当时的第一反应是电源域没配好。查了power-domains,发现指向的是RK3568_PD_GPU,看起来没问题。于是在内核启动参数里打开更多调试输出,才发现GPU的中断处理有问题。回头翻设备树,发现那个SDK里的interrupt-names写的是gpu, mmu, job,和Panfrost驱动要求的job, mmu, gpu顺序不一致。驱动按顺序申请中断,但实际上申请到的第一个中断是GPU中断而不是Job中断,之后提交GPU作业时中断也触发不了,整个调度器就卡死了。

修复方法很简单,把interrupt-namesinterrupts的顺序改成jobmmugpu,同时核对三个中断号对应关系。改完重新编译dtb,GPU正常初始化。

这个坑给的经验是:在RK3568上适配Panfrost,拿到设备树后第一件事就是检查GPU节点的interrupt-names,不要想当然认为SDK给的就是对的。

5.2 IOMMU Page Fault:渲染命令遇上了非法地址

跑通kmscube之后,我又去试了一个分辨率较高的合成场景,结果画面频繁花屏,然后进程崩溃。查看内核日志,看到反复刷类似这样的错误:

rockchip-iommu fdab9000.iommu: Page fault at 0x00000000xxxxxxxx

IOMMU page fault的意思是GPU访问的某个虚拟地址没有被映射到物理页。一类常见原因是用户态Mesa和内核态Panfrost版本不匹配,比如Mesa提交的job使用的是新的内存属性,但内核还是老版本,导致映射缺失。另一类常见原因是DMA-BUF导入链路出问题,比如显示层和GPU共用缓冲区时,某个缓冲区的fd没有被正确映射。

排查思路是:先升级Mesa到与内核时间接近的版本,再看/sys/kernel/debug/dri/0/state里的内存信息,确认每个buffer的状态。如果仍然复现,尝试在cmdline里关掉GPU的IOMMU,改用纯CMA方式看是否还是page fault。如果关掉IOMMU后不崩了,基本就是IOMMU映射问题;如果还崩,那就是用户态渲染指令本身有问题。

我最后定位到是Mesa版本太旧,Mesa 21.x对G52的某些渲染路径存在地址计算bug,升级到Mesa 23之后page fault再没出现过。

5.3 CPU/内存都不高但画面卡顿:总线与缓存一致性背锅

还有一个常见问题特别符合热词里有人搜的“gpu cpu 内存占用都不高但卡”。我遇到过一种情况:跑一个视频叠加UI的demo,CPU占用20%,内存占用才几百兆,GPU负载看起来也不高,但画面就是在定期抽风。

这类卡顿不能只看部件占用率,要看“某个部件在等待另一个部件”。我最先怀疑的是GPU调频太慢,于是手动锁到800MHz,卡顿依旧。然后又怀疑weston合成器软件栈开销,换成了kmscube直出,还是有周期性掉帧。

最后是在/sys/class/devfreq/dmc/里发现DDR频率在跑负载时降到了最低档。因为DDR的调频策略没有把GPU访问带宽纳入考量,GPU需要大量读纹理时DDR频率却降下去了,导致带宽瓶颈。解决办法是把DMC的governor切到performance,或者在设备树的DMC OPP表里调整降频阈值。这个问题的排查难点在于它不会报错,只会表现成“周期性卡顿”,一定要有性能计数器思维,别只看CPU和内存。

5.4 Mesa版本过旧:glmark2直接Segmentation Fault

有一回我在一个比较干净的Debian rootfs上只装了libgbm、libEGL和libGLESv2,没管版本,直接跑glmark2,结果进程起来就Segmentation Fault,留下一个core dump。最开始以为是Panfrost内核驱动的问题,但dmesg里干干净净,什么都没输出。

后来用gdb看core dump,栈停在Mesa内部的某个编译着色器函数里。查了版本,发现Mesa是20.x的,对Bifrost的支持还很不完整,G52这种v5架构很容易触发旧代码里的bug。把Mesa升到23.x之后重新测试,问题消失。

这个经验说明一个规律:Panfrost这个驱动还在快速演进期,太老的Mesa版本尽量别用。在RK3568上做产品化,最好锁定一个经过你完整回归测试的Mesa版本,而不是随手apt装一个。

另外一个相关的坑是,系统里如果同时存在多个libglvnd版本,也可能导致EGL入口被错误分发。检查方法是用eglinfo看EGL_VENDOR是不是Mesa Project,以及glxinfo -B里的OpenGL renderer字符串是不是Panfrost

结语

实际用下来,Panfrost在RK3568上已经从我印象里的“实验品”变成了可以进项目的稳定方案。内核态驱动和Mesa用户态配合好之后,日常GLES渲染、Wayland合成、视频叠加这些需求都不成问题。如果你想要更激进的体验,可以试试Collabora维护的panfork分支,它对Bifrost调度器有一些mainline里还没有的性能补丁,我在RK3568上做过基础对比,部分场景确实更快,但它毕竟不是主线,升级时注意跟着对应的Mesa版本走。

最后分享一个小技巧:在你把Panfrost跑通之后,建议把内核启动参数、设备树片段、Mesa版本和glmark2数据都记录下来,单独存一份文档。因为后面每次升级内核或者rootfs,都需要拿这组基线数据做对比,否则出了问题很难判断是新驱动引入的回归还是环境差异造成的。开源GPU驱动的优势之一就是可以回溯和对比,把这份优势用起来,适配工作会轻松很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询