All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFCv2 PATCH 0/5] CEC drivers for iMX6 and TDA9950
@ 2016-11-14 15:22 ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-media
  Cc: Russell King - ARM Linux, linux-fbdev, dri-devel, linux-arm-kernel

From: Hans Verkuil <hans.verkuil@cisco.com>

This patch series is an update to this RFC series from Russell:

https://lists.freedesktop.org/archives/dri-devel/2016-August/115733.html

I have not seen any updates to this, so I hope that that series is still
the latest version.

The main problem with that original series was that the notifier didn't
store the state, so if a CEC driver registered with the notifier, then
it wouldn't be informed of the current state.

The hdmi-notifier code has been changed to a per-HDMI-device and refcounted
block_notifier that stores the state and will report the current state
upon registration.

The other four patches have been adapted to the new notifier code, but
no other changes were made.

It has *only* been compile-tested. I might be able to verify it next week
with an actual i.MX6 device, but it will take time to set that up.

If someone has a ready-to-test setup, then I would very much appreciate
it if this series can be tested.

The patches are also available in my branch:

https://git.linuxtv.org/hverkuil/media_tree.git/log/?h=cec-notifiers

It is on top of my patch series that moves CEC out of staging. This
is planned for 4.10.

Regards,

	Hans

Hans Verkuil (1):
  video: add HDMI state notifier support

Russell King (4):
  drm/bridge: dw_hdmi: remove CEC engine register definitions
  drm/bridge: dw_hdmi: add HDMI notifier support
  drm/bridge: add dw-hdmi cec driver using Hans Verkuil's CEC code
  drm/i2c: add tda998x/tda9950 CEC driver

 drivers/gpu/drm/bridge/Kconfig            |   8 +
 drivers/gpu/drm/bridge/Makefile           |   1 +
 drivers/gpu/drm/bridge/dw-hdmi-cec.c      | 346 ++++++++++++++++++++
 drivers/gpu/drm/bridge/dw-hdmi.c          |  89 +++++-
 drivers/gpu/drm/bridge/dw-hdmi.h          |  45 ---
 drivers/gpu/drm/i2c/Kconfig               |   5 +
 drivers/gpu/drm/i2c/Makefile              |   1 +
 drivers/gpu/drm/i2c/tda9950.c             | 516 ++++++++++++++++++++++++++++++
 drivers/video/Kconfig                     |   3 +
 drivers/video/Makefile                    |   1 +
 drivers/video/hdmi-notifier.c             | 136 ++++++++
 include/linux/hdmi-notifier.h             |  43 +++
 include/linux/platform_data/dw_hdmi-cec.h |  16 +
 include/linux/platform_data/tda9950.h     |  15 +
 14 files changed, 1168 insertions(+), 57 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/dw-hdmi-cec.c
 create mode 100644 drivers/gpu/drm/i2c/tda9950.c
 create mode 100644 drivers/video/hdmi-notifier.c
 create mode 100644 include/linux/hdmi-notifier.h
 create mode 100644 include/linux/platform_data/dw_hdmi-cec.h
 create mode 100644 include/linux/platform_data/tda9950.h

-- 
2.8.1


^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 0/5] CEC drivers for iMX6 and TDA9950
@ 2016-11-14 15:22 ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-media
  Cc: Russell King - ARM Linux, linux-fbdev, dri-devel, linux-arm-kernel

From: Hans Verkuil <hans.verkuil@cisco.com>

This patch series is an update to this RFC series from Russell:

https://lists.freedesktop.org/archives/dri-devel/2016-August/115733.html

I have not seen any updates to this, so I hope that that series is still
the latest version.

The main problem with that original series was that the notifier didn't
store the state, so if a CEC driver registered with the notifier, then
it wouldn't be informed of the current state.

The hdmi-notifier code has been changed to a per-HDMI-device and refcounted
block_notifier that stores the state and will report the current state
upon registration.

The other four patches have been adapted to the new notifier code, but
no other changes were made.

It has *only* been compile-tested. I might be able to verify it next week
with an actual i.MX6 device, but it will take time to set that up.

If someone has a ready-to-test setup, then I would very much appreciate
it if this series can be tested.

The patches are also available in my branch:

https://git.linuxtv.org/hverkuil/media_tree.git/log/?hÎc-notifiers

It is on top of my patch series that moves CEC out of staging. This
is planned for 4.10.

Regards,

	Hans

Hans Verkuil (1):
  video: add HDMI state notifier support

Russell King (4):
  drm/bridge: dw_hdmi: remove CEC engine register definitions
  drm/bridge: dw_hdmi: add HDMI notifier support
  drm/bridge: add dw-hdmi cec driver using Hans Verkuil's CEC code
  drm/i2c: add tda998x/tda9950 CEC driver

 drivers/gpu/drm/bridge/Kconfig            |   8 +
 drivers/gpu/drm/bridge/Makefile           |   1 +
 drivers/gpu/drm/bridge/dw-hdmi-cec.c      | 346 ++++++++++++++++++++
 drivers/gpu/drm/bridge/dw-hdmi.c          |  89 +++++-
 drivers/gpu/drm/bridge/dw-hdmi.h          |  45 ---
 drivers/gpu/drm/i2c/Kconfig               |   5 +
 drivers/gpu/drm/i2c/Makefile              |   1 +
 drivers/gpu/drm/i2c/tda9950.c             | 516 ++++++++++++++++++++++++++++++
 drivers/video/Kconfig                     |   3 +
 drivers/video/Makefile                    |   1 +
 drivers/video/hdmi-notifier.c             | 136 ++++++++
 include/linux/hdmi-notifier.h             |  43 +++
 include/linux/platform_data/dw_hdmi-cec.h |  16 +
 include/linux/platform_data/tda9950.h     |  15 +
 14 files changed, 1168 insertions(+), 57 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/dw-hdmi-cec.c
 create mode 100644 drivers/gpu/drm/i2c/tda9950.c
 create mode 100644 drivers/video/hdmi-notifier.c
 create mode 100644 include/linux/hdmi-notifier.h
 create mode 100644 include/linux/platform_data/dw_hdmi-cec.h
 create mode 100644 include/linux/platform_data/tda9950.h

-- 
2.8.1


^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 0/5] CEC drivers for iMX6 and TDA9950
@ 2016-11-14 15:22 ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-arm-kernel

From: Hans Verkuil <hans.verkuil@cisco.com>

This patch series is an update to this RFC series from Russell:

https://lists.freedesktop.org/archives/dri-devel/2016-August/115733.html

I have not seen any updates to this, so I hope that that series is still
the latest version.

The main problem with that original series was that the notifier didn't
store the state, so if a CEC driver registered with the notifier, then
it wouldn't be informed of the current state.

The hdmi-notifier code has been changed to a per-HDMI-device and refcounted
block_notifier that stores the state and will report the current state
upon registration.

The other four patches have been adapted to the new notifier code, but
no other changes were made.

It has *only* been compile-tested. I might be able to verify it next week
with an actual i.MX6 device, but it will take time to set that up.

If someone has a ready-to-test setup, then I would very much appreciate
it if this series can be tested.

The patches are also available in my branch:

https://git.linuxtv.org/hverkuil/media_tree.git/log/?h=cec-notifiers

It is on top of my patch series that moves CEC out of staging. This
is planned for 4.10.

Regards,

	Hans

Hans Verkuil (1):
  video: add HDMI state notifier support

Russell King (4):
  drm/bridge: dw_hdmi: remove CEC engine register definitions
  drm/bridge: dw_hdmi: add HDMI notifier support
  drm/bridge: add dw-hdmi cec driver using Hans Verkuil's CEC code
  drm/i2c: add tda998x/tda9950 CEC driver

 drivers/gpu/drm/bridge/Kconfig            |   8 +
 drivers/gpu/drm/bridge/Makefile           |   1 +
 drivers/gpu/drm/bridge/dw-hdmi-cec.c      | 346 ++++++++++++++++++++
 drivers/gpu/drm/bridge/dw-hdmi.c          |  89 +++++-
 drivers/gpu/drm/bridge/dw-hdmi.h          |  45 ---
 drivers/gpu/drm/i2c/Kconfig               |   5 +
 drivers/gpu/drm/i2c/Makefile              |   1 +
 drivers/gpu/drm/i2c/tda9950.c             | 516 ++++++++++++++++++++++++++++++
 drivers/video/Kconfig                     |   3 +
 drivers/video/Makefile                    |   1 +
 drivers/video/hdmi-notifier.c             | 136 ++++++++
 include/linux/hdmi-notifier.h             |  43 +++
 include/linux/platform_data/dw_hdmi-cec.h |  16 +
 include/linux/platform_data/tda9950.h     |  15 +
 14 files changed, 1168 insertions(+), 57 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/dw-hdmi-cec.c
 create mode 100644 drivers/gpu/drm/i2c/tda9950.c
 create mode 100644 drivers/video/hdmi-notifier.c
 create mode 100644 include/linux/hdmi-notifier.h
 create mode 100644 include/linux/platform_data/dw_hdmi-cec.h
 create mode 100644 include/linux/platform_data/tda9950.h

-- 
2.8.1

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 1/5] video: add HDMI state notifier support
  2016-11-14 15:22 ` Hans Verkuil
  (?)
@ 2016-11-14 15:22   ` Hans Verkuil
  -1 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-media
  Cc: Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Hans Verkuil

From: Hans Verkuil <hans.verkuil@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@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;
+};
+
+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
-- 
2.8.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 1/5] video: add HDMI state notifier support
@ 2016-11-14 15:22   ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-media
  Cc: Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Hans Verkuil

From: Hans Verkuil <hans.verkuil@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@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;
+};
+
+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
-- 
2.8.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 1/5] video: add HDMI state notifier support
@ 2016-11-14 15:22   ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-arm-kernel

From: Hans Verkuil <hans.verkuil@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@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;
+};
+
+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
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
  2016-11-14 15:22 ` Hans Verkuil
  (?)
@ 2016-11-14 15:22   ` Hans Verkuil
  -1 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-media
  Cc: Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Russell King

From: Russell King <rmk+kernel@arm.linux.org.uk>

We don't need the CEC engine register definitions, so let's remove them.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
 1 file changed, 45 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
index fc9a560..26d6845 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.h
+++ b/drivers/gpu/drm/bridge/dw-hdmi.h
@@ -478,51 +478,6 @@
 #define HDMI_A_PRESETUP                         0x501A
 #define HDMI_A_SRM_BASE                         0x5020
 
-/* CEC Engine Registers */
-#define HDMI_CEC_CTRL                           0x7D00
-#define HDMI_CEC_STAT                           0x7D01
-#define HDMI_CEC_MASK                           0x7D02
-#define HDMI_CEC_POLARITY                       0x7D03
-#define HDMI_CEC_INT                            0x7D04
-#define HDMI_CEC_ADDR_L                         0x7D05
-#define HDMI_CEC_ADDR_H                         0x7D06
-#define HDMI_CEC_TX_CNT                         0x7D07
-#define HDMI_CEC_RX_CNT                         0x7D08
-#define HDMI_CEC_TX_DATA0                       0x7D10
-#define HDMI_CEC_TX_DATA1                       0x7D11
-#define HDMI_CEC_TX_DATA2                       0x7D12
-#define HDMI_CEC_TX_DATA3                       0x7D13
-#define HDMI_CEC_TX_DATA4                       0x7D14
-#define HDMI_CEC_TX_DATA5                       0x7D15
-#define HDMI_CEC_TX_DATA6                       0x7D16
-#define HDMI_CEC_TX_DATA7                       0x7D17
-#define HDMI_CEC_TX_DATA8                       0x7D18
-#define HDMI_CEC_TX_DATA9                       0x7D19
-#define HDMI_CEC_TX_DATA10                      0x7D1a
-#define HDMI_CEC_TX_DATA11                      0x7D1b
-#define HDMI_CEC_TX_DATA12                      0x7D1c
-#define HDMI_CEC_TX_DATA13                      0x7D1d
-#define HDMI_CEC_TX_DATA14                      0x7D1e
-#define HDMI_CEC_TX_DATA15                      0x7D1f
-#define HDMI_CEC_RX_DATA0                       0x7D20
-#define HDMI_CEC_RX_DATA1                       0x7D21
-#define HDMI_CEC_RX_DATA2                       0x7D22
-#define HDMI_CEC_RX_DATA3                       0x7D23
-#define HDMI_CEC_RX_DATA4                       0x7D24
-#define HDMI_CEC_RX_DATA5                       0x7D25
-#define HDMI_CEC_RX_DATA6                       0x7D26
-#define HDMI_CEC_RX_DATA7                       0x7D27
-#define HDMI_CEC_RX_DATA8                       0x7D28
-#define HDMI_CEC_RX_DATA9                       0x7D29
-#define HDMI_CEC_RX_DATA10                      0x7D2a
-#define HDMI_CEC_RX_DATA11                      0x7D2b
-#define HDMI_CEC_RX_DATA12                      0x7D2c
-#define HDMI_CEC_RX_DATA13                      0x7D2d
-#define HDMI_CEC_RX_DATA14                      0x7D2e
-#define HDMI_CEC_RX_DATA15                      0x7D2f
-#define HDMI_CEC_LOCK                           0x7D30
-#define HDMI_CEC_WKUPCTRL                       0x7D31
-
 /* I2C Master Registers (E-DDC) */
 #define HDMI_I2CM_SLAVE                         0x7E00
 #define HDMI_I2CM_ADDRESS                       0x7E01
-- 
2.8.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
@ 2016-11-14 15:22   ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-media
  Cc: Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Russell King

From: Russell King <rmk+kernel@arm.linux.org.uk>

We don't need the CEC engine register definitions, so let's remove them.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
 1 file changed, 45 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
index fc9a560..26d6845 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.h
+++ b/drivers/gpu/drm/bridge/dw-hdmi.h
@@ -478,51 +478,6 @@
 #define HDMI_A_PRESETUP                         0x501A
 #define HDMI_A_SRM_BASE                         0x5020
 
-/* CEC Engine Registers */
-#define HDMI_CEC_CTRL                           0x7D00
-#define HDMI_CEC_STAT                           0x7D01
-#define HDMI_CEC_MASK                           0x7D02
-#define HDMI_CEC_POLARITY                       0x7D03
-#define HDMI_CEC_INT                            0x7D04
-#define HDMI_CEC_ADDR_L                         0x7D05
-#define HDMI_CEC_ADDR_H                         0x7D06
-#define HDMI_CEC_TX_CNT                         0x7D07
-#define HDMI_CEC_RX_CNT                         0x7D08
-#define HDMI_CEC_TX_DATA0                       0x7D10
-#define HDMI_CEC_TX_DATA1                       0x7D11
-#define HDMI_CEC_TX_DATA2                       0x7D12
-#define HDMI_CEC_TX_DATA3                       0x7D13
-#define HDMI_CEC_TX_DATA4                       0x7D14
-#define HDMI_CEC_TX_DATA5                       0x7D15
-#define HDMI_CEC_TX_DATA6                       0x7D16
-#define HDMI_CEC_TX_DATA7                       0x7D17
-#define HDMI_CEC_TX_DATA8                       0x7D18
-#define HDMI_CEC_TX_DATA9                       0x7D19
-#define HDMI_CEC_TX_DATA10                      0x7D1a
-#define HDMI_CEC_TX_DATA11                      0x7D1b
-#define HDMI_CEC_TX_DATA12                      0x7D1c
-#define HDMI_CEC_TX_DATA13                      0x7D1d
-#define HDMI_CEC_TX_DATA14                      0x7D1e
-#define HDMI_CEC_TX_DATA15                      0x7D1f
-#define HDMI_CEC_RX_DATA0                       0x7D20
-#define HDMI_CEC_RX_DATA1                       0x7D21
-#define HDMI_CEC_RX_DATA2                       0x7D22
-#define HDMI_CEC_RX_DATA3                       0x7D23
-#define HDMI_CEC_RX_DATA4                       0x7D24
-#define HDMI_CEC_RX_DATA5                       0x7D25
-#define HDMI_CEC_RX_DATA6                       0x7D26
-#define HDMI_CEC_RX_DATA7                       0x7D27
-#define HDMI_CEC_RX_DATA8                       0x7D28
-#define HDMI_CEC_RX_DATA9                       0x7D29
-#define HDMI_CEC_RX_DATA10                      0x7D2a
-#define HDMI_CEC_RX_DATA11                      0x7D2b
-#define HDMI_CEC_RX_DATA12                      0x7D2c
-#define HDMI_CEC_RX_DATA13                      0x7D2d
-#define HDMI_CEC_RX_DATA14                      0x7D2e
-#define HDMI_CEC_RX_DATA15                      0x7D2f
-#define HDMI_CEC_LOCK                           0x7D30
-#define HDMI_CEC_WKUPCTRL                       0x7D31
-
 /* I2C Master Registers (E-DDC) */
 #define HDMI_I2CM_SLAVE                         0x7E00
 #define HDMI_I2CM_ADDRESS                       0x7E01
-- 
2.8.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
@ 2016-11-14 15:22   ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-arm-kernel

From: Russell King <rmk+kernel@arm.linux.org.uk>

We don't need the CEC engine register definitions, so let's remove them.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
 1 file changed, 45 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
index fc9a560..26d6845 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.h
+++ b/drivers/gpu/drm/bridge/dw-hdmi.h
@@ -478,51 +478,6 @@
 #define HDMI_A_PRESETUP                         0x501A
 #define HDMI_A_SRM_BASE                         0x5020
 
-/* CEC Engine Registers */
-#define HDMI_CEC_CTRL                           0x7D00
-#define HDMI_CEC_STAT                           0x7D01
-#define HDMI_CEC_MASK                           0x7D02
-#define HDMI_CEC_POLARITY                       0x7D03
-#define HDMI_CEC_INT                            0x7D04
-#define HDMI_CEC_ADDR_L                         0x7D05
-#define HDMI_CEC_ADDR_H                         0x7D06
-#define HDMI_CEC_TX_CNT                         0x7D07
-#define HDMI_CEC_RX_CNT                         0x7D08
-#define HDMI_CEC_TX_DATA0                       0x7D10
-#define HDMI_CEC_TX_DATA1                       0x7D11
-#define HDMI_CEC_TX_DATA2                       0x7D12
-#define HDMI_CEC_TX_DATA3                       0x7D13
-#define HDMI_CEC_TX_DATA4                       0x7D14
-#define HDMI_CEC_TX_DATA5                       0x7D15
-#define HDMI_CEC_TX_DATA6                       0x7D16
-#define HDMI_CEC_TX_DATA7                       0x7D17
-#define HDMI_CEC_TX_DATA8                       0x7D18
-#define HDMI_CEC_TX_DATA9                       0x7D19
-#define HDMI_CEC_TX_DATA10                      0x7D1a
-#define HDMI_CEC_TX_DATA11                      0x7D1b
-#define HDMI_CEC_TX_DATA12                      0x7D1c
-#define HDMI_CEC_TX_DATA13                      0x7D1d
-#define HDMI_CEC_TX_DATA14                      0x7D1e
-#define HDMI_CEC_TX_DATA15                      0x7D1f
-#define HDMI_CEC_RX_DATA0                       0x7D20
-#define HDMI_CEC_RX_DATA1                       0x7D21
-#define HDMI_CEC_RX_DATA2                       0x7D22
-#define HDMI_CEC_RX_DATA3                       0x7D23
-#define HDMI_CEC_RX_DATA4                       0x7D24
-#define HDMI_CEC_RX_DATA5                       0x7D25
-#define HDMI_CEC_RX_DATA6                       0x7D26
-#define HDMI_CEC_RX_DATA7                       0x7D27
-#define HDMI_CEC_RX_DATA8                       0x7D28
-#define HDMI_CEC_RX_DATA9                       0x7D29
-#define HDMI_CEC_RX_DATA10                      0x7D2a
-#define HDMI_CEC_RX_DATA11                      0x7D2b
-#define HDMI_CEC_RX_DATA12                      0x7D2c
-#define HDMI_CEC_RX_DATA13                      0x7D2d
-#define HDMI_CEC_RX_DATA14                      0x7D2e
-#define HDMI_CEC_RX_DATA15                      0x7D2f
-#define HDMI_CEC_LOCK                           0x7D30
-#define HDMI_CEC_WKUPCTRL                       0x7D31
-
 /* I2C Master Registers (E-DDC) */
 #define HDMI_I2CM_SLAVE                         0x7E00
 #define HDMI_I2CM_ADDRESS                       0x7E01
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 3/5] drm/bridge: dw_hdmi: add HDMI notifier support
  2016-11-14 15:22 ` Hans Verkuil
  (?)
@ 2016-11-14 15:22   ` Hans Verkuil
  -1 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-media
  Cc: Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Russell King

From: Russell King <rmk+kernel@arm.linux.org.uk>

Add HDMI notifiers to the HDMI bridge driver.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig   |  1 +
 drivers/gpu/drm/bridge/dw-hdmi.c | 25 ++++++++++++++++++++++++-
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 10e12e7..5f4ebe9 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -27,6 +27,7 @@ config DRM_DUMB_VGA_DAC
 config DRM_DW_HDMI
 	tristate
 	select DRM_KMS_HELPER
+	select HDMI_NOTIFIERS
 
 config DRM_DW_HDMI_AHB_AUDIO
 	tristate "Synopsis Designware AHB Audio interface"
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index ab7023e..bd02da5 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -16,6 +16,7 @@
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/hdmi.h>
+#include <linux/hdmi-notifier.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
 #include <linux/spinlock.h>
@@ -114,6 +115,7 @@ struct dw_hdmi {
 
 	struct hdmi_data_info hdmi_data;
 	const struct dw_hdmi_plat_data *plat_data;
+	struct hdmi_notifier *n;
 
 	int vic;
 
@@ -1448,9 +1450,11 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
 		hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
 		hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
 		drm_mode_connector_update_edid_property(connector, edid);
+		hdmi_event_new_edid(hdmi->n, edid, 0);
 		ret = drm_add_edid_modes(connector, edid);
 		/* Store the ELD */
 		drm_edid_to_eld(connector, edid);
+		hdmi_event_new_eld(hdmi->n, connector->eld);
 		kfree(edid);
 	} else {
 		dev_dbg(hdmi->dev, "failed to get edid\n");
@@ -1579,6 +1583,12 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 			dw_hdmi_update_phy_mask(hdmi);
 		}
 		mutex_unlock(&hdmi->mutex);
+
+		if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0)
+			hdmi_event_disconnect(hdmi->n);
+		else if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) ==
+			 (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_PHY_HPD))
+			hdmi_event_connect(hdmi->n);
 	}
 
 	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
@@ -1732,11 +1742,17 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 
 	initialize_hdmi_ih_mutes(hdmi);
 
+	hdmi->n = hdmi_notifier_get(dev);
+	if (!hdmi->n) {
+		ret = -ENOMEM;
+		goto err_iahb;
+	}
+
 	ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
 					dw_hdmi_irq, IRQF_SHARED,
 					dev_name(dev), hdmi);
 	if (ret)
-		goto err_iahb;
+		goto err_hdmi_not;
 
 	/*
 	 * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
@@ -1788,6 +1804,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 
 	return 0;
 
+err_hdmi_not:
+	hdmi_notifier_put(hdmi->n);
 err_iahb:
 	clk_disable_unprepare(hdmi->iahb_clk);
 err_isfr:
@@ -1804,6 +1822,11 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 	if (hdmi->audio && !IS_ERR(hdmi->audio))
 		platform_device_unregister(hdmi->audio);
 
+	hdmi_notifier_put(hdmi->n);
+
+	if (!IS_ERR(hdmi->cec))
+		platform_device_unregister(hdmi->cec);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
-- 
2.8.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 3/5] drm/bridge: dw_hdmi: add HDMI notifier support
@ 2016-11-14 15:22   ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-media
  Cc: Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Russell King

From: Russell King <rmk+kernel@arm.linux.org.uk>

Add HDMI notifiers to the HDMI bridge driver.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig   |  1 +
 drivers/gpu/drm/bridge/dw-hdmi.c | 25 ++++++++++++++++++++++++-
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 10e12e7..5f4ebe9 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -27,6 +27,7 @@ config DRM_DUMB_VGA_DAC
 config DRM_DW_HDMI
 	tristate
 	select DRM_KMS_HELPER
+	select HDMI_NOTIFIERS
 
 config DRM_DW_HDMI_AHB_AUDIO
 	tristate "Synopsis Designware AHB Audio interface"
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index ab7023e..bd02da5 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -16,6 +16,7 @@
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/hdmi.h>
+#include <linux/hdmi-notifier.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
 #include <linux/spinlock.h>
@@ -114,6 +115,7 @@ struct dw_hdmi {
 
 	struct hdmi_data_info hdmi_data;
 	const struct dw_hdmi_plat_data *plat_data;
+	struct hdmi_notifier *n;
 
 	int vic;
 
@@ -1448,9 +1450,11 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
 		hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
 		hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
 		drm_mode_connector_update_edid_property(connector, edid);
+		hdmi_event_new_edid(hdmi->n, edid, 0);
 		ret = drm_add_edid_modes(connector, edid);
 		/* Store the ELD */
 		drm_edid_to_eld(connector, edid);
+		hdmi_event_new_eld(hdmi->n, connector->eld);
 		kfree(edid);
 	} else {
 		dev_dbg(hdmi->dev, "failed to get edid\n");
@@ -1579,6 +1583,12 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 			dw_hdmi_update_phy_mask(hdmi);
 		}
 		mutex_unlock(&hdmi->mutex);
+
+		if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) = 0)
+			hdmi_event_disconnect(hdmi->n);
+		else if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) =
+			 (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_PHY_HPD))
+			hdmi_event_connect(hdmi->n);
 	}
 
 	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
@@ -1732,11 +1742,17 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 
 	initialize_hdmi_ih_mutes(hdmi);
 
+	hdmi->n = hdmi_notifier_get(dev);
+	if (!hdmi->n) {
+		ret = -ENOMEM;
+		goto err_iahb;
+	}
+
 	ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
 					dw_hdmi_irq, IRQF_SHARED,
 					dev_name(dev), hdmi);
 	if (ret)
-		goto err_iahb;
+		goto err_hdmi_not;
 
 	/*
 	 * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
@@ -1788,6 +1804,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 
 	return 0;
 
+err_hdmi_not:
+	hdmi_notifier_put(hdmi->n);
 err_iahb:
 	clk_disable_unprepare(hdmi->iahb_clk);
 err_isfr:
@@ -1804,6 +1822,11 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 	if (hdmi->audio && !IS_ERR(hdmi->audio))
 		platform_device_unregister(hdmi->audio);
 
+	hdmi_notifier_put(hdmi->n);
+
+	if (!IS_ERR(hdmi->cec))
+		platform_device_unregister(hdmi->cec);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
-- 
2.8.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 3/5] drm/bridge: dw_hdmi: add HDMI notifier support
@ 2016-11-14 15:22   ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-arm-kernel

From: Russell King <rmk+kernel@arm.linux.org.uk>

Add HDMI notifiers to the HDMI bridge driver.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig   |  1 +
 drivers/gpu/drm/bridge/dw-hdmi.c | 25 ++++++++++++++++++++++++-
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 10e12e7..5f4ebe9 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -27,6 +27,7 @@ config DRM_DUMB_VGA_DAC
 config DRM_DW_HDMI
 	tristate
 	select DRM_KMS_HELPER
+	select HDMI_NOTIFIERS
 
 config DRM_DW_HDMI_AHB_AUDIO
 	tristate "Synopsis Designware AHB Audio interface"
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index ab7023e..bd02da5 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -16,6 +16,7 @@
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/hdmi.h>
+#include <linux/hdmi-notifier.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
 #include <linux/spinlock.h>
@@ -114,6 +115,7 @@ struct dw_hdmi {
 
 	struct hdmi_data_info hdmi_data;
 	const struct dw_hdmi_plat_data *plat_data;
+	struct hdmi_notifier *n;
 
 	int vic;
 
@@ -1448,9 +1450,11 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
 		hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
 		hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
 		drm_mode_connector_update_edid_property(connector, edid);
+		hdmi_event_new_edid(hdmi->n, edid, 0);
 		ret = drm_add_edid_modes(connector, edid);
 		/* Store the ELD */
 		drm_edid_to_eld(connector, edid);
