summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_bridge.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_bridge.c')
-rw-r--r--drivers/gpu/drm/drm_bridge.c82
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;
}