On 11/14/16 16:22, Hans Verkuil wrote:
From: Hans Verkuil <[email protected]>
Add support for HDMI hotplug and EDID notifiers, which is used to convey
information from HDMI drivers to their CEC and audio counterparts.
I realized that the name 'HDMI notifier' isn't the best: the same mechanism
can be used with e.g. DisplayPort as well.
What would be a good alternative name?
"Video Notifier"?
Any objections to that? Or suggestions for a better name?
Regards,
Hans
Based on an earlier version from Russell King:
https://patchwork.kernel.org/patch/9277043/
The hdmi_notifier is a reference counted object containing the HDMI state
of an HDMI device.
When a new notifier is registered the current state will be reported to
that notifier at registration time.
Signed-off-by: Hans Verkuil <[email protected]>
---
drivers/video/Kconfig | 3 +
drivers/video/Makefile | 1 +
drivers/video/hdmi-notifier.c | 136 ++++++++++++++++++++++++++++++++++++++++++
include/linux/hdmi-notifier.h | 43 +++++++++++++
4 files changed, 183 insertions(+)
create mode 100644 drivers/video/hdmi-notifier.c
create mode 100644 include/linux/hdmi-notifier.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3c20af9..1ee7b9f 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -36,6 +36,9 @@ config VIDEOMODE_HELPERS
config HDMI
bool
+config HDMI_NOTIFIERS
+ bool
+
if VT
source "drivers/video/console/Kconfig"
endif
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 9ad3c17..65f5649 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_VGASTATE) += vgastate.o
obj-$(CONFIG_HDMI) += hdmi.o
+obj-$(CONFIG_HDMI_NOTIFIERS) += hdmi-notifier.o
obj-$(CONFIG_VT) += console/
obj-$(CONFIG_LOGO) += logo/
diff --git a/drivers/video/hdmi-notifier.c b/drivers/video/hdmi-notifier.c
new file mode 100644
index 0000000..c2a4f1b
--- /dev/null
+++ b/drivers/video/hdmi-notifier.c
@@ -0,0 +1,136 @@
+#include <linux/export.h>
+#include <linux/hdmi-notifier.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+struct hdmi_notifiers {
+ struct list_head head;
+ struct device *dev;
+ struct hdmi_notifier *n;
+};
+
+static LIST_HEAD(hdmi_notifiers);
+static DEFINE_MUTEX(hdmi_notifiers_lock);
+
+struct hdmi_notifier *hdmi_notifier_get(struct device *dev)
+{
+ struct hdmi_notifier *n;
+
+ mutex_lock(&hdmi_notifiers_lock);
+ list_for_each_entry(n, &hdmi_notifiers, head) {
+ if (n->dev == dev) {
+ mutex_unlock(&hdmi_notifiers_lock);
+ kref_get(&n->kref);
+ return n;
+ }
+ }
+ n = kzalloc(sizeof(*n), GFP_KERNEL);
+ if (!n)
+ goto unlock;
+ mutex_init(&n->lock);
+ BLOCKING_INIT_NOTIFIER_HEAD(&n->notifiers);
+ kref_init(&n->kref);
+ list_add_tail(&n->head, &hdmi_notifiers);
+unlock:
+ mutex_unlock(&hdmi_notifiers_lock);
+ return n;
+}
+EXPORT_SYMBOL_GPL(hdmi_notifier_get);
+
+static void hdmi_notifier_release(struct kref *kref)
+{
+ struct hdmi_notifier *n =
+ container_of(kref, struct hdmi_notifier, kref);
+
+ kfree(n->edid);
+ kfree(n);
+}
+
+void hdmi_notifier_put(struct hdmi_notifier *n)
+{
+ kref_put(&n->kref, hdmi_notifier_release);
+}
+EXPORT_SYMBOL_GPL(hdmi_notifier_put);
+
+int hdmi_notifier_register(struct hdmi_notifier *n, struct notifier_block *nb)
+{
+ int ret = blocking_notifier_chain_register(&n->notifiers, nb);
+
+ if (ret)
+ return ret;
+ kref_get(&n->kref);
+ mutex_lock(&n->lock);
+ if (n->connected) {
+ blocking_notifier_call_chain(&n->notifiers, HDMI_CONNECTED, n);
+ if (n->edid_size)
+ blocking_notifier_call_chain(&n->notifiers,
HDMI_NEW_EDID, n);
+ if (n->has_eld)
+ blocking_notifier_call_chain(&n->notifiers,
HDMI_NEW_ELD, n);
+ }
+ mutex_unlock(&n->lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hdmi_notifier_register);
+
+int hdmi_notifier_unregister(struct hdmi_notifier *n, struct notifier_block
*nb)
+{
+ int ret = blocking_notifier_chain_unregister(&n->notifiers, nb);
+
+ if (ret == 0)
+ hdmi_notifier_put(n);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(hdmi_notifier_unregister);
+
+void hdmi_event_connect(struct hdmi_notifier *n)
+{
+ mutex_lock(&n->lock);
+ n->connected = true;
+ blocking_notifier_call_chain(&n->notifiers, HDMI_CONNECTED, n);
+ mutex_unlock(&n->lock);
+}
+EXPORT_SYMBOL_GPL(hdmi_event_connect);
+
+void hdmi_event_disconnect(struct hdmi_notifier *n)
+{
+ mutex_lock(&n->lock);
+ n->connected = false;
+ n->has_eld = false;
+ n->edid_size = 0;
+ blocking_notifier_call_chain(&n->notifiers, HDMI_DISCONNECTED, n);
+ mutex_unlock(&n->lock);
+}
+EXPORT_SYMBOL_GPL(hdmi_event_disconnect);
+
+int hdmi_event_new_edid(struct hdmi_notifier *n, const void *edid, size_t size)
+{
+ mutex_lock(&n->lock);
+ if (n->edid_allocated_size < size) {
+ void *p = kmalloc(size, GFP_KERNEL);
+
+ if (p == NULL) {
+ mutex_unlock(&n->lock);
+ return -ENOMEM;
+ }
+ kfree(n->edid);
+ n->edid = p;
+ n->edid_allocated_size = size;
+ }
+ memcpy(n->edid, edid, size);
+ n->edid_size = size;
+ blocking_notifier_call_chain(&n->notifiers, HDMI_NEW_EDID, n);
+ mutex_unlock(&n->lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hdmi_event_new_edid);
+
+void hdmi_event_new_eld(struct hdmi_notifier *n, const u8 eld[128])
+{
+ mutex_lock(&n->lock);
+ memcpy(n->eld, eld, sizeof(n->eld));
+ n->has_eld = true;
+ blocking_notifier_call_chain(&n->notifiers, HDMI_NEW_ELD, n);
+ mutex_unlock(&n->lock);
+}
+EXPORT_SYMBOL_GPL(hdmi_event_new_eld);
diff --git a/include/linux/hdmi-notifier.h b/include/linux/hdmi-notifier.h
new file mode 100644
index 0000000..f7fc405
--- /dev/null
+++ b/include/linux/hdmi-notifier.h
@@ -0,0 +1,43 @@
+#ifndef LINUX_HDMI_NOTIFIER_H
+#define LINUX_HDMI_NOTIFIER_H
+
+#include <linux/types.h>
+#include <linux/notifier.h>
+#include <linux/kref.h>
+
+enum {
+ HDMI_CONNECTED,
+ HDMI_DISCONNECTED,
+ HDMI_NEW_EDID,
+ HDMI_NEW_ELD,
+};
+
+struct device;
+
+struct hdmi_notifier {
+ struct mutex lock;
+ struct list_head head;
+ struct kref kref;
+ struct blocking_notifier_head notifiers;
+ struct device *dev;
+
+ /* Current state */
+ bool connected;
+ bool has_eld;
+ unsigned char eld[128];
+ void *edid;
+ size_t edid_size;
+ size_t edid_allocated_size;
+};
+
+struct hdmi_notifier *hdmi_notifier_get(struct device *dev);
+void hdmi_notifier_put(struct hdmi_notifier *n);
+int hdmi_notifier_register(struct hdmi_notifier *n, struct notifier_block *nb);
+int hdmi_notifier_unregister(struct hdmi_notifier *n, struct notifier_block
*nb);
+
+void hdmi_event_connect(struct hdmi_notifier *n);
+void hdmi_event_disconnect(struct hdmi_notifier *n);
+int hdmi_event_new_edid(struct hdmi_notifier *n, const void *edid, size_t
size);
+void hdmi_event_new_eld(struct hdmi_notifier *n, const u8 eld[128]);
+
+#endif
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html