第4阶段:完善 GPIO 按键

目标

支持音量键/电源键

DTS 改动分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <dt-bindings/input/input.h>

/ {
gpio-keys {
compatible = "gpio-keys";

pinctrl-names = "default";
pinctrl-0 = <&gpio_keys_default>;

label = "GPIO Buttons";

button-volume-up {
label = "Volume Up";
gpios = <&tlmm 107 GPIO_ACTIVE_LOW>;
linux,code = <KEY_VOLUMEUP>;
};
};
};

&pm8916_resin {
linux,code = <KEY_VOLUMEDOWN>;
status = "okay";
};


&tlmm {
gpio_keys_default: gpio-keys-default-state {
pins = "gpio107";
function = "gpio";
drive-strength = <2>;
bias-pull-up;
};
};

最后的 DTS 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// SPDX-License-Identifier: GPL-2.0-only

/dts-v1/;

#include "msm8916-pm8916.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>

/ {
model = "vivo Y927 (PD1410V)";
compatible = "vivo,y927", "qcom,msm8916";
chassis-type = "handset";

aliases {
mmc0 = &sdhc_1; // eMMC 固化为 /dev/mmcblk0
mmc1 = &sdhc_2; // SD 卡 固化为 /dev/mmcblk1
};

usb_id: usb-id {
compatible = "linux,extcon-usb-gpio";
id-gpios = <&tlmm 110 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&usb_id_default>;
};

/* 背光(GPIO8 控制) */
backlight: backlight {
compatible = "gpio-backlight";
gpios = <&tlmm 8 GPIO_ACTIVE_HIGH>;
default-on;
};

/* LCD 偏压电源 VSP */
reg_lcd_enp: regulator-lcd-enp {
compatible = "regulator-fixed";
regulator-name = "lcd_enp";
gpio = <&tlmm 97 GPIO_ACTIVE_HIGH>;
enable-active-high;
regulator-always-on; /* lmdpdg 生成的 panel 驱动不读 vsp/vsn-supply */
regulator-boot-on; /* 必须 always-on,否则 fixed regulator 启动时被拉低,屏黑 */
};

/* LCD 偏压电源 VSN */
reg_lcd_enn: regulator-lcd-enn {
compatible = "regulator-fixed";
regulator-name = "lcd_enn";
gpio = <&tlmm 98 GPIO_ACTIVE_HIGH>;
enable-active-high;
regulator-always-on;
regulator-boot-on;
};

/* 预留 lk1st splash framebuffer 不被内核踩踏 */
reserved-memory {
cont-splash@83200000 {
reg = <0 0x83200000 0 0x800000>;
no-map;
};
};

gpio-keys {
compatible = "gpio-keys";

pinctrl-names = "default";
pinctrl-0 = <&gpio_keys_default>;

label = "GPIO Buttons";

button-volume-up {
label = "Volume Up";
gpios = <&tlmm 107 GPIO_ACTIVE_LOW>;
linux,code = <KEY_VOLUMEUP>;
};
};
};

&usb {
dr_mode = "peripheral";
extcon = <&usb_id>, <&usb_id>;
status = "okay";
};

&usb_hs_phy {
extcon = <&usb_id>;
status = "okay";
};

&tlmm {
usb_id_default: usb-id-default {
pins = "gpio110";
function = "gpio";
drive-strength = <8>;
bias-pull-up;
};

/* DSI reset pin (GPIO25) */
mdss_default: mdss-default-state {
pins = "gpio25";
function = "gpio";
drive-strength = <8>;
bias-disable;
};
mdss_sleep: mdss-sleep-state {
pins = "gpio25";
function = "gpio";
drive-strength = <2>;
bias-pull-down;
};
gpio_keys_default: gpio-keys-default-state {
pins = "gpio107";
function = "gpio";
drive-strength = <2>;
bias-pull-up;
};
};

&pm8916_resin {
linux,code = <KEY_VOLUMEDOWN>;
status = "okay";
};


&sdhc_1 {
status = "okay"; // eMMC(内部存储)
};

&sdhc_2 {
status = "okay"; // SD 卡(TF 卡槽)
cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>;
};

/* ====== DSI 主机 + 面板 ====== */
&mdss {
status = "okay";
};

&mdss_dsi0 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&mdss_default>;
pinctrl-1 = <&mdss_sleep>;
status = "okay";

panel@0 {
compatible = "vivo,y927-hx8394a";
reg = <0>;
reset-gpios = <&tlmm 25 GPIO_ACTIVE_LOW>;
backlight = <&backlight>;
vsp-supply = <&reg_lcd_enp>;
vsn-supply = <&reg_lcd_enn>;

port {
panel_in: endpoint {
remote-endpoint = <&mdss_dsi0_out>;
};
};
};
};

/* DSI host 输出端:data-lanes + remote-endpoint 必须放在这里 */
&mdss_dsi0_out {
data-lanes = <0 1 2 3>;
remote-endpoint = <&panel_in>;
};

编译 Linux 内核

1
2
cd ~/y927/linux
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) Image.gz dtbs

构建 boot.img 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cd ~/y927/linux

# 拼接内核 + DTB
cat arch/arm64/boot/Image.gz \
arch/arm64/boot/dts/qcom/msm8916-vivo-y927.dtb \
> vmlinuz-dtb

