Panel and Bridge
对于 dpi, mipi dsi connector, DRM core 提供了 struct drm_panel
来简化流程。
情况 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 drm_panel {
struct device *dev;
struct backlight_device *backlight;
const struct drm_panel_funcs *funcs;
int connector_type;
struct list_head list;
bool prepared;
bool enabled;
};
prepare_prev_first: 确保在调用 panel prepare 之前,mipi dsi driver 初始化完成,主动置起该 flag
prepared: panel 是否 prepared,在 drm_panel_prepare 中置起
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,这个回调看起来没怎么用到
APIs
panel:
void drm_panel_init(struct drm_panel *panel, struct device *dev,
const struct drm_panel_funcs *funcs,
int connector_type);
void drm_panel_add(struct drm_panel *panel);
void drm_panel_remove(struct drm_panel *panel);
int drm_panel_prepare(struct drm_panel *panel);
int drm_panel_unprepare(struct drm_panel *panel);
int drm_panel_enable(struct drm_panel *panel);
int drm_panel_disable(struct drm_panel *panel);
int drm_panel_get_modes(struct drm_panel *panel, struct drm_connector *connector);
struct drm_panel *of_drm_find_panel(const struct device_node *np);
int of_drm_get_panel_orientation(const struct device_node *np,
enum drm_panel_orientation *orientation);
int drm_panel_of_backlight(struct drm_panel *panel);
function flow
panel driver:
%%{init: {'theme': 'default' }}%% flowchart LR probe("probe()") -->1 & 2 1("drm_panel_init()") --> 3(" panel->dev = dev;<br>panel->funcs = funcs;<br>panel->connector_type = connector_type;"):::yellow 2("drm_panel_add()") --> 4("list_add_tail(&panel->list, &panel_list);"):::yellow classDef yellow fill:#fdfd00,color:#000000,stroke:#e0e000
bridge driver
%%{init: {'theme': 'default' }}%% flowchart LR probe("probe()") -->A & B A("devm_drm_of_get_bridge()") B("drm_bridge_add()") classDef yellow fill:#fdfd00,color:#000000,stroke:#e0e000
drm driver:
%%{init: {'theme': 'default' }}%% flowchart LR probe(probe) --> 1 & 11 1("devm_drm_of_get_bridge()") --> 2("drm_of_find_panel_or_bridge()") 2 --> 3("of_drm_find_panel()") 3 --> 5("list_for_each_entry(panel, &panel_list, list)"):::yellow 2 --> 4("of_drm_find_bridge()") 4 --> 6("list_for_each_entry(bridge, &bridge_list, list)"):::yellow 1 -- 如果找到了panel--> 7("devm_drm_panel_bridge_add()") 7 --> 8("drm_panel_bridge_add_typed()") 8 --> 9("注册struct panel_bridge"):::yellow 8 --注册bridge--> 10("drm_bridge_add()") 11(drm_bridge_attach) --> 12("bridge->encoder = encoder;"):::yellow 11 --> 13("bridge->funcs->attach<br>panel_bridge_attach") 11 --> 14("bridge->funcs->atomic_reset") 13 --> 15("drm_connector_helper_add<br>drm_connector_init<br>drm_connector_attach_encoder") classDef yellow fill:#fdfd00,color:#000000,stroke:#e0e000