File Index
Files and directories
Sign in
FILE

lsdc_pixpll.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/loongson/lsdc_pixpll.c C
  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 */
 21struct 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
 39union lsdc_pixpll_reg_bitmap {
 40	struct lsdc_pixpll_reg bitmap;
 41	u32 w[2];
 42	u64 d;
 43};
 44
 45struct 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
 58static 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
103static 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 */
119static 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 */
150static 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 */
193static 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
252static 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
263static 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
274static 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
285static 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
296static 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
307static 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
318static 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
329static 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
340static 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
351static 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
365static 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
376static 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 */
401static 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
429static 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
446static 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 */
460static 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
470int 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}
~/ Read-only file preview.