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