Hi Philipp, On 11/15/2016 07:24 PM, Philipp Zabel wrote: > Hi Hans, > > Am Montag, den 14.11.2016, 16:22 +0100 schrieb Hans Verkuil: >> From: Hans Verkuil <hans.verkuil@xxxxxxxxx> >> >> Add support for HDMI hotplug and EDID notifiers, which is used to convey >> information from HDMI drivers to their CEC and audio counterparts. >> >> 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 <hans.verkuil@xxxxxxxxx> >> --- >> 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; >> +}; > > This struct is not used, can be removed. Indeed. > >> +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); > > + n->dev = dev; > > Currently n->dev is never set, so every caller of this function gets its > own hdmi_notifier. Oops! Well, I did say it was compile-tested only :-) > >> + 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 > > With the above change, > > Reviewed-by: Philipp Zabel <p.zabel@xxxxxxxxxxxxxx> > Tested-by: Philipp Zabel <p.zabel@xxxxxxxxxxxxxx> (on MT8173) > > I'll send the patches for mediatek-drm and hdmi-codec that I used for > testing in a bit. Thanks for testing this so quickly! Much appreciated! Regards, Hans -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html