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