+		hdmi_event_new_eld(hdmi->n, connector->eld);
 		kfree(edid);
 	} else {
 		dev_dbg(hdmi->dev, "failed to get edid\n");
@@ -1579,6 +1583,12 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 			dw_hdmi_update_phy_mask(hdmi);
 		}
 		mutex_unlock(&hdmi->mutex);
+
+		if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0)
+			hdmi_event_disconnect(hdmi->n);
+		else if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) ==
+			 (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_PHY_HPD))
+			hdmi_event_connect(hdmi->n);
 	}
 
 	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
@@ -1732,11 +1742,17 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 
 	initialize_hdmi_ih_mutes(hdmi);
 
+	hdmi->n = hdmi_notifier_get(dev);
+	if (!hdmi->n) {
+		ret = -ENOMEM;
+		goto err_iahb;
+	}
+
 	ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
 					dw_hdmi_irq, IRQF_SHARED,
 					dev_name(dev), hdmi);
 	if (ret)
-		goto err_iahb;
+		goto err_hdmi_not;
 
 	/*
 	 * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
@@ -1788,6 +1804,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 
 	return 0;
 
+err_hdmi_not:
+	hdmi_notifier_put(hdmi->n);
 err_iahb:
 	clk_disable_unprepare(hdmi->iahb_clk);
 err_isfr:
@@ -1804,6 +1822,11 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 	if (hdmi->audio && !IS_ERR(hdmi->audio))
 		platform_device_unregister(hdmi->audio);
 
+	hdmi_notifier_put(hdmi->n);
+
+	if (!IS_ERR(hdmi->cec))
+		platform_device_unregister(hdmi->cec);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 4/5] drm/bridge: add dw-hdmi cec driver using Hans Verkuil's CEC code
  2016-11-14 15:22 ` Hans Verkuil
  (?)
@ 2016-11-14 15:22   ` Hans Verkuil
  -1 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-media
  Cc: Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Russell King

From: Russell King <rmk+kernel@arm.linux.org.uk>

Add a CEC driver for the dw-hdmi hardware using Hans Verkuil's CEC
implementation.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig            |   7 +
 drivers/gpu/drm/bridge/Makefile           |   1 +
 drivers/gpu/drm/bridge/dw-hdmi-cec.c      | 346 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw-hdmi.c          |  64 +++++-
 include/linux/platform_data/dw_hdmi-cec.h |  16 ++
 5 files changed, 423 insertions(+), 11 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/dw-hdmi-cec.c
 create mode 100644 include/linux/platform_data/dw_hdmi-cec.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 5f4ebe9..4ab137e 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -40,6 +40,13 @@ config DRM_DW_HDMI_AHB_AUDIO
 	  Designware HDMI block.  This is used in conjunction with
 	  the i.MX6 HDMI driver.
 
+config DRM_DW_HDMI_CEC
+	tristate "Synopsis Designware CEC interface"
+	depends on DRM_DW_HDMI && MEDIA_CEC_SUPPORT
+	help
+	  Support the CE interface which is part of the Synopsis
+	  Designware HDMI block.
+
 config DRM_NXP_PTN3460
 	tristate "NXP PTN3460 DP/LVDS bridge"
 	depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index cdf3a3c..6bdd8b9 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
 obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
 obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
+obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
 obj-$(CONFIG_DRM_SII902X) += sii902x.o
diff --git a/drivers/gpu/drm/bridge/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/dw-hdmi-cec.c
new file mode 100644
index 0000000..e7e12b5
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw-hdmi-cec.c
@@ -0,0 +1,346 @@
+/* http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/
+ * tree/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c?h=imx_3.0.35_4.1.0 */
+#include <linux/hdmi-notifier.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_data/dw_hdmi-cec.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <drm/drm_edid.h>
+
+#include <media/cec.h>
+#include <media/cec-edid.h>
+
+#define DEV_NAME "mxc_hdmi_cec"
+
+enum {
+	HDMI_IH_CEC_STAT0	= 0x0106,
+	HDMI_IH_MUTE_CEC_STAT0	= 0x0186,
+
+	HDMI_CEC_CTRL		= 0x7d00,
+	CEC_CTRL_START		= BIT(0),
+	CEC_CTRL_NORMAL		= 1 << 1,
+
+	HDMI_CEC_STAT		= 0x7d01,
+	CEC_STAT_DONE		= BIT(0),
+	CEC_STAT_EOM		= BIT(1),
+	CEC_STAT_NACK		= BIT(2),
+	CEC_STAT_ARBLOST	= BIT(3),
+	CEC_STAT_ERROR_INIT	= BIT(4),
+	CEC_STAT_ERROR_FOLL	= BIT(5),
+	CEC_STAT_WAKEUP		= BIT(6),
+
+	HDMI_CEC_MASK		= 0x7d02,
+	HDMI_CEC_POLARITY	= 0x7d03,
+	HDMI_CEC_INT		= 0x7d04,
+	HDMI_CEC_ADDR_L		= 0x7d05,
+	HDMI_CEC_ADDR_H		= 0x7d06,
+	HDMI_CEC_TX_CNT		= 0x7d07,
+	HDMI_CEC_RX_CNT		= 0x7d08,
+	HDMI_CEC_TX_DATA0	= 0x7d10,
+	HDMI_CEC_RX_DATA0	= 0x7d20,
+	HDMI_CEC_LOCK		= 0x7d30,
+	HDMI_CEC_WKUPCTRL	= 0x7d31,
+};
+
+struct dw_hdmi_cec {
+	void __iomem *base;
+	u32 addresses;
+	struct cec_adapter *adap;
+	struct cec_msg rx_msg;
+	unsigned int tx_status;
+	bool tx_done;
+	bool rx_done;
+	const struct dw_hdmi_cec_ops *ops;
+	void *ops_data;
+	int retries;
+	int irq;
+	struct hdmi_notifier *n;
+	struct notifier_block nb;
+};
+
+static int dw_hdmi_cec_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+	struct dw_hdmi_cec *cec = adap->priv;
+	u32 addresses;
+
+	if (logical_addr == CEC_LOG_ADDR_INVALID)
+		addresses = cec->addresses = BIT(15);
+	else
+		addresses = cec->addresses |= BIT(logical_addr);
+
+	writeb_relaxed(addresses & 255, cec->base + HDMI_CEC_ADDR_L);
+	writeb_relaxed(addresses >> 8, cec->base + HDMI_CEC_ADDR_H);
+
+	return 0;
+}
+
+static int dw_hdmi_cec_transmit(struct cec_adapter *adap, u8 attempts,
+				u32 signal_free_time, struct cec_msg *msg)
+{
+	struct dw_hdmi_cec *cec = adap->priv;
+	unsigned i;
+
+	cec->retries = attempts;
+
+	for (i = 0; i < msg->len; i++)
+		writeb_relaxed(msg->msg[i], cec->base + HDMI_CEC_TX_DATA0 + i);
+
+	writeb_relaxed(msg->len, cec->base + HDMI_CEC_TX_CNT);
+	writeb_relaxed(CEC_CTRL_NORMAL | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL);
+
+	return 0;
+}
+
+static irqreturn_t dw_hdmi_cec_hardirq(int irq, void *data)
+{
+	struct cec_adapter *adap = data;
+	struct dw_hdmi_cec *cec = adap->priv;
+	unsigned stat = readb_relaxed(cec->base + HDMI_IH_CEC_STAT0);
+	irqreturn_t ret = IRQ_HANDLED;
+
+	if (stat == 0)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, cec->base + HDMI_IH_CEC_STAT0);
+
+	if (stat & CEC_STAT_ERROR_INIT) {
+		if (cec->retries) {
+			unsigned v = readb_relaxed(cec->base + HDMI_CEC_CTRL);
+			writeb_relaxed(v | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL);
+			cec->retries -= 1;
+		} else {
+			cec->tx_status = CEC_TX_STATUS_MAX_RETRIES;
+			cec->tx_done = true;
+			ret = IRQ_WAKE_THREAD;
+		}
+	} else if (stat & CEC_STAT_DONE) {
+		cec->tx_status = CEC_TX_STATUS_OK;
+		cec->tx_done = true;
+		ret = IRQ_WAKE_THREAD;
+	} else if (stat & CEC_STAT_NACK) {
+		cec->tx_status = CEC_TX_STATUS_NACK;
+		cec->tx_done = true;
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	if (stat & CEC_STAT_EOM) {
+		unsigned len, i;
+		void *base = cec->base;
+
+		len = readb_relaxed(base + HDMI_CEC_RX_CNT);
+		if (len > sizeof(cec->rx_msg.msg))
+			len = sizeof(cec->rx_msg.msg);
+
+		for (i = 0; i < len; i++)
+			cec->rx_msg.msg[i] =
+				readb_relaxed(base + HDMI_CEC_RX_DATA0 + i);
+
+		writeb_relaxed(0, base + HDMI_CEC_LOCK);
+
+		cec->rx_msg.len = len;
+		smp_wmb();
+		cec->rx_done = true;
+
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+static irqreturn_t dw_hdmi_cec_thread(int irq, void *data)
+{
+	struct cec_adapter *adap = data;
+	struct dw_hdmi_cec *cec = adap->priv;
+
+	if (cec->tx_done) {
+		cec->tx_done = false;
+		cec_transmit_done(adap, cec->tx_status, 0, 0, 0, 0);
+	}
+	if (cec->rx_done) {
+		cec->rx_done = false;
+		smp_rmb();
+		cec_received_msg(adap, &cec->rx_msg);
+	}
+	return IRQ_HANDLED;
+}
+
+static int dw_hdmi_cec_enable(struct cec_adapter *adap, bool enable)
+{
+	struct dw_hdmi_cec *cec = adap->priv;
+
+	if (!enable) {
+		writeb_relaxed(~0, cec->base + HDMI_CEC_MASK);
+		writeb_relaxed(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+		writeb_relaxed(0, cec->base + HDMI_CEC_POLARITY);
+
+		cec->ops->disable(cec->ops_data);
+	} else {
+		unsigned irqs;
+
+		writeb_relaxed(0, cec->base + HDMI_CEC_CTRL);
+		writeb_relaxed(~0, cec->base + HDMI_IH_CEC_STAT0);
+		writeb_relaxed(0, cec->base + HDMI_CEC_LOCK);
+
+		dw_hdmi_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID);
+
+		cec->ops->enable(cec->ops_data);
+
+		irqs = CEC_STAT_ERROR_INIT | CEC_STAT_NACK | CEC_STAT_EOM |
+		       CEC_STAT_DONE;
+		writeb_relaxed(irqs, cec->base + HDMI_CEC_POLARITY);
+		writeb_relaxed(~irqs, cec->base + HDMI_CEC_MASK);
+		writeb_relaxed(~irqs, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+	}
+	return 0;
+}
+
+static const struct cec_adap_ops dw_hdmi_cec_ops = {
+	.adap_enable = dw_hdmi_cec_enable,
+	.adap_log_addr = dw_hdmi_cec_log_addr,
+	.adap_transmit = dw_hdmi_cec_transmit,
+};
+
+static unsigned int parse_hdmi_addr(const struct edid *edid)
+{
+	if (!edid || edid->extensions == 0)
+		return (u16)~0;
+
+	return cec_get_edid_phys_addr((u8 *)edid,
+				EDID_LENGTH * (edid->extensions + 1), NULL);
+}
+
+static int dw_hdmi_cec_notify(struct notifier_block *nb, unsigned long event,
+			      void *data)
+{
+	struct dw_hdmi_cec *cec = container_of(nb, struct dw_hdmi_cec, nb);
+	struct hdmi_notifier *n = data;
+	unsigned int phys;
+
+	dev_info(cec->adap->devnode.parent, "event %lu\n", event);
+
+	switch (event) {
+	case HDMI_CONNECTED:
+		break;
+
+	case HDMI_DISCONNECTED:
+		cec_s_phys_addr(cec->adap, CEC_PHYS_ADDR_INVALID, false);
+		break;
+
+	case HDMI_NEW_EDID:
+		phys = parse_hdmi_addr(n->edid);
+		cec_s_phys_addr(cec->adap, phys, false);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static void dw_hdmi_cec_del(void *data)
+{
+	struct dw_hdmi_cec *cec = data;
+
+	cec_delete_adapter(cec->adap);
+}
+
+static int dw_hdmi_cec_probe(struct platform_device *pdev)
+{
+	struct dw_hdmi_cec_data *data = dev_get_platdata(&pdev->dev);
+	struct dw_hdmi_cec *cec;
+	int ret;
+
+	if (!data)
+		return -ENXIO;
+
+	/*
+	 * Our device is just a convenience - we want to link to the real
+	 * hardware device here, so that userspace can see the association
+	 * between the HDMI hardware and its associated CEC chardev.
+	 */
+	cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
+	if (!cec)
+		return -ENOMEM;
+
+	cec->base = data->base;
+	cec->irq = data->irq;
+	cec->ops = data->ops;
+	cec->ops_data = data->ops_data;
+	cec->nb.notifier_call = dw_hdmi_cec_notify;
+
+	platform_set_drvdata(pdev, cec);
+
+	writeb_relaxed(0, cec->base + HDMI_CEC_TX_CNT);
+	writeb_relaxed(~0, cec->base + HDMI_CEC_MASK);
+	writeb_relaxed(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+	writeb_relaxed(0, cec->base + HDMI_CEC_POLARITY);
+
+	cec->adap = cec_allocate_adapter(&dw_hdmi_cec_ops, cec, "dw_hdmi",
+					 CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
+					 CEC_CAP_RC, CEC_MAX_LOG_ADDRS,
+					 pdev->dev.parent);
+	if (IS_ERR(cec->adap))
+		return PTR_ERR(cec->adap);
+
+	/* override the module pointer */
+	cec->adap->owner = THIS_MODULE;
+
+	ret = devm_add_action(&pdev->dev, dw_hdmi_cec_del, cec);
+	if (ret) {
+		cec_delete_adapter(cec->adap);
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(&pdev->dev, cec->irq,
+					dw_hdmi_cec_hardirq,
+					dw_hdmi_cec_thread, IRQF_SHARED,
+					DEV_NAME, cec->adap);
+	if (ret < 0)
+		return ret;
+
+	ret = cec_register_adapter(cec->adap);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * CEC documentation says we must not call cec_delete_adapter
+	 * after a successful call to cec_register_adapter().
+	 */
+	devm_remove_action(&pdev->dev, dw_hdmi_cec_del, cec);
+
+	cec->n = hdmi_notifier_get(cec->adap->devnode.parent);
+	if (!cec->n)
+		return -ENOMEM;
+	hdmi_notifier_register(cec->n, &cec->nb);
+
+	return 0;
+}
+
+static int dw_hdmi_cec_remove(struct platform_device *pdev)
+{
+	struct dw_hdmi_cec *cec = platform_get_drvdata(pdev);
+
+	hdmi_notifier_unregister(cec->n, &cec->nb);
+	hdmi_notifier_put(cec->n);
+	cec_unregister_adapter(cec->adap);
+
+	return 0;
+}
+
+static struct platform_driver dw_hdmi_cec_driver = {
+	.probe	= dw_hdmi_cec_probe,
+	.remove	= dw_hdmi_cec_remove,
+	.driver = {
+		.name = "dw-hdmi-cec",
+		.owner = THIS_MODULE,
+	},
+};
+module_platform_driver(dw_hdmi_cec_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI CEC driver for i.MX");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(PLATFORM_MODULE_PREFIX "dw-hdmi-cec");
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index bd02da5..06191b4 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -19,6 +19,7 @@
 #include <linux/hdmi-notifier.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
+#include <linux/platform_data/dw_hdmi-cec.h>
 #include <linux/spinlock.h>
 
 #include <drm/drm_of.h>
@@ -108,6 +109,7 @@ struct dw_hdmi {
 	struct drm_bridge *bridge;
 
 	struct platform_device *audio;
+	struct platform_device *cec;
 	enum dw_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -120,6 +122,7 @@ struct dw_hdmi {
 	int vic;
 
 	u8 edid[HDMI_EDID_LEN];
+	u8 mc_clkdis;
 	bool cable_plugin;
 
 	bool phy_enabled;
@@ -1110,8 +1113,6 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi)
 /* HDMI Initialization Step B.4 */
 static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
 {
-	u8 clkdis;
-
 	/* control period minimum duration */
 	hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
 	hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
@@ -1123,23 +1124,28 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
 	hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
 
 	/* Enable pixel clock and tmds data path */
-	clkdis = 0x7F;
-	clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE |
+			   HDMI_MC_CLKDIS_CSCCLK_DISABLE |
+			   HDMI_MC_CLKDIS_AUDCLK_DISABLE |
+			   HDMI_MC_CLKDIS_PREPCLK_DISABLE |
+			   HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 
-	clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 
 	/* Enable csc path */
 	if (is_color_space_conversion(hdmi)) {
-		clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
-		hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+		hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
+		hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 	}
 }
 
 static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
 {
-	hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 }
 
 /* Workaround to clear the overflow condition */
@@ -1299,7 +1305,6 @@ static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
 	hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK);
 	hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK);
 	hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
-	hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);
 	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT);
 	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT);
 
@@ -1640,6 +1645,27 @@ static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi)
 	return 0;
 }
 
+static void dw_hdmi_cec_enable(void *data)
+{
+	struct dw_hdmi *hdmi = data;
+
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+}
+
+static void dw_hdmi_cec_disable(void *data)
+{
+	struct dw_hdmi *hdmi = data;
+
+	hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CECCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+}
+
+static const struct dw_hdmi_cec_ops dw_hdmi_cec_ops = {
+	.enable = dw_hdmi_cec_enable,
+	.disable = dw_hdmi_cec_disable,
+};
+
 int dw_hdmi_bind(struct device *dev, struct device *master,
 		 void *data, struct drm_encoder *encoder,
 		 struct resource *iores, int irq,
@@ -1650,6 +1676,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
 	struct dw_hdmi_audio_data audio;
+	struct dw_hdmi_cec_data cec;
 	struct dw_hdmi *hdmi;
 	int ret;
 	u32 val = 1;
@@ -1668,6 +1695,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi->disabled = true;
 	hdmi->rxsense = true;
 	hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
+	hdmi->mc_clkdis = 0x7f;
 
 	mutex_init(&hdmi->mutex);
 	mutex_init(&hdmi->audio_mutex);
@@ -1800,6 +1828,18 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 		hdmi->audio = platform_device_register_full(&pdevinfo);
 	}
 
+	cec.base = hdmi->regs;
+	cec.irq = irq;
+	cec.ops = &dw_hdmi_cec_ops;
+	cec.ops_data = hdmi;
+
+	pdevinfo.name = "dw-hdmi-cec";
+	pdevinfo.data = &cec;
+	pdevinfo.size_data = sizeof(cec);
+	pdevinfo.dma_mask = 0;
+
+	hdmi->cec = platform_device_register_full(&pdevinfo);
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1821,6 +1861,8 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 
 	if (hdmi->audio && !IS_ERR(hdmi->audio))
 		platform_device_unregister(hdmi->audio);
+	if (!IS_ERR(hdmi->cec))
+		platform_device_unregister(hdmi->cec);
 
 	hdmi_notifier_put(hdmi->n);
 
diff --git a/include/linux/platform_data/dw_hdmi-cec.h b/include/linux/platform_data/dw_hdmi-cec.h
new file mode 100644
index 0000000..5ff40cc
--- /dev/null
+++ b/include/linux/platform_data/dw_hdmi-cec.h
@@ -0,0 +1,16 @@
+#ifndef DW_HDMI_CEC_H
+#define DW_HDMI_CEC_H
+
+struct dw_hdmi_cec_ops {
+	void (*enable)(void *);
+	void (*disable)(void *);
+};
+
+struct dw_hdmi_cec_data {
+	void __iomem *base;
+	int irq;
+	const struct dw_hdmi_cec_ops *ops;
+	void *ops_data;
+};
+
+#endif
-- 
2.8.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 4/5] drm/bridge: add dw-hdmi cec driver using Hans Verkuil's CEC code
@ 2016-11-14 15:22   ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-media
  Cc: Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Russell King

From: Russell King <rmk+kernel@arm.linux.org.uk>

Add a CEC driver for the dw-hdmi hardware using Hans Verkuil's CEC
implementation.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig            |   7 +
 drivers/gpu/drm/bridge/Makefile           |   1 +
 drivers/gpu/drm/bridge/dw-hdmi-cec.c      | 346 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw-hdmi.c          |  64 +++++-
 include/linux/platform_data/dw_hdmi-cec.h |  16 ++
 5 files changed, 423 insertions(+), 11 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/dw-hdmi-cec.c
 create mode 100644 include/linux/platform_data/dw_hdmi-cec.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 5f4ebe9..4ab137e 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -40,6 +40,13 @@ config DRM_DW_HDMI_AHB_AUDIO
 	  Designware HDMI block.  This is used in conjunction with
 	  the i.MX6 HDMI driver.
 
+config DRM_DW_HDMI_CEC
+	tristate "Synopsis Designware CEC interface"
+	depends on DRM_DW_HDMI && MEDIA_CEC_SUPPORT
+	help
+	  Support the CE interface which is part of the Synopsis
+	  Designware HDMI block.
+
 config DRM_NXP_PTN3460
 	tristate "NXP PTN3460 DP/LVDS bridge"
 	depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index cdf3a3c..6bdd8b9 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
 obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
 obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
+obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
 obj-$(CONFIG_DRM_SII902X) += sii902x.o
diff --git a/drivers/gpu/drm/bridge/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/dw-hdmi-cec.c
new file mode 100644
index 0000000..e7e12b5
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw-hdmi-cec.c
@@ -0,0 +1,346 @@
+/* http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/
+ * tree/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c?h=imx_3.0.35_4.1.0 */
+#include <linux/hdmi-notifier.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_data/dw_hdmi-cec.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <drm/drm_edid.h>
+
+#include <media/cec.h>
+#include <media/cec-edid.h>
+
+#define DEV_NAME "mxc_hdmi_cec"
+
+enum {
+	HDMI_IH_CEC_STAT0	= 0x0106,
+	HDMI_IH_MUTE_CEC_STAT0	= 0x0186,
+
+	HDMI_CEC_CTRL		= 0x7d00,
+	CEC_CTRL_START		= BIT(0),
+	CEC_CTRL_NORMAL		= 1 << 1,
+
+	HDMI_CEC_STAT		= 0x7d01,
+	CEC_STAT_DONE		= BIT(0),
+	CEC_STAT_EOM		= BIT(1),
+	CEC_STAT_NACK		= BIT(2),
+	CEC_STAT_ARBLOST	= BIT(3),
+	CEC_STAT_ERROR_INIT	= BIT(4),
+	CEC_STAT_ERROR_FOLL	= BIT(5),
+	CEC_STAT_WAKEUP		= BIT(6),
+
+	HDMI_CEC_MASK		= 0x7d02,
+	HDMI_CEC_POLARITY	= 0x7d03,
+	HDMI_CEC_INT		= 0x7d04,
+	HDMI_CEC_ADDR_L		= 0x7d05,
+	HDMI_CEC_ADDR_H		= 0x7d06,
+	HDMI_CEC_TX_CNT		= 0x7d07,
+	HDMI_CEC_RX_CNT		= 0x7d08,
+	HDMI_CEC_TX_DATA0	= 0x7d10,
+	HDMI_CEC_RX_DATA0	= 0x7d20,
+	HDMI_CEC_LOCK		= 0x7d30,
+	HDMI_CEC_WKUPCTRL	= 0x7d31,
+};
+
+struct dw_hdmi_cec {
+	void __iomem *base;
+	u32 addresses;
+	struct cec_adapter *adap;
+	struct cec_msg rx_msg;
+	unsigned int tx_status;
+	bool tx_done;
+	bool rx_done;
+	const struct dw_hdmi_cec_ops *ops;
+	void *ops_data;
+	int retries;
+	int irq;
+	struct hdmi_notifier *n;
+	struct notifier_block nb;
+};
+
+static int dw_hdmi_cec_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+	struct dw_hdmi_cec *cec = adap->priv;
+	u32 addresses;
+
+	if (logical_addr = CEC_LOG_ADDR_INVALID)
+		addresses = cec->addresses = BIT(15);
+	else
+		addresses = cec->addresses |= BIT(logical_addr);
+
+	writeb_relaxed(addresses & 255, cec->base + HDMI_CEC_ADDR_L);
+	writeb_relaxed(addresses >> 8, cec->base + HDMI_CEC_ADDR_H);
+
+	return 0;
+}
+
+static int dw_hdmi_cec_transmit(struct cec_adapter *adap, u8 attempts,
+				u32 signal_free_time, struct cec_msg *msg)
+{
+	struct dw_hdmi_cec *cec = adap->priv;
+	unsigned i;
+
+	cec->retries = attempts;
+
+	for (i = 0; i < msg->len; i++)
+		writeb_relaxed(msg->msg[i], cec->base + HDMI_CEC_TX_DATA0 + i);
+
+	writeb_relaxed(msg->len, cec->base + HDMI_CEC_TX_CNT);
+	writeb_relaxed(CEC_CTRL_NORMAL | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL);
+
+	return 0;
+}
+
+static irqreturn_t dw_hdmi_cec_hardirq(int irq, void *data)
+{
+	struct cec_adapter *adap = data;
+	struct dw_hdmi_cec *cec = adap->priv;
+	unsigned stat = readb_relaxed(cec->base + HDMI_IH_CEC_STAT0);
+	irqreturn_t ret = IRQ_HANDLED;
+
+	if (stat = 0)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, cec->base + HDMI_IH_CEC_STAT0);
+
+	if (stat & CEC_STAT_ERROR_INIT) {
+		if (cec->retries) {
+			unsigned v = readb_relaxed(cec->base + HDMI_CEC_CTRL);
+			writeb_relaxed(v | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL);
+			cec->retries -= 1;
+		} else {
+			cec->tx_status = CEC_TX_STATUS_MAX_RETRIES;
+			cec->tx_done = true;
+			ret = IRQ_WAKE_THREAD;
+		}
+	} else if (stat & CEC_STAT_DONE) {
+		cec->tx_status = CEC_TX_STATUS_OK;
+		cec->tx_done = true;
+		ret = IRQ_WAKE_THREAD;
+	} else if (stat & CEC_STAT_NACK) {
+		cec->tx_status = CEC_TX_STATUS_NACK;
+		cec->tx_done = true;
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	if (stat & CEC_STAT_EOM) {
+		unsigned len, i;
+		void *base = cec->base;
+
+		len = readb_relaxed(base + HDMI_CEC_RX_CNT);
+		if (len > sizeof(cec->rx_msg.msg))
+			len = sizeof(cec->rx_msg.msg);
+
+		for (i = 0; i < len; i++)
+			cec->rx_msg.msg[i] +				readb_relaxed(base + HDMI_CEC_RX_DATA0 + i);
+
+		writeb_relaxed(0, base + HDMI_CEC_LOCK);
+
+		cec->rx_msg.len = len;
+		smp_wmb();
+		cec->rx_done = true;
+
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+static irqreturn_t dw_hdmi_cec_thread(int irq, void *data)
+{
+	struct cec_adapter *adap = data;
+	struct dw_hdmi_cec *cec = adap->priv;
+
+	if (cec->tx_done) {
+		cec->tx_done = false;
+		cec_transmit_done(adap, cec->tx_status, 0, 0, 0, 0);
+	}
+	if (cec->rx_done) {
+		cec->rx_done = false;
+		smp_rmb();
+		cec_received_msg(adap, &cec->rx_msg);
+	}
+	return IRQ_HANDLED;
+}
+
+static int dw_hdmi_cec_enable(struct cec_adapter *adap, bool enable)
+{
+	struct dw_hdmi_cec *cec = adap->priv;
+
+	if (!enable) {
+		writeb_relaxed(~0, cec->base + HDMI_CEC_MASK);
+		writeb_relaxed(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+		writeb_relaxed(0, cec->base + HDMI_CEC_POLARITY);
+
+		cec->ops->disable(cec->ops_data);
+	} else {
+		unsigned irqs;
+
+		writeb_relaxed(0, cec->base + HDMI_CEC_CTRL);
+		writeb_relaxed(~0, cec->base + HDMI_IH_CEC_STAT0);
+		writeb_relaxed(0, cec->base + HDMI_CEC_LOCK);
+
+		dw_hdmi_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID);
+
+		cec->ops->enable(cec->ops_data);
+
+		irqs = CEC_STAT_ERROR_INIT | CEC_STAT_NACK | CEC_STAT_EOM |
+		       CEC_STAT_DONE;
+		writeb_relaxed(irqs, cec->base + HDMI_CEC_POLARITY);
+		writeb_relaxed(~irqs, cec->base + HDMI_CEC_MASK);
+		writeb_relaxed(~irqs, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+	}
+	return 0;
+}
+
+static const struct cec_adap_ops dw_hdmi_cec_ops = {
+	.adap_enable = dw_hdmi_cec_enable,
+	.adap_log_addr = dw_hdmi_cec_log_addr,
+	.adap_transmit = dw_hdmi_cec_transmit,
+};
+
+static unsigned int parse_hdmi_addr(const struct edid *edid)
+{
+	if (!edid || edid->extensions = 0)
+		return (u16)~0;
+
+	return cec_get_edid_phys_addr((u8 *)edid,
+				EDID_LENGTH * (edid->extensions + 1), NULL);
+}
+
+static int dw_hdmi_cec_notify(struct notifier_block *nb, unsigned long event,
+			      void *data)
+{
+	struct dw_hdmi_cec *cec = container_of(nb, struct dw_hdmi_cec, nb);
+	struct hdmi_notifier *n = data;
+	unsigned int phys;
+
+	dev_info(cec->adap->devnode.parent, "event %lu\n", event);
+
+	switch (event) {
+	case HDMI_CONNECTED:
+		break;
+
+	case HDMI_DISCONNECTED:
+		cec_s_phys_addr(cec->adap, CEC_PHYS_ADDR_INVALID, false);
+		break;
+
+	case HDMI_NEW_EDID:
+		phys = parse_hdmi_addr(n->edid);
+		cec_s_phys_addr(cec->adap, phys, false);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static void dw_hdmi_cec_del(void *data)
+{
+	struct dw_hdmi_cec *cec = data;
+
+	cec_delete_adapter(cec->adap);
+}
+
+static int dw_hdmi_cec_probe(struct platform_device *pdev)
+{
+	struct dw_hdmi_cec_data *data = dev_get_platdata(&pdev->dev);
+	struct dw_hdmi_cec *cec;
+	int ret;
+
+	if (!data)
+		return -ENXIO;
+
+	/*
+	 * Our device is just a convenience - we want to link to the real
+	 * hardware device here, so that userspace can see the association
+	 * between the HDMI hardware and its associated CEC chardev.
+	 */
+	cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
+	if (!cec)
+		return -ENOMEM;
+
+	cec->base = data->base;
+	cec->irq = data->irq;
+	cec->ops = data->ops;
+	cec->ops_data = data->ops_data;
+	cec->nb.notifier_call = dw_hdmi_cec_notify;
+
+	platform_set_drvdata(pdev, cec);
+
+	writeb_relaxed(0, cec->base + HDMI_CEC_TX_CNT);
+	writeb_relaxed(~0, cec->base + HDMI_CEC_MASK);
+	writeb_relaxed(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+	writeb_relaxed(0, cec->base + HDMI_CEC_POLARITY);
+
+	cec->adap = cec_allocate_adapter(&dw_hdmi_cec_ops, cec, "dw_hdmi",
+					 CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
+					 CEC_CAP_RC, CEC_MAX_LOG_ADDRS,
+					 pdev->dev.parent);
+	if (IS_ERR(cec->adap))
+		return PTR_ERR(cec->adap);
+
+	/* override the module pointer */
+	cec->adap->owner = THIS_MODULE;
+
+	ret = devm_add_action(&pdev->dev, dw_hdmi_cec_del, cec);
+	if (ret) {
+		cec_delete_adapter(cec->adap);
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(&pdev->dev, cec->irq,
+					dw_hdmi_cec_hardirq,
+					dw_hdmi_cec_thread, IRQF_SHARED,
+					DEV_NAME, cec->adap);
+	if (ret < 0)
+		return ret;
+
+	ret = cec_register_adapter(cec->adap);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * CEC documentation says we must not call cec_delete_adapter
+	 * after a successful call to cec_register_adapter().
+	 */
+	devm_remove_action(&pdev->dev, dw_hdmi_cec_del, cec);
+
+	cec->n = hdmi_notifier_get(cec->adap->devnode.parent);
+	if (!cec->n)
+		return -ENOMEM;
+	hdmi_notifier_register(cec->n, &cec->nb);
+
+	return 0;
+}
+
+static int dw_hdmi_cec_remove(struct platform_device *pdev)
+{
+	struct dw_hdmi_cec *cec = platform_get_drvdata(pdev);
+
+	hdmi_notifier_unregister(cec->n, &cec->nb);
+	hdmi_notifier_put(cec->n);
+	cec_unregister_adapter(cec->adap);
+
+	return 0;
+}
+
+static struct platform_driver dw_hdmi_cec_driver = {
+	.probe	= dw_hdmi_cec_probe,
+	.remove	= dw_hdmi_cec_remove,
+	.driver = {
+		.name = "dw-hdmi-cec",
+		.owner = THIS_MODULE,
+	},
+};
+module_platform_driver(dw_hdmi_cec_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI CEC driver for i.MX");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(PLATFORM_MODULE_PREFIX "dw-hdmi-cec");
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index bd02da5..06191b4 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -19,6 +19,7 @@
 #include <linux/hdmi-notifier.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
+#include <linux/platform_data/dw_hdmi-cec.h>
 #include <linux/spinlock.h>
 
 #include <drm/drm_of.h>
@@ -108,6 +109,7 @@ struct dw_hdmi {
 	struct drm_bridge *bridge;
 
 	struct platform_device *audio;
+	struct platform_device *cec;
 	enum dw_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -120,6 +122,7 @@ struct dw_hdmi {
 	int vic;
 
 	u8 edid[HDMI_EDID_LEN];
+	u8 mc_clkdis;
 	bool cable_plugin;
 
 	bool phy_enabled;
@@ -1110,8 +1113,6 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi)
 /* HDMI Initialization Step B.4 */
 static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
 {
-	u8 clkdis;
-
 	/* control period minimum duration */
 	hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
 	hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
@@ -1123,23 +1124,28 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
 	hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
 
 	/* Enable pixel clock and tmds data path */
-	clkdis = 0x7F;
-	clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE |
+			   HDMI_MC_CLKDIS_CSCCLK_DISABLE |
+			   HDMI_MC_CLKDIS_AUDCLK_DISABLE |
+			   HDMI_MC_CLKDIS_PREPCLK_DISABLE |
+			   HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 
-	clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 
 	/* Enable csc path */
 	if (is_color_space_conversion(hdmi)) {
-		clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
-		hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+		hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
+		hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 	}
 }
 
 static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
 {
-	hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 }
 
 /* Workaround to clear the overflow condition */
@@ -1299,7 +1305,6 @@ static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
 	hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK);
 	hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK);
 	hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
-	hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);
 	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT);
 	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT);
 
@@ -1640,6 +1645,27 @@ static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi)
 	return 0;
 }
 
+static void dw_hdmi_cec_enable(void *data)
+{
+	struct dw_hdmi *hdmi = data;
+
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+}
+
+static void dw_hdmi_cec_disable(void *data)
+{
+	struct dw_hdmi *hdmi = data;
+
+	hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CECCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+}
+
+static const struct dw_hdmi_cec_ops dw_hdmi_cec_ops = {
+	.enable = dw_hdmi_cec_enable,
+	.disable = dw_hdmi_cec_disable,
+};
+
 int dw_hdmi_bind(struct device *dev, struct device *master,
 		 void *data, struct drm_encoder *encoder,
 		 struct resource *iores, int irq,
@@ -1650,6 +1676,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
 	struct dw_hdmi_audio_data audio;
+	struct dw_hdmi_cec_data cec;
 	struct dw_hdmi *hdmi;
 	int ret;
 	u32 val = 1;
@@ -1668,6 +1695,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi->disabled = true;
 	hdmi->rxsense = true;
 	hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
+	hdmi->mc_clkdis = 0x7f;
 
 	mutex_init(&hdmi->mutex);
 	mutex_init(&hdmi->audio_mutex);
@@ -1800,6 +1828,18 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 		hdmi->audio = platform_device_register_full(&pdevinfo);
 	}
 
+	cec.base = hdmi->regs;
+	cec.irq = irq;
+	cec.ops = &dw_hdmi_cec_ops;
+	cec.ops_data = hdmi;
+
+	pdevinfo.name = "dw-hdmi-cec";
+	pdevinfo.data = &cec;
+	pdevinfo.size_data = sizeof(cec);
+	pdevinfo.dma_mask = 0;
+
+	hdmi->cec = platform_device_register_full(&pdevinfo);
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1821,6 +1861,8 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 
 	if (hdmi->audio && !IS_ERR(hdmi->audio))
 		platform_device_unregister(hdmi->audio);
+	if (!IS_ERR(hdmi->cec))
+		platform_device_unregister(hdmi->cec);
 
 	hdmi_notifier_put(hdmi->n);
 
diff --git a/include/linux/platform_data/dw_hdmi-cec.h b/include/linux/platform_data/dw_hdmi-cec.h
new file mode 100644
index 0000000..5ff40cc
--- /dev/null
+++ b/include/linux/platform_data/dw_hdmi-cec.h
@@ -0,0 +1,16 @@
+#ifndef DW_HDMI_CEC_H
+#define DW_HDMI_CEC_H
+
+struct dw_hdmi_cec_ops {
+	void (*enable)(void *);
+	void (*disable)(void *);
+};
+
+struct dw_hdmi_cec_data {
+	void __iomem *base;
+	int irq;
+	const struct dw_hdmi_cec_ops *ops;
+	void *ops_data;
+};
+
+#endif
-- 
2.8.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 4/5] drm/bridge: add dw-hdmi cec driver using Hans Verkuil's CEC code
@ 2016-11-14 15:22   ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-arm-kernel

From: Russell King <rmk+kernel@arm.linux.org.uk>

Add a CEC driver for the dw-hdmi hardware using Hans Verkuil's CEC
implementation.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/gpu/drm/bridge/Kconfig            |   7 +
 drivers/gpu/drm/bridge/Makefile           |   1 +
 drivers/gpu/drm/bridge/dw-hdmi-cec.c      | 346 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw-hdmi.c          |  64 +++++-
 include/linux/platform_data/dw_hdmi-cec.h |  16 ++
 5 files changed, 423 insertions(+), 11 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/dw-hdmi-cec.c
 create mode 100644 include/linux/platform_data/dw_hdmi-cec.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 5f4ebe9..4ab137e 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -40,6 +40,13 @@ config DRM_DW_HDMI_AHB_AUDIO
 	  Designware HDMI block.  This is used in conjunction with
 	  the i.MX6 HDMI driver.
 
+config DRM_DW_HDMI_CEC
+	tristate "Synopsis Designware CEC interface"
+	depends on DRM_DW_HDMI && MEDIA_CEC_SUPPORT
+	help
+	  Support the CE interface which is part of the Synopsis
+	  Designware HDMI block.
+
 config DRM_NXP_PTN3460
 	tristate "NXP PTN3460 DP/LVDS bridge"
 	depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index cdf3a3c..6bdd8b9 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
 obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
 obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
+obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
 obj-$(CONFIG_DRM_SII902X) += sii902x.o
diff --git a/drivers/gpu/drm/bridge/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/dw-hdmi-cec.c
new file mode 100644
index 0000000..e7e12b5
--- /dev/null
+++ b/drivers/gpu/drm/bridge/dw-hdmi-cec.c
@@ -0,0 +1,346 @@
+/* http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/
+ * tree/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c?h=imx_3.0.35_4.1.0 */
+#include <linux/hdmi-notifier.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_data/dw_hdmi-cec.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <drm/drm_edid.h>
+
+#include <media/cec.h>
+#include <media/cec-edid.h>
+
+#define DEV_NAME "mxc_hdmi_cec"
+
+enum {
+	HDMI_IH_CEC_STAT0	= 0x0106,
+	HDMI_IH_MUTE_CEC_STAT0	= 0x0186,
+
+	HDMI_CEC_CTRL		= 0x7d00,
+	CEC_CTRL_START		= BIT(0),
+	CEC_CTRL_NORMAL		= 1 << 1,
+
+	HDMI_CEC_STAT		= 0x7d01,
+	CEC_STAT_DONE		= BIT(0),
+	CEC_STAT_EOM		= BIT(1),
+	CEC_STAT_NACK		= BIT(2),
+	CEC_STAT_ARBLOST	= BIT(3),
+	CEC_STAT_ERROR_INIT	= BIT(4),
+	CEC_STAT_ERROR_FOLL	= BIT(5),
+	CEC_STAT_WAKEUP		= BIT(6),
+
+	HDMI_CEC_MASK		= 0x7d02,
+	HDMI_CEC_POLARITY	= 0x7d03,
+	HDMI_CEC_INT		= 0x7d04,
+	HDMI_CEC_ADDR_L		= 0x7d05,
+	HDMI_CEC_ADDR_H		= 0x7d06,
+	HDMI_CEC_TX_CNT		= 0x7d07,
+	HDMI_CEC_RX_CNT		= 0x7d08,
+	HDMI_CEC_TX_DATA0	= 0x7d10,
+	HDMI_CEC_RX_DATA0	= 0x7d20,
+	HDMI_CEC_LOCK		= 0x7d30,
+	HDMI_CEC_WKUPCTRL	= 0x7d31,
+};
+
+struct dw_hdmi_cec {
+	void __iomem *base;
+	u32 addresses;
+	struct cec_adapter *adap;
+	struct cec_msg rx_msg;
+	unsigned int tx_status;
+	bool tx_done;
+	bool rx_done;
+	const struct dw_hdmi_cec_ops *ops;
+	void *ops_data;
+	int retries;
+	int irq;
+	struct hdmi_notifier *n;
+	struct notifier_block nb;
+};
+
+static int dw_hdmi_cec_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+	struct dw_hdmi_cec *cec = adap->priv;
+	u32 addresses;
+
+	if (logical_addr == CEC_LOG_ADDR_INVALID)
+		addresses = cec->addresses = BIT(15);
+	else
+		addresses = cec->addresses |= BIT(logical_addr);
+
+	writeb_relaxed(addresses & 255, cec->base + HDMI_CEC_ADDR_L);
+	writeb_relaxed(addresses >> 8, cec->base + HDMI_CEC_ADDR_H);
+
+	return 0;
+}
+
+static int dw_hdmi_cec_transmit(struct cec_adapter *adap, u8 attempts,
+				u32 signal_free_time, struct cec_msg *msg)
+{
+	struct dw_hdmi_cec *cec = adap->priv;
+	unsigned i;
+
+	cec->retries = attempts;
+
+	for (i = 0; i < msg->len; i++)
+		writeb_relaxed(msg->msg[i], cec->base + HDMI_CEC_TX_DATA0 + i);
+
+	writeb_relaxed(msg->len, cec->base + HDMI_CEC_TX_CNT);
+	writeb_relaxed(CEC_CTRL_NORMAL | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL);
+
+	return 0;
+}
+
+static irqreturn_t dw_hdmi_cec_hardirq(int irq, void *data)
+{
+	struct cec_adapter *adap = data;
+	struct dw_hdmi_cec *cec = adap->priv;
+	unsigned stat = readb_relaxed(cec->base + HDMI_IH_CEC_STAT0);
+	irqreturn_t ret = IRQ_HANDLED;
+
+	if (stat == 0)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, cec->base + HDMI_IH_CEC_STAT0);
+
+	if (stat & CEC_STAT_ERROR_INIT) {
+		if (cec->retries) {
+			unsigned v = readb_relaxed(cec->base + HDMI_CEC_CTRL);
+			writeb_relaxed(v | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL);
+			cec->retries -= 1;
+		} else {
+			cec->tx_status = CEC_TX_STATUS_MAX_RETRIES;
+			cec->tx_done = true;
+			ret = IRQ_WAKE_THREAD;
+		}
+	} else if (stat & CEC_STAT_DONE) {
+		cec->tx_status = CEC_TX_STATUS_OK;
+		cec->tx_done = true;
+		ret = IRQ_WAKE_THREAD;
+	} else if (stat & CEC_STAT_NACK) {
+		cec->tx_status = CEC_TX_STATUS_NACK;
+		cec->tx_done = true;
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	if (stat & CEC_STAT_EOM) {
+		unsigned len, i;
+		void *base = cec->base;
+
+		len = readb_relaxed(base + HDMI_CEC_RX_CNT);
+		if (len > sizeof(cec->rx_msg.msg))
+			len = sizeof(cec->rx_msg.msg);
+
+		for (i = 0; i < len; i++)
+			cec->rx_msg.msg[i] =
+				readb_relaxed(base + HDMI_CEC_RX_DATA0 + i);
+
+		writeb_relaxed(0, base + HDMI_CEC_LOCK);
+
+		cec->rx_msg.len = len;
+		smp_wmb();
+		cec->rx_done = true;
+
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+static irqreturn_t dw_hdmi_cec_thread(int irq, void *data)
+{
+	struct cec_adapter *adap = data;
+	struct dw_hdmi_cec *cec = adap->priv;
+
+	if (cec->tx_done) {
+		cec->tx_done = false;
+		cec_transmit_done(adap, cec->tx_status, 0, 0, 0, 0);
+	}
+	if (cec->rx_done) {
+		cec->rx_done = false;
+		smp_rmb();
+		cec_received_msg(adap, &cec->rx_msg);
+	}
+	return IRQ_HANDLED;
+}
+
+static int dw_hdmi_cec_enable(struct cec_adapter *adap, bool enable)
+{
+	struct dw_hdmi_cec *cec = adap->priv;
+
+	if (!enable) {
+		writeb_relaxed(~0, cec->base + HDMI_CEC_MASK);
+		writeb_relaxed(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+		writeb_relaxed(0, cec->base + HDMI_CEC_POLARITY);
+
+		cec->ops->disable(cec->ops_data);
+	} else {
+		unsigned irqs;
+
+		writeb_relaxed(0, cec->base + HDMI_CEC_CTRL);
+		writeb_relaxed(~0, cec->base + HDMI_IH_CEC_STAT0);
+		writeb_relaxed(0, cec->base + HDMI_CEC_LOCK);
+
+		dw_hdmi_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID);
+
+		cec->ops->enable(cec->ops_data);
+
+		irqs = CEC_STAT_ERROR_INIT | CEC_STAT_NACK | CEC_STAT_EOM |
+		       CEC_STAT_DONE;
+		writeb_relaxed(irqs, cec->base + HDMI_CEC_POLARITY);
+		writeb_relaxed(~irqs, cec->base + HDMI_CEC_MASK);
+		writeb_relaxed(~irqs, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+	}
+	return 0;
+}
+
+static const struct cec_adap_ops dw_hdmi_cec_ops = {
+	.adap_enable = dw_hdmi_cec_enable,
+	.adap_log_addr = dw_hdmi_cec_log_addr,
+	.adap_transmit = dw_hdmi_cec_transmit,
+};
+
+static unsigned int parse_hdmi_addr(const struct edid *edid)
+{
+	if (!edid || edid->extensions == 0)
+		return (u16)~0;
+
+	return cec_get_edid_phys_addr((u8 *)edid,
+				EDID_LENGTH * (edid->extensions + 1), NULL);
+}
+
+static int dw_hdmi_cec_notify(struct notifier_block *nb, unsigned long event,
+			      void *data)
+{
+	struct dw_hdmi_cec *cec = container_of(nb, struct dw_hdmi_cec, nb);
+	struct hdmi_notifier *n = data;
+	unsigned int phys;
+
+	dev_info(cec->adap->devnode.parent, "event %lu\n", event);
+
+	switch (event) {
+	case HDMI_CONNECTED:
+		break;
+
+	case HDMI_DISCONNECTED:
+		cec_s_phys_addr(cec->adap, CEC_PHYS_ADDR_INVALID, false);
+		break;
+
+	case HDMI_NEW_EDID:
+		phys = parse_hdmi_addr(n->edid);
+		cec_s_phys_addr(cec->adap, phys, false);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static void dw_hdmi_cec_del(void *data)
+{
+	struct dw_hdmi_cec *cec = data;
+
+	cec_delete_adapter(cec->adap);
+}
+
+static int dw_hdmi_cec_probe(struct platform_device *pdev)
+{
+	struct dw_hdmi_cec_data *data = dev_get_platdata(&pdev->dev);
+	struct dw_hdmi_cec *cec;
+	int ret;
+
+	if (!data)
+		return -ENXIO;
+
+	/*
+	 * Our device is just a convenience - we want to link to the real
+	 * hardware device here, so that userspace can see the association
+	 * between the HDMI hardware and its associated CEC chardev.
+	 */
+	cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
+	if (!cec)
+		return -ENOMEM;
+
+	cec->base = data->base;
+	cec->irq = data->irq;
+	cec->ops = data->ops;
+	cec->ops_data = data->ops_data;
+	cec->nb.notifier_call = dw_hdmi_cec_notify;
+
+	platform_set_drvdata(pdev, cec);
+
+	writeb_relaxed(0, cec->base + HDMI_CEC_TX_CNT);
+	writeb_relaxed(~0, cec->base + HDMI_CEC_MASK);
+	writeb_relaxed(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+	writeb_relaxed(0, cec->base + HDMI_CEC_POLARITY);
+
+	cec->adap = cec_allocate_adapter(&dw_hdmi_cec_ops, cec, "dw_hdmi",
+					 CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
+					 CEC_CAP_RC, CEC_MAX_LOG_ADDRS,
+					 pdev->dev.parent);
+	if (IS_ERR(cec->adap))
+		return PTR_ERR(cec->adap);
+
+	/* override the module pointer */
+	cec->adap->owner = THIS_MODULE;
+
+	ret = devm_add_action(&pdev->dev, dw_hdmi_cec_del, cec);
+	if (ret) {
+		cec_delete_adapter(cec->adap);
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(&pdev->dev, cec->irq,
+					dw_hdmi_cec_hardirq,
+					dw_hdmi_cec_thread, IRQF_SHARED,
+					DEV_NAME, cec->adap);
+	if (ret < 0)
+		return ret;
+
+	ret = cec_register_adapter(cec->adap);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * CEC documentation says we must not call cec_delete_adapter
+	 * after a successful call to cec_register_adapter().
+	 */
+	devm_remove_action(&pdev->dev, dw_hdmi_cec_del, cec);
+
+	cec->n = hdmi_notifier_get(cec->adap->devnode.parent);
+	if (!cec->n)
+		return -ENOMEM;
+	hdmi_notifier_register(cec->n, &cec->nb);
+
+	return 0;
+}
+
+static int dw_hdmi_cec_remove(struct platform_device *pdev)
+{
+	struct dw_hdmi_cec *cec = platform_get_drvdata(pdev);
+
+	hdmi_notifier_unregister(cec->n, &cec->nb);
+	hdmi_notifier_put(cec->n);
+	cec_unregister_adapter(cec->adap);
+
+	return 0;
+}
+
+static struct platform_driver dw_hdmi_cec_driver = {
+	.probe	= dw_hdmi_cec_probe,
+	.remove	= dw_hdmi_cec_remove,
+	.driver = {
+		.name = "dw-hdmi-cec",
+		.owner = THIS_MODULE,
+	},
+};
+module_platform_driver(dw_hdmi_cec_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI CEC driver for i.MX");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(PLATFORM_MODULE_PREFIX "dw-hdmi-cec");
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index bd02da5..06191b4 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -19,6 +19,7 @@
 #include <linux/hdmi-notifier.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
+#include <linux/platform_data/dw_hdmi-cec.h>
 #include <linux/spinlock.h>
 
 #include <drm/drm_of.h>
@@ -108,6 +109,7 @@ struct dw_hdmi {
 	struct drm_bridge *bridge;
 
 	struct platform_device *audio;
+	struct platform_device *cec;
 	enum dw_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -120,6 +122,7 @@ struct dw_hdmi {
 	int vic;
 
 	u8 edid[HDMI_EDID_LEN];
+	u8 mc_clkdis;
 	bool cable_plugin;
 
 	bool phy_enabled;
@@ -1110,8 +1113,6 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi)
 /* HDMI Initialization Step B.4 */
 static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
 {
-	u8 clkdis;
-
 	/* control period minimum duration */
 	hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
 	hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
@@ -1123,23 +1124,28 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
 	hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
 
 	/* Enable pixel clock and tmds data path */
-	clkdis = 0x7F;
-	clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE |
+			   HDMI_MC_CLKDIS_CSCCLK_DISABLE |
+			   HDMI_MC_CLKDIS_AUDCLK_DISABLE |
+			   HDMI_MC_CLKDIS_PREPCLK_DISABLE |
+			   HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 
-	clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 
 	/* Enable csc path */
 	if (is_color_space_conversion(hdmi)) {
-		clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
-		hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+		hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
+		hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 	}
 }
 
 static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
 {
-	hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 }
 
 /* Workaround to clear the overflow condition */
@@ -1299,7 +1305,6 @@ static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
 	hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK);
 	hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK);
 	hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
-	hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);
 	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT);
 	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT);
 
@@ -1640,6 +1645,27 @@ static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi)
 	return 0;
 }
 
+static void dw_hdmi_cec_enable(void *data)
+{
+	struct dw_hdmi *hdmi = data;
+
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+}
+
+static void dw_hdmi_cec_disable(void *data)
+{
+	struct dw_hdmi *hdmi = data;
+
+	hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CECCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+}
+
+static const struct dw_hdmi_cec_ops dw_hdmi_cec_ops = {
+	.enable = dw_hdmi_cec_enable,
+	.disable = dw_hdmi_cec_disable,
+};
+
 int dw_hdmi_bind(struct device *dev, struct device *master,
 		 void *data, struct drm_encoder *encoder,
 		 struct resource *iores, int irq,
@@ -1650,6 +1676,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
 	struct dw_hdmi_audio_data audio;
+	struct dw_hdmi_cec_data cec;
 	struct dw_hdmi *hdmi;
 	int ret;
 	u32 val = 1;
@@ -1668,6 +1695,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi->disabled = true;
 	hdmi->rxsense = true;
 	hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
+	hdmi->mc_clkdis = 0x7f;
 
 	mutex_init(&hdmi->mutex);
 	mutex_init(&hdmi->audio_mutex);
@@ -1800,6 +1828,18 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 		hdmi->audio = platform_device_register_full(&pdevinfo);
 	}
 
+	cec.base = hdmi->regs;
+	cec.irq = irq;
+	cec.ops = &dw_hdmi_cec_ops;
+	cec.ops_data = hdmi;
+
+	pdevinfo.name = "dw-hdmi-cec";
+	pdevinfo.data = &cec;
+	pdevinfo.size_data = sizeof(cec);
+	pdevinfo.dma_mask = 0;
+
+	hdmi->cec = platform_device_register_full(&pdevinfo);
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1821,6 +1861,8 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
 
 	if (hdmi->audio && !IS_ERR(hdmi->audio))
 		platform_device_unregister(hdmi->audio);
+	if (!IS_ERR(hdmi->cec))
+		platform_device_unregister(hdmi->cec);
 
 	hdmi_notifier_put(hdmi->n);
 
diff --git a/include/linux/platform_data/dw_hdmi-cec.h b/include/linux/platform_data/dw_hdmi-cec.h
new file mode 100644
index 0000000..5ff40cc
--- /dev/null
+++ b/include/linux/platform_data/dw_hdmi-cec.h
@@ -0,0 +1,16 @@
+#ifndef DW_HDMI_CEC_H
+#define DW_HDMI_CEC_H
+
+struct dw_hdmi_cec_ops {
+	void (*enable)(void *);
+	void (*disable)(void *);
+};
+
+struct dw_hdmi_cec_data {
+	void __iomem *base;
+	int irq;
+	const struct dw_hdmi_cec_ops *ops;
+	void *ops_data;
+};
+
+#endif
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 5/5] drm/i2c: add tda998x/tda9950 CEC driver
  2016-11-14 15:22 ` Hans Verkuil
  (?)
@ 2016-11-14 15:22   ` Hans Verkuil
  -1 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-media
  Cc: Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Russell King

From: Russell King <rmk+kernel@armlinux.org.uk>

Add a CEC driver for the TDA9950, which is a stand-alone I2C CEC device.
The TDA9950 contains a command processor which handles retransmissions
and the low level bus protocol.  The driver just has to read and write
the messages, and handle error conditions.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/gpu/drm/i2c/Kconfig           |   5 +
 drivers/gpu/drm/i2c/Makefile          |   1 +
 drivers/gpu/drm/i2c/tda9950.c         | 516 ++++++++++++++++++++++++++++++++++
 include/linux/platform_data/tda9950.h |  15 +
 4 files changed, 537 insertions(+)
 create mode 100644 drivers/gpu/drm/i2c/tda9950.c
 create mode 100644 include/linux/platform_data/tda9950.h

diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
index a6c92be..b30ea3b 100644
--- a/drivers/gpu/drm/i2c/Kconfig
+++ b/drivers/gpu/drm/i2c/Kconfig
@@ -26,4 +26,9 @@ config DRM_I2C_NXP_TDA998X
 	help
 	  Support for NXP Semiconductors TDA998X HDMI encoders.
 
+config DRM_I2C_NXP_TDA9950
+	tristate "NXP Semiconductors TDA9950/TDA998X HDMI CEC"
+	depends on MEDIA_CEC_SUPPORT
+	select HDMI_NOTIFIERS
+
 endmenu
diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
index 43aa33b..8a6e13e 100644
--- a/drivers/gpu/drm/i2c/Makefile
+++ b/drivers/gpu/drm/i2c/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
 
 tda998x-y := tda998x_drv.o
 obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
+obj-$(CONFIG_DRM_I2C_NXP_TDA9950) += tda9950.o
diff --git a/drivers/gpu/drm/i2c/tda9950.c b/drivers/gpu/drm/i2c/tda9950.c
new file mode 100644
index 0000000..601da7c
--- /dev/null
+++ b/drivers/gpu/drm/i2c/tda9950.c
@@ -0,0 +1,516 @@
+/*
+ *  TDA9950 Consumer Electronics Control driver
+ *
+ * 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.
+ *
+ * The NXP TDA9950 implements the HDMI Consumer Electronics Control
+ * interface.  The host interface is similar to a mailbox: the data
+ * registers starting at REG_CDR0 are written to send a command to the
+ * internal CPU, and replies are read from these registers.
+ *
+ * As the data registers represent a mailbox, they must be accessed
+ * as a single I2C transaction.  See the TDA9950 data sheet for details.
+ */
+#include <linux/delay.h>
+#include <linux/hdmi-notifier.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_data/tda9950.h>
+#include <linux/slab.h>
+#include <drm/drm_edid.h>
+#include <media/cec.h>
+
+enum {
+	REG_CSR = 0x00,
+	CSR_BUSY = BIT(7),
+	CSR_INT  = BIT(6),
+	CSR_ERR  = BIT(5),
+
+	REG_CER = 0x01,
+
+	REG_CVR = 0x02,
+
+	REG_CCR = 0x03,
+	CCR_RESET = BIT(7),
+	CCR_ON    = BIT(6),
+
+	REG_ACKH = 0x04,
+	REG_ACKL = 0x05,
+
+	REG_CCONR = 0x06,
+	CCONR_ENABLE_ERROR = BIT(4),
+
+	REG_CDR0 = 0x07,
+
+	CDR1_REQ = 0x00,
+	CDR1_CNF = 0x01,
+	CDR1_IND = 0x81,
+	CDR1_ERR = 0x82,
+	CDR1_IER = 0x83,
+
+	CDR2_CNF_SUCCESS    = 0x00,
+	CDR2_CNF_OFF_STATE  = 0x80,
+	CDR2_CNF_BAD_REQ    = 0x81,
+	CDR2_CNF_CEC_ACCESS = 0x82,
+	CDR2_CNF_ARB_ERROR  = 0x83,
+	CDR2_CNF_BAD_TIMING = 0x84,
+	CDR2_CNF_NACK_ADDR  = 0x85,
+	CDR2_CNF_NACK_DATA  = 0x86,
+};
+
+struct tda9950_priv {
+	struct i2c_client *client;
+	struct device *hdmi;
+	struct cec_adapter *adap;
+	struct tda9950_glue *glue;
+	u16 addresses;
+	struct cec_msg rx_msg;
+	struct hdmi_notifier *n;
+	struct notifier_block nb;
+	bool open;
+};
+
+static int tda9950_write_range(struct i2c_client *client, u8 addr, u8 *p, int cnt)
+{
+	struct i2c_msg msg;
+	u8 buf[cnt + 1];
+	int ret;
+
+	buf[0] = addr;
+	memcpy(buf + 1, p, cnt);
+
+	msg.addr = client->addr;
+	msg.flags = 0;
+	msg.len = cnt + 1;
+	msg.buf = buf;
+
+	dev_dbg(&client->dev, "wr 0x%02x: %*ph\n", addr, cnt, p);
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		dev_err(&client->dev, "Error %d writing to cec:0x%x\n", ret, addr);
+	return ret < 0 ? ret : 0;
+}
+
+static void tda9950_write(struct i2c_client *client, u8 addr, u8 val)
+{
+	tda9950_write_range(client, addr, &val, 1);
+}
+
+static int tda9950_read_range(struct i2c_client *client, u8 addr, u8 *p, int cnt)
+{
+	struct i2c_msg msg[2];
+	int ret;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = &addr;
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = cnt;
+	msg[1].buf = p;
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret < 0)
+		dev_err(&client->dev, "Error %d reading from cec:0x%x\n", ret, addr);
+
+	dev_dbg(&client->dev, "rd 0x%02x: %*ph\n", addr, cnt, p);
+
+	return ret;
+}
+
+static u8 tda9950_read(struct i2c_client *client, u8 addr)
+{
+	int ret;
+	u8 val;
+
+	ret = tda9950_read_range(client, addr, &val, 1);
+	if (ret < 0)
+		val = 0;
+
+	return val;
+}
+
+static irqreturn_t tda9950_irq(int irq, void *data)
+{
+	struct tda9950_priv *priv = data;
+	unsigned int tx_status;
+	u8 csr, buf[19];
+
+	if (!priv->open)
+		return IRQ_NONE;
+
+	csr = tda9950_read(priv->client, REG_CSR);
+	if (!(csr & CSR_INT))
+		return IRQ_NONE;
+
+	tda9950_read_range(priv->client, REG_CDR0, buf, sizeof(buf));
+
+	/*
+	 * This should never happen: the data sheet says that there will
+	 * always be a valid message if the interrupt line is asserted.
+	 */
+	if (buf[0] == 0) {
+		dev_warn(&priv->client->dev, "interrupt pending, but no message?\n");
+		return IRQ_NONE;
+	}
+
+	switch (buf[1]) {
+	case CDR1_CNF: /* transmit result */
+		switch (buf[2]) {
+		case CDR2_CNF_SUCCESS:
+			tx_status = CEC_TX_STATUS_OK;
+			break;
+
+		case CDR2_CNF_NACK_ADDR:
+			tx_status = CEC_TX_STATUS_NACK;
+			break;
+
+		default: /* some other error, refer to TDA9950 docs */
+			dev_err(&priv->client->dev, "CNF reply error 0x%02x\n",
+				buf[2]);
+			tx_status = CEC_TX_STATUS_ERROR;
+			break;
+		}
+		cec_transmit_done(priv->adap, tx_status, 0, 0, 0, 0);
+		break;
+
+	case CDR1_IND:
+		priv->rx_msg.len = buf[0] - 2;
+		memcpy(priv->rx_msg.msg, buf + 2, priv->rx_msg.len);
+		cec_received_msg(priv->adap, &priv->rx_msg);
+		break;
+
+	default: /* unknown */
+		dev_err(&priv->client->dev, "unknown service id 0x%02x\n",
+			buf[1]);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int tda9950_cec_transmit(struct cec_adapter *adap, u8 attempts,
+				u32 signal_free_time, struct cec_msg *msg)
+{
+	struct tda9950_priv *priv = adap->priv;
+	u8 buf[16 + 2];
+
+	buf[0] = 2 + msg->len;
+	buf[1] = CDR1_REQ;
+	memcpy(buf + 2, msg->msg, msg->len);
+
+	return tda9950_write_range(priv->client, REG_CDR0, buf, 2 + msg->len);
+}
+
+static int tda9950_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+	struct tda9950_priv *priv = adap->priv;
+	u16 addresses;
+	u8 buf[2];
+
+	if (addr == CEC_LOG_ADDR_INVALID)
+		addresses = priv->addresses = BIT(15);
+	else
+		addresses = priv->addresses |= BIT(addr);
+
+	/* TDA9950 doesn't want address 15 set */
+	addr &= 0x7fff;
+	buf[0] = addresses >> 8;
+	buf[1] = addresses;
+
+	return tda9950_write_range(priv->client, REG_ACKH, buf, 2);
+}
+
+/*
+ * When operating as part of the TDA998x, we need additional handling
+ * to initialise and shut down the TDA9950 part of the device.  These
+ * two hooks are provided to allow the TDA998x code to perform those
+ * activities.
+ */
+static int tda9950_glue_open(struct tda9950_priv *priv)
+{
+	int ret = 0;
+
+	if (priv->glue && priv->glue->open)
+		ret = priv->glue->open(priv->glue->data);
+
+	priv->open = true;
+
+	return ret;
+}
+
+static void tda9950_glue_release(struct tda9950_priv *priv)
+{
+	priv->open = false;
+
+	if (priv->glue && priv->glue->release)
+		priv->glue->release(priv->glue->data);
+}
+
+static int tda9950_open(struct tda9950_priv *priv)
+{
+	struct i2c_client *client = priv->client;
+	int ret;
+
+	ret = tda9950_glue_open(priv);
+	if (ret)
+		return ret;
+
+	/* Reset the TDA9950, and wait 250ms for it to recover */
+	tda9950_write(client, REG_CCR, CCR_RESET);
+	msleep(250);
+
+	/* Configure for the standard 5 retries */
+	tda9950_write(client, REG_CCONR, 5);
+	tda9950_cec_adap_log_addr(priv->adap, CEC_LOG_ADDR_INVALID);
+
+	/* Start the command processor */
+	tda9950_write(client, REG_CCR, CCR_ON);
+
+	return 0;
+}
+
+static void tda9950_release(struct tda9950_priv *priv)
+{
+	struct i2c_client *client = priv->client;
+	int timeout = 50;
+	u8 csr;
+
+	/* Stop the command processor */
+	tda9950_write(client, REG_CCR, 0);
+
+	/* Wait up to .5s for it to signal non-busy */
+	do {
+		csr = tda9950_read(client, REG_CSR);
+		if (!(csr & CSR_BUSY) || --timeout)
+			break;
+		msleep(10);
+	} while (1);
+
+	/* Warn the user that their IRQ may die if it's shared. */
+	if (csr & CSR_BUSY)
+		dev_warn(&client->dev, "command processor failed to stop, irq%d may die (csr=0x%02x)\n",
+			 client->irq, csr);
+
+	tda9950_glue_release(priv);
+}
+
+static int tda9950_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+	struct tda9950_priv *priv = adap->priv;
+
+	if (!enable) {
+		tda9950_release(priv);
+		return 0;
+	} else {
+		return tda9950_open(priv);
+	}
+}
+
+static const struct cec_adap_ops tda9950_cec_ops = {
+	.adap_enable = tda9950_cec_adap_enable,
+	.adap_log_addr = tda9950_cec_adap_log_addr,
+	.adap_transmit = tda9950_cec_transmit,
+};
+
+static unsigned int parse_hdmi_addr(const struct edid *edid)
+{
+	if (!edid || edid->extensions == 0)
+		return (u16)~0;
+
+	return cec_get_edid_phys_addr((u8 *)edid,
+				EDID_LENGTH * (edid->extensions + 1), NULL);
+}
+
+static int tda9950_cec_notify(struct notifier_block *nb, unsigned long event,
+			      void *data)
+{
+	struct tda9950_priv *priv = container_of(nb, struct tda9950_priv, nb);
+	struct hdmi_notifier *n = data;
+	unsigned int phys;
+
+	switch (event) {
+	case HDMI_DISCONNECTED:
+		cec_s_phys_addr(priv->adap, CEC_PHYS_ADDR_INVALID, false);
+		break;
+
+	case HDMI_NEW_EDID:
+		phys = parse_hdmi_addr(n->edid);
+		cec_s_phys_addr(priv->adap, phys, false);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+/*
+ * When operating as part of the TDA998x, we need to claim additional
+ * resources.  These two hooks permit the management of those resources.
+ */
+static void tda9950_devm_glue_exit(void *data)
+{
+	struct tda9950_glue *glue = data;
+
+	if (glue && glue->exit)
+		glue->exit(glue->data);
+}
+
+static int tda9950_devm_glue_init(struct device *dev, struct tda9950_glue *glue)
+{
+	int ret;
+
+	if (glue && glue->init) {
+		ret = glue->init(glue->data);
+		if (ret)
+			return ret;
+	}
+
+	ret = devm_add_action(dev, tda9950_devm_glue_exit, glue);
+	if (ret)
+		tda9950_devm_glue_exit(glue);
+
+	return ret;
+}
+
+static void tda9950_cec_del(void *data)
+{
+	struct tda9950_priv *priv = data;
+
+	cec_delete_adapter(priv->adap);
+}
+
+static int tda9950_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct tda9950_glue *glue = client->dev.platform_data;
+	struct device *dev = &client->dev;
+	struct tda9950_priv *priv;
+	int ret;
+	u8 cvr;
+
+	/*
+	 * We must have I2C functionality: our multi-byte accesses
+	 * must be performed as a single contiguous transaction.
+	 */
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"adapter does not support I2C functionality\n");
+		return -ENXIO;
+	}
+
+	/* We must have an interrupt to be functional. */
+	if (client->irq <= 0) {
+		dev_err(&client->dev, "driver requires an interrupt\n");
+		return -ENXIO;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->client = client;
+	priv->glue = glue;
+	priv->nb.notifier_call = tda9950_cec_notify;
+
+	i2c_set_clientdata(client, priv);
+
+	/*
+	 * If we're part of a TDA998x, we want the class devices to be
+	 * associated with the HDMI Tx so we have a tight relationship
+	 * between the HDMI interface and the CEC interface.
+	 */
+	priv->hdmi = dev;
+	if (glue && glue->parent)
+		priv->hdmi = glue->parent;
+
+	priv->adap = cec_allocate_adapter(&tda9950_cec_ops, priv, "tda9950",
+					  CEC_CAP_LOG_ADDRS |
+					  CEC_CAP_TRANSMIT | CEC_CAP_RC,
+					  CEC_MAX_LOG_ADDRS, priv->hdmi);
+	if (IS_ERR(priv->adap))
+		return PTR_ERR(priv->adap);
+
+	ret = devm_add_action(dev, tda9950_cec_del, priv);
+	if (ret) {
+		cec_delete_adapter(priv->adap);
+		return ret;
+	}
+
+	ret = tda9950_devm_glue_init(dev, glue);
+	if (ret)
+		return ret;
+
+	ret = tda9950_glue_open(priv);
+	if (ret)
+		return ret;
+
+	cvr = tda9950_read(client, REG_CVR);
+
+	dev_info(&client->dev,
+		 "TDA9950 CEC interface, hardware version %u.%u\n",
+		 cvr >> 4, cvr & 15);
+
+	tda9950_glue_release(priv);
+
+	ret = devm_request_threaded_irq(dev, client->irq, NULL, tda9950_irq,
+					IRQF_TRIGGER_FALLING | IRQF_SHARED |
+					IRQF_ONESHOT,
+					dev_name(&client->dev), priv);
+	if (ret < 0)
+		return ret;
+
+	ret = cec_register_adapter(priv->adap);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * CEC documentation says we must not call cec_delete_adapter
+	 * after a successful call to cec_register_adapter().
+	 */
+	devm_remove_action(dev, tda9950_cec_del, priv);
+
+	priv->n = hdmi_notifier_get(priv->hdmi);
+	if (!priv->n)
+		return -ENOMEM;
+	hdmi_notifier_register(priv->n, &priv->nb);
+
+	return ret;
+}
+
+static int tda9950_remove(struct i2c_client *client)
+{
+	struct tda9950_priv *priv = i2c_get_clientdata(client);
+
+	hdmi_notifier_unregister(priv->n, &priv->nb);
+	hdmi_notifier_put(priv->n);
+	cec_unregister_adapter(priv->adap);
+
+	return 0;
+}
+
+static struct i2c_device_id tda9950_ids[] = {
+	{ "tda9950", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, tda9950_ids);
+
+static struct i2c_driver tda9950_driver = {
+	.probe = tda9950_probe,
+	.remove = tda9950_remove,
+	.driver = {
+		.name = "tda9950",
+	},
+	.id_table = tda9950_ids,
+};
+
+module_i2c_driver(tda9950_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@armlinux.org.uk>");
+MODULE_DESCRIPTION("TDA9950/TDA998x Consumer Electronics Control Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/platform_data/tda9950.h b/include/linux/platform_data/tda9950.h
new file mode 100644
index 0000000..08da9c9
--- /dev/null
+++ b/include/linux/platform_data/tda9950.h
@@ -0,0 +1,15 @@
+#ifndef LINUX_PLATFORM_DATA_TDA9950_H
+#define LINUX_PLATFORM_DATA_TDA9950_H
+
+struct device;
+
+struct tda9950_glue {
+	struct device *parent;
+	void *data;
+	int (*init)(void *);
+	void (*exit)(void *);
+	int (*open)(void *);
+	void (*release)(void *);
+};
+
+#endif
-- 
2.8.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 5/5] drm/i2c: add tda998x/tda9950 CEC driver
@ 2016-11-14 15:22   ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-media
  Cc: Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Russell King

From: Russell King <rmk+kernel@armlinux.org.uk>

Add a CEC driver for the TDA9950, which is a stand-alone I2C CEC device.
The TDA9950 contains a command processor which handles retransmissions
and the low level bus protocol.  The driver just has to read and write
the messages, and handle error conditions.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/gpu/drm/i2c/Kconfig           |   5 +
 drivers/gpu/drm/i2c/Makefile          |   1 +
 drivers/gpu/drm/i2c/tda9950.c         | 516 ++++++++++++++++++++++++++++++++++
 include/linux/platform_data/tda9950.h |  15 +
 4 files changed, 537 insertions(+)
 create mode 100644 drivers/gpu/drm/i2c/tda9950.c
 create mode 100644 include/linux/platform_data/tda9950.h

diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
index a6c92be..b30ea3b 100644
--- a/drivers/gpu/drm/i2c/Kconfig
+++ b/drivers/gpu/drm/i2c/Kconfig
@@ -26,4 +26,9 @@ config DRM_I2C_NXP_TDA998X
 	help
 	  Support for NXP Semiconductors TDA998X HDMI encoders.
 
+config DRM_I2C_NXP_TDA9950
+	tristate "NXP Semiconductors TDA9950/TDA998X HDMI CEC"
+	depends on MEDIA_CEC_SUPPORT
+	select HDMI_NOTIFIERS
+
 endmenu
diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
index 43aa33b..8a6e13e 100644
--- a/drivers/gpu/drm/i2c/Makefile
+++ b/drivers/gpu/drm/i2c/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
 
 tda998x-y := tda998x_drv.o
 obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
+obj-$(CONFIG_DRM_I2C_NXP_TDA9950) += tda9950.o
diff --git a/drivers/gpu/drm/i2c/tda9950.c b/drivers/gpu/drm/i2c/tda9950.c
new file mode 100644
index 0000000..601da7c
--- /dev/null
+++ b/drivers/gpu/drm/i2c/tda9950.c
@@ -0,0 +1,516 @@
+/*
+ *  TDA9950 Consumer Electronics Control driver
+ *
+ * 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.
+ *
+ * The NXP TDA9950 implements the HDMI Consumer Electronics Control
+ * interface.  The host interface is similar to a mailbox: the data
+ * registers starting at REG_CDR0 are written to send a command to the
+ * internal CPU, and replies are read from these registers.
+ *
+ * As the data registers represent a mailbox, they must be accessed
+ * as a single I2C transaction.  See the TDA9950 data sheet for details.
+ */
+#include <linux/delay.h>
+#include <linux/hdmi-notifier.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_data/tda9950.h>
+#include <linux/slab.h>
+#include <drm/drm_edid.h>
+#include <media/cec.h>
+
+enum {
+	REG_CSR = 0x00,
+	CSR_BUSY = BIT(7),
+	CSR_INT  = BIT(6),
+	CSR_ERR  = BIT(5),
+
+	REG_CER = 0x01,
+
+	REG_CVR = 0x02,
+
+	REG_CCR = 0x03,
+	CCR_RESET = BIT(7),
+	CCR_ON    = BIT(6),
+
+	REG_ACKH = 0x04,
+	REG_ACKL = 0x05,
+
+	REG_CCONR = 0x06,
+	CCONR_ENABLE_ERROR = BIT(4),
+
+	REG_CDR0 = 0x07,
+
+	CDR1_REQ = 0x00,
+	CDR1_CNF = 0x01,
+	CDR1_IND = 0x81,
+	CDR1_ERR = 0x82,
+	CDR1_IER = 0x83,
+
+	CDR2_CNF_SUCCESS    = 0x00,
+	CDR2_CNF_OFF_STATE  = 0x80,
+	CDR2_CNF_BAD_REQ    = 0x81,
+	CDR2_CNF_CEC_ACCESS = 0x82,
+	CDR2_CNF_ARB_ERROR  = 0x83,
+	CDR2_CNF_BAD_TIMING = 0x84,
+	CDR2_CNF_NACK_ADDR  = 0x85,
+	CDR2_CNF_NACK_DATA  = 0x86,
+};
+
+struct tda9950_priv {
+	struct i2c_client *client;
+	struct device *hdmi;
+	struct cec_adapter *adap;
+	struct tda9950_glue *glue;
+	u16 addresses;
+	struct cec_msg rx_msg;
+	struct hdmi_notifier *n;
+	struct notifier_block nb;
+	bool open;
+};
+
+static int tda9950_write_range(struct i2c_client *client, u8 addr, u8 *p, int cnt)
+{
+	struct i2c_msg msg;
+	u8 buf[cnt + 1];
+	int ret;
+
+	buf[0] = addr;
+	memcpy(buf + 1, p, cnt);
+
+	msg.addr = client->addr;
+	msg.flags = 0;
+	msg.len = cnt + 1;
+	msg.buf = buf;
+
+	dev_dbg(&client->dev, "wr 0x%02x: %*ph\n", addr, cnt, p);
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		dev_err(&client->dev, "Error %d writing to cec:0x%x\n", ret, addr);
+	return ret < 0 ? ret : 0;
+}
+
+static void tda9950_write(struct i2c_client *client, u8 addr, u8 val)
+{
+	tda9950_write_range(client, addr, &val, 1);
+}
+
+static int tda9950_read_range(struct i2c_client *client, u8 addr, u8 *p, int cnt)
+{
+	struct i2c_msg msg[2];
+	int ret;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = &addr;
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = cnt;
+	msg[1].buf = p;
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret < 0)
+		dev_err(&client->dev, "Error %d reading from cec:0x%x\n", ret, addr);
+
+	dev_dbg(&client->dev, "rd 0x%02x: %*ph\n", addr, cnt, p);
+
+	return ret;
+}
+
+static u8 tda9950_read(struct i2c_client *client, u8 addr)
+{
+	int ret;
+	u8 val;
+
+	ret = tda9950_read_range(client, addr, &val, 1);
+	if (ret < 0)
+		val = 0;
+
+	return val;
+}
+
+static irqreturn_t tda9950_irq(int irq, void *data)
+{
+	struct tda9950_priv *priv = data;
+	unsigned int tx_status;
+	u8 csr, buf[19];
+
+	if (!priv->open)
+		return IRQ_NONE;
+
+	csr = tda9950_read(priv->client, REG_CSR);
+	if (!(csr & CSR_INT))
+		return IRQ_NONE;
+
+	tda9950_read_range(priv->client, REG_CDR0, buf, sizeof(buf));
+
+	/*
+	 * This should never happen: the data sheet says that there will
+	 * always be a valid message if the interrupt line is asserted.
+	 */
+	if (buf[0] = 0) {
+		dev_warn(&priv->client->dev, "interrupt pending, but no message?\n");
+		return IRQ_NONE;
+	}
+
+	switch (buf[1]) {
+	case CDR1_CNF: /* transmit result */
+		switch (buf[2]) {
+		case CDR2_CNF_SUCCESS:
+			tx_status = CEC_TX_STATUS_OK;
+			break;
+
+		case CDR2_CNF_NACK_ADDR:
+			tx_status = CEC_TX_STATUS_NACK;
+			break;
+
+		default: /* some other error, refer to TDA9950 docs */
+			dev_err(&priv->client->dev, "CNF reply error 0x%02x\n",
+				buf[2]);
+			tx_status = CEC_TX_STATUS_ERROR;
+			break;
+		}
+		cec_transmit_done(priv->adap, tx_status, 0, 0, 0, 0);
+		break;
+
+	case CDR1_IND:
+		priv->rx_msg.len = buf[0] - 2;
+		memcpy(priv->rx_msg.msg, buf + 2, priv->rx_msg.len);
+		cec_received_msg(priv->adap, &priv->rx_msg);
+		break;
+
+	default: /* unknown */
+		dev_err(&priv->client->dev, "unknown service id 0x%02x\n",
+			buf[1]);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int tda9950_cec_transmit(struct cec_adapter *adap, u8 attempts,
+				u32 signal_free_time, struct cec_msg *msg)
+{
+	struct tda9950_priv *priv = adap->priv;
+	u8 buf[16 + 2];
+
+	buf[0] = 2 + msg->len;
+	buf[1] = CDR1_REQ;
+	memcpy(buf + 2, msg->msg, msg->len);
+
+	return tda9950_write_range(priv->client, REG_CDR0, buf, 2 + msg->len);
+}
+
+static int tda9950_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+	struct tda9950_priv *priv = adap->priv;
+	u16 addresses;
+	u8 buf[2];
+
+	if (addr = CEC_LOG_ADDR_INVALID)
+		addresses = priv->addresses = BIT(15);
+	else
+		addresses = priv->addresses |= BIT(addr);
+
+	/* TDA9950 doesn't want address 15 set */
+	addr &= 0x7fff;
+	buf[0] = addresses >> 8;
+	buf[1] = addresses;
+
+	return tda9950_write_range(priv->client, REG_ACKH, buf, 2);
+}
+
+/*
+ * When operating as part of the TDA998x, we need additional handling
+ * to initialise and shut down the TDA9950 part of the device.  These
+ * two hooks are provided to allow the TDA998x code to perform those
+ * activities.
+ */
+static int tda9950_glue_open(struct tda9950_priv *priv)
+{
+	int ret = 0;
+
+	if (priv->glue && priv->glue->open)
+		ret = priv->glue->open(priv->glue->data);
+
+	priv->open = true;
+
+	return ret;
+}
+
+static void tda9950_glue_release(struct tda9950_priv *priv)
+{
+	priv->open = false;
+
+	if (priv->glue && priv->glue->release)
+		priv->glue->release(priv->glue->data);
+}
+
+static int tda9950_open(struct tda9950_priv *priv)
+{
+	struct i2c_client *client = priv->client;
+	int ret;
+
+	ret = tda9950_glue_open(priv);
+	if (ret)
+		return ret;
+
+	/* Reset the TDA9950, and wait 250ms for it to recover */
+	tda9950_write(client, REG_CCR, CCR_RESET);
+	msleep(250);
+
+	/* Configure for the standard 5 retries */
+	tda9950_write(client, REG_CCONR, 5);
+	tda9950_cec_adap_log_addr(priv->adap, CEC_LOG_ADDR_INVALID);
+
+	/* Start the command processor */
+	tda9950_write(client, REG_CCR, CCR_ON);
+
+	return 0;
+}
+
+static void tda9950_release(struct tda9950_priv *priv)
+{
+	struct i2c_client *client = priv->client;
+	int timeout = 50;
+	u8 csr;
+
+	/* Stop the command processor */
+	tda9950_write(client, REG_CCR, 0);
+
+	/* Wait up to .5s for it to signal non-busy */
+	do {
+		csr = tda9950_read(client, REG_CSR);
+		if (!(csr & CSR_BUSY) || --timeout)
+			break;
+		msleep(10);
+	} while (1);
+
+	/* Warn the user that their IRQ may die if it's shared. */
+	if (csr & CSR_BUSY)
+		dev_warn(&client->dev, "command processor failed to stop, irq%d may die (csr=0x%02x)\n",
+			 client->irq, csr);
+
+	tda9950_glue_release(priv);
+}
+
+static int tda9950_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+	struct tda9950_priv *priv = adap->priv;
+
+	if (!enable) {
+		tda9950_release(priv);
+		return 0;
+	} else {
+		return tda9950_open(priv);
+	}
+}
+
+static const struct cec_adap_ops tda9950_cec_ops = {
+	.adap_enable = tda9950_cec_adap_enable,
+	.adap_log_addr = tda9950_cec_adap_log_addr,
+	.adap_transmit = tda9950_cec_transmit,
+};
+
+static unsigned int parse_hdmi_addr(const struct edid *edid)
+{
+	if (!edid || edid->extensions = 0)
+		return (u16)~0;
+
+	return cec_get_edid_phys_addr((u8 *)edid,
+				EDID_LENGTH * (edid->extensions + 1), NULL);
+}
+
+static int tda9950_cec_notify(struct notifier_block *nb, unsigned long event,
+			      void *data)
+{
+	struct tda9950_priv *priv = container_of(nb, struct tda9950_priv, nb);
+	struct hdmi_notifier *n = data;
+	unsigned int phys;
+
+	switch (event) {
+	case HDMI_DISCONNECTED:
+		cec_s_phys_addr(priv->adap, CEC_PHYS_ADDR_INVALID, false);
+		break;
+
+	case HDMI_NEW_EDID:
+		phys = parse_hdmi_addr(n->edid);
+		cec_s_phys_addr(priv->adap, phys, false);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+/*
+ * When operating as part of the TDA998x, we need to claim additional
+ * resources.  These two hooks permit the management of those resources.
+ */
+static void tda9950_devm_glue_exit(void *data)
+{
+	struct tda9950_glue *glue = data;
+
+	if (glue && glue->exit)
+		glue->exit(glue->data);
+}
+
+static int tda9950_devm_glue_init(struct device *dev, struct tda9950_glue *glue)
+{
+	int ret;
+
+	if (glue && glue->init) {
+		ret = glue->init(glue->data);
+		if (ret)
+			return ret;
+	}
+
+	ret = devm_add_action(dev, tda9950_devm_glue_exit, glue);
+	if (ret)
+		tda9950_devm_glue_exit(glue);
+
+	return ret;
+}
+
+static void tda9950_cec_del(void *data)
+{
+	struct tda9950_priv *priv = data;
+
+	cec_delete_adapter(priv->adap);
+}
+
+static int tda9950_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct tda9950_glue *glue = client->dev.platform_data;
+	struct device *dev = &client->dev;
+	struct tda9950_priv *priv;
+	int ret;
+	u8 cvr;
+
+	/*
+	 * We must have I2C functionality: our multi-byte accesses
+	 * must be performed as a single contiguous transaction.
+	 */
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"adapter does not support I2C functionality\n");
+		return -ENXIO;
+	}
+
+	/* We must have an interrupt to be functional. */
+	if (client->irq <= 0) {
+		dev_err(&client->dev, "driver requires an interrupt\n");
+		return -ENXIO;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->client = client;
+	priv->glue = glue;
+	priv->nb.notifier_call = tda9950_cec_notify;
+
+	i2c_set_clientdata(client, priv);
+
+	/*
+	 * If we're part of a TDA998x, we want the class devices to be
+	 * associated with the HDMI Tx so we have a tight relationship
+	 * between the HDMI interface and the CEC interface.
+	 */
+	priv->hdmi = dev;
+	if (glue && glue->parent)
+		priv->hdmi = glue->parent;
+
+	priv->adap = cec_allocate_adapter(&tda9950_cec_ops, priv, "tda9950",
+					  CEC_CAP_LOG_ADDRS |
+					  CEC_CAP_TRANSMIT | CEC_CAP_RC,
+					  CEC_MAX_LOG_ADDRS, priv->hdmi);
+	if (IS_ERR(priv->adap))
+		return PTR_ERR(priv->adap);
+
+	ret = devm_add_action(dev, tda9950_cec_del, priv);
+	if (ret) {
+		cec_delete_adapter(priv->adap);
+		return ret;
+	}
+
+	ret = tda9950_devm_glue_init(dev, glue);
+	if (ret)
+		return ret;
+
+	ret = tda9950_glue_open(priv);
+	if (ret)
+		return ret;
+
+	cvr = tda9950_read(client, REG_CVR);
+
+	dev_info(&client->dev,
+		 "TDA9950 CEC interface, hardware version %u.%u\n",
+		 cvr >> 4, cvr & 15);
+
+	tda9950_glue_release(priv);
+
+	ret = devm_request_threaded_irq(dev, client->irq, NULL, tda9950_irq,
+					IRQF_TRIGGER_FALLING | IRQF_SHARED |
+					IRQF_ONESHOT,
+					dev_name(&client->dev), priv);
+	if (ret < 0)
+		return ret;
+
+	ret = cec_register_adapter(priv->adap);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * CEC documentation says we must not call cec_delete_adapter
+	 * after a successful call to cec_register_adapter().
+	 */
+	devm_remove_action(dev, tda9950_cec_del, priv);
+
+	priv->n = hdmi_notifier_get(priv->hdmi);
+	if (!priv->n)
+		return -ENOMEM;
+	hdmi_notifier_register(priv->n, &priv->nb);
+
+	return ret;
+}
+
+static int tda9950_remove(struct i2c_client *client)
+{
+	struct tda9950_priv *priv = i2c_get_clientdata(client);
+
+	hdmi_notifier_unregister(priv->n, &priv->nb);
+	hdmi_notifier_put(priv->n);
+	cec_unregister_adapter(priv->adap);
+
+	return 0;
+}
+
+static struct i2c_device_id tda9950_ids[] = {
+	{ "tda9950", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, tda9950_ids);
+
+static struct i2c_driver tda9950_driver = {
+	.probe = tda9950_probe,
+	.remove = tda9950_remove,
+	.driver = {
+		.name = "tda9950",
+	},
+	.id_table = tda9950_ids,
+};
+
+module_i2c_driver(tda9950_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@armlinux.org.uk>");
+MODULE_DESCRIPTION("TDA9950/TDA998x Consumer Electronics Control Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/platform_data/tda9950.h b/include/linux/platform_data/tda9950.h
new file mode 100644
index 0000000..08da9c9
--- /dev/null
+++ b/include/linux/platform_data/tda9950.h
@@ -0,0 +1,15 @@
+#ifndef LINUX_PLATFORM_DATA_TDA9950_H
+#define LINUX_PLATFORM_DATA_TDA9950_H
+
+struct device;
+
+struct tda9950_glue {
+	struct device *parent;
+	void *data;
+	int (*init)(void *);
+	void (*exit)(void *);
+	int (*open)(void *);
+	void (*release)(void *);
+};
+
+#endif
-- 
2.8.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 5/5] drm/i2c: add tda998x/tda9950 CEC driver
@ 2016-11-14 15:22   ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:22 UTC (permalink / raw)
  To: linux-arm-kernel

From: Russell King <rmk+kernel@armlinux.org.uk>

Add a CEC driver for the TDA9950, which is a stand-alone I2C CEC device.
The TDA9950 contains a command processor which handles retransmissions
and the low level bus protocol.  The driver just has to read and write
the messages, and handle error conditions.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/gpu/drm/i2c/Kconfig           |   5 +
 drivers/gpu/drm/i2c/Makefile          |   1 +
 drivers/gpu/drm/i2c/tda9950.c         | 516 ++++++++++++++++++++++++++++++++++
 include/linux/platform_data/tda9950.h |  15 +
 4 files changed, 537 insertions(+)
 create mode 100644 drivers/gpu/drm/i2c/tda9950.c
 create mode 100644 include/linux/platform_data/tda9950.h

diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
index a6c92be..b30ea3b 100644
--- a/drivers/gpu/drm/i2c/Kconfig
+++ b/drivers/gpu/drm/i2c/Kconfig
@@ -26,4 +26,9 @@ config DRM_I2C_NXP_TDA998X
 	help
 	  Support for NXP Semiconductors TDA998X HDMI encoders.
 
+config DRM_I2C_NXP_TDA9950
+	tristate "NXP Semiconductors TDA9950/TDA998X HDMI CEC"
+	depends on MEDIA_CEC_SUPPORT
+	select HDMI_NOTIFIERS
+
 endmenu
diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
index 43aa33b..8a6e13e 100644
--- a/drivers/gpu/drm/i2c/Makefile
+++ b/drivers/gpu/drm/i2c/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
 
 tda998x-y := tda998x_drv.o
 obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
+obj-$(CONFIG_DRM_I2C_NXP_TDA9950) += tda9950.o
diff --git a/drivers/gpu/drm/i2c/tda9950.c b/drivers/gpu/drm/i2c/tda9950.c
new file mode 100644
index 0000000..601da7c
--- /dev/null
+++ b/drivers/gpu/drm/i2c/tda9950.c
@@ -0,0 +1,516 @@
+/*
+ *  TDA9950 Consumer Electronics Control driver
+ *
+ * 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.
+ *
+ * The NXP TDA9950 implements the HDMI Consumer Electronics Control
+ * interface.  The host interface is similar to a mailbox: the data
+ * registers starting at REG_CDR0 are written to send a command to the
+ * internal CPU, and replies are read from these registers.
+ *
+ * As the data registers represent a mailbox, they must be accessed
+ * as a single I2C transaction.  See the TDA9950 data sheet for details.
+ */
+#include <linux/delay.h>
+#include <linux/hdmi-notifier.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_data/tda9950.h>
+#include <linux/slab.h>
+#include <drm/drm_edid.h>
+#include <media/cec.h>
+
+enum {
+	REG_CSR = 0x00,
+	CSR_BUSY = BIT(7),
+	CSR_INT  = BIT(6),
+	CSR_ERR  = BIT(5),
+
+	REG_CER = 0x01,
+
+	REG_CVR = 0x02,
+
+	REG_CCR = 0x03,
+	CCR_RESET = BIT(7),
+	CCR_ON    = BIT(6),
+
+	REG_ACKH = 0x04,
+	REG_ACKL = 0x05,
+
+	REG_CCONR = 0x06,
+	CCONR_ENABLE_ERROR = BIT(4),
+
+	REG_CDR0 = 0x07,
+
+	CDR1_REQ = 0x00,
+	CDR1_CNF = 0x01,
+	CDR1_IND = 0x81,
+	CDR1_ERR = 0x82,
+	CDR1_IER = 0x83,
+
+	CDR2_CNF_SUCCESS    = 0x00,
+	CDR2_CNF_OFF_STATE  = 0x80,
+	CDR2_CNF_BAD_REQ    = 0x81,
+	CDR2_CNF_CEC_ACCESS = 0x82,
+	CDR2_CNF_ARB_ERROR  = 0x83,
+	CDR2_CNF_BAD_TIMING = 0x84,
+	CDR2_CNF_NACK_ADDR  = 0x85,
+	CDR2_CNF_NACK_DATA  = 0x86,
+};
+
+struct tda9950_priv {
+	struct i2c_client *client;
+	struct device *hdmi;
+	struct cec_adapter *adap;
+	struct tda9950_glue *glue;
+	u16 addresses;
+	struct cec_msg rx_msg;
+	struct hdmi_notifier *n;
+	struct notifier_block nb;
+	bool open;
+};
+
+static int tda9950_write_range(struct i2c_client *client, u8 addr, u8 *p, int cnt)
+{
+	struct i2c_msg msg;
+	u8 buf[cnt + 1];
+	int ret;
+
+	buf[0] = addr;
+	memcpy(buf + 1, p, cnt);
+
+	msg.addr = client->addr;
+	msg.flags = 0;
+	msg.len = cnt + 1;
+	msg.buf = buf;
+
+	dev_dbg(&client->dev, "wr 0x%02x: %*ph\n", addr, cnt, p);
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		dev_err(&client->dev, "Error %d writing to cec:0x%x\n", ret, addr);
+	return ret < 0 ? ret : 0;
+}
+
+static void tda9950_write(struct i2c_client *client, u8 addr, u8 val)
+{
+	tda9950_write_range(client, addr, &val, 1);
+}
+
+static int tda9950_read_range(struct i2c_client *client, u8 addr, u8 *p, int cnt)
+{
+	struct i2c_msg msg[2];
+	int ret;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = &addr;
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = cnt;
+	msg[1].buf = p;
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret < 0)
+		dev_err(&client->dev, "Error %d reading from cec:0x%x\n", ret, addr);
+
+	dev_dbg(&client->dev, "rd 0x%02x: %*ph\n", addr, cnt, p);
+
+	return ret;
+}
+
+static u8 tda9950_read(struct i2c_client *client, u8 addr)
+{
+	int ret;
+	u8 val;
+
+	ret = tda9950_read_range(client, addr, &val, 1);
+	if (ret < 0)
+		val = 0;
+
+	return val;
+}
+
+static irqreturn_t tda9950_irq(int irq, void *data)
+{
+	struct tda9950_priv *priv = data;
+	unsigned int tx_status;
+	u8 csr, buf[19];
+
+	if (!priv->open)
+		return IRQ_NONE;
+
+	csr = tda9950_read(priv->client, REG_CSR);
+	if (!(csr & CSR_INT))
+		return IRQ_NONE;
+
+	tda9950_read_range(priv->client, REG_CDR0, buf, sizeof(buf));
+
+	/*
+	 * This should never happen: the data sheet says that there will
+	 * always be a valid message if the interrupt line is asserted.
+	 */
+	if (buf[0] == 0) {
+		dev_warn(&priv->client->dev, "interrupt pending, but no message?\n");
+		return IRQ_NONE;
+	}
+
+	switch (buf[1]) {
+	case CDR1_CNF: /* transmit result */
+		switch (buf[2]) {
+		case CDR2_CNF_SUCCESS:
+			tx_status = CEC_TX_STATUS_OK;
+			break;
+
+		case CDR2_CNF_NACK_ADDR:
+			tx_status = CEC_TX_STATUS_NACK;
+			break;
+
+		default: /* some other error, refer to TDA9950 docs */
+			dev_err(&priv->client->dev, "CNF reply error 0x%02x\n",
+				buf[2]);
+			tx_status = CEC_TX_STATUS_ERROR;
+			break;
+		}
+		cec_transmit_done(priv->adap, tx_status, 0, 0, 0, 0);
+		break;
+
+	case CDR1_IND:
+		priv->rx_msg.len = buf[0] - 2;
+		memcpy(priv->rx_msg.msg, buf + 2, priv->rx_msg.len);
+		cec_received_msg(priv->adap, &priv->rx_msg);
+		break;
+
+	default: /* unknown */
+		dev_err(&priv->client->dev, "unknown service id 0x%02x\n",
+			buf[1]);
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int tda9950_cec_transmit(struct cec_adapter *adap, u8 attempts,
+				u32 signal_free_time, struct cec_msg *msg)
+{
+	struct tda9950_priv *priv = adap->priv;
+	u8 buf[16 + 2];
+
+	buf[0] = 2 + msg->len;
+	buf[1] = CDR1_REQ;
+	memcpy(buf + 2, msg->msg, msg->len);
+
+	return tda9950_write_range(priv->client, REG_CDR0, buf, 2 + msg->len);
+}
+
+static int tda9950_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+	struct tda9950_priv *priv = adap->priv;
+	u16 addresses;
+	u8 buf[2];
+
+	if (addr == CEC_LOG_ADDR_INVALID)
+		addresses = priv->addresses = BIT(15);
+	else
+		addresses = priv->addresses |= BIT(addr);
+
+	/* TDA9950 doesn't want address 15 set */
+	addr &= 0x7fff;
+	buf[0] = addresses >> 8;
+	buf[1] = addresses;
+
+	return tda9950_write_range(priv->client, REG_ACKH, buf, 2);
+}
+
+/*
+ * When operating as part of the TDA998x, we need additional handling
+ * to initialise and shut down the TDA9950 part of the device.  These
+ * two hooks are provided to allow the TDA998x code to perform those
+ * activities.
+ */
+static int tda9950_glue_open(struct tda9950_priv *priv)
+{
+	int ret = 0;
+
+	if (priv->glue && priv->glue->open)
+		ret = priv->glue->open(priv->glue->data);
+
+	priv->open = true;
+
+	return ret;
+}
+
+static void tda9950_glue_release(struct tda9950_priv *priv)
+{
+	priv->open = false;
+
+	if (priv->glue && priv->glue->release)
+		priv->glue->release(priv->glue->data);
+}
+
+static int tda9950_open(struct tda9950_priv *priv)
+{
+	struct i2c_client *client = priv->client;
+	int ret;
+
+	ret = tda9950_glue_open(priv);
+	if (ret)
+		return ret;
+
+	/* Reset the TDA9950, and wait 250ms for it to recover */
+	tda9950_write(client, REG_CCR, CCR_RESET);
+	msleep(250);
+
+	/* Configure for the standard 5 retries */
+	tda9950_write(client, REG_CCONR, 5);
+	tda9950_cec_adap_log_addr(priv->adap, CEC_LOG_ADDR_INVALID);
+
+	/* Start the command processor */
+	tda9950_write(client, REG_CCR, CCR_ON);
+
+	return 0;
+}
+
+static void tda9950_release(struct tda9950_priv *priv)
+{
+	struct i2c_client *client = priv->client;
+	int timeout = 50;
+	u8 csr;
+
+	/* Stop the command processor */
+	tda9950_write(client, REG_CCR, 0);
+
+	/* Wait up to .5s for it to signal non-busy */
+	do {
+		csr = tda9950_read(client, REG_CSR);
+		if (!(csr & CSR_BUSY) || --timeout)
+			break;
+		msleep(10);
+	} while (1);
+
+	/* Warn the user that their IRQ may die if it's shared. */
+	if (csr & CSR_BUSY)
+		dev_warn(&client->dev, "command processor failed to stop, irq%d may die (csr=0x%02x)\n",
+			 client->irq, csr);
+
+	tda9950_glue_release(priv);
+}
+
+static int tda9950_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+	struct tda9950_priv *priv = adap->priv;
+
+	if (!enable) {
+		tda9950_release(priv);
+		return 0;
+	} else {
+		return tda9950_open(priv);
+	}
+}
+
+static const struct cec_adap_ops tda9950_cec_ops = {
+	.adap_enable = tda9950_cec_adap_enable,
+	.adap_log_addr = tda9950_cec_adap_log_addr,
+	.adap_transmit = tda9950_cec_transmit,
+};
+
+static unsigned int parse_hdmi_addr(const struct edid *edid)
+{
+	if (!edid || edid->extensions == 0)
+		return (u16)~0;
+
+	return cec_get_edid_phys_addr((u8 *)edid,
+				EDID_LENGTH * (edid->extensions + 1), NULL);
+}
+
+static int tda9950_cec_notify(struct notifier_block *nb, unsigned long event,
+			      void *data)
+{
+	struct tda9950_priv *priv = container_of(nb, struct tda9950_priv, nb);
+	struct hdmi_notifier *n = data;
+	unsigned int phys;
+
+	switch (event) {
+	case HDMI_DISCONNECTED:
+		cec_s_phys_addr(priv->adap, CEC_PHYS_ADDR_INVALID, false);
+		break;
+
+	case HDMI_NEW_EDID:
+		phys = parse_hdmi_addr(n->edid);
+		cec_s_phys_addr(priv->adap, phys, false);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+/*
+ * When operating as part of the TDA998x, we need to claim additional
+ * resources.  These two hooks permit the management of those resources.
+ */
+static void tda9950_devm_glue_exit(void *data)
+{
+	struct tda9950_glue *glue = data;
+
+	if (glue && glue->exit)
+		glue->exit(glue->data);
+}
+
+static int tda9950_devm_glue_init(struct device *dev, struct tda9950_glue *glue)
+{
+	int ret;
+
+	if (glue && glue->init) {
+		ret = glue->init(glue->data);
+		if (ret)
+			return ret;
+	}
+
+	ret = devm_add_action(dev, tda9950_devm_glue_exit, glue);
+	if (ret)
+		tda9950_devm_glue_exit(glue);
+
+	return ret;
+}
+
+static void tda9950_cec_del(void *data)
+{
+	struct tda9950_priv *priv = data;
+
+	cec_delete_adapter(priv->adap);
+}
+
+static int tda9950_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct tda9950_glue *glue = client->dev.platform_data;
+	struct device *dev = &client->dev;
+	struct tda9950_priv *priv;
+	int ret;
+	u8 cvr;
+
+	/*
+	 * We must have I2C functionality: our multi-byte accesses
+	 * must be performed as a single contiguous transaction.
+	 */
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		dev_err(&client->dev,
+			"adapter does not support I2C functionality\n");
+		return -ENXIO;
+	}
+
+	/* We must have an interrupt to be functional. */
+	if (client->irq <= 0) {
+		dev_err(&client->dev, "driver requires an interrupt\n");
+		return -ENXIO;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->client = client;
+	priv->glue = glue;
+	priv->nb.notifier_call = tda9950_cec_notify;
+
+	i2c_set_clientdata(client, priv);
+
+	/*
+	 * If we're part of a TDA998x, we want the class devices to be
+	 * associated with the HDMI Tx so we have a tight relationship
+	 * between the HDMI interface and the CEC interface.
+	 */
+	priv->hdmi = dev;
+	if (glue && glue->parent)
+		priv->hdmi = glue->parent;
+
+	priv->adap = cec_allocate_adapter(&tda9950_cec_ops, priv, "tda9950",
+					  CEC_CAP_LOG_ADDRS |
+					  CEC_CAP_TRANSMIT | CEC_CAP_RC,
+					  CEC_MAX_LOG_ADDRS, priv->hdmi);
+	if (IS_ERR(priv->adap))
+		return PTR_ERR(priv->adap);
+
+	ret = devm_add_action(dev, tda9950_cec_del, priv);
+	if (ret) {
+		cec_delete_adapter(priv->adap);
+		return ret;
+	}
+
+	ret = tda9950_devm_glue_init(dev, glue);
+	if (ret)
+		return ret;
+
+	ret = tda9950_glue_open(priv);
+	if (ret)
+		return ret;
+
+	cvr = tda9950_read(client, REG_CVR);
+
+	dev_info(&client->dev,
+		 "TDA9950 CEC interface, hardware version %u.%u\n",
+		 cvr >> 4, cvr & 15);
+
+	tda9950_glue_release(priv);
+
+	ret = devm_request_threaded_irq(dev, client->irq, NULL, tda9950_irq,
+					IRQF_TRIGGER_FALLING | IRQF_SHARED |
+					IRQF_ONESHOT,
+					dev_name(&client->dev), priv);
+	if (ret < 0)
+		return ret;
+
+	ret = cec_register_adapter(priv->adap);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * CEC documentation says we must not call cec_delete_adapter
+	 * after a successful call to cec_register_adapter().
+	 */
+	devm_remove_action(dev, tda9950_cec_del, priv);
+
+	priv->n = hdmi_notifier_get(priv->hdmi);
+	if (!priv->n)
+		return -ENOMEM;
+	hdmi_notifier_register(priv->n, &priv->nb);
+
+	return ret;
+}
+
+static int tda9950_remove(struct i2c_client *client)
+{
+	struct tda9950_priv *priv = i2c_get_clientdata(client);
+
+	hdmi_notifier_unregister(priv->n, &priv->nb);
+	hdmi_notifier_put(priv->n);
+	cec_unregister_adapter(priv->adap);
+
+	return 0;
+}
+
+static struct i2c_device_id tda9950_ids[] = {
+	{ "tda9950", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, tda9950_ids);
+
+static struct i2c_driver tda9950_driver = {
+	.probe = tda9950_probe,
+	.remove = tda9950_remove,
+	.driver = {
+		.name = "tda9950",
+	},
+	.id_table = tda9950_ids,
+};
+
+module_i2c_driver(tda9950_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@armlinux.org.uk>");
+MODULE_DESCRIPTION("TDA9950/TDA998x Consumer Electronics Control Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/platform_data/tda9950.h b/include/linux/platform_data/tda9950.h
new file mode 100644
index 0000000..08da9c9
--- /dev/null
+++ b/include/linux/platform_data/tda9950.h
@@ -0,0 +1,15 @@
+#ifndef LINUX_PLATFORM_DATA_TDA9950_H
+#define LINUX_PLATFORM_DATA_TDA9950_H
+
+struct device;
+
+struct tda9950_glue {
+	struct device *parent;
+	void *data;
+	int (*init)(void *);
+	void (*exit)(void *);
+	int (*open)(void *);
+	void (*release)(void *);
+};
+
+#endif
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
  2016-11-14 15:22   ` Hans Verkuil
@ 2016-11-14 15:39     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 55+ messages in thread
From: Russell King - ARM Linux @ 2016-11-14 15:39 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, linux-arm-kernel, linux-fbdev, dri-devel

I can't comment on these, you've not included me in patch 1 nor the
covering message.

On Mon, Nov 14, 2016 at 04:22:45PM +0100, Hans Verkuil wrote:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
> 
> We don't need the CEC engine register definitions, so let's remove them.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
>  1 file changed, 45 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
> index fc9a560..26d6845 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi.h
> +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
> @@ -478,51 +478,6 @@
>  #define HDMI_A_PRESETUP                         0x501A
>  #define HDMI_A_SRM_BASE                         0x5020
>  
> -/* CEC Engine Registers */
> -#define HDMI_CEC_CTRL                           0x7D00
> -#define HDMI_CEC_STAT                           0x7D01
> -#define HDMI_CEC_MASK                           0x7D02
> -#define HDMI_CEC_POLARITY                       0x7D03
> -#define HDMI_CEC_INT                            0x7D04
> -#define HDMI_CEC_ADDR_L                         0x7D05
> -#define HDMI_CEC_ADDR_H                         0x7D06
> -#define HDMI_CEC_TX_CNT                         0x7D07
> -#define HDMI_CEC_RX_CNT                         0x7D08
> -#define HDMI_CEC_TX_DATA0                       0x7D10
> -#define HDMI_CEC_TX_DATA1                       0x7D11
> -#define HDMI_CEC_TX_DATA2                       0x7D12
> -#define HDMI_CEC_TX_DATA3                       0x7D13
> -#define HDMI_CEC_TX_DATA4                       0x7D14
> -#define HDMI_CEC_TX_DATA5                       0x7D15
> -#define HDMI_CEC_TX_DATA6                       0x7D16
> -#define HDMI_CEC_TX_DATA7                       0x7D17
> -#define HDMI_CEC_TX_DATA8                       0x7D18
> -#define HDMI_CEC_TX_DATA9                       0x7D19
> -#define HDMI_CEC_TX_DATA10                      0x7D1a
> -#define HDMI_CEC_TX_DATA11                      0x7D1b
> -#define HDMI_CEC_TX_DATA12                      0x7D1c
> -#define HDMI_CEC_TX_DATA13                      0x7D1d
> -#define HDMI_CEC_TX_DATA14                      0x7D1e
> -#define HDMI_CEC_TX_DATA15                      0x7D1f
> -#define HDMI_CEC_RX_DATA0                       0x7D20
> -#define HDMI_CEC_RX_DATA1                       0x7D21
> -#define HDMI_CEC_RX_DATA2                       0x7D22
> -#define HDMI_CEC_RX_DATA3                       0x7D23
> -#define HDMI_CEC_RX_DATA4                       0x7D24
> -#define HDMI_CEC_RX_DATA5                       0x7D25
> -#define HDMI_CEC_RX_DATA6                       0x7D26
> -#define HDMI_CEC_RX_DATA7                       0x7D27
> -#define HDMI_CEC_RX_DATA8                       0x7D28
> -#define HDMI_CEC_RX_DATA9                       0x7D29
> -#define HDMI_CEC_RX_DATA10                      0x7D2a
> -#define HDMI_CEC_RX_DATA11                      0x7D2b
> -#define HDMI_CEC_RX_DATA12                      0x7D2c
> -#define HDMI_CEC_RX_DATA13                      0x7D2d
> -#define HDMI_CEC_RX_DATA14                      0x7D2e
> -#define HDMI_CEC_RX_DATA15                      0x7D2f
> -#define HDMI_CEC_LOCK                           0x7D30
> -#define HDMI_CEC_WKUPCTRL                       0x7D31
> -
>  /* I2C Master Registers (E-DDC) */
>  #define HDMI_I2CM_SLAVE                         0x7E00
>  #define HDMI_I2CM_ADDRESS                       0x7E01
> -- 
> 2.8.1
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
@ 2016-11-14 15:39     ` Russell King - ARM Linux
  0 siblings, 0 replies; 55+ messages in thread
From: Russell King - ARM Linux @ 2016-11-14 15:39 UTC (permalink / raw)
  To: linux-arm-kernel

I can't comment on these, you've not included me in patch 1 nor the
covering message.

On Mon, Nov 14, 2016 at 04:22:45PM +0100, Hans Verkuil wrote:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
> 
> We don't need the CEC engine register definitions, so let's remove them.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
>  1 file changed, 45 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
> index fc9a560..26d6845 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi.h
> +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
> @@ -478,51 +478,6 @@
>  #define HDMI_A_PRESETUP                         0x501A
>  #define HDMI_A_SRM_BASE                         0x5020
>  
> -/* CEC Engine Registers */
> -#define HDMI_CEC_CTRL                           0x7D00
> -#define HDMI_CEC_STAT                           0x7D01
> -#define HDMI_CEC_MASK                           0x7D02
> -#define HDMI_CEC_POLARITY                       0x7D03
> -#define HDMI_CEC_INT                            0x7D04
> -#define HDMI_CEC_ADDR_L                         0x7D05
> -#define HDMI_CEC_ADDR_H                         0x7D06
> -#define HDMI_CEC_TX_CNT                         0x7D07
> -#define HDMI_CEC_RX_CNT                         0x7D08
> -#define HDMI_CEC_TX_DATA0                       0x7D10
> -#define HDMI_CEC_TX_DATA1                       0x7D11
> -#define HDMI_CEC_TX_DATA2                       0x7D12
> -#define HDMI_CEC_TX_DATA3                       0x7D13
> -#define HDMI_CEC_TX_DATA4                       0x7D14
> -#define HDMI_CEC_TX_DATA5                       0x7D15
> -#define HDMI_CEC_TX_DATA6                       0x7D16
> -#define HDMI_CEC_TX_DATA7                       0x7D17
> -#define HDMI_CEC_TX_DATA8                       0x7D18
> -#define HDMI_CEC_TX_DATA9                       0x7D19
> -#define HDMI_CEC_TX_DATA10                      0x7D1a
> -#define HDMI_CEC_TX_DATA11                      0x7D1b
> -#define HDMI_CEC_TX_DATA12                      0x7D1c
> -#define HDMI_CEC_TX_DATA13                      0x7D1d
> -#define HDMI_CEC_TX_DATA14                      0x7D1e
> -#define HDMI_CEC_TX_DATA15                      0x7D1f
> -#define HDMI_CEC_RX_DATA0                       0x7D20
> -#define HDMI_CEC_RX_DATA1                       0x7D21
> -#define HDMI_CEC_RX_DATA2                       0x7D22
> -#define HDMI_CEC_RX_DATA3                       0x7D23
> -#define HDMI_CEC_RX_DATA4                       0x7D24
> -#define HDMI_CEC_RX_DATA5                       0x7D25
> -#define HDMI_CEC_RX_DATA6                       0x7D26
> -#define HDMI_CEC_RX_DATA7                       0x7D27
> -#define HDMI_CEC_RX_DATA8                       0x7D28
> -#define HDMI_CEC_RX_DATA9                       0x7D29
> -#define HDMI_CEC_RX_DATA10                      0x7D2a
> -#define HDMI_CEC_RX_DATA11                      0x7D2b
> -#define HDMI_CEC_RX_DATA12                      0x7D2c
> -#define HDMI_CEC_RX_DATA13                      0x7D2d
> -#define HDMI_CEC_RX_DATA14                      0x7D2e
> -#define HDMI_CEC_RX_DATA15                      0x7D2f
> -#define HDMI_CEC_LOCK                           0x7D30
> -#define HDMI_CEC_WKUPCTRL                       0x7D31
> -
>  /* I2C Master Registers (E-DDC) */
>  #define HDMI_I2CM_SLAVE                         0x7E00
>  #define HDMI_I2CM_ADDRESS                       0x7E01
> -- 
> 2.8.1
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
  2016-11-14 15:39     ` Russell King - ARM Linux
  (?)
  (?)
@ 2016-11-14 15:42       ` Hans Verkuil
  -1 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:42 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-media, linux-arm-kernel, linux-fbdev, dri-devel

You're CC-ed for all, so if you don't receive it in the next 15 minutes
let me know and I'll forward it to you. But my guess is that the mails were
delayed for some reason and that they simply haven't arrived yet.

	Hans

On 11/14/2016 04:39 PM, Russell King - ARM Linux wrote:
> I can't comment on these, you've not included me in patch 1 nor the
> covering message.
> 
> On Mon, Nov 14, 2016 at 04:22:45PM +0100, Hans Verkuil wrote:
>> From: Russell King <rmk+kernel@arm.linux.org.uk>
>>
>> We don't need the CEC engine register definitions, so let's remove them.
>>
>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> ---
>>  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
>>  1 file changed, 45 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
>> index fc9a560..26d6845 100644
>> --- a/drivers/gpu/drm/bridge/dw-hdmi.h
>> +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
>> @@ -478,51 +478,6 @@
>>  #define HDMI_A_PRESETUP                         0x501A
>>  #define HDMI_A_SRM_BASE                         0x5020
>>  
>> -/* CEC Engine Registers */
>> -#define HDMI_CEC_CTRL                           0x7D00
>> -#define HDMI_CEC_STAT                           0x7D01
>> -#define HDMI_CEC_MASK                           0x7D02
>> -#define HDMI_CEC_POLARITY                       0x7D03
>> -#define HDMI_CEC_INT                            0x7D04
>> -#define HDMI_CEC_ADDR_L                         0x7D05
>> -#define HDMI_CEC_ADDR_H                         0x7D06
>> -#define HDMI_CEC_TX_CNT                         0x7D07
>> -#define HDMI_CEC_RX_CNT                         0x7D08
>> -#define HDMI_CEC_TX_DATA0                       0x7D10
>> -#define HDMI_CEC_TX_DATA1                       0x7D11
>> -#define HDMI_CEC_TX_DATA2                       0x7D12
>> -#define HDMI_CEC_TX_DATA3                       0x7D13
>> -#define HDMI_CEC_TX_DATA4                       0x7D14
>> -#define HDMI_CEC_TX_DATA5                       0x7D15
>> -#define HDMI_CEC_TX_DATA6                       0x7D16
>> -#define HDMI_CEC_TX_DATA7                       0x7D17
>> -#define HDMI_CEC_TX_DATA8                       0x7D18
>> -#define HDMI_CEC_TX_DATA9                       0x7D19
>> -#define HDMI_CEC_TX_DATA10                      0x7D1a
>> -#define HDMI_CEC_TX_DATA11                      0x7D1b
>> -#define HDMI_CEC_TX_DATA12                      0x7D1c
>> -#define HDMI_CEC_TX_DATA13                      0x7D1d
>> -#define HDMI_CEC_TX_DATA14                      0x7D1e
>> -#define HDMI_CEC_TX_DATA15                      0x7D1f
>> -#define HDMI_CEC_RX_DATA0                       0x7D20
>> -#define HDMI_CEC_RX_DATA1                       0x7D21
>> -#define HDMI_CEC_RX_DATA2                       0x7D22
>> -#define HDMI_CEC_RX_DATA3                       0x7D23
>> -#define HDMI_CEC_RX_DATA4                       0x7D24
>> -#define HDMI_CEC_RX_DATA5                       0x7D25
>> -#define HDMI_CEC_RX_DATA6                       0x7D26
>> -#define HDMI_CEC_RX_DATA7                       0x7D27
>> -#define HDMI_CEC_RX_DATA8                       0x7D28
>> -#define HDMI_CEC_RX_DATA9                       0x7D29
>> -#define HDMI_CEC_RX_DATA10                      0x7D2a
>> -#define HDMI_CEC_RX_DATA11                      0x7D2b
>> -#define HDMI_CEC_RX_DATA12                      0x7D2c
>> -#define HDMI_CEC_RX_DATA13                      0x7D2d
>> -#define HDMI_CEC_RX_DATA14                      0x7D2e
>> -#define HDMI_CEC_RX_DATA15                      0x7D2f
>> -#define HDMI_CEC_LOCK                           0x7D30
>> -#define HDMI_CEC_WKUPCTRL                       0x7D31
>> -
>>  /* I2C Master Registers (E-DDC) */
>>  #define HDMI_I2CM_SLAVE                         0x7E00
>>  #define HDMI_I2CM_ADDRESS                       0x7E01
>> -- 
>> 2.8.1
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
@ 2016-11-14 15:42       ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:42 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-fbdev, dri-devel, linux-arm-kernel, linux-media

You're CC-ed for all, so if you don't receive it in the next 15 minutes
let me know and I'll forward it to you. But my guess is that the mails were
delayed for some reason and that they simply haven't arrived yet.

	Hans

On 11/14/2016 04:39 PM, Russell King - ARM Linux wrote:
> I can't comment on these, you've not included me in patch 1 nor the
> covering message.
> 
> On Mon, Nov 14, 2016 at 04:22:45PM +0100, Hans Verkuil wrote:
>> From: Russell King <rmk+kernel@arm.linux.org.uk>
>>
>> We don't need the CEC engine register definitions, so let's remove them.
>>
>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> ---
>>  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
>>  1 file changed, 45 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
>> index fc9a560..26d6845 100644
>> --- a/drivers/gpu/drm/bridge/dw-hdmi.h
>> +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
>> @@ -478,51 +478,6 @@
>>  #define HDMI_A_PRESETUP                         0x501A
>>  #define HDMI_A_SRM_BASE                         0x5020
>>  
>> -/* CEC Engine Registers */
>> -#define HDMI_CEC_CTRL                           0x7D00
>> -#define HDMI_CEC_STAT                           0x7D01
>> -#define HDMI_CEC_MASK                           0x7D02
>> -#define HDMI_CEC_POLARITY                       0x7D03
>> -#define HDMI_CEC_INT                            0x7D04
>> -#define HDMI_CEC_ADDR_L                         0x7D05
>> -#define HDMI_CEC_ADDR_H                         0x7D06
>> -#define HDMI_CEC_TX_CNT                         0x7D07
>> -#define HDMI_CEC_RX_CNT                         0x7D08
>> -#define HDMI_CEC_TX_DATA0                       0x7D10
>> -#define HDMI_CEC_TX_DATA1                       0x7D11
>> -#define HDMI_CEC_TX_DATA2                       0x7D12
>> -#define HDMI_CEC_TX_DATA3                       0x7D13
>> -#define HDMI_CEC_TX_DATA4                       0x7D14
>> -#define HDMI_CEC_TX_DATA5                       0x7D15
>> -#define HDMI_CEC_TX_DATA6                       0x7D16
>> -#define HDMI_CEC_TX_DATA7                       0x7D17
>> -#define HDMI_CEC_TX_DATA8                       0x7D18
>> -#define HDMI_CEC_TX_DATA9                       0x7D19
>> -#define HDMI_CEC_TX_DATA10                      0x7D1a
>> -#define HDMI_CEC_TX_DATA11                      0x7D1b
>> -#define HDMI_CEC_TX_DATA12                      0x7D1c
>> -#define HDMI_CEC_TX_DATA13                      0x7D1d
>> -#define HDMI_CEC_TX_DATA14                      0x7D1e
>> -#define HDMI_CEC_TX_DATA15                      0x7D1f
>> -#define HDMI_CEC_RX_DATA0                       0x7D20
>> -#define HDMI_CEC_RX_DATA1                       0x7D21
>> -#define HDMI_CEC_RX_DATA2                       0x7D22
>> -#define HDMI_CEC_RX_DATA3                       0x7D23
>> -#define HDMI_CEC_RX_DATA4                       0x7D24
>> -#define HDMI_CEC_RX_DATA5                       0x7D25
>> -#define HDMI_CEC_RX_DATA6                       0x7D26
>> -#define HDMI_CEC_RX_DATA7                       0x7D27
>> -#define HDMI_CEC_RX_DATA8                       0x7D28
>> -#define HDMI_CEC_RX_DATA9                       0x7D29
>> -#define HDMI_CEC_RX_DATA10                      0x7D2a
>> -#define HDMI_CEC_RX_DATA11                      0x7D2b
>> -#define HDMI_CEC_RX_DATA12                      0x7D2c
>> -#define HDMI_CEC_RX_DATA13                      0x7D2d
>> -#define HDMI_CEC_RX_DATA14                      0x7D2e
>> -#define HDMI_CEC_RX_DATA15                      0x7D2f
>> -#define HDMI_CEC_LOCK                           0x7D30
>> -#define HDMI_CEC_WKUPCTRL                       0x7D31
>> -
>>  /* I2C Master Registers (E-DDC) */
>>  #define HDMI_I2CM_SLAVE                         0x7E00
>>  #define HDMI_I2CM_ADDRESS                       0x7E01
>> -- 
>> 2.8.1
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
@ 2016-11-14 15:42       ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:42 UTC (permalink / raw)
  To: linux-arm-kernel

You're CC-ed for all, so if you don't receive it in the next 15 minutes
let me know and I'll forward it to you. But my guess is that the mails were
delayed for some reason and that they simply haven't arrived yet.

	Hans

On 11/14/2016 04:39 PM, Russell King - ARM Linux wrote:
> I can't comment on these, you've not included me in patch 1 nor the
> covering message.
> 
> On Mon, Nov 14, 2016 at 04:22:45PM +0100, Hans Verkuil wrote:
>> From: Russell King <rmk+kernel@arm.linux.org.uk>
>>
>> We don't need the CEC engine register definitions, so let's remove them.
>>
>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> ---
>>  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
>>  1 file changed, 45 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
>> index fc9a560..26d6845 100644
>> --- a/drivers/gpu/drm/bridge/dw-hdmi.h
>> +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
>> @@ -478,51 +478,6 @@
>>  #define HDMI_A_PRESETUP                         0x501A
>>  #define HDMI_A_SRM_BASE                         0x5020
>>  
>> -/* CEC Engine Registers */
>> -#define HDMI_CEC_CTRL                           0x7D00
>> -#define HDMI_CEC_STAT                           0x7D01
>> -#define HDMI_CEC_MASK                           0x7D02
>> -#define HDMI_CEC_POLARITY                       0x7D03
>> -#define HDMI_CEC_INT                            0x7D04
>> -#define HDMI_CEC_ADDR_L                         0x7D05
>> -#define HDMI_CEC_ADDR_H                         0x7D06
>> -#define HDMI_CEC_TX_CNT                         0x7D07
>> -#define HDMI_CEC_RX_CNT                         0x7D08
>> -#define HDMI_CEC_TX_DATA0                       0x7D10
>> -#define HDMI_CEC_TX_DATA1                       0x7D11
>> -#define HDMI_CEC_TX_DATA2                       0x7D12
>> -#define HDMI_CEC_TX_DATA3                       0x7D13
>> -#define HDMI_CEC_TX_DATA4                       0x7D14
>> -#define HDMI_CEC_TX_DATA5                       0x7D15
>> -#define HDMI_CEC_TX_DATA6                       0x7D16
>> -#define HDMI_CEC_TX_DATA7                       0x7D17
>> -#define HDMI_CEC_TX_DATA8                       0x7D18
>> -#define HDMI_CEC_TX_DATA9                       0x7D19
>> -#define HDMI_CEC_TX_DATA10                      0x7D1a
>> -#define HDMI_CEC_TX_DATA11                      0x7D1b
>> -#define HDMI_CEC_TX_DATA12                      0x7D1c
>> -#define HDMI_CEC_TX_DATA13                      0x7D1d
>> -#define HDMI_CEC_TX_DATA14                      0x7D1e
>> -#define HDMI_CEC_TX_DATA15                      0x7D1f
>> -#define HDMI_CEC_RX_DATA0                       0x7D20
>> -#define HDMI_CEC_RX_DATA1                       0x7D21
>> -#define HDMI_CEC_RX_DATA2                       0x7D22
>> -#define HDMI_CEC_RX_DATA3                       0x7D23
>> -#define HDMI_CEC_RX_DATA4                       0x7D24
>> -#define HDMI_CEC_RX_DATA5                       0x7D25
>> -#define HDMI_CEC_RX_DATA6                       0x7D26
>> -#define HDMI_CEC_RX_DATA7                       0x7D27
>> -#define HDMI_CEC_RX_DATA8                       0x7D28
>> -#define HDMI_CEC_RX_DATA9                       0x7D29
>> -#define HDMI_CEC_RX_DATA10                      0x7D2a
>> -#define HDMI_CEC_RX_DATA11                      0x7D2b
>> -#define HDMI_CEC_RX_DATA12                      0x7D2c
>> -#define HDMI_CEC_RX_DATA13                      0x7D2d
>> -#define HDMI_CEC_RX_DATA14                      0x7D2e
>> -#define HDMI_CEC_RX_DATA15                      0x7D2f
>> -#define HDMI_CEC_LOCK                           0x7D30
>> -#define HDMI_CEC_WKUPCTRL                       0x7D31
>> -
>>  /* I2C Master Registers (E-DDC) */
>>  #define HDMI_I2CM_SLAVE                         0x7E00
>>  #define HDMI_I2CM_ADDRESS                       0x7E01
>> -- 
>> 2.8.1
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
@ 2016-11-14 15:42       ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-14 15:42 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-fbdev, dri-devel, linux-arm-kernel, linux-media

You're CC-ed for all, so if you don't receive it in the next 15 minutes
let me know and I'll forward it to you. But my guess is that the mails were
delayed for some reason and that they simply haven't arrived yet.

	Hans

On 11/14/2016 04:39 PM, Russell King - ARM Linux wrote:
> I can't comment on these, you've not included me in patch 1 nor the
> covering message.
> 
> On Mon, Nov 14, 2016 at 04:22:45PM +0100, Hans Verkuil wrote:
>> From: Russell King <rmk+kernel@arm.linux.org.uk>
>>
>> We don't need the CEC engine register definitions, so let's remove them.
>>
>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> ---
>>  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
>>  1 file changed, 45 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
>> index fc9a560..26d6845 100644
>> --- a/drivers/gpu/drm/bridge/dw-hdmi.h
>> +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
>> @@ -478,51 +478,6 @@
>>  #define HDMI_A_PRESETUP                         0x501A
>>  #define HDMI_A_SRM_BASE                         0x5020
>>  
>> -/* CEC Engine Registers */
>> -#define HDMI_CEC_CTRL                           0x7D00
>> -#define HDMI_CEC_STAT                           0x7D01
>> -#define HDMI_CEC_MASK                           0x7D02
>> -#define HDMI_CEC_POLARITY                       0x7D03
>> -#define HDMI_CEC_INT                            0x7D04
>> -#define HDMI_CEC_ADDR_L                         0x7D05
>> -#define HDMI_CEC_ADDR_H                         0x7D06
>> -#define HDMI_CEC_TX_CNT                         0x7D07
>> -#define HDMI_CEC_RX_CNT                         0x7D08
>> -#define HDMI_CEC_TX_DATA0                       0x7D10
>> -#define HDMI_CEC_TX_DATA1                       0x7D11
>> -#define HDMI_CEC_TX_DATA2                       0x7D12
>> -#define HDMI_CEC_TX_DATA3                       0x7D13
>> -#define HDMI_CEC_TX_DATA4                       0x7D14
>> -#define HDMI_CEC_TX_DATA5                       0x7D15
>> -#define HDMI_CEC_TX_DATA6                       0x7D16
>> -#define HDMI_CEC_TX_DATA7                       0x7D17
>> -#define HDMI_CEC_TX_DATA8                       0x7D18
>> -#define HDMI_CEC_TX_DATA9                       0x7D19
>> -#define HDMI_CEC_TX_DATA10                      0x7D1a
>> -#define HDMI_CEC_TX_DATA11                      0x7D1b
>> -#define HDMI_CEC_TX_DATA12                      0x7D1c
>> -#define HDMI_CEC_TX_DATA13                      0x7D1d
>> -#define HDMI_CEC_TX_DATA14                      0x7D1e
>> -#define HDMI_CEC_TX_DATA15                      0x7D1f
>> -#define HDMI_CEC_RX_DATA0                       0x7D20
>> -#define HDMI_CEC_RX_DATA1                       0x7D21
>> -#define HDMI_CEC_RX_DATA2                       0x7D22
>> -#define HDMI_CEC_RX_DATA3                       0x7D23
>> -#define HDMI_CEC_RX_DATA4                       0x7D24
>> -#define HDMI_CEC_RX_DATA5                       0x7D25
>> -#define HDMI_CEC_RX_DATA6                       0x7D26
>> -#define HDMI_CEC_RX_DATA7                       0x7D27
>> -#define HDMI_CEC_RX_DATA8                       0x7D28
>> -#define HDMI_CEC_RX_DATA9                       0x7D29
>> -#define HDMI_CEC_RX_DATA10                      0x7D2a
>> -#define HDMI_CEC_RX_DATA11                      0x7D2b
>> -#define HDMI_CEC_RX_DATA12                      0x7D2c
>> -#define HDMI_CEC_RX_DATA13                      0x7D2d
>> -#define HDMI_CEC_RX_DATA14                      0x7D2e
>> -#define HDMI_CEC_RX_DATA15                      0x7D2f
>> -#define HDMI_CEC_LOCK                           0x7D30
>> -#define HDMI_CEC_WKUPCTRL                       0x7D31
>> -
>>  /* I2C Master Registers (E-DDC) */
>>  #define HDMI_I2CM_SLAVE                         0x7E00
>>  #define HDMI_I2CM_ADDRESS                       0x7E01
>> -- 
>> 2.8.1
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 1/5] video: add HDMI state notifier support
  2016-11-14 15:22   ` Hans Verkuil
  (?)
@ 2016-11-15 18:24     ` Philipp Zabel
  -1 siblings, 0 replies; 55+ messages in thread
From: Philipp Zabel @ 2016-11-15 18:24 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Hans Verkuil

Hi Hans,

Am Montag, den 14.11.2016, 16:22 +0100 schrieb Hans Verkuil:
> From: Hans Verkuil <hans.verkuil@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@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.

> +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.

> +	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@pengutronix.de>
Tested-by: Philipp Zabel <p.zabel@pengutronix.de> (on MT8173)

I'll send the patches for mediatek-drm and hdmi-codec that I used for
testing in a bit.

regards
Philipp


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 1/5] video: add HDMI state notifier support
@ 2016-11-15 18:24     ` Philipp Zabel
  0 siblings, 0 replies; 55+ messages in thread
From: Philipp Zabel @ 2016-11-15 18:24 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Hans Verkuil

Hi Hans,

Am Montag, den 14.11.2016, 16:22 +0100 schrieb Hans Verkuil:
> From: Hans Verkuil <hans.verkuil@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@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.

> +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.

> +	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@pengutronix.de>
Tested-by: Philipp Zabel <p.zabel@pengutronix.de> (on MT8173)

I'll send the patches for mediatek-drm and hdmi-codec that I used for
testing in a bit.

regards
Philipp


^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 1/5] video: add HDMI state notifier support
@ 2016-11-15 18:24     ` Philipp Zabel
  0 siblings, 0 replies; 55+ messages in thread
From: Philipp Zabel @ 2016-11-15 18:24 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Hans,

Am Montag, den 14.11.2016, 16:22 +0100 schrieb Hans Verkuil:
> From: Hans Verkuil <hans.verkuil@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@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.

> +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.

> +	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@pengutronix.de>
Tested-by: Philipp Zabel <p.zabel@pengutronix.de> (on MT8173)

I'll send the patches for mediatek-drm and hdmi-codec that I used for
testing in a bit.

regards
Philipp

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 1/5] video: add HDMI state notifier support
  2016-11-15 18:24     ` Philipp Zabel
  (?)
  (?)
@ 2016-11-15 20:41       ` Hans Verkuil
  -1 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-15 20:41 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: linux-media, Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Hans Verkuil

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@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@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@pengutronix.de>
> Tested-by: Philipp Zabel <p.zabel@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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 1/5] video: add HDMI state notifier support
@ 2016-11-15 20:41       ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-15 20:41 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: linux-fbdev, Russell King - ARM Linux, dri-devel, Hans Verkuil,
	linux-arm-kernel, linux-media

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@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@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@pengutronix.de>
> Tested-by: Philipp Zabel <p.zabel@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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 1/5] video: add HDMI state notifier support
@ 2016-11-15 20:41       ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-15 20:41 UTC (permalink / raw)
  To: linux-arm-kernel

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@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@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@pengutronix.de>
> Tested-by: Philipp Zabel <p.zabel@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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 1/5] video: add HDMI state notifier support
@ 2016-11-15 20:41       ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-15 20:41 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: linux-fbdev, Russell King - ARM Linux, dri-devel, Hans Verkuil,
	linux-arm-kernel, linux-media

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@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@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@pengutronix.de>
> Tested-by: Philipp Zabel <p.zabel@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
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 3/5] drm/bridge: dw_hdmi: add HDMI notifier support
  2016-11-14 15:22   ` Hans Verkuil
  (?)
@ 2016-11-15 23:21     ` Pierre-Hugues Husson
  -1 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2016-11-15 23:21 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, linux-arm-kernel, Russell King, linux-fbdev,
	Russell King - ARM Linux, dri-devel

