summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/display/vlv_clock.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/display/vlv_clock.c')
-rw-r--r--drivers/gpu/drm/i915/display/vlv_clock.c88
1 files changed, 88 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/display/vlv_clock.c b/drivers/gpu/drm/i915/display/vlv_clock.c
new file mode 100644
index 000000000000..1abdae453514
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/vlv_clock.c
@@ -0,0 +1,88 @@
+// 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(drm);
+ /* Obtain SKU information */
+ hpll_freq = vlv_cck_read(drm, CCK_FUSE_REG) &
+ CCK_FUSE_HPLL_FREQ_MASK;
+ vlv_cck_put(drm);
+
+ 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)
+{
+ u32 val;
+ int divider;
+
+ vlv_cck_get(drm);
+ val = vlv_cck_read(drm, reg);
+ vlv_cck_put(drm);
+
+ 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));
+}