3. 实现Rockchip Encoder驱动

实现Rockchip Encoder驱动 学习卡片

任务说明

本案例要求在Rockchip DRM框架中实现一个Encoder驱动,完成CRTC输出信号到外部接口(HDMI/MIPI DSI)的转换。核心工作是注册drm_encoder并实现其atomic_check与atomic_mode_set回调,用于验证显示模式与当前Encoder的兼容性,并在模式切换时配置硬件输出参数。

驱动框架与依赖

本驱动基于Linux内核DRM子系统,依赖drm_atomic_helper提供的默认回调。硬件寄存器操作通过regmap完成,设备树节点需提供寄存器映射地址。以下代码聚焦Encoder核心逻辑,实际项目中需在probe函数中完成drm_encoder_init注册,并通过component框架与CRTC绑定。

关键完整代码

#include <drm/drm_encoder.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include <linux/regmap.h>

/* 硬件寄存器定义(示例) */
#define ENCODER_CTRL_REG   0x00
#define ENCODER_MODE_REG   0x04
#define MODE_HDMI          0x01
#define MODE_MIPI_DSI      0x02

struct rockchip_encoder {
    struct drm_encoder base;
    struct regmap *regmap;
    int interface_type;  /* 0=HDMI, 1=MIPI DSI */
};

static inline struct rockchip_encoder *
to_rockchip_encoder(struct drm_encoder *encoder)
{
    return container_of(encoder, struct rockchip_encoder, base);
}

static int rockchip_encoder_atomic_check(struct drm_encoder *encoder,
                     struct drm_crtc_state *crtc_state,
                     struct drm_connector_state *conn_state)
{
    struct rockchip_encoder *rk_encoder = to_rockchip_encoder(encoder);
    struct drm_display_mode *mode = &crtc_state->mode;

    /* 检查分辨率是否超出接口能力 */
    if (rk_encoder->interface_type == 0) {  /* HDMI */
        if (mode->clock > 340000)  /* 340MHz 像素时钟上限 */
            return -EINVAL;
    } else {  /* MIPI DSI */
        if (mode->clock > 150000)  /* 150MHz 像素时钟上限 */
            return -EINVAL;
    }

    /* 检查时序参数有效性 */
    if (mode->hdisplay == 0 || mode->vdisplay == 0)
        return -EINVAL;

    return 0;
}

static void rockchip_encoder_atomic_mode_set(struct drm_encoder *encoder,
                      struct drm_crtc_state *crtc_state,
                      struct drm_connector_state *conn_state)
{
    struct rockchip_encoder *rk_encoder = to_rockchip_encoder(encoder);
    struct drm_display_mode *mode = &crtc_state->mode;
    u32 ctrl_val = 0;
    u32 mode_val;

    /* 根据接口类型选择输出模式 */
    if (rk_encoder->interface_type == 0) {
        mode_val = MODE_HDMI;
        ctrl_val |= BIT(0);  /* 使能HDMI输出 */
    } else {
        mode_val = MODE_MIPI_DSI;
        ctrl_val |= BIT(1);  /* 使能MIPI DSI输出 */
    }

    /* 配置输出模式 */
    regmap_write(rk_encoder->regmap, ENCODER_MODE_REG, mode_val);

    /* 配置像素时钟分频(示例:按模式时钟设置) */
    ctrl_val |= (mode->clock / 1000) & 0xFFFF;
    regmap_write(rk_encoder->regmap, ENCODER_CTRL_REG, ctrl_val);
}

static const struct drm_encoder_helper_funcs
rockchip_encoder_helper_funcs = {
    .atomic_check = rockchip_encoder_atomic_check,
    .atomic_mode_set = rockchip_encoder_atomic_mode_set,
};

static void rockchip_encoder_destroy(struct drm_encoder *encoder)
{
    drm_encoder_cleanup(encoder);
}

static const struct drm_encoder_funcs rockchip_encoder_funcs = {
    .destroy = rockchip_encoder_destroy,
};

/* 初始化函数——在probe中调用 */
int rockchip_encoder_init(struct drm_device *drm_dev,
              struct rockchip_encoder *rk_encoder,
              int interface_type)
{
    int ret;

    rk_encoder->interface_type = interface_type;

    ret = drm_encoder_init(drm_dev, &rk_encoder->base,
                   &rockchip_encoder_funcs,
                   DRM_MODE_ENCODER_TMDS, NULL);
    if (ret)
        return ret;

    drm_encoder_helper_add(&rk_encoder->base,
                   &rockchip_encoder_helper_funcs);

    return 0;
}
EXPORT_SYMBOL_GPL(rockchip_encoder_init);

编译与集成

  1. 将上述代码保存为rockchip_encoder.c,加入内核源码drivers/gpu/drm/rockchip/目录。
  2. 在Kconfig中添加配置项config DRM_ROCKCHIP_ENCODER,依赖DRM_ROCKCHIP。
  3. 在Makefile中添加obj-$(CONFIG_DRM_ROCKCHIP_ENCODER) += rockchip_encoder.o。
  4. 设备树节点需提供reg属性指向寄存器基地址,并在驱动probe中通过syscon_node_to_regmap获取regmap实例。

验证方法

  1. 编译内核并烧录至RK3568/RK3588平台,启动后检查dmesg确认Encoder注册成功:dmesg | grep -i encoder,应看到类似[drm] Initialized encoder的日志。
  2. 使用modetest工具列出当前Encoder:modetest -M rockchip -e,确认Encoder ID存在且类型为TMDS。
  3. 连接HDMI显示器并设置分辨率:modetest -M rockchip -s 32:1920x1080,观察屏幕是否正常输出。若输出异常,检查atomic_check返回的EINVAL是否因像素时钟超限。
  4. 验证atomic_mode_set生效:在驱动中临时添加printk打印寄存器值,对比设置前后ENCODER_CTRL_REG的值是否随分辨率变化。

通过判定标准

  • Encoder成功注册且无错误日志。
  • HDMI接口在1080p@60Hz下正常显示,无花屏或信号丢失。
  • 切换分辨率至4K@30Hz时,atomic_check正确返回-EINVAL(因超过340MHz上限),验证模式过滤逻辑生效。