[RFCv2 PATCH 1/5] video: add HDMI state notifier support

Hans Verkuil hverkuil at xs4all.nl
Tue Nov 15 12:41:39 PST 2016


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 at cisco.com>
>>
>> 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 at cisco.com>
>> ---
>>  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 at pengutronix.de>
> Tested-by: Philipp Zabel <p.zabel at pengutronix.de> (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



More information about the linux-arm-kernel mailing list