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