~/y927/lk2nd/lk2nd/scripts/mkbootimg \
--kernel vmlinuz-dtb \
--ramdisk $HOME/y927/initramfs/initramfs-phase-3.cpio.gz \
--cmdline "console=tty0 console=ttyMSM0,115200 panic=1 initcall_blacklist=simpledrm_platform_driver_init" \
--base 0x80000000 \
--kernel_offset 0x00008000 \
--ramdisk_offset 0x01000000 \
--second_offset 0x00f00000 \
--tags_offset 0x00000100 \
--pagesize 2048 \
--header_version 0 \
--output boot-phase4.img

刷入 boot.img 文件

1
2
fastboot flash boot boot-phase4.img
fastboot reboot

测试

1
sudo picocom -b 115200 /dev/ttyACM0

2026-06-14 12:32 所有物理按键被成功识别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
~ #  cat /proc/bus/input/devices
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="pm8941_pwrkey"
P: Phys=pm8941_pwrkey/input0
S: Sysfs=/devices/platform/soc@0/200f000.spmi/spmi-0/0-00/200f000.spmi:pmic@0:pon@800/200f000.spmi:pmic@0:pon@800:pwrkey/input/input0
U: Uniq=
H: Handlers=kbd event0
B: PROP=0
B: EV=3
B: KEY=10000000000000 0

I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="pm8941_resin"
P: Phys=pm8941_resin/input0
S: Sysfs=/devices/platform/soc@0/200f000.spmi/spmi-0/0-00/200f000.spmi:pmic@0:pon@800/200f000.spmi:pmic@0:pon@800:resin/input/input1
U: Uniq=
H: Handlers=kbd event1
B: PROP=0
B: EV=3
B: KEY=4000000000000 0

I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="GPIO Buttons"
P: Phys=gpio-keys/input0
S: Sysfs=/devices/platform/gpio-keys/input/input2
U: Uniq=
H: Handlers=kbd event2
B: PROP=0
B: EV=3
B: KEY=8000000000000 0

第4.1阶段:编译evtest打包进initramfs测试

目标

编译一个arm64版本的evtest,并打包到initramfs里用于测试物理按键是否正常工作。

工具准备

1
sudo apt install -y autoconf automake libtool pkg-config gcc-aarch64-linux-gnu

克隆源码

1
2
3
mkdir -p ~/y927/evtest && cd ~/y927/evtest
git clone https://gitlab.freedesktop.org/libevdev/evtest.git src
cd src

交叉编译

1
2
3
4
5
6
7
8
9
10
11
12
cd ~/y927/evtest/src
./autogen.sh --host=aarch64-linux-gnu \
CC=aarch64-linux-gnu-gcc \
CFLAGS='-Os -static' \
LDFLAGS='-static'
make -j$(nproc)

file evtest
# evtest: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=6c3d0f801c43c0e2289299aabd618c04889c10b5, for GNU/Linux 3.7.0, not stripped

# 精简符号信息,从800多KB到700KB
aarch64-linux-gnu-strip evtest

制作initramfs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
BUSYBOX_BINARY=~/y927/busybox-1.38.0/busybox
EVTEST_BINARY=~/y927/evtest/src/evtest
cd ~/y927/initramfs
rm -rf phase-4
mkdir -p phase-4 && cd phase-4

# 创建 initramfs 目录结构
mkdir -p bin dev proc sys lib/modules

# 复制 BusyBox 到 initramfs
cp $BUSYBOX_BINARY bin/
cp $EVTEST_BINARY bin/

# 创建基础命令的软链接
cd bin
for cmd in sh mount ls cat sleep exec echo; do
ln -sf busybox $cmd
done
cd ..

# 创建 /init 脚本
cat > init << 'EOF'
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sys /sys
mount -t devtmpfs dev /dev
echo "===== Phase 1: kernel booted! ====="
echo "Serial: $(cat /proc/cmdline)"
sleep 5
exec sh </dev/ttyGS0 >/dev/ttyGS0 2>&1
EOF
chmod +x init

# 打包
find . | cpio -o -H newc | gzip > ../initramfs-phase-4.cpio.gz

构建 boot.img 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cd ~/y927/linux

# 拼接内核 + DTB
cat arch/arm64/boot/Image.gz \
arch/arm64/boot/dts/qcom/msm8916-vivo-y927.dtb \
> vmlinuz-dtb

~/y927/lk2nd/lk2nd/scripts/mkbootimg \
--kernel vmlinuz-dtb \
--ramdisk $HOME/y927/initramfs/initramfs-phase-4.cpio.gz \
--cmdline "console=tty0 console=ttyMSM0,115200 panic=1 initcall_blacklist=simpledrm_platform_driver_init" \
--base 0x80000000 \
--kernel_offset 0x00008000 \
--ramdisk_offset 0x01000000 \
--second_offset 0x00f00000 \
--tags_offset 0x00000100 \
--pagesize 2048 \
--header_version 0 \
--output boot-phase4.img

刷入 boot.img 文件

1
2
fastboot flash boot boot-phase4.img
fastboot reboot

测试

1
sudo picocom -b 115200 /dev/ttyACM0
1
2
3
evtest /dev/input/event0     # 电源键
evtest /dev/input/event1 # 大概率是 pm8941_resin(音量减)
evtest /dev/input/event2 # 大概率是 gpio-keys(音量加)

然后按对应的按键,就可以滚动输出对应的按键状态了