484 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (C) 2023 Loongson Technology Corporation Limited4 */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 */20struct 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 38union lsdc_pixpll_reg_bitmap {39 struct lsdc_pixpll_reg bitmap;40 u32 w[2];41 u64 d;42};43 44struct 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 57static 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 102static 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 registers115 *116 * @this: point to the object where this function is called from117 */118static 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 avoid141 * computing the pll parameter eachtime a modeset is triggered.142 *143 * @this: point to the object where this function is called from144 * @clock: the desired output pixel clock, the unit is kHz145 * @pout: point to where the parameters to store if found146 *147 * Return 0 if success, return -1 if not found.148 */149static 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 the176 * desired pixel clock frequency. It does that by computing all of the177 * possible combination. Compute the diff and find the combination with178 * minimal diff.179 *180 * clock_out = refclk / div_ref * loopc / div_out181 *182 * refclk is determined by the oscillator mounted on motherboard(100MHz183 * in almost all board)184 *185 * @this: point to the object from where this function is called186 * @clock: the desired output pixel clock, the unit is kHz187 * @pout: point to the out struct of lsdc_pixpll_parms188 *189 * Return 0 if a set of parameter is found, otherwise return the error190 * between clock_kHz we wanted and the most closest candidate with it.191 */192static 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 else221 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 251static 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#else257 dst->w[0] = readl(this->mmio);258 dst->w[1] = readl(this->mmio + 4);259#endif260}261 262static 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#else268 writel(src->w[0], this->mmio);269 writel(src->w[1], this->mmio + 4);270#endif271}272 273static 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 284static 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 295static 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 306static 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 317static 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 328static 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 339static 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 350static 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 364static 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 375static 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 hardware394 *395 * @this: point to the object from which this function is called396 * @pin: point to the struct of lsdc_pixpll_parms passed in397 *398 * return 0 if successful.399 */400static 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 428static 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 445static 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 is457 * introduced.458 */459static 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 469int 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