xref: /linux/drivers/gpu/drm/i915/display/vlv_clock.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
15615e78eSJani Nikula // SPDX-License-Identifier: MIT
25615e78eSJani Nikula /* Copyright © 2025 Intel Corporation */
35615e78eSJani Nikula 
45615e78eSJani Nikula #include <drm/drm_print.h>
55615e78eSJani Nikula 
65615e78eSJani Nikula #include "intel_display_core.h"
75615e78eSJani Nikula #include "intel_display_types.h"
85615e78eSJani Nikula #include "vlv_clock.h"
95615e78eSJani Nikula #include "vlv_sideband.h"
105615e78eSJani Nikula 
117a356ee5SJani Nikula /*
127a356ee5SJani Nikula  * FIXME: The caching of hpll_freq and czclk_freq relies on the first calls
137a356ee5SJani Nikula  * occurring at a time when they can actually be read. This appears to be the
147a356ee5SJani Nikula  * case, but is somewhat fragile. Make the initialization explicit at a point
157a356ee5SJani Nikula  * where they can be reliably read.
167a356ee5SJani Nikula  */
177a356ee5SJani Nikula 
185615e78eSJani Nikula /* returns HPLL frequency in kHz */
195615e78eSJani Nikula int vlv_clock_get_hpll_vco(struct drm_device *drm)
205615e78eSJani Nikula {
215615e78eSJani Nikula 	struct intel_display *display = to_intel_display(drm);
225615e78eSJani Nikula 	int hpll_freq, vco_freq[] = { 800, 1600, 2000, 2400 };
235615e78eSJani Nikula 
245615e78eSJani Nikula 	if (!display->vlv_clock.hpll_freq) {
255615e78eSJani Nikula 		vlv_cck_get(drm);
265615e78eSJani Nikula 		/* Obtain SKU information */
275615e78eSJani Nikula 		hpll_freq = vlv_cck_read(drm, CCK_FUSE_REG) &
285615e78eSJani Nikula 			CCK_FUSE_HPLL_FREQ_MASK;
295615e78eSJani Nikula 		vlv_cck_put(drm);
305615e78eSJani Nikula 
315615e78eSJani Nikula 		display->vlv_clock.hpll_freq = vco_freq[hpll_freq] * 1000;
325615e78eSJani Nikula 
335615e78eSJani Nikula 		drm_dbg_kms(drm, "HPLL frequency: %d kHz\n", display->vlv_clock.hpll_freq);
345615e78eSJani Nikula 	}
355615e78eSJani Nikula 
365615e78eSJani Nikula 	return display->vlv_clock.hpll_freq;
375615e78eSJani Nikula }
385615e78eSJani Nikula 
39*90930b63SMichał Grzelak static int vlv_clock_get_cck(struct drm_device *drm,
405615e78eSJani Nikula 			     const char *name, u32 reg, int ref_freq)
415615e78eSJani Nikula {
425615e78eSJani Nikula 	u32 val;
435615e78eSJani Nikula 	int divider;
445615e78eSJani Nikula 
455615e78eSJani Nikula 	vlv_cck_get(drm);
465615e78eSJani Nikula 	val = vlv_cck_read(drm, reg);
475615e78eSJani Nikula 	vlv_cck_put(drm);
485615e78eSJani Nikula 
495615e78eSJani Nikula 	divider = val & CCK_FREQUENCY_VALUES;
505615e78eSJani Nikula 
515615e78eSJani Nikula 	drm_WARN(drm, (val & CCK_FREQUENCY_STATUS) !=
525615e78eSJani Nikula 		 (divider << CCK_FREQUENCY_STATUS_SHIFT),
535615e78eSJani Nikula 		 "%s change in progress\n", name);
545615e78eSJani Nikula 
555615e78eSJani Nikula 	return DIV_ROUND_CLOSEST(ref_freq << 1, divider + 1);
565615e78eSJani Nikula }
575615e78eSJani Nikula 
585615e78eSJani Nikula int vlv_clock_get_hrawclk(struct drm_device *drm)
595615e78eSJani Nikula {
605615e78eSJani Nikula 	/* RAWCLK_FREQ_VLV register updated from power well code */
61*90930b63SMichał Grzelak 	return vlv_clock_get_cck(drm, "hrawclk", CCK_DISPLAY_REF_CLOCK_CONTROL,
625615e78eSJani Nikula 				 vlv_clock_get_hpll_vco(drm));
635615e78eSJani Nikula }
645615e78eSJani Nikula 
655615e78eSJani Nikula int vlv_clock_get_czclk(struct drm_device *drm)
665615e78eSJani Nikula {
675615e78eSJani Nikula 	struct intel_display *display = to_intel_display(drm);
685615e78eSJani Nikula 
695615e78eSJani Nikula 	if (!display->vlv_clock.czclk_freq) {
70*90930b63SMichał Grzelak 		display->vlv_clock.czclk_freq = vlv_clock_get_cck(drm, "czclk", CCK_CZ_CLOCK_CONTROL,
715615e78eSJani Nikula 								  vlv_clock_get_hpll_vco(drm));
725615e78eSJani Nikula 		drm_dbg_kms(drm, "CZ clock rate: %d kHz\n", display->vlv_clock.czclk_freq);
735615e78eSJani Nikula 	}
745615e78eSJani Nikula 
755615e78eSJani Nikula 	return display->vlv_clock.czclk_freq;
765615e78eSJani Nikula }
775615e78eSJani Nikula 
785615e78eSJani Nikula int vlv_clock_get_cdclk(struct drm_device *drm)
795615e78eSJani Nikula {
80*90930b63SMichał Grzelak 	return vlv_clock_get_cck(drm, "cdclk", CCK_DISPLAY_CLOCK_CONTROL,
815615e78eSJani Nikula 				 vlv_clock_get_hpll_vco(drm));
825615e78eSJani Nikula }
835615e78eSJani Nikula 
845615e78eSJani Nikula int vlv_clock_get_gpll(struct drm_device *drm)
855615e78eSJani Nikula {
86*90930b63SMichał Grzelak 	return vlv_clock_get_cck(drm, "GPLL ref", CCK_GPLL_CLOCK_CONTROL,
875615e78eSJani Nikula 				 vlv_clock_get_czclk(drm));
885615e78eSJani Nikula }
89