Skip to content

Commit

Permalink
drm/mgag200: Provide per-device callbacks for PIXPLLC
Browse files Browse the repository at this point in the history
Move the PIXPLLC code into per-model source files and wire it up
with per-model callbacks. No functional changes.

The PIXPLLC pixel-clock is part of the CRTC, but really separate
hardware that varies with each model of the G200. Move the PIXPLLC
code for each model into the per-model source file and call it from
CRTC helpers via device functions.

This allows to remove struct mgag200_pll and the related code. The
new callbacks behave like the CRTC's atomic_check and atomic_enable
functions.

v3:
	* clean up style

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Jocelyn Falempe <jfalempe@redhat.com>
Tested-by: Jocelyn Falempe <jfalempe@redhat.com>
Acked-by: Sam Ravnborg <sam@ravnborg.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20220728124103.30159-12-tzimmermann@suse.de
  • Loading branch information
Thomas Zimmermann committed Jul 29, 2022
1 parent 8aeeb31 commit 877507b
Show file tree
Hide file tree
Showing 12 changed files with 1,026 additions and 1,029 deletions.
3 changes: 1 addition & 2 deletions drivers/gpu/drm/mgag200/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ mgag200-y := \
mgag200_g200se.o \
mgag200_g200wb.o \
mgag200_i2c.o \
mgag200_mode.o \
mgag200_pll.o
mgag200_mode.o

obj-$(CONFIG_DRM_MGAG200) += mgag200.o
32 changes: 16 additions & 16 deletions drivers/gpu/drm/mgag200/mgag200_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@
#define MGAG200_MAX_FB_WIDTH 4096

struct mga_device;
struct mgag200_pll;

/*
* Stores parameters for programming the PLLs
Expand All @@ -175,17 +174,6 @@ struct mgag200_pll_values {
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;
};

struct mgag200_crtc_state {
struct drm_crtc_state base;

Expand Down Expand Up @@ -274,6 +262,20 @@ struct mgag200_device_funcs {
* a new display mode.
*/
void (*enable_vidrst)(struct mga_device *mdev);

/*
* Validate that the given state can be programmed into PIXPLLC. On
* success, the calculated parameters should be stored in the CRTC's
* state in struct @mgag200_crtc_state.pixpllc.
*/
int (*pixpllc_atomic_check)(struct drm_crtc *crtc, struct drm_atomic_state *new_state);

/*
* Program PIXPLLC from the CRTC state. The parameters should have been
* stored in struct @mgag200_crtc_state.pixpllc by the corresponding
* implementation of @pixpllc_atomic_check.
*/
void (*pixpllc_atomic_update)(struct drm_crtc *crtc, struct drm_atomic_state *old_state);
};

struct mga_device {
Expand All @@ -292,7 +294,6 @@ struct mga_device {

enum mga_type type;

struct mgag200_pll pixpll;
struct drm_plane primary_plane;
struct drm_crtc crtc;
struct drm_encoder encoder;
Expand Down Expand Up @@ -346,11 +347,13 @@ struct mga_device *mgag200_g200_device_create(struct pci_dev *pdev, const struct
struct mga_device *mgag200_g200se_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
enum mga_type type);
void mgag200_g200wb_init_registers(struct mga_device *mdev);
void mgag200_g200wb_pixpllc_atomic_update(struct drm_crtc *crtc, struct drm_atomic_state *old_state);
struct mga_device *mgag200_g200wb_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
enum mga_type type);
struct mga_device *mgag200_g200ev_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
enum mga_type type);
void mgag200_g200eh_init_registers(struct mga_device *mdev);
void mgag200_g200eh_pixpllc_atomic_update(struct drm_crtc *crtc, struct drm_atomic_state *old_state);
struct mga_device *mgag200_g200eh_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
enum mga_type type);
struct mga_device *mgag200_g200eh3_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
Expand All @@ -372,7 +375,4 @@ void mgag200_bmc_enable_vidrst(struct mga_device *mdev);
/* mgag200_i2c.c */
int mgag200_i2c_init(struct mga_device *mdev, struct mga_i2c_chan *i2c);

