diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index e2c6a131e609..9b2a443be113 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -129,6 +129,14 @@ config ROCKCHIP_LVDS support LVDS, rgb, dual LVDS output mode. say Y to enable its driver. +config ROCKCHIP_PANEL_NOTIFIER + tristate "Rockchip panel notifier" + depends on OF + help + Say Y here if you want to enable support for the panel notifier. The + notifier is used to notify other devices (such as TP) to perform the + corresponding action when the panel is in different actions. + config ROCKCHIP_RGB bool "Rockchip RGB support" depends on PINCTRL diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index a211c295664b..3809b69975bc 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -28,6 +28,7 @@ rockchipdrm-$(CONFIG_ROCKCHIP_RK3066_HDMI) += rk3066_hdmi.o rockchipdrm-$(CONFIG_ROCKCHIP_VCONN) += rockchip_drm_vconn.o rockchipdrm-$(CONFIG_DRM_ROCKCHIP_VVOP) += rockchip_drm_vvop.o +obj-$(CONFIG_ROCKCHIP_PANEL_NOTIFIER) += rockchip_panel_notifier.o obj-$(CONFIG_ROCKCHIP_DW_HDCP2) += dw_hdcp2.o obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o obj-$(CONFIG_DRM_ROCKCHIP_RK618) += rk618/ diff --git a/drivers/gpu/drm/rockchip/rockchip_panel_notifier.c b/drivers/gpu/drm/rockchip/rockchip_panel_notifier.c new file mode 100644 index 000000000000..455a9be63124 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_panel_notifier.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 Rockchip Electronics Co. Ltd. + * Author: Zhibin Huang + */ + +#include +#include +#include + +#include + +struct rockchip_panel_notifier_client { + struct list_head list; + struct rockchip_panel_notifier *pn; + struct notifier_block *nb; +}; + +static DEFINE_MUTEX(notifier_list_lock); +static LIST_HEAD(notifier_list); +static DEFINE_MUTEX(notifier_client_list_lock); +static LIST_HEAD(notifier_client_list); + +static int +rockchip_panel_notifier_register_client(struct device *dev, + struct rockchip_panel_notifier_client **client) +{ + struct rockchip_panel_notifier_client *this = *client; + struct rockchip_panel_notifier *pn; + struct device_node *node; + struct drm_panel *panel; + int ret; + + if (!dev || !dev->of_node || !this->nb) + return -EINVAL; + + node = of_parse_phandle(dev->of_node, "rockchip,panel-notifier", 0); + if (!node) + return -ENODEV; + + panel = of_drm_find_panel(node); + if (IS_ERR(panel)) { + of_node_put(node); + return PTR_ERR(panel); + } + + of_node_put(node); + + mutex_lock(¬ifier_list_lock); + list_for_each_entry(pn, ¬ifier_list, list) { + if (pn->panel == panel) + goto find; + } + pn = NULL; +find: + mutex_unlock(¬ifier_list_lock); + if (!pn) + return -ENODEV; + + ret = blocking_notifier_chain_register(&pn->nh, this->nb); + if (ret) + return ret; + + this->pn = pn; + + mutex_lock(¬ifier_client_list_lock); + list_add_tail(&this->list, ¬ifier_client_list); + mutex_unlock(¬ifier_client_list_lock); + + return 0; +} + +static void +rockchip_panel_notifier_unregister_client(struct rockchip_panel_notifier_client *client) +{ + if (!client) + return; + + blocking_notifier_chain_unregister(&client->pn->nh, client->nb); +} + +static void +devm_rockchip_panel_notifier_unreg_client(struct device *dev, void *res) +{ + struct rockchip_panel_notifier_client *pn_client, *next, *client = res; + + mutex_lock(¬ifier_client_list_lock); + list_for_each_entry_safe(pn_client, next, ¬ifier_client_list, list) { + if (pn_client != client) + continue; + + rockchip_panel_notifier_unregister_client(pn_client); + list_del_init(&pn_client->list); + } + mutex_unlock(¬ifier_client_list_lock); +} + +int devm_rockchip_panel_notifier_register_client(struct device *dev, + struct notifier_block *nb) +{ + int ret; + struct rockchip_panel_notifier_client *pn_client; + + if (!dev || !nb) + return -EINVAL; + + pn_client = devres_alloc(devm_rockchip_panel_notifier_unreg_client, + sizeof(*pn_client), GFP_KERNEL); + if (!pn_client) + return -ENOMEM; + + pn_client->nb = nb; + + ret = rockchip_panel_notifier_register_client(dev, &pn_client); + if (ret) { + devres_free(pn_client); + return ret; + } + + devres_add(dev, pn_client); + + return 0; +} +EXPORT_SYMBOL(devm_rockchip_panel_notifier_register_client); + +void devm_rockchip_panel_notifier_unregister_client(struct device *dev) +{ + WARN_ON(devres_release(dev, devm_rockchip_panel_notifier_unreg_client, + NULL, NULL)); +} +EXPORT_SYMBOL(devm_rockchip_panel_notifier_unregister_client); + +static int rockchip_panel_notifier_register(struct drm_panel *panel, + struct rockchip_panel_notifier *pn) +{ + if (!panel || !pn) + return -EINVAL; + + pn->panel = panel; + + BLOCKING_INIT_NOTIFIER_HEAD(&pn->nh); + + mutex_lock(¬ifier_list_lock); + list_add_tail(&pn->list, ¬ifier_list); + mutex_unlock(¬ifier_list_lock); + + return 0; +} + +static void rockchip_panel_notifier_unregister(struct rockchip_panel_notifier *pn) +{ + struct rockchip_panel_notifier_client *pn_client, *next; + + if (!pn) + return; + + mutex_lock(¬ifier_client_list_lock); + list_for_each_entry_safe(pn_client, next, ¬ifier_client_list, list) { + if (pn_client->pn != pn) + continue; + + rockchip_panel_notifier_unregister_client(pn_client); + list_del_init(&pn_client->list); + } + mutex_unlock(¬ifier_client_list_lock); + + mutex_lock(¬ifier_list_lock); + list_del_init(&pn->list); + mutex_unlock(¬ifier_list_lock); +} + +static void devm_rockchip_panel_notifier_unreg(struct device *dev, void *res) +{ + rockchip_panel_notifier_unregister(*(struct rockchip_panel_notifier **)res); +} + +void devm_rockchip_panel_notifier_unregister(struct device *dev) +{ + WARN_ON(devres_release(dev, devm_rockchip_panel_notifier_unreg, + NULL, NULL)); +} +EXPORT_SYMBOL(devm_rockchip_panel_notifier_unregister); + +int devm_rockchip_panel_notifier_register(struct device *dev, + struct drm_panel *panel, + struct rockchip_panel_notifier *pn) +{ + int ret; + struct rockchip_panel_notifier **ptr; + + ptr = devres_alloc(devm_rockchip_panel_notifier_unreg, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = rockchip_panel_notifier_register(panel, pn); + if (ret) { + devres_free(ptr); + return ret; + } + + *ptr = pn; + devres_add(dev, ptr); + + return 0; +} +EXPORT_SYMBOL(devm_rockchip_panel_notifier_register); + +int rockchip_panel_notifier_call_chain(struct rockchip_panel_notifier *pn, + enum rockchip_panel_event panel_event, + struct rockchip_panel_edata *panel_edata) +{ + if (!pn) + return -EINVAL; + + return blocking_notifier_call_chain(&pn->nh, (unsigned long)panel_event, + (void *)panel_edata); +} +EXPORT_SYMBOL(rockchip_panel_notifier_call_chain); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhibin Huang "); +MODULE_DESCRIPTION("rockchip panel notifier"); diff --git a/include/linux/rockchip-panel-notifier.h b/include/linux/rockchip-panel-notifier.h new file mode 100644 index 000000000000..6fb90045ca7e --- /dev/null +++ b/include/linux/rockchip-panel-notifier.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2024 Rockchip Electronics Co. Ltd. + * Author: Zhibin Huang + */ + +#ifndef __LINUX_ROCKCHIP_PANEL_NOTIFIER_H__ +#define __LINUX_ROCKCHIP_PANEL_NOTIFIER_H__ + +#include +#include + +struct rockchip_panel_notifier { + struct list_head list; + struct drm_panel *panel; + struct blocking_notifier_head nh; +}; + +enum rockchip_panel_event { + PANEL_PRE_ENABLE, + PANEL_ENABLED, + PANEL_PRE_DISABLE, + PANEL_DISABLED, +}; + +struct rockchip_panel_edata { + void *data; +}; + +#if IS_REACHABLE(CONFIG_ROCKCHIP_PANEL_NOTIFIER) +int devm_rockchip_panel_notifier_register(struct device *dev, + struct drm_panel *panel, + struct rockchip_panel_notifier *pn); +void devm_rockchip_panel_notifier_unregister(struct device *dev); +int rockchip_panel_notifier_call_chain(struct rockchip_panel_notifier *pn, + enum rockchip_panel_event panel_event, + struct rockchip_panel_edata *panel_edata); +int devm_rockchip_panel_notifier_register_client(struct device *dev, struct notifier_block *nb); +void devm_rockchip_panel_notifier_unregister_client(struct device *dev); +#else +static inline int +devm_rockchip_panel_notifier_register(struct device *dev, + struct drm_panel *panel, + struct rockchip_panel_notifier *pn) +{ + return 0; +} + +static inline void devm_rockchip_panel_notifier_unregister(struct device *dev) +{ +} + +static inline int +rockchip_panel_notifier_call_chain(struct rockchip_panel_notifier *pn, + enum rockchip_panel_event panel_event, + struct rockchip_panel_edata *panel_edata) +{ + return 0; +} + +static inline int +devm_rockchip_panel_notifier_register_client(struct device *dev, + struct notifier_block *nb) +{ + return 0; +} + +static inline void +devm_rockchip_panel_notifier_unregister_client(struct device *dev) +{ +} +#endif /* CONFIG_ROCKCHIP_PANEL_NOTIFIER */ + +#endif /* __LINUX_ROCKCHIP_PANEL_NOTIFIER_H__ */