@@ -1,2 +1,3 @@
-display-y := display-core.o
+display-y := display-core.o \
+ display-notifier.o
obj-$(CONFIG_DISPLAY_CORE) += display.o
new file mode 100644
@@ -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, ¬ifier->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, ¬ifier->done);
+
+ if (list_empty(¬ifier->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, ¬ifier->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(¬ifier->waiting);
+ INIT_LIST_HEAD(¬ifier->done);
+
+ for (i = 0; i < notifier->num_entities; i++) {
+ match = ¬ifier->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, ¬ifier->waiting);
+ }
+
+ mutex_lock(&display_entity_mutex);
+
+ list_add_tail(¬ifier->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(¬ifier->list);
+
+ list_for_each_entry_safe(entity, next, ¬ifier->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, ¬ifier->waiting);
+
+ mutex_unlock(&display_entity_mutex);
+}
+EXPORT_SYMBOL_GPL(display_entity_remove);
@@ -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__ */
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