Contributors: 9
Author Tokens Token Proportion Commits Commit Proportion
Jani Nikula 207 56.87% 19 55.88%
Ville Syrjälä 135 37.09% 8 23.53%
Michał Grzelak 5 1.37% 1 2.94%
Daniel Vetter 5 1.37% 1 2.94%
Jesse Barnes 4 1.10% 1 2.94%
Pankaj Bharadiya 3 0.82% 1 2.94%
Nemesa Garg 2 0.55% 1 2.94%
Wambui Karuga 2 0.55% 1 2.94%
Chris Wilson 1 0.27% 1 2.94%
Total 364 34


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

#include <drm/drm_print.h>

#include "intel_display_core.h"
#include "intel_display_types.h"
#include "vlv_clock.h"
#include "vlv_sideband.h"

/*
 * FIXME: The caching of hpll_freq and czclk_freq relies on the first calls
 * occurring at a time when they can actually be read. This appears to be the
 * case, but is somewhat fragile. Make the initialization explicit at a point
 * where they can be reliably read.
 */

/* returns HPLL frequency in kHz */
int vlv_clock_get_hpll_vco(struct drm_device *drm)
{
	struct intel_display *display = to_intel_display(drm);
	int hpll_freq, vco_freq[] = { 800, 1600, 2000, 2400 };

	if (!display->vlv_clock.hpll_freq) {
		vlv_cck_get(display);
		/* Obtain SKU information */
		hpll_freq = vlv_cck_read(display, CCK_FUSE_REG) &
			CCK_FUSE_HPLL_FREQ_MASK;
		vlv_cck_put(display);

		display->vlv_clock.hpll_freq = vco_freq[hpll_freq] * 1000;

		drm_dbg_kms(drm, "HPLL frequency: %d kHz\n", display->vlv_clock.hpll_freq);
	}

	return display->vlv_clock.hpll_freq;
}

static int vlv_clock_get_cck(struct drm_device *drm,
			     const char *name, u32 reg, int ref_freq)
{
	struct intel_display *display = to_intel_display(drm);
	u32 val;
	int divider;

	vlv_cck_get(display);
	val = vlv_cck_read(display, reg);
	vlv_cck_put(display);

	divider = val & CCK_FREQUENCY_VALUES;

	drm_WARN(drm, (val & CCK_FREQUENCY_STATUS) !=
		 (divider << CCK_FREQUENCY_STATUS_SHIFT),
		 "%s change in progress\n", name);

	return DIV_ROUND_CLOSEST(ref_freq << 1, divider + 1);
}

int vlv_clock_get_hrawclk(struct drm_device *drm)
{
	/* RAWCLK_FREQ_VLV register updated from power well code */
	return vlv_clock_get_cck(drm, "hrawclk", CCK_DISPLAY_REF_CLOCK_CONTROL,
				 vlv_clock_get_hpll_vco(drm));
}

int vlv_clock_get_czclk(struct drm_device *drm)
{
	struct intel_display *display = to_intel_display(drm);

	if (!display->vlv_clock.czclk_freq) {
		display->vlv_clock.czclk_freq = vlv_clock_get_cck(drm, "czclk", CCK_CZ_CLOCK_CONTROL,
								  vlv_clock_get_hpll_vco(drm));
		drm_dbg_kms(drm, "CZ clock rate: %d kHz\n", display->vlv_clock.czclk_freq);
	}

	return display->vlv_clock.czclk_freq;
}

int vlv_clock_get_cdclk(struct drm_device *drm)
{
	return vlv_clock_get_cck(drm, "cdclk", CCK_DISPLAY_CLOCK_CONTROL,
				 vlv_clock_get_hpll_vco(drm));
}

int vlv_clock_get_gpll(struct drm_device *drm)
{
	return vlv_clock_get_cck(drm, "GPLL ref", CCK_GPLL_CLOCK_CONTROL,
				 vlv_clock_get_czclk(drm));
}