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

This commit is contained in:
qctecmdr 2020-09-21 18:12:27 -07:00 • committed by Gerrit - the friendly Code Review server
commit 2cb49d5ea4

View file

@ -19,6 +19,8 @@
#include <linux/regmap.h>
#include <linux/workqueue.h>
#include <drm/drm_panel.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
* @drm_notifier: The notifier block for receiving DRM notifications
* @active_panel: Active DRM panel which sends DRM notifications
* @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 notifier_block drm_notifier;
struct drm_panel *active_panel;
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;
@ -672,6 +673,37 @@ irq_exit:
return IRQ_HANDLED;
}
#ifdef CONFIG_DRM
static int amoled_ecm_parse_panel_dt(struct amoled_ecm *ecm)
{
struct device_node *np = ecm->dev->of_node;
struct device_node *pnode;
struct drm_panel *panel;
int i, count;
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);
panel = of_drm_find_panel(pnode);
of_node_put(pnode);
if (!IS_ERR(panel)) {
ecm->active_panel = panel;
return 0;
}
}
return PTR_ERR(panel);
}
#else
static inline int amoled_ecm_parse_panel_dt(struct amoled_ecm *ecm)
{
return 0;
}
#endif
static int amoled_ecm_parse_dt(struct amoled_ecm *ecm)
{
int rc = 0, i;
@ -726,9 +758,93 @@ static int amoled_ecm_parse_dt(struct amoled_ecm *ecm)
}
}
rc = amoled_ecm_parse_panel_dt(ecm);
if (rc < 0)
pr_err("failed to get active panel, rc=%d\n", rc);
return rc;
}
#ifdef CONFIG_DRM
static int drm_notifier_callback(struct notifier_block *nb,
unsigned long event, void *data)
{
struct amoled_ecm *ecm = container_of(nb,
struct amoled_ecm, drm_notifier);
struct drm_panel_notifier *evdata = data;
int blank, rc;
pr_debug("DRM event received: %d\n", event);
if (event != DRM_PANEL_EVENT_BLANK)
return NOTIFY_DONE;
blank = *(int *)evdata->data;
if (blank == DRM_PANEL_BLANK_POWERDOWN && ecm->enable) {
rc = amoled_ecm_disable(ecm);
if (rc < 0) {
pr_err("Failed to disable ECM for display BLANK, rc=%d\n",
rc);
return rc;
}
ecm->reenable = true;
pr_debug("Disabled ECM for display BLANK\n");
} else if (blank == DRM_PANEL_BLANK_UNBLANK && ecm->reenable) {
rc = amoled_ecm_enable(ecm);
if (rc < 0) {
pr_err("Failed to reenable ECM for display UNBLANK, rc=%d\n",
rc);
return rc;
}
ecm->reenable = false;
pr_debug("Enabled ECM for display UNBLANK\n");
}
return NOTIFY_DONE;
}
static int qti_amoled_register_drm_notifier(struct amoled_ecm *ecm)
{
int rc = 0;
if (ecm->active_panel) {
ecm->drm_notifier.notifier_call = drm_notifier_callback;
rc = drm_panel_notifier_register(ecm->active_panel,
&ecm->drm_notifier);
if (rc < 0)
pr_err("failed to register DRM notifier, rc=%d\n", rc);
}
return rc;
}
static int qti_amoled_unregister_drm_notifier(struct amoled_ecm *ecm)
{
if (ecm->active_panel)
return drm_panel_notifier_unregister(ecm->active_panel,
&ecm->drm_notifier);
return 0;
}
#else
static inline int drm_notifier_callback(struct notifier_block *nb,
unsigned long event, void *data)
{
return NOTIFY_DONE;
}
static inline int qti_amoled_register_drm_notifier(struct amoled_ecm *ecm)
{
return 0;
}
static inline int qti_amoled_unregister_drm_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_drm_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_drm_notifier(ecm);
}
static const struct of_device_id amoled_ecm_match_table[] = {