diff --git a/drivers/hwmon/qti_amoled_ecm.c b/drivers/hwmon/qti_amoled_ecm.c index 91b8cd101ce1..804c02f5436c 100644 --- a/drivers/hwmon/qti_amoled_ecm.c +++ b/drivers/hwmon/qti_amoled_ecm.c @@ -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 + * @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[] = {