Panel and Bridge
情况 1:设备树存在 panel 节点
imx6ull-dhcom-pdk2.dts 中的存在 panel 节点,对应 panel-simple.c:
&lcdif {
status = "okay";
port {
display_out: endpoint {
remote-endpoint = <&panel_in>;
};
};
};
panel {
compatible = "auo,g101evn010";
power-supply = <&ldo4_ext>;
backlight = <&lcd_backlight>;
port {
panel_in: endpoint {
remote-endpoint = <&display_out>;
};
};
};
这种情况比较简单,在底层 driver 中调用drm_of_find_panel_or_bridge找到设备树中的 panel 节点,和 panel driver 匹配,找到 panel driver 注册的 drm_panel 结构体。
再调用devm_drm_panel_bridge_add, 分配 panel_bridge, 注册一个固定的drm_bridge。
最后调用drm_bridge_attach, 调用到 bridge->funcs->attach, 即 panel_bridge_attach, 注册 connector,以及把 connector attach 到 encoder。
情况 2:设备树存在 bridge 节点, connector 节点
da850-lcdk.dts 中存在 bridge 和 connector 节点,对应 simple-bridge.c:
&lcdc {
compatible = "ti,da850-tilcdc";
status = "okay";
port {
lcdc_out_vga: endpoint {
remote-endpoint = <&vga_bridge_in>;
};
};
};
vga-bridge {
compatible = "ti,ths8135";
#address-cells = <1>;
#size-cells = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
vga_bridge_in: endpoint {
remote-endpoint = <&lcdc_out_vga>;
};
};
port@1 {
reg = <1>;
vga_bridge_out: endpoint {
remote-endpoint = <&vga_con_in>;
};
};
};
};
vga {
compatible = "vga-connector";
ddc-i2c-bus = <&i2c0>;
port {
vga_con_in: endpoint {
remote-endpoint = <&vga_bridge_out>;
};
};
};
底层 driver 仍然调用drm_of_find_panel_or_bridge函数,因为 panel 节点不存在,所以会找到设备树中的 bridge 节点,和 bridge driver 匹配,找到 bridge driver 注册的 drm_bridge 结构体。
再调用drm_bridge_attach, 调用到 bridge->funcs->attach, 即 simple_bridge_attach。
在这个函数中又会调用一次 drm_bridge_attach,不过这个时候传入的 bridge 为设备树中 port@1 中的 remote-endpoint 节点,对应的是 connector。
最后注册 connector,以及把 connector attach 到 encoder。
数据结构
struct panel_bridge {
struct drm_bridge bridge;
struct drm_connector connector;
struct drm_panel *panel;
u32 connector_type;
};
panel bridge 是有 drm_panel 注册后,需要固定注册一个 drm_bridge。
struct 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; // 确保在调用panel prepare之前,mipi dsi driver初始化完成, 主动置起该flag
bool prepared; // panel是否prepared,在drm_panel_prepare中置起
bool enabled; // panel是否enable,在drm_panel_enable中置起
};
struct drm_panel_funcs {
int (*prepare)(struct drm_panel *panel);
int (*enable)(struct drm_panel *panel);
int (*disable)(struct drm_panel *panel);
int (*unprepare)(struct drm_panel *panel);
int (*get_modes)(struct drm_panel *panel,
struct drm_connector *connector);
enum drm_panel_orientation (*get_orientation)(struct drm_panel *panel);
int (*get_timings)(struct drm_panel *panel, unsigned int num_timings,
struct display_timing *timings);
void (*debugfs_init)(struct drm_panel *panel, struct dentry *root);
};
prepare
: optinal, 做一些 setup 工作, 在 drm_panel_prepare 中调用
enable
: optional, enable panel, 打开背光等,在 drm_panel_enable 中调用
disable
: optional, 在 drm_panel_disable 中调用
unprepare
: optional, 和 prepare 相反
get_modes
: mandatory, 把 panel 支持的 mode 加入到 connector->probed_modes 中。在 drm_panel_get_modes 中调用,如果 drvier 绑定了 panel,那么 connector 的 get_modes 回调,直接调用 drm_panel_get_modes 就可以
get_orientation
: optional, 从设备树或者 edid 中获取 panel 的方向
get_timings
: optional, 返回 panel driver 中 fixed timing,这个回调看起来没怎么用到
struct drm_bridge {
struct drm_private_obj base;
struct drm_device *dev;
struct drm_encoder *encoder; // bridge连接的encoder
struct list_head chain_node;
struct device_node *of_node; // bridge在设备树中对应的节点
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; // bridge output的格式 DRM_MODE_CONNECTOR_*
bool interlace_allowed; // bridge是否能处理interlace mode
bool pre_enable_prev_first;
struct i2c_adapter *ddc;
struct mutex hpd_mutex;
void (*hpd_cb)(void *data, enum drm_connector_status status);
void *hpd_data;
};
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 (*disable)(struct drm_bridge *bridge);
void (*post_disable)(struct drm_bridge *bridge);
void (*mode_set)(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode);
void (*pre_enable)(struct drm_bridge *bridge);
void (*enable)(struct drm_bridge *bridge);
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
disable
: deprecatedpost_disable
: deprecatedmode_set
: deprecatedpre_enable
: deprecatedenable
: deprecated
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
: