Rockchip RK3399 - Mali-T864 GPU驱动

发布时间 2023-06-05 22:48:47作者: 大奥特曼打小怪兽

一、图像界面卡顿问题

在前面的文章我们已经移植了在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...