Hi Hans,

2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
>
> Add HDMI notifiers to the HDMI bridge driver.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/Kconfig   |  1 +
>  drivers/gpu/drm/bridge/dw-hdmi.c | 25 ++++++++++++++++++++++++-
>  2 files changed, 25 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 10e12e7..5f4ebe9 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -27,6 +27,7 @@ config DRM_DUMB_VGA_DAC
>  config DRM_DW_HDMI
>         tristate
>         select DRM_KMS_HELPER
> +       select HDMI_NOTIFIERS
>
>  config DRM_DW_HDMI_AHB_AUDIO
>         tristate "Synopsis Designware AHB Audio interface"
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
> index ab7023e..bd02da5 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw-hdmi.c
> @@ -16,6 +16,7 @@
>  #include <linux/err.h>
>  #include <linux/clk.h>
>  #include <linux/hdmi.h>
> +#include <linux/hdmi-notifier.h>
>  #include <linux/mutex.h>
>  #include <linux/of_device.h>
>  #include <linux/spinlock.h>
> @@ -114,6 +115,7 @@ struct dw_hdmi {
>
>         struct hdmi_data_info hdmi_data;
>         const struct dw_hdmi_plat_data *plat_data;
> +       struct hdmi_notifier *n;
>
>         int vic;
>
> @@ -1448,9 +1450,11 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
>                 hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>                 hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
>                 drm_mode_connector_update_edid_property(connector, edid);
> +               hdmi_event_new_edid(hdmi->n, edid, 0);
>                 ret = drm_add_edid_modes(connector, edid);
>                 /* Store the ELD */
>                 drm_edid_to_eld(connector, edid);
> +               hdmi_event_new_eld(hdmi->n, connector->eld);
>                 kfree(edid);
>         } else {
>                 dev_dbg(hdmi->dev, "failed to get edid\n");
> @@ -1579,6 +1583,12 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>                         dw_hdmi_update_phy_mask(hdmi);
>                 }
>                 mutex_unlock(&hdmi->mutex);
> +
> +               if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0)
> +                       hdmi_event_disconnect(hdmi->n);
> +               else if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) ==
> +                        (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_PHY_HPD))
> +                       hdmi_event_connect(hdmi->n);
>         }
>
>         if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
> @@ -1732,11 +1742,17 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>
>         initialize_hdmi_ih_mutes(hdmi);
>
> +       hdmi->n = hdmi_notifier_get(dev);
> +       if (!hdmi->n) {
> +               ret = -ENOMEM;
> +               goto err_iahb;
> +       }
> +
>         ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
>                                         dw_hdmi_irq, IRQF_SHARED,
>                                         dev_name(dev), hdmi);
>         if (ret)
> -               goto err_iahb;
> +               goto err_hdmi_not;
>
>         /*
>          * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
> @@ -1788,6 +1804,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>
>         return 0;
>
> +err_hdmi_not:
> +       hdmi_notifier_put(hdmi->n);
>  err_iahb:
>         clk_disable_unprepare(hdmi->iahb_clk);
>  err_isfr:
> @@ -1804,6 +1822,11 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
>         if (hdmi->audio && !IS_ERR(hdmi->audio))
>                 platform_device_unregister(hdmi->audio);
>
> +       hdmi_notifier_put(hdmi->n);
> +
> +       if (!IS_ERR(hdmi->cec))
> +               platform_device_unregister(hdmi->cec);
Those two lines should be in your 4/5
> +
>         /* Disable all interrupts */
>         hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 3/5] drm/bridge: dw_hdmi: add HDMI notifier support
@ 2016-11-15 23:21     ` Pierre-Hugues Husson
  0 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2016-11-15 23:21 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, linux-arm-kernel, Russell King, linux-fbdev,
	Russell King - ARM Linux, dri-devel

Hi Hans,

2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
>
> Add HDMI notifiers to the HDMI bridge driver.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/Kconfig   |  1 +
>  drivers/gpu/drm/bridge/dw-hdmi.c | 25 ++++++++++++++++++++++++-
>  2 files changed, 25 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 10e12e7..5f4ebe9 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -27,6 +27,7 @@ config DRM_DUMB_VGA_DAC
>  config DRM_DW_HDMI
>         tristate
>         select DRM_KMS_HELPER
> +       select HDMI_NOTIFIERS
>
>  config DRM_DW_HDMI_AHB_AUDIO
>         tristate "Synopsis Designware AHB Audio interface"
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
> index ab7023e..bd02da5 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw-hdmi.c
> @@ -16,6 +16,7 @@
>  #include <linux/err.h>
>  #include <linux/clk.h>
>  #include <linux/hdmi.h>
> +#include <linux/hdmi-notifier.h>
>  #include <linux/mutex.h>
>  #include <linux/of_device.h>
>  #include <linux/spinlock.h>
> @@ -114,6 +115,7 @@ struct dw_hdmi {
>
>         struct hdmi_data_info hdmi_data;
>         const struct dw_hdmi_plat_data *plat_data;
> +       struct hdmi_notifier *n;
>
>         int vic;
>
> @@ -1448,9 +1450,11 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
>                 hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>                 hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
>                 drm_mode_connector_update_edid_property(connector, edid);
> +               hdmi_event_new_edid(hdmi->n, edid, 0);
>                 ret = drm_add_edid_modes(connector, edid);
>                 /* Store the ELD */
>                 drm_edid_to_eld(connector, edid);
> +               hdmi_event_new_eld(hdmi->n, connector->eld);
>                 kfree(edid);
>         } else {
>                 dev_dbg(hdmi->dev, "failed to get edid\n");
> @@ -1579,6 +1583,12 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>                         dw_hdmi_update_phy_mask(hdmi);
>                 }
>                 mutex_unlock(&hdmi->mutex);
> +
> +               if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) = 0)
> +                       hdmi_event_disconnect(hdmi->n);
> +               else if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) =
> +                        (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_PHY_HPD))
> +                       hdmi_event_connect(hdmi->n);
>         }
>
>         if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
> @@ -1732,11 +1742,17 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>
>         initialize_hdmi_ih_mutes(hdmi);
>
> +       hdmi->n = hdmi_notifier_get(dev);
> +       if (!hdmi->n) {
> +               ret = -ENOMEM;
> +               goto err_iahb;
> +       }
> +
>         ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
>                                         dw_hdmi_irq, IRQF_SHARED,
>                                         dev_name(dev), hdmi);
>         if (ret)
> -               goto err_iahb;
> +               goto err_hdmi_not;
>
>         /*
>          * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
> @@ -1788,6 +1804,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>
>         return 0;
>
> +err_hdmi_not:
> +       hdmi_notifier_put(hdmi->n);
>  err_iahb:
>         clk_disable_unprepare(hdmi->iahb_clk);
>  err_isfr:
> @@ -1804,6 +1822,11 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
>         if (hdmi->audio && !IS_ERR(hdmi->audio))
>                 platform_device_unregister(hdmi->audio);
>
> +       hdmi_notifier_put(hdmi->n);
> +
> +       if (!IS_ERR(hdmi->cec))
> +               platform_device_unregister(hdmi->cec);
Those two lines should be in your 4/5
> +
>         /* Disable all interrupts */
>         hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 3/5] drm/bridge: dw_hdmi: add HDMI notifier support
@ 2016-11-15 23:21     ` Pierre-Hugues Husson
  0 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2016-11-15 23:21 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Hans,

