1// SPDX-License-Identifier: MIT
2/* Copyright © 2025 Intel Corporation */
3
4#include <drm/drm_print.h>
5
6#include "intel_display_core.h"
7#include "intel_display_types.h"
8#include "vlv_clock.h"
9#include "vlv_sideband.h"
10
11/*
12 * FIXME: The caching of hpll_freq and czclk_freq relies on the first calls
13 * occurring at a time when they can actually be read. This appears to be the
14 * case, but is somewhat fragile. Make the initialization explicit at a point
15 * where they can be reliably read.
16 */
17
18/* returns HPLL frequency in kHz */
19int vlv_clock_get_hpll_vco(struct drm_device *drm)
20{
21 struct intel_display *display = to_intel_display(drm);
22 int hpll_freq, vco_freq[] = { 800, 1600, 2000, 2400 };
23
24 if (!display->vlv_clock.hpll_freq) {
25 vlv_cck_get(drm);
26 /* Obtain SKU information */
27 hpll_freq = vlv_cck_read(drm, CCK_FUSE_REG) &
28 CCK_FUSE_HPLL_FREQ_MASK;
29 vlv_cck_put(drm);
30
31 display->vlv_clock.hpll_freq = vco_freq[hpll_freq] * 1000;
32
33 drm_dbg_kms(drm, "HPLL frequency: %d kHz\n", display->vlv_clock.hpll_freq);
34 }
35
36 return display->vlv_clock.hpll_freq;
37}
38
39static int vlv_clock_get_cck(struct drm_device *drm,
40 const char *name, u32 reg, int ref_freq)
41{
42 u32 val;
43 int divider;
44
45 vlv_cck_get(drm);
46 val = vlv_cck_read(drm, reg);
47 vlv_cck_put(drm);
48
49 divider = val & CCK_FREQUENCY_VALUES;
50
51 drm_WARN(drm, (val & CCK_FREQUENCY_STATUS) !=
52 (divider << CCK_FREQUENCY_STATUS_SHIFT),
53 "%s change in progress\n", name);
54
55 return DIV_ROUND_CLOSEST(ref_freq << 1, divider + 1);
56}
57
58int vlv_clock_get_hrawclk(struct drm_device *drm)
59{
60 /* RAWCLK_FREQ_VLV register updated from power well code */
61 return vlv_clock_get_cck(drm, name: "hrawclk", CCK_DISPLAY_REF_CLOCK_CONTROL,
62 ref_freq: vlv_clock_get_hpll_vco(drm));
63}
64
65int vlv_clock_get_czclk(struct drm_device *drm)
66{
67 struct intel_display *display = to_intel_display(drm);
68
69 if (!display->vlv_clock.czclk_freq) {
70 display->vlv_clock.czclk_freq = vlv_clock_get_cck(drm, name: "czclk", CCK_CZ_CLOCK_CONTROL,
71 ref_freq: vlv_clock_get_hpll_vco(drm));
72 drm_dbg_kms(drm, "CZ clock rate: %d kHz\n", display->vlv_clock.czclk_freq);
73 }
74
75 return display->vlv_clock.czclk_freq;
76}
77
78int vlv_clock_get_cdclk(struct drm_device *drm)
79{
80 return vlv_clock_get_cck(drm, name: "cdclk", CCK_DISPLAY_CLOCK_CONTROL,
81 ref_freq: vlv_clock_get_hpll_vco(drm));
82}
83
84int vlv_clock_get_gpll(struct drm_device *drm)
85{
86 return vlv_clock_get_cck(drm, name: "GPLL ref", CCK_GPLL_CLOCK_CONTROL,
87 ref_freq: vlv_clock_get_czclk(drm));
88}
89

source code of linux/drivers/gpu/drm/i915/display/vlv_clock.c