Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cd3230bf authored by Fenglin Wu's avatar Fenglin Wu
Browse files

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



When ECM is enabled, disable ECM if display goes blank, and reenable
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: default avatarFenglin Wu <fenglinw@codeaurora.org>
parent 31f07c0c
Loading
Loading
Loading
Loading
+142 −18
Original line number Diff line number Diff line
@@ -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;

	mutex_lock(&ecm->sdam_lock);

	if (!data->num_m_samples || !data->m_cumulative) {
		pr_err("num_m_samples=%u m_cumulative:%u disabling ECM\n",
		pr_warn("Invalid data, num_m_samples=%u m_cumulative:%u\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);

	} 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 @@ static irqreturn_t sdam_full_irq_handler(int irq, void *_ecm)
	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[] = {