From patchwork Mon Nov 14 15:22:44 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans Verkuil X-Patchwork-Id: 9427705 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 827FA60484 for ; Mon, 14 Nov 2016 15:25:35 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 72C1928781 for ; Mon, 14 Nov 2016 15:25:35 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 65E5D28783; Mon, 14 Nov 2016 15:25:35 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id EF63D28781 for ; Mon, 14 Nov 2016 15:25:34 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1c6J7A-0002XS-Ef; Mon, 14 Nov 2016 15:24:08 +0000 Received: from lb3-smtp-cloud3.xs4all.net ([194.109.24.30]) by bombadil.infradead.org with esmtps (Exim 4.85_2 #1 (Red Hat Linux)) id 1c6J6R-0002Ea-Gw for linux-arm-kernel@lists.infradead.org; Mon, 14 Nov 2016 15:23:26 +0000 Received: from tschai.lan ([90.149.38.145]) by smtp-cloud3.xs4all.net with ESMTP id 7rNw1u00A37uBN201rNzvp; Mon, 14 Nov 2016 16:23:00 +0100 Received: from tschai.fritz.box (localhost [127.0.0.1]) by tschai.lan (Postfix) with ESMTPSA id BD9C1186B75; Mon, 14 Nov 2016 16:22:48 +0100 (CET) From: Hans Verkuil To: linux-media@vger.kernel.org Subject: [RFCv2 PATCH 1/5] video: add HDMI state notifier support Date: Mon, 14 Nov 2016 16:22:44 +0100 Message-Id: <1479136968-24477-2-git-send-email-hverkuil@xs4all.nl> X-Mailer: git-send-email 2.8.1 In-Reply-To: <1479136968-24477-1-git-send-email-hverkuil@xs4all.nl> References: <1479136968-24477-1-git-send-email-hverkuil@xs4all.nl> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20161114_072323_857452_4F08ECC1 X-CRM114-Status: GOOD ( 18.24 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-arm-kernel@lists.infradead.org, linux-fbdev@vger.kernel.org, Hans Verkuil , Russell King - ARM Linux , dri-devel@lists.freedesktop.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP From: Hans Verkuil 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 Reviewed-by: Philipp Zabel Tested-by: Philipp Zabel (on MT8173) --- 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 +#include +#include +#include +#include + +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 +#include +#include + +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