drm/rockchip: panel-notifier: add panel notifier for panel-related device

Type: Function
Redmine ID: N/A
Associated modifications: N/A
Test: N/A

Signed-off-by: Zhibin Huang <zhibin.huang@rock-chips.com>
Change-Id: I22fb562772fcbf3808d61c7419407bf50d947e0d
This commit is contained in:
Zhibin Huang
2024-04-16 20:28:40 +08:00
committed by Tao Huang
parent 0ad1b882f4
commit d74b36463b
4 changed files with 306 additions and 0 deletions

View File

@@ -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

View File

@@ -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/

View File

@@ -0,0 +1,223 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2024 Rockchip Electronics Co. Ltd.
* Author: Zhibin Huang <zhibin.huang@rock-chips.com>
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/rockchip-panel-notifier.h>
#include <drm/drm_panel.h>
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(&notifier_list_lock);
list_for_each_entry(pn, &notifier_list, list) {
if (pn->panel == panel)
goto find;
}
pn = NULL;
find:
mutex_unlock(&notifier_list_lock);
if (!pn)
return -ENODEV;
ret = blocking_notifier_chain_register(&pn->nh, this->nb);
if (ret)
return ret;
this->pn = pn;
mutex_lock(&notifier_client_list_lock);
list_add_tail(&this->list, &notifier_client_list);
mutex_unlock(&notifier_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(&notifier_client_list_lock);
list_for_each_entry_safe(pn_client, next, &notifier_client_list, list) {
if (pn_client != client)
continue;
rockchip_panel_notifier_unregister_client(pn_client);
list_del_init(&pn_client->list);
}
mutex_unlock(&notifier_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(&notifier_list_lock);
list_add_tail(&pn->list, &notifier_list);
mutex_unlock(&notifier_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(&notifier_client_list_lock);
list_for_each_entry_safe(pn_client, next, &notifier_client_list, list) {
if (pn_client->pn != pn)
continue;
rockchip_panel_notifier_unregister_client(pn_client);
list_del_init(&pn_client->list);
}
mutex_unlock(&notifier_client_list_lock);
mutex_lock(&notifier_list_lock);
list_del_init(&pn->list);
mutex_unlock(&notifier_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 <zhibin.huang@rock-chips.com>");
MODULE_DESCRIPTION("rockchip panel notifier");

View File

@@ -0,0 +1,74 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2024 Rockchip Electronics Co. Ltd.
* Author: Zhibin Huang <zhibin.huang@rock-chips.com>
*/
#ifndef __LINUX_ROCKCHIP_PANEL_NOTIFIER_H__
#define __LINUX_ROCKCHIP_PANEL_NOTIFIER_H__
#include <linux/of.h>
#include <linux/notifier.h>
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__ */