xref: /linux/drivers/gpu/drm/i915/display/vlv_clock.c (revision bba2c3615bd6cfee7456d1130f2e6b01b3f4e9ba)
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 */
19 int 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(display);
26 		/* Obtain SKU information */
27 		hpll_freq = vlv_cck_read(display, CCK_FUSE_REG) &
28 			CCK_FUSE_HPLL_FREQ_MASK;
29 		vlv_cck_put(display);
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 
39 static int vlv_clock_get_cck(struct drm_device *drm,
40 			     const char *name, u32 reg, int ref_freq)
41 {
42 	struct intel_display *display = to_intel_display(drm);
43 	u32 val;
44 	int divider;
45 
46 	vlv_cck_get(display);
47 	val = vlv_cck_read(display, reg);
48 	vlv_cck_put(display);
49 
50 	divider = val & CCK_FREQUENCY_VALUES;
51 
52 	drm_WARN(drm, (val & CCK_FREQUENCY_STATUS) !=
53 		 (divider << CCK_FREQUENCY_STATUS_SHIFT),
54 		 "%s change in progress\n", name);
55 
56 	return DIV_ROUND_CLOSEST(ref_freq << 1, divider + 1);
57 }
58 
59 int vlv_clock_get_hrawclk(struct drm_device *drm)
60 {
61 	/* RAWCLK_FREQ_VLV register updated from power well code */
62 	return vlv_clock_get_cck(drm, "hrawclk", CCK_DISPLAY_REF_CLOCK_CONTROL,
63 				 vlv_clock_get_hpll_vco(drm));
64 }
65 
66 int vlv_clock_get_czclk(struct drm_device *drm)
67 {
68 	struct intel_display *display = to_intel_display(drm);
69 
70 	if (!display->vlv_clock.czclk_freq) {
71 		display->vlv_clock.czclk_freq = vlv_clock_get_cck(drm, "czclk", CCK_CZ_CLOCK_CONTROL,
72 								  vlv_clock_get_hpll_vco(drm));
73 		drm_dbg_kms(drm, "CZ clock rate: %d kHz\n", display->vlv_clock.czclk_freq);
74 	}
75 
76 	return display->vlv_clock.czclk_freq;
77 }
78 
79 int vlv_clock_get_cdclk(struct drm_device *drm)
80 {
81 	return vlv_clock_get_cck(drm, "cdclk", CCK_DISPLAY_CLOCK_CONTROL,
82 				 vlv_clock_get_hpll_vco(drm));
83 }
84 
85 int vlv_clock_get_gpll(struct drm_device *drm)
86 {
87 	return vlv_clock_get_cck(drm, "GPLL ref", CCK_GPLL_CLOCK_CONTROL,
88 				 vlv_clock_get_czclk(drm));
89 }
90