diff options
Diffstat (limited to 'drivers/gpu/drm/drm_bridge.c')
| -rw-r--r-- | drivers/gpu/drm/drm_bridge.c | 82 |
1 files changed, 61 insertions, 21 deletions
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index d6f11c68bb6a..d6f512b73389 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -304,6 +304,9 @@ EXPORT_SYMBOL(drm_bridge_get); * * This function decrements the bridge's reference count and frees the * object if the reference count drops to zero. + * + * See also drm_bridge_clear_and_put() if you also need to set the pointer + * to NULL */ void drm_bridge_put(struct drm_bridge *bridge) { @@ -313,6 +316,37 @@ void drm_bridge_put(struct drm_bridge *bridge) EXPORT_SYMBOL(drm_bridge_put); /** + * drm_bridge_clear_and_put - Given a bridge pointer, clear the pointer + * then put the bridge + * @bridge_pp: pointer to pointer to a struct drm_bridge; ``bridge_pp`` + * must be non-NULL; if ``*bridge_pp`` is NULL this function + * does nothing + * + * Helper to put a DRM bridge, but only after setting its pointer to + * NULL. Useful when a struct drm_bridge reference must be dropped without + * leaving a use-after-free window where the pointed bridge might have been + * freed while still holding a pointer to it. + * + * For struct ``drm_bridge *some_bridge``, this code:: + * + * drm_bridge_clear_and_put(&some_bridge); + * + * is equivalent to the more complex:: + * + * struct drm_bridge *temp = some_bridge; + * some_bridge = NULL; + * drm_bridge_put(temp); + */ +void drm_bridge_clear_and_put(struct drm_bridge **bridge_pp) +{ + struct drm_bridge *bridge = *bridge_pp; + + *bridge_pp = NULL; + drm_bridge_put(bridge); +} +EXPORT_SYMBOL(drm_bridge_clear_and_put); + +/** * drm_bridge_put_void - wrapper to drm_bridge_put() taking a void pointer * * @data: pointer to @struct drm_bridge, cast to a void pointer @@ -387,7 +421,7 @@ void drm_bridge_add(struct drm_bridge *bridge) if (bridge->ops & DRM_BRIDGE_OP_HDMI) bridge->ycbcr_420_allowed = !!(bridge->supported_formats & - BIT(HDMI_COLORSPACE_YUV420)); + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)); mutex_lock(&bridge_lock); list_add_tail(&bridge->list, &bridge_list); @@ -460,7 +494,21 @@ drm_bridge_atomic_destroy_priv_state(struct drm_private_obj *obj, bridge->funcs->atomic_destroy_state(bridge, state); } +static struct drm_private_state * +drm_bridge_atomic_create_priv_state(struct drm_private_obj *obj) +{ + struct drm_bridge *bridge = drm_priv_to_bridge(obj); + struct drm_bridge_state *state; + + state = bridge->funcs->atomic_reset(bridge); + if (IS_ERR(state)) + return ERR_CAST(state); + + return &state->base; +} + static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = { + .atomic_create_state = drm_bridge_atomic_create_priv_state, .atomic_duplicate_state = drm_bridge_atomic_duplicate_priv_state, .atomic_destroy_state = drm_bridge_atomic_destroy_priv_state, }; @@ -537,26 +585,12 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, goto err_reset_bridge; } - if (drm_bridge_is_atomic(bridge)) { - struct drm_bridge_state *state; - - state = bridge->funcs->atomic_reset(bridge); - if (IS_ERR(state)) { - ret = PTR_ERR(state); - goto err_detach_bridge; - } - + if (drm_bridge_is_atomic(bridge)) drm_atomic_private_obj_init(bridge->dev, &bridge->base, - &state->base, &drm_bridge_priv_state_funcs); - } return 0; -err_detach_bridge: - if (bridge->funcs->detach) - bridge->funcs->detach(bridge); - err_reset_bridge: bridge->dev = NULL; bridge->encoder = NULL; @@ -1569,11 +1603,17 @@ EXPORT_SYMBOL(devm_drm_put_bridge); static void drm_bridge_debugfs_show_bridge(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx, - bool lingering) + bool lingering, + bool scoped) { + unsigned int refcount = kref_read(&bridge->refcount); + + if (scoped) + refcount--; + drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs); - drm_printf(p, "\trefcount: %u%s\n", kref_read(&bridge->refcount), + drm_printf(p, "\trefcount: %u%s\n", refcount, lingering ? " [lingering]" : ""); drm_printf(p, "\ttype: [%d] %s\n", @@ -1607,10 +1647,10 @@ static int allbridges_show(struct seq_file *m, void *data) mutex_lock(&bridge_lock); list_for_each_entry(bridge, &bridge_list, list) - drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false); + drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false, false); list_for_each_entry(bridge, &bridge_lingering_list, list) - drm_bridge_debugfs_show_bridge(&p, bridge, idx++, true); + drm_bridge_debugfs_show_bridge(&p, bridge, idx++, true, false); mutex_unlock(&bridge_lock); @@ -1625,7 +1665,7 @@ static int encoder_bridges_show(struct seq_file *m, void *data) unsigned int idx = 0; drm_for_each_bridge_in_chain_scoped(encoder, bridge) - drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false); + drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false, true); return 0; } |
