diff --git a/drivers/hwmon/qti_amoled_ecm.c b/drivers/hwmon/qti_amoled_ecm.c index 91b8cd101ce1..9954598ae8d3 100644 --- a/drivers/hwmon/qti_amoled_ecm.c +++ b/drivers/hwmon/qti_amoled_ecm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "AMOLED_ECM: %s: " fmt, __func__ @@ -19,6 +19,8 @@ #include #include +#include + /* AMOLED AB register definitions */ #define AB_REVISION2 0x01 @@ -150,11 +152,14 @@ struct amoled_ecm_data { * @sdam: Pointer for array of ECM sdams * @sdam_lock: Locking for mutual exclusion * @average_work: Delayed work to calculate ECM average + * @active_panel: Active DRM panel which sends panel notifications + * @notifier_cookie: The cookie from panel notifier * @num_sdams: Number of SDAMs used for AMOLED ECM * @base: Base address of the AMOLED ECM module * @ab_revision: Revision of the AMOLED AB module * @enable: Flag to enable/disable AMOLED ECM * @abort: Flag to indicated AMOLED ECM has aborted + * @reenable: Flag to reenable ECM when display goes unblank */ struct amoled_ecm { struct regmap *regmap; @@ -163,11 +168,14 @@ struct amoled_ecm { struct amoled_ecm_sdam *sdam; struct mutex sdam_lock; struct delayed_work average_work; + struct drm_panel *active_panel; + void *notifier_cookie; u32 num_sdams; u32 base; u8 ab_revision; bool enable; bool abort; + bool reenable; }; static struct amoled_ecm_sdam_config ecm_reset_config[] = { @@ -309,8 +317,6 @@ static int amoled_ecm_disable(struct amoled_ecm *ecm) cancel_delayed_work(&ecm->average_work); - ecm->data.frames = 0; - ecm->data.time_period_ms = 0; ecm->data.avg_current = 0; ecm->data.m_cumulative = 0; ecm->data.num_m_samples = 0; @@ -327,25 +333,17 @@ static void ecm_average_work(struct work_struct *work) struct amoled_ecm *ecm = container_of(work, struct amoled_ecm, average_work.work); struct amoled_ecm_data *data = &ecm->data; - int rc; - - if (!data->num_m_samples || !data->m_cumulative) { - pr_err("num_m_samples=%u m_cumulative:%u disabling ECM\n", - data->num_m_samples, data->m_cumulative); - data->avg_current = -EINVAL; - - rc = amoled_ecm_disable(ecm); - if (rc < 0) - pr_err("Failed to disable AMOLED ECM, rc=%d\n", rc); - - return; - } mutex_lock(&ecm->sdam_lock); - data->avg_current = data->m_cumulative / data->num_m_samples; - - pr_debug("avg_current=%u mA\n", data->avg_current); + if (!data->num_m_samples || !data->m_cumulative) { + pr_warn("Invalid data, num_m_samples=%u m_cumulative:%u\n", + data->num_m_samples, data->m_cumulative); + data->avg_current = -EINVAL; + } else { + data->avg_current = data->m_cumulative / data->num_m_samples; + pr_debug("avg_current=%u mA\n", data->avg_current); + } data->m_cumulative = 0; data->num_m_samples = 0; @@ -399,6 +397,9 @@ static ssize_t enable_store(struct device *dev, pr_err("Failed to disable AMOLED ECM, rc=%d\n", rc); return rc; } + + ecm->data.frames = 0; + ecm->data.time_period_ms = 0; } return count; @@ -726,9 +727,124 @@ static int amoled_ecm_parse_dt(struct amoled_ecm *ecm) } } - return rc; + return 0; } +#if IS_ENABLED(CONFIG_QCOM_PANEL_EVENT_NOTIFIER) +static void panel_event_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *data) +{ + struct amoled_ecm *ecm = data; + int rc; + + if (!notification) { + pr_err("Invalid panel notification\n"); + return; + } + + pr_debug("panel event received, type: %d\n", notification->notif_type); + switch (notification->notif_type) { + case DRM_PANEL_EVENT_BLANK: + if (ecm->enable) { + rc = amoled_ecm_disable(ecm); + if (rc < 0) { + pr_err("Failed to disable ECM for display BLANK, rc=%d\n", + rc); + return; + } + + ecm->reenable = true; + pr_debug("Disabled ECM for display BLANK\n"); + } + break; + case DRM_PANEL_EVENT_UNBLANK: + if (ecm->reenable) { + rc = amoled_ecm_enable(ecm); + if (rc < 0) { + pr_err("Failed to re-enable ECM for display UNBLANK, rc=%d\n", + rc); + return; + } + + ecm->reenable = false; + pr_debug("Enabled ECM for display UNBLANK\n"); + } + break; + default: + pr_debug("Ignore panel event: %d\n", notification->notif_type); + break; + } +} + +static int qti_amoled_register_panel_notifier(struct amoled_ecm *ecm) +{ + struct device_node *np = ecm->dev->of_node; + struct device_node *pnode; + struct drm_panel *panel; + void *cookie = NULL; + int i, count, rc; + + count = of_count_phandle_with_args(np, "display-panels", NULL); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + pnode = of_parse_phandle(np, "display-panels", i); + if (!pnode) + return -ENODEV; + + panel = of_drm_find_panel(pnode); + of_node_put(pnode); + if (!IS_ERR(panel)) { + ecm->active_panel = panel; + break; + } + } + + if (!ecm->active_panel) { + rc = PTR_ERR(panel); + if (rc != -EPROBE_DEFER) + pr_err("failed to find active panel, rc=%d\n", rc); + + return rc; + } + + cookie = panel_event_notifier_register( + PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_ECM, + ecm->active_panel, + panel_event_notifier_callback, + (void *)ecm); + if (IS_ERR(cookie)) { + rc = PTR_ERR(cookie); + pr_err("failed to register panel event notifier, rc=%d\n", rc); + return rc; + } + + pr_debug("register panel notifier successfully\n"); + ecm->notifier_cookie = cookie; + return 0; +} + +static int qti_amoled_unregister_panel_notifier(struct amoled_ecm *ecm) +{ + if (ecm->notifier_cookie) + panel_event_notifier_unregister(ecm->notifier_cookie); + + return 0; +} +#else +static inline int qti_amoled_register_panel_notifier(struct amoled_ecm *ecm) +{ + return 0; +} + +static inline int qti_amoled_unregister_panel_notifier(struct amoled_ecm *ecm) +{ + return 0; +} +#endif + static int qti_amoled_ecm_probe(struct platform_device *pdev) { struct device *hwmon_dev; @@ -793,13 +909,21 @@ static int qti_amoled_ecm_probe(struct platform_device *pdev) hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, "amoled_ecm", ecm, amoled_ecm_groups); + if (IS_ERR_OR_NULL(hwmon_dev)) { + rc = PTR_ERR(hwmon_dev); + pr_err("failed to register hwmon device for amoled-ecm, rc=%d\n", + rc); + return rc; + } - return PTR_ERR_OR_ZERO(hwmon_dev); + return qti_amoled_register_panel_notifier(ecm); } static int qti_amoled_ecm_remove(struct platform_device *pdev) { - return 0; + struct amoled_ecm *ecm = dev_get_drvdata(&pdev->dev); + + return qti_amoled_unregister_panel_notifier(ecm); } static const struct of_device_id amoled_ecm_match_table[] = {