2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
>
> Add HDMI notifiers to the HDMI bridge driver.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/Kconfig   |  1 +
>  drivers/gpu/drm/bridge/dw-hdmi.c | 25 ++++++++++++++++++++++++-
>  2 files changed, 25 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 10e12e7..5f4ebe9 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -27,6 +27,7 @@ config DRM_DUMB_VGA_DAC
>  config DRM_DW_HDMI
>         tristate
>         select DRM_KMS_HELPER
> +       select HDMI_NOTIFIERS
>
>  config DRM_DW_HDMI_AHB_AUDIO
>         tristate "Synopsis Designware AHB Audio interface"
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
> index ab7023e..bd02da5 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/dw-hdmi.c
> @@ -16,6 +16,7 @@
>  #include <linux/err.h>
>  #include <linux/clk.h>
>  #include <linux/hdmi.h>
> +#include <linux/hdmi-notifier.h>
>  #include <linux/mutex.h>
>  #include <linux/of_device.h>
>  #include <linux/spinlock.h>
> @@ -114,6 +115,7 @@ struct dw_hdmi {
>
>         struct hdmi_data_info hdmi_data;
>         const struct dw_hdmi_plat_data *plat_data;
> +       struct hdmi_notifier *n;
>
>         int vic;
>
> @@ -1448,9 +1450,11 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
>                 hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>                 hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
>                 drm_mode_connector_update_edid_property(connector, edid);
> +               hdmi_event_new_edid(hdmi->n, edid, 0);
>                 ret = drm_add_edid_modes(connector, edid);
>                 /* Store the ELD */
>                 drm_edid_to_eld(connector, edid);
> +               hdmi_event_new_eld(hdmi->n, connector->eld);
>                 kfree(edid);
>         } else {
>                 dev_dbg(hdmi->dev, "failed to get edid\n");
> @@ -1579,6 +1583,12 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>                         dw_hdmi_update_phy_mask(hdmi);
>                 }
>                 mutex_unlock(&hdmi->mutex);
> +
> +               if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0)
> +                       hdmi_event_disconnect(hdmi->n);
> +               else if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) ==
> +                        (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_PHY_HPD))
> +                       hdmi_event_connect(hdmi->n);
>         }
>
>         if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
> @@ -1732,11 +1742,17 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>
>         initialize_hdmi_ih_mutes(hdmi);
>
> +       hdmi->n = hdmi_notifier_get(dev);
> +       if (!hdmi->n) {
> +               ret = -ENOMEM;
> +               goto err_iahb;
> +       }
> +
>         ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
>                                         dw_hdmi_irq, IRQF_SHARED,
>                                         dev_name(dev), hdmi);
>         if (ret)
> -               goto err_iahb;
> +               goto err_hdmi_not;
>
>         /*
>          * To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
> @@ -1788,6 +1804,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
>
>         return 0;
>
> +err_hdmi_not:
> +       hdmi_notifier_put(hdmi->n);
>  err_iahb:
>         clk_disable_unprepare(hdmi->iahb_clk);
>  err_isfr:
> @@ -1804,6 +1822,11 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
>         if (hdmi->audio && !IS_ERR(hdmi->audio))
>                 platform_device_unregister(hdmi->audio);
>
> +       hdmi_notifier_put(hdmi->n);
> +
> +       if (!IS_ERR(hdmi->cec))
> +               platform_device_unregister(hdmi->cec);
Those two lines should be in your 4/5
> +
>         /* Disable all interrupts */
>         hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
  2016-11-14 15:22   ` Hans Verkuil
  (?)
@ 2016-11-15 23:23     ` Pierre-Hugues Husson
  -1 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2016-11-15 23:23 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, linux-arm-kernel, Russell King, linux-fbdev,
	Russell King - ARM Linux, dri-devel

