一、图像界面卡顿问题
在前面的文章我们已经移植了在NanoPC-T4开发板上移植了uboot 2023.04、linux 5.2.8、以及ubuntu 20.04.4根文件系统。然后在使用ubuntu桌面环境的时候,发现了一个问题,随便打开一个网页,视频都加载不出来,同时看到CPU的占用率会急剧升高;
1.1 产生原因
造成这种情况一般由两个原因:
- 开发板上主控 SoC 的性能比较弱,没有带3D图形加速(即GPU)功能;
- 开发板上的SoC带了GPU,但是没有用起来;
第一个原因基本是无解的,如果你选的SoC上面没有带GPU,唯一的办法就是尽量去跑轻量级的图形界面,如果想跑debian、ubuntu这种发行版上默认搭配的GNOME或者KDE,这种没有GPU支持,所有的图像合成渲染都要通过CPU来运算,是很难跑流畅的。所以如果你对图形显示功能比较看重,在选开发板的时候一定要查阅与之搭配的主控SoC是否带有GPU。
也有很多人被卡在了第二个关卡,SoC上搭配了强劲的GPU、比如 RK3399,S912,他们都搭载了ARM Mali GPU,但是mainline内核(指的是主线linux内核)却缺少相应的驱动支持,GPU 驱动一般分为两部分:
- 一小部分在 linux 内核中;
- 另外一大部分在 userspace,在 usercapce的部分向下操作内核中的驱动,向上对应用层提供标准的 OpenGL 接口,这样各种图形显示相关的应用才能通过标准的OpenGL API进行图形渲染加速;
1.2 ARM Mail GPU
Mali GPU IP 提供商ARM公司只开放了内核部分驱动,而且这部分驱动还没有按照linux kernel的规范以 Drm 的框架去实现,所以它无法被linux mainline接受,还有一个更重要的部分,usersapce 部分ARM没有开源,只是以库的形式提供给购买了Mali GPU授权的SoC厂商,比如Rockchip,Amlogic。而且这套代码主要是为Android系统设计的,对debian、ubuntu这种系统的兼容性也不好。
看到这里,也许你就想骂ARM了,其实也不能完全怪他们,因为这并不是ARM独创的玩法,其他的GPU供应商也都这样玩,比如 Nvidia、Vivante、Imagination。大神 Linus 为这事还对Nvidia竖过中指呢,怒吼:“Nvidia,F*K you!”。
面对这种状况,很多人为了能够利用GPU加速,就只能使用SoC原厂提供的内核,和他们定制的系统,这种内核和系统一般都比较老。
如果想跑mainline的内核,基本就没法使用GPU加速了,这也是为什么我们目前看到的大部分开发板如果搭载了mainline内核,基本都不会有GPU加速功能,或者直接就不开图形显示功能。
但是也有一部分黑客们不满于这种封锁,他们勇于探索,积极尝试,逆向了ARM发布的二进制库,然后重写了针对ARM Mali GPU的开源驱动,最终在 Linux 5.2发布的时候合并到 mainline中:
- 针对 Mali-400/Mali-450 的驱动叫做 lima;
- 针对Mali-T6xx / Mali-T7xx / Mali-T8xx GPU和GXX 系列的叫做Panfrost;
- usersacpe部分的开源库叫做 mesa,对 Mali gpu 的支持从mesa 19.2开始;
这里我们将学习如何在运行linux kernrl 5.2.8的RK3399开发板上开启GPU加速:RK3399 集成了Mali-T864 GPU,所以我们可以利用linux kernel的panfrost驱动 + userspace 的 mesa来解锁3D图形加速功能。
二、配置Panfrost
关于linux 5.2.8内核的下载和配置、编译参考:Rockchip RK3399 - 移植linux 5.2.8。
2.1 配置内核
2..1.1 panfrost GPU驱动
在linux内核根目录下执行make menuconfig命令,进入如下配置:
Device Drivers ---> Graphics support ---> <*> Panfrost (DRM support for ARM Mali Midgard/Bifrost GPUs)
默认编译到内核中或者以模块的形式加载都可以。
2.1.2 驱动代码
驱动位于drivers/gpu/drm/panfrost/文件夹下;
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# ll drivers/gpu/drm/panfrost/ 总用量 1720 drwxrwxr-x 2 root root 4096 Jun 5 20:58 ./ drwxrwxr-x 64 root root 16384 Jun 5 20:22 ../-rw-rw-r-- 1 root root 472 Aug 9 2019 Kconfig -rw-rw-r-- 1 root root 225 Aug 9 2019 Makefile-rw-rw-r-- 1 root root 5934 Aug 9 2019 panfrost_devfreq.c -rw-rw-r-- 1 root root 441 Aug 9 2019 panfrost_devfreq.h-rw-rw-r-- 1 root root 6427 Aug 9 2019 panfrost_device.c -rw-rw-r-- 1 root root 2678 Aug 9 2019 panfrost_device.h-rw-rw-r-- 1 root root 11971 Aug 9 2019 panfrost_drv.c-rw-rw-r-- 1 root root 11398 Aug 9 2019 panfrost_features.h -rw-rw-r-- 1 root root 2353 Aug 9 2019 panfrost_gem.c -rw-rw-r-- 1 root root 785 Aug 9 2019 panfrost_gem.h-rw-rw-r-- 1 root root 10974 Aug 9 2019 panfrost_gpu.c -rw-rw-r-- 1 root root 586 Aug 9 2019 panfrost_gpu.h-rw-rw-r-- 1 root root 3944 Aug 9 2019 panfrost_issues.h -rw-rw-r-- 1 root root 13584 Aug 9 2019 panfrost_job.c -rw-rw-r-- 1 root root 1428 Aug 9 2019 panfrost_job.h-rw-rw-r-- 1 root root 9752 Aug 9 2019 panfrost_mmu.c -rw-rw-r-- 1 root root 486 Aug 9 2019 panfrost_mmu.h-rw-rw-r-- 1 root root 14759 Aug 9 2019 panfrost_regs.h -rw-rw-r-- 1 root root 880 Aug 9 2019 TODO
驱动入口文件在panfrost_drv.c,在该文件我们可以看到支持的GPU型号;
static const struct of_device_id dt_match[] = { { .compatible = "arm,mali-t604" }, { .compatible = "arm,mali-t624" }, { .compatible = "arm,mali-t628" }, { .compatible = "arm,mali-t720" }, { .compatible = "arm,mali-t760" }, { .compatible = "arm,mali-t820" }, { .compatible = "arm,mali-t830" }, { .compatible = "arm,mali-t860" }, { .compatible = "arm,mali-t880" }, {} }; MODULE_DEVICE_TABLE(of, dt_match); static const struct dev_pm_ops panfrost_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) SET_RUNTIME_PM_OPS(panfrost_device_suspend, panfrost_device_resume, NULL) }; static struct platform_driver panfrost_driver = { .probe = panfrost_probe, .remove = panfrost_remove, .driver = { .name = "panfrost", .pm = &panfrost_pm_ops, .of_match_table = dt_match, }, }; module_platform_driver(panfrost_driver);
2.2 gpu设备节点
我们可以在arch/arm64/boot/dts/rockchip/rk3399.dtsi文件找到gpu设备节点的定义:
gpu: gpu@ff9a0000 { compatible = "rockchip,rk3399-mali", "arm,mali-t860"; reg = <0x0 0xff9a0000 0x0 0x10000>; interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH 0>, <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH 0>, <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH 0>; interrupt-names = "gpu", "job", "mmu"; clocks = <&cru ACLK_GPU>; power-domains = <&power RK3399_PD_GPU>; status = "disabled"; };
其中:
- compatible:说明了设备兼容的驱动名称,即"rockchip,rk3399-mali"和"arm,mali-t860";可以看到arm,mali-t860是和panfrost驱动相匹配的,因此会执行驱动的.probe函数,这里就不深入研究了;
- reg:指定了寄存器的基地址和大小,即基地址0xff9a0000,大小为0x10000;
- interrupts和interrupt-names:分别指定了该设备所使用的中断号和中断的名称;
- clocks:指定了使用哪个时钟控制器(CRU)提供GPU时钟;
- power-domains:用于指定设备所属的电源域,即RK3399_PD_GPU;
- status:设置为"disabled"表示GPU设备当前处于禁用状态,无法使用;
2.2.1 设备节点gpu新增属性
我们需要在arch/arm64/boot/dts/rockchip/rk3399-evb.dtsi文件中为gpu设备节点新增以下属性;
&gpu { mali-supply = <&vdd_gpu>; status = "okay"; };
其中:
-
mali-supply:指定了GPU设备使用的电源管脚;
-
status:指定GPU设备的状态("okay" 表示设备正常工作);
2.2.2 GPU电源接线原理图
我们看一下GPU电源的接线图;
从图中可以看到VDD_GPU是由SYR838PKC 输出的,SYR837/SYR838是一款高效率的同步降压 DC/DC变换器芯片,内部应该就是通过BUCK降压电路实现的。其主要特点包括:
- 最大输出电流可达6A;
- 宽输入电压范围:2.6V至5.5V;
- 集成主开关和同步开关,具有非常低的导通损耗(即 R DS(ON) 值很低);
- 输出电压可以通过I2C接口编程,范围从0.7125V到1.5V;
SYR837/SYR838具有I2C接口,允许主处理器通过控制输出电压来实现电压缩放(DVS)功能。I2C接口支持最高达3.4MHz的时钟速度,并使用标准的I2C命令。SYR837/SYR838始终作为从设备运行,并通过一个由 7 位从地址和一个第8位读写标志组成的地址被寻址。SYR837的I2C地址在出厂时设置为0x40H,SYR838的地址设置为0x41H。
通过I2C接口,主处理器可以向SYR837/SYR838发送命令,以控制输出电压大小,实现电压缩放功能,从而适应不同的工作负载和功耗需求。此外,I2C 接口还可以实现对芯片的配置、监测和故障诊断等功能,提高了系统的可靠性和灵活性。
I2C接线如下:
SYR838PKC | RK3399 | 描述 |
SCL | I2C_SCL_PMIC(GPIO1_C0/SPI3_TXD/I2C0_SCL) |
I2C串行时钟线 |
SDA | I2C_SDA_PMIC(GPIO1_B7/SPI3_RXD/I2C0_SDA) | I2C串行数据线 |
VSEL | GPU_SLEEP(GPIO1_B6/PWM3B_IR) |
电压选择引脚; 低电平时,该芯片将根据VSEL0寄存器的值来设置输出电压VOUT; 高电平时,该芯片将根据VSEL1寄存器的值来设置输出电压VOUT。 |
VIN | VCC3V3_SYS | 输入电压 |
VOUT | VDDGPU(GPU_VDD1~20) | 输出电压 |
需要注意的是:以上内容来自SYR838PKC datasheet,更多细节请自己查看手册。
2.2.3 设备节点vdd_gpu
vdd_gpu设备节点描述GPU所需的电源配置信息。具体来说,它描述了一个名为vdd_gpu的稳压管(也就是SYR838PKC芯片),用于为GPU 提供电源。
vdd_gpu设备节点节点是i2c0设备节点的子节点,配置如下
vdd_gpu: regulator@41 { compatible = "silergy,syr828"; reg = <0x41>; fcs,suspend-voltage-selector = <1>; pinctrl-names = "default"; pinctrl-0 = <&gpu_sleep>; regulator-always-on; regulator-boot-on; regulator-min-microvolt = <712500>; regulator-max-microvolt = <1500000>; regulator-name = "vdd_gpu"; regulator-ramp-delay = <1000>; vin-supply = <&vcc3v3_sys>; regulator-state-mem { regulator-off-in-suspend; }; };
这个稳压管的配置信息包括:
- compatible:指定了稳压管使用的驱动程序类型和版本信息;
- reg:指定了I2C设备的地址;
- fcs,suspend-voltage-selector :指定了稳压管在睡眠模式下的工作电压;
- pinctrl-names:设置了引脚的默认状态,引脚配置设置为gpu_sleep;
- pinctrl-0:指定了default状态的对应的引脚配置,即gpu_sleep;
- regulator-always-on:表示稳压管始终处于开启状态;
- regulator-boot-on:表示稳压管在启动时自动开启;
- regulator-min-microvolt:指定了稳压管的最小输出电压;
- regulator-max-microvolt:指定了稳压管的最大输出电压;
- regulator-name:指定了稳压管在的名称。
- regulator-ramp-delay:指定了稳压管从关闭到开启时的延迟时间;
- vin-supply :指定了稳压管的输入电源,VCC3V3_SYS是由电源输入的12V电源经过稳压管NB680GD输出得到的;
既然我们已经看到这里了,不妨继续看一下引脚配置节点gpu_sleep,其定义在pinctrl设备节点下:
pmic { cpu_b_sleep: cpu-b-sleep { rockchip,pins = <1 RK_PC1 RK_FUNC_GPIO &pcfg_pull_down>; }; gpu_sleep: gpu-sleep { rockchip,pins = <1 RK_PB6 RK_FUNC_GPIO &pcfg_pull_down>; }; pmic_int_l: pmic-int-l { rockchip,pins = <1 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>; }; };
可以看到这里配置GPIO1_B6引脚功能为GPIO,电气特性为pcfg_pull_down,表示下拉。GPIO1_B6连接的就是SYR838PKC 的VSEL引脚。
至于设备vcc3v3_sys设备节点,实际上就比较简单了,其它描述了一个名为vcc3v3_sys的稳压管(也就是NB680GD芯片,其输出电压是固定的就是3.3V,因此也没啥好说的);
vcc3v3_sys: vcc3v3-sys { compatible = "regulator-fixed"; regulator-always-on; regulator-boot-on; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; regulator-name = "vcc3v3_sys"; };
2.3 保存配置
配置完内核之后记得保存配置:
存档:
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# mv rk3399_defconfig ./arch/arm64/configs/
重新配置内核:
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# make rk3399_defconfig
2.4 编译内核
在linux内核根目录下执行如下命令进行编译内核:
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# make -j8
u-boot-2023.04路径下的mkimage工具拷贝过来,然后在命令行使用mkimage工具编译即可:
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# cp ../u-boot-2023.04/tools/mkimage ./ root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# ./mkimage -f kernel.its kernel.itb
需要注意的是这里一定不能指定-E参数,不然uboot在进行kernel镜像hash校验的时候就会失败。
2.4 通过tftp烧录内核
给开发板上电,同时连接上网线,进入uboot命令行。我们将内核拷贝到tftp文件目录:
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# cp kernel.itb /work/tftpboot/
接着通过uboot命令行将kernel.itb下到内存地址0x10000000处:
=> tftp 0x10000000 kernel.itb
通过mmc write命令将内核镜像烧录到eMMC第0x8000个扇区处:
=> mmc erase 0x8000 0xA000 => mmc write 0x10000000 0x8000 0xA000
2.5 启动内核
我们重新启动开发板,如果Panfrost GPU驱动正常加载,我们会看到类似下面的日志:
[ 1.605438] panfrost ff9a0000.gpu: clock rate = 500000000 [ 1.611515] panfrost ff9a0000.gpu: failed to get regulator: -517 [ 1.618246] panfrost ff9a0000.gpu: regulator init failed -517 ...... [ 4.576304] panfrost ff9a0000.gpu: clock rate = 500000000 [ 4.583134] panfrost ff9a0000.gpu: mali-t860 id 0x860 major 0x2 minor 0x0 status 0x0 [ 4.591970] panfrost ff9a0000.gpu: features: 00000000,100e77bf, issues: 00000000,24040400 [ 4.601269] panfrost ff9a0000.gpu: Features: L2:0x07120206 Shader:0x00000000 Tiler:0x00000809 Mem:0x1 MMU:0x00002830 AS:0xff JS:0x7 [ 4.614692] panfrost ff9a0000.gpu: shader_present=0xf l2_present=0x1 [ 4.623663] [drm] Initialized panfrost 1.0.0 20180908 for ff9a0000.gpu on minor 1
我们通过cat /proc/interrupts查看的中断资源申请信息:
zhengyang@rk3399:~$ cat /proc/interrupts CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 10: 0 0 0 0 0 0 GICv3 25 Level vgic 12: 0 0 0 0 0 0 GICv3 27 Level kvm guest vtimer 15: 5168 4615 4766 4172 16377 14289 GICv3 30 Level arch_timer 17: 1703 1352 1570 1055 4494 1180 GICv3 113 Level rk_timer 18: 0 0 0 0 0 0 GICv3-23 0 Level arm-pmu 19: 0 0 0 0 0 0 GICv3-23 1 Level arm-pmu 20: 0 0 0 0 0 0 GICv3 37 Level ff6d0000.dma-controller 21: 0 0 0 0 0 0 GICv3 38 Level ff6d0000.dma-controller 22: 0 0 0 0 0 0 GICv3 39 Level ff6e0000.dma-controller 23: 0 0 0 0 0 0 GICv3 40 Level ff6e0000.dma-controller 24: 354 0 0 0 0 0 GICv3 44 Level eth0 25: 5738 0 0 0 0 0 GICv3 96 Level dw-mci 26: 12798 0 0 0 0 0 GICv3 43 Level mmc1 27: 2 0 0 0 0 0 GICv3 58 Level ehci_hcd:usb1 28: 34 0 0 0 0 0 GICv3 60 Level ohci_hcd:usb3 29: 2 0 0 0 0 0 GICv3 62 Level ehci_hcd:usb2 30: 48 0 0 0 0 0 GICv3 64 Level ohci_hcd:usb4 31: 126 0 0 0 0 0 GICv3 68 Level ff160000.i2c 32: 430 0 0 0 0 0 GICv3 132 Level ttyS2 33: 222 0 0 0 0 0 GICv3 89 Level ff3c0000.i2c 36: 0 0 0 0 0 0 GICv3 147 Level ff650800.iommu 39: 3811 0 0 0 0 0 GICv3 151 Level ff8f3f00.iommu, ff8f0000.vop 40: 0 0 0 0 0 0 GICv3 150 Level ff903f00.iommu, ff900000.vop 41: 0 0 0 0 0 0 GICv3 75 Level ff914000.iommu 42: 0 0 0 0 0 0 GICv3 76 Level ff924000.iommu 43: 1 0 0 0 0 0 GICv3 55 Level ff940000.hdmi 44: 103 0 0 0 0 0 GICv3 51 Level gpu 45: 2168 0 0 0 0 0 GICv3 52 Level job 46: 0 0 0 0 0 0 GICv3 53 Level mmu 56: 1 0 0 0 0 0 rockchip_gpio_irq 4 Edge bt_default_wake_host_irq 105: 0 0 0 0 0 0 rockchip_gpio_irq 21 Level rk808 212: 1 0 0 0 0 0 GICv3 59 Level rockchip_usb2phy 213: 1 0 0 0 0 0 GICv3 63 Level rockchip_usb2phy IPI0: 4556 7145 7933 8236 6813 6441 Rescheduling interrupts IPI1: 206 205 223 195 5458 3657 Function call interrupts IPI2: 0 0 0 0 0 0 CPU stop interrupts IPI3: 0 0 0 0 0 0 CPU stop (for crash dump) interrupts IPI4: 591 997 1080 878 901 908 Timer broadcast interrupts IPI5: 0 0 0 0 0 0 IRQ work interrupts IPI6: 0 0 0 0 0 0 CPU wake-up interrupts
gpu、 job、mmu都是Panfrost GPU驱动注册的中断,都是电平触发类型的中断,高电平触发。左边的数字103和2168是当前产生的中断的数量。
三、安装
3.1 安装weston
weston是负责对各种应用绘制的图层进行合成的软件框架,它是按照Wayland标准实现的,目标是替代在Linux PC端存在了很久的X11,如果对Android图形系统比较了解的话,它相当于Android中的HWC。
我们直接在开发板上运行如下面命令:
root@rk3399:~$ sudo apt install weston
3.2 安装依赖
前面有讲到,GPU驱动分两部分,一部分在内核中,我们已经启动了,另外还有一个重要的部分在 userspace中,对于Panfrost GPU驱动来说,它叫做 mesa。mesa不能直接安装,我们需要在 RK3399开发板上编译。
首先要安装一些依赖库:
root@rk3399:~$ sudo apt install apt-utils flex bison python3-mako libwayland-egl-backend-dev libxcb-dri3-dev libxcb-dri2-0-dev libxcb-glx0-dev libx11-xcb-dev libxcb-present-dev libxcb-sync-dev libxxf86vm-dev libxshmfence-dev libxrandr-dev libwayland-dev libxdamage-dev libxext-dev libxfixes-dev x11proto-dri2-dev x11proto-dri3-dev x11proto-present-dev x11proto-gl-dev x11proto-xf86vidmode-dev libexpat1-dev libudev-dev gettext mesa-utils xutils-dev libpthread-stubs0-dev ninja-build bc flex bison cmake git valgrind llvm llvm-8-dev python3-pip pkg-config zlib1g-dev wayland-protocols meson
3.3 编译安装libdrm
libdrm是一个封装了和linux kernel DRM驱动交互的api库。
root@rk3399:~# git clone git://anongit.freedesktop.org/mesa/drm root@rk3399:~# cd drm meson build --prefix=/usr ninja -C build sudo -E ninja -C build install cd ..
3.4 编译安装mesa
mesa中实现了Panfrost GPU驱动的userspace部分,它向下操作内核中的GPU驱动,相上提供标准的opengl接口供各种绘图应用使用。
git clone git://anongit.freedesktop.org/mesa/mesa cd mesa meson -Ddri-drivers= -Dvulkan-drivers= -Dgallium-drivers=panfrost,kmsro -Dlibunwind=false -Dprefix=/usr build/ ninja -C build/ sudo ninja -C build/ install
3.5 编译安装SDL
如果想运行一些模拟器之类的游戏,比如supertuxkart可能会用到这个库。
git clone https://github.com/SDL-mirror/SDL.git cd SDL mkdir build cd build cmake ../ make -j6 sudo make install
到这里所有GPU相关的软件都安装完了,重启系统,登录图形桌面,就可以把GPU用起来了。
四、测试
4.1 运行Glmark2 测试
Glmark2 是一款比较出名的GPU benchmark测试程序,支持OpenGL 2.0和OpenGL ES 2.0。在 ubuntu系统上可以直接通过apt install命令安装,在debian系统上需要通过源码编译:
运行glmark2-es-wayland命令即可进行测试。
参考文章
[1] 在 RK3399 上运行开源的 Mali GPU 驱动(部分转载)
[2] mainline 的 u-boot 和 linux kernel
[3] 《MaliGraphics》https://wiki.debian.org/MaliG...
[4] 《ARM Mali GPU》https://en.opensuse.org/ARM_M...
标签:rw,Level,Rockchip,Mali,gpu,RK3399,GPU,panfrost,root From: https://www.cnblogs.com/zyly/p/17459196.html