310 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright 2012 Red Hat4 *5 * Authors: Matthew Garrett6 * Dave Airlie7 */8 9#include <linux/module.h>10#include <linux/pci.h>11 12#include <drm/drm_aperture.h>13#include <drm/drm_atomic_helper.h>14#include <drm/drm_drv.h>15#include <drm/drm_fbdev_shmem.h>16#include <drm/drm_file.h>17#include <drm/drm_ioctl.h>18#include <drm/drm_managed.h>19#include <drm/drm_module.h>20#include <drm/drm_pciids.h>21 22#include "mgag200_drv.h"23 24static int mgag200_modeset = -1;25MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");26module_param_named(modeset, mgag200_modeset, int, 0400);27 28int mgag200_init_pci_options(struct pci_dev *pdev, u32 option, u32 option2)29{30 struct device *dev = &pdev->dev;31 int err;32 33 err = pci_write_config_dword(pdev, PCI_MGA_OPTION, option);34 if (err != PCIBIOS_SUCCESSFUL) {35 dev_err(dev, "pci_write_config_dword(PCI_MGA_OPTION) failed: %d\n", err);36 return pcibios_err_to_errno(err);37 }38 39 err = pci_write_config_dword(pdev, PCI_MGA_OPTION2, option2);40 if (err != PCIBIOS_SUCCESSFUL) {41 dev_err(dev, "pci_write_config_dword(PCI_MGA_OPTION2) failed: %d\n", err);42 return pcibios_err_to_errno(err);43 }44 45 return 0;46}47 48resource_size_t mgag200_probe_vram(void __iomem *mem, resource_size_t size)49{50 int offset;51 int orig;52 int test1, test2;53 int orig1, orig2;54 size_t vram_size;55 56 /* Probe */57 orig = ioread16(mem);58 iowrite16(0, mem);59 60 vram_size = size;61 62 for (offset = 0x100000; offset < vram_size; offset += 0x4000) {63 orig1 = ioread8(mem + offset);64 orig2 = ioread8(mem + offset + 0x100);65 66 iowrite16(0xaa55, mem + offset);67 iowrite16(0xaa55, mem + offset + 0x100);68 69 test1 = ioread16(mem + offset);70 test2 = ioread16(mem);71 72 iowrite16(orig1, mem + offset);73 iowrite16(orig2, mem + offset + 0x100);74 75 if (test1 != 0xaa55)76 break;77 78 if (test2)79 break;80 }81 82 iowrite16(orig, mem);83 84 return offset - 65536;85}86 87/*88 * DRM driver89 */90 91DEFINE_DRM_GEM_FOPS(mgag200_driver_fops);92 93static const struct drm_driver mgag200_driver = {94 .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,95 .fops = &mgag200_driver_fops,96 .name = DRIVER_NAME,97 .desc = DRIVER_DESC,98 .date = DRIVER_DATE,99 .major = DRIVER_MAJOR,100 .minor = DRIVER_MINOR,101 .patchlevel = DRIVER_PATCHLEVEL,102 DRM_GEM_SHMEM_DRIVER_OPS,103};104 105/*106 * DRM device107 */108 109resource_size_t mgag200_device_probe_vram(struct mga_device *mdev)110{111 return mgag200_probe_vram(mdev->vram, resource_size(mdev->vram_res));112}113 114int mgag200_device_preinit(struct mga_device *mdev)115{116 struct drm_device *dev = &mdev->base;117 struct pci_dev *pdev = to_pci_dev(dev->dev);118 resource_size_t start, len;119 struct resource *res;120 121 /* BAR 1 contains registers */122 123 start = pci_resource_start(pdev, 1);124 len = pci_resource_len(pdev, 1);125 126 res = devm_request_mem_region(dev->dev, start, len, "mgadrmfb_mmio");127 if (!res) {128 drm_err(dev, "devm_request_mem_region(MMIO) failed\n");129 return -ENXIO;130 }131 mdev->rmmio_res = res;132 133 mdev->rmmio = pcim_iomap(pdev, 1, 0);134 if (!mdev->rmmio)135 return -ENOMEM;136 137 /* BAR 0 is VRAM */138 139 start = pci_resource_start(pdev, 0);140 len = pci_resource_len(pdev, 0);141 142 res = devm_request_mem_region(dev->dev, start, len, "mgadrmfb_vram");143 if (!res) {144 drm_err(dev, "devm_request_mem_region(VRAM) failed\n");145 return -ENXIO;146 }147 mdev->vram_res = res;148 149#if defined(CONFIG_DRM_MGAG200_DISABLE_WRITECOMBINE)150 mdev->vram = devm_ioremap(dev->dev, res->start, resource_size(res));151 if (!mdev->vram)152 return -ENOMEM;153#else154 mdev->vram = devm_ioremap_wc(dev->dev, res->start, resource_size(res));155 if (!mdev->vram)156 return -ENOMEM;157 158 /* Don't fail on errors, but performance might be reduced. */159 devm_arch_phys_wc_add(dev->dev, res->start, resource_size(res));160#endif161 162 return 0;163}164 165int mgag200_device_init(struct mga_device *mdev,166 const struct mgag200_device_info *info,167 const struct mgag200_device_funcs *funcs)168{169 struct drm_device *dev = &mdev->base;170 u8 crtcext3, misc;171 int ret;172 173 mdev->info = info;174 mdev->funcs = funcs;175 176 ret = drmm_mutex_init(dev, &mdev->rmmio_lock);177 if (ret)178 return ret;179 180 mutex_lock(&mdev->rmmio_lock);181 182 RREG_ECRT(0x03, crtcext3);183 crtcext3 |= MGAREG_CRTCEXT3_MGAMODE;184 WREG_ECRT(0x03, crtcext3);185 186 WREG_ECRT(0x04, 0x00);187 188 misc = RREG8(MGA_MISC_IN);189 misc |= MGAREG_MISC_RAMMAPEN |190 MGAREG_MISC_HIGH_PG_SEL;191 WREG8(MGA_MISC_OUT, misc);192 193 mutex_unlock(&mdev->rmmio_lock);194 195 WREG32(MGAREG_IEN, 0);196 197 return 0;198}199 200/*201 * PCI driver202 */203 204static const struct pci_device_id mgag200_pciidlist[] = {205 { PCI_VENDOR_ID_MATROX, 0x520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_PCI },206 { PCI_VENDOR_ID_MATROX, 0x521, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_AGP },207 { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A },208 { PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B },209 { PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV },210 { PCI_VENDOR_ID_MATROX, 0x532, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_WB },211 { PCI_VENDOR_ID_MATROX, 0x533, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH },212 { PCI_VENDOR_ID_MATROX, 0x534, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_ER },213 { PCI_VENDOR_ID_MATROX, 0x536, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EW3 },214 { PCI_VENDOR_ID_MATROX, 0x538, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH3 },215 {0,}216};217 218MODULE_DEVICE_TABLE(pci, mgag200_pciidlist);219 220static int221mgag200_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)222{223 enum mga_type type = (enum mga_type)ent->driver_data;224 struct mga_device *mdev;225 struct drm_device *dev;226 int ret;227 228 ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &mgag200_driver);229 if (ret)230 return ret;231 232 ret = pcim_enable_device(pdev);233 if (ret)234 return ret;235 236 switch (type) {237 case G200_PCI:238 case G200_AGP:239 mdev = mgag200_g200_device_create(pdev, &mgag200_driver);240 break;241 case G200_SE_A:242 case G200_SE_B:243 mdev = mgag200_g200se_device_create(pdev, &mgag200_driver, type);244 break;245 case G200_WB:246 mdev = mgag200_g200wb_device_create(pdev, &mgag200_driver);247 break;248 case G200_EV:249 mdev = mgag200_g200ev_device_create(pdev, &mgag200_driver);250 break;251 case G200_EH:252 mdev = mgag200_g200eh_device_create(pdev, &mgag200_driver);253 break;254 case G200_EH3:255 mdev = mgag200_g200eh3_device_create(pdev, &mgag200_driver);256 break;257 case G200_ER:258 mdev = mgag200_g200er_device_create(pdev, &mgag200_driver);259 break;260 case G200_EW3:261 mdev = mgag200_g200ew3_device_create(pdev, &mgag200_driver);262 break;263 default:264 dev_err(&pdev->dev, "Device type %d is unsupported\n", type);265 return -ENODEV;266 }267 if (IS_ERR(mdev))268 return PTR_ERR(mdev);269 dev = &mdev->base;270 271 ret = drm_dev_register(dev, 0);272 if (ret)273 return ret;274 275 /*276 * FIXME: A 24-bit color depth does not work with 24 bpp on277 * G200ER. Force 32 bpp.278 */279 drm_fbdev_shmem_setup(dev, 32);280 281 return 0;282}283 284static void mgag200_pci_remove(struct pci_dev *pdev)285{286 struct drm_device *dev = pci_get_drvdata(pdev);287 288 drm_dev_unregister(dev);289 drm_atomic_helper_shutdown(dev);290}291 292static void mgag200_pci_shutdown(struct pci_dev *pdev)293{294 drm_atomic_helper_shutdown(pci_get_drvdata(pdev));295}296 297static struct pci_driver mgag200_pci_driver = {298 .name = DRIVER_NAME,299 .id_table = mgag200_pciidlist,300 .probe = mgag200_pci_probe,301 .remove = mgag200_pci_remove,302 .shutdown = mgag200_pci_shutdown,303};304 305drm_module_pci_driver_if_modeset(mgag200_pci_driver, mgag200_modeset);306 307MODULE_AUTHOR(DRIVER_AUTHOR);308MODULE_DESCRIPTION(DRIVER_DESC);309MODULE_LICENSE("GPL");310