xref: /linux/drivers/gpu/drm/loongson/lsdc_pixpll.c (revision 815e260a18a3af4dab59025ee99a7156c0e8b5e0)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2023 Loongson Technology Corporation Limited
4  */
5 
6 #include <linux/delay.h>
7 
8 #include <drm/drm_managed.h>
9 #include <drm/drm_print.h>
10 
11 #include "lsdc_drv.h"
12 
13 /*
14  * The structure of the pixel PLL registers is evolved with times,
15  * it can be different across different chip also.
16  */
17 
18 /* size is u64, note that all loongson's cpu is little endian.
19  * This structure is same for ls7a2000, ls7a1000 and ls2k2000.
20  */
21 struct lsdc_pixpll_reg {
22 	/* Byte 0 ~ Byte 3 */
23 	unsigned div_out       : 7;   /*  6 : 0     Output clock divider  */
24 	unsigned _reserved_1_  : 14;  /* 20 : 7                           */
25 	unsigned loopc         : 9;   /* 29 : 21    Clock multiplier      */
26 	unsigned _reserved_2_  : 2;   /* 31 : 30                          */
27 
28 	/* Byte 4 ~ Byte 7 */
29 	unsigned div_ref       : 7;   /* 38 : 32    Input clock divider   */
30 	unsigned locked        : 1;   /* 39         PLL locked indicator  */
31 	unsigned sel_out       : 1;   /* 40         output clk selector   */
32 	unsigned _reserved_3_  : 2;   /* 42 : 41                          */
33 	unsigned set_param     : 1;   /* 43         Trigger the update    */
34 	unsigned bypass        : 1;   /* 44                               */
35 	unsigned powerdown     : 1;   /* 45                               */
36 	unsigned _reserved_4_  : 18;  /* 46 : 63    no use                */
37 };
38 
39 union lsdc_pixpll_reg_bitmap {
40 	struct lsdc_pixpll_reg bitmap;
41 	u32 w[2];
42 	u64 d;
43 };
44 
45 struct clk_to_pixpll_parms_lookup_t {
46 	unsigned int clock;        /* kHz */
47 
48 	unsigned short width;
49 	unsigned short height;
50 	unsigned short vrefresh;
51 
52 	/* Stores parameters for programming the Hardware PLLs */
53 	unsigned short div_out;
54 	unsigned short loopc;
55 	unsigned short div_ref;
56 };
57 
58 static const struct clk_to_pixpll_parms_lookup_t pixpll_parms_table[] = {
59 	{148500, 1920, 1080, 60,  11, 49,  3},   /* 1920x1080@60Hz */
60 	{141750, 1920, 1080, 60,  11, 78,  5},   /* 1920x1080@60Hz */
61 						 /* 1920x1080@50Hz */
62 	{174500, 1920, 1080, 75,  17, 89,  3},   /* 1920x1080@75Hz */
63 	{181250, 2560, 1080, 75,  8,  58,  4},   /* 2560x1080@75Hz */
64 	{297000, 2560, 1080, 30,  8,  95,  4},   /* 3840x2160@30Hz */
65 	{301992, 1920, 1080, 100, 10, 151, 5},   /* 1920x1080@100Hz */
66 	{146250, 1680, 1050, 60,  16, 117, 5},   /* 1680x1050@60Hz */
67 	{135000, 1280, 1024, 75,  10, 54,  4},   /* 1280x1024@75Hz */
68 	{119000, 1680, 1050, 60,  20, 119, 5},   /* 1680x1050@60Hz */
69 	{108000, 1600, 900,  60,  15, 81,  5},   /* 1600x900@60Hz  */
70 						 /* 1280x1024@60Hz */
71 						 /* 1280x960@60Hz */
72 						 /* 1152x864@75Hz */
73 
74 	{106500, 1440, 900,  60,  19, 81,  4},   /* 1440x900@60Hz */
75 	{88750,  1440, 900,  60,  16, 71,  5},   /* 1440x900@60Hz */
76 	{83500,  1280, 800,  60,  17, 71,  5},   /* 1280x800@60Hz */
77 	{71000,  1280, 800,  60,  20, 71,  5},   /* 1280x800@60Hz */
78 
79 	{74250,  1280, 720,  60,  22, 49,  3},   /* 1280x720@60Hz */
80 						 /* 1280x720@50Hz */
81 
82 	{78750,  1024, 768,  75,  16, 63,  5},   /* 1024x768@75Hz */
83 	{75000,  1024, 768,  70,  29, 87,  4},   /* 1024x768@70Hz */
84 	{65000,  1024, 768,  60,  20, 39,  3},   /* 1024x768@60Hz */
85 
86 	{51200,  1024, 600,  60,  25, 64,  5},   /* 1024x600@60Hz */
87 
88 	{57284,  832,  624,  75,  24, 55,  4},   /* 832x624@75Hz */
89 	{49500,  800,  600,  75,  40, 99,  5},   /* 800x600@75Hz */
90 	{50000,  800,  600,  72,  44, 88,  4},   /* 800x600@72Hz */
91 	{40000,  800,  600,  60,  30, 36,  3},   /* 800x600@60Hz */
92 	{36000,  800,  600,  56,  50, 72,  4},   /* 800x600@56Hz */
93 	{31500,  640,  480,  75,  40, 63,  5},   /* 640x480@75Hz */
94 						 /* 640x480@73Hz */
95 
96 	{30240,  640,  480,  67,  62, 75,  4},   /* 640x480@67Hz */
97 	{27000,  720,  576,  50,  50, 54,  4},   /* 720x576@60Hz */
98 	{25175,  640,  480,  60,  85, 107, 5},   /* 640x480@60Hz */
99 	{25200,  640,  480,  60,  50, 63,  5},   /* 640x480@60Hz */
100 						 /* 720x480@60Hz */
101 };
102 
103 static void lsdc_pixel_pll_free(struct drm_device *ddev, void *data)
104 {
105 	struct lsdc_pixpll *this = (struct lsdc_pixpll *)data;
106 
107 	iounmap(this->mmio);
108 
109 	kfree(this->priv);
110 
111 	drm_dbg(ddev, "pixpll private data freed\n");
112 }
113 
114 /*
115  * ioremap the device dependent PLL registers
116  *
117  * @this: point to the object where this function is called from
118  */
119 static int lsdc_pixel_pll_setup(struct lsdc_pixpll * const this)
120 {
121 	struct lsdc_pixpll_parms *pparms;
122 
123 	this->mmio = ioremap(this->reg_base, this->reg_size);
124 	if (!this->mmio)
125 		return -ENOMEM;
126 
127 	pparms = kzalloc(sizeof(*pparms), GFP_KERNEL);
128 	if (!pparms) {
129 		iounmap(this->mmio);
130 		return -ENOMEM;
131 	}
132 
133 	pparms->ref_clock = LSDC_PLL_REF_CLK_KHZ;
134 
135 	this->priv = pparms;
136 
137 	return drmm_add_action_or_reset(this->ddev, lsdc_pixel_pll_free, this);
138 }
139 
140 /*
141  * Find a set of pll parameters from a static local table which avoid
142  * computing the pll parameter eachtime a modeset is triggered.
143  *
144  * @this: point to the object where this function is called from
145  * @clock: the desired output pixel clock, the unit is kHz
146  * @pout: point to where the parameters to store if found
147  *
148  * Return 0 if success, return -1 if not found.
149  */
150 static int lsdc_pixpll_find(struct lsdc_pixpll * const this,
151 			    unsigned int clock,
152 			    struct lsdc_pixpll_parms *pout)
153 {
154 	unsigned int num = ARRAY_SIZE(pixpll_parms_table);
155 	const struct clk_to_pixpll_parms_lookup_t *pt;
156 	unsigned int i;
157 
158 	for (i = 0; i < num; ++i) {
159 		pt = &pixpll_parms_table[i];
160 
161 		if (clock == pt->clock) {
162 			pout->div_ref = pt->div_ref;
163 			pout->loopc   = pt->loopc;
164 			pout->div_out = pt->div_out;
165 
166 			return 0;
167 		}
168 	}
169 
170 	drm_dbg_kms(this->ddev, "pixel clock %u: miss\n", clock);
171 
172 	return -1;
173 }
174 
175 /*
176  * Find a set of pll parameters which have minimal difference with the
177  * desired pixel clock frequency. It does that by computing all of the
178  * possible combination. Compute the diff and find the combination with
179  * minimal diff.
180  *
181  * clock_out = refclk / div_ref * loopc / div_out
182  *
183  * refclk is determined by the oscillator mounted on motherboard(100MHz
184  * in almost all board)
185  *
186  * @this: point to the object from where this function is called
187  * @clock: the desired output pixel clock, the unit is kHz
188  * @pout: point to the out struct of lsdc_pixpll_parms
189  *
190  * Return 0 if a set of parameter is found, otherwise return the error
191  * between clock_kHz we wanted and the most closest candidate with it.
192  */
193 static int lsdc_pixel_pll_compute(struct lsdc_pixpll * const this,
194 				  unsigned int clock,
195 				  struct lsdc_pixpll_parms *pout)
196 {
197 	struct lsdc_pixpll_parms *pparms = this->priv;
198 	unsigned int refclk = pparms->ref_clock;
199 	const unsigned int tolerance = 1000;
200 	unsigned int min = tolerance;
201 	unsigned int div_out, loopc, div_ref;
202 	unsigned int computed;
203 
204 	if (!lsdc_pixpll_find(this, clock, pout))
205 		return 0;
206 
207 	for (div_out = 6; div_out < 64; div_out++) {
208 		for (div_ref = 3; div_ref < 6; div_ref++) {
209 			for (loopc = 6; loopc < 161; loopc++) {
210 				unsigned int diff = 0;
211 
212 				if (loopc < 12 * div_ref)
213 					continue;
214 				if (loopc > 32 * div_ref)
215 					continue;
216 
217 				computed = refclk / div_ref * loopc / div_out;
218 
219 				if (clock >= computed)
220 					diff = clock - computed;
221 				else
222 					diff = computed - clock;
223 
224 				if (diff < min) {
225 					min = diff;
226 					pparms->div_ref = div_ref;
227 					pparms->div_out = div_out;
228 					pparms->loopc = loopc;
229 
230 					if (diff == 0) {
231 						*pout = *pparms;
232 						return 0;
233 					}
234 				}
235 			}
236 		}
237 	}
238 
239 	/* still acceptable */
240 	if (min < tolerance) {
241 		*pout = *pparms;
242 		return 0;
243 	}
244 
245 	drm_dbg(this->ddev, "can't find suitable params for %u khz\n", clock);
246 
247 	return min;
248 }
249 
250 /* Pixel pll hardware related ops, per display pipe */
251 
252 static void __pixpll_rreg(struct lsdc_pixpll *this,
253 			  union lsdc_pixpll_reg_bitmap *dst)
254 {
255 #if defined(CONFIG_64BIT)
256 	dst->d = readq(this->mmio);
257 #else
258 	dst->w[0] = readl(this->mmio);
259 	dst->w[1] = readl(this->mmio + 4);
260 #endif
261 }
262 
263 static void __pixpll_wreg(struct lsdc_pixpll *this,
264 			  union lsdc_pixpll_reg_bitmap *src)
265 {
266 #if defined(CONFIG_64BIT)
267 	writeq(src->d, this->mmio);
268 #else
269 	writel(src->w[0], this->mmio);
270 	writel(src->w[1], this->mmio + 4);
271 #endif
272 }
273 
274 static void __pixpll_ops_powerup(struct lsdc_pixpll * const this)
275 {
276 	union lsdc_pixpll_reg_bitmap pixpll_reg;
277 
278 	__pixpll_rreg(this, &pixpll_reg);
279 
280 	pixpll_reg.bitmap.powerdown = 0;
281 
282 	__pixpll_wreg(this, &pixpll_reg);
283 }
284 
285 static void __pixpll_ops_powerdown(struct lsdc_pixpll * const this)
286 {
287 	union lsdc_pixpll_reg_bitmap pixpll_reg;
288 
289 	__pixpll_rreg(this, &pixpll_reg);
290 
291 	pixpll_reg.bitmap.powerdown = 1;
292 
293 	__pixpll_wreg(this, &pixpll_reg);
294 }
295 
296 static void __pixpll_ops_on(struct lsdc_pixpll * const this)
297 {
298 	union lsdc_pixpll_reg_bitmap pixpll_reg;
299 
300 	__pixpll_rreg(this, &pixpll_reg);
301 
302 	pixpll_reg.bitmap.sel_out = 1;
303 
304 	__pixpll_wreg(this, &pixpll_reg);
305 }
306 
307 static void __pixpll_ops_off(struct lsdc_pixpll * const this)
308 {
309 	union lsdc_pixpll_reg_bitmap pixpll_reg;
310 
311 	__pixpll_rreg(this, &pixpll_reg);
312 
313 	pixpll_reg.bitmap.sel_out = 0;
314 
315 	__pixpll_wreg(this, &pixpll_reg);
316 }
317 
318 static void __pixpll_ops_bypass(struct lsdc_pixpll * const this)
319 {
320 	union lsdc_pixpll_reg_bitmap pixpll_reg;
321 
322 	__pixpll_rreg(this, &pixpll_reg);
323 
324 	pixpll_reg.bitmap.bypass = 1;
325 
326 	__pixpll_wreg(this, &pixpll_reg);
327 }
328 
329 static void __pixpll_ops_unbypass(struct lsdc_pixpll * const this)
330 {
331 	union lsdc_pixpll_reg_bitmap pixpll_reg;
332 
333 	__pixpll_rreg(this, &pixpll_reg);
334 
335 	pixpll_reg.bitmap.bypass = 0;
336 
337 	__pixpll_wreg(this, &pixpll_reg);
338 }
339 
340 static void __pixpll_ops_untoggle_param(struct lsdc_pixpll * const this)
341 {
342 	union lsdc_pixpll_reg_bitmap pixpll_reg;
343 
344 	__pixpll_rreg(this, &pixpll_reg);
345 
346 	pixpll_reg.bitmap.set_param = 0;
347 
348 	__pixpll_wreg(this, &pixpll_reg);
349 }
350 
351 static void __pixpll_ops_set_param(struct lsdc_pixpll * const this,
352 				   struct lsdc_pixpll_parms const *p)
353 {
354 	union lsdc_pixpll_reg_bitmap pixpll_reg;
355 
356 	__pixpll_rreg(this, &pixpll_reg);
357 
358 	pixpll_reg.bitmap.div_ref = p->div_ref;
359 	pixpll_reg.bitmap.loopc = p->loopc;
360 	pixpll_reg.bitmap.div_out = p->div_out;
361 
362 	__pixpll_wreg(this, &pixpll_reg);
363 }
364 
365 static void __pixpll_ops_toggle_param(struct lsdc_pixpll * const this)
366 {
367 	union lsdc_pixpll_reg_bitmap pixpll_reg;
368 
369 	__pixpll_rreg(this, &pixpll_reg);
370 
371 	pixpll_reg.bitmap.set_param = 1;
372 
373 	__pixpll_wreg(this, &pixpll_reg);
374 }
375 
376 static void __pixpll_ops_wait_locked(struct lsdc_pixpll * const this)
377 {
378 	union lsdc_pixpll_reg_bitmap pixpll_reg;
379 	unsigned int counter = 0;
380 
381 	do {
382 		__pixpll_rreg(this, &pixpll_reg);
383 
384 		if (pixpll_reg.bitmap.locked)
385 			break;
386 
387 		++counter;
388 	} while (counter < 2000);
389 
390 	drm_dbg(this->ddev, "%u loop waited\n", counter);
391 }
392 
393 /*
394  * Update the PLL parameters to the PLL hardware
395  *
396  * @this: point to the object from which this function is called
397  * @pin: point to the struct of lsdc_pixpll_parms passed in
398  *
399  * return 0 if successful.
400  */
401 static int lsdc_pixpll_update(struct lsdc_pixpll * const this,
402 			      struct lsdc_pixpll_parms const *pin)
403 {
404 	__pixpll_ops_bypass(this);
405 
406 	__pixpll_ops_off(this);
407 
408 	__pixpll_ops_powerdown(this);
409 
410 	__pixpll_ops_toggle_param(this);
411 
412 	__pixpll_ops_set_param(this, pin);
413 
414 	__pixpll_ops_untoggle_param(this);
415 
416 	__pixpll_ops_powerup(this);
417 
418 	udelay(2);
419 
420 	__pixpll_ops_wait_locked(this);
421 
422 	__pixpll_ops_on(this);
423 
424 	__pixpll_ops_unbypass(this);
425 
426 	return 0;
427 }
428 
429 static unsigned int lsdc_pixpll_get_freq(struct lsdc_pixpll * const this)
430 {
431 	struct lsdc_pixpll_parms *ppar = this->priv;
432 	union lsdc_pixpll_reg_bitmap pix_pll_reg;
433 	unsigned int freq;
434 
435 	__pixpll_rreg(this, &pix_pll_reg);
436 
437 	ppar->div_ref = pix_pll_reg.bitmap.div_ref;
438 	ppar->loopc = pix_pll_reg.bitmap.loopc;
439 	ppar->div_out = pix_pll_reg.bitmap.div_out;
440 
441 	freq = ppar->ref_clock / ppar->div_ref * ppar->loopc / ppar->div_out;
442 
443 	return freq;
444 }
445 
446 static void lsdc_pixpll_print(struct lsdc_pixpll * const this,
447 			      struct drm_printer *p)
448 {
449 	struct lsdc_pixpll_parms *parms = this->priv;
450 
451 	drm_printf(p, "div_ref: %u, loopc: %u, div_out: %u\n",
452 		   parms->div_ref, parms->loopc, parms->div_out);
453 }
454 
455 /*
456  * LS7A1000, LS7A2000 and ls2k2000's pixel pll setting register is same,
457  * we take this as default, create a new instance if a different model is
458  * introduced.
459  */
460 static const struct lsdc_pixpll_funcs __pixpll_default_funcs = {
461 	.setup = lsdc_pixel_pll_setup,
462 	.compute = lsdc_pixel_pll_compute,
463 	.update = lsdc_pixpll_update,
464 	.get_rate = lsdc_pixpll_get_freq,
465 	.print = lsdc_pixpll_print,
466 };
467 
468 /* pixel pll initialization */
469 
470 int lsdc_pixpll_init(struct lsdc_pixpll * const this,
471 		     struct drm_device *ddev,
472 		     unsigned int index)
473 {
474 	struct lsdc_device *ldev = to_lsdc(ddev);
475 	const struct lsdc_desc *descp = ldev->descp;
476 	const struct loongson_gfx_desc *gfx = to_loongson_gfx(descp);
477 
478 	this->ddev = ddev;
479 	this->reg_size = 8;
480 	this->reg_base = gfx->conf_reg_base + gfx->pixpll[index].reg_offset;
481 	this->funcs = &__pixpll_default_funcs;
482 
483 	return this->funcs->setup(this);
484 }
485