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(addr: this->mmio); |
107 | |
108 | kfree(objp: 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(offset: this->reg_base, size: this->reg_size); |
123 | if (!this->mmio) |
124 | return -ENOMEM; |
125 | |
126 | pparms = kzalloc(size: sizeof(*pparms), GFP_KERNEL); |
127 | if (!pparms) { |
128 | iounmap(addr: 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 | */ |
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 | */ |
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 | |
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(addr: this->mmio); |
256 | #else |
257 | dst->w[0] = readl(this->mmio); |
258 | dst->w[1] = readl(this->mmio + 4); |
259 | #endif |
260 | } |
261 | |
262 | static void __pixpll_wreg(struct lsdc_pixpll *this, |
263 | union lsdc_pixpll_reg_bitmap *src) |
264 | { |
265 | #if defined(CONFIG_64BIT) |
266 | writeq(val: src->d, addr: this->mmio); |
267 | #else |
268 | writel(src->w[0], this->mmio); |
269 | writel(src->w[1], this->mmio + 4); |
270 | #endif |
271 | } |
272 | |
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, dst: &pixpll_reg); |
278 | |
279 | pixpll_reg.bitmap.powerdown = 0; |
280 | |
281 | __pixpll_wreg(this, src: &pixpll_reg); |
282 | } |
283 | |
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, dst: &pixpll_reg); |
289 | |
290 | pixpll_reg.bitmap.powerdown = 1; |
291 | |
292 | __pixpll_wreg(this, src: &pixpll_reg); |
293 | } |
294 | |
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, dst: &pixpll_reg); |
300 | |
301 | pixpll_reg.bitmap.sel_out = 1; |
302 | |
303 | __pixpll_wreg(this, src: &pixpll_reg); |
304 | } |
305 | |
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, dst: &pixpll_reg); |
311 | |
312 | pixpll_reg.bitmap.sel_out = 0; |
313 | |
314 | __pixpll_wreg(this, src: &pixpll_reg); |
315 | } |
316 | |
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, dst: &pixpll_reg); |
322 | |
323 | pixpll_reg.bitmap.bypass = 1; |
324 | |
325 | __pixpll_wreg(this, src: &pixpll_reg); |
326 | } |
327 | |
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, dst: &pixpll_reg); |
333 | |
334 | pixpll_reg.bitmap.bypass = 0; |
335 | |
336 | __pixpll_wreg(this, src: &pixpll_reg); |
337 | } |
338 | |
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, dst: &pixpll_reg); |
344 | |
345 | pixpll_reg.bitmap.set_param = 0; |
346 | |
347 | __pixpll_wreg(this, src: &pixpll_reg); |
348 | } |
349 | |
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, dst: &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, src: &pixpll_reg); |
362 | } |
363 | |
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, dst: &pixpll_reg); |
369 | |
370 | pixpll_reg.bitmap.set_param = 1; |
371 | |
372 | __pixpll_wreg(this, src: &pixpll_reg); |
373 | } |
374 | |
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, dst: &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 | */ |
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, p: 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 | |
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, dst: &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 | |
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, f: "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 | |
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(dcp: 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 | |