diff mbox

[07/14] drm/mgag200: Add support for MATROX PCI device IDs 0x520 and 0x521

Message ID 20170718144320.8354-8-tiwai@suse.de (mailing list archive)
State New, archived
Headers show

Commit Message

Takashi Iwai July 18, 2017, 2:43 p.m. UTC
From: Egbert Eich <eich@suse.de>

Add two more models G200_PCI and G200 for PCI device IDs 0x520 and
0x521, respectively.  They need to retrieve the reference clock and
pclk min/max values from BIOS, and set up the PLLs accordingly.

Signed-off-by: Egbert Eich <eich@suse.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 drivers/gpu/drm/mgag200/mgag200_drv.c  |   2 +
 drivers/gpu/drm/mgag200/mgag200_drv.h  |   7 ++
 drivers/gpu/drm/mgag200/mgag200_main.c | 113 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/mgag200/mgag200_mode.c |  81 +++++++++++++++++++++++
 4 files changed, 203 insertions(+)

Comments

Dave Airlie July 20, 2017, 4:17 a.m. UTC | #1
On 19 July 2017 at 00:43, Takashi Iwai <tiwai@suse.de> wrote:
> From: Egbert Eich <eich@suse.de>
>
> Add two more models G200_PCI and G200 for PCI device IDs 0x520 and
> 0x521, respectively.  They need to retrieve the reference clock and
> pclk min/max values from BIOS, and set up the PLLs accordingly.

Is there any advantage in supporting these GPUs?

The main idea of the mgag200 driver is for unaccelerated support for
server GPUs.

These look like ancient desktop GPUs, I can't imagine we see them
shipping in any modern server hw, and also I'm guessing they had basic
3D accel features that the old UMS mga driver would "support".

Dave.
Takashi Iwai July 20, 2017, 9:47 a.m. UTC | #2
On Thu, 20 Jul 2017 06:17:58 +0200,
Dave Airlie wrote:
> 
> On 19 July 2017 at 00:43, Takashi Iwai <tiwai@suse.de> wrote:
> > From: Egbert Eich <eich@suse.de>
> >
> > Add two more models G200_PCI and G200 for PCI device IDs 0x520 and
> > 0x521, respectively.  They need to retrieve the reference clock and
> > pclk min/max values from BIOS, and set up the PLLs accordingly.
> 
> Is there any advantage in supporting these GPUs?

Heh, you are suggesting that KMS support has no merit? ;)

> The main idea of the mgag200 driver is for unaccelerated support for
> server GPUs.
> 
> These look like ancient desktop GPUs, I can't imagine we see them
> shipping in any modern server hw, and also I'm guessing they had basic
> 3D accel features that the old UMS mga driver would "support".

Right, but I thought we want to move on to support KMS.
If there is another big picture to glean such leftover devices, I'd
love to follow that.  But this patch itself is simple enough and it
can't regress the other existing mgag200 devices, so it's still worth
to consider, IMO.


thanks,

Takashi
Egbert Eich July 20, 2017, 3:05 p.m. UTC | #3
Takashi Iwai writes:
 > On Thu, 20 Jul 2017 06:17:58 +0200,
 > Dave Airlie wrote:
 > > 
 > > On 19 July 2017 at 00:43, Takashi Iwai <tiwai@suse.de> wrote:
 > > > From: Egbert Eich <eich@suse.de>
 > > >
 > > > Add two more models G200_PCI and G200 for PCI device IDs 0x520 and
 > > > 0x521, respectively.  They need to retrieve the reference clock and
 > > > pclk min/max values from BIOS, and set up the PLLs accordingly.
 > > 
 > > Is there any advantage in supporting these GPUs?
 > 
 > Heh, you are suggesting that KMS support has no merit? ;)

The merit of this patch was that I could test a lot of functionality of
this  driver without having access to a machine with a built-in mgag200.
Such servers tend not to be home office friendly ;)
Instead I was able to pull a vintage g200 discrete PCI card from my 
collection and shove it into a test machine.

So there was some merit to have this patch and it was small enough to
preserve it.

Cheers,
	Egbert.
diff mbox

Patch

diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
index 9ac007880328..03258e9fc6c0 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.c
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
@@ -29,6 +29,8 @@  module_param_named(modeset, mgag200_modeset, int, 0400);
 static struct drm_driver driver;
 
 static const struct pci_device_id pciidlist[] = {
+	{ PCI_VENDOR_ID_MATROX, 0x520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_PCI },
+	{ PCI_VENDOR_ID_MATROX, 0x521, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200 },
 	{ PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A },
 	{ PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B },
 	{ PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV },
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h
index 2ba4060e3363..1ba559c93b8f 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.h
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.h
@@ -175,6 +175,8 @@  struct mga_mc {
 };
 
 enum mga_type {
+	G200_PCI,
+	G200,
 	G200_SE_A,
 	G200_SE_B,
 	G200_WB,
@@ -219,6 +221,11 @@  struct mga_device {
 
 	/* SE model number stored in reg 0x1e24 */
 	u32 unique_rev_id;
+	struct  {
+		long ref_clk;
+		long pclk_min;
+		long pclk_max;
+	} bios;
 };
 
 
diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c
index cf3a602f453f..87c5469d06a7 100644
--- a/drivers/gpu/drm/mgag200/mgag200_main.c
+++ b/drivers/gpu/drm/mgag200/mgag200_main.c
@@ -155,6 +155,116 @@  static int mga_vram_init(struct mga_device *mdev)
 	return 0;
 }
 
+#define MGA_BIOS_OFFSET 0x7ffc
+
+static void mgag200_interpret_bios(struct mga_device *mdev,
+				   unsigned char __iomem *bios,
+				   size_t size)
+{
+	static const unsigned int expected_length[6] = {
+		0, 64, 64, 64, 128, 128
+	};
+	unsigned char __iomem *pins;
+	unsigned int pins_len, version;
+	int offset;
+	int tmp;
+
+	if (size < MGA_BIOS_OFFSET + 1)
+		return;
+
+	if (bios[45] != 'M' || bios[46] != 'A' || bios[47] != 'T' ||
+	    bios[48] != 'R' || bios[49] != 'O' || bios[50] != 'X')
+		return;
+
+	offset = (bios[MGA_BIOS_OFFSET + 1] << 8) | bios[MGA_BIOS_OFFSET];
+
+	if (offset + 5 > size)
+		return;
+
+	pins = bios + offset;
+	if (pins[0] == 0x2e && pins[1] == 0x41) {
+		version = pins[5];
+		pins_len = pins[2];
+	} else {
+		version = 1;
+		pins_len = pins[0] + (pins[1] << 8);
+	}
+
+	if (version < 1 || version > 5) {
+		DRM_WARN("Unknown BIOS PInS version: %d\n", version);
+		return;
+	}
+	if (pins_len != expected_length[version]) {
+		DRM_WARN("Unexpected BIOS PInS size: %d expeced: %d\n",
+			 pins_len, expected_length[version]);
+		return;
+	}
+
+	if (offset + pins_len > size)
+		return;
+
+	DRM_DEBUG_KMS("MATROX BIOS PInS version %d size: %d found\n",
+		      version, pins_len);
+
+	switch (version) {
+	case 1:
+		tmp = pins[24] + (pins[25] << 8);
+		if (tmp)
+			mdev->bios.pclk_max = tmp * 10;
+		break;
+	case 2:
+		if (pins[41] != 0xff)
+			mdev->bios.pclk_max = (pins[41] + 100) * 1000;
+		break;
+	case 3:
+		if (pins[36] != 0xff)
+			mdev->bios.pclk_max = (pins[36] + 100) * 1000;
+		if (pins[52] & 0x20)
+			mdev->bios.ref_clk = 14318;
+		break;
+	case 4:
+		if (pins[39] != 0xff)
+			mdev->bios.pclk_max = pins[39] * 4 * 1000;
+		if (pins[92] & 0x01)
+			mdev->bios.ref_clk = 14318;
+		break;
+	case 5:
+		tmp = pins[4] ? 8000 : 6000;
+		if (pins[123] != 0xff)
+			mdev->bios.pclk_min = pins[123] * tmp;
+		if (pins[38] != 0xff)
+			mdev->bios.pclk_max = pins[38] * tmp;
+		if (pins[110] & 0x01)
+			mdev->bios.ref_clk = 14318;
+		break;
+	default:
+		break;
+	}
+}
+
+static void mgag200_probe_bios(struct mga_device *mdev)
+{
+	unsigned char __iomem *bios;
+	size_t size;
+
+	mdev->bios.pclk_min = 50000;
+	mdev->bios.pclk_max = 230000;
+	mdev->bios.ref_clk = 27050;
+
+	bios = pci_map_rom(mdev->dev->pdev, &size);
+	if (!bios)
+		return;
+
+	if (size != 0 && bios[0] == 0x55 && bios[1] == 0xaa)
+		mgag200_interpret_bios(mdev, bios, size);
+
+	pci_unmap_rom(mdev->dev->pdev, bios);
+
+	DRM_DEBUG_KMS("pclk_min: %ld pclk_max: %ld ref_clk: %ld\n",
+		      mdev->bios.pclk_min, mdev->bios.pclk_max,
+		      mdev->bios.ref_clk);
+}
+
 static int mgag200_device_init(struct drm_device *dev,
 			       uint32_t flags)
 {
@@ -187,6 +297,9 @@  static int mgag200_device_init(struct drm_device *dev,
 	if (IS_G200_SE(mdev))
 		mdev->unique_rev_id = RREG32(0x1e24);
 
+	if (mdev->type == G200_PCI || mdev->type == G200)
+		mgag200_probe_bios(mdev);
+
 	ret = mga_vram_init(mdev);
 	if (ret)
 		return ret;
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index afec8f190ee3..ba7ee93b57a9 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -92,6 +92,72 @@  static inline void mga_wait_busy(struct mga_device *mdev)
 	} while ((status & 0x01) && time_before(jiffies, timeout));
 }
 
+static int mga_set_plls(struct mga_device *mdev, long clock)
+{
+	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 testm, testn;
+	u8 n = 0, m = 0, p, s;
+	long f_vco;
+	long computed;
+	long delta, tmp_delta;
+	long ref_clk = mdev->bios.ref_clk;
+	long p_clk_min = mdev->bios.pclk_min;
+	long p_clk_max =  mdev->bios.pclk_max;
+
+	if (clock > p_clk_max) {
+		DRM_WARN("Pixel Clock %ld too high\n", clock);
+		return 1;
+	}
+
+	if (clock <  p_clk_min >> 3)
+		clock = p_clk_min >> 3;
+
+	f_vco = clock;
+	for (p = 0;
+	     p <= post_div_max && f_vco < p_clk_min;
+	     p = (p << 1) + 1, f_vco <<= 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;
+				n = testn;
+			}
+		}
+	}
+	f_vco = ref_clk * (n + 1) / (m + 1);
+	if (f_vco < 100000)
+		s = 0;
+	else if (f_vco < 140000)
+		s = 1;
+	else if (f_vco < 180000)
+		s = 2;
+	else
+		s = 3;
+
+	DRM_DEBUG_KMS("clock: %ld vco: %ld m: %d n: %d p: %d s: %d\n",
+		      clock, f_vco, m, n, p, s);
+
+	WREG_DAC(MGA1064_PIX_PLLC_M, m);
+	WREG_DAC(MGA1064_PIX_PLLC_N, n);
+	WREG_DAC(MGA1064_PIX_PLLC_P, (p | (s << 3)));
+
+	return 0;
+}
+
 #define P_ARRAY_SIZE 9
 
 static int mga_g200se_set_plls(struct mga_device *mdev, long clock)
@@ -698,6 +764,10 @@  static int mga_g200er_set_plls(struct mga_device *mdev, long clock)
 static int mga_crtc_set_plls(struct mga_device *mdev, long clock)
 {
 	switch(mdev->type) {
+	case G200:
+	case G200_PCI:
+		return mga_set_plls(mdev, clock);
+		break;
 	case G200_SE_A:
 	case G200_SE_B:
 		return mga_g200se_set_plls(mdev, clock);
@@ -944,6 +1014,17 @@  static int mga_crtc_mode_set(struct drm_crtc *crtc,
 	bppshift = mdev->bpp_shifts[fb->format->cpp[0] - 1];
 
 	switch (mdev->type) {
+	case G200:
+	case G200_PCI:
+		dacvalue[MGA1064_SYS_PLL_M] = 0x04;
+		dacvalue[MGA1064_SYS_PLL_N] = 0x2D;
+		dacvalue[MGA1064_SYS_PLL_P] = 0x19;
+		if (mdev->has_sdram)
+			option = 0x40499121;
+		else
+			option = 0x4049cd21;
+		option2 = 0x00008000;
+		break;
 	case G200_SE_A:
 	case G200_SE_B:
 		dacvalue[MGA1064_VREF_CTL] = 0x03;