Skip to content

Commit

Permalink
drm/bridge: Make the bridge chain a double-linked list
Browse files Browse the repository at this point in the history
So that each element in the chain can easily access its predecessor.
This will be needed to support bus format negotiation between elements
of the bridge chain.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191203141515.3597631-5-boris.brezillon@collabora.com
  • Loading branch information
Boris Brezillon committed Dec 9, 2019
1 parent 35a61fe commit 05193dc
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 78 deletions.
171 changes: 111 additions & 60 deletions drivers/gpu/drm/drm_bridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
* just provide additional hooks to get the desired output at the end of the
* encoder chain.
*
* Bridges can also be chained up using the &drm_bridge.next pointer.
* Bridges can also be chained up using the &drm_bridge.chain_node field.
*
* Both legacy CRTC helpers and the new atomic modeset helpers support bridges.
*/
Expand Down Expand Up @@ -128,20 +128,21 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
bridge->dev = encoder->dev;
bridge->encoder = encoder;

if (previous)
list_add(&bridge->chain_node, &previous->chain_node);
else
list_add(&bridge->chain_node, &encoder->bridge_chain);

if (bridge->funcs->attach) {
ret = bridge->funcs->attach(bridge);
if (ret < 0) {
list_del(&bridge->chain_node);
bridge->dev = NULL;
bridge->encoder = NULL;
return ret;
}
}

if (previous)
previous->next = bridge;
else
encoder->bridge = bridge;

return 0;
}
EXPORT_SYMBOL(drm_bridge_attach);
Expand All @@ -157,6 +158,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)
if (bridge->funcs->detach)
bridge->funcs->detach(bridge);

list_del(&bridge->chain_node);
bridge->dev = NULL;
}

Expand Down Expand Up @@ -190,18 +192,21 @@ bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
bool ret = true;
struct drm_encoder *encoder;

if (!bridge)
return true;

if (bridge->funcs->mode_fixup)
ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode);
encoder = bridge->encoder;
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (!bridge->funcs->mode_fixup)
continue;

ret = ret && drm_bridge_chain_mode_fixup(bridge->next, mode,
adjusted_mode);
if (!bridge->funcs->mode_fixup(bridge, mode, adjusted_mode))
return false;
}

return ret;
return true;
}
EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);

Expand All @@ -224,18 +229,24 @@ enum drm_mode_status
drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
const struct drm_display_mode *mode)
{
enum drm_mode_status ret = MODE_OK;
struct drm_encoder *encoder;

if (!bridge)
return ret;
return MODE_OK;

if (bridge->funcs->mode_valid)
ret = bridge->funcs->mode_valid(bridge, mode);
encoder = bridge->encoder;
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
enum drm_mode_status ret;

if (!bridge->funcs->mode_valid)
continue;

if (ret != MODE_OK)
return ret;
ret = bridge->funcs->mode_valid(bridge, mode);
if (ret != MODE_OK)
return ret;
}

return drm_bridge_chain_mode_valid(bridge->next, mode);
return MODE_OK;
}
EXPORT_SYMBOL(drm_bridge_chain_mode_valid);

Expand All @@ -251,13 +262,20 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
*/
void drm_bridge_chain_disable(struct drm_bridge *bridge)
{
struct drm_encoder *encoder;
struct drm_bridge *iter;

if (!bridge)
return;

drm_bridge_chain_disable(bridge->next);
encoder = bridge->encoder;
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->disable)
iter->funcs->disable(iter);

if (bridge->funcs->disable)
bridge->funcs->disable(bridge);
if (iter == bridge)
break;
}
}
EXPORT_SYMBOL(drm_bridge_chain_disable);

Expand All @@ -274,13 +292,16 @@ EXPORT_SYMBOL(drm_bridge_chain_disable);
*/
void drm_bridge_chain_post_disable(struct drm_bridge *bridge)
{
struct drm_encoder *encoder;

if (!bridge)
return;

if (bridge->funcs->post_disable)
bridge->funcs->post_disable(bridge);

drm_bridge_chain_post_disable(bridge->next);
encoder = bridge->encoder;
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->post_disable)
bridge->funcs->post_disable(bridge);
}
}
EXPORT_SYMBOL(drm_bridge_chain_post_disable);

Expand All @@ -300,13 +321,16 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{
struct drm_encoder *encoder;

if (!bridge)
return;

if (bridge->funcs->mode_set)
bridge->funcs->mode_set(bridge, mode, adjusted_mode);

drm_bridge_chain_mode_set(bridge->next, mode, adjusted_mode);
encoder = bridge->encoder;
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->mode_set)
bridge->funcs->mode_set(bridge, mode, adjusted_mode);
}
}
EXPORT_SYMBOL(drm_bridge_chain_mode_set);

Expand All @@ -323,13 +347,17 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set);
*/
void drm_bridge_chain_pre_enable(struct drm_bridge *bridge)
{
struct drm_encoder *encoder;
struct drm_bridge *iter;

if (!bridge)
return;

drm_bridge_chain_pre_enable(bridge->next);

if (bridge->funcs->pre_enable)
bridge->funcs->pre_enable(bridge);
encoder = bridge->encoder;
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->pre_enable)
iter->funcs->pre_enable(iter);
}
}
EXPORT_SYMBOL(drm_bridge_chain_pre_enable);