Hi,


2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
>
> We don't need the CEC engine register definitions, so let's remove them.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
>  1 file changed, 45 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
> index fc9a560..26d6845 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi.h
> +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
> @@ -478,51 +478,6 @@
>  #define HDMI_A_PRESETUP                         0x501A
>  #define HDMI_A_SRM_BASE                         0x5020
>
> -/* CEC Engine Registers */
> -#define HDMI_CEC_CTRL                           0x7D00
> -#define HDMI_CEC_STAT                           0x7D01
> -#define HDMI_CEC_MASK                           0x7D02
I don't know if this is relevant for a submission, but the build stops
working here because of a missing definition HDMI_CEC_MASK
Perhaps this should be inverted with 3/5 to make bissecting easier?
I was trying to bissect a kernel panic, and I had to fix this by hand
> -#define HDMI_CEC_POLARITY                       0x7D03
> -#define HDMI_CEC_INT                            0x7D04
> -#define HDMI_CEC_ADDR_L                         0x7D05
> -#define HDMI_CEC_ADDR_H                         0x7D06
> -#define HDMI_CEC_TX_CNT                         0x7D07
> -#define HDMI_CEC_RX_CNT                         0x7D08
> -#define HDMI_CEC_TX_DATA0                       0x7D10
> -#define HDMI_CEC_TX_DATA1                       0x7D11
> -#define HDMI_CEC_TX_DATA2                       0x7D12
> -#define HDMI_CEC_TX_DATA3                       0x7D13
> -#define HDMI_CEC_TX_DATA4                       0x7D14
> -#define HDMI_CEC_TX_DATA5                       0x7D15
> -#define HDMI_CEC_TX_DATA6                       0x7D16
> -#define HDMI_CEC_TX_DATA7                       0x7D17
> -#define HDMI_CEC_TX_DATA8                       0x7D18
> -#define HDMI_CEC_TX_DATA9                       0x7D19
> -#define HDMI_CEC_TX_DATA10                      0x7D1a
> -#define HDMI_CEC_TX_DATA11                      0x7D1b
> -#define HDMI_CEC_TX_DATA12                      0x7D1c
> -#define HDMI_CEC_TX_DATA13                      0x7D1d
> -#define HDMI_CEC_TX_DATA14                      0x7D1e
> -#define HDMI_CEC_TX_DATA15                      0x7D1f
> -#define HDMI_CEC_RX_DATA0                       0x7D20
> -#define HDMI_CEC_RX_DATA1                       0x7D21
> -#define HDMI_CEC_RX_DATA2                       0x7D22
> -#define HDMI_CEC_RX_DATA3                       0x7D23
> -#define HDMI_CEC_RX_DATA4                       0x7D24
> -#define HDMI_CEC_RX_DATA5                       0x7D25
> -#define HDMI_CEC_RX_DATA6                       0x7D26
> -#define HDMI_CEC_RX_DATA7                       0x7D27
> -#define HDMI_CEC_RX_DATA8                       0x7D28
> -#define HDMI_CEC_RX_DATA9                       0x7D29
> -#define HDMI_CEC_RX_DATA10                      0x7D2a
> -#define HDMI_CEC_RX_DATA11                      0x7D2b
> -#define HDMI_CEC_RX_DATA12                      0x7D2c
> -#define HDMI_CEC_RX_DATA13                      0x7D2d
> -#define HDMI_CEC_RX_DATA14                      0x7D2e
> -#define HDMI_CEC_RX_DATA15                      0x7D2f
> -#define HDMI_CEC_LOCK                           0x7D30
> -#define HDMI_CEC_WKUPCTRL                       0x7D31
> -
>  /* I2C Master Registers (E-DDC) */
>  #define HDMI_I2CM_SLAVE                         0x7E00
>  #define HDMI_I2CM_ADDRESS                       0x7E01
> --
> 2.8.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
@ 2016-11-15 23:23     ` Pierre-Hugues Husson
  0 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2016-11-15 23:23 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, linux-arm-kernel, Russell King, linux-fbdev,
	Russell King - ARM Linux, dri-devel

Hi,


2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
>
> We don't need the CEC engine register definitions, so let's remove them.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
>  1 file changed, 45 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
> index fc9a560..26d6845 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi.h
> +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
> @@ -478,51 +478,6 @@
>  #define HDMI_A_PRESETUP                         0x501A
>  #define HDMI_A_SRM_BASE                         0x5020
>
> -/* CEC Engine Registers */
> -#define HDMI_CEC_CTRL                           0x7D00
> -#define HDMI_CEC_STAT                           0x7D01
> -#define HDMI_CEC_MASK                           0x7D02
I don't know if this is relevant for a submission, but the build stops
working here because of a missing definition HDMI_CEC_MASK
Perhaps this should be inverted with 3/5 to make bissecting easier?
I was trying to bissect a kernel panic, and I had to fix this by hand
> -#define HDMI_CEC_POLARITY                       0x7D03
> -#define HDMI_CEC_INT                            0x7D04
> -#define HDMI_CEC_ADDR_L                         0x7D05
> -#define HDMI_CEC_ADDR_H                         0x7D06
> -#define HDMI_CEC_TX_CNT                         0x7D07
> -#define HDMI_CEC_RX_CNT                         0x7D08
> -#define HDMI_CEC_TX_DATA0                       0x7D10
> -#define HDMI_CEC_TX_DATA1                       0x7D11
> -#define HDMI_CEC_TX_DATA2                       0x7D12
> -#define HDMI_CEC_TX_DATA3                       0x7D13
> -#define HDMI_CEC_TX_DATA4                       0x7D14
> -#define HDMI_CEC_TX_DATA5                       0x7D15
> -#define HDMI_CEC_TX_DATA6                       0x7D16
> -#define HDMI_CEC_TX_DATA7                       0x7D17
> -#define HDMI_CEC_TX_DATA8                       0x7D18
> -#define HDMI_CEC_TX_DATA9                       0x7D19
> -#define HDMI_CEC_TX_DATA10                      0x7D1a
> -#define HDMI_CEC_TX_DATA11                      0x7D1b
> -#define HDMI_CEC_TX_DATA12                      0x7D1c
> -#define HDMI_CEC_TX_DATA13                      0x7D1d
> -#define HDMI_CEC_TX_DATA14                      0x7D1e
> -#define HDMI_CEC_TX_DATA15                      0x7D1f
> -#define HDMI_CEC_RX_DATA0                       0x7D20
> -#define HDMI_CEC_RX_DATA1                       0x7D21
> -#define HDMI_CEC_RX_DATA2                       0x7D22
> -#define HDMI_CEC_RX_DATA3                       0x7D23
> -#define HDMI_CEC_RX_DATA4                       0x7D24
> -#define HDMI_CEC_RX_DATA5                       0x7D25
> -#define HDMI_CEC_RX_DATA6                       0x7D26
> -#define HDMI_CEC_RX_DATA7                       0x7D27
> -#define HDMI_CEC_RX_DATA8                       0x7D28
> -#define HDMI_CEC_RX_DATA9                       0x7D29
> -#define HDMI_CEC_RX_DATA10                      0x7D2a
> -#define HDMI_CEC_RX_DATA11                      0x7D2b
> -#define HDMI_CEC_RX_DATA12                      0x7D2c
> -#define HDMI_CEC_RX_DATA13                      0x7D2d
> -#define HDMI_CEC_RX_DATA14                      0x7D2e
> -#define HDMI_CEC_RX_DATA15                      0x7D2f
> -#define HDMI_CEC_LOCK                           0x7D30
> -#define HDMI_CEC_WKUPCTRL                       0x7D31
> -
>  /* I2C Master Registers (E-DDC) */
>  #define HDMI_I2CM_SLAVE                         0x7E00
>  #define HDMI_I2CM_ADDRESS                       0x7E01
> --
> 2.8.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
@ 2016-11-15 23:23     ` Pierre-Hugues Husson
  0 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2016-11-15 23:23 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,


