From 31960cf2507485b20434b0040a22754a0c2fe911 Mon Sep 17 00:00:00 2001 From: Shashank Babu Chinta Venkata Date: Tue, 12 Oct 2021 12:33:11 -0700 Subject: [PATCH] soc: qcom: panel_event_notifier: add panel_event_notifier driver Add a notification frameowrk to notify all clients interested in getting various display panel events like power on, power off, FPS change, etc. Clients can use these notifications for power saving or align its operations with display panel power state. Change-Id: Ic4ed7f3207111824a5da84ed2a5a2e0e50e925f0 Signed-off-by: Shashank Babu Chinta Venkata --- drivers/soc/qcom/panel_event_notifier.c | 184 ++++++++++++++++++ include/linux/soc/qcom/panel_event_notifier.h | 81 ++++++++ 2 files changed, 265 insertions(+) create mode 100644 drivers/soc/qcom/panel_event_notifier.c create mode 100644 include/linux/soc/qcom/panel_event_notifier.h diff --git a/drivers/soc/qcom/panel_event_notifier.c b/drivers/soc/qcom/panel_event_notifier.c new file mode 100644 index 000000000000..f0feefa36ded --- /dev/null +++ b/drivers/soc/qcom/panel_event_notifier.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * + */ + +#include +#include +#include +#include + +struct panel_event_notifier_entry { + panel_event_notifier_handler handler; + void *pvt_data; + struct drm_panel *panel; + enum panel_event_notifier_tag tag; +}; + +static DEFINE_MUTEX(panel_event_notifier_entries_lock); +static struct panel_event_notifier_entry + panel_event_notifier_entries[PANEL_EVENT_NOTIFIER_CLIENT_MAX]; + +static bool panel_event_notifier_tag_valid(enum panel_event_notifier_tag tag) +{ + return tag > PANEL_EVENT_NOTIFICATION_NONE && + tag < PANEL_EVENT_NOTIFICATION_MAX; +} + +/** + * panel_event_notifier_register: responsible for registering clients + * interested in panel event notifications. + * clients register with a client handle and tag + * suggesting the notifications they are + * interested in and a callback which is + * triggered when the interested panel + * notifications are received. + * + * @tag: The tag for which the caller would like to receive panel events for. + * + * @client_handle: handle to recongnize the client registering for + * notifications. + * + * @panel: struct drm_panel for which the panel events are requested for. + * + * @handler: The handler that will be invoked when a panel event notification is + * received pertaining to @tag. The handler will be invoked with a + * pointer to private data registered by the client that is needed + * for servicing the notification. + * + * @pvt_data: The data that should be passed to @handler when a notification + * occurs + * + * On success, the function will return a cookie. + * + */ +void *panel_event_notifier_register(enum panel_event_notifier_tag tag, + enum panel_event_notifier_client client_handle, + struct drm_panel *panel, + panel_event_notifier_handler handler, void *pvt_data) +{ + struct panel_event_notifier_entry *entry; + + if (!panel_event_notifier_tag_valid(tag) || !handler) { + pr_err("Invalid tag or handler found while registering\n"); + return ERR_PTR(-EINVAL); + } + + if (client_handle < 0 || + client_handle >= PANEL_EVENT_NOTIFIER_CLIENT_MAX) { + pr_err("Invalid client handle used for registering\n"); + return ERR_PTR(-EINVAL); + } + + mutex_lock(&panel_event_notifier_entries_lock); + entry = &panel_event_notifier_entries[client_handle]; + if (entry->handler) { + mutex_unlock(&panel_event_notifier_entries_lock); + return ERR_PTR(-EEXIST); + } + entry->panel = panel; + entry->handler = handler; + entry->pvt_data = pvt_data; + entry->tag = tag; + mutex_unlock(&panel_event_notifier_entries_lock); + + pr_debug("client %d registered successfully\n", client_handle); + return entry; +} +EXPORT_SYMBOL(panel_event_notifier_register); + +/** + * panel_event_notifier_unregister: responsible for unregistering clients. + * + * @cookie: cookie used for unregistering client. + */ +void panel_event_notifier_unregister(void *cookie) +{ + struct panel_event_notifier_entry *entry = cookie; + + if (!cookie) + return; + + mutex_lock(&panel_event_notifier_entries_lock); + entry->panel = NULL; + entry->handler = NULL; + entry->pvt_data = NULL; + entry->tag = PANEL_EVENT_NOTIFICATION_NONE; + mutex_unlock(&panel_event_notifier_entries_lock); +} +EXPORT_SYMBOL(panel_event_notifier_unregister); + +/** + * panel_event_notifion_trigger: responsible for triggering notifications. + * Contains tag which notifies the panel + * notification is on premiary or secondary + * display and a notifcation carrying necessary + * data for clients to consume while servicing the + * notification. A handler registered by the + * client will be triggered to notify the event. + * + * @tag: tag suggesting the panel on which notification is triggered whether + *I primary or secondary display panel. + * + * @notification: contains data required for client to address the notification. + */ +void panel_event_notification_trigger(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification) +{ + struct panel_event_notifier_entry *entry; + panel_event_notifier_handler handler = NULL; + void *pvt_data; + int i; + + if (!panel_event_notifier_tag_valid(tag)) { + pr_err("Invalid panel notifier tag\n"); + return; + } + + for (i = 0; i < PANEL_EVENT_NOTIFIER_CLIENT_MAX; i++) { + mutex_lock(&panel_event_notifier_entries_lock); + entry = &panel_event_notifier_entries[i]; + if (notification->panel != entry->panel) { + pr_debug("invalid panel found notification_panel:0x%x entry_panel:0x%x\n", + notification->panel, entry->panel); + mutex_unlock(&panel_event_notifier_entries_lock); + continue; + } + + /* skip client entries not subscribed to tag */ + if (entry->tag != tag) { + pr_err("tag mismatch entry->tag:%d tag:%d\n", entry->tag, tag); + mutex_unlock(&panel_event_notifier_entries_lock); + continue; + } + + handler = entry->handler; + pvt_data = entry->pvt_data; + mutex_unlock(&panel_event_notifier_entries_lock); + + pr_debug("triggering notification for tag:%d, type:%d\n", + tag, notification->notif_type); + + if (handler) + handler(tag, notification, pvt_data); + + } +} +EXPORT_SYMBOL(panel_event_notification_trigger); + +static int __init panel_event_notifier_init(void) +{ + pr_debug("Panel event notifier initialized\n"); + return 0; +} +module_init(panel_event_notifier_init); + +static void __exit panel_event_notifier_exit(void) +{ + pr_debug("Panel event notifier exited\n"); +} +module_exit(panel_event_notifier_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Panel event notifier"); diff --git a/include/linux/soc/qcom/panel_event_notifier.h b/include/linux/soc/qcom/panel_event_notifier.h new file mode 100644 index 000000000000..db2b24921adf --- /dev/null +++ b/include/linux/soc/qcom/panel_event_notifier.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * + */ + +#ifndef __PANEL_EVENT_NOTIFIER_H +#define __PANEL_EVENT_NOTIFIER_H + +#include +#include +#include +#include + +enum panel_event_notifier_tag { + PANEL_EVENT_NOTIFICATION_NONE, + PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFICATION_SECONDARY, + PANEL_EVENT_NOTIFICATION_MAX +}; + +enum panel_event_notifier_client { + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, + PANEL_EVENT_NOTIFIER_CLIENT_SECONDARY_TOUCH, + PANEL_EVENT_NOTIFIER_CLIENT_ECM, + PANEL_EVENT_NOTIFIER_CLIENT_MAX +}; + +enum panel_event_notification_type { + DRM_PANEL_EVENT_NONE, + DRM_PANEL_EVENT_BLANK, + DRM_PANEL_EVENT_UNBLANK, + DRM_PANEL_EVENT_BLANK_LP, + DRM_PANEL_EVENT_FPS_CHANGE, + DRM_PANEL_EVENT_MAX +}; + +struct panel_event_notification_data { + u32 old_fps; + u32 new_fps; + bool early_trigger; +}; + +struct panel_event_notification { + enum panel_event_notification_type notif_type; + struct panel_event_notification_data notif_data; + struct drm_panel *panel; +}; + +typedef void (*panel_event_notifier_handler)(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, + void *pvt_data); + +#if IS_ENABLED(CONFIG_QCOM_PANEL_EVENT_NOTIFIER) +void *panel_event_notifier_register(enum panel_event_notifier_tag tag, + enum panel_event_notifier_client client_handle, + struct drm_panel *panel, + panel_event_notifier_handler notif_handler, void *pvt_data); +void panel_event_notifier_unregister(void *cookie); +void panel_event_notification_trigger(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification); + +#else +static inline void *panel_event_notifier_register(enum panel_event_notifier_tag tag, + enum panel_event_notifier_client client_handle, + struct drm_panel *panel, + panel_event_notifier_handler notif_handler, void *pvt_data) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void panel_event_notifier_unregister(void *cookie) +{ +} +static inline void panel_event_notification_trigger( + enum panel_event_notifier_tag tag, + struct panel_event_notification *notification) +{ +} +#endif +#endif