xref: /linux/drivers/gpu/drm/i915/display/vlv_clock.c (revision 815e260a18a3af4dab59025ee99a7156c0e8b5e0)
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(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 
39 static 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 
58 int 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, "hrawclk", CCK_DISPLAY_REF_CLOCK_CONTROL,
62 				 vlv_clock_get_hpll_vco(drm));
63 }
64 
65 int 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, "czclk", CCK_CZ_CLOCK_CONTROL,
71 								  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 
78 int vlv_clock_get_cdclk(struct drm_device *drm)
79 {
80 	return vlv_clock_get_cck(drm, "cdclk", CCK_DISPLAY_CLOCK_CONTROL,
81 				 vlv_clock_get_hpll_vco(drm));
82 }
83 
84 int vlv_clock_get_gpll(struct drm_device *drm)
85 {
86 	return vlv_clock_get_cck(drm, "GPLL ref", CCK_GPLL_CLOCK_CONTROL,
87 				 vlv_clock_get_czclk(drm));
88 }
89