0. 引言
DRM ENCODER 和 CONNECTOR 模块由图显外设抽象而来,从传统意义上来讲,ENCODER 包含外设控制器的功能,而 CONNECTOR 包含外设 PHY 或者显示器参数。但是这两部分又紧密关联,因此,软件 DRM 架构下的 ENCODER 和 CONNECTOR 需要按照一个整体来理解和实现。
目前的图显外设接口主要有 MIPI、HDMI、LVDS、DP、VGA、DVI、RGB 等等,同时,又可以通过转接卡来实现各种接口之间的转换。目前,市面上的处理器 SoC 又同时具备以上接口。
因此,DRM 架构为了统一管理、无差别化对待不同芯片厂商的 IP,抽象出了 ENCODER 和 CONNECTOR 两部分内容。
2. 功能
ENCODER 作为图显外设的逻辑控制器,从图显处理器接收并行的 RGB 数据,并按照接口类型对 RGB 数据进行编码,例如 HDMI 通过 encoder 将 RGB 数据编码为 TMDS 类型的数据。
在软件层面的 DRM 架构中规定了使用下面两个数据结构,注册 DRM ENCODER 相关功能。
encoder 控制
struct drm_encoder_funcs {
void (*reset)(struct drm_encoder *encoder);
void (*destroy)(struct drm_encoder *encoder);
int (*late_register)(struct drm_encoder *encoder);
void (*early_unregister)(struct drm_encoder *encoder);
};
encoder 配置
struct drm_encoder_helper_funcs {
void (*dpms)();
enum drm_mode_status ();
bool (*mode_fixup)();
void (*prepare)();
void (*commit)();
void (*mode_set)();
void (*atomic_mode_set)();
struct drm_crtc *(*get_crtc)();
enum drm_connector_status ();
void (*atomic_disable)();
void (*atomic_enable)();
void (*disable)();
void (*enable)();
int (*atomic_check)();
};
图显外设 IP 厂商需要提供相关的例化代码,如下:
static const struct drm_encoder_funcs exynos_hdmi_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = {
.mode_fixup = hdmi_mode_fixup,
.enable = hdmi_enable,
.disable = hdmi_disable,
};
mode_fixup()会将上层应用下发的图显参数进行校验,可以对参数进行修正也可以终止不符合显示要求的请求。若显示参数符合要求,将其拷贝给 struct drm_display_mode。
static bool hdmi_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
...
mode_ok = hdmi_mode_valid(connector, adjusted_mode);
...
drm_mode_copy(adjusted_mode, m);
...
CONNECTOR 包含 PHY 或者显示屏的参数,接收 ENCODER 发送过来的编码数据后,按照显示接口类型发送给显示器。例如 HDMI PHY 内部集成 Serializer,将 TMDS 数据转换成高速串行 bit 流发送到差分数据通道。
DRM 架构中 connector 需要提供支持的显示参数以及显示屏的连接状态等。规定使用下面两个数据结构,注册 DRM CONNECTR 相关功能。
connctor 控制
struct drm_connector_funcs {
int (*dpms)();
void (*reset)();
enum drm_connector_status ();
void (*force)();
int (*fill_modes)();
int (*set_property)();
int (*late_register)();
void (*early_unregister)();
void (*destroy)();
struct drm_connector_state *();
void (*atomic_destroy_state)();
int (*atomic_set_property)();
int (*atomic_get_property)();
void (*atomic_print_state)();
};
connector 配置
struct drm_connector_helper_funcs {
int (*get_modes)();
int (*detect_ctx)();
enum drm_mode_status (*mode_valid)();
struct drm_encoder *(*best_encoder)();
struct drm_encoder *(*atomic_best_encoder)();
int (*atomic_check)();
void (*atomic_commit)();
int (*prepare_writeback_job)();
void (*cleanup_writeback_job)();
};
图显外设 IP 厂商需要提供相关的例化代码,如下:
static const struct drm_connector_funcs hdmi_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = hdmi_detect,
.destroy = hdmi_connector_destroy,
.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 const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
.get_modes = hdmi_get_modes,
.mode_valid = hdmi_mode_valid,
};
对于显示参数等信息需要通过某种方式从外部获取的图显 IP,如 HDMI、MIPI,hdmi_get_modes 是一个很关键的函数。
对于 HDMI 而言,通过 DDC 总线从外部显示屏中获取显示参数 EDID,以此决定是否支持当前显示要求。若无法获取 EDID 信息,那么是无法点亮屏幕的。DRM 也规定了获取 EDID 的统一标准接口,即 drm_get_edid()。
static int hdmi_get_modes(struct drm_connector *connector)
{
...
edid = drm_get_edid(connector, hdata->ddc_adpt);
if (!edid)
return -ENODEV;
...
}
SoC 芯片能够同时连接、同时工作的 ENCODER 和 CONNECTOR 数量,取决于内部集成的图显处理器的数量以及每个图显处理器的端口数,当然,SoC 芯片的时钟系统也是影响多个接口能否同时显示的一个关键要素。
3. 初始化
图显外设使用 component_add()注册进 DRM 系统,关于 component_add()
链接
encoder 的初始化包含两部分内容,分别是:
- drm_encoder_init()
- drm_encoder_helper_add()
DRM通过数据结构struct drm_encoder来描述ENCODER设备信息及同其他DRM组件之间的关系。
struct drm_encoder {
struct drm_device *dev;
struct list_head head;
struct drm_mode_object base;
char *name;
int encoder_type;
unsigned index;
uint32_t possible_crtcs;
uint32_t possible_clones;
struct drm_crtc *crtc;
struct drm_bridge *bridge;
const struct drm_encoder_funcs *funcs;
const struct drm_encoder_helper_funcs *helper_private;
};
DRM ENCODER初始化流程如下图所示,其主要实现了drm_encoder的例化以及注册drm_encoder_funcs、drm_encoder_helper_funcs两个数据结构。
DRM CONNECTOR初始化流程如下图所示,其主要实现了drm_connector的例化以及注册drm_connector_funcs、drm_connector_helper_funcs两个数据结构。