2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
>
> We don't need the CEC engine register definitions, so let's remove them.
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
>  1 file changed, 45 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
> index fc9a560..26d6845 100644
> --- a/drivers/gpu/drm/bridge/dw-hdmi.h
> +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
> @@ -478,51 +478,6 @@
>  #define HDMI_A_PRESETUP                         0x501A
>  #define HDMI_A_SRM_BASE                         0x5020
>
> -/* CEC Engine Registers */
> -#define HDMI_CEC_CTRL                           0x7D00
> -#define HDMI_CEC_STAT                           0x7D01
> -#define HDMI_CEC_MASK                           0x7D02
I don't know if this is relevant for a submission, but the build stops
working here because of a missing definition HDMI_CEC_MASK
Perhaps this should be inverted with 3/5 to make bissecting easier?
I was trying to bissect a kernel panic, and I had to fix this by hand
> -#define HDMI_CEC_POLARITY                       0x7D03
> -#define HDMI_CEC_INT                            0x7D04
> -#define HDMI_CEC_ADDR_L                         0x7D05
> -#define HDMI_CEC_ADDR_H                         0x7D06
> -#define HDMI_CEC_TX_CNT                         0x7D07
> -#define HDMI_CEC_RX_CNT                         0x7D08
> -#define HDMI_CEC_TX_DATA0                       0x7D10
> -#define HDMI_CEC_TX_DATA1                       0x7D11
> -#define HDMI_CEC_TX_DATA2                       0x7D12
> -#define HDMI_CEC_TX_DATA3                       0x7D13
> -#define HDMI_CEC_TX_DATA4                       0x7D14
> -#define HDMI_CEC_TX_DATA5                       0x7D15
> -#define HDMI_CEC_TX_DATA6                       0x7D16
> -#define HDMI_CEC_TX_DATA7                       0x7D17
> -#define HDMI_CEC_TX_DATA8                       0x7D18
> -#define HDMI_CEC_TX_DATA9                       0x7D19
> -#define HDMI_CEC_TX_DATA10                      0x7D1a
> -#define HDMI_CEC_TX_DATA11                      0x7D1b
> -#define HDMI_CEC_TX_DATA12                      0x7D1c
> -#define HDMI_CEC_TX_DATA13                      0x7D1d
> -#define HDMI_CEC_TX_DATA14                      0x7D1e
> -#define HDMI_CEC_TX_DATA15                      0x7D1f
> -#define HDMI_CEC_RX_DATA0                       0x7D20
> -#define HDMI_CEC_RX_DATA1                       0x7D21
> -#define HDMI_CEC_RX_DATA2                       0x7D22
> -#define HDMI_CEC_RX_DATA3                       0x7D23
> -#define HDMI_CEC_RX_DATA4                       0x7D24
> -#define HDMI_CEC_RX_DATA5                       0x7D25
> -#define HDMI_CEC_RX_DATA6                       0x7D26
> -#define HDMI_CEC_RX_DATA7                       0x7D27
> -#define HDMI_CEC_RX_DATA8                       0x7D28
> -#define HDMI_CEC_RX_DATA9                       0x7D29
> -#define HDMI_CEC_RX_DATA10                      0x7D2a
> -#define HDMI_CEC_RX_DATA11                      0x7D2b
> -#define HDMI_CEC_RX_DATA12                      0x7D2c
> -#define HDMI_CEC_RX_DATA13                      0x7D2d
> -#define HDMI_CEC_RX_DATA14                      0x7D2e
> -#define HDMI_CEC_RX_DATA15                      0x7D2f
> -#define HDMI_CEC_LOCK                           0x7D30
> -#define HDMI_CEC_WKUPCTRL                       0x7D31
> -
>  /* I2C Master Registers (E-DDC) */
>  #define HDMI_I2CM_SLAVE                         0x7E00
>  #define HDMI_I2CM_ADDRESS                       0x7E01
> --
> 2.8.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
  2016-11-15 23:23     ` Pierre-Hugues Husson
@ 2016-11-15 23:27       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 55+ messages in thread
From: Russell King - ARM Linux @ 2016-11-15 23:27 UTC (permalink / raw)
  To: Pierre-Hugues Husson
  Cc: Hans Verkuil, linux-media, linux-arm-kernel, linux-fbdev, dri-devel

On Wed, Nov 16, 2016 at 12:23:50AM +0100, Pierre-Hugues Husson wrote:
> Hi,
> 
> 
> 2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> > From: Russell King <rmk+kernel@arm.linux.org.uk>
> >
> > We don't need the CEC engine register definitions, so let's remove them.
> >
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > ---
> >  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
> >  1 file changed, 45 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
> > index fc9a560..26d6845 100644
> > --- a/drivers/gpu/drm/bridge/dw-hdmi.h
> > +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
> > @@ -478,51 +478,6 @@
> >  #define HDMI_A_PRESETUP                         0x501A
> >  #define HDMI_A_SRM_BASE                         0x5020
> >
> > -/* CEC Engine Registers */
> > -#define HDMI_CEC_CTRL                           0x7D00
> > -#define HDMI_CEC_STAT                           0x7D01
> > -#define HDMI_CEC_MASK                           0x7D02
> I don't know if this is relevant for a submission, but the build stops
> working here because of a missing definition HDMI_CEC_MASK
> Perhaps this should be inverted with 3/5 to make bissecting easier?
> I was trying to bissect a kernel panic, and I had to fix this by hand

Doesn't make sense - patch 3 doesn't reference HDMI_CEC_MASK.

Please show the build error in full.

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
@ 2016-11-15 23:27       ` Russell King - ARM Linux
  0 siblings, 0 replies; 55+ messages in thread
