xref: /openbmc/linux/drivers/gpu/drm/loongson/lsdc_pixpll.c (revision b97d6790d03b763eca08847a9a5869a4291b9f9a)
1f39db26cSSui Jingfeng // SPDX-License-Identifier: GPL-2.0+
2f39db26cSSui Jingfeng /*
3f39db26cSSui Jingfeng  * Copyright (C) 2023 Loongson Technology Corporation Limited
4f39db26cSSui Jingfeng  */
5f39db26cSSui Jingfeng 
6f39db26cSSui Jingfeng #include <linux/delay.h>
7f39db26cSSui Jingfeng 
8f39db26cSSui Jingfeng #include <drm/drm_managed.h>
9f39db26cSSui Jingfeng 
10f39db26cSSui Jingfeng #include "lsdc_drv.h"
11f39db26cSSui Jingfeng 
12f39db26cSSui Jingfeng /*
13f39db26cSSui Jingfeng  * The structure of the pixel PLL registers is evolved with times,
14f39db26cSSui Jingfeng  * it can be different across different chip also.
15f39db26cSSui Jingfeng  */
16f39db26cSSui Jingfeng 
17f39db26cSSui Jingfeng /* size is u64, note that all loongson's cpu is little endian.
18f39db26cSSui Jingfeng  * This structure is same for ls7a2000, ls7a1000 and ls2k2000.
19f39db26cSSui Jingfeng  */
20f39db26cSSui Jingfeng struct lsdc_pixpll_reg {
21f39db26cSSui Jingfeng 	/* Byte 0 ~ Byte 3 */
22f39db26cSSui Jingfeng 	unsigned div_out       : 7;   /*  6 : 0     Output clock divider  */
23f39db26cSSui Jingfeng 	unsigned _reserved_1_  : 14;  /* 20 : 7                           */
24f39db26cSSui Jingfeng 	unsigned loopc         : 9;   /* 29 : 21    Clock multiplier      */
25f39db26cSSui Jingfeng 	unsigned _reserved_2_  : 2;   /* 31 : 30                          */
26f39db26cSSui Jingfeng 
27f39db26cSSui Jingfeng 	/* Byte 4 ~ Byte 7 */
28f39db26cSSui Jingfeng 	unsigned div_ref       : 7;   /* 38 : 32    Input clock divider   */
29f39db26cSSui Jingfeng 	unsigned locked        : 1;   /* 39         PLL locked indicator  */
30f39db26cSSui Jingfeng 	unsigned sel_out       : 1;   /* 40         output clk selector   */
31f39db26cSSui Jingfeng 	unsigned _reserved_3_  : 2;   /* 42 : 41                          */
32f39db26cSSui Jingfeng 	unsigned set_param     : 1;   /* 43         Trigger the update    */
33f39db26cSSui Jingfeng 	unsigned bypass        : 1;   /* 44                               */
34f39db26cSSui Jingfeng 	unsigned powerdown     : 1;   /* 45                               */
35f39db26cSSui Jingfeng 	unsigned _reserved_4_  : 18;  /* 46 : 63    no use                */
36f39db26cSSui Jingfeng };
37f39db26cSSui Jingfeng 
38f39db26cSSui Jingfeng union lsdc_pixpll_reg_bitmap {
39f39db26cSSui Jingfeng 	struct lsdc_pixpll_reg bitmap;
40f39db26cSSui Jingfeng 	u32 w[2];
41f39db26cSSui Jingfeng 	u64 d;
42f39db26cSSui Jingfeng };
43f39db26cSSui Jingfeng 
44f39db26cSSui Jingfeng struct clk_to_pixpll_parms_lookup_t {
45f39db26cSSui Jingfeng 	unsigned int clock;        /* kHz */
46f39db26cSSui Jingfeng 
47f39db26cSSui Jingfeng 	unsigned short width;
48f39db26cSSui Jingfeng 	unsigned short height;
49f39db26cSSui Jingfeng 	unsigned short vrefresh;
50f39db26cSSui Jingfeng 
51f39db26cSSui Jingfeng 	/* Stores parameters for programming the Hardware PLLs */
52f39db26cSSui Jingfeng 	unsigned short div_out;
53f39db26cSSui Jingfeng 	unsigned short loopc;
54f39db26cSSui Jingfeng 	unsigned short div_ref;
55f39db26cSSui Jingfeng };
56f39db26cSSui Jingfeng 
57f39db26cSSui Jingfeng static const struct clk_to_pixpll_parms_lookup_t pixpll_parms_table[] = {
58f39db26cSSui Jingfeng 	{148500, 1920, 1080, 60,  11, 49,  3},   /* 1920x1080@60Hz */
59f39db26cSSui Jingfeng 	{141750, 1920, 1080, 60,  11, 78,  5},   /* 1920x1080@60Hz */
60f39db26cSSui Jingfeng 						 /* 1920x1080@50Hz */
61f39db26cSSui Jingfeng 	{174500, 1920, 1080, 75,  17, 89,  3},   /* 1920x1080@75Hz */
62f39db26cSSui Jingfeng 	{181250, 2560, 1080, 75,  8,  58,  4},   /* 2560x1080@75Hz */
63f39db26cSSui Jingfeng 	{297000, 2560, 1080, 30,  8,  95,  4},   /* 3840x2160@30Hz */
64f39db26cSSui Jingfeng 	{301992, 1920, 1080, 100, 10, 151, 5},   /* 1920x1080@100Hz */
65f39db26cSSui Jingfeng 	{146250, 1680, 1050, 60,  16, 117, 5},   /* 1680x1050@60Hz */
66f39db26cSSui Jingfeng 	{135000, 1280, 1024, 75,  10, 54,  4},   /* 1280x1024@75Hz */
67f39db26cSSui Jingfeng 	{119000, 1680, 1050, 60,  20, 119, 5},   /* 1680x1050@60Hz */
68f39db26cSSui Jingfeng 	{108000, 1600, 900,  60,  15, 81,  5},   /* 1600x900@60Hz  */
69f39db26cSSui Jingfeng 						 /* 1280x1024@60Hz */
70f39db26cSSui Jingfeng 						 /* 1280x960@60Hz */
71f39db26cSSui Jingfeng 						 /* 1152x864@75Hz */
72f39db26cSSui Jingfeng 
73f39db26cSSui Jingfeng 	{106500, 1440, 900,  60,  19, 81,  4},   /* 1440x900@60Hz */
74f39db26cSSui Jingfeng 	{88750,  1440, 900,  60,  16, 71,  5},   /* 1440x900@60Hz */
75f39db26cSSui Jingfeng 	{83500,  1280, 800,  60,  17, 71,  5},   /* 1280x800@60Hz */
76f39db26cSSui Jingfeng 	{71000,  1280, 800,  60,  20, 71,  5},   /* 1280x800@60Hz */
77f39db26cSSui Jingfeng 
78f39db26cSSui Jingfeng 	{74250,  1280, 720,  60,  22, 49,  3},   /* 1280x720@60Hz */
79f39db26cSSui Jingfeng 						 /* 1280x720@50Hz */
80f39db26cSSui Jingfeng 
81f39db26cSSui Jingfeng 	{78750,  1024, 768,  75,  16, 63,  5},   /* 1024x768@75Hz */
82f39db26cSSui Jingfeng 	{75000,  1024, 768,  70,  29, 87,  4},   /* 1024x768@70Hz */
83f39db26cSSui Jingfeng 	{65000,  1024, 768,  60,  20, 39,  3},   /* 1024x768@60Hz */
84f39db26cSSui Jingfeng 
85f39db26cSSui Jingfeng 	{51200,  1024, 600,  60,  25, 64,  5},   /* 1024x600@60Hz */
86f39db26cSSui Jingfeng 
87f39db26cSSui Jingfeng 	{57284,  832,  624,  75,  24, 55,  4},   /* 832x624@75Hz */
88f39db26cSSui Jingfeng 	{49500,  800,  600,  75,  40, 99,  5},   /* 800x600@75Hz */
89f39db26cSSui Jingfeng 	{50000,  800,  600,  72,  44, 88,  4},   /* 800x600@72Hz */
90f39db26cSSui Jingfeng 	{40000,  800,  600,  60,  30, 36,  3},   /* 800x600@60Hz */
91f39db26cSSui Jingfeng 	{36000,  800,  600,  56,  50, 72,  4},   /* 800x600@56Hz */
92f39db26cSSui Jingfeng 	{31500,  640,  480,  75,  40, 63,  5},   /* 640x480@75Hz */
93f39db26cSSui Jingfeng 						 /* 640x480@73Hz */
94f39db26cSSui Jingfeng 
95f39db26cSSui Jingfeng 	{30240,  640,  480,  67,  62, 75,  4},   /* 640x480@67Hz */
96f39db26cSSui Jingfeng 	{27000,  720,  576,  50,  50, 54,  4},   /* 720x576@60Hz */
97f39db26cSSui Jingfeng 	{25175,  640,  480,  60,  85, 107, 5},   /* 640x480@60Hz */
98f39db26cSSui Jingfeng 	{25200,  640,  480,  60,  50, 63,  5},   /* 640x480@60Hz */
99f39db26cSSui Jingfeng 						 /* 720x480@60Hz */
100f39db26cSSui Jingfeng };
101f39db26cSSui Jingfeng 
lsdc_pixel_pll_free(struct drm_device * ddev,void * data)102f39db26cSSui Jingfeng static void lsdc_pixel_pll_free(struct drm_device *ddev, void *data)
103f39db26cSSui Jingfeng {
104f39db26cSSui Jingfeng 	struct lsdc_pixpll *this = (struct lsdc_pixpll *)data;
105f39db26cSSui Jingfeng 
106f39db26cSSui Jingfeng 	iounmap(this->mmio);
107f39db26cSSui Jingfeng 
108f39db26cSSui Jingfeng 	kfree(this->priv);
109f39db26cSSui Jingfeng 
110f39db26cSSui Jingfeng 	drm_dbg(ddev, "pixpll private data freed\n");
111f39db26cSSui Jingfeng }
112f39db26cSSui Jingfeng 
113f39db26cSSui Jingfeng /*
114f39db26cSSui Jingfeng  * ioremap the device dependent PLL registers
115f39db26cSSui Jingfeng  *
116f39db26cSSui Jingfeng  * @this: point to the object where this function is called from
117f39db26cSSui Jingfeng  */
lsdc_pixel_pll_setup(struct lsdc_pixpll * const this)118f39db26cSSui Jingfeng static int lsdc_pixel_pll_setup(struct lsdc_pixpll * const this)
119f39db26cSSui Jingfeng {
120f39db26cSSui Jingfeng 	struct lsdc_pixpll_parms *pparms;
121f39db26cSSui Jingfeng 
122f39db26cSSui Jingfeng 	this->mmio = ioremap(this->reg_base, this->reg_size);
123*40a4db81SHarshit Mogalapalli 	if (!this->mmio)
124f39db26cSSui Jingfeng 		return -ENOMEM;
125f39db26cSSui Jingfeng 
126f39db26cSSui Jingfeng 	pparms = kzalloc(sizeof(*pparms), GFP_KERNEL);
127*40a4db81SHarshit Mogalapalli 	if (!pparms) {
128*40a4db81SHarshit Mogalapalli 		iounmap(this->mmio);
129f39db26cSSui Jingfeng 		return -ENOMEM;
130*40a4db81SHarshit Mogalapalli 	}
131f39db26cSSui Jingfeng 
132f39db26cSSui Jingfeng 	pparms->ref_clock = LSDC_PLL_REF_CLK_KHZ;
133f39db26cSSui Jingfeng 
134f39db26cSSui Jingfeng 	this->priv = pparms;
135f39db26cSSui Jingfeng 
136f39db26cSSui Jingfeng 	return drmm_add_action_or_reset(this->ddev, lsdc_pixel_pll_free, this);
137f39db26cSSui Jingfeng }
138f39db26cSSui Jingfeng 
139f39db26cSSui Jingfeng /*
140f39db26cSSui Jingfeng  * Find a set of pll parameters from a static local table which avoid
141f39db26cSSui Jingfeng  * computing the pll parameter eachtime a modeset is triggered.
142f39db26cSSui Jingfeng  *
143f39db26cSSui Jingfeng  * @this: point to the object where this function is called from
144f39db26cSSui Jingfeng  * @clock: the desired output pixel clock, the unit is kHz
145f39db26cSSui Jingfeng  * @pout: point to where the parameters to store if found
146f39db26cSSui Jingfeng  *
147f39db26cSSui Jingfeng  * Return 0 if success, return -1 if not found.
148f39db26cSSui Jingfeng  */
lsdc_pixpll_find(struct lsdc_pixpll * const this,unsigned int clock,struct lsdc_pixpll_parms * pout)149f39db26cSSui Jingfeng static int lsdc_pixpll_find(struct lsdc_pixpll * const this,
150f39db26cSSui Jingfeng 			    unsigned int clock,
151f39db26cSSui Jingfeng 			    struct lsdc_pixpll_parms *pout)
152f39db26cSSui Jingfeng {
153f39db26cSSui Jingfeng 	unsigned int num = ARRAY_SIZE(pixpll_parms_table);
154f39db26cSSui Jingfeng 	const struct clk_to_pixpll_parms_lookup_t *pt;
155f39db26cSSui Jingfeng 	unsigned int i;
156f39db26cSSui Jingfeng 
157f39db26cSSui Jingfeng 	for (i = 0; i < num; ++i) {
158f39db26cSSui Jingfeng 		pt = &pixpll_parms_table[i];
159f39db26cSSui Jingfeng 
160f39db26cSSui Jingfeng 		if (clock == pt->clock) {
161f39db26cSSui Jingfeng 			pout->div_ref = pt->div_ref;
162f39db26cSSui Jingfeng 			pout->loopc   = pt->loopc;
163f39db26cSSui Jingfeng 			pout->div_out = pt->div_out;
164f39db26cSSui Jingfeng 
165f39db26cSSui Jingfeng 			return 0;
166f39db26cSSui Jingfeng 		}
167f39db26cSSui Jingfeng 	}
168f39db26cSSui Jingfeng 
169f39db26cSSui Jingfeng 	drm_dbg_kms(this->ddev, "pixel clock %u: miss\n", clock);
170f39db26cSSui Jingfeng 
171f39db26cSSui Jingfeng 	return -1;
172f39db26cSSui Jingfeng }
173f39db26cSSui Jingfeng 
174f39db26cSSui Jingfeng /*
175f39db26cSSui Jingfeng  * Find a set of pll parameters which have minimal difference with the
176f39db26cSSui Jingfeng  * desired pixel clock frequency. It does that by computing all of the
177f39db26cSSui Jingfeng  * possible combination. Compute the diff and find the combination with
178f39db26cSSui Jingfeng  * minimal diff.
179f39db26cSSui Jingfeng  *
180f39db26cSSui Jingfeng  * clock_out = refclk / div_ref * loopc / div_out
181f39db26cSSui Jingfeng  *
182f39db26cSSui Jingfeng  * refclk is determined by the oscillator mounted on motherboard(100MHz
183f39db26cSSui Jingfeng  * in almost all board)
184f39db26cSSui Jingfeng  *
185f39db26cSSui Jingfeng  * @this: point to the object from where this function is called
186f39db26cSSui Jingfeng  * @clock: the desired output pixel clock, the unit is kHz
187f39db26cSSui Jingfeng  * @pout: point to the out struct of lsdc_pixpll_parms
188f39db26cSSui Jingfeng  *
189f39db26cSSui Jingfeng  * Return 0 if a set of parameter is found, otherwise return the error
190f39db26cSSui Jingfeng  * between clock_kHz we wanted and the most closest candidate with it.
191f39db26cSSui Jingfeng  */
lsdc_pixel_pll_compute(struct lsdc_pixpll * const this,unsigned int clock,struct lsdc_pixpll_parms * pout)192f39db26cSSui Jingfeng static int lsdc_pixel_pll_compute(struct lsdc_pixpll * const this,
193f39db26cSSui Jingfeng 				  unsigned int clock,
194f39db26cSSui Jingfeng 				  struct lsdc_pixpll_parms *pout)
195f39db26cSSui Jingfeng {
196f39db26cSSui Jingfeng 	struct lsdc_pixpll_parms *pparms = this->priv;
197f39db26cSSui Jingfeng 	unsigned int refclk = pparms->ref_clock;
198f39db26cSSui Jingfeng 	const unsigned int tolerance = 1000;
199f39db26cSSui Jingfeng 	unsigned int min = tolerance;
200f39db26cSSui Jingfeng 	unsigned int div_out, loopc, div_ref;
201f39db26cSSui Jingfeng 	unsigned int computed;
202f39db26cSSui Jingfeng 
203f39db26cSSui Jingfeng 	if (!lsdc_pixpll_find(this, clock, pout))
204f39db26cSSui Jingfeng 		return 0;
205f39db26cSSui Jingfeng 
206f39db26cSSui Jingfeng 	for (div_out = 6; div_out < 64; div_out++) {
207f39db26cSSui Jingfeng 		for (div_ref = 3; div_ref < 6; div_ref++) {
208f39db26cSSui Jingfeng 			for (loopc = 6; loopc < 161; loopc++) {
209f39db26cSSui Jingfeng 				unsigned int diff = 0;
210f39db26cSSui Jingfeng 
211f39db26cSSui Jingfeng 				if (loopc < 12 * div_ref)
212f39db26cSSui Jingfeng 					continue;
213f39db26cSSui Jingfeng 				if (loopc > 32 * div_ref)
214f39db26cSSui Jingfeng 					continue;
215f39db26cSSui Jingfeng 
216f39db26cSSui Jingfeng 				computed = refclk / div_ref * loopc / div_out;
217f39db26cSSui Jingfeng 
218f39db26cSSui Jingfeng 				if (clock >= computed)
219f39db26cSSui Jingfeng 					diff = clock - computed;
220f39db26cSSui Jingfeng 				else
221f39db26cSSui Jingfeng 					diff = computed - clock;
222f39db26cSSui Jingfeng 
223f39db26cSSui Jingfeng 				if (diff < min) {
224f39db26cSSui Jingfeng 					min = diff;
225f39db26cSSui Jingfeng 					pparms->div_ref = div_ref;
226f39db26cSSui Jingfeng 					pparms->div_out = div_out;
227f39db26cSSui Jingfeng 					pparms->loopc = loopc;
228f39db26cSSui Jingfeng 
229f39db26cSSui Jingfeng 					if (diff == 0) {
230f39db26cSSui Jingfeng 						*pout = *pparms;
231f39db26cSSui Jingfeng 						return 0;
232f39db26cSSui Jingfeng 					}
233f39db26cSSui Jingfeng 				}
234f39db26cSSui Jingfeng 			}
235f39db26cSSui Jingfeng 		}
236f39db26cSSui Jingfeng 	}
237f39db26cSSui Jingfeng 
238f39db26cSSui Jingfeng 	/* still acceptable */
239f39db26cSSui Jingfeng 	if (min < tolerance) {
240f39db26cSSui Jingfeng 		*pout = *pparms;
241f39db26cSSui Jingfeng 		return 0;
242f39db26cSSui Jingfeng 	}
243f39db26cSSui Jingfeng 
244f39db26cSSui Jingfeng 	drm_dbg(this->ddev, "can't find suitable params for %u khz\n", clock);
245f39db26cSSui Jingfeng 
246f39db26cSSui Jingfeng 	return min;
247f39db26cSSui Jingfeng }
248f39db26cSSui Jingfeng 
249f39db26cSSui Jingfeng /* Pixel pll hardware related ops, per display pipe */
250f39db26cSSui Jingfeng 
__pixpll_rreg(struct lsdc_pixpll * this,union lsdc_pixpll_reg_bitmap * dst)251f39db26cSSui Jingfeng static void __pixpll_rreg(struct lsdc_pixpll *this,
252f39db26cSSui Jingfeng 			  union lsdc_pixpll_reg_bitmap *dst)
253f39db26cSSui Jingfeng {
254f39db26cSSui Jingfeng #if defined(CONFIG_64BIT)
255f39db26cSSui Jingfeng 	dst->d = readq(this->mmio);
256f39db26cSSui Jingfeng #else
257f39db26cSSui Jingfeng 	dst->w[0] = readl(this->mmio);
258f39db26cSSui Jingfeng 	dst->w[1] = readl(this->mmio + 4);
259f39db26cSSui Jingfeng #endif
260f39db26cSSui Jingfeng }
261f39db26cSSui Jingfeng 
__pixpll_wreg(struct lsdc_pixpll * this,union lsdc_pixpll_reg_bitmap * src)262f39db26cSSui Jingfeng static void __pixpll_wreg(struct lsdc_pixpll *this,
263f39db26cSSui Jingfeng 			  union lsdc_pixpll_reg_bitmap *src)
264f39db26cSSui Jingfeng {
265f39db26cSSui Jingfeng #if defined(CONFIG_64BIT)
266f39db26cSSui Jingfeng 	writeq(src->d, this->mmio);
267f39db26cSSui Jingfeng #else
268f39db26cSSui Jingfeng 	writel(src->w[0], this->mmio);
269f39db26cSSui Jingfeng 	writel(src->w[1], this->mmio + 4);
270f39db26cSSui Jingfeng #endif
271f39db26cSSui Jingfeng }
272f39db26cSSui Jingfeng 
__pixpll_ops_powerup(struct lsdc_pixpll * const this)273f39db26cSSui Jingfeng static void __pixpll_ops_powerup(struct lsdc_pixpll * const this)
274f39db26cSSui Jingfeng {
275f39db26cSSui Jingfeng 	union lsdc_pixpll_reg_bitmap pixpll_reg;
276f39db26cSSui Jingfeng 
277f39db26cSSui Jingfeng 	__pixpll_rreg(this, &pixpll_reg);
278f39db26cSSui Jingfeng 
279f39db26cSSui Jingfeng 	pixpll_reg.bitmap.powerdown = 0;
280f39db26cSSui Jingfeng 
281f39db26cSSui Jingfeng 	__pixpll_wreg(this, &pixpll_reg);
282f39db26cSSui Jingfeng }
283f39db26cSSui Jingfeng 
__pixpll_ops_powerdown(struct lsdc_pixpll * const this)284f39db26cSSui Jingfeng static void __pixpll_ops_powerdown(struct lsdc_pixpll * const this)
285f39db26cSSui Jingfeng {
286f39db26cSSui Jingfeng 	union lsdc_pixpll_reg_bitmap pixpll_reg;
287f39db26cSSui Jingfeng 
288f39db26cSSui Jingfeng 	__pixpll_rreg(this, &pixpll_reg);
289f39db26cSSui Jingfeng 
290f39db26cSSui Jingfeng 	pixpll_reg.bitmap.powerdown = 1;
291f39db26cSSui Jingfeng 
292f39db26cSSui Jingfeng 	__pixpll_wreg(this, &pixpll_reg);
293f39db26cSSui Jingfeng }
294f39db26cSSui Jingfeng 
__pixpll_ops_on(struct lsdc_pixpll * const this)295f39db26cSSui Jingfeng static void __pixpll_ops_on(struct lsdc_pixpll * const this)
296f39db26cSSui Jingfeng {
297f39db26cSSui Jingfeng 	union lsdc_pixpll_reg_bitmap pixpll_reg;
298f39db26cSSui Jingfeng 
299f39db26cSSui Jingfeng 	__pixpll_rreg(this, &pixpll_reg);
300f39db26cSSui Jingfeng 
301f39db26cSSui Jingfeng 	pixpll_reg.bitmap.sel_out = 1;
302f39db26cSSui Jingfeng 
303f39db26cSSui Jingfeng 	__pixpll_wreg(this, &pixpll_reg);
304f39db26cSSui Jingfeng }
305f39db26cSSui Jingfeng 
__pixpll_ops_off(struct lsdc_pixpll * const this)306f39db26cSSui Jingfeng static void __pixpll_ops_off(struct lsdc_pixpll * const this)
307f39db26cSSui Jingfeng {
308f39db26cSSui Jingfeng 	union lsdc_pixpll_reg_bitmap pixpll_reg;
309f39db26cSSui Jingfeng 
310f39db26cSSui Jingfeng 	__pixpll_rreg(this, &pixpll_reg);
311f39db26cSSui Jingfeng 
312f39db26cSSui Jingfeng 	pixpll_reg.bitmap.sel_out = 0;
313f39db26cSSui Jingfeng 
314f39db26cSSui Jingfeng 	__pixpll_wreg(this, &pixpll_reg);
315f39db26cSSui Jingfeng }
316f39db26cSSui Jingfeng 
__pixpll_ops_bypass(struct lsdc_pixpll * const this)317f39db26cSSui Jingfeng static void __pixpll_ops_bypass(struct lsdc_pixpll * const this)
318f39db26cSSui Jingfeng {
319f39db26cSSui Jingfeng 	union lsdc_pixpll_reg_bitmap pixpll_reg;
320f39db26cSSui Jingfeng 
321f39db26cSSui Jingfeng 	__pixpll_rreg(this, &pixpll_reg);
322f39db26cSSui Jingfeng 
323f39db26cSSui Jingfeng 	pixpll_reg.bitmap.bypass = 1;
324f39db26cSSui Jingfeng 
325f39db26cSSui Jingfeng 	__pixpll_wreg(this, &pixpll_reg);
326f39db26cSSui Jingfeng }
327f39db26cSSui Jingfeng 
__pixpll_ops_unbypass(struct lsdc_pixpll * const this)328f39db26cSSui Jingfeng static void __pixpll_ops_unbypass(struct lsdc_pixpll * const this)
329f39db26cSSui Jingfeng {
330f39db26cSSui Jingfeng 	union lsdc_pixpll_reg_bitmap pixpll_reg;
331f39db26cSSui Jingfeng 
332f39db26cSSui Jingfeng 	__pixpll_rreg(this, &pixpll_reg);
333f39db26cSSui Jingfeng 
334f39db26cSSui Jingfeng 	pixpll_reg.bitmap.bypass = 0;
335f39db26cSSui Jingfeng 
336f39db26cSSui Jingfeng 	__pixpll_wreg(this, &pixpll_reg);
337f39db26cSSui Jingfeng }
338f39db26cSSui Jingfeng 
__pixpll_ops_untoggle_param(struct lsdc_pixpll * const this)339f39db26cSSui Jingfeng static void __pixpll_ops_untoggle_param(struct lsdc_pixpll * const this)
340f39db26cSSui Jingfeng {
341f39db26cSSui Jingfeng 	union lsdc_pixpll_reg_bitmap pixpll_reg;
342f39db26cSSui Jingfeng 
343f39db26cSSui Jingfeng 	__pixpll_rreg(this, &pixpll_reg);
344f39db26cSSui Jingfeng 
345f39db26cSSui Jingfeng 	pixpll_reg.bitmap.set_param = 0;
346f39db26cSSui Jingfeng 
347f39db26cSSui Jingfeng 	__pixpll_wreg(this, &pixpll_reg);
348f39db26cSSui Jingfeng }
349f39db26cSSui Jingfeng 
__pixpll_ops_set_param(struct lsdc_pixpll * const this,struct lsdc_pixpll_parms const * p)350f39db26cSSui Jingfeng static void __pixpll_ops_set_param(struct lsdc_pixpll * const this,
351f39db26cSSui Jingfeng 				   struct lsdc_pixpll_parms const *p)
352f39db26cSSui Jingfeng {
353f39db26cSSui Jingfeng 	union lsdc_pixpll_reg_bitmap pixpll_reg;
354f39db26cSSui Jingfeng 
355f39db26cSSui Jingfeng 	__pixpll_rreg(this, &pixpll_reg);
356f39db26cSSui Jingfeng 
357f39db26cSSui Jingfeng 	pixpll_reg.bitmap.div_ref = p->div_ref;
358f39db26cSSui Jingfeng 	pixpll_reg.bitmap.loopc = p->loopc;
359f39db26cSSui Jingfeng 	pixpll_reg.bitmap.div_out = p->div_out;
360f39db26cSSui Jingfeng 
361f39db26cSSui Jingfeng 	__pixpll_wreg(this, &pixpll_reg);
362f39db26cSSui Jingfeng }
363f39db26cSSui Jingfeng 
__pixpll_ops_toggle_param(struct lsdc_pixpll * const this)364f39db26cSSui Jingfeng static void __pixpll_ops_toggle_param(struct lsdc_pixpll * const this)
365f39db26cSSui Jingfeng {
366f39db26cSSui Jingfeng 	union lsdc_pixpll_reg_bitmap pixpll_reg;
367f39db26cSSui Jingfeng 
368f39db26cSSui Jingfeng 	__pixpll_rreg(this, &pixpll_reg);
369f39db26cSSui Jingfeng 
370f39db26cSSui Jingfeng 	pixpll_reg.bitmap.set_param = 1;
371f39db26cSSui Jingfeng 
372f39db26cSSui Jingfeng 	__pixpll_wreg(this, &pixpll_reg);
373f39db26cSSui Jingfeng }
374f39db26cSSui Jingfeng 
__pixpll_ops_wait_locked(struct lsdc_pixpll * const this)375f39db26cSSui Jingfeng static void __pixpll_ops_wait_locked(struct lsdc_pixpll * const this)
376f39db26cSSui Jingfeng {
377f39db26cSSui Jingfeng 	union lsdc_pixpll_reg_bitmap pixpll_reg;
378f39db26cSSui Jingfeng 	unsigned int counter = 0;
379f39db26cSSui Jingfeng 
380f39db26cSSui Jingfeng 	do {
381f39db26cSSui Jingfeng 		__pixpll_rreg(this, &pixpll_reg);
382f39db26cSSui Jingfeng 
383f39db26cSSui Jingfeng 		if (pixpll_reg.bitmap.locked)
384f39db26cSSui Jingfeng 			break;
385f39db26cSSui Jingfeng 
386f39db26cSSui Jingfeng 		++counter;
387f39db26cSSui Jingfeng 	} while (counter < 2000);
388f39db26cSSui Jingfeng 
389f39db26cSSui Jingfeng 	drm_dbg(this->ddev, "%u loop waited\n", counter);
390f39db26cSSui Jingfeng }
391f39db26cSSui Jingfeng 
392f39db26cSSui Jingfeng /*
393f39db26cSSui Jingfeng  * Update the PLL parameters to the PLL hardware
394f39db26cSSui Jingfeng  *
395f39db26cSSui Jingfeng  * @this: point to the object from which this function is called
396f39db26cSSui Jingfeng  * @pin: point to the struct of lsdc_pixpll_parms passed in
397f39db26cSSui Jingfeng  *
398f39db26cSSui Jingfeng  * return 0 if successful.
399f39db26cSSui Jingfeng  */
lsdc_pixpll_update(struct lsdc_pixpll * const this,struct lsdc_pixpll_parms const * pin)400f39db26cSSui Jingfeng static int lsdc_pixpll_update(struct lsdc_pixpll * const this,
401f39db26cSSui Jingfeng 			      struct lsdc_pixpll_parms const *pin)
402f39db26cSSui Jingfeng {
403f39db26cSSui Jingfeng 	__pixpll_ops_bypass(this);
404f39db26cSSui Jingfeng 
405f39db26cSSui Jingfeng 	__pixpll_ops_off(this);
406f39db26cSSui Jingfeng 
407f39db26cSSui Jingfeng 	__pixpll_ops_powerdown(this);
408f39db26cSSui Jingfeng 
409f39db26cSSui Jingfeng 	__pixpll_ops_toggle_param(this);
410f39db26cSSui Jingfeng 
411f39db26cSSui Jingfeng 	__pixpll_ops_set_param(this, pin);
412f39db26cSSui Jingfeng 
413f39db26cSSui Jingfeng 	__pixpll_ops_untoggle_param(this);
414f39db26cSSui Jingfeng 
415f39db26cSSui Jingfeng 	__pixpll_ops_powerup(this);
416f39db26cSSui Jingfeng 
417f39db26cSSui Jingfeng 	udelay(2);
418f39db26cSSui Jingfeng 
419f39db26cSSui Jingfeng 	__pixpll_ops_wait_locked(this);
420f39db26cSSui Jingfeng 
421f39db26cSSui Jingfeng 	__pixpll_ops_on(this);
422f39db26cSSui Jingfeng 
423f39db26cSSui Jingfeng 	__pixpll_ops_unbypass(this);
424f39db26cSSui Jingfeng 
425f39db26cSSui Jingfeng 	return 0;
426f39db26cSSui Jingfeng }
427f39db26cSSui Jingfeng 
lsdc_pixpll_get_freq(struct lsdc_pixpll * const this)428f39db26cSSui Jingfeng static unsigned int lsdc_pixpll_get_freq(struct lsdc_pixpll * const this)
429f39db26cSSui Jingfeng {
430f39db26cSSui Jingfeng 	struct lsdc_pixpll_parms *ppar = this->priv;
431f39db26cSSui Jingfeng 	union lsdc_pixpll_reg_bitmap pix_pll_reg;
432f39db26cSSui Jingfeng 	unsigned int freq;
433f39db26cSSui Jingfeng 
434f39db26cSSui Jingfeng 	__pixpll_rreg(this, &pix_pll_reg);
435f39db26cSSui Jingfeng 
436f39db26cSSui Jingfeng 	ppar->div_ref = pix_pll_reg.bitmap.div_ref;
437f39db26cSSui Jingfeng 	ppar->loopc = pix_pll_reg.bitmap.loopc;
438f39db26cSSui Jingfeng 	ppar->div_out = pix_pll_reg.bitmap.div_out;
439f39db26cSSui Jingfeng 
440f39db26cSSui Jingfeng 	freq = ppar->ref_clock / ppar->div_ref * ppar->loopc / ppar->div_out;
441f39db26cSSui Jingfeng 
442f39db26cSSui Jingfeng 	return freq;
443f39db26cSSui Jingfeng }
444f39db26cSSui Jingfeng 
lsdc_pixpll_print(struct lsdc_pixpll * const this,struct drm_printer * p)445f39db26cSSui Jingfeng static void lsdc_pixpll_print(struct lsdc_pixpll * const this,
446f39db26cSSui Jingfeng 			      struct drm_printer *p)
447f39db26cSSui Jingfeng {
448f39db26cSSui Jingfeng 	struct lsdc_pixpll_parms *parms = this->priv;
449f39db26cSSui Jingfeng 
450f39db26cSSui Jingfeng 	drm_printf(p, "div_ref: %u, loopc: %u, div_out: %u\n",
451f39db26cSSui Jingfeng 		   parms->div_ref, parms->loopc, parms->div_out);
452f39db26cSSui Jingfeng }
453f39db26cSSui Jingfeng 
454f39db26cSSui Jingfeng /*
455f39db26cSSui Jingfeng  * LS7A1000, LS7A2000 and ls2k2000's pixel pll setting register is same,
456f39db26cSSui Jingfeng  * we take this as default, create a new instance if a different model is
457f39db26cSSui Jingfeng  * introduced.
458f39db26cSSui Jingfeng  */
459f39db26cSSui Jingfeng static const struct lsdc_pixpll_funcs __pixpll_default_funcs = {
460f39db26cSSui Jingfeng 	.setup = lsdc_pixel_pll_setup,
461f39db26cSSui Jingfeng 	.compute = lsdc_pixel_pll_compute,
462f39db26cSSui Jingfeng 	.update = lsdc_pixpll_update,
463f39db26cSSui Jingfeng 	.get_rate = lsdc_pixpll_get_freq,
464f39db26cSSui Jingfeng 	.print = lsdc_pixpll_print,
465f39db26cSSui Jingfeng };
466f39db26cSSui Jingfeng 
467f39db26cSSui Jingfeng /* pixel pll initialization */
468f39db26cSSui Jingfeng 
lsdc_pixpll_init(struct lsdc_pixpll * const this,struct drm_device * ddev,unsigned int index)469f39db26cSSui Jingfeng int lsdc_pixpll_init(struct lsdc_pixpll * const this,
470f39db26cSSui Jingfeng 		     struct drm_device *ddev,
471f39db26cSSui Jingfeng 		     unsigned int index)
472f39db26cSSui Jingfeng {
473f39db26cSSui Jingfeng 	struct lsdc_device *ldev = to_lsdc(ddev);
474f39db26cSSui Jingfeng 	const struct lsdc_desc *descp = ldev->descp;
475f39db26cSSui Jingfeng 	const struct loongson_gfx_desc *gfx = to_loongson_gfx(descp);
476f39db26cSSui Jingfeng 
477f39db26cSSui Jingfeng 	this->ddev = ddev;
478f39db26cSSui Jingfeng 	this->reg_size = 8;
479f39db26cSSui Jingfeng 	this->reg_base = gfx->conf_reg_base + gfx->pixpll[index].reg_offset;
480f39db26cSSui Jingfeng 	this->funcs = &__pixpll_default_funcs;
481f39db26cSSui Jingfeng 
482f39db26cSSui Jingfeng 	return this->funcs->setup(this);
483f39db26cSSui Jingfeng }
484