4. 实现Rockchip Connector驱动
任务目标
在Rockchip DRM/KMS驱动框架中实现一个Connector驱动,完成两件事:检测显示器是否连接,以及读取并返回显示器的EDID信息。本案例基于RK3568平台,使用Linux内核DRM子系统标准接口,不涉及具体硬件寄存器操作,以最小可编译模块演示Connector驱动的核心实现路径。
环境与依赖
- 内核版本:Linux 5.10(Rockchip SDK内核)
- 平台:RK3568 EVB
- 依赖头文件:
drm_connector.h、drm_edid.h、drm_probe_helper.h - 设备树:需在
&hdmi或自定义节点中声明compatible = "rockchip,rk3568-demo-connector"
核心实现
#include <linux/module.h>
#include <linux/platform_device.h>
#include <drm/drm_connector.h>
#include <drm/drm_edid.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_atomic_helper.h>
struct demo_connector {
struct drm_connector base;
bool connected;
};
static inline struct demo_connector *
to_demo_connector(struct drm_connector *connector)
{
return container_of(connector, struct demo_connector, base);
}
static int demo_connector_get_modes(struct drm_connector *connector)
{
struct demo_connector *demo = to_demo_connector(connector);
struct edid *edid = NULL;
int count = 0;
if (!demo->connected)
return 0;
/* 实际项目中此处通过I2C读取EDID,本案例使用固定EDID模拟 */
edid = drm_edid_alloc(128);
if (!edid)
return 0;
/* 填充最小有效EDID头(模拟真实读取结果) */
edid->header[0] = 0x00;
edid->header[1] = 0xFF;
edid->header[2] = 0xFF;
edid->header[3] = 0xFF;
edid->header[4] = 0xFF;
edid->header[5] = 0xFF;
edid->header[6] = 0xFF;
edid->header[7] = 0x00;
count = drm_add_edid_modes(connector, edid);
drm_edid_free(edid);
return count;
}
static enum drm_connector_status
demo_connector_detect(struct drm_connector *connector, bool force)
{
struct demo_connector *demo = to_demo_connector(connector);
/* 实际项目中通过GPIO/HPD引脚检测,本案例固定为已连接 */
demo->connected = true;
return connector_status_connected;
}
static const struct drm_connector_helper_funcs demo_connector_helper_funcs = {
.get_modes = demo_connector_get_modes,
};
static const struct drm_connector_funcs demo_connector_funcs = {
.detect = demo_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int demo_connector_probe(struct platform_device *pdev)
{
struct drm_device *drm = dev_get_drvdata(pdev->dev.parent);
struct demo_connector *demo;
int ret;
demo = devm_kzalloc(&pdev->dev, sizeof(*demo), GFP_KERNEL);
if (!demo)
return -ENOMEM;
demo->connected = false;
drm_connector_init(drm, &demo->base,
&demo_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA);
drm_connector_helper_add(&demo->base,
&demo_connector_helper_funcs);
demo->base.polled = DRM_CONNECTOR_POLL_HPD;
demo->base.dpms = DRM_MODE_DPMS_ON;
ret = drm_connector_register(&demo->base);
if (ret)
return ret;
platform_set_drvdata(pdev, demo);
return 0;
}
static const struct of_device_id demo_connector_of_match[] = {
{ .compatible = "rockchip,rk3568-demo-connector" },
{ }
};
MODULE_DEVICE_TABLE(of, demo_connector_of_match);
static struct platform_driver demo_connector_driver = {
.probe = demo_connector_probe,
.driver = {
.name = "rk3568-demo-connector",
.of_match_table = demo_connector_of_match,
},
};
module_platform_driver(demo_connector_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("RK Demo");
MODULE_DESCRIPTION("Rockchip Demo Connector Driver");
编译与加载
# 将上述代码保存为 drivers/gpu/drm/rockchip/demo_connector.c
# 在 drivers/gpu/drm/rockchip/Makefile 中添加:
# obj-$(CONFIG_DRM_ROCKCHIP) += demo_connector.o
# 编译内核模块
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- drivers/gpu/drm/rockchip/demo_connector.o
# 将生成的 .ko 文件拷贝到目标板并加载
insmod demo_connector.ko
验证方法
- 加载模块后执行
dmesg | grep drm,确认无错误日志。 - 执行
cat /sys/class/drm/card0-DP-1/status(具体connector编号以实际注册为准),预期输出connected。 - 执行
modetest -M rockchip -c(需安装libdrm测试工具),查看connector列表中是否出现HDMI-A类型且状态为connected的条目。 - 执行
drm_info或modetest -e查看EDID信息,确认能读取到EDID数据块。
通过条件:系统能识别该connector,状态检测为connected,并能从该connector读取到有效的EDID数据(至少包含EDID头0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00)。
常见问题
- connector注册失败:确认
drm_device指针获取正确,platform设备父节点需为DRM设备。 - EDID读取为空:检查
demo->connected标志是否被正确置位,以及drm_add_edid_modes返回值是否为0。 - modetest看不到connector:确认模块加载顺序,DRM主设备驱动需先完成初始化。