本文
主要分析encoder的初始化和配置,drm_encoder结构体如下:
/**
* struct drm_encoder - central DRM encoder structure
* @dev: parent DRM device
* @head: list management
* @base: base KMS object
* @name: human readable name, can be overwritten by the driver
* @funcs: control functions, can be NULL for simple managed encoders
* @helper_private: mid-layer private data
*
* CRTCs drive pixels to encoders, which convert them into signals
* appropriate for a given connector or set of connectors.
*/
struct drm_encoder {
struct drm_device *dev;
struct list_head head;
struct drm_mode_object base;
char *name;
/**
* @encoder_type:
*
* One of the DRM_MODE_ENCODER_ types in drm_mode.h. The following
* encoder types are defined thus far:
*
* - DRM_MODE_ENCODER_DAC for VGA and analog on DVI-I/DVI-A.
*
* - DRM_MODE_ENCODER_TMDS for DVI, HDMI and (embedded) DisplayPort.
*
* - DRM_MODE_ENCODER_LVDS for display panels, or in general any panel
* with a proprietary parallel connector.
*
* - DRM_MODE_ENCODER_TVDAC for TV output (Composite, S-Video,
* Component, SCART).
*
* - DRM_MODE_ENCODER_VIRTUAL for virtual machine displays
*
* - DRM_MODE_ENCODER_DSI for panels connected using the DSI serial bus.
*
* - DRM_MODE_ENCODER_DPI for panels connected using the DPI parallel
* bus.
*
* - DRM_MODE_ENCODER_DPMST for special fake encoders used to allow
* mutliple DP MST streams to share one physical encoder.
*/
int encoder_type;
/**
* @index: Position inside the mode_config.list, can be used as an array
* index. It is invariant over the lifetime of the encoder.
*/
unsigned index;
/**
* @possible_crtcs: Bitmask of potential CRTC bindings, using
* drm_crtc_index() as the index into the bitfield. The driver must set
* the bits for all &drm_crtc objects this encoder can be connected to
* before calling drm_dev_register().
*
* You will get a WARN if you get this wrong in the driver.
*
* Note that since CRTC objects can't be hotplugged the assigned indices
* are stable and hence known before registering all objects.
*/
uint32_t possible_crtcs;
uint32_t possible_clones;
/**
* @crtc: Currently bound CRTC, only really meaningful for non-atomic
* drivers. Atomic drivers should instead check
* &drm_connector_state.crtc.
*/
struct drm_crtc *crtc;
/**
* @bridge_chain: Bridges attached to this encoder. Drivers shall not
* access this field directly.
*/
struct list_head bridge_chain;
const struct drm_encoder_funcs *funcs;
const struct drm_encoder_helper_funcs *helper_private;
};
drm_encoder实例一般通过drm_simple_encoder_init简化接口进行初始化,通过drm_encoder_helper_add增加helper回调,并在connector初始化后,通过drm_connector_attach_encoder绑定到crtc上。
//zynqmp dp
zynqmp_dp_drm_init
//设置encoder支持的crtc
encoder->possible_crtcs != zynpmq_disp_get_crtc(dpsub->disp);
//初始化drm_encoder实例
drm_simple_encoder_init(dp->drm, encoder, DRM_MODE_ENCODER_TMDS);
//初始化drm_encoder实例,
drm_encoder_init(dev, encoder,
&drm_simple_encoder_funcs_cleanup, encoder_type, NULL);
__drm_encoder_init()
//记录encoder_type
encoder->encoder_type = encoder_type
//初始化encoder->funcs
encoder->funcs = funcs //drm_simple_encoder_funcs_cleanup
//将drm_encoder实例添加到mode_config.encoder_list链表
list_add_tail(&encoder->head, &dev->mode_config.encoder_list)
//初始化encoder序号
encoder->index = dev->mode_config.num_encoder++;
//初始化encoder->helper_private
drm_encoder_helper_add(encoder, &zynqmp_dp_encoder_helper_funcs)
encoder->helper_private = funcs ;//zynqmp_dp_encoder_helper_funcs
drm_encoder_funcs结构体比较简单,如下:
struct drm_encoder_funcs {
//通过drm_mode_config_reset调用
void (*reset)(struct drm_encoder *encoder);
//通过drm_mode_config_cleanup调用
void (*destroy)(struct drm_encoder *encoder);
//如下两个接口很少有encoder实现
//drm_dev_register阶段调用,用于注册额外的用户空间接口,如debugfs等
int (*late_register)(struct drm_encoder *encoder);
//drm_dev_unregister阶段调用
void (*early_unregister)(struct drm_encoder *encoder);
};
drm_simple_encoder_init初始化接口,drm_encoder.funcs = drm_simple_encoder_funcs_cleanup,该实例仅仅初始化了destroy成员
drm_encoder_funcs drm_simple_encoder_funcs_cleanup = {
.destroy = drm_encoder_cleanup,
}
helper回调接口,通过drm_encoder_helper_add赋值
/**
* struct drm_encoder_helper_funcs - helper operations for encoders
*
* These hooks are used by the legacy CRTC helpers, the transitional plane
* helpers and the new atomic modesetting helpers.
*/
struct drm_encoder_helper_funcs {
/**
* @dpms:
drm_helper_connector_dpms会调用其绑定的drm_encoder实例,进一步通过
drm_helper_encoder_dmps调用该dpms接口
*/
void (*dpms)(struct drm_encoder *encoder, int mode);
/**
* @mode_valid:
验证output mode的有效性, 通过drm_helper_probe_single_connector_modes()或者
drm_atomic_helper_check_modeset调用
*/
enum drm_mode_status (*mode_valid)(struct drm_encoder *crtc,
const struct drm_display_mode *mode);
/**
* @mode_fixup:
修正output mode, drm_atomic_helper_check_modeset会调用
*/
bool (*mode_fixup)(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
/**
* @prepare:
* 如果encoder正在运行,此时进行modeset需要通过该接口关掉,
一般情况下通过调用dpms接口(DRM_MODE_DPMS_OFF)实现
Atomic helpers接口使用@disable替代
*/
void (*prepare)(struct drm_encoder *encoder);
/**
* @commit:
* 该接口是在完成modeset之后,调用该接口打开encoder, 大部分驱动的该接口实现是
进一步调用其dmps(参数DRM_MODE_DPMS_ON), Atomic helpers接口使用@enable替代
*/
void (*commit)(struct drm_encoder *encoder);
/**
* @mode_set:
*
* 设置encoder的 display mode, crtc_set_mode接口中会调用
*/
void (*mode_set)(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
/**
* @atomic_mode_set:
atomic modeset helpers 使用该接口替代mode_set, crtc_set_mode接口中会调用
void (*atomic_mode_set)(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
/**
* @detect:
* 使用比较少,不做分析
*/
enum drm_connector_status (*detect)(struct drm_encoder *encoder,
struct drm_connector *connector);
/**
* @atomic_disable:
关闭encoder, 优先级比disable接口高, crtc_needs_disable接口中调用
*/
void (*atomic_disable)(struct drm_encoder *encoder,
struct drm_atomic_state *state);
/**
* @atomic_enable:
使用encoder,优先级比enable高, drm_atomic_helper_commit_modeset_enables中调用
*/
void (*atomic_enable)(struct drm_encoder *encoder,
struct drm_atomic_state *state);
/**
* @disable:
*
关闭encoder, 优先级比atomic_disable接口低, crtc_needs_disable接口中调用
*/
void (*disable)(struct drm_encoder *encoder);
/**
* @enable:
使用encoder,优先级比atomic_enable低, drm_atomic_helper_commit_modeset_enables中调用
*/
void (*enable)(struct drm_encoder *encoder);
/**
* @atomic_check:
检查encoder state
*/
int (*atomic_check)(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
};
调用逻辑如下:
其中注意到mode_fixup和atomic_check其实做了相同的事情,只是atomic_check优先级更高
drm_atomic_helper_check //一般被赋值给drm_mode_config_funcs.atomic_check
drm_atomic_helper_check_modeset
ret = mode_valid(state)
mode_valid_path
drm_encoder_mode_valid
//encoder侧验证mode的有效性
encoder_funcs = encoder->helper_private
encoder_funcs->mode_valid(encoder, mode);
mode_fixup(state)
//encoder侧修正mode
funcs = encoder->helper_private
//优先调用atomic_check
if ( funcs && funcs->atomic_check )
ret = funcs->atomic_check()
//其次调用mode_fixup
else if ( funcs & funcs->mode_fixup )
ret = funcs->mode_fixup()
funcs->mode_fixup(...)
用来设置encoder的display mode, 这两个接口会在crtc_set_mode接口中调用, 优先使用atomic_mode_set, 如果其未被实现,才会使用mode_set
drm_mode_config_helper_funcs vkms_mode_config_helper = {
.atomic_commit_tail = vkms_atomic_commit_tail;
}
//vkms_atomic_commit_tail
drm_atomic_helper_commit_modeset_disables
crtc_set_mode
funcs = encoder->helper_private
//优先使用atomic_mode_set
if (funcs && funcs->atomic_mode_set)
funcs->atomic_mode_set()
else if (funcs && funcs->mode_set)
funcs->mode_set
//由上述调用流程也可以看出这个是在commit阶段调用的
从下面的代码逻辑中,我们可以看出atomic_disable/disable/prepare/dpms其实都可以实现关闭encoder的作用,其优先级依次是atomic_disable > prepare > disable > dpms
drm_mode_config_helper_funcs vkms_mode_config_helper = {
.atomic_commit_tail = vkms_atomic_commit_tail;
}
//vkms_atomic_commit_tail
drm_atomic_helper_commit_modeset_disables
//关掉输出
disable_outputs
funcs = encoder->helper_private
//优先调用atomic_disable
if ( funcs->atomic_disable )
funcs->atomic_disable(encode, old_state)
//其次调用preparedisable
else if (new_conn_state->crtc && funcs->prepare)
funcs->prepare(encoder)
//其次调用disable
else if (funcs->disable)
funcs->disable(encoder)
//其次调用dpms
else if (funcs->dpms)
funcs->dpms(encoder, DRM_MODE_DPMS_OFF)
使能encoder,调用逻辑如下:
从下面的逻辑可以看出使用encoder有三种接口,其优先级依次为:
atomic_enable > enable > commit
drm_mode_config_helper_funcs vkms_mode_config_helper = {
.atomic_commit_tail = vkms_atomic_commit_tail;
}
//vkms_atomic_commit_tail
drm_atomic_helper_commit_modeset_enables
funcs = encoder->helper_private
//优先调用atomic_enable
if ( funcs->atomic_enable )
funcs->atomic_enable(encode, old_state)
//其次调用enable
else if (funcs->enable)
funcs->enable(encoder)
//其次调用commit
else if (funcs->commit)
funcs->commit(encoder)
(5)atomic_check
检查encoder的state, 注意到与mode_fixup功能相同,优先级更高
drm_atomic_helper_check //一般被赋值给drm_mode_config_funcs.atomic_check
drm_atomic_helper_check_modeset
ret = mode_valid(state)
mode_valid_path
drm_encoder_mode_valid
//encoder侧验证mode的有效性
encoder_funcs = encoder->helper_private
encoder_funcs->mode_valid(encoder, mode);
mode_fixup(state)
//encoder侧修正mode
funcs = encoder->helper_private
//优先调用atomic_check
if ( funcs && funcs->atomic_check )
ret = funcs->atomic_check()
//其次调用mode_fixup
else if ( funcs & funcs->mode_fixup )
ret = funcs->mode_fixup()
funcs->mode_fixup(...)