From patchwork Sat Jun 8 16:52:38 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Simmons X-Patchwork-Id: 2692841 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by patchwork1.kernel.org (Postfix) with ESMTP id 1E0C03FC23 for ; Sat, 8 Jun 2013 16:58:12 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 1DDD4E6139 for ; Sat, 8 Jun 2013 09:58:12 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) by gabe.freedesktop.org (Postfix) with ESMTP id 4439FE6138; Sat, 8 Jun 2013 09:52:43 -0700 (PDT) Received: from jsimmons (helo=localhost) by casper.infradead.org with local-esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1UlMNa-00064v-6w; Sat, 08 Jun 2013 16:52:42 +0000 Date: Sat, 8 Jun 2013 17:52:38 +0100 (BST) From: James Simmons To: DRI development list Subject: [RFC 13/21] DRM: Add VIA DRM driver Message-ID: User-Agent: Alpine 2.03 (LFD 1266 2009-07-14) MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130608_175238_395089_01FE1B55 X-CRM114-Status: GOOD ( 16.06 ) X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.3.2 on casper.infradead.org summary: Content analysis details: (-1.9 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 NO_RELAYS Informational: message was not relayed via SMTP -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: OpenChrome Development X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org Errors-To: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org commit 991c37448ff8403727f12af331eb164eb5ed4048 Author: James Simmons 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 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 . 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; +}