From patchwork Mon Jul 25 23:51:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kevin Brace X-Patchwork-Id: 12928522 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id D72B0C43334 for ; Mon, 25 Jul 2022 23:53:25 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 0F12C10F6AA; Mon, 25 Jul 2022 23:53:23 +0000 (UTC) Received: from mout.gmx.net (mout.gmx.net [212.227.15.18]) by gabe.freedesktop.org (Postfix) with ESMTPS id 2DCCC10F5F9 for ; Mon, 25 Jul 2022 23:53:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1658793198; bh=4E5K4pKzVYvpe//npvj+OicZtaUkdupAHM7JvaEeujI=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:In-Reply-To:References; b=aS6JAJk8B1et7i9gUn1rPdxSTIfCvst4E+nEt7xrnzZbUmhKIgcUFEDOFQyh8VRmD B5h1WLjaVlvuv8C4e7MuMk4ZFGOyy+zoutwdpvRtv/tRzqEZXEa4uvm74gnFsJeXMx U0CnsiwsEx8m5GYPzATTpoX19NoYpx0q+QT9psfs= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Received: from localhost.localdomain ([108.233.15.105]) by mail.gmx.net (mrgmx005 [212.227.17.184]) with ESMTPSA (Nemesis) id 1N8GQs-1nTuwe05vE-014APg; Tue, 26 Jul 2022 01:53:18 +0200 From: Kevin Brace To: dri-devel@lists.freedesktop.org Subject: [PATCH v3 16/32] drm/via: Add via_init.c Date: Mon, 25 Jul 2022 16:51:11 -0700 Message-Id: <20220725235127.20433-17-kevinbrace@gmx.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220725235127.20433-1-kevinbrace@gmx.com> References: <20220725235127.20433-1-kevinbrace@gmx.com> MIME-Version: 1.0 X-Provags-ID: V03:K1:WoZheu3tvY5nHKVGe3aH1cnlgZw527r6DmH7ZfCOyvqgRnk9dMn fadnXhGxu3gRvVlfsnkHl8jjzpyUgsrFfplHZdg7xxkiyqP56kYAKF/DT2Fo4ObFl+kxOB7 dYhhpKqAzDfhMa++NurALnBHWj39k8kJqWW/C8arMeUgIQi7A6rAL+A+F0sNciqIxAQaRTq Ae/Dh1kVnnR+IE9iDDrIg== X-UI-Out-Filterresults: notjunk:1;V03:K0:UGWLxCxhjyU=:zzuzU5XVRD7hzuDW9xSXY0 M2k74FcgFkoV/6y6sda3WvWP1F/Np4JWL2vRI7vKQ8ntz1zJs2jmLf530WIGmIXx1tkTcVlNu RXll33kvLTEUzXytDuvzwnMD3NASXnjJ3TO1iFxD1wdiHGeN03io2P4CQgEgvyDWxmaGwfrZL cdyWZQQYo7rNgOZwsTdCqpK+3odV4CxfDVtxkaaSVGPa1TnZdotj3fdHkOJD+4XEEsuowQNDj 6nB8/HYLCOLK8Hro2qMTz6nsno/uWrsjs8cRKyNT4O7UuViLRtFEVNfdShtm9/Udse6TPhqWb VpbFT0G3mJi0ohugPSBcPF+B9hwVp0aqlbEB0oH4BhC15JoUmSQFVtVFvBVI8tOgW3JkgRFh6 V8ow+/bw7m8d26LG4sj8+fITNLbj8rRpV2OwasAL0RqvVHfr6dwNhjvExVcJ9sYmVuO7NmvGl /zI8DPmCinrTk3Hf7nt8STRv56mtppGn+Hie2UFM1XxJwugyjjLBFZfPLLBLrq0uyQIf01szf FnYzbIQN5+g87zur0+FYOw0bBHCH063bzP0w48OSR/z/xTpYSRWDEQ18KvsNDLKLOYqCJ+qPz 3ASiw47/FBHyzHz6frOjmDHwVNAAG/ywRq7XbbQWsUpFxvdbXbAAedBcUwFXc2Z4gZ1gAVCif dfPKNwM65EmA6QHI0zZpL94NkCSu8uY3WUNMRBfGPgFybmAJTe4c8Z9p78tYjgCVQrVe9nagb jUCiIfferFY+VaPRETACXXgFpCr+gpSVMy7Q1ZqdQ7mMElWWUd2DMg6MrNdVQfErIGKZVPKLt 6wuxn75nxKnKdN2XYKbhazkVP514OkAYascQrwJ9pkz9tUye2dpM0n7QhEQjQeuRCOf2vviq2 X5cNr3jdNubxSZXkRxS9TzjZwzX5lyDTyrT0bYrWjitGhi9D0MxOUPceP5lxNge3yOq2Grf/U 0HLkLQ1nkwgOO3JFIl41UG/Eyaoi8qt5MF2q/lx787W77LAmvY/oeXYlM2aTOb2l0tO9lDc2o 3O6G25NhWkmfvRnDQFY6Bz65oL1Vt2dG9iiqytF02hesyLVhYaS1S7jpvfVX21FzC/MrSF+tt h87uknEnddVlowbstDOZOpNU/aplNRR9d04QmIKIApJE49JxXoClYqfkw== X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Brace Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" From: Kevin Brace Signed-off-by: Kevin Brace --- drivers/gpu/drm/via/via_init.c | 1300 ++++++++++++++++++++++++++++++++ 1 file changed, 1300 insertions(+) create mode 100644 drivers/gpu/drm/via/via_init.c -- 2.35.1 diff --git a/drivers/gpu/drm/via/via_init.c b/drivers/gpu/drm/via/via_init.c new file mode 100644 index 000000000000..0e478b974628 --- /dev/null +++ b/drivers/gpu/drm/via/via_init.c @@ -0,0 +1,1300 @@ +/* + * Copyright © 2019 Kevin Brace. + * Copyright 2012 James Simmons. All Rights Reserved. + * Copyright 2006-2009 Luc Verhaegen. + * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2009 S3 Graphics, Inc. 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. + * + * Author(s): + * Kevin Brace + * James Simmons + * Luc Verhaegen + */ + +#include + +#include +#include +#include + +#include "via_drv.h" + + +static int cle266_mem_type(struct via_drm_priv *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; + break; + 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; + break; + default: + break; + } + break; + default: + break; + } + return ret; +} + +static int km400_mem_type(struct via_drm_priv *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; + break; + 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; + break; + 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; + break; + default: + break; + } + 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; + break; + 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; + break; + 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; + break; + default: + break; + } + break; + default: + break; + } + } + return ret; +} + +static int p4m800_mem_type(struct via_drm_priv *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 */ + break; + 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; + break; + default: + break; + } + return ret; +} + +static int km8xx_mem_type(struct via_drm_priv *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; + break; + 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; + break; + 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; + break; + 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; + break; + 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; + break; + default: + break; + } + } + return ret; +} + +static int cn400_mem_type(struct via_drm_priv *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 */ + break; + 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; + break; + default: + break; + } + return ret; +} + +static int cn700_mem_type(struct via_drm_priv *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; + break; + default: + break; + } + } + return ret; +} + +static int cx700_mem_type(struct via_drm_priv *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; + break; + 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; + break; + default: + break; + } + break; + default: + break; + } + return ret; +} + +static int vx900_mem_type(struct via_drm_priv *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; + break; + 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; + break; + default: + break; + } + break; + } + return ret; +} + +int via_vram_detect(struct via_drm_priv *dev_priv) +{ + struct drm_device *dev = &dev_priv->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct pci_dev *bridge = NULL; + struct pci_dev *fn3 = NULL; + char *name = "unknown"; + struct pci_bus *bus; + u8 size; + int ret = 0; + + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + 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 && pdev->device != PCI_DEVICE_ID_VIA_CLE266_GFX + && pdev->device != PCI_DEVICE_ID_VIA_KM400_GFX) { + ret = -EINVAL; + DRM_ERROR("No function 3 on host bridge...\n"); + goto out_err; + } + + if (pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HD) { + dev_priv->vram_start = pci_resource_start(pdev, 2); + } else { + dev_priv->vram_start = pci_resource_start(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; + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 20; + break; + + /* KM400 / KN400 / KM400A / KN400A */ + 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; + dev_priv->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; + dev_priv->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; + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 20; + + if (bridge->device == PCI_DEVICE_ID_VIA_VT3336) + dev_priv->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; + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 20; + + ret = cn400_mem_type(dev_priv, bus, fn3); + if (ret) + goto out_err; + break; + + /* P4M800CE / P4M800 Pro / VN800 / CN700 */ + 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; + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 20; + + if (bridge->device != PCI_DEVICE_ID_VIA_P4M800CE) + dev_priv->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 / VN890 */ + case PCI_DEVICE_ID_VIA_P4M890: + /* VX800 / VX820 */ + case PCI_DEVICE_ID_VIA_VX800_HB: + /* VX855 / VX875 */ + case PCI_DEVICE_ID_VIA_VX855_HB: + ret = pci_read_config_byte(fn3, 0xA1, &size); + if (ret) + goto out_err; + dev_priv->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_VX900_HB: + ret = pci_read_config_byte(fn3, 0xA1, &size); + if (ret) + goto out_err; + dev_priv->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; + } + + DRM_INFO("Found %s video RAM.\n", name); +out_err: + if (bridge) + pci_dev_put(bridge); + if (fn3) + pci_dev_put(fn3); + + DRM_DEBUG_KMS("Exiting %s.\n", __func__); + return ret; +} + +static void via_flag_init(struct via_drm_priv *dev_priv) +{ + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + /* Set this flag for ttm_bo_device_init. */ + dev_priv->need_dma32 = true; + + DRM_DEBUG_KMS("Exiting %s.\n", __func__); +} + +static void via_quirks_init(struct via_drm_priv *dev_priv) +{ + struct drm_device *dev = &dev_priv->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + /* + * Checking for VIA Technologies NanoBook reference design. + * Examples include Everex CloudBook and Sylvania g netbook. + * It is also called FIC CE260 or CE261 by its ODM (Original + * Design Manufacturer) name. + * This device has its strapping resistors set to a wrong + * setting to handle DVI. As a result, the code needs to know + * this in order to support DVI properly. + */ + if ((pdev->device == PCI_DEVICE_ID_VIA_UNICHROME_PRO_II) && + (pdev->subsystem_vendor == 0x1509) && + (pdev->subsystem_device == 0x2d30)) { + dev_priv->is_via_nanobook = true; + } else { + dev_priv->is_via_nanobook = false; + } + + /* + * Check for Quanta IL1 netbook. This is necessary + * due to its flat panel connected to DVP1 (Digital + * Video Port 1) rather than its LVDS channel. + */ + if ((pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HC3) && + (pdev->subsystem_vendor == 0x152d) && + (pdev->subsystem_device == 0x0771)) { + dev_priv->is_quanta_il1 = true; + } else { + dev_priv->is_quanta_il1 = false; + } + + /* + * Samsung NC20 netbook has its FP connected to LVDS2 + * rather than the more logical LVDS1, hence, a special + * flag register is needed for properly controlling its + * FP. + */ + if ((pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HC3) && + (pdev->subsystem_vendor == 0x144d) && + (pdev->subsystem_device == 0xc04e)) { + dev_priv->is_samsung_nc20 = true; + } else { + dev_priv->is_samsung_nc20 = false; + } + + DRM_DEBUG_KMS("Exiting %s.\n", __func__); +} + +int via_vram_init(struct via_drm_priv *dev_priv) +{ + int ret = 0; + + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + /* Add an MTRR for the video RAM. */ + dev_priv->vram_mtrr = arch_phys_wc_add(dev_priv->vram_start, + dev_priv->vram_size); + + DRM_DEBUG_KMS("Exiting %s.\n", __func__); + return ret; +} + +void via_vram_fini(struct via_drm_priv *dev_priv) +{ + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + if (dev_priv->vram_mtrr) { + arch_phys_wc_del(dev_priv->vram_mtrr); + arch_io_free_memtype_wc(dev_priv->vram_start, + dev_priv->vram_size); + dev_priv->vram_mtrr = 0; + } + + DRM_DEBUG_KMS("Exiting %s.\n", __func__); +} + +int via_mmio_init(struct via_drm_priv *dev_priv) +{ + struct drm_device *dev = &dev_priv->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + int ret = 0; + + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + /* + * PCI BAR1 is the MMIO memory window for all + * VIA Technologies Chrome IGPs. + * Obtain the starting base address and size, and + * map it to the OS for use. + */ + dev_priv->mmio_base = pci_resource_start(pdev, 1); + dev_priv->mmio_size = pci_resource_len(pdev, 1); + dev_priv->mmio = ioremap(dev_priv->mmio_base, + dev_priv->mmio_size); + if (!dev_priv->mmio) { + ret = -ENOMEM; + goto exit; + } + +exit: + DRM_DEBUG_KMS("Exiting %s.\n", __func__); + return ret; +} + +void via_mmio_fini(struct via_drm_priv *dev_priv) +{ + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + if (dev_priv->mmio) { + iounmap(dev_priv->mmio); + dev_priv->mmio = NULL; + } + + DRM_DEBUG_KMS("Exiting %s.\n", __func__); +} + +void via_graphics_unlock(struct via_drm_priv *dev_priv) +{ + uint8_t temp; + + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + /* + * Enable VGA subsystem. + */ + temp = vga_io_r(0x03C3); + vga_io_w(0x03C3, temp | 0x01); + svga_wmisc_mask(VGABASE, BIT(0), BIT(0)); + + /* + * Unlock VIA Technologies Chrome IGP extended + * registers. + */ + svga_wseq_mask(VGABASE, 0x10, BIT(0), BIT(0)); + + /* + * Unlock VIA Technologies Chrome IGP extended + * graphics functionality. + */ + svga_wseq_mask(VGABASE, 0x1a, BIT(3), BIT(3)); + + DRM_DEBUG_KMS("Exiting %s.\n", __func__); +} + +void chip_revision_info(struct via_drm_priv *dev_priv) +{ + struct drm_device *dev = &dev_priv->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + u8 tmp; + + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + switch (pdev->device) { + /* CLE266 Chipset */ + case PCI_DEVICE_ID_VIA_CLE266_GFX: + /* CR4F only defined in CLE266.CX chipset. */ + tmp = vga_rcrt(VGABASE, 0x4F); + vga_wcrt(VGABASE, 0x4F, 0x55); + if (vga_rcrt(VGABASE, 0x4F) != 0x55) { + dev_priv->revision = CLE266_REVISION_AX; + } else { + dev_priv->revision = CLE266_REVISION_CX; + } + + /* Restore original CR4F value. */ + vga_wcrt(VGABASE, 0x4F, tmp); + break; + /* CX700 / VX700 Chipset */ + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + tmp = vga_rseq(VGABASE, 0x43); + if (tmp & 0x02) { + dev_priv->revision = CX700_REVISION_700M2; + } else if (tmp & 0x40) { + dev_priv->revision = CX700_REVISION_700M; + } else { + dev_priv->revision = CX700_REVISION_700; + } + + break; + /* VX800 / VX820 Chipset */ + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + break; + /* VX855 / VX875 Chipset */ + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + /* VX900 Chipset */ + case PCI_DEVICE_ID_VIA_CHROME9_HD: + dev_priv->revision = vga_rseq(VGABASE, 0x3B); + break; + default: + break; + } + + DRM_DEBUG_KMS("Exiting %s.\n", __func__); +} + +int via_device_init(struct via_drm_priv *dev_priv) +{ + int ret; + + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + via_flag_init(dev_priv); + via_quirks_init(dev_priv); + + ret = via_vram_detect(dev_priv); + if (ret) { + DRM_ERROR("Failed to detect video RAM.\n"); + goto exit; + } + + /* + * Map VRAM. + */ + ret = via_vram_init(dev_priv); + if (ret) { + DRM_ERROR("Failed to initialize video RAM.\n"); + goto exit; + } + + ret = via_mmio_init(dev_priv); + if (ret) { + DRM_ERROR("Failed to initialize MMIO.\n"); + goto exit; + } + + via_graphics_unlock(dev_priv); +exit: + DRM_DEBUG_KMS("Exiting %s.\n", __func__); + return ret; +} + +void via_device_fini(struct via_drm_priv *dev_priv) +{ + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + via_mmio_fini(dev_priv); + via_vram_fini(dev_priv); + + DRM_DEBUG_KMS("Exiting %s.\n", __func__); +} + +static const struct drm_mode_config_funcs via_drm_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +void via_mode_config_init(struct via_drm_priv *dev_priv) +{ + struct drm_device *dev = &dev_priv->dev; + + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + drm_mode_config_init(dev); + + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.max_width = 2044; + dev->mode_config.max_height = 4096; + + dev->mode_config.funcs = &via_drm_mode_config_funcs; + + dev->mode_config.fb_base = dev_priv->vram_start; + + dev->mode_config.preferred_depth = 24; + dev->mode_config.prefer_shadow_fbdev = true; + + dev->mode_config.cursor_width = + dev->mode_config.cursor_height = VIA_CURSOR_SIZE; + + DRM_DEBUG_KMS("Exiting %s.\n", __func__); +} + +int via_drm_init(struct drm_device *dev) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + int ret = 0; + + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + dev_priv->vram_mtrr = -ENXIO; + + ret = via_device_init(dev_priv); + if (ret) { + DRM_ERROR("Failed to initialize Chrome IGP.\n"); + goto exit; + } + + ret = via_mm_init(dev_priv); + if (ret) { + DRM_ERROR("Failed to initialize TTM.\n"); + goto exit; + } + + chip_revision_info(dev_priv); + + ret = via_modeset_init(dev); + if (ret) { + DRM_ERROR("Failed to initialize mode setting.\n"); + goto error_modeset; + } + + goto exit; +error_modeset: + via_modeset_fini(dev); + via_mm_fini(dev_priv); + via_mmio_fini(dev_priv); + via_vram_fini(dev_priv); +exit: + DRM_DEBUG_KMS("Exiting %s.\n", __func__); + return ret; +} + +void via_drm_fini(struct drm_device *dev) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + DRM_DEBUG_KMS("Entered %s.\n", __func__); + + via_modeset_fini(dev); + via_mm_fini(dev_priv); + via_mmio_fini(dev_priv); + via_vram_fini(dev_priv); + + DRM_DEBUG_KMS("Exiting %s.\n", __func__); +}