Overview

struct drm_bridge 代表挂在 encoder 后面的设备。当 drm_encoder 不能完全代表整个 encoder chain 的时候, drm_bridge 就有用了。

Encoder –> Bridge A –> Bridge B

drm bridge 和 drm panel 一样,不是 drm_mode_object, 对 userspace 是不可见的。他们只是用来提供额外的 hooks 来得到 encoder chain 最终的理想输出。

Display driver 负责把 encoder link 到第一个 bridge,通过devm_drm_of_get_bridge()获取 bridge,再通过drm_bridge_attach()将 bridge attach 到 encoder 上。

Bridge driver 负责把自己 link 到下一级 bridge,通过在 drm_bridges_funcs.attach 中调用drm_bridge_attach()

最后一级 Bridge driver 还参与实现 drm connector, 通过drm_bridge_connector_init() helper 来创建 drm_connector. 或者通过 bridge 暴露出来的 connector 相关操作函数来手动实现 connector.

bridge driver

在这里我们考虑设备树中只存在 panel node,而不存在 bridge node 的情况。

Data Structure

struct drm_bridge {
	struct drm_private_obj base;
	struct drm_device *dev;
	struct drm_encoder *encoder;
	struct list_head chain_node;
	struct device_node *of_node;
	struct list_head list;
	const struct drm_bridge_timings *timings;
	const struct drm_bridge_funcs *funcs;
	void *driver_private;
	enum drm_bridge_ops ops;
	int type;
	bool interlace_allowed;
	struct i2c_adapter *ddc;
	struct mutex hpd_mutex;
	void (*hpd_cb)(void *data, enum drm_connector_status status);
	void *hpd_data;
};

encoder: bridge 连接的 encoder

of_node: bridge 在设备树中对应的节点

type: bridge output 的格式 DRM_MODE_CONNECTOR_*

interlace_allowed: bridge 是否能处理 interlace mode

struct drm_bridge_state {
	struct drm_private_state base;
	struct drm_bridge *bridge;
	struct drm_bus_cfg input_bus_cfg;
	struct drm_bus_cfg output_bus_cfg;
}
struct drm_bridge_funcs {
	int (*attach)(struct drm_bridge *bridge,
		      enum drm_bridge_attach_flags flags);
	void (*detach)(struct drm_bridge *bridge);
	enum drm_mode_status (*mode_valid)(struct drm_bridge *bridge,
					   const struct drm_display_info *info,
					   const struct drm_display_mode *mode);
	bool (*mode_fixup)(struct drm_bridge *bridge,
			   const struct drm_display_mode *mode,
			   struct drm_display_mode *adjusted_mode);
	void (*atomic_pre_enable)(struct drm_bridge *bridge,
				  struct drm_bridge_state *old_bridge_state);
	void (*atomic_enable)(struct drm_bridge *bridge,
			      struct drm_bridge_state *old_bridge_state);
	void (*atomic_disable)(struct drm_bridge *bridge,
			       struct drm_bridge_state *old_bridge_state);
	void (*atomic_post_disable)(struct drm_bridge *bridge,
				    struct drm_bridge_state *old_bridge_state);
	struct drm_bridge_state *(*atomic_duplicate_state)(struct drm_bridge *bridge);
	void (*atomic_destroy_state)(struct drm_bridge *bridge,
				     struct drm_bridge_state *state);
	u32 *(*atomic_get_output_bus_fmts)(struct drm_bridge *bridge,
					   struct drm_bridge_state *bridge_state,
					   struct drm_crtc_state *crtc_state,
					   struct drm_connector_state *conn_state,
					   unsigned int *num_output_fmts);
	u32 *(*atomic_get_input_bus_fmts)(struct drm_bridge *bridge,
					  struct drm_bridge_state *bridge_state,
					  struct drm_crtc_state *crtc_state,
					  struct drm_connector_state *conn_state,
					  u32 output_fmt,
					  unsigned int *num_input_fmts);
	int (*atomic_check)(struct drm_bridge *bridge,
			    struct drm_bridge_state *bridge_state,
			    struct drm_crtc_state *crtc_state,
			    struct drm_connector_state *conn_state);
	struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);
	enum drm_connector_status (*detect)(struct drm_bridge *bridge);
	int (*get_modes)(struct drm_bridge *bridge,
			 struct drm_connector *connector);
	const struct drm_edid *(*edid_read)(struct drm_bridge *bridge,
					    struct drm_connector *connector);
	void (*hpd_notify)(struct drm_bridge *bridge,
			   enum drm_connector_status status);
	void (*hpd_enable)(struct drm_bridge *bridge);
	void (*hpd_disable)(struct drm_bridge *bridge);
	void (*debugfs_init)(struct drm_bridge *bridge, struct dentry *root);
};

