162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2023 Loongson Technology Corporation Limited 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/delay.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <drm/drm_managed.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "lsdc_drv.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* 1362306a36Sopenharmony_ci * The structure of the pixel PLL registers is evolved with times, 1462306a36Sopenharmony_ci * it can be different across different chip also. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* size is u64, note that all loongson's cpu is little endian. 1862306a36Sopenharmony_ci * This structure is same for ls7a2000, ls7a1000 and ls2k2000. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_cistruct lsdc_pixpll_reg { 2162306a36Sopenharmony_ci /* Byte 0 ~ Byte 3 */ 2262306a36Sopenharmony_ci unsigned div_out : 7; /* 6 : 0 Output clock divider */ 2362306a36Sopenharmony_ci unsigned _reserved_1_ : 14; /* 20 : 7 */ 2462306a36Sopenharmony_ci unsigned loopc : 9; /* 29 : 21 Clock multiplier */ 2562306a36Sopenharmony_ci unsigned _reserved_2_ : 2; /* 31 : 30 */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci /* Byte 4 ~ Byte 7 */ 2862306a36Sopenharmony_ci unsigned div_ref : 7; /* 38 : 32 Input clock divider */ 2962306a36Sopenharmony_ci unsigned locked : 1; /* 39 PLL locked indicator */ 3062306a36Sopenharmony_ci unsigned sel_out : 1; /* 40 output clk selector */ 3162306a36Sopenharmony_ci unsigned _reserved_3_ : 2; /* 42 : 41 */ 3262306a36Sopenharmony_ci unsigned set_param : 1; /* 43 Trigger the update */ 3362306a36Sopenharmony_ci unsigned bypass : 1; /* 44 */ 3462306a36Sopenharmony_ci unsigned powerdown : 1; /* 45 */ 3562306a36Sopenharmony_ci unsigned _reserved_4_ : 18; /* 46 : 63 no use */ 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciunion lsdc_pixpll_reg_bitmap { 3962306a36Sopenharmony_ci struct lsdc_pixpll_reg bitmap; 4062306a36Sopenharmony_ci u32 w[2]; 4162306a36Sopenharmony_ci u64 d; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct clk_to_pixpll_parms_lookup_t { 4562306a36Sopenharmony_ci unsigned int clock; /* kHz */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci unsigned short width; 4862306a36Sopenharmony_ci unsigned short height; 4962306a36Sopenharmony_ci unsigned short vrefresh; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* Stores parameters for programming the Hardware PLLs */ 5262306a36Sopenharmony_ci unsigned short div_out; 5362306a36Sopenharmony_ci unsigned short loopc; 5462306a36Sopenharmony_ci unsigned short div_ref; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic const struct clk_to_pixpll_parms_lookup_t pixpll_parms_table[] = { 5862306a36Sopenharmony_ci {148500, 1920, 1080, 60, 11, 49, 3}, /* 1920x1080@60Hz */ 5962306a36Sopenharmony_ci {141750, 1920, 1080, 60, 11, 78, 5}, /* 1920x1080@60Hz */ 6062306a36Sopenharmony_ci /* 1920x1080@50Hz */ 6162306a36Sopenharmony_ci {174500, 1920, 1080, 75, 17, 89, 3}, /* 1920x1080@75Hz */ 6262306a36Sopenharmony_ci {181250, 2560, 1080, 75, 8, 58, 4}, /* 2560x1080@75Hz */ 6362306a36Sopenharmony_ci {297000, 2560, 1080, 30, 8, 95, 4}, /* 3840x2160@30Hz */ 6462306a36Sopenharmony_ci {301992, 1920, 1080, 100, 10, 151, 5}, /* 1920x1080@100Hz */ 6562306a36Sopenharmony_ci {146250, 1680, 1050, 60, 16, 117, 5}, /* 1680x1050@60Hz */ 6662306a36Sopenharmony_ci {135000, 1280, 1024, 75, 10, 54, 4}, /* 1280x1024@75Hz */ 6762306a36Sopenharmony_ci {119000, 1680, 1050, 60, 20, 119, 5}, /* 1680x1050@60Hz */ 6862306a36Sopenharmony_ci {108000, 1600, 900, 60, 15, 81, 5}, /* 1600x900@60Hz */ 6962306a36Sopenharmony_ci /* 1280x1024@60Hz */ 7062306a36Sopenharmony_ci /* 1280x960@60Hz */ 7162306a36Sopenharmony_ci /* 1152x864@75Hz */ 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci {106500, 1440, 900, 60, 19, 81, 4}, /* 1440x900@60Hz */ 7462306a36Sopenharmony_ci {88750, 1440, 900, 60, 16, 71, 5}, /* 1440x900@60Hz */ 7562306a36Sopenharmony_ci {83500, 1280, 800, 60, 17, 71, 5}, /* 1280x800@60Hz */ 7662306a36Sopenharmony_ci {71000, 1280, 800, 60, 20, 71, 5}, /* 1280x800@60Hz */ 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci {74250, 1280, 720, 60, 22, 49, 3}, /* 1280x720@60Hz */ 7962306a36Sopenharmony_ci /* 1280x720@50Hz */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci {78750, 1024, 768, 75, 16, 63, 5}, /* 1024x768@75Hz */ 8262306a36Sopenharmony_ci {75000, 1024, 768, 70, 29, 87, 4}, /* 1024x768@70Hz */ 8362306a36Sopenharmony_ci {65000, 1024, 768, 60, 20, 39, 3}, /* 1024x768@60Hz */ 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci {51200, 1024, 600, 60, 25, 64, 5}, /* 1024x600@60Hz */ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci {57284, 832, 624, 75, 24, 55, 4}, /* 832x624@75Hz */ 8862306a36Sopenharmony_ci {49500, 800, 600, 75, 40, 99, 5}, /* 800x600@75Hz */ 8962306a36Sopenharmony_ci {50000, 800, 600, 72, 44, 88, 4}, /* 800x600@72Hz */ 9062306a36Sopenharmony_ci {40000, 800, 600, 60, 30, 36, 3}, /* 800x600@60Hz */ 9162306a36Sopenharmony_ci {36000, 800, 600, 56, 50, 72, 4}, /* 800x600@56Hz */ 9262306a36Sopenharmony_ci {31500, 640, 480, 75, 40, 63, 5}, /* 640x480@75Hz */ 9362306a36Sopenharmony_ci /* 640x480@73Hz */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci {30240, 640, 480, 67, 62, 75, 4}, /* 640x480@67Hz */ 9662306a36Sopenharmony_ci {27000, 720, 576, 50, 50, 54, 4}, /* 720x576@60Hz */ 9762306a36Sopenharmony_ci {25175, 640, 480, 60, 85, 107, 5}, /* 640x480@60Hz */ 9862306a36Sopenharmony_ci {25200, 640, 480, 60, 50, 63, 5}, /* 640x480@60Hz */ 9962306a36Sopenharmony_ci /* 720x480@60Hz */ 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void lsdc_pixel_pll_free(struct drm_device *ddev, void *data) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct lsdc_pixpll *this = (struct lsdc_pixpll *)data; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci iounmap(this->mmio); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci kfree(this->priv); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci drm_dbg(ddev, "pixpll private data freed\n"); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* 11462306a36Sopenharmony_ci * ioremap the device dependent PLL registers 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * @this: point to the object where this function is called from 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_cistatic int lsdc_pixel_pll_setup(struct lsdc_pixpll * const this) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct lsdc_pixpll_parms *pparms; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci this->mmio = ioremap(this->reg_base, this->reg_size); 12362306a36Sopenharmony_ci if (!this->mmio) 12462306a36Sopenharmony_ci return -ENOMEM; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci pparms = kzalloc(sizeof(*pparms), GFP_KERNEL); 12762306a36Sopenharmony_ci if (!pparms) { 12862306a36Sopenharmony_ci iounmap(this->mmio); 12962306a36Sopenharmony_ci return -ENOMEM; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci pparms->ref_clock = LSDC_PLL_REF_CLK_KHZ; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci this->priv = pparms; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return drmm_add_action_or_reset(this->ddev, lsdc_pixel_pll_free, this); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* 14062306a36Sopenharmony_ci * Find a set of pll parameters from a static local table which avoid 14162306a36Sopenharmony_ci * computing the pll parameter eachtime a modeset is triggered. 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * @this: point to the object where this function is called from 14462306a36Sopenharmony_ci * @clock: the desired output pixel clock, the unit is kHz 14562306a36Sopenharmony_ci * @pout: point to where the parameters to store if found 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci * Return 0 if success, return -1 if not found. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cistatic int lsdc_pixpll_find(struct lsdc_pixpll * const this, 15062306a36Sopenharmony_ci unsigned int clock, 15162306a36Sopenharmony_ci struct lsdc_pixpll_parms *pout) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci unsigned int num = ARRAY_SIZE(pixpll_parms_table); 15462306a36Sopenharmony_ci const struct clk_to_pixpll_parms_lookup_t *pt; 15562306a36Sopenharmony_ci unsigned int i; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci for (i = 0; i < num; ++i) { 15862306a36Sopenharmony_ci pt = &pixpll_parms_table[i]; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (clock == pt->clock) { 16162306a36Sopenharmony_ci pout->div_ref = pt->div_ref; 16262306a36Sopenharmony_ci pout->loopc = pt->loopc; 16362306a36Sopenharmony_ci pout->div_out = pt->div_out; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci drm_dbg_kms(this->ddev, "pixel clock %u: miss\n", clock); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return -1; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* 17562306a36Sopenharmony_ci * Find a set of pll parameters which have minimal difference with the 17662306a36Sopenharmony_ci * desired pixel clock frequency. It does that by computing all of the 17762306a36Sopenharmony_ci * possible combination. Compute the diff and find the combination with 17862306a36Sopenharmony_ci * minimal diff. 17962306a36Sopenharmony_ci * 18062306a36Sopenharmony_ci * clock_out = refclk / div_ref * loopc / div_out 18162306a36Sopenharmony_ci * 18262306a36Sopenharmony_ci * refclk is determined by the oscillator mounted on motherboard(100MHz 18362306a36Sopenharmony_ci * in almost all board) 18462306a36Sopenharmony_ci * 18562306a36Sopenharmony_ci * @this: point to the object from where this function is called 18662306a36Sopenharmony_ci * @clock: the desired output pixel clock, the unit is kHz 18762306a36Sopenharmony_ci * @pout: point to the out struct of lsdc_pixpll_parms 18862306a36Sopenharmony_ci * 18962306a36Sopenharmony_ci * Return 0 if a set of parameter is found, otherwise return the error 19062306a36Sopenharmony_ci * between clock_kHz we wanted and the most closest candidate with it. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_cistatic int lsdc_pixel_pll_compute(struct lsdc_pixpll * const this, 19362306a36Sopenharmony_ci unsigned int clock, 19462306a36Sopenharmony_ci struct lsdc_pixpll_parms *pout) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct lsdc_pixpll_parms *pparms = this->priv; 19762306a36Sopenharmony_ci unsigned int refclk = pparms->ref_clock; 19862306a36Sopenharmony_ci const unsigned int tolerance = 1000; 19962306a36Sopenharmony_ci unsigned int min = tolerance; 20062306a36Sopenharmony_ci unsigned int div_out, loopc, div_ref; 20162306a36Sopenharmony_ci unsigned int computed; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (!lsdc_pixpll_find(this, clock, pout)) 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci for (div_out = 6; div_out < 64; div_out++) { 20762306a36Sopenharmony_ci for (div_ref = 3; div_ref < 6; div_ref++) { 20862306a36Sopenharmony_ci for (loopc = 6; loopc < 161; loopc++) { 20962306a36Sopenharmony_ci unsigned int diff = 0; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (loopc < 12 * div_ref) 21262306a36Sopenharmony_ci continue; 21362306a36Sopenharmony_ci if (loopc > 32 * div_ref) 21462306a36Sopenharmony_ci continue; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci computed = refclk / div_ref * loopc / div_out; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (clock >= computed) 21962306a36Sopenharmony_ci diff = clock - computed; 22062306a36Sopenharmony_ci else 22162306a36Sopenharmony_ci diff = computed - clock; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (diff < min) { 22462306a36Sopenharmony_ci min = diff; 22562306a36Sopenharmony_ci pparms->div_ref = div_ref; 22662306a36Sopenharmony_ci pparms->div_out = div_out; 22762306a36Sopenharmony_ci pparms->loopc = loopc; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (diff == 0) { 23062306a36Sopenharmony_ci *pout = *pparms; 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* still acceptable */ 23962306a36Sopenharmony_ci if (min < tolerance) { 24062306a36Sopenharmony_ci *pout = *pparms; 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci drm_dbg(this->ddev, "can't find suitable params for %u khz\n", clock); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return min; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/* Pixel pll hardware related ops, per display pipe */ 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void __pixpll_rreg(struct lsdc_pixpll *this, 25262306a36Sopenharmony_ci union lsdc_pixpll_reg_bitmap *dst) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci#if defined(CONFIG_64BIT) 25562306a36Sopenharmony_ci dst->d = readq(this->mmio); 25662306a36Sopenharmony_ci#else 25762306a36Sopenharmony_ci dst->w[0] = readl(this->mmio); 25862306a36Sopenharmony_ci dst->w[1] = readl(this->mmio + 4); 25962306a36Sopenharmony_ci#endif 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic void __pixpll_wreg(struct lsdc_pixpll *this, 26362306a36Sopenharmony_ci union lsdc_pixpll_reg_bitmap *src) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci#if defined(CONFIG_64BIT) 26662306a36Sopenharmony_ci writeq(src->d, this->mmio); 26762306a36Sopenharmony_ci#else 26862306a36Sopenharmony_ci writel(src->w[0], this->mmio); 26962306a36Sopenharmony_ci writel(src->w[1], this->mmio + 4); 27062306a36Sopenharmony_ci#endif 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic void __pixpll_ops_powerup(struct lsdc_pixpll * const this) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci union lsdc_pixpll_reg_bitmap pixpll_reg; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci __pixpll_rreg(this, &pixpll_reg); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci pixpll_reg.bitmap.powerdown = 0; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci __pixpll_wreg(this, &pixpll_reg); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void __pixpll_ops_powerdown(struct lsdc_pixpll * const this) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci union lsdc_pixpll_reg_bitmap pixpll_reg; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci __pixpll_rreg(this, &pixpll_reg); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci pixpll_reg.bitmap.powerdown = 1; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci __pixpll_wreg(this, &pixpll_reg); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void __pixpll_ops_on(struct lsdc_pixpll * const this) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci union lsdc_pixpll_reg_bitmap pixpll_reg; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci __pixpll_rreg(this, &pixpll_reg); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci pixpll_reg.bitmap.sel_out = 1; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci __pixpll_wreg(this, &pixpll_reg); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic void __pixpll_ops_off(struct lsdc_pixpll * const this) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci union lsdc_pixpll_reg_bitmap pixpll_reg; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci __pixpll_rreg(this, &pixpll_reg); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci pixpll_reg.bitmap.sel_out = 0; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci __pixpll_wreg(this, &pixpll_reg); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic void __pixpll_ops_bypass(struct lsdc_pixpll * const this) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci union lsdc_pixpll_reg_bitmap pixpll_reg; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci __pixpll_rreg(this, &pixpll_reg); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci pixpll_reg.bitmap.bypass = 1; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci __pixpll_wreg(this, &pixpll_reg); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic void __pixpll_ops_unbypass(struct lsdc_pixpll * const this) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci union lsdc_pixpll_reg_bitmap pixpll_reg; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci __pixpll_rreg(this, &pixpll_reg); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci pixpll_reg.bitmap.bypass = 0; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci __pixpll_wreg(this, &pixpll_reg); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic void __pixpll_ops_untoggle_param(struct lsdc_pixpll * const this) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci union lsdc_pixpll_reg_bitmap pixpll_reg; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci __pixpll_rreg(this, &pixpll_reg); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci pixpll_reg.bitmap.set_param = 0; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci __pixpll_wreg(this, &pixpll_reg); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void __pixpll_ops_set_param(struct lsdc_pixpll * const this, 35162306a36Sopenharmony_ci struct lsdc_pixpll_parms const *p) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci union lsdc_pixpll_reg_bitmap pixpll_reg; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci __pixpll_rreg(this, &pixpll_reg); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci pixpll_reg.bitmap.div_ref = p->div_ref; 35862306a36Sopenharmony_ci pixpll_reg.bitmap.loopc = p->loopc; 35962306a36Sopenharmony_ci pixpll_reg.bitmap.div_out = p->div_out; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci __pixpll_wreg(this, &pixpll_reg); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic void __pixpll_ops_toggle_param(struct lsdc_pixpll * const this) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci union lsdc_pixpll_reg_bitmap pixpll_reg; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci __pixpll_rreg(this, &pixpll_reg); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci pixpll_reg.bitmap.set_param = 1; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci __pixpll_wreg(this, &pixpll_reg); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic void __pixpll_ops_wait_locked(struct lsdc_pixpll * const this) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci union lsdc_pixpll_reg_bitmap pixpll_reg; 37862306a36Sopenharmony_ci unsigned int counter = 0; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci do { 38162306a36Sopenharmony_ci __pixpll_rreg(this, &pixpll_reg); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (pixpll_reg.bitmap.locked) 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci ++counter; 38762306a36Sopenharmony_ci } while (counter < 2000); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci drm_dbg(this->ddev, "%u loop waited\n", counter); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* 39362306a36Sopenharmony_ci * Update the PLL parameters to the PLL hardware 39462306a36Sopenharmony_ci * 39562306a36Sopenharmony_ci * @this: point to the object from which this function is called 39662306a36Sopenharmony_ci * @pin: point to the struct of lsdc_pixpll_parms passed in 39762306a36Sopenharmony_ci * 39862306a36Sopenharmony_ci * return 0 if successful. 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_cistatic int lsdc_pixpll_update(struct lsdc_pixpll * const this, 40162306a36Sopenharmony_ci struct lsdc_pixpll_parms const *pin) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci __pixpll_ops_bypass(this); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci __pixpll_ops_off(this); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci __pixpll_ops_powerdown(this); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci __pixpll_ops_toggle_param(this); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci __pixpll_ops_set_param(this, pin); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci __pixpll_ops_untoggle_param(this); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci __pixpll_ops_powerup(this); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci udelay(2); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci __pixpll_ops_wait_locked(this); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci __pixpll_ops_on(this); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci __pixpll_ops_unbypass(this); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic unsigned int lsdc_pixpll_get_freq(struct lsdc_pixpll * const this) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct lsdc_pixpll_parms *ppar = this->priv; 43162306a36Sopenharmony_ci union lsdc_pixpll_reg_bitmap pix_pll_reg; 43262306a36Sopenharmony_ci unsigned int freq; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci __pixpll_rreg(this, &pix_pll_reg); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci ppar->div_ref = pix_pll_reg.bitmap.div_ref; 43762306a36Sopenharmony_ci ppar->loopc = pix_pll_reg.bitmap.loopc; 43862306a36Sopenharmony_ci ppar->div_out = pix_pll_reg.bitmap.div_out; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci freq = ppar->ref_clock / ppar->div_ref * ppar->loopc / ppar->div_out; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return freq; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic void lsdc_pixpll_print(struct lsdc_pixpll * const this, 44662306a36Sopenharmony_ci struct drm_printer *p) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct lsdc_pixpll_parms *parms = this->priv; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci drm_printf(p, "div_ref: %u, loopc: %u, div_out: %u\n", 45162306a36Sopenharmony_ci parms->div_ref, parms->loopc, parms->div_out); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/* 45562306a36Sopenharmony_ci * LS7A1000, LS7A2000 and ls2k2000's pixel pll setting register is same, 45662306a36Sopenharmony_ci * we take this as default, create a new instance if a different model is 45762306a36Sopenharmony_ci * introduced. 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_cistatic const struct lsdc_pixpll_funcs __pixpll_default_funcs = { 46062306a36Sopenharmony_ci .setup = lsdc_pixel_pll_setup, 46162306a36Sopenharmony_ci .compute = lsdc_pixel_pll_compute, 46262306a36Sopenharmony_ci .update = lsdc_pixpll_update, 46362306a36Sopenharmony_ci .get_rate = lsdc_pixpll_get_freq, 46462306a36Sopenharmony_ci .print = lsdc_pixpll_print, 46562306a36Sopenharmony_ci}; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci/* pixel pll initialization */ 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ciint lsdc_pixpll_init(struct lsdc_pixpll * const this, 47062306a36Sopenharmony_ci struct drm_device *ddev, 47162306a36Sopenharmony_ci unsigned int index) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct lsdc_device *ldev = to_lsdc(ddev); 47462306a36Sopenharmony_ci const struct lsdc_desc *descp = ldev->descp; 47562306a36Sopenharmony_ci const struct loongson_gfx_desc *gfx = to_loongson_gfx(descp); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci this->ddev = ddev; 47862306a36Sopenharmony_ci this->reg_size = 8; 47962306a36Sopenharmony_ci this->reg_base = gfx->conf_reg_base + gfx->pixpll[index].reg_offset; 48062306a36Sopenharmony_ci this->funcs = &__pixpll_default_funcs; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return this->funcs->setup(this); 48362306a36Sopenharmony_ci} 484