linux/drivers/gpu/drm/mgag200/mgag200_drv.c

205 lines
4.6 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2012 Red Hat
*
* Authors: Matthew Garrett
* Dave Airlie
*/
#include <linux/console.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_pciids.h>
#include "mgag200_drv.h"
int mgag200_modeset = -1;
MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
module_param_named(modeset, mgag200_modeset, int, 0400);
/*
* DRM driver
*/
DEFINE_DRM_GEM_FOPS(mgag200_driver_fops);
static struct drm_driver mgag200_driver = {
.driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
.fops = &mgag200_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
DRM_GEM_SHMEM_DRIVER_OPS,
};
/*
* DRM device
*/
static int mgag200_driver_load(struct drm_device *dev, unsigned long flags)
{
struct mga_device *mdev;
int ret, option;
mdev = devm_kzalloc(dev->dev, sizeof(struct mga_device), GFP_KERNEL);
if (mdev == NULL)
return -ENOMEM;
dev->dev_private = (void *)mdev;
mdev->dev = dev;
mdev->flags = mgag200_flags_from_driver_data(flags);
mdev->type = mgag200_type_from_driver_data(flags);
pci_read_config_dword(dev->pdev, PCI_MGA_OPTION, &option);
mdev->has_sdram = !(option & (1 << 14));
/* BAR 0 is the framebuffer, BAR 1 contains registers */
mdev->rmmio_base = pci_resource_start(dev->pdev, 1);
mdev->rmmio_size = pci_resource_len(dev->pdev, 1);
if (!devm_request_mem_region(dev->dev, mdev->rmmio_base,
mdev->rmmio_size, "mgadrmfb_mmio")) {
drm_err(dev, "can't reserve mmio registers\n");
return -ENOMEM;
}
mdev->rmmio = pcim_iomap(dev->pdev, 1, 0);
if (mdev->rmmio == NULL)
return -ENOMEM;
/* stash G200 SE model number for later use */
if (IS_G200_SE(mdev)) {
mdev->unique_rev_id = RREG32(0x1e24);
drm_dbg(dev, "G200 SE unique revision id is 0x%x\n",
mdev->unique_rev_id);
}
ret = mgag200_mm_init(mdev);
if (ret)
goto err_mm;
ret = mgag200_modeset_init(mdev);
if (ret) {
drm_err(dev, "Fatal error during modeset init: %d\n", ret);
goto err_mm;
}
return 0;
err_mm:
dev->dev_private = NULL;
return ret;
}
static void mgag200_driver_unload(struct drm_device *dev)
{
struct mga_device *mdev = to_mga_device(dev);
if (mdev == NULL)
return;
dev->dev_private = NULL;
}
/*
* PCI driver
*/
static const struct pci_device_id mgag200_pciidlist[] = {
drm/mgag200: Flag all G200 SE A machines as broken wrt <startadd> Several MGA G200 SE machines don't respect the value of the startadd register field. After more feedback on affected machines, neither PCI subvendor ID nor the internal ID seem to hint towards the bug. All affected machines have a PCI ID of 0x0522 (i.e., G200 SE A). It was decided to flag all G200 SE A machines as broken. Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de> Acked-by: Gerd Hoffmann <kraxel@redhat.com> Fixes: 1591fadf857c ("drm/mgag200: Add workaround for HW that does not support 'startadd'") Cc: Thomas Zimmermann <tzimmermann@suse.de> Cc: John Donnelly <john.p.donnelly@oracle.com> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Dave Airlie <airlied@redhat.com> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Cc: Maxime Ripard <mripard@kernel.org> Cc: David Airlie <airlied@linux.ie> Cc: Sam Ravnborg <sam@ravnborg.org> Cc: "Y.C. Chen" <yc_chen@aspeedtech.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: "José Roberto de Souza" <jose.souza@intel.com> Cc: Andrzej Pietrasiewicz <andrzej.p@collabora.com> Cc: dri-devel@lists.freedesktop.org Cc: <stable@vger.kernel.org> # v5.3+ Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Allison Randal <allison@lohutok.net> Cc: Alex Deucher <alexander.deucher@amd.com> Cc: "Noralf Trønnes" <noralf@tronnes.org> Link: https://patchwork.freedesktop.org/patch/msgid/20191206081901.9938-1-tzimmermann@suse.de
2019-12-06 09:19:01 +01:00
{ PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
G200_SE_A | MGAG200_FLAG_HW_BUG_NO_STARTADD},
{ PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B },
{ PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV },
{ PCI_VENDOR_ID_MATROX, 0x532, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_WB },
{ PCI_VENDOR_ID_MATROX, 0x533, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH },
{ PCI_VENDOR_ID_MATROX, 0x534, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_ER },
{ PCI_VENDOR_ID_MATROX, 0x536, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EW3 },
{ PCI_VENDOR_ID_MATROX, 0x538, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH3 },
{0,}
};
MODULE_DEVICE_TABLE(pci, mgag200_pciidlist);
static int
mgag200_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct drm_device *dev;
int ret;
drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, "mgag200drmfb");
ret = pcim_enable_device(pdev);
if (ret)
return ret;
dev = drm_dev_alloc(&mgag200_driver, &pdev->dev);
if (IS_ERR(dev))
return PTR_ERR(dev);
dev->pdev = pdev;
pci_set_drvdata(pdev, dev);
ret = mgag200_driver_load(dev, ent->driver_data);
if (ret)
goto err_drm_dev_put;
ret = drm_dev_register(dev, ent->driver_data);
if (ret)
goto err_mgag200_driver_unload;
drm_fbdev_generic_setup(dev, 0);
return 0;
err_mgag200_driver_unload:
mgag200_driver_unload(dev);
err_drm_dev_put:
drm_dev_put(dev);
return ret;
}
static void mgag200_pci_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
drm_dev_unregister(dev);
mgag200_driver_unload(dev);
drm_dev_put(dev);
}
static struct pci_driver mgag200_pci_driver = {
.name = DRIVER_NAME,
.id_table = mgag200_pciidlist,
.probe = mgag200_pci_probe,
.remove = mgag200_pci_remove,
};
static int __init mgag200_init(void)
{
if (vgacon_text_force() && mgag200_modeset == -1)
return -EINVAL;
if (mgag200_modeset == 0)
return -EINVAL;
return pci_register_driver(&mgag200_pci_driver);
}
static void __exit mgag200_exit(void)
{
pci_unregister_driver(&mgag200_pci_driver);
}
module_init(mgag200_init);
module_exit(mgag200_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");