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

验证方法

  1. 加载模块后执行 dmesg | grep drm,确认无错误日志。
  2. 执行 cat /sys/class/drm/card0-DP-1/status(具体connector编号以实际注册为准),预期输出 connected。
  3. 执行 modetest -M rockchip -c(需安装libdrm测试工具),查看connector列表中是否出现HDMI-A类型且状态为connected的条目。
  4. 执行 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主设备驱动需先完成初始化。