/* mgag200_pll.c */
int mgag200_pixpll_init(struct mgag200_pll *pixpll, struct mga_device *mdev);

#endif /* __MGAG200_DRV_H__ */
109 changes: 109 additions & 0 deletions drivers/gpu/drm/mgag200/mgag200_g200.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <linux/pci.h>
#include <linux/vmalloc.h>

#include <drm/drm_atomic.h>
#include <drm/drm_drv.h>

#include "mgag200_drv.h"
Expand Down Expand Up @@ -53,6 +54,112 @@ static void mgag200_g200_init_registers(struct mgag200_g200_device *g200)
mgag200_init_registers(mdev);
}

/*
* PIXPLLC
*/

static int mgag200_g200_pixpllc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state)
{
static const int post_div_max = 7;
static const int in_div_min = 1;
static const int in_div_max = 6;
static const int feed_div_min = 7;
static const int feed_div_max = 127;

struct drm_device *dev = crtc->dev;
struct mgag200_g200_device *g200 = to_mgag200_g200_device(dev);
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
long clock = new_crtc_state->mode.clock;
struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
u8 testp, testm, testn;
u8 n = 0, m = 0, p, s;
long f_vco;
long computed;
long delta, tmp_delta;
long ref_clk = g200->ref_clk;
long p_clk_min = g200->pclk_min;
long p_clk_max = 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_g200_pixpllc_atomic_update(struct drm_crtc *crtc,
struct drm_atomic_state *old_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
struct drm_crtc_state *crtc_state = crtc->state;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->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);
}

/*
* DRM Device
*/
Expand Down Expand Up @@ -184,6 +291,8 @@ static void mgag200_g200_init_refclk(struct mgag200_g200_device *g200)
}

static const struct mgag200_device_funcs mgag200_g200_device_funcs = {
.pixpllc_atomic_check = mgag200_g200_pixpllc_atomic_check,
.pixpllc_atomic_update = mgag200_g200_pixpllc_atomic_update,
};

struct mga_device *mgag200_g200_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
Expand Down
131 changes: 131 additions & 0 deletions drivers/gpu/drm/mgag200/mgag200_g200eh.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only

#include <linux/delay.h>
#include <linux/pci.h>

#include <drm/drm_atomic.h>
#include <drm/drm_drv.h>

#include "mgag200_drv.h"
Expand Down Expand Up @@ -30,6 +32,133 @@ void mgag200_g200eh_init_registers(struct mga_device *mdev)
mgag200_init_registers(mdev);
}

/*
* PIXPLLC
*/

static int mgag200_g200eh_pixpllc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_state *new_state)
{
static const unsigned int vcomax = 800000;
static const unsigned int vcomin = 400000;
static const unsigned int pllreffreq = 33333;

struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
long clock = new_crtc_state->mode.clock;
struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
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;
}

void mgag200_g200eh_pixpllc_atomic_update(struct drm_crtc *crtc,
struct drm_atomic_state *old_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
struct drm_crtc_state *crtc_state = crtc->state;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->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);
}
}
}

/*
* DRM device
*/
Expand All @@ -38,6 +167,8 @@ static const struct mgag200_device_info mgag200_g200eh_device_info =
MGAG200_DEVICE_INFO_INIT(2048, 2048, 37500, false, 1, 0, false);

static const struct mgag200_device_funcs mgag200_g200eh_device_funcs = {
.pixpllc_atomic_check = mgag200_g200eh_pixpllc_atomic_check,
.pixpllc_atomic_update = mgag200_g200eh_pixpllc_atomic_update,
};

struct mga_device *mgag200_g200eh_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
Expand Down
Loading

0 comments on commit 877507b

Please sign in to comment.