diff mbox

[RFC,13/21] DRM: Add VIA DRM driver

Message ID alpine.LFD.2.03.1306081752140.18671@infradead.org (mailing list archive)
State New, archived
Headers show

Commit Message

James Simmons June 8, 2013, 4:52 p.m. UTC
commit 991c37448ff8403727f12af331eb164eb5ed4048
Author: James Simmons <jsimmons@infradead.org>
Date:   Sat Jun 8 11:12:36 2013 -0400

    via: VIA kms frame buffer support
    
    Each version of hardware has a unquie way to detect the amount of
    video ram. We detect the amount for all known device. Here we
    implement the drm_framebuffer user land interface hooks as well
    as setup a framebuffer for the fbdev emulator.
    
    Signed-Off-by: James Simmons <jsimmons@infradead.org>
diff mbox

Patch

diff --git a/drivers/gpu/drm/via/via_fb.c b/drivers/gpu/drm/via/via_fb.c
new file mode 100644
index 0000000..5cca9f2
--- /dev/null
+++ b/drivers/gpu/drm/via/via_fb.c
@@ -0,0 +1,1267 @@ 
+/*
+ * Copyright 2012 James Simmons <jsimmons@infradead.org>. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHOR(S) OR COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include "drmP.h"
+#include "via_drv.h"
+#include "drm_fb_helper.h"
+#include "drm_crtc_helper.h"
+
+struct ttm_fb_helper {
+	struct drm_fb_helper base;
+	struct ttm_bo_kmap_obj kmap;
+};
+
+static int
+cle266_mem_type(struct drm_via_private *dev_priv, struct pci_dev *bridge)
+{
+	u8 type, fsb, freq;
+	int ret;
+
+	ret = pci_read_config_byte(bridge, 0x54, &fsb);
+	if (ret)
+		return ret;
+	ret = pci_read_config_byte(bridge, 0x69, &freq);
+	if (ret)
+		return ret;
+
+	freq >>= 6;
+	fsb >>= 6;
+
+	/* FSB frequency */
+	switch (fsb) {
+	case 0x01: /* 100MHz */
+		switch (freq) {
+		case 0x00:
+			freq = 100;
+			break;
+		case 0x01:
+			freq = 133;
+			break;
+		case 0x02:
+			freq = 66;
+			break;
+		default:
+			freq = 0;
+			break;
+		}
+		break;
+
+	case 0x02: /* 133 MHz */
+	case 0x03:
+		switch (freq) {
+		case 0x00:
+			freq = 133;
+			break;
+		case 0x02:
+			freq = 100;
+			break;
+		default:
+			freq = 0;
+			break;
+		}
+		break;
+	default:
+		freq = 0;
+		break;
+	}
+
+	ret = pci_read_config_byte(bridge, 0x60, &fsb);
+	if (ret)
+		return ret;
+	ret = pci_read_config_byte(bridge, 0xE3, &type);
+	if (ret)
+		return ret;
+
+	/* On bank 2/3 */
+	if (type & 0x02)
+		fsb >>= 2;
+
+	/* Memory type */
+	switch (fsb & 0x03) {
+	case 0x00: /* SDR */
+		switch (freq) {
+		case 66:
+			dev_priv->vram_type = VIA_MEM_SDR66;
+			break;
+		case 100:
+			dev_priv->vram_type = VIA_MEM_SDR100;
+			break;
+		case 133:
+			dev_priv->vram_type = VIA_MEM_SDR133;
+		default:
+			break;
+		}
+		break;
+
+	case 0x02: /* DDR */
+		switch (freq) {
+		case 100:
+			dev_priv->vram_type = VIA_MEM_DDR_200;
+			break;
+		case 133:
+			dev_priv->vram_type = VIA_MEM_DDR_266;
+		default:
+			break;
+		}
+	default:
+		break;
+	}
+	return ret;
+}
+
+static int
+km400_mem_type(struct drm_via_private *dev_priv, struct pci_dev *bridge)
+{
+	u8 fsb, freq, rev;
+	int ret;
+
+	ret = pci_read_config_byte(bridge, 0xF6, &rev);
+	if (ret)
+		return ret;
+	ret = pci_read_config_byte(bridge, 0x54, &fsb);
+	if (ret)
+		return ret;
+	ret = pci_read_config_byte(bridge, 0x69, &freq);
+	if (ret)
+		return ret;
+
+	freq >>= 6;
+	fsb >>= 6;
+
+	/* KM400 */
+	if (rev < 0x80) {
+		/* FSB frequency */
+		switch (fsb) {
+		case 0x00:
+			switch (freq) {
+			case 0x00:
+				dev_priv->vram_type = VIA_MEM_DDR_200;
+				break;
+			case 0x01:
+				dev_priv->vram_type = VIA_MEM_DDR_266;
+				break;
+			case 0x02:
+				dev_priv->vram_type = VIA_MEM_DDR_400;
+				break;
+			case 0x03:
+				dev_priv->vram_type = VIA_MEM_DDR_333;
+			default:
+				break;
+			}
+			break;
+
+		case 0x01:
+			switch (freq) {
+			case 0x00:
+				dev_priv->vram_type = VIA_MEM_DDR_266;
+				break;
+			case 0x01:
+				dev_priv->vram_type = VIA_MEM_DDR_333;
+				break;
+			case 0x02:
+				dev_priv->vram_type = VIA_MEM_DDR_400;
+			default:
+				break;
+			}
+			break;
+
+		case 0x02:
+		case 0x03:
+			switch (freq) {
+			case 0x00:
+				dev_priv->vram_type = VIA_MEM_DDR_333;
+				break;
+			case 0x02:
+				dev_priv->vram_type = VIA_MEM_DDR_400;
+				break;
+			case 0x03:
+				dev_priv->vram_type = VIA_MEM_DDR_266;
+			default:
+				break;
+			}
+		default:
+			break;
+		}
+	} else {
+		/* KM400A */
+		ret = pci_read_config_byte(bridge, 0x67, &rev);
+		if (ret)
+			return ret;
+		if (rev & 0x80)
+			freq |= 0x04;
+
+		switch (fsb) {
+		case 0x00:
+			switch (freq) {
+			case 0x00:
+				dev_priv->vram_type = VIA_MEM_DDR_200;
+				break;
+			case 0x01:
+				dev_priv->vram_type = VIA_MEM_DDR_266;
+				break;
+			case 0x03:
+				dev_priv->vram_type = VIA_MEM_DDR_333;
+				break;
+			case 0x07:
+				dev_priv->vram_type = VIA_MEM_DDR_400;
+				break;
+			default:
+				dev_priv->vram_type = VIA_MEM_NONE;
+				break;
+			}
+			break;
+
+		case 0x01:
+			switch (freq) {
+			case 0x00:
+				dev_priv->vram_type = VIA_MEM_DDR_266;
+				break;
+			case 0x01:
+				dev_priv->vram_type = VIA_MEM_DDR_333;
+				break;
+			case 0x03:
+				dev_priv->vram_type = VIA_MEM_DDR_400;
+			default:
+				break;
+			}
+			break;
+
+		case 0x02:
+			switch (freq) {
+			case 0x00:
+				dev_priv->vram_type = VIA_MEM_DDR_400;
+				break;
+			case 0x04:
+				dev_priv->vram_type = VIA_MEM_DDR_333;
+				break;
+			case 0x06:
+				dev_priv->vram_type = VIA_MEM_DDR_266;
+			default:
+				break;
+			}
+			break;
+
+		case 0x03:
+			switch (freq) {
+			case 0x00:
+				dev_priv->vram_type = VIA_MEM_DDR_333;
+				break;
+			case 0x01:
+				dev_priv->vram_type = VIA_MEM_DDR_400;
+				break;
+			case 0x04:
+				dev_priv->vram_type = VIA_MEM_DDR_266;
+			default:
+				break;
+			}
+		default:
+			break;
+		}
+	}
+	return ret;
+}
+
+static int
+p4m800_mem_type(struct drm_via_private *dev_priv, struct pci_bus *bus,
+		struct pci_dev *fn3)
+{
+	struct pci_dev *fn4 = pci_get_slot(bus, PCI_DEVFN(0, 4));
+	int ret, freq = 0;
+	u8 type, fsb;
+
+	/* VIA Scratch region */
+	ret = pci_read_config_byte(fn4, 0xF3, &fsb);
+	if (ret) {
+		pci_dev_put(fn4);
+		return ret;
+	}
+
+	switch (fsb >> 5) {
+	case 0:
+		freq = 3; /* 100 MHz */
+		break;
+	case 1:
+		freq = 4; /* 133 MHz */
+		break;
+	case 3:
+		freq = 5; /* 166 MHz */
+		break;
+	case 2:
+		freq = 6; /* 200 MHz */
+		break;
+	case 4:
+		freq = 7; /* 233 MHz */
+	default:
+		break;
+	}
+	pci_dev_put(fn4);
+
+	ret = pci_read_config_byte(fn3, 0x68, &type);
+	if (ret)
+		return ret;
+	type &= 0x0f;
+
+	if (type & 0x02)
+		freq -= type >> 2;
+	else {
+		freq += type >> 2;
+		if (type & 0x01)
+			freq++;
+	}
+
+	switch (freq) {
+	case 0x03:
+		dev_priv->vram_type = VIA_MEM_DDR_200;
+		break;
+	case 0x04:
+		dev_priv->vram_type = VIA_MEM_DDR_266;
+		break;
+	case 0x05:
+		dev_priv->vram_type = VIA_MEM_DDR_333;
+		break;
+	case 0x06:
+		dev_priv->vram_type = VIA_MEM_DDR_400;
+	default:
+		break;
+	}
+	return ret;
+}
+
+static int
+km8xx_mem_type(struct drm_via_private *dev_priv)
+{
+	struct pci_dev *dram, *misc = NULL;
+	int ret = -ENXIO;
+	u8 type, tmp;
+
+	dram = pci_get_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MEMCTL, NULL);
+	if (dram) {
+		misc = pci_get_device(PCI_VENDOR_ID_AMD,
+					PCI_DEVICE_ID_AMD_K8_NB_MISC, NULL);
+
+		ret = pci_read_config_byte(misc, 0xFD, &type);
+		if (type) {
+			pci_read_config_byte(dram, 0x94, &type);
+			switch (type & 0x03) {
+			case 0x00:
+				dev_priv->vram_type = VIA_MEM_DDR2_400;
+				break;
+			case 0x01:
+				dev_priv->vram_type = VIA_MEM_DDR2_533;
+				break;
+			case 0x02:
+				dev_priv->vram_type = VIA_MEM_DDR2_667;
+				break;
+			case 0x03:
+				dev_priv->vram_type = VIA_MEM_DDR2_800;
+			default:
+				break;
+			}
+		} else {
+			ret = pci_read_config_byte(dram, 0x96, &type);
+			if (ret)
+				return ret;
+			type >>= 4;
+			type &= 0x07;
+
+			switch (type) {
+			case 0x00:
+				dev_priv->vram_type = VIA_MEM_DDR_200;
+				break;
+			case 0x02:
+				dev_priv->vram_type = VIA_MEM_DDR_266;
+				break;
+			case 0x05:
+				dev_priv->vram_type = VIA_MEM_DDR_333;
+				break;
+			case 0x07:
+				dev_priv->vram_type = VIA_MEM_DDR_400;
+			default:
+				break;
+			}
+		}
+	}
+
+	/* AMD 10h DRAM Controller */
+	dram = pci_get_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_10H_NB_DRAM, NULL);
+	if (dram) {
+		ret = pci_read_config_byte(misc, 0x94, &tmp);
+		if (ret)
+			return ret;
+		ret = pci_read_config_byte(misc, 0x95, &type);
+		if (ret)
+			return ret;
+
+		if (type & 0x01) {	/* DDR3 */
+			switch(tmp & 0x07) {
+			case 0x03:
+				dev_priv->vram_type = VIA_MEM_DDR3_800;
+				break;
+			case 0x04:
+				dev_priv->vram_type = VIA_MEM_DDR3_1066;
+				break;
+			case 0x05:
+				dev_priv->vram_type = VIA_MEM_DDR3_1333;
+				break;
+			case 0x06:
+				dev_priv->vram_type = VIA_MEM_DDR3_1600;
+			default:
+				break;
+			}
+		} else {		/* DDR2 */
+			switch(tmp & 0x07) {
+			case 0x00:
+				dev_priv->vram_type = VIA_MEM_DDR2_400;
+				break;
+			case 0x01:
+				dev_priv->vram_type = VIA_MEM_DDR2_533;
+				break;
+			case 0x02:
+				dev_priv->vram_type = VIA_MEM_DDR2_667;
+				break;
+			case 0x03:
+				dev_priv->vram_type = VIA_MEM_DDR2_800;
+				break;
+			case 0x04:
+				dev_priv->vram_type = VIA_MEM_DDR2_1066;
+			default:
+				break;
+			}
+		}
+	}
+
+	/* AMD 11h DRAM Controller */
+	dram = pci_get_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_11H_NB_DRAM, NULL);
+	if (dram) {
+		ret = pci_read_config_byte(misc, 0x94, &type);
+		if (ret)
+			return ret;
+
+		switch(tmp & 0x07) {
+		case 0x01:
+			dev_priv->vram_type = VIA_MEM_DDR2_533;
+			break;
+		case 0x02:
+			dev_priv->vram_type = VIA_MEM_DDR2_667;
+			break;
+		case 0x03:
+			dev_priv->vram_type = VIA_MEM_DDR2_800;
+		default:
+			break;
+		}
+	}
+	return ret;
+}
+
+static int
+cn400_mem_type(struct drm_via_private *dev_priv, struct pci_bus *bus,
+		struct pci_dev *fn3)
+{
+	struct pci_dev *fn2 = pci_get_slot(bus, PCI_DEVFN(0, 2));
+	int ret, freq = 0;
+	u8 type, fsb;
+
+	ret = pci_read_config_byte(fn2, 0x54, &fsb);
+	if (ret) {
+		pci_dev_put(fn2);
+		return ret;
+	}
+
+	switch (fsb >> 5) {
+	case 0:
+		freq = 3; /* 100 MHz */
+		break;
+	case 1:
+		freq = 4; /* 133 MHz */
+		break;
+	case 3:
+		freq = 5; /* 166 MHz */
+		break;
+	case 2:
+		freq = 6; /* 200 MHz */
+		break;
+	case 4:
+		freq = 7; /* 233 MHz */
+	default:
+		break;
+	}
+	pci_dev_put(fn2);
+
+	ret = pci_read_config_byte(fn3, 0x68, &type);
+	if (ret)
+		return ret;
+	type &= 0x0f;
+
+	if (type & 0x01)
+		freq += 1 + (type >> 2);
+	else
+		freq -= 1 + (type >> 2);
+
+	switch (freq) {
+	case 0x03:
+		dev_priv->vram_type = VIA_MEM_DDR_200;
+		break;
+	case 0x04:
+		dev_priv->vram_type = VIA_MEM_DDR_266;
+		break;
+	case 0x05:
+		dev_priv->vram_type = VIA_MEM_DDR_333;
+		break;
+	case 0x06:
+		dev_priv->vram_type = VIA_MEM_DDR_400;
+	default:
+		break;
+	}
+	return ret;
+}
+
+static int
+cn700_mem_type(struct drm_via_private *dev_priv, struct pci_dev *fn3)
+{
+	int ret;
+	u8 tmp;
+
+	ret = pci_read_config_byte(fn3, 0x90, &tmp);
+	if (!ret) {
+		switch(tmp & 0x07) {
+		case 0x00:
+			dev_priv->vram_type = VIA_MEM_DDR_200;
+			break;
+		case 0x01:
+			dev_priv->vram_type = VIA_MEM_DDR_266;
+			break;
+		case 0x02:
+			dev_priv->vram_type = VIA_MEM_DDR_333;
+			break;
+		case 0x03:
+			dev_priv->vram_type = VIA_MEM_DDR_400;
+			break;
+		case 0x04:
+			dev_priv->vram_type = VIA_MEM_DDR2_400;
+			break;
+		case 0x05:
+			dev_priv->vram_type = VIA_MEM_DDR2_533;
+		default:
+			break;
+		}
+	}
+	return ret;
+}
+
+static int
+cx700_mem_type(struct drm_via_private *dev_priv, struct pci_dev *fn3)
+{
+	u8 type, clock;
+	int ret;
+
+	ret = pci_read_config_byte(fn3, 0x90, &clock);
+	if (ret)
+		return ret;
+	ret = pci_read_config_byte(fn3, 0x6C, &type);
+	if (ret)
+		return ret;
+	type &= 0x40;
+	type >>= 6;
+
+	switch (type) {
+	case 0:
+		switch (clock & 0x07) {
+		case 0:
+			dev_priv->vram_type = VIA_MEM_DDR_200;
+			break;
+		case 1:
+			dev_priv->vram_type = VIA_MEM_DDR_266;
+			break;
+		case 2:
+			dev_priv->vram_type = VIA_MEM_DDR_333;
+			break;
+		case 3:
+			dev_priv->vram_type = VIA_MEM_DDR_400;
+		default:
+			break;
+		}
+		break;
+
+	case 1:
+		switch (clock & 0x07) {
+		case 3:
+			dev_priv->vram_type = VIA_MEM_DDR2_400;
+			break;
+		case 4:
+			dev_priv->vram_type = VIA_MEM_DDR2_533;
+			break;
+		case 5:
+			dev_priv->vram_type = VIA_MEM_DDR2_667;
+			break;
+		case 6:
+			dev_priv->vram_type = VIA_MEM_DDR2_800;
+		default:
+			break;
+		}
+	default:
+		break;
+	}
+	return ret;
+}
+
+static int
+vx900_mem_type(struct drm_via_private *dev_priv, struct pci_dev *fn3)
+{
+	int ret;
+	u8 clock, type, volt;
+
+	ret = pci_read_config_byte(fn3, 0x90, &clock);
+	if (ret)
+		return ret;
+	ret = pci_read_config_byte(fn3, 0x6C, &type);
+	if (ret)
+		return ret;
+	volt = type;
+	type &= 0xC0;
+	type >>= 6;
+	volt &= 0x20;
+	volt >>=5;
+
+	switch (type) {
+	case 1:
+		switch (clock & 0x0F) {
+		case 0:
+			if (volt)
+				dev_priv->vram_type = VIA_MEM_DDR2_800;
+			else
+				dev_priv->vram_type = VIA_MEM_DDR2_533;
+			break;
+		case 4:
+			dev_priv->vram_type = VIA_MEM_DDR2_533;
+			break;
+		case 5:
+			dev_priv->vram_type = VIA_MEM_DDR2_667;
+			break;
+		case 6:
+			dev_priv->vram_type = VIA_MEM_DDR2_800;
+			break;
+		case 7:
+			dev_priv->vram_type = VIA_MEM_DDR2_1066;
+		default:
+			break;
+		}
+		break;
+	case 2:
+		switch (clock & 0x0F) {
+		case 0:
+			if (volt)
+				dev_priv->vram_type = VIA_MEM_DDR3_800;
+			else
+				dev_priv->vram_type = VIA_MEM_DDR3_533;
+			break;
+		case 4:
+			dev_priv->vram_type = VIA_MEM_DDR3_533;
+			break;
+		case 5:
+			dev_priv->vram_type = VIA_MEM_DDR3_667;
+			break;
+		case 6:
+			dev_priv->vram_type = VIA_MEM_DDR3_800;
+			break;
+		case 7:
+			dev_priv->vram_type = VIA_MEM_DDR3_1066;
+		default:
+			break;
+		}
+		break;
+	}
+	return ret;
+}
+
+int via_detect_vram(struct drm_device *dev)
+{
+	struct drm_via_private *dev_priv = dev->dev_private;
+	struct pci_dev *bridge = NULL, *fn3 = NULL;
+	unsigned long long vram_start;
+	int vram_size = 0, ret = 0;
+	char *name = "Unknown";
+	struct pci_bus *bus;
+	u8 size;
+
+	bus = pci_find_bus(0, 0);
+	if (bus == NULL) {
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	bridge = pci_get_slot(bus, PCI_DEVFN(0, 0));
+	fn3 = pci_get_slot(bus, PCI_DEVFN(0, 3));
+
+	if (!bridge) {
+		ret = -EINVAL;
+		DRM_ERROR("No host bridge found...\n");
+		goto out_err;
+	}
+
+	if (!fn3 && dev->pci_device != PCI_DEVICE_ID_VIA_CLE266
+		&& dev->pci_device != PCI_DEVICE_ID_VIA_KM400) {
+		ret = -EINVAL;
+		DRM_ERROR("No function 3 on host bridge...\n");
+		goto out_err;
+	}
+	vram_start = pci_resource_start(dev->pdev, 0);
+
+	switch (bridge->device) {
+
+	/* CLE266 */
+	case PCI_DEVICE_ID_VIA_862X_0:
+		ret = cle266_mem_type(dev_priv, bridge);
+		if (ret)
+			goto out_err;
+
+		ret = pci_read_config_byte(bridge, 0xE1, &size);
+		if (ret)
+			goto out_err;
+		vram_size = (1 << ((size & 0x70) >> 4)) << 20;
+		break;
+
+	/* KM400/KN400 */
+	case PCI_DEVICE_ID_VIA_8378_0:
+		ret = km400_mem_type(dev_priv, bridge);
+
+		ret = pci_read_config_byte(bridge, 0xE1, &size);
+		if (ret)
+			goto out_err;
+		vram_size = (1 << ((size & 0x70) >> 4)) << 20;
+		break;
+
+	/* P4M800 */
+	case PCI_DEVICE_ID_VIA_3296_0:
+		ret = p4m800_mem_type(dev_priv, bus, fn3);
+
+		ret = pci_read_config_byte(fn3, 0xA1, &size);
+		if (ret)
+			goto out_err;
+		vram_size = (1 << ((size & 0x70) >> 4)) << 20;
+		break;
+
+	/* K8M800/K8N800 */
+	case PCI_DEVICE_ID_VIA_8380_0:
+	/* K8M890 */
+	case PCI_DEVICE_ID_VIA_VT3336:
+		ret = pci_read_config_byte(fn3, 0xA1, &size);
+		if (ret)
+			goto out_err;
+		vram_size = (1 << ((size & 0x70) >> 4)) << 20;
+
+		if (bridge->device == PCI_DEVICE_ID_VIA_VT3336)
+			vram_size <<= 2;
+
+		ret = km8xx_mem_type(dev_priv);
+		if (ret)
+			goto out_err;
+		break;
+
+	/* CN400/PM800/PM880 */
+	case PCI_DEVICE_ID_VIA_PX8X0_0:
+		ret = pci_read_config_byte(fn3, 0xA1, &size);
+		if (ret)
+			goto out_err;
+		vram_size = (1 << ((size & 0x70) >> 4)) << 20;
+
+		ret = cn400_mem_type(dev_priv, bus, fn3);
+		if (ret)
+			goto out_err;
+		break;
+
+	/* CN700/VN800/P4M800CE/P4M800Pro */
+	case PCI_DEVICE_ID_VIA_P4M800CE:
+	/* P4M900/VN896/CN896 */
+	case PCI_DEVICE_ID_VIA_VT3364:
+		ret = pci_read_config_byte(fn3, 0xA1, &size);
+		if (ret)
+			goto out_err;
+		vram_size = (1 << ((size & 0x70) >> 4)) << 20;
+
+		if (bridge->device != PCI_DEVICE_ID_VIA_P4M800CE)
+			vram_size <<= 2;
+
+		ret = cn700_mem_type(dev_priv, fn3);
+		if  (ret)
+			goto out_err;
+		break;
+
+	/* CX700/VX700 */
+	case PCI_DEVICE_ID_VIA_VT3324:
+	/* P4M890 */
+	case PCI_DEVICE_ID_VIA_P4M890:
+	/* VX800 */
+	case PCI_DEVICE_ID_VIA_VT3353:
+	/* VX855 */
+	case PCI_DEVICE_ID_VIA_VT3409:
+		ret = pci_read_config_byte(fn3, 0xA1, &size);
+		if (ret)
+			goto out_err;
+		vram_size = (1 << ((size & 0x70) >> 4)) << 22;
+
+		ret = cx700_mem_type(dev_priv, fn3);
+		if (ret)
+			goto out_err;
+		break;
+
+	/* VX900 */
+	case PCI_DEVICE_ID_VIA_VT3410:
+		vram_start = pci_resource_start(dev->pdev, 2);
+
+		ret = pci_read_config_byte(fn3, 0xA1, &size);
+		if (ret)
+			goto out_err;
+		vram_size = (1 << ((size & 0x70) >> 4)) << 22;
+
+		ret = vx900_mem_type(dev_priv, fn3);
+		if (ret)
+			goto out_err;
+		break;
+
+	default:
+		DRM_ERROR("Unknown North Bridge device 0x%04x.\n", bridge->device);
+		goto out_err;
+	}
+
+	switch (dev_priv->vram_type) {
+	case VIA_MEM_SDR66:
+		name = "SDR 66";
+		break;
+	case VIA_MEM_SDR100:
+		name = "SDR 100";
+		break;
+	case VIA_MEM_SDR133:
+		name = "SDR 133";
+		break;
+	case VIA_MEM_DDR_200:
+		name = "DDR 200";
+		break;
+	case VIA_MEM_DDR_266:
+		name = "DDR 266";
+		break;
+	case VIA_MEM_DDR_333:
+		name = "DDR 333";
+		break;
+	case VIA_MEM_DDR_400:
+		name = "DDR 400";
+		break;
+	case VIA_MEM_DDR2_400:
+		name = "DDR2 400";
+		break;
+	case VIA_MEM_DDR2_533:
+		name = "DDR2 533";
+		break;
+	case VIA_MEM_DDR2_667:
+		name = "DDR2 667";
+		break;
+	case VIA_MEM_DDR2_800:
+		name = "DDR2 800";
+		break;
+	case VIA_MEM_DDR2_1066:
+		name = "DDR2 1066";
+		break;
+	case VIA_MEM_DDR3_533:
+		name = "DDR3 533";
+		break;
+	case VIA_MEM_DDR3_667:
+		name = "DDR3 667";
+		break;
+	case VIA_MEM_DDR3_800:
+		name = "DDR3 800";
+		break;
+	case VIA_MEM_DDR3_1066:
+		name = "DDR3 1066";
+		break;
+	case VIA_MEM_DDR3_1333:
+		name = "DDR3 1333";
+		break;
+	case VIA_MEM_DDR3_1600:
+		name = "DDR3 1600";
+		break;
+	default:
+		break;
+	}
+
+	/* Add an MTRR for the VRAM */
+	if (drm_core_has_MTRR(dev))
+		dev_priv->vram_mtrr = arch_phys_wc_add(vram_start, vram_size);
+
+	ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM, vram_size >> PAGE_SHIFT);
+	if (!ret) {
+		DRM_INFO("Detected %llu MB of %s Video RAM at physical address 0x%08llx.\n",
+			(unsigned long long) vram_size >> 20, name, vram_start);
+	}
+out_err:
+	if (bridge)
+		pci_dev_put(bridge);
+	if (fn3)
+		pci_dev_put(fn3);
+	return ret;
+}
+
+static int
+via_user_framebuffer_create_handle(struct drm_framebuffer *fb,
+					struct drm_file *file_priv,
+					unsigned int *handle)
+{
+	struct drm_gem_object *obj = fb->helper_private;
+
+	return drm_gem_handle_create(file_priv, obj, handle);
+}
+
+static void
+via_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+	struct drm_gem_object *obj = fb->helper_private;
+
+	if (obj) {
+		drm_gem_object_unreference_unlocked(obj);
+		fb->helper_private = NULL;
+	}
+	drm_framebuffer_cleanup(fb);
+	kfree(fb);
+}
+
+static const struct drm_framebuffer_funcs via_fb_funcs = {
+	.create_handle	= via_user_framebuffer_create_handle,
+	.destroy	= via_user_framebuffer_destroy,
+};
+
+static void
+via_output_poll_changed(struct drm_device *dev)
+{
+	struct drm_via_private *dev_priv = dev->dev_private;
+
+	drm_fb_helper_hotplug_event(dev_priv->helper);
+}
+
+static struct drm_framebuffer *
+via_user_framebuffer_create(struct drm_device *dev,
+				struct drm_file *file_priv,
+				struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	struct drm_framebuffer *fb;
+	struct drm_gem_object *obj;
+	int ret;
+
+	obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
+	if (obj ==  NULL) {
+		DRM_ERROR("No GEM object found for handle 0x%08X\n",
+				mode_cmd->handles[0]);
+		return ERR_PTR(-ENOENT);
+	}
+
+	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+	if (fb == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	ret = drm_framebuffer_init(dev, fb, &via_fb_funcs);
+	if (ret) {
+		drm_gem_object_unreference(obj);
+		kfree(fb);
+		return ERR_PTR(ret);
+	}
+	fb->helper_private = obj;
+	drm_helper_mode_fill_fb_struct(fb, mode_cmd);
+	return fb;
+}
+
+static const struct drm_mode_config_funcs via_mode_funcs = {
+	.fb_create		= via_user_framebuffer_create,
+	.output_poll_changed	= via_output_poll_changed
+};
+
+static int
+via_fb_probe(struct drm_fb_helper *helper,
+		struct drm_fb_helper_surface_size *sizes)
+{
+	struct ttm_fb_helper *ttmfb = container_of(helper, struct ttm_fb_helper, base);
+	struct drm_via_private *dev_priv = helper->dev->dev_private;
+	struct ttm_bo_kmap_obj *kmap = &ttmfb->kmap;
+	struct fb_info *info = helper->fbdev;
+	struct drm_framebuffer *fb = NULL;
+	struct drm_gem_object *obj = NULL;
+	struct drm_mode_fb_cmd2 mode_cmd;
+	struct apertures_struct *ap;
+	int size, ret = 0;
+
+	/* Already exist */
+	if (helper->fb)
+		return ret;
+
+	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+	if (fb == NULL)
+		return -ENOMEM;
+
+	mode_cmd.height = sizes->surface_height;
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+							sizes->surface_depth);
+	mode_cmd.pitches[0] = (mode_cmd.width * sizes->surface_bpp >> 3);
+	mode_cmd.pitches[0] = round_up(mode_cmd.pitches[0], 16);
+	size = mode_cmd.pitches[0] * mode_cmd.height;
+	size = ALIGN(size, PAGE_SIZE);
+
+	obj = drm_gem_object_alloc(helper->dev, size);
+	if (unlikely(IS_ERR(obj))) {
+		ret = PTR_ERR(obj);
+		goto out_err;
+	}
+	ret = ttm_bo_allocate(&dev_priv->bdev, size, ttm_bo_type_kernel,
+				TTM_PL_FLAG_VRAM, 1, PAGE_SIZE, false,
+				NULL, obj->filp, &kmap->bo);
+	if (unlikely(ret))
+		goto out_err;
+
+	ret = ttm_bo_pin(kmap->bo, kmap);
+	if (unlikely(ret))
+		goto out_err;
+
+	ret = drm_framebuffer_init(helper->dev, fb, &via_fb_funcs);
+	if (unlikely(ret))
+		goto out_err;
+
+	obj->driver_private = kmap->bo;
+	fb->helper_private = obj;
+	ttmfb->base.fb = fb;
+
+	drm_helper_mode_fill_fb_struct(fb, &mode_cmd);
+	info->fix.smem_start = kmap->bo->mem.bus.base +
+				kmap->bo->mem.bus.offset;
+	info->fix.smem_len = info->screen_size = size;
+	info->screen_base = kmap->virtual;
+
+	/* setup aperture base/size for takeover (vesafb, efifb etc) */
+	ap = alloc_apertures(1);
+	if (!ap) {
+		drm_framebuffer_cleanup(fb);
+		goto out_err;
+	}
+	ap->ranges[0].size = kmap->bo->bdev->man[kmap->bo->mem.mem_type].size;
+	ap->ranges[0].base = kmap->bo->mem.bus.base;
+	info->apertures = ap;
+
+	drm_fb_helper_fill_var(info, helper, sizes->fb_width, sizes->fb_height);
+	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
+	ret = 1;
+out_err:
+	if (ret < 0) {
+		if (kmap->bo) {
+			ttm_bo_unpin(kmap->bo, kmap);
+			ttm_bo_unref(&kmap->bo);
+		}
+
+		if (obj) {
+			drm_gem_object_unreference_unlocked(obj);
+			helper->fb->helper_private = NULL;
+		}
+		kfree(fb);
+	}
+	return ret;
+}
+
+/** Sets the color ramps on behalf of fbcon */
+static void
+via_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+		u16 blue, int regno)
+{
+	int size = crtc->gamma_size * sizeof(uint16_t);
+	u16 *r_base, *g_base, *b_base;
+
+	r_base = crtc->gamma_store;
+	g_base = r_base + size;
+	b_base = g_base + size;
+
+	r_base[regno] = red;
+	g_base[regno] = green;
+	b_base[regno] = blue;
+}
+
+static void
+via_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+		u16 *blue, int regno)
+{
+	int size = crtc->gamma_size * sizeof(uint16_t);
+	u16 *r_base, *g_base, *b_base;
+
+	r_base = crtc->gamma_store;
+	g_base = r_base + size;
+	b_base = g_base + size;
+
+	*red = r_base[regno];
+	*green = g_base[regno];
+	*blue = b_base[regno];
+}
+
+static struct drm_fb_helper_funcs via_fb_helper_funcs = {
+	.gamma_set = via_fb_gamma_set,
+	.gamma_get = via_fb_gamma_get,
+	.fb_probe = via_fb_probe,
+};
+
+int
+drmfb_helper_pan_display(struct fb_var_screeninfo *var,
+				struct fb_info *info)
+{
+	struct drm_fb_helper *fb_helper = info->par;
+	struct drm_crtc_helper_funcs *crtc_funcs;
+	struct drm_device *dev = fb_helper->dev;
+	struct drm_mode_set *modeset;
+	struct drm_crtc *crtc;
+	int ret = -ENXIO, i;
+
+	mutex_lock(&dev->mode_config.mutex);
+	for (i = 0; i < fb_helper->crtc_count; i++) {
+		crtc = fb_helper->crtc_info[i].mode_set.crtc;
+		crtc_funcs = crtc->helper_private;
+
+		if (!crtc_funcs->mode_set_base)
+			continue;
+
+		modeset = &fb_helper->crtc_info[i].mode_set;
+		modeset->x = var->xoffset;
+		modeset->y = var->yoffset;
+
+		if (modeset->num_connectors) {
+			ret = crtc_funcs->mode_set_base(crtc, modeset->x,
+							modeset->y, crtc->fb);
+			if (!ret) {
+				info->flags |= FBINFO_HWACCEL_YPAN;
+				info->var.xoffset = var->xoffset;
+				info->var.yoffset = var->yoffset;
+			}
+		}
+	}
+	if (ret) info->flags &= ~FBINFO_HWACCEL_YPAN;
+	mutex_unlock(&dev->mode_config.mutex);
+	return ret;
+}
+
+static struct fb_ops viafb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_check_var	= drm_fb_helper_check_var,
+	.fb_set_par	= drm_fb_helper_set_par,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+	.fb_pan_display	= drmfb_helper_pan_display,
+	.fb_blank	= drm_fb_helper_blank,
+	.fb_setcmap	= drm_fb_helper_setcmap,
+	.fb_debug_enter	= drm_fb_helper_debug_enter,
+	.fb_debug_leave	= drm_fb_helper_debug_leave,
+};
+
+int
+via_framebuffer_init(struct drm_device *dev, struct drm_fb_helper **ptr)
+{
+	struct ttm_fb_helper *helper;
+	struct fb_info *info;
+	int ret = -ENOMEM;
+
+	dev->mode_config.funcs = (void *)&via_mode_funcs;
+
+	info = framebuffer_alloc(sizeof(*helper), dev->dev);
+	if (!info) {
+		DRM_ERROR("allocate fb_info error\n");
+		return ret;
+	}
+
+	helper = info->par;
+	helper->base.fbdev = info;
+	helper->base.funcs = &via_fb_helper_funcs;
+
+	strcpy(info->fix.id, dev->driver->name);
+	strcat(info->fix.id, "drmfb");
+	info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
+	info->fbops = &viafb_ops;
+
+	info->pixmap.size = 64*1024;
+	info->pixmap.buf_align = 8;
+	info->pixmap.access_align = 32;
+	info->pixmap.flags = FB_PIXMAP_SYSTEM;
+	info->pixmap.scan_align = 1;
+
+	/* Should be based on the crtc color map size */
+	ret = fb_alloc_cmap(&info->cmap, 256, 0);
+	if (ret)
+		goto out_err;
+
+	ret = drm_fb_helper_init(dev, &helper->base, dev->num_crtcs,
+				dev->mode_config.num_connector);
+	if (ret) {
+		fb_dealloc_cmap(&info->cmap);
+		goto out_err;
+	}
+
+	drm_fb_helper_single_add_all_connectors(&helper->base);
+	drm_helper_disable_unused_functions(dev);
+	drm_fb_helper_initial_config(&helper->base, 32);
+	drm_kms_helper_poll_init(dev);
+	*ptr = (struct drm_fb_helper *) helper;
+out_err:
+	if (ret)
+		framebuffer_release(info);
+	return ret;
+}
+
+void
+via_framebuffer_fini(struct drm_device *dev)
+{
+	struct drm_via_private *dev_priv = dev->dev_private;
+	struct drm_fb_helper *helper = dev_priv->helper;
+	struct ttm_fb_helper *ttmfb;
+	struct drm_gem_object *obj;
+	struct fb_info *info;
+
+	if (!helper)
+		return;
+
+	ttmfb = container_of(helper, struct ttm_fb_helper, base);
+	info = helper->fbdev;
+	if (info) {
+		unregister_framebuffer(info);
+		if (info->cmap.len)
+			fb_dealloc_cmap(&info->cmap);
+		if (info->apertures)
+			kfree(info->apertures);
+
+		framebuffer_release(info);
+		helper->fbdev = NULL;
+	}
+
+	ttmfb = container_of(helper, struct ttm_fb_helper, base);
+	if (ttmfb->kmap.bo) {
+		ttm_bo_unpin(ttmfb->kmap.bo, &ttmfb->kmap);
+		ttm_bo_unref(&ttmfb->kmap.bo);
+	}
+
+	obj = helper->fb->helper_private;
+	if (obj) {
+		drm_gem_object_unreference_unlocked(obj);
+		helper->fb->helper_private = NULL;
+	}
+	drm_fb_helper_fini(helper);
+	drm_framebuffer_cleanup(helper->fb);
+
+	kfree(dev_priv->helper);
+	dev_priv->helper = NULL;
+}