* Authors: Ben Skeggs
*/
+#include <linux/console.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <subdev/vm.h>
#include "nouveau_drm.h"
+#include "nouveau_irq.h"
#include "nouveau_dma.h"
+#include "nouveau_ttm.h"
+#include "nouveau_gem.h"
#include "nouveau_agp.h"
+#include "nouveau_vga.h"
+#include "nouveau_pm.h"
+#include "nouveau_acpi.h"
+#include "nouveau_bios.h"
+#include "nouveau_ioctl.h"
#include "nouveau_abi16.h"
#include "nouveau_fbcon.h"
#include "nouveau_fence.h"
#include "nouveau_ttm.h"
-int __devinit nouveau_pci_probe(struct pci_dev *, const struct pci_device_id *);
-void nouveau_pci_remove(struct pci_dev *);
-int nouveau_pci_suspend(struct pci_dev *, pm_message_t);
-int nouveau_pci_resume(struct pci_dev *);
-int __init nouveau_init(struct pci_driver *);
-void __exit nouveau_exit(struct pci_driver *);
-
-int nouveau_load(struct drm_device *, unsigned long);
-int nouveau_unload(struct drm_device *);
-
MODULE_PARM_DESC(config, "option string to pass to driver core");
static char *nouveau_config;
module_param_named(config, nouveau_config, charp, 0400);
static int nouveau_noaccel = 0;
module_param_named(noaccel, nouveau_noaccel, int, 0400);
+MODULE_PARM_DESC(modeset, "enable driver");
+static int nouveau_modeset = -1;
+module_param_named(modeset, nouveau_modeset, int, 0400);
+
+static struct drm_driver driver;
+
static u64
nouveau_name(struct pci_dev *pdev)
{
}
static int
-nouveau_cli_create(struct pci_dev *pdev, u32 name, int size, void **pcli)
+nouveau_cli_create(struct pci_dev *pdev, const char *name,
+ int size, void **pcli)
{
struct nouveau_cli *cli;
int ret;
{
nouveau_gpuobj_ref(NULL, &drm->notify);
nouveau_channel_del(&drm->channel);
+ nouveau_channel_del(&drm->cechan);
if (drm->fence)
nouveau_fence(drm)->dtor(drm);
}
{
struct nouveau_device *device = nv_device(drm->device);
struct nouveau_object *object;
+ u32 arg0, arg1;
int ret;
if (nouveau_noaccel)
return;
}
+ if (device->card_type >= NV_E0) {
+ ret = nouveau_channel_new(drm, &drm->client, NVDRM_DEVICE,
+ NVDRM_CHAN + 1,
+ NVE0_CHANNEL_IND_ENGINE_CE0 |
+ NVE0_CHANNEL_IND_ENGINE_CE1, 0,
+ &drm->cechan);
+ if (ret)
+ NV_ERROR(drm, "failed to create ce channel, %d\n", ret);
+
+ arg0 = NVE0_CHANNEL_IND_ENGINE_GR;
+ arg1 = 0;
+ } else {
+ arg0 = NvDmaFB;
+ arg1 = NvDmaTT;
+ }
+
ret = nouveau_channel_new(drm, &drm->client, NVDRM_DEVICE, NVDRM_CHAN,
- NvDmaFB, NvDmaTT, &drm->channel);
+ arg0, arg1, &drm->channel);
if (ret) {
NV_ERROR(drm, "failed to create kernel channel, %d\n", ret);
nouveau_accel_fini(drm);
}
- nouveau_bo_move_init(drm->channel);
+ nouveau_bo_move_init(drm);
}
static int __devinit
pci_set_master(pdev);
- ret = nouveau_pci_probe(pdev, pent);
+ ret = drm_get_pci_dev(pdev, pent, &driver);
if (ret) {
nouveau_object_ref(NULL, (struct nouveau_object **)&device);
return ret;
return 0;
}
-int
+static int
nouveau_drm_load(struct drm_device *dev, unsigned long flags)
{
struct pci_dev *pdev = dev->pdev;
struct nouveau_drm *drm;
int ret;
- ret = nouveau_cli_create(pdev, 0, sizeof(*drm), (void**)&drm);
- dev->dev_private = drm;
+ ret = nouveau_cli_create(pdev, "DRM", sizeof(*drm), (void**)&drm);
if (ret)
return ret;
+ dev->dev_private = drm;
+ drm->dev = dev;
+
INIT_LIST_HEAD(&drm->clients);
spin_lock_init(&drm->tile.lock);
- drm->dev = dev;
/* make sure AGP controller is in a consistent state before we
* (possibly) execute vbios init tables (see nouveau_agp.h)
if (ret)
goto fail_device;
+ /* workaround an odd issue on nvc1 by disabling the device's
+ * nosnoop capability. hopefully won't cause issues until a
+ * better fix is found - assuming there is one...
+ */
device = nv_device(drm->device);
+ if (nv_device(drm->device)->chipset == 0xc1)
+ nv_mask(device, 0x00088080, 0x00000800, 0x00000000);
- /* initialise AGP */
+ nouveau_vga_init(drm);
nouveau_agp_init(drm);
if (device->card_type >= NV_50) {
ret = nouveau_ttm_init(drm);
if (ret)
- goto fail_device;
+ goto fail_ttm;
+
+ ret = nouveau_bios_init(dev);
+ if (ret)
+ goto fail_bios;
+
+ ret = nouveau_irq_init(dev);
+ if (ret)
+ goto fail_irq;
- ret = nouveau_load(dev, flags);
+ ret = nouveau_display_create(dev);
if (ret)
- goto fail_load;
+ goto fail_dispctor;
+
+ if (dev->mode_config.num_crtc) {
+ ret = nouveau_display_init(dev);
+ if (ret)
+ goto fail_dispinit;
+ }
+
+ nouveau_pm_init(dev);
nouveau_accel_init(drm);
nouveau_fbcon_init(dev);
return 0;
-fail_load:
+fail_dispinit:
+ nouveau_display_destroy(dev);
+fail_dispctor:
+ nouveau_irq_fini(dev);
+fail_irq:
+ nouveau_bios_takedown(dev);
+fail_bios:
nouveau_ttm_fini(drm);
+fail_ttm:
+ nouveau_agp_fini(drm);
+ nouveau_vga_fini(drm);
fail_device:
nouveau_cli_destroy(&drm->client);
return ret;
}
-int
+static int
nouveau_drm_unload(struct drm_device *dev)
{
- struct nouveau_drm *drm = nouveau_newpriv(dev);
- struct pci_dev *pdev = dev->pdev;
- int ret;
+ struct nouveau_drm *drm = nouveau_drm(dev);
nouveau_fbcon_fini(dev);
nouveau_accel_fini(drm);
- ret = nouveau_unload(dev);
- if (ret)
- return ret;
+ nouveau_pm_fini(dev);
+
+ nouveau_display_fini(dev);
+ nouveau_display_destroy(dev);
+
+ nouveau_irq_fini(dev);
+ nouveau_bios_takedown(dev);
nouveau_ttm_fini(drm);
nouveau_agp_fini(drm);
+ nouveau_vga_fini(drm);
- pci_set_drvdata(pdev, drm->client.base.device);
nouveau_cli_destroy(&drm->client);
return 0;
}
static void
nouveau_drm_remove(struct pci_dev *pdev)
{
+ struct drm_device *dev = pci_get_drvdata(pdev);
+ struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_object *device;
- nouveau_pci_remove(pdev);
- device = pci_get_drvdata(pdev);
+
+ device = drm->client.base.device;
+ drm_put_dev(dev);
+
nouveau_object_ref(NULL, &device);
nouveau_object_debug();
}
nouveau_drm_suspend(struct pci_dev *pdev, pm_message_t pm_state)
{
struct drm_device *dev = pci_get_drvdata(pdev);
- struct nouveau_drm *drm = nouveau_newpriv(dev);
+ struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_cli *cli;
int ret;
NV_INFO(drm, "suspending fbcon...\n");
nouveau_fbcon_set_suspend(dev, 1);
- NV_INFO(drm, "suspending drm...\n");
- ret = nouveau_pci_suspend(pdev, pm_state);
+ NV_INFO(drm, "suspending display...\n");
+ ret = nouveau_display_suspend(dev);
if (ret)
return ret;
nouveau_client_init(&cli->base);
}
- nouveau_pci_resume(pdev);
+ NV_INFO(drm, "resuming display...\n");
+ nouveau_display_resume(dev);
return ret;
}
nouveau_drm_resume(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
- struct nouveau_drm *drm = nouveau_newpriv(dev);
+ struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_cli *cli;
int ret;
if (drm->fence && nouveau_fence(drm)->resume)
nouveau_fence(drm)->resume(drm);
- return nouveau_pci_resume(pdev);
+ nouveau_run_vbios_init(dev);
+ nouveau_irq_postinstall(dev);
+ nouveau_pm_resume(dev);
+
+ NV_INFO(drm, "resuming display...\n");
+ nouveau_display_resume(dev);
+ return 0;
}
-int
+static int
nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
{
struct pci_dev *pdev = dev->pdev;
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_cli *cli;
+ char name[16];
int ret;
- ret = nouveau_cli_create(pdev, fpriv->pid, sizeof(*cli), (void **)&cli);
+ snprintf(name, sizeof(name), "%d", pid_nr(fpriv->pid));
+
+ ret = nouveau_cli_create(pdev, name, sizeof(*cli), (void **)&cli);
if (ret)
return ret;
return 0;
}
-void
+static void
nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv)
{
struct nouveau_cli *cli = nouveau_cli(fpriv);
mutex_unlock(&drm->client.mutex);
}
-void
+static void
nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
{
struct nouveau_cli *cli = nouveau_cli(fpriv);
nouveau_cli_destroy(cli);
}
+static struct drm_ioctl_desc
+nouveau_ioctls[] = {
+ DRM_IOCTL_DEF_DRV(NOUVEAU_GETPARAM, nouveau_abi16_ioctl_getparam, DRM_UNLOCKED|DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(NOUVEAU_SETPARAM, nouveau_abi16_ioctl_setparam, DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF_DRV(NOUVEAU_CHANNEL_ALLOC, nouveau_abi16_ioctl_channel_alloc, DRM_UNLOCKED|DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(NOUVEAU_CHANNEL_FREE, nouveau_abi16_ioctl_channel_free, DRM_UNLOCKED|DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(NOUVEAU_GROBJ_ALLOC, nouveau_abi16_ioctl_grobj_alloc, DRM_UNLOCKED|DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(NOUVEAU_NOTIFIEROBJ_ALLOC, nouveau_abi16_ioctl_notifierobj_alloc, DRM_UNLOCKED|DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(NOUVEAU_GPUOBJ_FREE, nouveau_abi16_ioctl_gpuobj_free, DRM_UNLOCKED|DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_UNLOCKED|DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_UNLOCKED|DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_UNLOCKED|DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_UNLOCKED|DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_UNLOCKED|DRM_AUTH),
+};
+
+static const struct file_operations
+nouveau_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+ .mmap = nouveau_ttm_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+ .read = drm_read,
+#if defined(CONFIG_COMPAT)
+ .compat_ioctl = nouveau_compat_ioctl,
+#endif
+ .llseek = noop_llseek,
+};
+
+static struct drm_driver
+driver = {
+ .driver_features =
+ DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG |
+ DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM |
+ DRIVER_MODESET | DRIVER_PRIME,
+
+ .load = nouveau_drm_load,
+ .unload = nouveau_drm_unload,
+ .open = nouveau_drm_open,
+ .preclose = nouveau_drm_preclose,
+ .postclose = nouveau_drm_postclose,
+ .lastclose = nouveau_vga_lastclose,
+
+ .irq_preinstall = nouveau_irq_preinstall,
+ .irq_postinstall = nouveau_irq_postinstall,
+ .irq_uninstall = nouveau_irq_uninstall,
+ .irq_handler = nouveau_irq_handler,
+
+ .get_vblank_counter = drm_vblank_count,
+ .enable_vblank = nouveau_vblank_enable,
+ .disable_vblank = nouveau_vblank_disable,
+
+ .ioctls = nouveau_ioctls,
+ .fops = &nouveau_driver_fops,
+
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_export = nouveau_gem_prime_export,
+ .gem_prime_import = nouveau_gem_prime_import,
+
+ .gem_init_object = nouveau_gem_object_new,
+ .gem_free_object = nouveau_gem_object_del,
+ .gem_open_object = nouveau_gem_object_open,
+ .gem_close_object = nouveau_gem_object_close,
+
+ .dumb_create = nouveau_display_dumb_create,
+ .dumb_map_offset = nouveau_display_dumb_map_offset,
+ .dumb_destroy = nouveau_display_dumb_destroy,
+
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+#ifdef GIT_REVISION
+ .date = GIT_REVISION,
+#else
+ .date = DRIVER_DATE,
+#endif
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = DRIVER_PATCHLEVEL,
+};
+
static struct pci_device_id
nouveau_drm_pci_table[] = {
{
static int __init
nouveau_drm_init(void)
{
- return nouveau_init(&nouveau_drm_pci_driver);
+ driver.num_ioctls = ARRAY_SIZE(nouveau_ioctls);
+
+ if (nouveau_modeset == -1) {
+#ifdef CONFIG_VGA_CONSOLE
+ if (vgacon_text_force())
+ nouveau_modeset = 0;
+ else
+#endif
+ nouveau_modeset = 1;
+ }
+
+ if (!nouveau_modeset)
+ return 0;
+
+ nouveau_register_dsm_handler();
+ return drm_pci_init(&driver, &nouveau_drm_pci_driver);
}
static void __exit
nouveau_drm_exit(void)
{
- nouveau_exit(&nouveau_drm_pci_driver);
+ if (!nouveau_modeset)
+ return;
+
+ drm_pci_exit(&driver, &nouveau_drm_pci_driver);
+ nouveau_unregister_dsm_handler();
}
module_init(nouveau_drm_init);
module_exit(nouveau_drm_exit);
MODULE_DEVICE_TABLE(pci, nouveau_drm_pci_table);
-MODULE_AUTHOR("Nouveau Project");
-MODULE_DESCRIPTION("nVidia Riva/TNT/GeForce/Quadro/Tesla");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL and additional rights");