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.c246
1 files changed, 186 insertions, 60 deletions
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 241a384ebce3..b4c89ec01998 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -21,6 +21,7 @@
* DEALINGS IN THE SOFTWARE.
*/
+#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/media-bus-format.h>
#include <linux/module.h>
@@ -198,10 +199,96 @@
static DEFINE_MUTEX(bridge_lock);
static LIST_HEAD(bridge_list);
+static void __drm_bridge_free(struct kref *kref)
+{
+ struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount);
+
+ kfree(bridge->container);
+}
+
+/**
+ * drm_bridge_get - Acquire a bridge reference
+ * @bridge: DRM bridge
+ *
+ * This function increments the bridge's refcount.
+ *
+ * Returns:
+ * Pointer to @bridge.
+ */
+struct drm_bridge *drm_bridge_get(struct drm_bridge *bridge)
+{
+ if (bridge)
+ kref_get(&bridge->refcount);
+
+ return bridge;
+}
+EXPORT_SYMBOL(drm_bridge_get);
+
+/**
+ * drm_bridge_put - Release a bridge reference
+ * @bridge: DRM bridge
+ *
+ * This function decrements the bridge's reference count and frees the
+ * object if the reference count drops to zero.
+ */
+void drm_bridge_put(struct drm_bridge *bridge)
+{
+ if (bridge)
+ kref_put(&bridge->refcount, __drm_bridge_free);
+}
+EXPORT_SYMBOL(drm_bridge_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
+ *
+ * Wrapper of drm_bridge_put() to be used when a function taking a void
+ * pointer is needed, for example as a devm action.
+ */
+static void drm_bridge_put_void(void *data)
+{
+ struct drm_bridge *bridge = (struct drm_bridge *)data;
+
+ drm_bridge_put(bridge);
+}
+
+void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
+ const struct drm_bridge_funcs *funcs)
+{
+ void *container;
+ struct drm_bridge *bridge;
+ int err;
+
+ if (!funcs) {
+ dev_warn(dev, "Missing funcs pointer\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ container = kzalloc(size, GFP_KERNEL);
+ if (!container)
+ return ERR_PTR(-ENOMEM);
+
+ bridge = container + offset;
+ bridge->container = container;
+ bridge->funcs = funcs;
+ kref_init(&bridge->refcount);
+
+ err = devm_add_action_or_reset(dev, drm_bridge_put_void, bridge);
+ if (err)
+ return ERR_PTR(err);
+
+ return container;
+}
+EXPORT_SYMBOL(__devm_drm_bridge_alloc);
+
/**
* drm_bridge_add - add the given bridge to the global bridge list
*
* @bridge: bridge control structure
+ *
+ * The bridge to be added must have been allocated by
+ * devm_drm_bridge_alloc().
*/
void drm_bridge_add(struct drm_bridge *bridge)
{
@@ -280,6 +367,11 @@ static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = {
.atomic_destroy_state = drm_bridge_atomic_destroy_priv_state,
};
+static bool drm_bridge_is_atomic(struct drm_bridge *bridge)
+{
+ return bridge->funcs->atomic_reset != NULL;
+}
+
/**
* drm_bridge_attach - attach the bridge to an encoder's chain
*
@@ -327,12 +419,12 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
list_add(&bridge->chain_node, &encoder->bridge_chain);
if (bridge->funcs->attach) {
- ret = bridge->funcs->attach(bridge, flags);
+ ret = bridge->funcs->attach(bridge, encoder, flags);
if (ret < 0)
goto err_reset_bridge;
}
- if (bridge->funcs->atomic_reset) {
+ if (drm_bridge_is_atomic(bridge)) {
struct drm_bridge_state *state;
state = bridge->funcs->atomic_reset(bridge);
@@ -377,7 +469,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)
if (WARN_ON(!bridge->dev))
return;
- if (bridge->funcs->atomic_reset)
+ if (drm_bridge_is_atomic(bridge))
drm_atomic_private_obj_fini(&bridge->base);
if (bridge->funcs->detach)
@@ -550,7 +642,7 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set);
/**
* drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain
* @bridge: bridge control structure
- * @old_state: old atomic state
+ * @state: atomic state being committed
*
* Calls &drm_bridge_funcs.atomic_disable (falls back on
* &drm_bridge_funcs.disable) op for all the bridges in the encoder chain,
@@ -560,7 +652,7 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set);
* Note: the bridge passed should be the one closest to the encoder
*/
void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
- struct drm_atomic_state *old_state)
+ struct drm_atomic_state *state)
{
struct drm_encoder *encoder;
struct drm_bridge *iter;
@@ -571,15 +663,7 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
encoder = bridge->encoder;
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->atomic_disable) {
- struct drm_bridge_state *old_bridge_state;
-
- old_bridge_state =
- drm_atomic_get_old_bridge_state(old_state,
- iter);
- if (WARN_ON(!old_bridge_state))
- return;
-
- iter->funcs->atomic_disable(iter, old_bridge_state);
+ iter->funcs->atomic_disable(iter, state);
} else if (iter->funcs->disable) {
iter->funcs->disable(iter);
}
@@ -591,29 +675,19 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
static void drm_atomic_bridge_call_post_disable(struct drm_bridge *bridge,
- struct drm_atomic_state *old_state)
+ struct drm_atomic_state *state)
{
- if (old_state && bridge->funcs->atomic_post_disable) {
- struct drm_bridge_state *old_bridge_state;
-
- old_bridge_state =
- drm_atomic_get_old_bridge_state(old_state,
- bridge);
- if (WARN_ON(!old_bridge_state))
- return;
-
- bridge->funcs->atomic_post_disable(bridge,
- old_bridge_state);
- } else if (bridge->funcs->post_disable) {
+ if (state && 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 - cleans up after disabling all bridges
* in the encoder chain
* @bridge: bridge control structure
- * @old_state: old atomic state
+ * @state: atomic state being committed
*
* Calls &drm_bridge_funcs.atomic_post_disable (falls back on
* &drm_bridge_funcs.post_disable) op for all the bridges in the encoder chain,
@@ -634,7 +708,7 @@ static void drm_atomic_bridge_call_post_disable(struct drm_bridge *bridge,
* Note: the bridge passed should be the one closest to the encoder
*/
void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
- struct drm_atomic_state *old_state)
+ struct drm_atomic_state *state)
{
struct drm_encoder *encoder;
struct drm_bridge *next, *limit;
@@ -681,12 +755,12 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
break;
drm_atomic_bridge_call_post_disable(next,
- old_state);
+ state);
}
}
}
- drm_atomic_bridge_call_post_disable(bridge, old_state);
+ drm_atomic_bridge_call_post_disable(bridge, state);
if (limit)
/* Jump all bridges that we have already post_disabled */
@@ -696,28 +770,19 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
static void drm_atomic_bridge_call_pre_enable(struct drm_bridge *bridge,
- struct drm_atomic_state *old_state)
+ struct drm_atomic_state *state)
{
- if (old_state && bridge->funcs->atomic_pre_enable) {
- struct drm_bridge_state *old_bridge_state;
-
- old_bridge_state =
- drm_atomic_get_old_bridge_state(old_state,
- bridge);
- if (WARN_ON(!old_bridge_state))
- return;
-
- bridge->funcs->atomic_pre_enable(bridge, old_bridge_state);
- } else if (bridge->funcs->pre_enable) {
+ if (state && bridge->funcs->atomic_pre_enable)
+ bridge->funcs->atomic_pre_enable(bridge, state);
+ else if (bridge->funcs->pre_enable)
bridge->funcs->pre_enable(bridge);
- }
}
/**
* drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in
* the encoder chain
* @bridge: bridge control structure
- * @old_state: old atomic state
+ * @state: atomic state being committed
*
* Calls &drm_bridge_funcs.atomic_pre_enable (falls back on
* &drm_bridge_funcs.pre_enable) op for all the bridges in the encoder chain,
@@ -737,7 +802,7 @@ static void drm_atomic_bridge_call_pre_enable(struct drm_bridge *bridge,
* Note: the bridge passed should be the one closest to the encoder
*/
void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
- struct drm_atomic_state *old_state)
+ struct drm_atomic_state *state)
{
struct drm_encoder *encoder;
struct drm_bridge *iter, *next, *limit;
@@ -776,11 +841,11 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
*/
break;
- drm_atomic_bridge_call_pre_enable(next, old_state);
+ drm_atomic_bridge_call_pre_enable(next, state);
}
}
- drm_atomic_bridge_call_pre_enable(iter, old_state);
+ drm_atomic_bridge_call_pre_enable(iter, state);
if (iter->pre_enable_prev_first)
/* Jump all bridges that we have already pre_enabled */
@@ -795,7 +860,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
/**
* drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain
* @bridge: bridge control structure
- * @old_state: old atomic state
+ * @state: atomic state being committed
*
* Calls &drm_bridge_funcs.atomic_enable (falls back on
* &drm_bridge_funcs.enable) op for all the bridges in the encoder chain,
@@ -805,7 +870,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
* Note: the bridge passed should be the one closest to the encoder
*/
void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
- struct drm_atomic_state *old_state)
+ struct drm_atomic_state *state)
{
struct drm_encoder *encoder;
@@ -815,15 +880,7 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
encoder = bridge->encoder;
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->atomic_enable) {
- struct drm_bridge_state *old_bridge_state;
-
- old_bridge_state =
- drm_atomic_get_old_bridge_state(old_state,
- bridge);
- if (WARN_ON(!old_bridge_state))
- return;
-
- bridge->funcs->atomic_enable(bridge, old_bridge_state);
+ bridge->funcs->atomic_enable(bridge, state);
} else if (bridge->funcs->enable) {
bridge->funcs->enable(bridge);
}
@@ -1335,6 +1392,75 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np)
EXPORT_SYMBOL(of_drm_find_bridge);
#endif
+static void drm_bridge_debugfs_show_bridge(struct drm_printer *p,
+ struct drm_bridge *bridge,
+ unsigned int idx)
+{
+ drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs);
+ drm_printf(p, "\ttype: [%d] %s\n",
+ bridge->type,
+ drm_get_connector_type_name(bridge->type));
+
+ if (bridge->of_node)
+ drm_printf(p, "\tOF: %pOFfc\n", bridge->of_node);
+
+ drm_printf(p, "\tops: [0x%x]", bridge->ops);
+ if (bridge->ops & DRM_BRIDGE_OP_DETECT)
+ drm_puts(p, " detect");
+ if (bridge->ops & DRM_BRIDGE_OP_EDID)
+ drm_puts(p, " edid");
+ if (bridge->ops & DRM_BRIDGE_OP_HPD)
+ drm_puts(p, " hpd");
+ if (bridge->ops & DRM_BRIDGE_OP_MODES)
+ drm_puts(p, " modes");
+ if (bridge->ops & DRM_BRIDGE_OP_HDMI)
+ drm_puts(p, " hdmi");
+ drm_puts(p, "\n");
+}
+
+static int allbridges_show(struct seq_file *m, void *data)
+{
+ struct drm_printer p = drm_seq_file_printer(m);
+ struct drm_bridge *bridge;
+ unsigned int idx = 0;
+
+ mutex_lock(&bridge_lock);
+
+ list_for_each_entry(bridge, &bridge_list, list)
+ drm_bridge_debugfs_show_bridge(&p, bridge, idx++);
+
+ mutex_unlock(&bridge_lock);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(allbridges);
+
+static int encoder_bridges_show(struct seq_file *m, void *data)
+{
+ struct drm_encoder *encoder = m->private;
+ struct drm_printer p = drm_seq_file_printer(m);
+ struct drm_bridge *bridge;
+ unsigned int idx = 0;
+
+ drm_for_each_bridge_in_chain(encoder, bridge)
+ drm_bridge_debugfs_show_bridge(&p, bridge, idx++);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(encoder_bridges);
+
+void drm_bridge_debugfs_params(struct dentry *root)
+{
+ debugfs_create_file("bridges", 0444, root, NULL, &allbridges_fops);
+}
+
+void drm_bridge_debugfs_encoder_params(struct dentry *root,
+ struct drm_encoder *encoder)
+{
+ /* bridges list */
+ debugfs_create_file("bridges", 0444, root, encoder, &encoder_bridges_fops);
+}
+
MODULE_AUTHOR("Ajay Kumar <ajaykumar.rs@samsung.com>");
MODULE_DESCRIPTION("DRM bridge infrastructure");
MODULE_LICENSE("GPL and additional rights");