Thread (26 messages) 26 messages, 4 authors, 2013-09-11

[PATCH/RFC v3 04/19] video: display: Add display entity notifier

flat view
STALE4778d

From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Date: 2013-08-09 23:02:25
Also in: dri-devel, linux-media
Subsystem: framebuffer layer, the rest · Maintainers: Helge Deller, Linus Torvalds

Revision v3 of 2 in this series.

Revisions (2)
  1. v3
  2. v3 current
Display entities are initialized by they respective drivers
asynchronously with the master display driver. The notifier
infrastructure allows display drivers to create a list of entities they
need (based on platform data) and be notified when those entities are
added to or removed from the system.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/video/display/Makefile           |   3 +-
 drivers/video/display/display-notifier.c | 304 +++++++++++++++++++++++++++++++
 include/video/display.h                  |  66 +++++++
 3 files changed, 372 insertions(+), 1 deletion(-)
 create mode 100644 drivers/video/display/display-notifier.c
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 3054adc..b907aad 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -1,2 +1,3 @@
-display-y					:= display-core.o
+display-y					:= display-core.o \
+						   display-notifier.o
 obj-$(CONFIG_DISPLAY_CORE)			+= display.o
diff --git a/drivers/video/display/display-notifier.c b/drivers/video/display/display-notifier.c
new file mode 100644
index 0000000..c9210ec
--- /dev/null
+++ b/drivers/video/display/display-notifier.c
@@ -0,0 +1,304 @@
+/*
+ * Display Notifier
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+
+#include <video/display.h>
+
+static LIST_HEAD(display_entity_list);
+static LIST_HEAD(display_entity_notifiers);
+static DEFINE_MUTEX(display_entity_mutex);
+
+/* -----------------------------------------------------------------------------
+ * Notifiers
+ */
+
+static bool match_platform(struct device *dev,
+			   struct display_entity_match *match)
+{
+	pr_debug("%s: matching device '%s' with name '%s'\n", __func__,
+		 dev_name(dev), match->match.platform.name);
+
+	return !strcmp(match->match.platform.name, dev_name(dev));
+}
+
+static struct display_entity_match *
+display_entity_notifier_match(struct display_entity_notifier *notifier,
+			      struct display_entity *entity)
+{
+	bool (*match_func)(struct device *, struct display_entity_match *);
+	struct display_entity_match *match;
+
+	pr_debug("%s: matching entity '%s' (ptr 0x%p dev '%s')\n", __func__,
+		 entity->name, entity, dev_name(entity->dev));
+
+	list_for_each_entry(match, &notifier->waiting, list) {
+		switch (match->type) {
+		default:
+		case DISPLAY_ENTITY_BUS_PLATFORM:
+			match_func = match_platform;
+			break;
+		}
+
+		if (match_func(entity->dev, match))
+			return match;
+	}
+
+	return NULL;
+}
+
+static void display_entity_notifier_cleanup(struct display_entity *entity)
+{
+	entity->match = NULL;
+	entity->notifier = NULL;
+}
+
+static int
+display_entity_notifier_notify(struct display_entity_notifier *notifier,
+			       struct display_entity *entity,
+			       struct display_entity_match *match)
+{
+	int ret;
+
+	pr_debug("%s: notifying device '%s' for entity '%s' (ptr 0x%p dev '%s')\n",
+		 __func__, dev_name(notifier->dev), entity->name, entity,
+		 dev_name(entity->dev));
+
+	/* Remove the match from waiting list. */
+	list_del(&match->list);
+	entity->match = match;
+	entity->notifier = notifier;
+
+	if (notifier->bound) {
+		ret = notifier->bound(notifier, entity, match);
+		if (ret < 0)
+			goto error_bound;
+	}
+
+	/* Move the entity from the global list to the notifier's done list. */
+	list_move(&entity->list, &notifier->done);
+
+	if (list_empty(&notifier->waiting) && notifier->complete) {
+		pr_debug("%s: notifying device '%s' of completion\n", __func__,
+			 dev_name(notifier->dev));
+		ret = notifier->complete(notifier);
+		if (ret < 0)
+			goto error_complete;
+	}
+
+	return 0;
+
+error_complete:
+	/* Move the entity back to the global list. */
+	list_move(&entity->list, &display_entity_list);
+	if (notifier->unbind)
+		notifier->unbind(notifier, entity, match);
+error_bound:
+	/* Put the match back to the waiting list. */
+	list_add_tail(&match->list, &notifier->waiting);
+	display_entity_notifier_cleanup(entity);
+
+	return ret;
+}
+
+/**
+ * display_entity_register_notifier - register a display entity notifier
+ * @notifier: display entity notifier structure we want to register
+ *
+ * Display entity notifiers are called to notify drivers of display
+ * entity-related events for matching display_entitys.
+ *
+ * Notifiers and display_entitys are matched through the device they correspond
+ * to. If the notifier dev field is equal to the display entity dev field the
+ * notifier will be called when an event is reported. Notifiers with a NULL dev
+ * field act as catch-all and will be called for all display_entitys.
+ *
+ * Supported events are
+ *
+ * - DISPLAY_ENTITY_NOTIFIER_CONNECT reports display entity connection and is
+ *   sent at display entity or notifier registration time
+ * - DISPLAY_ENTITY_NOTIFIER_DISCONNECT reports display entity disconnection and
+ *   is sent at display entity unregistration time
+ *
+ * Registering a notifier sends DISPLAY_ENTITY_NOTIFIER_CONNECT events for all
+ * previously registered display_entitys that match the notifiers.
+ *
+ * Return 0 on success.
+ */
+int display_entity_register_notifier(struct display_entity_notifier *notifier)
+{
+	struct display_entity_match *match;
+	struct display_entity *entity;
+	struct display_entity *next;
+	unsigned int i;
+	int ret = 0;
+
+	if (notifier->num_entities = 0)
+		return -EINVAL;
+
+	INIT_LIST_HEAD(&notifier->waiting);
+	INIT_LIST_HEAD(&notifier->done);
+
+	for (i = 0; i < notifier->num_entities; i++) {
+		match = &notifier->entities[i];
+
+		switch (match->type) {
+		case DISPLAY_ENTITY_BUS_PLATFORM:
+			break;
+		default:
+			dev_err(notifier->dev,
+				"%s: Invalid bus type %u on %p\n", __func__,
+				match->type, match);
+			return -EINVAL;
+		}
+
+		list_add_tail(&match->list, &notifier->waiting);
+	}
+
+	mutex_lock(&display_entity_mutex);
+
+	list_add_tail(&notifier->list, &display_entity_notifiers);
+
+	list_for_each_entry_safe(entity, next, &display_entity_list, list) {
+		match = display_entity_notifier_match(notifier, entity);
+		if (!match)
+			continue;
+
+		ret = display_entity_notifier_notify(notifier, entity, match);
+		if (ret)
+			break;
+	}
+
+	mutex_unlock(&display_entity_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(display_entity_register_notifier);
+
+/**
+ * display_entity_unregister_notifier - unregister a display entity notifier
+ * @notifier: display entity notifier to be unregistered
+ *
+ * Unregistration guarantees that the notifier will never be called upon return
+ * of this function.
+ */
+void display_entity_unregister_notifier(struct display_entity_notifier *notifier)
+{
+	struct display_entity *entity;
+	struct display_entity *next;
+
+	if (notifier->num_entities = 0)
+		return;
+
+	mutex_lock(&display_entity_mutex);
+
+	list_del(&notifier->list);
+
+	list_for_each_entry_safe(entity, next, &notifier->done, list) {
+		if (notifier->unbind)
+			notifier->unbind(notifier, entity, entity->match);
+
+		/* Move the entity back to the global list. */
+		display_entity_notifier_cleanup(entity);
+		list_move(&entity->list, &display_entity_list);
+	}
+	mutex_unlock(&display_entity_mutex);
+}
+EXPORT_SYMBOL_GPL(display_entity_unregister_notifier);
+
+/* -----------------------------------------------------------------------------
+ * Entity Registration
+ */
+
+/**
+ * display_entity_add - add a display entity to the list of available entities
+ * @entity: display entity to be added
+ *
+ * Add the display entity to the list of available entities and send the
+ * DISPLAY_ENTITY_NOTIFIER_CONNECT event to all matching registered notifiers.
+ *
+ * Return 0 on success.
+ */
+int display_entity_add(struct display_entity *entity)
+{
+	struct display_entity_notifier *notifier;
+	struct display_entity_match *match = NULL;
+
+	pr_debug("%s: adding entity '%s' (ptr 0x%p dev '%s')\n", __func__,
+		 entity->name, entity, dev_name(entity->dev));
+
+	mutex_lock(&display_entity_mutex);
+
+	/* Add the entity to the global unbound entities list. It will later be
+	 * moved to the notifier done list by display_entity_notifier_notify().
+	 */
+	list_add_tail(&entity->list, &display_entity_list);
+
+	list_for_each_entry(notifier, &display_entity_notifiers, list) {
+		match = display_entity_notifier_match(notifier, entity);
+		if (match)
+			break;
+	}
+
+	if (match) {
+		/* A match has been found, notify the notifier. */
+		display_entity_notifier_notify(notifier, entity, match);
+	}
+
+	mutex_unlock(&display_entity_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_add);
+
+/**
+ * display_entity_remove - removea display entity
+ * @entity: display entity to be removed
+ *
+ * Remove the display entity from the list of available entities and send the
+ * DISPLAY_ENTITY_NOTIFIER_DISCONNECT event to all matching registered
+ * notifiers.
+ */
+void display_entity_remove(struct display_entity *entity)
+{
+	struct display_entity_notifier *notifier = entity->notifier;
+	struct display_entity_match *match = entity->match;
+
+	pr_debug("%s: removing entity '%s' (ptr 0x%p dev '%s')\n", __func__,
+		 entity->name, entity, dev_name(entity->dev));
+
+	if (!notifier) {
+		/* Remove the entity from the global list. */
+		list_del(&entity->list);
+		return;
+	}
+
+	mutex_lock(&display_entity_mutex);
+
+	if (notifier->unbind)
+		notifier->unbind(notifier, entity, match);
+
+	/* Remove the entity from the notifier's done list. */
+	display_entity_notifier_cleanup(entity);
+	list_del(&entity->list);
+
+	/* Move the match back to the waiting list. */
+	list_add_tail(&match->list, &notifier->waiting);
+
+	mutex_unlock(&display_entity_mutex);
+}
+EXPORT_SYMBOL_GPL(display_entity_remove);
diff --git a/include/video/display.h b/include/video/display.h
index fef05a68..2063694 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -23,6 +23,8 @@
  */
 
 struct display_entity;
+struct display_entity_match;
+struct display_entity_notify;
 struct videomode;
 
 /**
@@ -101,6 +103,9 @@ struct display_entity {
 	char name[32];
 	struct media_entity entity;
 
+	struct display_entity_match *match;
+	struct display_entity_notifier *notifier;
+
 	const struct display_entity_ops *ops;
 
 	void(*release)(struct display_entity *ent);
@@ -140,4 +145,65 @@ int display_entity_get_params(struct display_entity *entity, unsigned int port,
 int display_entity_set_stream(struct display_entity *entity, unsigned int port,
 			      enum display_entity_stream_state state);
 
+/* -----------------------------------------------------------------------------
+ * Notifier
+ */
+
+enum display_entity_bus_type {
+	DISPLAY_ENTITY_BUS_PLATFORM,
+};
+
+/**
+ * struct display_entity_match - Display entity description
+ * @type: display entity bus type
+ * @match.platform.name: platform device name
+ * @match.dt.node: DT node
+ * @list: link match objects waiting to be matched
+ */
+struct display_entity_match {
+	enum display_entity_bus_type type;
+	union {
+		struct {
+			const char *name;
+		} platform;
+	} match;
+
+	struct list_head list;
+};
+
+/**
+ * display_entity_notifier - display entity notifier
+ * @num_entities: number of display entities
+ * @entities: array of pointers to subdevice descriptors
+ * @waiting: list of struct v4l2_async_subdev, waiting for their drivers
+ * @done: list of struct v4l2_async_subdev_list, already probed
+ * @list: member in a global list of notifiers
+ * @bound: a display entity has been registered
+ * @complete: all display entities have been registered
+ * @unbind: a display entity is being unregistered
+ */
+struct display_entity_notifier {
+	struct device *dev;
+
+	unsigned int num_entities;
+	struct display_entity_match *entities;
+	struct list_head waiting;
+	struct list_head done;
+	struct list_head list;
+
+	int (*bound)(struct display_entity_notifier *notifier,
+		     struct display_entity *entity,
+		     struct display_entity_match *match);
+	int (*complete)(struct display_entity_notifier *notifier);
+	void (*unbind)(struct display_entity_notifier *notifier,
+		       struct display_entity *entity,
+		       struct display_entity_match *match);
+};
+
+int display_entity_register_notifier(struct display_entity_notifier *notifier);
+void display_entity_unregister_notifier(struct display_entity_notifier *notifier);
+
+int display_entity_add(struct display_entity *entity);
+void display_entity_remove(struct display_entity *entity);
+
 #endif /* __DISPLAY_H__ */
-- 
1.8.1.5
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help