Commit 2545ac96 authored by Thomas Zimmermann's avatar Thomas Zimmermann

drm/mgag200: Abstract pixel PLL via struct mgag200_pll

Move all PLL compute and update functions into mgag200_pll.c. No
functional changes to the rsp algorithms.

Introduce struct mgag200_pll and mgag200_pll_funcs. The data strutures
abstract the details of each revision's PLL. Perform calls to compute
and update functionality via function pointers. Init the PLL once as
part of the driver initialization.
Signed-off-by: default avatarThomas Zimmermann <tzimmermann@suse.de>
Acked-by: default avatarSam Ravnborg <sam@ravnborg.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20210714142240.21979-11-tzimmermann@suse.de
parent 8fb60d1b
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
mgag200-y := mgag200_drv.o mgag200_i2c.o mgag200_mm.o mgag200_mode.o mgag200-y := mgag200_drv.o mgag200_i2c.o mgag200_mm.o mgag200_mode.o mgag200_pll.o
obj-$(CONFIG_DRM_MGAG200) += mgag200.o obj-$(CONFIG_DRM_MGAG200) += mgag200.o
...@@ -126,6 +126,9 @@ ...@@ -126,6 +126,9 @@
#define MGAG200_MAX_FB_HEIGHT 4096 #define MGAG200_MAX_FB_HEIGHT 4096
#define MGAG200_MAX_FB_WIDTH 4096 #define MGAG200_MAX_FB_WIDTH 4096
struct mga_device;
struct mgag200_pll;
/* /*
* Stores parameters for programming the PLLs * Stores parameters for programming the PLLs
* *
...@@ -143,6 +146,17 @@ struct mgag200_pll_values { ...@@ -143,6 +146,17 @@ struct mgag200_pll_values {
unsigned int s; unsigned int s;
}; };
struct mgag200_pll_funcs {
int (*compute)(struct mgag200_pll *pll, long clock, struct mgag200_pll_values *pllc);
void (*update)(struct mgag200_pll *pll, const struct mgag200_pll_values *pllc);
};
struct mgag200_pll {
struct mga_device *mdev;
const struct mgag200_pll_funcs *funcs;
};
#define to_mga_connector(x) container_of(x, struct mga_connector, base) #define to_mga_connector(x) container_of(x, struct mga_connector, base)
struct mga_i2c_chan { struct mga_i2c_chan {
...@@ -213,8 +227,8 @@ struct mga_device { ...@@ -213,8 +227,8 @@ struct mga_device {
} g200se; } g200se;
} model; } model;
struct mga_connector connector; struct mga_connector connector;
struct mgag200_pll pixpll;
struct drm_simple_display_pipe display_pipe; struct drm_simple_display_pipe display_pipe;
}; };
...@@ -233,4 +247,7 @@ void mgag200_i2c_destroy(struct mga_i2c_chan *i2c); ...@@ -233,4 +247,7 @@ void mgag200_i2c_destroy(struct mga_i2c_chan *i2c);
/* mgag200_mm.c */ /* mgag200_mm.c */
int mgag200_mm_init(struct mga_device *mdev); int mgag200_mm_init(struct mga_device *mdev);
/* mgag200_pll.c */
int mgag200_pixpll_init(struct mgag200_pll *pixpll, struct mga_device *mdev);
#endif /* __MGAG200_DRV_H__ */ #endif /* __MGAG200_DRV_H__ */
...@@ -114,914 +114,17 @@ static inline void mga_wait_busy(struct mga_device *mdev) ...@@ -114,914 +114,17 @@ static inline void mga_wait_busy(struct mga_device *mdev)
* PLL setup * PLL setup
*/ */
static int mgag200_compute_pixpll_values_g200(struct mga_device *mdev, long clock,
struct mgag200_pll_values *pixpllc)
{
struct drm_device *dev = &mdev->base;
const int post_div_max = 7;
const int in_div_min = 1;
const int in_div_max = 6;
const int feed_div_min = 7;
const int feed_div_max = 127;
u8 testp, testm, testn;
u8 n = 0, m = 0, p, s;
long f_vco;
long computed;
long delta, tmp_delta;
long ref_clk = mdev->model.g200.ref_clk;
long p_clk_min = mdev->model.g200.pclk_min;
long p_clk_max = mdev->model.g200.pclk_max;
if (clock > p_clk_max) {
drm_err(dev, "Pixel Clock %ld too high\n", clock);
return -EINVAL;
}
if (clock < p_clk_min >> 3)
clock = p_clk_min >> 3;
f_vco = clock;
for (testp = 0;
testp <= post_div_max && f_vco < p_clk_min;
testp = (testp << 1) + 1, f_vco <<= 1)
;
p = testp + 1;
delta = clock;
for (testm = in_div_min; testm <= in_div_max; testm++) {
for (testn = feed_div_min; testn <= feed_div_max; testn++) {
computed = ref_clk * (testn + 1) / (testm + 1);
if (computed < f_vco)
tmp_delta = f_vco - computed;
else
tmp_delta = computed - f_vco;
if (tmp_delta < delta) {
delta = tmp_delta;
m = testm + 1;
n = testn + 1;
}
}
}
f_vco = ref_clk * n / m;
if (f_vco < 100000)
s = 0;
else if (f_vco < 140000)
s = 1;
else if (f_vco < 180000)
s = 2;
else
s = 3;
drm_dbg_kms(dev, "clock: %ld vco: %ld m: %d n: %d p: %d s: %d\n",
clock, f_vco, m, n, p, s);
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void mgag200_set_pixpll_g200(struct mga_device *mdev,
const struct mgag200_pll_values *pixpllc)
{
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp);
}
static int mgag200_compute_pixpll_values_g200se_00(struct mga_device *mdev, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 320000;
static const unsigned int vcomin = 160000;
static const unsigned int pllreffreq = 25000;
unsigned int delta, tmpdelta, permitteddelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
permitteddelta = clock * 5 / 1000;
for (testp = 8; testp > 0; testp /= 2) {
if (clock * testp > vcomax)
continue;
if (clock * testp < vcomin)
continue;
for (testn = 17; testn < 256; testn++) {
for (testm = 1; testm < 32; testm++) {
computed = (pllreffreq * testn) / (testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
m = testm;
n = testn;
p = testp;
}
}
}
}
if (delta > permitteddelta) {
pr_warn("PLL delta too large\n");
return -EINVAL;
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static int mgag200_compute_pixpll_values_g200se_04(struct mga_device *mdev, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 1600000;
static const unsigned int vcomin = 800000;
static const unsigned int pllreffreq = 25000;
static const unsigned int pvalues_e4[] = {16, 14, 12, 10, 8, 6, 4, 2, 1};
unsigned int delta, tmpdelta, permitteddelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
unsigned int fvv;
unsigned int i;
m = n = p = s = 0;
delta = 0xffffffff;
if (clock < 25000)
clock = 25000;
clock = clock * 2;
/* Permited delta is 0.5% as VESA Specification */
permitteddelta = clock * 5 / 1000;
for (i = 0 ; i < ARRAY_SIZE(pvalues_e4); i++) {
testp = pvalues_e4[i];
if ((clock * testp) > vcomax)
continue;
if ((clock * testp) < vcomin)
continue;
for (testn = 50; testn <= 256; testn++) {
for (testm = 1; testm <= 32; testm++) {
computed = (pllreffreq * testn) / (testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
m = testm;
n = testn;
p = testp;
}
}
}
}
fvv = pllreffreq * n / m;
fvv = (fvv - 800000) / 50000;
if (fvv > 15)
fvv = 15;
s = fvv << 1;
if (delta > permitteddelta) {
pr_warn("PLL delta too large\n");
return -EINVAL;
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static int mgag200_compute_pixpll_values_g200se(struct mga_device *mdev, long clock,
struct mgag200_pll_values *pixpllc)
{
u32 unique_rev_id = mdev->model.g200se.unique_rev_id;
if (unique_rev_id >= 0x04)
return mgag200_compute_pixpll_values_g200se_04(mdev, clock, pixpllc);
else
return mgag200_compute_pixpll_values_g200se_00(mdev, clock, pixpllc);
}
static void mgag200_set_pixpll_g200se(struct mga_device *mdev,
const struct mgag200_pll_values *pixpllc)
{
u32 unique_rev_id = mdev->model.g200se.unique_rev_id;
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = pixpllcm | ((pixpllcn & BIT(8)) >> 1);
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp);
if (unique_rev_id >= 0x04) {
WREG_DAC(0x1a, 0x09);
msleep(20);
WREG_DAC(0x1a, 0x01);
}
}
static int mgag200_compute_pixpll_values_g200wb(struct mga_device *mdev, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 550000;
static const unsigned int vcomin = 150000;
static const unsigned int pllreffreq = 48000;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
for (testp = 1; testp < 9; testp++) {
if (clock * testp > vcomax)
continue;
if (clock * testp < vcomin)
continue;
for (testm = 1; testm < 17; testm++) {
for (testn = 1; testn < 151; testn++) {
computed = (pllreffreq * testn) / (testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
n = testn;
m = testm;
p = testp;
s = 0;
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void mgag200_set_pixpll_g200wb(struct mga_device *mdev,
const struct mgag200_pll_values *pixpllc)
{
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp;
int i, j, tmpcount, vcount;
bool pll_locked = false;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = ((pixpllcn & BIT(8)) >> 1) | pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = ((pixpllcn & GENMASK(10, 9)) >> 3) | (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
for (i = 0; i <= 32 && pll_locked == false; i++) {
if (i > 0) {
WREG8(MGAREG_CRTC_INDEX, 0x1e);
tmp = RREG8(MGAREG_CRTC_DATA);
if (tmp < 0xff)
WREG8(MGAREG_CRTC_DATA, tmp+1);
}
/* set pixclkdis to 1 */
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_REMHEADCTL_CLKDIS;
WREG8(DAC_DATA, tmp);
/* select PLL Set C */
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= 0x3 << 2;
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN | 0x80;
WREG8(DAC_DATA, tmp);
udelay(500);
/* reset the PLL */
WREG8(DAC_INDEX, MGA1064_VREF_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~0x04;
WREG8(DAC_DATA, tmp);
udelay(50);
/* program pixel pll register */
WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp);
udelay(50);
/* turn pll on */
WREG8(DAC_INDEX, MGA1064_VREF_CTL);
tmp = RREG8(DAC_DATA);
tmp |= 0x04;
WREG_DAC(MGA1064_VREF_CTL, tmp);
udelay(500);
/* select the pixel pll */
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_REMHEADCTL_CLKSL_MSK;
tmp |= MGA1064_REMHEADCTL_CLKSL_PLL;
WREG8(DAC_DATA, tmp);
/* reset dotclock rate bit */
WREG8(MGAREG_SEQ_INDEX, 1);
tmp = RREG8(MGAREG_SEQ_DATA);
tmp &= ~0x8;
WREG8(MGAREG_SEQ_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
vcount = RREG8(MGAREG_VCOUNT);
for (j = 0; j < 30 && pll_locked == false; j++) {
tmpcount = RREG8(MGAREG_VCOUNT);
if (tmpcount < vcount)
vcount = 0;
if ((tmpcount - vcount) > 2)
pll_locked = true;
else
udelay(5);
}
}
WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_REMHEADCTL_CLKDIS;
WREG_DAC(MGA1064_REMHEADCTL, tmp);
}
static int mgag200_compute_pixpll_values_g200ev(struct mga_device *mdev, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 550000;
static const unsigned int vcomin = 150000;
static const unsigned int pllreffreq = 50000;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
for (testp = 16; testp > 0; testp--) {
if (clock * testp > vcomax)
continue;
if (clock * testp < vcomin)
continue;
for (testn = 1; testn < 257; testn++) {
for (testm = 1; testm < 17; testm++) {
computed = (pllreffreq * testn) /
(testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
n = testn;
m = testm;
p = testp;
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void mgag200_set_pixpll_g200ev(struct mga_device *mdev,
const struct mgag200_pll_values *pixpllc)
{
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= 0x3 << 2;
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_PLL_STAT);
tmp = RREG8(DAC_DATA);
WREG8(DAC_DATA, tmp & ~0x40);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
WREG_DAC(MGA1064_EV_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_EV_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_EV_PIX_PLLC_P, xpixpllcp);
udelay(50);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
udelay(500);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_PLL_STAT);
tmp = RREG8(DAC_DATA);
WREG8(DAC_DATA, tmp | 0x40);
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= (0x3 << 2);
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
}
static int mgag200_compute_pixpll_values_g200eh(struct mga_device *mdev, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 800000;
static const unsigned int vcomin = 400000;
static const unsigned int pllreffreq = 33333;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
for (testp = 16; testp > 0; testp >>= 1) {
if (clock * testp > vcomax)
continue;
if (clock * testp < vcomin)
continue;
for (testm = 1; testm < 33; testm++) {
for (testn = 17; testn < 257; testn++) {
computed = (pllreffreq * testn) / (testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
n = testn;
m = testm;
p = testp;
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void mgag200_set_pixpll_g200eh(struct mga_device *mdev,
const struct mgag200_pll_values *pixpllc)
{
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp;
int i, j, tmpcount, vcount;
bool pll_locked = false;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = ((pixpllcn & BIT(8)) >> 1) | pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
for (i = 0; i <= 32 && pll_locked == false; i++) {
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= 0x3 << 2;
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
udelay(500);
WREG_DAC(MGA1064_EH_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_EH_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_EH_PIX_PLLC_P, xpixpllcp);
udelay(500);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
vcount = RREG8(MGAREG_VCOUNT);
for (j = 0; j < 30 && pll_locked == false; j++) {
tmpcount = RREG8(MGAREG_VCOUNT);
if (tmpcount < vcount)
vcount = 0;
if ((tmpcount - vcount) > 2)
pll_locked = true;
else
udelay(5);
}
}
}
static int mgag200_compute_pixpll_values_g200eh3(struct mga_device *mdev, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 3000000;
static const unsigned int vcomin = 1500000;
static const unsigned int pllreffreq = 25000;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
testp = 0;
for (testm = 150; testm >= 6; testm--) {
if (clock * testm > vcomax)
continue;
if (clock * testm < vcomin)
continue;
for (testn = 120; testn >= 60; testn--) {
computed = (pllreffreq * testn) / testm;
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
n = testn + 1;
m = testm + 1;
p = testp + 1;
}
if (delta == 0)
break;
}
if (delta == 0)
break;
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static int mgag200_compute_pixpll_values_g200er(struct mga_device *mdev, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 1488000;
static const unsigned int vcomin = 1056000;
static const unsigned int pllreffreq = 48000;
static const unsigned int m_div_val[] = { 1, 2, 4, 8 };
unsigned int delta, tmpdelta;
int testr, testn, testm, testo;
unsigned int p, m, n, s;
unsigned int computed, vco;
m = n = p = s = 0;
delta = 0xffffffff;
for (testr = 0; testr < 4; testr++) {
if (delta == 0)
break;
for (testn = 5; testn < 129; testn++) {
if (delta == 0)
break;
for (testm = 3; testm >= 0; testm--) {
if (delta == 0)
break;
for (testo = 5; testo < 33; testo++) {
vco = pllreffreq * (testn + 1) /
(testr + 1);
if (vco < vcomin)
continue;
if (vco > vcomax)
continue;
computed = vco / (m_div_val[testm] * (testo + 1));
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
m = (testm | (testo << 3)) + 1;
n = testn + 1;
p = testr + 1;
s = testr;
}
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void mgag200_set_pixpll_g200er(struct mga_device *mdev,
const struct mgag200_pll_values *pixpllc)
{
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_REMHEADCTL_CLKDIS;
WREG8(DAC_DATA, tmp);
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= (0x3<<2) | 0xc0;
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
udelay(500);
WREG_DAC(MGA1064_ER_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_ER_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_ER_PIX_PLLC_P, xpixpllcp);
udelay(50);
}
static int mgag200_compute_pixpll_values_g200ew3(struct mga_device *mdev, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 800000;
static const unsigned int vcomin = 400000;
static const unsigned int pllreffreq = 25000;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn, testp2;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
for (testp = 1; testp < 8; testp++) {
for (testp2 = 1; testp2 < 8; testp2++) {
if (testp < testp2)
continue;
if ((clock * testp * testp2) > vcomax)
continue;
if ((clock * testp * testp2) < vcomin)
continue;
for (testm = 1; testm < 26; testm++) {
for (testn = 32; testn < 2048 ; testn++) {
computed = (pllreffreq * testn) / (testm * testp * testp2);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
m = testm + 1;
n = testn + 1;
p = testp + 1;
s = testp2;
}
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void mgag200_crtc_set_plls(struct mga_device *mdev, long clock) static void mgag200_crtc_set_plls(struct mga_device *mdev, long clock)
{ {
struct mgag200_pll_values pixpll; struct mgag200_pll *pixpll = &mdev->pixpll;
struct mgag200_pll_values pixpllc;
int ret; int ret;
switch(mdev->type) { ret = pixpll->funcs->compute(pixpll, clock, &pixpllc);
case G200_PCI:
case G200_AGP:
ret = mgag200_compute_pixpll_values_g200(mdev, clock, &pixpll);
break;
case G200_SE_A:
case G200_SE_B:
ret = mgag200_compute_pixpll_values_g200se(mdev, clock, &pixpll);
break;
case G200_WB:
ret = mgag200_compute_pixpll_values_g200wb(mdev, clock, &pixpll);
break;
case G200_EV:
ret = mgag200_compute_pixpll_values_g200ev(mdev, clock, &pixpll);
break;
case G200_EH:
ret = mgag200_compute_pixpll_values_g200eh(mdev, clock, &pixpll);
break;
case G200_EH3:
ret = mgag200_compute_pixpll_values_g200eh3(mdev, clock, &pixpll);
break;
case G200_ER:
ret = mgag200_compute_pixpll_values_g200er(mdev, clock, &pixpll);
break;
case G200_EW3:
ret = mgag200_compute_pixpll_values_g200ew3(mdev, clock, &pixpll);
break;
}
if (ret) if (ret)
return; return;
switch (mdev->type) { pixpll->funcs->update(pixpll, &pixpllc);
case G200_PCI:
case G200_AGP:
mgag200_set_pixpll_g200(mdev, &pixpll);
break;
case G200_SE_A:
case G200_SE_B:
mgag200_set_pixpll_g200se(mdev, &pixpll);
break;
case G200_WB:
case G200_EW3:
mgag200_set_pixpll_g200wb(mdev, &pixpll);
break;
case G200_EV:
mgag200_set_pixpll_g200ev(mdev, &pixpll);
break;
case G200_EH:
case G200_EH3:
mgag200_set_pixpll_g200eh(mdev, &pixpll);
break;
case G200_ER:
mgag200_set_pixpll_g200er(mdev, &pixpll);
break;
}
} }
static void mgag200_g200wb_hold_bmc(struct mga_device *mdev) static void mgag200_g200wb_hold_bmc(struct mga_device *mdev)
...@@ -1934,6 +1037,10 @@ int mgag200_modeset_init(struct mga_device *mdev) ...@@ -1934,6 +1037,10 @@ int mgag200_modeset_init(struct mga_device *mdev)
return ret; return ret;
} }
ret = mgag200_pixpll_init(&mdev->pixpll, mdev);
if (ret)
return ret;
ret = drm_simple_display_pipe_init(dev, pipe, ret = drm_simple_display_pipe_init(dev, pipe,
&mgag200_simple_display_pipe_funcs, &mgag200_simple_display_pipe_funcs,
mgag200_simple_display_pipe_formats, mgag200_simple_display_pipe_formats,
......
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/delay.h>
#include "mgag200_drv.h"
/*
* G200
*/
static int mgag200_pixpll_compute_g200(struct mgag200_pll *pixpll, long clock,
struct mgag200_pll_values *pixpllc)
{
struct mga_device *mdev = pixpll->mdev;
struct drm_device *dev = &mdev->base;
const int post_div_max = 7;
const int in_div_min = 1;
const int in_div_max = 6;
const int feed_div_min = 7;
const int feed_div_max = 127;
u8 testp, testm, testn;
u8 n = 0, m = 0, p, s;
long f_vco;
long computed;
long delta, tmp_delta;
long ref_clk = mdev->model.g200.ref_clk;
long p_clk_min = mdev->model.g200.pclk_min;
long p_clk_max = mdev->model.g200.pclk_max;
if (clock > p_clk_max) {
drm_err(dev, "Pixel Clock %ld too high\n", clock);
return -EINVAL;
}
if (clock < p_clk_min >> 3)
clock = p_clk_min >> 3;
f_vco = clock;
for (testp = 0;
testp <= post_div_max && f_vco < p_clk_min;
testp = (testp << 1) + 1, f_vco <<= 1)
;
p = testp + 1;
delta = clock;
for (testm = in_div_min; testm <= in_div_max; testm++) {
for (testn = feed_div_min; testn <= feed_div_max; testn++) {
computed = ref_clk * (testn + 1) / (testm + 1);
if (computed < f_vco)
tmp_delta = f_vco - computed;
else
tmp_delta = computed - f_vco;
if (tmp_delta < delta) {
delta = tmp_delta;
m = testm + 1;
n = testn + 1;
}
}
}
f_vco = ref_clk * n / m;
if (f_vco < 100000)
s = 0;
else if (f_vco < 140000)
s = 1;
else if (f_vco < 180000)
s = 2;
else
s = 3;
drm_dbg_kms(dev, "clock: %ld vco: %ld m: %d n: %d p: %d s: %d\n",
clock, f_vco, m, n, p, s);
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void
mgag200_pixpll_update_g200(struct mgag200_pll *pixpll, const struct mgag200_pll_values *pixpllc)
{
struct mga_device *mdev = pixpll->mdev;
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp);
}
static const struct mgag200_pll_funcs mgag200_pixpll_funcs_g200 = {
.compute = mgag200_pixpll_compute_g200,
.update = mgag200_pixpll_update_g200,
};
/*
* G200SE
*/
static int mgag200_pixpll_compute_g200se_00(struct mgag200_pll *pixpll, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 320000;
static const unsigned int vcomin = 160000;
static const unsigned int pllreffreq = 25000;
unsigned int delta, tmpdelta, permitteddelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
permitteddelta = clock * 5 / 1000;
for (testp = 8; testp > 0; testp /= 2) {
if (clock * testp > vcomax)
continue;
if (clock * testp < vcomin)
continue;
for (testn = 17; testn < 256; testn++) {
for (testm = 1; testm < 32; testm++) {
computed = (pllreffreq * testn) / (testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
m = testm;
n = testn;
p = testp;
}
}
}
}
if (delta > permitteddelta) {
pr_warn("PLL delta too large\n");
return -EINVAL;
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void mgag200_pixpll_update_g200se_00(struct mgag200_pll *pixpll,
const struct mgag200_pll_values *pixpllc)
{
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp;
struct mga_device *mdev = pixpll->mdev;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = pixpllcm | ((pixpllcn & BIT(8)) >> 1);
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp);
}
static int mgag200_pixpll_compute_g200se_04(struct mgag200_pll *pixpll, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 1600000;
static const unsigned int vcomin = 800000;
static const unsigned int pllreffreq = 25000;
static const unsigned int pvalues_e4[] = {16, 14, 12, 10, 8, 6, 4, 2, 1};
unsigned int delta, tmpdelta, permitteddelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
unsigned int fvv;
unsigned int i;
m = n = p = s = 0;
delta = 0xffffffff;
if (clock < 25000)
clock = 25000;
clock = clock * 2;
/* Permited delta is 0.5% as VESA Specification */
permitteddelta = clock * 5 / 1000;
for (i = 0 ; i < ARRAY_SIZE(pvalues_e4); i++) {
testp = pvalues_e4[i];
if ((clock * testp) > vcomax)
continue;
if ((clock * testp) < vcomin)
continue;
for (testn = 50; testn <= 256; testn++) {
for (testm = 1; testm <= 32; testm++) {
computed = (pllreffreq * testn) / (testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
m = testm;
n = testn;
p = testp;
}
}
}
}
fvv = pllreffreq * n / m;
fvv = (fvv - 800000) / 50000;
if (fvv > 15)
fvv = 15;
s = fvv << 1;
if (delta > permitteddelta) {
pr_warn("PLL delta too large\n");
return -EINVAL;
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void mgag200_pixpll_update_g200se_04(struct mgag200_pll *pixpll,
const struct mgag200_pll_values *pixpllc)
{
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp;
struct mga_device *mdev = pixpll->mdev;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = pixpllcm | ((pixpllcn & BIT(8)) >> 1);
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp);
WREG_DAC(0x1a, 0x09);
msleep(20);
WREG_DAC(0x1a, 0x01);
}
static const struct mgag200_pll_funcs mgag200_pixpll_funcs_g200se_00 = {
.compute = mgag200_pixpll_compute_g200se_00,
.update = mgag200_pixpll_update_g200se_00,
};
static const struct mgag200_pll_funcs mgag200_pixpll_funcs_g200se_04 = {
.compute = mgag200_pixpll_compute_g200se_04,
.update = mgag200_pixpll_update_g200se_04,
};
/*
* G200WB
*/
static int mgag200_pixpll_compute_g200wb(struct mgag200_pll *pixpll, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 550000;
static const unsigned int vcomin = 150000;
static const unsigned int pllreffreq = 48000;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
for (testp = 1; testp < 9; testp++) {
if (clock * testp > vcomax)
continue;
if (clock * testp < vcomin)
continue;
for (testm = 1; testm < 17; testm++) {
for (testn = 1; testn < 151; testn++) {
computed = (pllreffreq * testn) / (testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
n = testn;
m = testm;
p = testp;
s = 0;
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void
mgag200_pixpll_update_g200wb(struct mgag200_pll *pixpll, const struct mgag200_pll_values *pixpllc)
{
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp;
int i, j, tmpcount, vcount;
struct mga_device *mdev = pixpll->mdev;
bool pll_locked = false;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = ((pixpllcn & BIT(8)) >> 1) | pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = ((pixpllcn & GENMASK(10, 9)) >> 3) | (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
for (i = 0; i <= 32 && pll_locked == false; i++) {
if (i > 0) {
WREG8(MGAREG_CRTC_INDEX, 0x1e);
tmp = RREG8(MGAREG_CRTC_DATA);
if (tmp < 0xff)
WREG8(MGAREG_CRTC_DATA, tmp+1);
}
/* set pixclkdis to 1 */
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_REMHEADCTL_CLKDIS;
WREG8(DAC_DATA, tmp);
/* select PLL Set C */
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= 0x3 << 2;
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN | 0x80;
WREG8(DAC_DATA, tmp);
udelay(500);
/* reset the PLL */
WREG8(DAC_INDEX, MGA1064_VREF_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~0x04;
WREG8(DAC_DATA, tmp);
udelay(50);
/* program pixel pll register */
WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp);
udelay(50);
/* turn pll on */
WREG8(DAC_INDEX, MGA1064_VREF_CTL);
tmp = RREG8(DAC_DATA);
tmp |= 0x04;
WREG_DAC(MGA1064_VREF_CTL, tmp);
udelay(500);
/* select the pixel pll */
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_REMHEADCTL_CLKSL_MSK;
tmp |= MGA1064_REMHEADCTL_CLKSL_PLL;
WREG8(DAC_DATA, tmp);
/* reset dotclock rate bit */
WREG8(MGAREG_SEQ_INDEX, 1);
tmp = RREG8(MGAREG_SEQ_DATA);
tmp &= ~0x8;
WREG8(MGAREG_SEQ_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
vcount = RREG8(MGAREG_VCOUNT);
for (j = 0; j < 30 && pll_locked == false; j++) {
tmpcount = RREG8(MGAREG_VCOUNT);
if (tmpcount < vcount)
vcount = 0;
if ((tmpcount - vcount) > 2)
pll_locked = true;
else
udelay(5);
}
}
WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_REMHEADCTL_CLKDIS;
WREG_DAC(MGA1064_REMHEADCTL, tmp);
}
static const struct mgag200_pll_funcs mgag200_pixpll_funcs_g200wb = {
.compute = mgag200_pixpll_compute_g200wb,
.update = mgag200_pixpll_update_g200wb,
};
/*
* G200EV
*/
static int mgag200_pixpll_compute_g200ev(struct mgag200_pll *pixpll, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 550000;
static const unsigned int vcomin = 150000;
static const unsigned int pllreffreq = 50000;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
for (testp = 16; testp > 0; testp--) {
if (clock * testp > vcomax)
continue;
if (clock * testp < vcomin)
continue;
for (testn = 1; testn < 257; testn++) {
for (testm = 1; testm < 17; testm++) {
computed = (pllreffreq * testn) /
(testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
n = testn;
m = testm;
p = testp;
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void
mgag200_pixpll_update_g200ev(struct mgag200_pll *pixpll, const struct mgag200_pll_values *pixpllc)
{
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp;
struct mga_device *mdev = pixpll->mdev;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= 0x3 << 2;
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_PLL_STAT);
tmp = RREG8(DAC_DATA);
WREG8(DAC_DATA, tmp & ~0x40);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
WREG_DAC(MGA1064_EV_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_EV_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_EV_PIX_PLLC_P, xpixpllcp);
udelay(50);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
udelay(500);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_PLL_STAT);
tmp = RREG8(DAC_DATA);
WREG8(DAC_DATA, tmp | 0x40);
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= (0x3 << 2);
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
}
static const struct mgag200_pll_funcs mgag200_pixpll_funcs_g200ev = {
.compute = mgag200_pixpll_compute_g200ev,
.update = mgag200_pixpll_update_g200ev,
};
/*
* G200EH
*/
static int mgag200_pixpll_compute_g200eh(struct mgag200_pll *pixpll, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 800000;
static const unsigned int vcomin = 400000;
static const unsigned int pllreffreq = 33333;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
for (testp = 16; testp > 0; testp >>= 1) {
if (clock * testp > vcomax)
continue;
if (clock * testp < vcomin)
continue;
for (testm = 1; testm < 33; testm++) {
for (testn = 17; testn < 257; testn++) {
computed = (pllreffreq * testn) / (testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
n = testn;
m = testm;
p = testp;
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void
mgag200_pixpll_update_g200eh(struct mgag200_pll *pixpll, const struct mgag200_pll_values *pixpllc)
{
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp;
int i, j, tmpcount, vcount;
struct mga_device *mdev = pixpll->mdev;
bool pll_locked = false;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = ((pixpllcn & BIT(8)) >> 1) | pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
for (i = 0; i <= 32 && pll_locked == false; i++) {
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= 0x3 << 2;
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
udelay(500);
WREG_DAC(MGA1064_EH_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_EH_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_EH_PIX_PLLC_P, xpixpllcp);
udelay(500);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
vcount = RREG8(MGAREG_VCOUNT);
for (j = 0; j < 30 && pll_locked == false; j++) {
tmpcount = RREG8(MGAREG_VCOUNT);
if (tmpcount < vcount)
vcount = 0;
if ((tmpcount - vcount) > 2)
pll_locked = true;
else
udelay(5);
}
}
}
static const struct mgag200_pll_funcs mgag200_pixpll_funcs_g200eh = {
.compute = mgag200_pixpll_compute_g200eh,
.update = mgag200_pixpll_update_g200eh,
};
/*
* G200EH3
*/
static int mgag200_pixpll_compute_g200eh3(struct mgag200_pll *pixpll, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 3000000;
static const unsigned int vcomin = 1500000;
static const unsigned int pllreffreq = 25000;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
testp = 0;
for (testm = 150; testm >= 6; testm--) {
if (clock * testm > vcomax)
continue;
if (clock * testm < vcomin)
continue;
for (testn = 120; testn >= 60; testn--) {
computed = (pllreffreq * testn) / testm;
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
n = testn + 1;
m = testm + 1;
p = testp + 1;
}
if (delta == 0)
break;
}
if (delta == 0)
break;
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static const struct mgag200_pll_funcs mgag200_pixpll_funcs_g200eh3 = {
.compute = mgag200_pixpll_compute_g200eh3,
.update = mgag200_pixpll_update_g200eh, // same as G200EH
};
/*
* G200ER
*/
static int mgag200_pixpll_compute_g200er(struct mgag200_pll *pixpll, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 1488000;
static const unsigned int vcomin = 1056000;
static const unsigned int pllreffreq = 48000;
static const unsigned int m_div_val[] = { 1, 2, 4, 8 };
unsigned int delta, tmpdelta;
int testr, testn, testm, testo;
unsigned int p, m, n, s;
unsigned int computed, vco;
m = n = p = s = 0;
delta = 0xffffffff;
for (testr = 0; testr < 4; testr++) {
if (delta == 0)
break;
for (testn = 5; testn < 129; testn++) {
if (delta == 0)
break;
for (testm = 3; testm >= 0; testm--) {
if (delta == 0)
break;
for (testo = 5; testo < 33; testo++) {
vco = pllreffreq * (testn + 1) /
(testr + 1);
if (vco < vcomin)
continue;
if (vco > vcomax)
continue;
computed = vco / (m_div_val[testm] * (testo + 1));
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
m = (testm | (testo << 3)) + 1;
n = testn + 1;
p = testr + 1;
s = testr;
}
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void
mgag200_pixpll_update_g200er(struct mgag200_pll *pixpll, const struct mgag200_pll_values *pixpllc)
{
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp;
struct mga_device *mdev = pixpll->mdev;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_REMHEADCTL_CLKDIS;
WREG8(DAC_DATA, tmp);
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= (0x3<<2) | 0xc0;
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
udelay(500);
WREG_DAC(MGA1064_ER_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_ER_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_ER_PIX_PLLC_P, xpixpllcp);
udelay(50);
}
static const struct mgag200_pll_funcs mgag200_pixpll_funcs_g200er = {
.compute = mgag200_pixpll_compute_g200er,
.update = mgag200_pixpll_update_g200er,
};
/*
* G200EW3
*/
static int mgag200_pixpll_compute_g200ew3(struct mgag200_pll *pixpll, long clock,
struct mgag200_pll_values *pixpllc)
{
static const unsigned int vcomax = 800000;
static const unsigned int vcomin = 400000;
static const unsigned int pllreffreq = 25000;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn, testp2;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
for (testp = 1; testp < 8; testp++) {
for (testp2 = 1; testp2 < 8; testp2++) {
if (testp < testp2)
continue;
if ((clock * testp * testp2) > vcomax)
continue;
if ((clock * testp * testp2) < vcomin)
continue;
for (testm = 1; testm < 26; testm++) {
for (testn = 32; testn < 2048 ; testn++) {
computed = (pllreffreq * testn) / (testm * testp * testp2);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
m = testm + 1;
n = testn + 1;
p = testp + 1;
s = testp2;
}
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static const struct mgag200_pll_funcs mgag200_pixpll_funcs_g200ew3 = {
.compute = mgag200_pixpll_compute_g200ew3,
.update = mgag200_pixpll_update_g200wb, // same as G200WB
};
/*
* PLL initialization
*/
int mgag200_pixpll_init(struct mgag200_pll *pixpll, struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
pixpll->mdev = mdev;
switch (mdev->type) {
case G200_PCI:
case G200_AGP:
pixpll->funcs = &mgag200_pixpll_funcs_g200;
break;
case G200_SE_A:
case G200_SE_B:
if (mdev->model.g200se.unique_rev_id >= 0x04)
pixpll->funcs = &mgag200_pixpll_funcs_g200se_04;
else
pixpll->funcs = &mgag200_pixpll_funcs_g200se_00;
break;
case G200_WB:
pixpll->funcs = &mgag200_pixpll_funcs_g200wb;
break;
case G200_EV:
pixpll->funcs = &mgag200_pixpll_funcs_g200ev;
break;
case G200_EH:
pixpll->funcs = &mgag200_pixpll_funcs_g200eh;
break;
case G200_EH3:
pixpll->funcs = &mgag200_pixpll_funcs_g200eh3;
break;
case G200_ER:
pixpll->funcs = &mgag200_pixpll_funcs_g200er;
break;
case G200_EW3:
pixpll->funcs = &mgag200_pixpll_funcs_g200ew3;
break;
default:
drm_err(dev, "unknown device type %d\n", mdev->type);
return -ENODEV;
}
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment