Contributors: 5
Author Tokens Token Proportion Commits Commit Proportion
Ville Syrjälä 444 53.05% 14 56.00%
Jani Nikula 246 29.39% 8 32.00%
Maarten Lankhorst 86 10.27% 1 4.00%
Luciano Coelho 44 5.26% 1 4.00%
Gustavo Sousa 17 2.03% 1 4.00%
Total 837 25


// SPDX-License-Identifier: MIT
/*
 * Copyright © 2026 Intel Corporation
 */

#include <linux/delay.h>

#include <drm/drm_print.h>

#include "intel_de.h"

static int __intel_de_wait_for_register(struct intel_display *display,
					intel_reg_t reg, u32 mask, u32 value,
					unsigned int timeout_us,
					u32 (*read)(struct intel_display *display, intel_reg_t reg),
					u32 *out_val, bool is_atomic)
{
	const ktime_t end = ktime_add_us(ktime_get_raw(), timeout_us);
	int wait_max = 1000;
	int wait = 10;
	u32 reg_value;
	int ret;

	might_sleep_if(!is_atomic);

	if (timeout_us <= 10) {
		is_atomic = true;
		wait = 1;
	}

	for (;;) {
		bool expired = ktime_after(ktime_get_raw(), end);

		/* guarantee the condition is evaluated after timeout expired */
		barrier();

		reg_value = read(display, reg);
		if ((reg_value & mask) == value) {
			ret = 0;
			break;
		}

		if (expired) {
			ret = -ETIMEDOUT;
			break;
		}

		if (is_atomic)
			udelay(wait);
		else
			usleep_range(wait, wait << 1);

		if (wait < wait_max)
			wait <<= 1;
	}

	if (out_val)
		*out_val = reg_value;

	return ret;
}

static int intel_de_wait_for_register(struct intel_display *display,
				      intel_reg_t reg, u32 mask, u32 value,
				      unsigned int fast_timeout_us,
				      unsigned int slow_timeout_us,
				      u32 (*read)(struct intel_display *display, intel_reg_t reg),
				      u32 *out_value, bool is_atomic)
{
	int ret = -EINVAL;

	if (fast_timeout_us)
		ret = __intel_de_wait_for_register(display, reg, mask, value,
						   fast_timeout_us, read,
						   out_value, is_atomic);

	if (ret && slow_timeout_us)
		ret = __intel_de_wait_for_register(display, reg, mask, value,
						   slow_timeout_us, read,
						   out_value, is_atomic);

	return ret;
}

int intel_de_wait_us(struct intel_display *display, intel_reg_t reg,
		     u32 mask, u32 value, unsigned int timeout_us,
		     u32 *out_value)
{
	int ret;

	intel_dmc_wl_get(display, reg);

	ret = intel_de_wait_for_register(display, reg, mask, value,
					 timeout_us, 0,
					 intel_de_read,
					 out_value, false);

	intel_dmc_wl_put(display, reg);

	return ret;
}

int intel_de_wait_ms(struct intel_display *display, intel_reg_t reg,
		     u32 mask, u32 value, unsigned int timeout_ms,
		     u32 *out_value)
{
	int ret;

	intel_dmc_wl_get(display, reg);

	ret = intel_de_wait_for_register(display, reg, mask, value,
					 2, timeout_ms * 1000,
					 intel_de_read,
					 out_value, false);

	intel_dmc_wl_put(display, reg);

	return ret;
}

int intel_de_wait_fw_ms(struct intel_display *display, intel_reg_t reg,
			u32 mask, u32 value, unsigned int timeout_ms,
			u32 *out_value)
{
	return intel_de_wait_for_register(display, reg, mask, value,
					  2, timeout_ms * 1000,
					  intel_de_read_fw,
					  out_value, false);
}

int intel_de_wait_fw_us_atomic(struct intel_display *display, intel_reg_t reg,
			       u32 mask, u32 value, unsigned int timeout_us,
			       u32 *out_value)
{
	return intel_de_wait_for_register(display, reg, mask, value,
					  timeout_us, 0,
					  intel_de_read_fw,
					  out_value, true);
}

int intel_de_wait_for_set_us(struct intel_display *display, intel_reg_t reg,
			     u32 mask, unsigned int timeout_us)
{
	return intel_de_wait_us(display, reg, mask, mask, timeout_us, NULL);
}

int intel_de_wait_for_clear_us(struct intel_display *display, intel_reg_t reg,
			       u32 mask, unsigned int timeout_us)
{
	return intel_de_wait_us(display, reg, mask, 0, timeout_us, NULL);
}

int intel_de_wait_for_set_ms(struct intel_display *display, intel_reg_t reg,
			     u32 mask, unsigned int timeout_ms)
{
	return intel_de_wait_ms(display, reg, mask, mask, timeout_ms, NULL);
}

int intel_de_wait_for_clear_ms(struct intel_display *display, intel_reg_t reg,
			       u32 mask, unsigned int timeout_ms)
{
	return intel_de_wait_ms(display, reg, mask, 0, timeout_ms, NULL);
}

u8 intel_de_read8(struct intel_display *display, intel_reg_t reg)
{
	/* this is only used on VGA registers (possible on pre-g4x) */
	drm_WARN_ON(display->drm, DISPLAY_VER(display) >= 5 || display->platform.g4x);

	return intel_uncore_read8(__to_uncore(display), reg);
}

void intel_de_write8(struct intel_display *display, intel_reg_t reg, u8 val)
{
	drm_WARN_ON(display->drm, DISPLAY_VER(display) >= 5 || display->platform.g4x);

	intel_uncore_write8(__to_uncore(display), reg, val);
}

u16 intel_de_read16(struct intel_display *display, intel_reg_t reg)
{
	/* this is only used on MCHBAR registers on pre-SNB */
	drm_WARN_ON(display->drm, DISPLAY_VER(display) >= 6);

	return intel_uncore_read16(__to_uncore(display), reg);
}