From: Russell King - ARM Linux @ 2016-11-15 23:27 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Nov 16, 2016 at 12:23:50AM +0100, Pierre-Hugues Husson wrote:
> Hi,
> 
> 
> 2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> > From: Russell King <rmk+kernel@arm.linux.org.uk>
> >
> > We don't need the CEC engine register definitions, so let's remove them.
> >
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > ---
> >  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
> >  1 file changed, 45 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
> > index fc9a560..26d6845 100644
> > --- a/drivers/gpu/drm/bridge/dw-hdmi.h
> > +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
> > @@ -478,51 +478,6 @@
> >  #define HDMI_A_PRESETUP                         0x501A
> >  #define HDMI_A_SRM_BASE                         0x5020
> >
> > -/* CEC Engine Registers */
> > -#define HDMI_CEC_CTRL                           0x7D00
> > -#define HDMI_CEC_STAT                           0x7D01
> > -#define HDMI_CEC_MASK                           0x7D02
> I don't know if this is relevant for a submission, but the build stops
> working here because of a missing definition HDMI_CEC_MASK
> Perhaps this should be inverted with 3/5 to make bissecting easier?
> I was trying to bissect a kernel panic, and I had to fix this by hand

Doesn't make sense - patch 3 doesn't reference HDMI_CEC_MASK.

Please show the build error in full.

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
  2016-11-15 23:27       ` Russell King - ARM Linux
  (?)
@ 2016-11-15 23:35         ` Pierre-Hugues Husson
  -1 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2016-11-15 23:35 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Hans Verkuil, linux-media, linux-arm-kernel, linux-fbdev, dri-devel