Expand All @@ -345,13 +373,16 @@ EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
*/
void drm_bridge_chain_enable(struct drm_bridge *bridge)
{
struct drm_encoder *encoder;

if (!bridge)
return;

if (bridge->funcs->enable)
bridge->funcs->enable(bridge);

drm_bridge_chain_enable(bridge->next);
encoder = bridge->encoder;
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->enable)
bridge->funcs->enable(bridge);
}
}
EXPORT_SYMBOL(drm_bridge_chain_enable);

Expand All @@ -370,15 +401,22 @@ EXPORT_SYMBOL(drm_bridge_chain_enable);
void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct drm_encoder *encoder;
struct drm_bridge *iter;

if (!bridge)
return;

drm_atomic_bridge_chain_disable(bridge->next, state);
encoder = bridge->encoder;
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->atomic_disable)
iter->funcs->atomic_disable(iter, state);
else if (iter->funcs->disable)
iter->funcs->disable(iter);

if (bridge->funcs->atomic_disable)
bridge->funcs->atomic_disable(bridge, state);
else if (bridge->funcs->disable)
bridge->funcs->disable(bridge);
if (iter == bridge)
break;
}
}
EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);

Expand All @@ -398,15 +436,18 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct drm_encoder *encoder;

if (!bridge)
return;

if (bridge->funcs->atomic_post_disable)
bridge->funcs->atomic_post_disable(bridge, state);
else if (bridge->funcs->post_disable)
bridge->funcs->post_disable(bridge);

drm_atomic_bridge_chain_post_disable(bridge->next, state);
encoder = bridge->encoder;
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->atomic_post_disable)
bridge->funcs->atomic_post_disable(bridge, state);
else if (bridge->funcs->post_disable)
bridge->funcs->post_disable(bridge);
}
}
EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);

Expand All @@ -426,15 +467,22 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct drm_encoder *encoder;
struct drm_bridge *iter;

if (!bridge)
return;

drm_atomic_bridge_chain_pre_enable(bridge->next, state);
encoder = bridge->encoder;
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->atomic_pre_enable)
iter->funcs->atomic_pre_enable(iter, state);
else if (iter->funcs->pre_enable)
iter->funcs->pre_enable(iter);

if (bridge->funcs->atomic_pre_enable)
bridge->funcs->atomic_pre_enable(bridge, state);
else if (bridge->funcs->pre_enable)
bridge->funcs->pre_enable(bridge);
if (iter == bridge)
break;
}
}
EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);

Expand All @@ -453,15 +501,18 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct drm_encoder *encoder;

if (!bridge)
return;

if (bridge->funcs->atomic_enable)
bridge->funcs->atomic_enable(bridge, state);
else if (bridge->funcs->enable)
bridge->funcs->enable(bridge);

drm_atomic_bridge_chain_enable(bridge->next, state);
encoder = bridge->encoder;
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->atomic_enable)
bridge->funcs->atomic_enable(bridge, state);
else if (bridge->funcs->enable)
bridge->funcs->enable(bridge);
}
}
EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);

Expand Down
16 changes: 5 additions & 11 deletions drivers/gpu/drm/drm_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ int drm_encoder_init(struct drm_device *dev,
goto out_put;
}

INIT_LIST_HEAD(&encoder->bridge_chain);
list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
encoder->index = dev->mode_config.num_encoder++;

Expand All @@ -160,23 +161,16 @@ EXPORT_SYMBOL(drm_encoder_init);
void drm_encoder_cleanup(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_bridge *bridge, *next;

/* Note that the encoder_list is considered to be static; should we
* remove the drm_encoder at runtime we would have to decrement all
* the indices on the drm_encoder after us in the encoder_list.
*/

if (encoder->bridge) {
struct drm_bridge *bridge;
struct drm_bridge *next;

bridge = drm_bridge_chain_get_first_bridge(encoder);
while (bridge) {
next = drm_bridge_get_next_bridge(bridge);
drm_bridge_detach(bridge);
bridge = next;
}
}
list_for_each_entry_safe(bridge, next, &encoder->bridge_chain,
chain_node)
drm_bridge_detach(bridge);

drm_mode_object_unregister(dev, &encoder->base);
kfree(encoder->name);
Expand Down
5 changes: 4 additions & 1 deletion drivers/gpu/drm/exynos/exynos_drm_dsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ struct exynos_dsi {
struct mipi_dsi_host dsi_host;
struct drm_connector connector;
struct drm_panel *panel;
struct list_head bridge_chain;
struct drm_bridge *out_bridge;
struct device *dev;

Expand Down Expand Up @@ -1522,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
if (out_bridge) {
drm_bridge_attach(encoder, out_bridge, NULL);
dsi->out_bridge = out_bridge;
encoder->bridge = NULL;
list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
} else {
int ret = exynos_dsi_create_connector(encoder);

Expand Down Expand Up @@ -1588,6 +1589,7 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
if (dsi->out_bridge->funcs->detach)
dsi->out_bridge->funcs->detach(dsi->out_bridge);
dsi->out_bridge = NULL;
INIT_LIST_HEAD(&dsi->bridge_chain);
}

if (drm->mode_config.poll_enabled)
Expand Down Expand Up @@ -1735,6 +1737,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
init_completion(&dsi->completed);
spin_lock_init(&dsi->transfer_lock);
INIT_LIST_HEAD(&dsi->transfer_list);
INIT_LIST_HEAD(&dsi->bridge_chain);

dsi->dsi_host.ops = &exynos_dsi_ops;
dsi->dsi_host.dev = dev;
Expand Down
Loading

0 comments on commit 05193dc

Please sign in to comment.