4.13.3. encoder代码分析

4.13.3.1. struct drm_encoder结构体

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;

    /**
     * @crtc: Currently bound CRTC, only really meaningful for non-atomic
     * drivers.  Atomic drivers should instead check
     * &drm_connector_state.crtc.
     */
    struct drm_crtc *crtc;
    struct drm_bridge *bridge;
    const struct drm_encoder_funcs *funcs;
    const struct drm_encoder_helper_funcs *helper_private;
};
  • 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" },
};
  • struct drm_encoder_funcs

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);
};
  • drm_encoder_helper_funcs

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);

    /**
     * @atomic_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.atomic_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 callback is a variant of @disable that provides the atomic state
     * to the driver. If @atomic_disable is implemented, @disable is not
     * called by the helpers.
     *
     * This hook is only used by 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 @atomic_disable must be the inverse of
     * @atomic_enable.
     */
    void (*atomic_disable)(struct drm_encoder *encoder,
                   struct drm_atomic_state *state);

    /**
     * @atomic_enable:
     *
     * This callback should be used to enable the encoder. It is called
     * after this encoder's CRTC has been enabled using their own
     * &drm_crtc_helper_funcs.atomic_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 callback is a variant of @enable that provides the atomic state
     * to the driver. If @atomic_enable is implemented, @enable is not
     * called by the helpers.
     *
     * This hook is only used by atomic helpers, it is the opposite of
     * @atomic_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 works @atomic_enable must be the inverse of
     * @atomic_disable.
     */
    void (*atomic_enable)(struct drm_encoder *encoder,
                  struct drm_atomic_state *state);

    /**
     * @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.
     *
     * For atomic drivers also consider @atomic_disable and save yourself
     * from having to read the NOTE below!
     *
     * 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 only used by atomic helpers, it is the opposite of
     * @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);
};

4.13.3.2. drm_encoder_init

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;

    /* encoder index is used with 32bit bitmasks */
    if (WARN_ON(dev->mode_config.num_encoder >= 32))
        return -EINVAL;

    //与connector类似,生成一个类型为DRM_MODE_OBJECT_ENCODER的struct drm_mode_object结构体
    ret = drm_mode_object_add(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
    if (ret)
        return ret;

    //初始化encoder成员变量
    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;
    }

    //将encoder加入到结构体struct drm_mode_config的encoder链表中
    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;
}
  • 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 -EOPNOTSUPP;

    //根据传入的id找到encoder
    encoder = drm_encoder_find(dev, file_priv, enc_resp->encoder_id);
    if (!encoder)
        return -ENOENT;

    drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
    //找到encoder适配的crtc
    crtc = drm_encoder_get_crtc(encoder);
    if (crtc && drm_lease_held(file_priv, crtc->base.id))
        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 = drm_lease_filter_crtcs(file_priv,
                              encoder->possible_crtcs);
    enc_resp->possible_clones = encoder->possible_clones;

    return 0;
}

4.13.3.3. rcar_du_encoder_init

//drivers/gpu/drm/rcar-du/rcar_du_encoder.c
int rcar_du_encoder_init(struct rcar_du_device *rcdu,
             enum rcar_du_output output,
             struct device_node *enc_node)
{
    struct rcar_du_encoder *renc;
    struct drm_encoder *encoder;
    struct drm_bridge *bridge;
    int ret;

    renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
    if (renc == NULL)
        return -ENOMEM;

    rcdu->encoders[output] = renc;
    renc->output = output;
    encoder = rcar_encoder_to_drm_encoder(renc);

    dev_dbg(rcdu->dev, "initializing encoder %pOF for output %u\n",
        enc_node, output);

    /*
     * Locate the DRM bridge from the DT node. For the DPAD outputs, if the
     * DT node has a single port, assume that it describes a panel and
     * create a panel bridge.
     */
    if ((output == RCAR_DU_OUTPUT_DPAD0 ||
         output == RCAR_DU_OUTPUT_DPAD1) &&
        rcar_du_encoder_count_ports(enc_node) == 1) {
        struct drm_panel *panel = of_drm_find_panel(enc_node);

        if (IS_ERR(panel)) {
            ret = PTR_ERR(panel);
            goto done;
        }

        bridge = devm_drm_panel_bridge_add(rcdu->dev, panel,
                           DRM_MODE_CONNECTOR_DPI);
        if (IS_ERR(bridge)) {
            ret = PTR_ERR(bridge);
            goto done;
        }
    } else {
        bridge = of_drm_find_bridge(enc_node);
        if (!bridge) {
            if (output == RCAR_DU_OUTPUT_HDMI0 ||
                output == RCAR_DU_OUTPUT_HDMI1) {
#if IS_ENABLED(CONFIG_DRM_RCAR_DW_HDMI)
                ret = -EPROBE_DEFER;
#else
                ret = 0;
#endif
                goto done;
            } else if (output == RCAR_DU_OUTPUT_LVDS0 ||
                   output == RCAR_DU_OUTPUT_LVDS1) {
#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
                ret = -EPROBE_DEFER;
#else
                ret = 0;
#endif
                goto done;
            } else if (output == RCAR_DU_OUTPUT_MIPI_DSI0 ||
                   output == RCAR_DU_OUTPUT_MIPI_DSI1) {
#if IS_ENABLED(CONFIG_DRM_RCAR_MIPI_DSI)
                ret = -EPROBE_DEFER;
#else
                ret = 0;
#endif
                goto done;
            } else {
                ret = -EPROBE_DEFER;
                goto done;
            }
        }

        if (output == RCAR_DU_OUTPUT_LVDS0 ||
            output == RCAR_DU_OUTPUT_LVDS1)
            rcdu->lvds[output - RCAR_DU_OUTPUT_LVDS0] = bridge;
    }

    /*
     * On Gen3 skip the LVDS1 output if the LVDS1 encoder is used as a
     * companion for LVDS0 in dual-link mode.
     */
    if (rcdu->info->gen >= 3 && output == RCAR_DU_OUTPUT_LVDS1) {
        if (rcar_lvds_dual_link(bridge)) {
            ret = -ENOLINK;
            goto done;
        }
    }

    renc->bridge = bridge;

    ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
                   DRM_MODE_ENCODER_NONE, NULL);
    if (ret < 0)
        goto done;

    drm_encoder_helper_add(encoder, &encoder_helper_funcs);

    /*
     * Attach the bridge to the encoder. The bridge will create the
     * connector.
     */
    ret = drm_bridge_attach(encoder, bridge, NULL);
    if (ret) {
        drm_encoder_cleanup(encoder);
        return ret;
    }

done:
    if (ret < 0) {
        if (encoder->name)
            encoder->funcs->destroy(encoder);
        devm_kfree(rcdu->dev, renc);
    }

    return ret;
}
  • 调用关系

rcar_du_probe
|-- rcar_du_modeset_init
|---|---drm_mode_config_init
    |---rcar_du_properties_init
    |---drm_vblank_init
    |---rcar_du_planes_init
    |---rcar_du_vsps_init
    |---rcar_du_crtc_create
    |---rcar_du_encoders_init
    |   |---rcar_du_encoders_init_one
    |       |---rcar_du_encoder_init
    |---rcar_du_writeback_init
|---drm_dev_register
|---drm_fbdev_generic_setup