目录
1.2 struct drm_mode_object base
1.5 const struct drm_encoder_helper_funcs *helper_private
本章分析encoder本分
1. struct 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
* @crtc: currently bound CRTC
* @bridge: bridge associated to the encoder
* @funcs: control functions
* @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_<foo> 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_encoder_init().
*
* In reality almost every driver gets this wrong.
*
* 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;
/**
* @possible_clones: Bitmask of potential sibling encoders for cloning,
* using drm_encoder_index() as the index into the bitfield. The driver
* must set the bits for all &drm_encoder objects which can clone a
* &drm_crtc together with this encoder before calling
* drm_encoder_init(). Drivers should set the bit representing the
* encoder itself, too. Cloning bits should be set such that when two
* encoders can be used in a cloned configuration, they both should have
* each another bits set.
*
* In reality almost every driver gets this wrong.
*
* Note that since encoder objects can't be hotplugged the assigned indices
* are stable and hence known before registering all objects.
*/
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;
};
1.1 struct list_head head
和connector差不多的意思。
1.2 struct drm_mode_object base
和connector差不多的意思。
1.3 encoder_type
static const struct drm_prop_enum_list drm_encoder_enum_list[] = {
{ DRM_MODE_ENCODER_NONE, "None" },
{ DRM_MODE_ENCODER_DAC, "DAC" },
{ DRM_MODE_ENCODER_TMDS, "TMDS" },
{ DRM_MODE_ENCODER_LVDS, "LVDS" },
{ DRM_MODE_ENCODER_TVDAC, "TV" },
{ DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
{ DRM_MODE_ENCODER_DSI, "DSI" },
{ DRM_MODE_ENCODER_DPMST, "DP MST" },
{ DRM_MODE_ENCODER_DPI, "DPI" },
};
不同的encoder使用不同的类型。
1.4 struct drm_encoder_funcs
/**
* struct drm_encoder_funcs - encoder controls
*
* Encoders sit between CRTCs and connectors.
*/
struct drm_encoder_funcs {
/**
* @reset:
*
* Reset encoder hardware and software state to off. This function isn't
* called by the core directly, only through drm_mode_config_reset().
* It's not a helper hook only for historical reasons.
*/
void (*reset)(struct drm_encoder *encoder);
/**
* @destroy:
*
* Clean up encoder resources. This is only called at driver unload time
* through drm_mode_config_cleanup() since an encoder cannot be
* hotplugged in DRM.
*/
void (*destroy)(struct drm_encoder *encoder);
/**
* @late_register:
*
* This optional hook can be used to register additional userspace
* interfaces attached to the encoder like debugfs interfaces.
* It is called late in the driver load sequence from drm_dev_register().
* Everything added from this callback should be unregistered in
* the early_unregister callback.
*
* Returns:
*
* 0 on success, or a negative error code on failure.
*/
int (*late_register)(struct drm_encoder *encoder);
/**
* @early_unregister:
*
* This optional hook should be used to unregister the additional
* userspace interfaces attached to the encoder from
* @late_register. It is called from drm_dev_unregister(),
* early in the driver unload sequence to disable userspace access
* before data structures are torndown.
*/
void (*early_unregister)(struct drm_encoder *encoder);
};
1.5 const struct drm_encoder_helper_funcs *helper_private
/**
* 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:
*
* Callback to control power levels on the encoder. If the mode passed in
* is unsupported, the provider must use the next lowest power level.
* This is used by the legacy encoder helpers to implement DPMS
* functionality in drm_helper_connector_dpms().
*
* This callback is also used to disable an encoder by calling it with
* DRM_MODE_DPMS_OFF if the @disable hook isn't used.
*
* This callback is used by the legacy CRTC helpers. Atomic helpers
* also support using this hook for enabling and disabling an encoder to
* facilitate transitions to atomic, but it is deprecated. Instead
* @enable and @disable should be used.
*/
void (*dpms)(struct drm_encoder *encoder, int mode);
/**
* @mode_valid:
*
* This callback is used to check if a specific mode is valid in this
* encoder. This should be implemented if the encoder has some sort
* of restriction in the modes it can display. For example, a given
* encoder may be responsible to set a clock value. If the clock can
* not produce all the values for the available modes then this callback
* can be used to restrict the number of modes to only the ones that
* can be displayed.
*
* This hook is used by the probe helpers to filter the mode list in
* drm_helper_probe_single_connector_modes(), and it is used by the
* atomic helpers to validate modes supplied by userspace in
* drm_atomic_helper_check_modeset().
*
* This function is optional.
*
* NOTE:
*
* Since this function is both called from the check phase of an atomic
* commit, and the mode validation in the probe paths it is not allowed
* to look at anything else but the passed-in mode, and validate it
* against configuration-invariant hardward constraints. Any further
* limits which depend upon the configuration can only be checked in
* @mode_fixup or @atomic_check.
*
* RETURNS:
*
* drm_mode_status Enum
*/
enum drm_mode_status (*mode_valid)(struct drm_encoder *crtc,
const struct drm_display_mode *mode);
/**
* @mode_fixup:
*
* This callback is used to validate and adjust a mode. The parameter
* mode is the display mode that should be fed to the next element in
* the display chain, either the final &drm_connector or a &drm_bridge.
* The parameter adjusted_mode is the input mode the encoder requires. It
* can be modified by this callback and does not need to match mode. See
* also &drm_crtc_state.adjusted_mode for more details.
*
* This function is used by both legacy CRTC helpers and atomic helpers.
* This hook is optional.
*
* NOTE:
*
* This function is called in the check phase of atomic modesets, which
* can be aborted for any reason (including on userspace's request to
* just check whether a configuration would be possible). Atomic drivers
* MUST NOT touch any persistent state (hardware or software) or data
* structures except the passed in adjusted_mode parameter.
*
* This is in contrast to the legacy CRTC helpers where this was
* allowed.
*
* Atomic drivers which need to inspect and adjust more state should
* instead use the @atomic_check callback. If @atomic_check is used,
* this hook isn't called since @atomic_check allows a strict superset
* of the functionality of @mode_fixup.
*
* Also beware that userspace can request its own custom modes, neither
* core nor helpers filter modes to the list of probe modes reported by
* the GETCONNECTOR IOCTL and stored in &drm_connector.modes. To ensure
* that modes are filtered consistently put any encoder constraints and
* limits checks into @mode_valid.
*
* RETURNS:
*
* True if an acceptable configuration is possible, false if the modeset
* operation should be rejected.
*/
bool (*mode_fixup)(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
/**
* @prepare:
*
* This callback should prepare the encoder for a subsequent modeset,
* which in practice means the driver should disable the encoder if it
* is running. Most drivers ended up implementing this by calling their
* @dpms hook with DRM_MODE_DPMS_OFF.
*
* This callback is used by the legacy CRTC helpers. Atomic helpers
* also support using this hook for disabling an encoder to facilitate
* transitions to atomic, but it is deprecated. Instead @disable should
* be used.
*/
void (*prepare)(struct drm_encoder *encoder);
/**
* @commit:
*
* This callback should commit the new mode on the encoder after a modeset,
* which in practice means the driver should enable the encoder. Most
* drivers ended up implementing this by calling their @dpms hook with
* DRM_MODE_DPMS_ON.
*
* This callback is used by the legacy CRTC helpers. Atomic helpers
* also support using this hook for enabling an encoder to facilitate
* transitions to atomic, but it is deprecated. Instead @enable should
* be used.
*/
void (*commit)(struct drm_encoder *encoder);
/**
* @mode_set:
*
* This callback is used to update the display mode of an encoder.
*
* Note that the display pipe is completely off when this function is
* called. Drivers which need hardware to be running before they program
* the new display mode (because they implement runtime PM) should not
* use this hook, because the helper library calls it only once and not
* every time the display pipeline is suspend using either DPMS or the
* new "ACTIVE" property. Such drivers should instead move all their
* encoder setup into the @enable callback.
*
* This callback is used both by the legacy CRTC helpers and the atomic
* modeset helpers. It is optional in the atomic helpers.
*
* NOTE:
*
* If the driver uses the atomic modeset helpers and needs to inspect
* the connector state or connector display info during mode setting,
* @atomic_mode_set can be used instead.
*/
void (*mode_set)(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
/**
* @atomic_mode_set:
*
* This callback is used to update the display mode of an encoder.
*
* Note that the display pipe is completely off when this function is
* called. Drivers which need hardware to be running before they program
* the new display mode (because they implement runtime PM) should not
* use this hook, because the helper library calls it only once and not
* every time the display pipeline is suspended using either DPMS or the
* new "ACTIVE" property. Such drivers should instead move all their
* encoder setup into the @enable callback.
*
* This callback is used by the atomic modeset helpers in place of the
* @mode_set callback, if set by the driver. It is optional and should
* be used instead of @mode_set if the driver needs to inspect the
* connector state or display info, since there is no direct way to
* go from the encoder to the current connector.
*/
void (*atomic_mode_set)(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
/**
* @get_crtc:
*
* This callback is used by the legacy CRTC helpers to work around
* deficiencies in its own book-keeping.
*
* Do not use, use atomic helpers instead, which get the book keeping
* right.
*
* FIXME:
*
* Currently only nouveau is using this, and as soon as nouveau is
* atomic we can ditch this hook.
*/
struct drm_crtc *(*get_crtc)(struct drm_encoder *encoder);
/**
* @detect:
*
* This callback can be used by drivers who want to do detection on the
* encoder object instead of in connector functions.
*
* It is not used by any helper and therefore has purely driver-specific
* semantics. New drivers shouldn't use this and instead just implement
* their own private callbacks.
*
* FIXME:
*
* This should just be converted into a pile of driver vfuncs.
* Currently radeon, amdgpu and nouveau are using it.
*/
enum drm_connector_status (*detect)(struct drm_encoder *encoder,
struct drm_connector *connector);
/**
* @disable:
*
* This callback should be used to disable the encoder. With the atomic
* drivers it is called before this encoder's CRTC has been shut off
* using their own &drm_crtc_helper_funcs.disable hook. If that
* sequence is too simple drivers can just add their own driver private
* encoder hooks and call them from CRTC's callback by looping over all
* encoders connected to it using for_each_encoder_on_crtc().
*
* This hook is used both by legacy CRTC helpers and atomic helpers.
* Atomic drivers don't need to implement it if there's no need to
* disable anything at the encoder level. To ensure that runtime PM
* handling (using either DPMS or the new "ACTIVE" property) works
* @disable must be the inverse of @enable for atomic drivers.
*
* NOTE:
*
* With legacy CRTC helpers there's a big semantic difference between
* @disable and other hooks (like @prepare or @dpms) used to shut down a
* encoder: @disable is only called when also logically disabling the
* display pipeline and needs to release any resources acquired in
* @mode_set (like shared PLLs, or again release pinned framebuffers).
*
* Therefore @disable must be the inverse of @mode_set plus @commit for
* drivers still using legacy CRTC helpers, which is different from the
* rules under atomic.
*/
void (*disable)(struct drm_encoder *encoder);
/**
* @enable:
*
* This callback should be used to enable the encoder. With the atomic
* drivers it is called after this encoder's CRTC has been enabled using
* their own &drm_crtc_helper_funcs.enable hook. If that sequence is
* too simple drivers can just add their own driver private encoder
* hooks and call them from CRTC's callback by looping over all encoders
* connected to it using for_each_encoder_on_crtc().
*
* This hook is used only by atomic helpers, for symmetry with @disable.
* Atomic drivers don't need to implement it if there's no need to
* enable anything at the encoder level. To ensure that runtime PM handling
* (using either DPMS or the new "ACTIVE" property) works
* @enable must be the inverse of @disable for atomic drivers.
*/
void (*enable)(struct drm_encoder *encoder);
/**
* @atomic_check:
*
* This callback is used to validate encoder state for atomic drivers.
* Since the encoder is the object connecting the CRTC and connector it
* gets passed both states, to be able to validate interactions and
* update the CRTC to match what the encoder needs for the requested
* connector.
*
* Since this provides a strict superset of the functionality of
* @mode_fixup (the requested and adjusted modes are both available
* through the passed in &struct drm_crtc_state) @mode_fixup is not
* called when @atomic_check is implemented.
*
* This function is used by the atomic helpers, but it is optional.
*
* NOTE:
*
* This function is called in the check phase of an atomic update. The
* driver is not allowed to change anything outside of the free-standing
* state objects passed-in or assembled in the overall &drm_atomic_state
* update tracking structure.
*
* Also beware that userspace can request its own custom modes, neither
* core nor helpers filter modes to the list of probe modes reported by
* the GETCONNECTOR IOCTL and stored in &drm_connector.modes. To ensure
* that modes are filtered consistently put any encoder constraints and
* limits checks into @mode_valid.
*
* RETURNS:
*
* 0 on success, -EINVAL if the state or the transition can't be
* supported, -ENOMEM on memory allocation failure and -EDEADLK if an
* attempt to obtain another state object ran into a &drm_modeset_lock
* deadlock.
*/
int (*atomic_check)(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
};
static const struct drm_encoder_helper_funcs dw_hdmi_ts_encoder_helper_funcs = {
.mode_fixup = dw_hdmi_ts_encoder_mode_fixup,
.mode_set = dw_hdmi_ts_encoder_mode_set,
.enable = dw_hdmi_ts_encoder_enable,
.disable = dw_hdmi_ts_encoder_disable,
.atomic_check = dw_hdmi_ts_encoder_atomic_check,
};
这些API我们会在后面的分析中讲到。
2. encoider的API
2.1 drm_encoder_init
/**
* drm_encoder_init - Init a preallocated encoder
* @dev: drm device
* @encoder: the encoder to init
* @funcs: callbacks for this encoder
* @encoder_type: user visible type of the encoder
* @name: printf style format string for the encoder name, or NULL for default name
*
* Initialises a preallocated encoder. Encoder should be subclassed as part of
* driver encoder objects. At driver unload time drm_encoder_cleanup() should be
* called from the driver's &drm_encoder_funcs.destroy hook.
*
* Returns:
* Zero on success, error code on failure.
*/
int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type, const char *name, ...)
{
int ret;
ret = drm_mode_object_add(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
if (ret)
return ret;
encoder->dev = dev;
encoder->encoder_type = encoder_type;
encoder->funcs = funcs;
if (name) {
va_list ap;
va_start(ap, name);
encoder->name = kvasprintf(GFP_KERNEL, name, ap);
va_end(ap);
} else {
encoder->name = kasprintf(GFP_KERNEL, "%s-%d",
drm_encoder_enum_list[encoder_type].name,
encoder->base.id);
}
if (!encoder->name) {
ret = -ENOMEM;
goto out_put;
}
list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
encoder->index = dev->mode_config.num_encoder++;
out_put:
if (ret)
drm_mode_object_unregister(dev, &encoder->base);
return ret;
}
(1)和drm_connector_init差不多,生成一个类型为DRM_MODE_OBJECT_ENCODER的struct drm_mode_object结构体。
(2)根据传进来的参数赋值encoder变量。
(3)把encoder加入到结构体struct drm_mode_config 的encoder_list链表中。
(4)结构体struct drm_mode_config中的num_encoder增加计数。
2.2 drm_mode_getencoder
int drm_mode_getencoder(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_mode_get_encoder *enc_resp = data;
struct drm_encoder *encoder;
struct drm_crtc *crtc;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
encoder = drm_encoder_find(dev, enc_resp->encoder_id);
if (!encoder)
return -ENOENT;
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
crtc = drm_encoder_get_crtc(encoder);
if (crtc)
enc_resp->crtc_id = crtc->base.id;
else
enc_resp->crtc_id = 0;
drm_modeset_unlock(&dev->mode_config.connection_mutex);
enc_resp->encoder_type = encoder->encoder_type;
enc_resp->encoder_id = encoder->base.id;
enc_resp->possible_crtcs = encoder->possible_crtcs;
enc_resp->possible_clones = encoder->possible_clones;
return 0;
}
(1)根据APP传入的ID找到encoder。
(2)根据找到的encoder找到适配的crtc。
(3)参数赋值。