2016-11-16 0:27 GMT+01:00 Russell King - ARM Linux <linux@armlinux.org.uk>:
> On Wed, Nov 16, 2016 at 12:23:50AM +0100, Pierre-Hugues Husson wrote:
>> Hi,
>>
>>
>> 2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
>> > From: Russell King <rmk+kernel@arm.linux.org.uk>
>> >
>> > We don't need the CEC engine register definitions, so let's remove them.
>> >
>> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> > ---
>> >  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
>> >  1 file changed, 45 deletions(-)
>> >
>> > diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
>> > index fc9a560..26d6845 100644
>> > --- a/drivers/gpu/drm/bridge/dw-hdmi.h
>> > +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
>> > @@ -478,51 +478,6 @@
>> >  #define HDMI_A_PRESETUP                         0x501A
>> >  #define HDMI_A_SRM_BASE                         0x5020
>> >
>> > -/* CEC Engine Registers */
>> > -#define HDMI_CEC_CTRL                           0x7D00
>> > -#define HDMI_CEC_STAT                           0x7D01
>> > -#define HDMI_CEC_MASK                           0x7D02
>> I don't know if this is relevant for a submission, but the build stops
>> working here because of a missing definition HDMI_CEC_MASK
>> Perhaps this should be inverted with 3/5 to make bissecting easier?
>> I was trying to bissect a kernel panic, and I had to fix this by hand
>
> Doesn't make sense - patch 3 doesn't reference HDMI_CEC_MASK.
>
> Please show the build error in full.
The build is actually fixed with patch 4.

Building after patch 2 fails with:
drivers/gpu/drm/bridge/dw-hdmi.c: In function ‘initialize_hdmi_ih_mutes’:
drivers/gpu/drm/bridge/dw-hdmi.c:1300:26: error: ‘HDMI_CEC_MASK’
undeclared (first use in this function)
  hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);

The point of switching patch 3 and patch 2, is that the build works
with patch 1,3 applied.
Applying patch 2 breaks the build, but doesn't change any active code,
so it's ok.

So with the order 1,3,2,4,5, the build is broken only after 2, while
with 1,2,3,4,5, it is broken after 2 and 3.

I hope this makes my remark more explicit.

If it doesn't, I think it is quite safe to just ignore it

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
@ 2016-11-15 23:35         ` Pierre-Hugues Husson
  0 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2016-11-15 23:35 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Hans Verkuil, linux-media, linux-arm-kernel, linux-fbdev, dri-devel

2016-11-16 0:27 GMT+01:00 Russell King - ARM Linux <linux@armlinux.org.uk>:
> On Wed, Nov 16, 2016 at 12:23:50AM +0100, Pierre-Hugues Husson wrote:
>> Hi,
>>
>>
>> 2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
>> > From: Russell King <rmk+kernel@arm.linux.org.uk>
>> >
>> > We don't need the CEC engine register definitions, so let's remove them.
>> >
>> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> > ---
>> >  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
>> >  1 file changed, 45 deletions(-)
>> >
>> > diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
>> > index fc9a560..26d6845 100644
>> > --- a/drivers/gpu/drm/bridge/dw-hdmi.h
>> > +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
>> > @@ -478,51 +478,6 @@
>> >  #define HDMI_A_PRESETUP                         0x501A
>> >  #define HDMI_A_SRM_BASE                         0x5020
>> >
>> > -/* CEC Engine Registers */
>> > -#define HDMI_CEC_CTRL                           0x7D00
>> > -#define HDMI_CEC_STAT                           0x7D01
>> > -#define HDMI_CEC_MASK                           0x7D02
>> I don't know if this is relevant for a submission, but the build stops
>> working here because of a missing definition HDMI_CEC_MASK
>> Perhaps this should be inverted with 3/5 to make bissecting easier?
>> I was trying to bissect a kernel panic, and I had to fix this by hand
>
> Doesn't make sense - patch 3 doesn't reference HDMI_CEC_MASK.
>
> Please show the build error in full.
The build is actually fixed with patch 4.

Building after patch 2 fails with:
drivers/gpu/drm/bridge/dw-hdmi.c: In function ‘initialize_hdmi_ih_mutes’:
drivers/gpu/drm/bridge/dw-hdmi.c:1300:26: error: ‘HDMI_CEC_MASK’
undeclared (first use in this function)
  hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);

The point of switching patch 3 and patch 2, is that the build works
with patch 1,3 applied.
Applying patch 2 breaks the build, but doesn't change any active code,
so it's ok.

So with the order 1,3,2,4,5, the build is broken only after 2, while
with 1,2,3,4,5, it is broken after 2 and 3.

I hope this makes my remark more explicit.

If it doesn't, I think it is quite safe to just ignore it

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions
@ 2016-11-15 23:35         ` Pierre-Hugues Husson
  0 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2016-11-15 23:35 UTC (permalink / raw)
  To: linux-arm-kernel

2016-11-16 0:27 GMT+01:00 Russell King - ARM Linux <linux@armlinux.org.uk>:
> On Wed, Nov 16, 2016 at 12:23:50AM +0100, Pierre-Hugues Husson wrote:
>> Hi,
>>
>>
>> 2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
>> > From: Russell King <rmk+kernel@arm.linux.org.uk>
>> >
>> > We don't need the CEC engine register definitions, so let's remove them.
>> >
>> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> > ---
>> >  drivers/gpu/drm/bridge/dw-hdmi.h | 45 ----------------------------------------
>> >  1 file changed, 45 deletions(-)
>> >
>> > diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h
>> > index fc9a560..26d6845 100644
>> > --- a/drivers/gpu/drm/bridge/dw-hdmi.h
>> > +++ b/drivers/gpu/drm/bridge/dw-hdmi.h
>> > @@ -478,51 +478,6 @@
>> >  #define HDMI_A_PRESETUP                         0x501A
>> >  #define HDMI_A_SRM_BASE                         0x5020
>> >
>> > -/* CEC Engine Registers */
>> > -#define HDMI_CEC_CTRL                           0x7D00
>> > -#define HDMI_CEC_STAT                           0x7D01
>> > -#define HDMI_CEC_MASK                           0x7D02
>> I don't know if this is relevant for a submission, but the build stops
>> working here because of a missing definition HDMI_CEC_MASK
>> Perhaps this should be inverted with 3/5 to make bissecting easier?
>> I was trying to bissect a kernel panic, and I had to fix this by hand
>
> Doesn't make sense - patch 3 doesn't reference HDMI_CEC_MASK.
>
> Please show the build error in full.
The build is actually fixed with patch 4.

Building after patch 2 fails with:
drivers/gpu/drm/bridge/dw-hdmi.c: In function ?initialize_hdmi_ih_mutes?:
drivers/gpu/drm/bridge/dw-hdmi.c:1300:26: error: ?HDMI_CEC_MASK?
undeclared (first use in this function)
  hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);

The point of switching patch 3 and patch 2, is that the build works
with patch 1,3 applied.
Applying patch 2 breaks the build, but doesn't change any active code,
so it's ok.

So with the order 1,3,2,4,5, the build is broken only after 2, while
with 1,2,3,4,5, it is broken after 2 and 3.

I hope this makes my remark more explicit.

If it doesn't, I think it is quite safe to just ignore it

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 1/5] video: add HDMI state notifier support
  2016-11-15 20:41       ` Hans Verkuil
  (?)
@ 2016-11-16  9:43         ` Hans Verkuil
  -1 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-16  9:43 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: linux-media, Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Hans Verkuil

On 11/15/16 21:41, Hans Verkuil wrote:
> 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@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@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);
>>> +

I also forgot to remove this notifier from the global list.

I've updated my git branch to fix this.

	Hans

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 1/5] video: add HDMI state notifier support
@ 2016-11-16  9:43         ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-16  9:43 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: linux-media, Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Hans Verkuil

On 11/15/16 21:41, Hans Verkuil wrote:
> 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@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@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);
>>> +

I also forgot to remove this notifier from the global list.

I've updated my git branch to fix this.

	Hans

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 1/5] video: add HDMI state notifier support
@ 2016-11-16  9:43         ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-11-16  9:43 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/15/16 21:41, Hans Verkuil wrote:
> 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@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@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);
>>> +

I also forgot to remove this notifier from the global list.

I've updated my git branch to fix this.

	Hans

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 1/5] video: add HDMI state notifier support
  2016-11-14 15:22   ` Hans Verkuil
  (?)
  (?)
@ 2016-12-01 10:09     ` Hans Verkuil
  -1 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-12-01 10:09 UTC (permalink / raw)
  To: linux-media
  Cc: Russell King - ARM Linux, linux-fbdev, dri-devel,
	linux-arm-kernel, Hans Verkuil

On 11/14/16 16:22, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@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.

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 <hans.verkuil@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;
> +};
> +
> +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
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 1/5] video: add HDMI state notifier support
@ 2016-12-01 10:09     ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-12-01 10:09 UTC (permalink / raw)
  To: linux-media
  Cc: linux-arm-kernel, linux-fbdev, Hans Verkuil,
	Russell King - ARM Linux, dri-devel

On 11/14/16 16:22, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@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.

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 <hans.verkuil@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;
> +};
> +
> +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
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 1/5] video: add HDMI state notifier support
@ 2016-12-01 10:09     ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-12-01 10:09 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/14/16 16:22, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@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.

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 <hans.verkuil@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;
> +};
> +
> +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
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 1/5] video: add HDMI state notifier support
@ 2016-12-01 10:09     ` Hans Verkuil
  0 siblings, 0 replies; 55+ messages in thread
From: Hans Verkuil @ 2016-12-01 10:09 UTC (permalink / raw)
  To: linux-media
  Cc: linux-arm-kernel, linux-fbdev, Hans Verkuil,
	Russell King - ARM Linux, dri-devel

On 11/14/16 16:22, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@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.

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 <hans.verkuil@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;
> +};
> +
> +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
>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 4/5] drm/bridge: add dw-hdmi cec driver using Hans Verkuil's CEC code
  2016-11-14 15:22   ` Hans Verkuil
  (?)
@ 2017-02-05  0:17     ` Pierre-Hugues Husson
  -1 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2017-02-05  0:17 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, linux-arm-kernel, linux-fbdev, Russell King,
	Russell King - ARM Linux, dri-devel

Hi,

2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
>
> Add a CEC driver for the dw-hdmi hardware using Hans Verkuil's CEC
> implementation.
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
I've seen that the patchset concerning CEC/HDMI notifier after this
one dropped dw-hdmi support.
Is this only temporary, or does this driver need someone to take care of it?

> diff --git a/drivers/gpu/drm/bridge/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/dw-hdmi-cec.c
> new file mode 100644
> index 0000000..e7e12b5
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw-hdmi-cec.c
> @@ -0,0 +1,346 @@
> +/* http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/
> + * tree/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c?h=imx_3.0.35_4.1.0 */
It is perhaps mandatory to have GPL header?

> +#include <linux/hdmi-notifier.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/notifier.h>
> +#include <linux/platform_data/dw_hdmi-cec.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +
> +#include <drm/drm_edid.h>
> +
> +#include <media/cec.h>
> +#include <media/cec-edid.h>
> +
> +#define DEV_NAME "mxc_hdmi_cec"
I think that to respect the convention it should be dw-hdmi-cec?

> +       writeb_relaxed(addresses & 255, cec->base + HDMI_CEC_ADDR_L);
> +       writeb_relaxed(addresses >> 8, cec->base + HDMI_CEC_ADDR_H);
Some platforms (at least rockchip) discuss with dw-hdmi with longs
instead of bytes
dw-hdmi-i2s-audio.c uses hdmi_read/hdmi_write for that

Is it ok to add write and read functions to dw_hdmi_cec_ops ?

> +static unsigned int parse_hdmi_addr(const struct edid *edid)
> +{
> +       if (!edid || edid->extensions == 0)
> +               return (u16)~0;
> +
> +       return cec_get_edid_phys_addr((u8 *)edid,
> +                               EDID_LENGTH * (edid->extensions + 1), NULL);
> +}
> +
> +static int dw_hdmi_cec_notify(struct notifier_block *nb, unsigned long event,
> +                             void *data)
> +{
> +       struct dw_hdmi_cec *cec = container_of(nb, struct dw_hdmi_cec, nb);
> +       struct hdmi_notifier *n = data;
> +       unsigned int phys;
> +
> +       dev_info(cec->adap->devnode.parent, "event %lu\n", event);
> +
> +       switch (event) {
> +       case HDMI_CONNECTED:
> +               break;
> +
> +       case HDMI_DISCONNECTED:
> +               cec_s_phys_addr(cec->adap, CEC_PHYS_ADDR_INVALID, false);
> +               break;
> +
> +       case HDMI_NEW_EDID:
> +               phys = parse_hdmi_addr(n->edid);
> +               cec_s_phys_addr(cec->adap, phys, false);
> +               break;
> +       }
> +
> +       return NOTIFY_OK;
> +}
Thanks to "cec: integrate HDMI notifier support" this code can be dropped

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 4/5] drm/bridge: add dw-hdmi cec driver using Hans Verkuil's CEC code
@ 2017-02-05  0:17     ` Pierre-Hugues Husson
  0 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2017-02-05  0:17 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, linux-arm-kernel, linux-fbdev, Russell King,
	Russell King - ARM Linux, dri-devel

Hi,

2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
>
> Add a CEC driver for the dw-hdmi hardware using Hans Verkuil's CEC
> implementation.
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
I've seen that the patchset concerning CEC/HDMI notifier after this
one dropped dw-hdmi support.
Is this only temporary, or does this driver need someone to take care of it?

> diff --git a/drivers/gpu/drm/bridge/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/dw-hdmi-cec.c
> new file mode 100644
> index 0000000..e7e12b5
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw-hdmi-cec.c
> @@ -0,0 +1,346 @@
> +/* http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/
> + * tree/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c?h=imx_3.0.35_4.1.0 */
It is perhaps mandatory to have GPL header?

> +#include <linux/hdmi-notifier.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/notifier.h>
> +#include <linux/platform_data/dw_hdmi-cec.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +
> +#include <drm/drm_edid.h>
> +
> +#include <media/cec.h>
> +#include <media/cec-edid.h>
> +
> +#define DEV_NAME "mxc_hdmi_cec"
I think that to respect the convention it should be dw-hdmi-cec?

> +       writeb_relaxed(addresses & 255, cec->base + HDMI_CEC_ADDR_L);
> +       writeb_relaxed(addresses >> 8, cec->base + HDMI_CEC_ADDR_H);
Some platforms (at least rockchip) discuss with dw-hdmi with longs
instead of bytes
dw-hdmi-i2s-audio.c uses hdmi_read/hdmi_write for that

Is it ok to add write and read functions to dw_hdmi_cec_ops ?

> +static unsigned int parse_hdmi_addr(const struct edid *edid)
> +{
> +       if (!edid || edid->extensions = 0)
> +               return (u16)~0;
> +
> +       return cec_get_edid_phys_addr((u8 *)edid,
> +                               EDID_LENGTH * (edid->extensions + 1), NULL);
> +}
> +
> +static int dw_hdmi_cec_notify(struct notifier_block *nb, unsigned long event,
> +                             void *data)
> +{
> +       struct dw_hdmi_cec *cec = container_of(nb, struct dw_hdmi_cec, nb);
> +       struct hdmi_notifier *n = data;
> +       unsigned int phys;
> +
> +       dev_info(cec->adap->devnode.parent, "event %lu\n", event);
> +
> +       switch (event) {
> +       case HDMI_CONNECTED:
> +               break;
> +
> +       case HDMI_DISCONNECTED:
> +               cec_s_phys_addr(cec->adap, CEC_PHYS_ADDR_INVALID, false);
> +               break;
> +
> +       case HDMI_NEW_EDID:
> +               phys = parse_hdmi_addr(n->edid);
> +               cec_s_phys_addr(cec->adap, phys, false);
> +               break;
> +       }
> +
> +       return NOTIFY_OK;
> +}
Thanks to "cec: integrate HDMI notifier support" this code can be dropped

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 4/5] drm/bridge: add dw-hdmi cec driver using Hans Verkuil's CEC code
@ 2017-02-05  0:17     ` Pierre-Hugues Husson
  0 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2017-02-05  0:17 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
>
> Add a CEC driver for the dw-hdmi hardware using Hans Verkuil's CEC
> implementation.
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
I've seen that the patchset concerning CEC/HDMI notifier after this
one dropped dw-hdmi support.
Is this only temporary, or does this driver need someone to take care of it?

> diff --git a/drivers/gpu/drm/bridge/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/dw-hdmi-cec.c
> new file mode 100644
> index 0000000..e7e12b5
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/dw-hdmi-cec.c
> @@ -0,0 +1,346 @@
> +/* http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/
> + * tree/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c?h=imx_3.0.35_4.1.0 */
It is perhaps mandatory to have GPL header?

> +#include <linux/hdmi-notifier.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/notifier.h>
> +#include <linux/platform_data/dw_hdmi-cec.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +
> +#include <drm/drm_edid.h>
> +
> +#include <media/cec.h>
> +#include <media/cec-edid.h>
> +
> +#define DEV_NAME "mxc_hdmi_cec"
I think that to respect the convention it should be dw-hdmi-cec?

> +       writeb_relaxed(addresses & 255, cec->base + HDMI_CEC_ADDR_L);
> +       writeb_relaxed(addresses >> 8, cec->base + HDMI_CEC_ADDR_H);
Some platforms (at least rockchip) discuss with dw-hdmi with longs
instead of bytes
dw-hdmi-i2s-audio.c uses hdmi_read/hdmi_write for that

Is it ok to add write and read functions to dw_hdmi_cec_ops ?

> +static unsigned int parse_hdmi_addr(const struct edid *edid)
> +{
> +       if (!edid || edid->extensions == 0)
> +               return (u16)~0;
> +
> +       return cec_get_edid_phys_addr((u8 *)edid,
> +                               EDID_LENGTH * (edid->extensions + 1), NULL);
> +}
> +
> +static int dw_hdmi_cec_notify(struct notifier_block *nb, unsigned long event,
> +                             void *data)
> +{
> +       struct dw_hdmi_cec *cec = container_of(nb, struct dw_hdmi_cec, nb);
> +       struct hdmi_notifier *n = data;
> +       unsigned int phys;
> +
> +       dev_info(cec->adap->devnode.parent, "event %lu\n", event);
> +
> +       switch (event) {
> +       case HDMI_CONNECTED:
> +               break;
> +
> +       case HDMI_DISCONNECTED:
> +               cec_s_phys_addr(cec->adap, CEC_PHYS_ADDR_INVALID, false);
> +               break;
> +
> +       case HDMI_NEW_EDID:
> +               phys = parse_hdmi_addr(n->edid);
> +               cec_s_phys_addr(cec->adap, phys, false);
> +               break;
> +       }
> +
> +       return NOTIFY_OK;
> +}
Thanks to "cec: integrate HDMI notifier support" this code can be dropped

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 3/5] drm/bridge: dw_hdmi: add HDMI notifier support
  2016-11-14 15:22   ` Hans Verkuil
  (?)
@ 2017-02-05  0:22     ` Pierre-Hugues Husson
  -1 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2017-02-05  0:22 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, linux-arm-kernel, Russell King, linux-fbdev,
	Russell King - ARM Linux, dri-devel

Hi

2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
>
> Add HDMI notifiers to the HDMI bridge driver.


> @@ -1448,9 +1450,11 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
>                 hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>                 hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
>                 drm_mode_connector_update_edid_property(connector, edid);
> +               hdmi_event_new_edid(hdmi->n, edid, 0);
Considering current hdmi-notifier code, the size must not be 0

Should this be done this way?
> hdmi_event_new_edid(hdmi->n, edid, (edid->extensions + 1) * EDID_LENGTH);

>                 ret = drm_add_edid_modes(connector, edid);
>                 /* Store the ELD */
>                 drm_edid_to_eld(connector, edid);
> +               hdmi_event_new_eld(hdmi->n, connector->eld);
>                 kfree(edid);
>         } else {
>                 dev_dbg(hdmi->dev, "failed to get edid\n");
> @@ -1579,6 +1583,12 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>                         dw_hdmi_update_phy_mask(hdmi);
>                 }
>                 mutex_unlock(&hdmi->mutex);
> +
> +               if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0)
> +                       hdmi_event_disconnect(hdmi->n);
> +               else if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) ==
> +                        (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_PHY_HPD))
> +                       hdmi_event_connect(hdmi->n);
On rk3288, I never get this event (connect) to trigger, everything
else (disconnect/edid/eld) are ok though. (which is enough for CEC)
I'll need some extra debugging...
Do you have a platform to test on, which does trigger this event?

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [RFCv2 PATCH 3/5] drm/bridge: dw_hdmi: add HDMI notifier support
@ 2017-02-05  0:22     ` Pierre-Hugues Husson
  0 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2017-02-05  0:22 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, linux-arm-kernel, Russell King, linux-fbdev,
	Russell King - ARM Linux, dri-devel

Hi

2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
>
> Add HDMI notifiers to the HDMI bridge driver.


> @@ -1448,9 +1450,11 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
>                 hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>                 hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
>                 drm_mode_connector_update_edid_property(connector, edid);
> +               hdmi_event_new_edid(hdmi->n, edid, 0);
Considering current hdmi-notifier code, the size must not be 0

Should this be done this way?
> hdmi_event_new_edid(hdmi->n, edid, (edid->extensions + 1) * EDID_LENGTH);

>                 ret = drm_add_edid_modes(connector, edid);
>                 /* Store the ELD */
>                 drm_edid_to_eld(connector, edid);
> +               hdmi_event_new_eld(hdmi->n, connector->eld);
>                 kfree(edid);
>         } else {
>                 dev_dbg(hdmi->dev, "failed to get edid\n");
> @@ -1579,6 +1583,12 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>                         dw_hdmi_update_phy_mask(hdmi);
>                 }
>                 mutex_unlock(&hdmi->mutex);
> +
> +               if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) = 0)
> +                       hdmi_event_disconnect(hdmi->n);
> +               else if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) =
> +                        (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_PHY_HPD))
> +                       hdmi_event_connect(hdmi->n);
On rk3288, I never get this event (connect) to trigger, everything
else (disconnect/edid/eld) are ok though. (which is enough for CEC)
I'll need some extra debugging...
Do you have a platform to test on, which does trigger this event?

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [RFCv2 PATCH 3/5] drm/bridge: dw_hdmi: add HDMI notifier support
@ 2017-02-05  0:22     ` Pierre-Hugues Husson
  0 siblings, 0 replies; 55+ messages in thread
From: Pierre-Hugues Husson @ 2017-02-05  0:22 UTC (permalink / raw)
  To: linux-arm-kernel

Hi

2016-11-14 16:22 GMT+01:00 Hans Verkuil <hverkuil@xs4all.nl>:
> From: Russell King <rmk+kernel@arm.linux.org.uk>
>
> Add HDMI notifiers to the HDMI bridge driver.


> @@ -1448,9 +1450,11 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
>                 hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
>                 hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
>                 drm_mode_connector_update_edid_property(connector, edid);
> +               hdmi_event_new_edid(hdmi->n, edid, 0);
Considering current hdmi-notifier code, the size must not be 0

Should this be done this way?
> hdmi_event_new_edid(hdmi->n, edid, (edid->extensions + 1) * EDID_LENGTH);

>                 ret = drm_add_edid_modes(connector, edid);
>                 /* Store the ELD */
>                 drm_edid_to_eld(connector, edid);
> +               hdmi_event_new_eld(hdmi->n, connector->eld);
>                 kfree(edid);
>         } else {
>                 dev_dbg(hdmi->dev, "failed to get edid\n");
> @@ -1579,6 +1583,12 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
>                         dw_hdmi_update_phy_mask(hdmi);
>                 }
>                 mutex_unlock(&hdmi->mutex);
> +
> +               if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) == 0)
> +                       hdmi_event_disconnect(hdmi->n);
> +               else if ((phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD)) ==
> +                        (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_PHY_HPD))
> +                       hdmi_event_connect(hdmi->n);
On rk3288, I never get this event (connect) to trigger, everything
else (disconnect/edid/eld) are ok though. (which is enough for CEC)
I'll need some extra debugging...
Do you have a platform to test on, which does trigger this event?

^ permalink raw reply	[flat|nested] 55+ messages in thread

end of thread, other threads:[~2017-02-05  0:23 UTC | newest]

Thread overview: 55+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-11-14 15:22 [RFCv2 PATCH 0/5] CEC drivers for iMX6 and TDA9950 Hans Verkuil
2016-11-14 15:22 ` Hans Verkuil
2016-11-14 15:22 ` Hans Verkuil
2016-11-14 15:22 ` [RFCv2 PATCH 1/5] video: add HDMI state notifier support Hans Verkuil
2016-11-14 15:22   ` Hans Verkuil
2016-11-14 15:22   ` Hans Verkuil
2016-11-15 18:24   ` Philipp Zabel
2016-11-15 18:24     ` Philipp Zabel
2016-11-15 18:24     ` Philipp Zabel
2016-11-15 20:41     ` Hans Verkuil
2016-11-15 20:41       ` Hans Verkuil
2016-11-15 20:41       ` Hans Verkuil
2016-11-15 20:41       ` Hans Verkuil
2016-11-16  9:43       ` Hans Verkuil
2016-11-16  9:43         ` Hans Verkuil
2016-11-16  9:43         ` Hans Verkuil
2016-12-01 10:09   ` Hans Verkuil
2016-12-01 10:09     ` Hans Verkuil
2016-12-01 10:09     ` Hans Verkuil
2016-12-01 10:09     ` Hans Verkuil
2016-11-14 15:22 ` [RFCv2 PATCH 2/5] drm/bridge: dw_hdmi: remove CEC engine register definitions Hans Verkuil
2016-11-14 15:22   ` Hans Verkuil
2016-11-14 15:22   ` Hans Verkuil
2016-11-14 15:39   ` Russell King - ARM Linux
2016-11-14 15:39     ` Russell King - ARM Linux
2016-11-14 15:42     ` Hans Verkuil
2016-11-14 15:42       ` Hans Verkuil
2016-11-14 15:42       ` Hans Verkuil
2016-11-14 15:42       ` Hans Verkuil
2016-11-15 23:23   ` Pierre-Hugues Husson
2016-11-15 23:23     ` Pierre-Hugues Husson
2016-11-15 23:23     ` Pierre-Hugues Husson
2016-11-15 23:27     ` Russell King - ARM Linux
2016-11-15 23:27       ` Russell King - ARM Linux
2016-11-15 23:35       ` Pierre-Hugues Husson
2016-11-15 23:35         ` Pierre-Hugues Husson
2016-11-15 23:35         ` Pierre-Hugues Husson
2016-11-14 15:22 ` [RFCv2 PATCH 3/5] drm/bridge: dw_hdmi: add HDMI notifier support Hans Verkuil
2016-11-14 15:22   ` Hans Verkuil
2016-11-14 15:22   ` Hans Verkuil
2016-11-15 23:21   ` Pierre-Hugues Husson
2016-11-15 23:21     ` Pierre-Hugues Husson
2016-11-15 23:21     ` Pierre-Hugues Husson
2017-02-05  0:22   ` Pierre-Hugues Husson
2017-02-05  0:22     ` Pierre-Hugues Husson
2017-02-05  0:22     ` Pierre-Hugues Husson
2016-11-14 15:22 ` [RFCv2 PATCH 4/5] drm/bridge: add dw-hdmi cec driver using Hans Verkuil's CEC code Hans Verkuil
2016-11-14 15:22   ` Hans Verkuil
2016-11-14 15:22   ` Hans Verkuil
2017-02-05  0:17   ` Pierre-Hugues Husson
2017-02-05  0:17     ` Pierre-Hugues Husson
2017-02-05  0:17     ` Pierre-Hugues Husson
2016-11-14 15:22 ` [RFCv2 PATCH 5/5] drm/i2c: add tda998x/tda9950 CEC driver Hans Verkuil
2016-11-14 15:22   ` Hans Verkuil
2016-11-14 15:22   ` Hans Verkuil

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.