xref: /linux/drivers/gpu/drm/loongson/lsdc_pixpll.c (revision 03c11eb3b16dc0058589751dfd91f254be2be613)
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 
lsdc_pixel_pll_free(struct drm_device * ddev,void * data)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  */
lsdc_pixel_pll_setup(struct lsdc_pixpll * const this)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 (!this->mmio)
124 		return -ENOMEM;
125 
126 	pparms = kzalloc(sizeof(*pparms), GFP_KERNEL);
127 	if (!pparms) {
128 		iounmap(this->mmio);
129 		return -ENOMEM;
130 	}
131 
132 	pparms->ref_clock = LSDC_PLL_REF_CLK_KHZ;
133 
134 	this->priv = pparms;
135 
136 	return drmm_add_action_or_reset(this->ddev, lsdc_pixel_pll_free, this);
137 }
138 
139 /*
140  * Find a set of pll parameters from a static local table which avoid
141  * computing the pll parameter eachtime a modeset is triggered.
142  *
143  * @this: point to the object where this function is called from
144  * @clock: the desired output pixel clock, the unit is kHz
145  * @pout: point to where the parameters to store if found
146  *
147  * Return 0 if success, return -1 if not found.
148  */
lsdc_pixpll_find(struct lsdc_pixpll * const this,unsigned int clock,struct lsdc_pixpll_parms * pout)149 static int lsdc_pixpll_find(struct lsdc_pixpll * const this,
150 			    unsigned int clock,
151 			    struct lsdc_pixpll_parms *pout)
152 {
153 	unsigned int num = ARRAY_SIZE(pixpll_parms_table);
154 	const struct clk_to_pixpll_parms_lookup_t *pt;
155 	unsigned int i;
156 
157 	for (i = 0; i < num; ++i) {
158 		pt = &pixpll_parms_table[i];
159 
160 		if (clock == pt->clock) {
161 			pout->div_ref = pt->div_ref;
162 			pout->loopc   = pt->loopc;
163 			pout->div_out = pt->div_out;
164 
165 			return 0;
166 		}
167 	}
168 
169 	drm_dbg_kms(this->ddev, "pixel clock %u: miss\n", clock);
170 
171 	return -1;
172 }
173 
174 /*
175  * Find a set of pll parameters which have minimal difference with the
176  * desired pixel clock frequency. It does that by computing all of the
177  * possible combination. Compute the diff and find the combination with
178  * minimal diff.
179  *
180  * clock_out = refclk / div_ref * loopc / div_out
181  *
182  * refclk is determined by the oscillator mounted on motherboard(100MHz
183  * in almost all board)
184  *
185  * @this: point to the object from where this function is called
186  * @clock: the desired output pixel clock, the unit is kHz
187  * @pout: point to the out struct of lsdc_pixpll_parms
188  *
189  * Return 0 if a set of parameter is found, otherwise return the error
190  * between clock_kHz we wanted and the most closest candidate with it.
191  */
lsdc_pixel_pll_compute(struct lsdc_pixpll * const this,unsigned int clock,struct lsdc_pixpll_parms * pout)192 static int lsdc_pixel_pll_compute(struct lsdc_pixpll * const this,
193 				  unsigned int clock,
194 				  struct lsdc_pixpll_parms *pout)
195 {
196 	struct lsdc_pixpll_parms *pparms = this->priv;
197 	unsigned int refclk = pparms->ref_clock;
198 	const unsigned int tolerance = 1000;
199 	unsigned int min = tolerance;
200 	unsigned int div_out, loopc, div_ref;
201 	unsigned int computed;
202 
203 	if (!lsdc_pixpll_find(this, clock, pout))
204 		return 0;
205 
206 	for (div_out = 6; div_out < 64; div_out++) {
207 		for (div_ref = 3; div_ref < 6; div_ref++) {
208 			for (loopc = 6; loopc < 161; loopc++) {
209 				unsigned int diff = 0;
210 
211 				if (loopc < 12 * div_ref)
212 					continue;
213 				if (loopc > 32 * div_ref)
214 					continue;
215 
216 				computed = refclk / div_ref * loopc / div_out;
217 
218 				if (clock >= computed)
219 					diff = clock - computed;
220 				else
221 					diff = computed - clock;
222 
223 				if (diff < min) {
224 					min = diff;
225 					pparms->div_ref = div_ref;
226 					pparms->div_out = div_out;
227 					pparms->loopc = loopc;
228 
229 					if (diff == 0) {
230 						*pout = *pparms;
231 						return 0;
232 					}
233 				}
234 			}
235 		}
236 	}
237 
238 	/* still acceptable */
239 	if (min < tolerance) {
240 		*pout = *pparms;
241 		return 0;
242 	}
243 
244 	drm_dbg(this->ddev, "can't find suitable params for %u khz\n", clock);
245 
246 	return min;
247 }
248 
249 /* Pixel pll hardware related ops, per display pipe */
250 
__pixpll_rreg(struct lsdc_pixpll * this,union lsdc_pixpll_reg_bitmap * dst)251 static void __pixpll_rreg(struct lsdc_pixpll *this,
252 			  union lsdc_pixpll_reg_bitmap *dst)
253 {
254 #if defined(CONFIG_64BIT)
255 	dst->d = readq(this->mmio);
256 #else
257 	dst->w[0] = readl(this->mmio);
258 	dst->w[1] = readl(this->mmio + 4);
259 #endif
260 }
261 
__pixpll_wreg(struct lsdc_pixpll * this,union lsdc_pixpll_reg_bitmap * src)262 static void __pixpll_wreg(struct lsdc_pixpll *this,
263 			  union lsdc_pixpll_reg_bitmap *src)
264 {
265 #if defined(CONFIG_64BIT)
266 	writeq(src->d, this->mmio);
267 #else
268 	writel(src->w[0], this->mmio);
269 	writel(src->w[1], this->mmio + 4);
270 #endif
271 }
272 
__pixpll_ops_powerup(struct lsdc_pixpll * const this)273 static void __pixpll_ops_powerup(struct lsdc_pixpll * const this)
274 {
275 	union lsdc_pixpll_reg_bitmap pixpll_reg;
276 
277 	__pixpll_rreg(this, &pixpll_reg);
278 
279 	pixpll_reg.bitmap.powerdown = 0;
280 
281 	__pixpll_wreg(this, &pixpll_reg);
282 }
283 
__pixpll_ops_powerdown(struct lsdc_pixpll * const this)284 static void __pixpll_ops_powerdown(struct lsdc_pixpll * const this)
285 {
286 	union lsdc_pixpll_reg_bitmap pixpll_reg;
287 
288 	__pixpll_rreg(this, &pixpll_reg);
289 
290 	pixpll_reg.bitmap.powerdown = 1;
291 
292 	__pixpll_wreg(this, &pixpll_reg);
293 }
294 
__pixpll_ops_on(struct lsdc_pixpll * const this)295 static void __pixpll_ops_on(struct lsdc_pixpll * const this)
296 {
297 	union lsdc_pixpll_reg_bitmap pixpll_reg;
298 
299 	__pixpll_rreg(this, &pixpll_reg);
300 
301 	pixpll_reg.bitmap.sel_out = 1;
302 
303 	__pixpll_wreg(this, &pixpll_reg);
304 }
305 
__pixpll_ops_off(struct lsdc_pixpll * const this)306 static void __pixpll_ops_off(struct lsdc_pixpll * const this)
307 {
308 	union lsdc_pixpll_reg_bitmap pixpll_reg;
309 
310 	__pixpll_rreg(this, &pixpll_reg);
311 
312 	pixpll_reg.bitmap.sel_out = 0;
313 
314 	__pixpll_wreg(this, &pixpll_reg);
315 }
316 
__pixpll_ops_bypass(struct lsdc_pixpll * const this)317 static void __pixpll_ops_bypass(struct lsdc_pixpll * const this)
318 {
319 	union lsdc_pixpll_reg_bitmap pixpll_reg;
320 
321 	__pixpll_rreg(this, &pixpll_reg);
322 
323 	pixpll_reg.bitmap.bypass = 1;
324 
325 	__pixpll_wreg(this, &pixpll_reg);
326 }
327 
__pixpll_ops_unbypass(struct lsdc_pixpll * const this)328 static void __pixpll_ops_unbypass(struct lsdc_pixpll * const this)
329 {
330 	union lsdc_pixpll_reg_bitmap pixpll_reg;
331 
332 	__pixpll_rreg(this, &pixpll_reg);
333 
334 	pixpll_reg.bitmap.bypass = 0;
335 
336 	__pixpll_wreg(this, &pixpll_reg);
337 }
338 
__pixpll_ops_untoggle_param(struct lsdc_pixpll * const this)339 static void __pixpll_ops_untoggle_param(struct lsdc_pixpll * const this)
340 {
341 	union lsdc_pixpll_reg_bitmap pixpll_reg;
342 
343 	__pixpll_rreg(this, &pixpll_reg);
344 
345 	pixpll_reg.bitmap.set_param = 0;
346 
347 	__pixpll_wreg(this, &pixpll_reg);
348 }
349 
__pixpll_ops_set_param(struct lsdc_pixpll * const this,struct lsdc_pixpll_parms const * p)350 static void __pixpll_ops_set_param(struct lsdc_pixpll * const this,
351 				   struct lsdc_pixpll_parms const *p)
352 {
353 	union lsdc_pixpll_reg_bitmap pixpll_reg;
354 
355 	__pixpll_rreg(this, &pixpll_reg);
356 
357 	pixpll_reg.bitmap.div_ref = p->div_ref;
358 	pixpll_reg.bitmap.loopc = p->loopc;
359 	pixpll_reg.bitmap.div_out = p->div_out;
360 
361 	__pixpll_wreg(this, &pixpll_reg);
362 }
363 
__pixpll_ops_toggle_param(struct lsdc_pixpll * const this)364 static void __pixpll_ops_toggle_param(struct lsdc_pixpll * const this)
365 {
366 	union lsdc_pixpll_reg_bitmap pixpll_reg;
367 
368 	__pixpll_rreg(this, &pixpll_reg);
369 
370 	pixpll_reg.bitmap.set_param = 1;
371 
372 	__pixpll_wreg(this, &pixpll_reg);
373 }
374 
__pixpll_ops_wait_locked(struct lsdc_pixpll * const this)375 static void __pixpll_ops_wait_locked(struct lsdc_pixpll * const this)
376 {
377 	union lsdc_pixpll_reg_bitmap pixpll_reg;
378 	unsigned int counter = 0;
379 
380 	do {
381 		__pixpll_rreg(this, &pixpll_reg);
382 
383 		if (pixpll_reg.bitmap.locked)
384 			break;
385 
386 		++counter;
387 	} while (counter < 2000);
388 
389 	drm_dbg(this->ddev, "%u loop waited\n", counter);
390 }
391 
392 /*
393  * Update the PLL parameters to the PLL hardware
394  *
395  * @this: point to the object from which this function is called
396  * @pin: point to the struct of lsdc_pixpll_parms passed in
397  *
398  * return 0 if successful.
399  */
lsdc_pixpll_update(struct lsdc_pixpll * const this,struct lsdc_pixpll_parms const * pin)400 static int lsdc_pixpll_update(struct lsdc_pixpll * const this,
401 			      struct lsdc_pixpll_parms const *pin)
402 {
403 	__pixpll_ops_bypass(this);
404 
405 	__pixpll_ops_off(this);
406 
407 	__pixpll_ops_powerdown(this);
408 
409 	__pixpll_ops_toggle_param(this);
410 
411 	__pixpll_ops_set_param(this, pin);
412 
413 	__pixpll_ops_untoggle_param(this);
414 
415 	__pixpll_ops_powerup(this);
416 
417 	udelay(2);
418 
419 	__pixpll_ops_wait_locked(this);
420 
421 	__pixpll_ops_on(this);
422 
423 	__pixpll_ops_unbypass(this);
424 
425 	return 0;
426 }
427 
lsdc_pixpll_get_freq(struct lsdc_pixpll * const this)428 static unsigned int lsdc_pixpll_get_freq(struct lsdc_pixpll * const this)
429 {
430 	struct lsdc_pixpll_parms *ppar = this->priv;
431 	union lsdc_pixpll_reg_bitmap pix_pll_reg;
432 	unsigned int freq;
433 
434 	__pixpll_rreg(this, &pix_pll_reg);
435 
436 	ppar->div_ref = pix_pll_reg.bitmap.div_ref;
437 	ppar->loopc = pix_pll_reg.bitmap.loopc;
438 	ppar->div_out = pix_pll_reg.bitmap.div_out;
439 
440 	freq = ppar->ref_clock / ppar->div_ref * ppar->loopc / ppar->div_out;
441 
442 	return freq;
443 }
444 
lsdc_pixpll_print(struct lsdc_pixpll * const this,struct drm_printer * p)445 static void lsdc_pixpll_print(struct lsdc_pixpll * const this,
446 			      struct drm_printer *p)
447 {
448 	struct lsdc_pixpll_parms *parms = this->priv;
449 
450 	drm_printf(p, "div_ref: %u, loopc: %u, div_out: %u\n",
451 		   parms->div_ref, parms->loopc, parms->div_out);
452 }
453 
454 /*
455  * LS7A1000, LS7A2000 and ls2k2000's pixel pll setting register is same,
456  * we take this as default, create a new instance if a different model is
457  * introduced.
458  */
459 static const struct lsdc_pixpll_funcs __pixpll_default_funcs = {
460 	.setup = lsdc_pixel_pll_setup,
461 	.compute = lsdc_pixel_pll_compute,
462 	.update = lsdc_pixpll_update,
463 	.get_rate = lsdc_pixpll_get_freq,
464 	.print = lsdc_pixpll_print,
465 };
466 
467 /* pixel pll initialization */
468 
lsdc_pixpll_init(struct lsdc_pixpll * const this,struct drm_device * ddev,unsigned int index)469 int lsdc_pixpll_init(struct lsdc_pixpll * const this,
470 		     struct drm_device *ddev,
471 		     unsigned int index)
472 {
473 	struct lsdc_device *ldev = to_lsdc(ddev);
474 	const struct lsdc_desc *descp = ldev->descp;
475 	const struct loongson_gfx_desc *gfx = to_loongson_gfx(descp);
476 
477 	this->ddev = ddev;
478 	this->reg_size = 8;
479 	this->reg_base = gfx->conf_reg_base + gfx->pixpll[index].reg_offset;
480 	this->funcs = &__pixpll_default_funcs;
481 
482 	return this->funcs->setup(this);
483 }
484