18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: MIT
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2019 Google, Inc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:
68c2ecf20Sopenharmony_ci * Sean Paul <seanpaul@chromium.org>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <linux/average.h>
98c2ecf20Sopenharmony_ci#include <linux/bitops.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h>
148c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
158c2ecf20Sopenharmony_ci#include <drm/drm_connector.h>
168c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h>
178c2ecf20Sopenharmony_ci#include <drm/drm_device.h>
188c2ecf20Sopenharmony_ci#include <drm/drm_mode_config.h>
198c2ecf20Sopenharmony_ci#include <drm/drm_modeset_lock.h>
208c2ecf20Sopenharmony_ci#include <drm/drm_print.h>
218c2ecf20Sopenharmony_ci#include <drm/drm_self_refresh_helper.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/**
248c2ecf20Sopenharmony_ci * DOC: overview
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * This helper library provides an easy way for drivers to leverage the atomic
278c2ecf20Sopenharmony_ci * framework to implement panel self refresh (SR) support. Drivers are
288c2ecf20Sopenharmony_ci * responsible for initializing and cleaning up the SR helpers on load/unload
298c2ecf20Sopenharmony_ci * (see &drm_self_refresh_helper_init/&drm_self_refresh_helper_cleanup).
308c2ecf20Sopenharmony_ci * The connector is responsible for setting
318c2ecf20Sopenharmony_ci * &drm_connector_state.self_refresh_aware to true at runtime if it is SR-aware
328c2ecf20Sopenharmony_ci * (meaning it knows how to initiate self refresh on the panel).
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * Once a crtc has enabled SR using &drm_self_refresh_helper_init, the
358c2ecf20Sopenharmony_ci * helpers will monitor activity and call back into the driver to enable/disable
368c2ecf20Sopenharmony_ci * SR as appropriate. The best way to think about this is that it's a DPMS
378c2ecf20Sopenharmony_ci * on/off request with &drm_crtc_state.self_refresh_active set in crtc state
388c2ecf20Sopenharmony_ci * that tells you to disable/enable SR on the panel instead of power-cycling it.
398c2ecf20Sopenharmony_ci *
408c2ecf20Sopenharmony_ci * During SR, drivers may choose to fully disable their crtc/encoder/bridge
418c2ecf20Sopenharmony_ci * hardware (in which case no driver changes are necessary), or they can inspect
428c2ecf20Sopenharmony_ci * &drm_crtc_state.self_refresh_active if they want to enter low power mode
438c2ecf20Sopenharmony_ci * without full disable (in case full disable/enable is too slow).
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * SR will be deactivated if there are any atomic updates affecting the
468c2ecf20Sopenharmony_ci * pipe that is in SR mode. If a crtc is driving multiple connectors, all
478c2ecf20Sopenharmony_ci * connectors must be SR aware and all will enter/exit SR mode at the same time.
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci * If the crtc and connector are SR aware, but the panel connected does not
508c2ecf20Sopenharmony_ci * support it (or is otherwise unable to enter SR), the driver should fail
518c2ecf20Sopenharmony_ci * atomic_check when &drm_crtc_state.self_refresh_active is true.
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define SELF_REFRESH_AVG_SEED_MS 200
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ciDECLARE_EWMA(psr_time, 4, 4)
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistruct drm_self_refresh_data {
598c2ecf20Sopenharmony_ci	struct drm_crtc *crtc;
608c2ecf20Sopenharmony_ci	struct delayed_work entry_work;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	struct mutex avg_mutex;
638c2ecf20Sopenharmony_ci	struct ewma_psr_time entry_avg_ms;
648c2ecf20Sopenharmony_ci	struct ewma_psr_time exit_avg_ms;
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic void drm_self_refresh_helper_entry_work(struct work_struct *work)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct drm_self_refresh_data *sr_data = container_of(
708c2ecf20Sopenharmony_ci				to_delayed_work(work),
718c2ecf20Sopenharmony_ci				struct drm_self_refresh_data, entry_work);
728c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = sr_data->crtc;
738c2ecf20Sopenharmony_ci	struct drm_device *dev = crtc->dev;
748c2ecf20Sopenharmony_ci	struct drm_modeset_acquire_ctx ctx;
758c2ecf20Sopenharmony_ci	struct drm_atomic_state *state;
768c2ecf20Sopenharmony_ci	struct drm_connector *conn;
778c2ecf20Sopenharmony_ci	struct drm_connector_state *conn_state;
788c2ecf20Sopenharmony_ci	struct drm_crtc_state *crtc_state;
798c2ecf20Sopenharmony_ci	int i, ret = 0;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	drm_modeset_acquire_init(&ctx, 0);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	state = drm_atomic_state_alloc(dev);
848c2ecf20Sopenharmony_ci	if (!state) {
858c2ecf20Sopenharmony_ci		ret = -ENOMEM;
868c2ecf20Sopenharmony_ci		goto out_drop_locks;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ciretry:
908c2ecf20Sopenharmony_ci	state->acquire_ctx = &ctx;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	crtc_state = drm_atomic_get_crtc_state(state, crtc);
938c2ecf20Sopenharmony_ci	if (IS_ERR(crtc_state)) {
948c2ecf20Sopenharmony_ci		ret = PTR_ERR(crtc_state);
958c2ecf20Sopenharmony_ci		goto out;
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (!crtc_state->enable)
998c2ecf20Sopenharmony_ci		goto out;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	ret = drm_atomic_add_affected_connectors(state, crtc);
1028c2ecf20Sopenharmony_ci	if (ret)
1038c2ecf20Sopenharmony_ci		goto out;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	for_each_new_connector_in_state(state, conn, conn_state, i) {
1068c2ecf20Sopenharmony_ci		if (!conn_state->self_refresh_aware)
1078c2ecf20Sopenharmony_ci			goto out;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	crtc_state->active = false;
1118c2ecf20Sopenharmony_ci	crtc_state->self_refresh_active = true;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	ret = drm_atomic_commit(state);
1148c2ecf20Sopenharmony_ci	if (ret)
1158c2ecf20Sopenharmony_ci		goto out;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ciout:
1188c2ecf20Sopenharmony_ci	if (ret == -EDEADLK) {
1198c2ecf20Sopenharmony_ci		drm_atomic_state_clear(state);
1208c2ecf20Sopenharmony_ci		ret = drm_modeset_backoff(&ctx);
1218c2ecf20Sopenharmony_ci		if (!ret)
1228c2ecf20Sopenharmony_ci			goto retry;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	drm_atomic_state_put(state);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ciout_drop_locks:
1288c2ecf20Sopenharmony_ci	drm_modeset_drop_locks(&ctx);
1298c2ecf20Sopenharmony_ci	drm_modeset_acquire_fini(&ctx);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/**
1338c2ecf20Sopenharmony_ci * drm_self_refresh_helper_update_avg_times - Updates a crtc's SR time averages
1348c2ecf20Sopenharmony_ci * @state: the state which has just been applied to hardware
1358c2ecf20Sopenharmony_ci * @commit_time_ms: the amount of time in ms that this commit took to complete
1368c2ecf20Sopenharmony_ci * @new_self_refresh_mask: bitmask of crtc's that have self_refresh_active in
1378c2ecf20Sopenharmony_ci *    new state
1388c2ecf20Sopenharmony_ci *
1398c2ecf20Sopenharmony_ci * Called after &drm_mode_config_funcs.atomic_commit_tail, this function will
1408c2ecf20Sopenharmony_ci * update the average entry/exit self refresh times on self refresh transitions.
1418c2ecf20Sopenharmony_ci * These averages will be used when calculating how long to delay before
1428c2ecf20Sopenharmony_ci * entering self refresh mode after activity.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_civoid
1458c2ecf20Sopenharmony_cidrm_self_refresh_helper_update_avg_times(struct drm_atomic_state *state,
1468c2ecf20Sopenharmony_ci					 unsigned int commit_time_ms,
1478c2ecf20Sopenharmony_ci					 unsigned int new_self_refresh_mask)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	struct drm_crtc *crtc;
1508c2ecf20Sopenharmony_ci	struct drm_crtc_state *old_crtc_state;
1518c2ecf20Sopenharmony_ci	int i;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
1548c2ecf20Sopenharmony_ci		bool new_self_refresh_active = new_self_refresh_mask & BIT(i);
1558c2ecf20Sopenharmony_ci		struct drm_self_refresh_data *sr_data = crtc->self_refresh_data;
1568c2ecf20Sopenharmony_ci		struct ewma_psr_time *time;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		if (old_crtc_state->self_refresh_active ==
1598c2ecf20Sopenharmony_ci		    new_self_refresh_active)
1608c2ecf20Sopenharmony_ci			continue;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci		if (new_self_refresh_active)
1638c2ecf20Sopenharmony_ci			time = &sr_data->entry_avg_ms;
1648c2ecf20Sopenharmony_ci		else
1658c2ecf20Sopenharmony_ci			time = &sr_data->exit_avg_ms;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		mutex_lock(&sr_data->avg_mutex);
1688c2ecf20Sopenharmony_ci		ewma_psr_time_add(time, commit_time_ms);
1698c2ecf20Sopenharmony_ci		mutex_unlock(&sr_data->avg_mutex);
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_self_refresh_helper_update_avg_times);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/**
1758c2ecf20Sopenharmony_ci * drm_self_refresh_helper_alter_state - Alters the atomic state for SR exit
1768c2ecf20Sopenharmony_ci * @state: the state currently being checked
1778c2ecf20Sopenharmony_ci *
1788c2ecf20Sopenharmony_ci * Called at the end of atomic check. This function checks the state for flags
1798c2ecf20Sopenharmony_ci * incompatible with self refresh exit and changes them. This is a bit
1808c2ecf20Sopenharmony_ci * disingenuous since userspace is expecting one thing and we're giving it
1818c2ecf20Sopenharmony_ci * another. However in order to keep self refresh entirely hidden from
1828c2ecf20Sopenharmony_ci * userspace, this is required.
1838c2ecf20Sopenharmony_ci *
1848c2ecf20Sopenharmony_ci * At the end, we queue up the self refresh entry work so we can enter PSR after
1858c2ecf20Sopenharmony_ci * the desired delay.
1868c2ecf20Sopenharmony_ci */
1878c2ecf20Sopenharmony_civoid drm_self_refresh_helper_alter_state(struct drm_atomic_state *state)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct drm_crtc *crtc;
1908c2ecf20Sopenharmony_ci	struct drm_crtc_state *crtc_state;
1918c2ecf20Sopenharmony_ci	int i;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (state->async_update || !state->allow_modeset) {
1948c2ecf20Sopenharmony_ci		for_each_old_crtc_in_state(state, crtc, crtc_state, i) {
1958c2ecf20Sopenharmony_ci			if (crtc_state->self_refresh_active) {
1968c2ecf20Sopenharmony_ci				state->async_update = false;
1978c2ecf20Sopenharmony_ci				state->allow_modeset = true;
1988c2ecf20Sopenharmony_ci				break;
1998c2ecf20Sopenharmony_ci			}
2008c2ecf20Sopenharmony_ci		}
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
2048c2ecf20Sopenharmony_ci		struct drm_self_refresh_data *sr_data;
2058c2ecf20Sopenharmony_ci		unsigned int delay;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci		/* Don't trigger the entry timer when we're already in SR */
2088c2ecf20Sopenharmony_ci		if (crtc_state->self_refresh_active)
2098c2ecf20Sopenharmony_ci			continue;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		sr_data = crtc->self_refresh_data;
2128c2ecf20Sopenharmony_ci		if (!sr_data)
2138c2ecf20Sopenharmony_ci			continue;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		mutex_lock(&sr_data->avg_mutex);
2168c2ecf20Sopenharmony_ci		delay = (ewma_psr_time_read(&sr_data->entry_avg_ms) +
2178c2ecf20Sopenharmony_ci			 ewma_psr_time_read(&sr_data->exit_avg_ms)) * 2;
2188c2ecf20Sopenharmony_ci		mutex_unlock(&sr_data->avg_mutex);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		mod_delayed_work(system_wq, &sr_data->entry_work,
2218c2ecf20Sopenharmony_ci				 msecs_to_jiffies(delay));
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_self_refresh_helper_alter_state);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci/**
2278c2ecf20Sopenharmony_ci * drm_self_refresh_helper_init - Initializes self refresh helpers for a crtc
2288c2ecf20Sopenharmony_ci * @crtc: the crtc which supports self refresh supported displays
2298c2ecf20Sopenharmony_ci *
2308c2ecf20Sopenharmony_ci * Returns zero if successful or -errno on failure
2318c2ecf20Sopenharmony_ci */
2328c2ecf20Sopenharmony_ciint drm_self_refresh_helper_init(struct drm_crtc *crtc)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct drm_self_refresh_data *sr_data = crtc->self_refresh_data;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/* Helper is already initialized */
2378c2ecf20Sopenharmony_ci	if (WARN_ON(sr_data))
2388c2ecf20Sopenharmony_ci		return -EINVAL;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	sr_data = kzalloc(sizeof(*sr_data), GFP_KERNEL);
2418c2ecf20Sopenharmony_ci	if (!sr_data)
2428c2ecf20Sopenharmony_ci		return -ENOMEM;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&sr_data->entry_work,
2458c2ecf20Sopenharmony_ci			  drm_self_refresh_helper_entry_work);
2468c2ecf20Sopenharmony_ci	sr_data->crtc = crtc;
2478c2ecf20Sopenharmony_ci	mutex_init(&sr_data->avg_mutex);
2488c2ecf20Sopenharmony_ci	ewma_psr_time_init(&sr_data->entry_avg_ms);
2498c2ecf20Sopenharmony_ci	ewma_psr_time_init(&sr_data->exit_avg_ms);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/*
2528c2ecf20Sopenharmony_ci	 * Seed the averages so they're non-zero (and sufficiently large
2538c2ecf20Sopenharmony_ci	 * for even poorly performing panels). As time goes on, this will be
2548c2ecf20Sopenharmony_ci	 * averaged out and the values will trend to their true value.
2558c2ecf20Sopenharmony_ci	 */
2568c2ecf20Sopenharmony_ci	ewma_psr_time_add(&sr_data->entry_avg_ms, SELF_REFRESH_AVG_SEED_MS);
2578c2ecf20Sopenharmony_ci	ewma_psr_time_add(&sr_data->exit_avg_ms, SELF_REFRESH_AVG_SEED_MS);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	crtc->self_refresh_data = sr_data;
2608c2ecf20Sopenharmony_ci	return 0;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_self_refresh_helper_init);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci/**
2658c2ecf20Sopenharmony_ci * drm_self_refresh_helper_cleanup - Cleans up self refresh helpers for a crtc
2668c2ecf20Sopenharmony_ci * @crtc: the crtc to cleanup
2678c2ecf20Sopenharmony_ci */
2688c2ecf20Sopenharmony_civoid drm_self_refresh_helper_cleanup(struct drm_crtc *crtc)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct drm_self_refresh_data *sr_data = crtc->self_refresh_data;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	/* Helper is already uninitialized */
2738c2ecf20Sopenharmony_ci	if (!sr_data)
2748c2ecf20Sopenharmony_ci		return;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	crtc->self_refresh_data = NULL;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&sr_data->entry_work);
2798c2ecf20Sopenharmony_ci	kfree(sr_data);
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_self_refresh_helper_cleanup);
282