attach: optional, attach bridge to encoder, invoked in drm_bridge_attach

detach: optional, detach bridge to encoder

mode_valid: optional, check display mode restraints in bridge

mode_fixup: optional, fix display mode and store into adjusted mode

atomic_pre_enable: optional, enable bridge before the preceding element is enabled(like encoder enable function).

atomic_enable: optional, enable bridge after the preceding element is enabled

atomic_disable: optional, disable bridge before the preceding element is disabled

atomic_post_disable: optional, disable bridge after the preceding element is disabled

atomic_duplicate_state: mandatory, duplicate drm_bridge_state. If &drm_bridge_state is note subclassed, then use drm_atomic_helper_bridge_duplicate_state

atomic_destroy_state: mandatory, drm_atomic_helper_bridge_destroy_state

atomic_get_output_bus_fmts:

atomic_get_input_bus_fmts:

atomic_check: 检查

atomic_reset:

detect:

get_modes:

edid_read:

hpd_notify:

hpd_enable:

hpd_disable:

  classDiagram
    class 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 list_head bridge_chain
        +const struct drm_encoder_funcs *funcs
        +const struct drm_encoder_helper_funcs *helper_private
	+int (*attach)("struct drm_bridge *bridge, enum drm_bridge_attach_flags flags")
    }

    class drm_connector {
        +struct drm_device *dev
        +struct list_head head
        +struct drm_mode_object base
        +char *name
        +unsigned index
        +int connector_type
        +int connector_type_id
        +struct list_head modes
        +enum drm_connector_status status
        +uint8_t polled
        +const struct drm_connector_helper_funcs *helper_private
        +enum drm_connector_force force
	+u32 possible_encoders;
    }

    class drm_bridge {
        +struct drm_private_obj base
        +struct drm_device *dev
        +struct drm_encoder *encoder
        +struct list_head chain_node
        +struct device_node *of_node
        +struct list_head list
        +const struct drm_bridge_timings *timings
        +const struct drm_bridge_funcs *funcs
        +void *driver_private
        +enum drm_bridge_ops ops
        +int type
        +bool interlace_allowed
        +bool pre_enable_prev_first
        +struct i2c_adapter *ddc
    }

    class drm_panel {
        +struct device *dev
        +struct backlight_device *backlight
        +const struct drm_panel_funcs *funcs
        +int connector_type
        +struct list_head list
        +struct list_head followers
        +struct mutex follower_lock
        +bool prepare_prev_first
        +bool prepared
        +bool enabled
    }

    note for panel_bridge "Bridge driver private data"
    class panel_bridge {
        +struct drm_bridge bridge
        +struct drm_connector connector
        +struct drm_panel *panel
        +u32 connector_type
    }

    drm_encoder "1" --> "*" drm_bridge : bridge_chain
    drm_bridge "1" o-- "1" drm_encoder : encoder
    panel_bridge --|> drm_bridge : 继承
    panel_bridge "1" o-- "1" drm_panel : panel
    panel_bridge "1" o-- "1" drm_connector : connector

API

bridge:

void drm_bridge_add(struct drm_bridge *bridge);
int devm_drm_bridge_add(struct device *dev, struct drm_bridge *bridge);
void drm_bridge_remove(struct drm_bridge *bridge);
int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
		      struct drm_bridge *previous,
		      enum drm_bridge_attach_flags flags);
struct drm_bridge *of_drm_find_bridge(struct device_node *np);
  sequenceDiagram
    participant A as encoder driver
    participant B as drm core<br/>(drm_bridge.c)<br/>(panel.c)
    participant C as bridge driver<br/>(panel.c)
    participant D as drm connector<br/>(drm_connector.c)<br/>(panel.c)

    A ->>+ B: drmm_of_get_bridge
    B -->> B: drm_of_find_panel_or_bridge
    B -->>+ B: drmm_panel_bridge_add
    B -->> B: drm_panel_bridge_add_typed
    Note right of B: 分配注册struct panel_bridge
    B -->>- B: drm_bridge_add
    Note right of B: list_add_tail(&bridge->list, &bridge_list);
    B -->>- A: struct drm_bridge
    A ->>+ B: drm_bridge_attach
    B -->> B: bridge->dev = encoder->dev<br/>bridge->encoder = encoder
    B ->>+ C: bridge->funcs->attach<br/>(panel_bridge_attach)
    C -->> C: drm_connector_helper_add()<br/>drm_connector_init()<br/>drm_connector_attach_encoder()
    C ->> D: connector->func->reset
    C ->>- D: drm_connector_register
    B ->>- C: bridge->funcs->atomic_reset