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
在这里我们考虑设备树中只存在 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
connector 创建方式的演变
drm 最早是 vendor driver 自行定义并创建 connector。
后面演变成最后一个 drm bridge 在 attach 函数里负责创建 connector。
最新的做法演变成不在 drm bridge 中创建 connector,drm_bridge_attach() 需要传入 DRM_BRIDGE_ATTACH_NO_CONNECTOR。 而需要调用 drm_bridge_connector_init() 来自动创建 connector。
对于 dpi 接口的屏,之前的做法是 panel bridge driver(panel.c) 在 .attach 接口中创建 connector,更符合现在 drm 框架的做法需要 vendor display driver 利用 drm_bridge_connector_init() 来创建。
对于 hdmi, dp 等接口的屏,利用 connector bridge driver (display-connector.c) + drm_bridge_connector_init() 来创建。
