hwmon: amoled-ecm: add support to re-enable ECM dynamically

When ECM is enabled, disable ECM if display goes blank, and re-enable
ECM once display restores to unblank. This helps in keeping ECM HW
enabled only when display is active.

Meanwhile, update the logic a little bit on calculating the average
current. When any of the accumulated values is invalid, just ignore
the calculation instead of disabling the ECM.

Change-Id: Ibfe973df4dabd1b1cd48927ce567a094da6382a3
Signed-off-by: Fenglin Wu <fenglinw@codeaurora.org>
This commit is contained in:
Fenglin Wu 2020-09-16 07:19:13 +08:00 committed by David Collins
parent ad61c83861
commit 48c3279d8d

View File

@ -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 <linux/regmap.h>
#include <linux/workqueue.h>
#include <linux/soc/qcom/panel_event_notifier.h>
/* 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[] = {