162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * SPDX-License-Identifier: MIT
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright © 2019 Intel Corporation
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/wait_bit.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "intel_runtime_pm.h"
1062306a36Sopenharmony_ci#include "intel_wakeref.h"
1162306a36Sopenharmony_ci#include "i915_drv.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic void rpm_get(struct intel_wakeref *wf)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	wf->wakeref = intel_runtime_pm_get(&wf->i915->runtime_pm);
1662306a36Sopenharmony_ci}
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic void rpm_put(struct intel_wakeref *wf)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	intel_wakeref_t wakeref = fetch_and_zero(&wf->wakeref);
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	intel_runtime_pm_put(&wf->i915->runtime_pm, wakeref);
2362306a36Sopenharmony_ci	INTEL_WAKEREF_BUG_ON(!wakeref);
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ciint __intel_wakeref_get_first(struct intel_wakeref *wf)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	/*
2962306a36Sopenharmony_ci	 * Treat get/put as different subclasses, as we may need to run
3062306a36Sopenharmony_ci	 * the put callback from under the shrinker and do not want to
3162306a36Sopenharmony_ci	 * cross-contanimate that callback with any extra work performed
3262306a36Sopenharmony_ci	 * upon acquiring the wakeref.
3362306a36Sopenharmony_ci	 */
3462306a36Sopenharmony_ci	mutex_lock_nested(&wf->mutex, SINGLE_DEPTH_NESTING);
3562306a36Sopenharmony_ci	if (!atomic_read(&wf->count)) {
3662306a36Sopenharmony_ci		int err;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci		rpm_get(wf);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci		err = wf->ops->get(wf);
4162306a36Sopenharmony_ci		if (unlikely(err)) {
4262306a36Sopenharmony_ci			rpm_put(wf);
4362306a36Sopenharmony_ci			mutex_unlock(&wf->mutex);
4462306a36Sopenharmony_ci			return err;
4562306a36Sopenharmony_ci		}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		smp_mb__before_atomic(); /* release wf->count */
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci	atomic_inc(&wf->count);
5062306a36Sopenharmony_ci	mutex_unlock(&wf->mutex);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	INTEL_WAKEREF_BUG_ON(atomic_read(&wf->count) <= 0);
5362306a36Sopenharmony_ci	return 0;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void ____intel_wakeref_put_last(struct intel_wakeref *wf)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	INTEL_WAKEREF_BUG_ON(atomic_read(&wf->count) <= 0);
5962306a36Sopenharmony_ci	if (unlikely(!atomic_dec_and_test(&wf->count)))
6062306a36Sopenharmony_ci		goto unlock;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* ops->put() must reschedule its own release on error/deferral */
6362306a36Sopenharmony_ci	if (likely(!wf->ops->put(wf))) {
6462306a36Sopenharmony_ci		rpm_put(wf);
6562306a36Sopenharmony_ci		wake_up_var(&wf->wakeref);
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciunlock:
6962306a36Sopenharmony_ci	mutex_unlock(&wf->mutex);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_civoid __intel_wakeref_put_last(struct intel_wakeref *wf, unsigned long flags)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	INTEL_WAKEREF_BUG_ON(delayed_work_pending(&wf->work));
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* Assume we are not in process context and so cannot sleep. */
7762306a36Sopenharmony_ci	if (flags & INTEL_WAKEREF_PUT_ASYNC || !mutex_trylock(&wf->mutex)) {
7862306a36Sopenharmony_ci		mod_delayed_work(wf->i915->unordered_wq, &wf->work,
7962306a36Sopenharmony_ci				 FIELD_GET(INTEL_WAKEREF_PUT_DELAY, flags));
8062306a36Sopenharmony_ci		return;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	____intel_wakeref_put_last(wf);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic void __intel_wakeref_put_work(struct work_struct *wrk)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct intel_wakeref *wf = container_of(wrk, typeof(*wf), work.work);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (atomic_add_unless(&wf->count, -1, 1))
9162306a36Sopenharmony_ci		return;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	mutex_lock(&wf->mutex);
9462306a36Sopenharmony_ci	____intel_wakeref_put_last(wf);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_civoid __intel_wakeref_init(struct intel_wakeref *wf,
9862306a36Sopenharmony_ci			  struct drm_i915_private *i915,
9962306a36Sopenharmony_ci			  const struct intel_wakeref_ops *ops,
10062306a36Sopenharmony_ci			  struct intel_wakeref_lockclass *key)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	wf->i915 = i915;
10362306a36Sopenharmony_ci	wf->ops = ops;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	__mutex_init(&wf->mutex, "wakeref.mutex", &key->mutex);
10662306a36Sopenharmony_ci	atomic_set(&wf->count, 0);
10762306a36Sopenharmony_ci	wf->wakeref = 0;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	INIT_DELAYED_WORK(&wf->work, __intel_wakeref_put_work);
11062306a36Sopenharmony_ci	lockdep_init_map(&wf->work.work.lockdep_map,
11162306a36Sopenharmony_ci			 "wakeref.work", &key->work, 0);
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ciint intel_wakeref_wait_for_idle(struct intel_wakeref *wf)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	int err;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	might_sleep();
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	err = wait_var_event_killable(&wf->wakeref,
12162306a36Sopenharmony_ci				      !intel_wakeref_is_active(wf));
12262306a36Sopenharmony_ci	if (err)
12362306a36Sopenharmony_ci		return err;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	intel_wakeref_unlock_wait(wf);
12662306a36Sopenharmony_ci	return 0;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic void wakeref_auto_timeout(struct timer_list *t)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	struct intel_wakeref_auto *wf = from_timer(wf, t, timer);
13262306a36Sopenharmony_ci	intel_wakeref_t wakeref;
13362306a36Sopenharmony_ci	unsigned long flags;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (!refcount_dec_and_lock_irqsave(&wf->count, &wf->lock, &flags))
13662306a36Sopenharmony_ci		return;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	wakeref = fetch_and_zero(&wf->wakeref);
13962306a36Sopenharmony_ci	spin_unlock_irqrestore(&wf->lock, flags);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	intel_runtime_pm_put(&wf->i915->runtime_pm, wakeref);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_civoid intel_wakeref_auto_init(struct intel_wakeref_auto *wf,
14562306a36Sopenharmony_ci			     struct drm_i915_private *i915)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	spin_lock_init(&wf->lock);
14862306a36Sopenharmony_ci	timer_setup(&wf->timer, wakeref_auto_timeout, 0);
14962306a36Sopenharmony_ci	refcount_set(&wf->count, 0);
15062306a36Sopenharmony_ci	wf->wakeref = 0;
15162306a36Sopenharmony_ci	wf->i915 = i915;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_civoid intel_wakeref_auto(struct intel_wakeref_auto *wf, unsigned long timeout)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	unsigned long flags;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (!timeout) {
15962306a36Sopenharmony_ci		if (del_timer_sync(&wf->timer))
16062306a36Sopenharmony_ci			wakeref_auto_timeout(&wf->timer);
16162306a36Sopenharmony_ci		return;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Our mission is that we only extend an already active wakeref */
16562306a36Sopenharmony_ci	assert_rpm_wakelock_held(&wf->i915->runtime_pm);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (!refcount_inc_not_zero(&wf->count)) {
16862306a36Sopenharmony_ci		spin_lock_irqsave(&wf->lock, flags);
16962306a36Sopenharmony_ci		if (!refcount_inc_not_zero(&wf->count)) {
17062306a36Sopenharmony_ci			INTEL_WAKEREF_BUG_ON(wf->wakeref);
17162306a36Sopenharmony_ci			wf->wakeref =
17262306a36Sopenharmony_ci				intel_runtime_pm_get_if_in_use(&wf->i915->runtime_pm);
17362306a36Sopenharmony_ci			refcount_set(&wf->count, 1);
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci		spin_unlock_irqrestore(&wf->lock, flags);
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/*
17962306a36Sopenharmony_ci	 * If we extend a pending timer, we will only get a single timer
18062306a36Sopenharmony_ci	 * callback and so need to cancel the local inc by running the
18162306a36Sopenharmony_ci	 * elided callback to keep the wf->count balanced.
18262306a36Sopenharmony_ci	 */
18362306a36Sopenharmony_ci	if (mod_timer(&wf->timer, jiffies + timeout))
18462306a36Sopenharmony_ci		wakeref_auto_timeout(&wf->timer);
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_civoid intel_wakeref_auto_fini(struct intel_wakeref_auto *wf)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	intel_wakeref_auto(wf, 0);
19062306a36Sopenharmony_ci	INTEL_WAKEREF_BUG_ON(wf->wakeref);
19162306a36Sopenharmony_ci}
192