summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/nouveau/compote
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/compote')
-rw-r--r--drivers/gpu/drm/nouveau/compote/Kbuild3
-rw-r--r--drivers/gpu/drm/nouveau/compote/compote-driver.c299
-rw-r--r--drivers/gpu/drm/nouveau/compote/compote-interface.h48
-rw-r--r--drivers/gpu/drm/nouveau/compote/compote.h37
4 files changed, 387 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/compote/Kbuild b/drivers/gpu/drm/nouveau/compote/Kbuild
new file mode 100644
index 000000000000..89af6841ab15
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/compote/Kbuild
@@ -0,0 +1,3 @@
+ifdef CONFIG_DRM_COMPOTE
+compote-y := compote/compote-driver.o
+endif
diff --git a/drivers/gpu/drm/nouveau/compote/compote-driver.c b/drivers/gpu/drm/nouveau/compote/compote-driver.c
new file mode 100644
index 000000000000..409e4c803c26
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/compote/compote-driver.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2017 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Authors: Jérôme Glisse <jglisse@redhat.com>
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include "compote.h"
+
+#include <nvif/driver.h>
+#include <nvif/class.h>
+
+#include "nouveau_ttm.h"
+#include "nouveau_usif.h"
+#include "nouveau_abi16.h"
+
+static DEFINE_SPINLOCK(compote_minor_lock);
+static DECLARE_BITMAP(compote_minor, 256);
+static struct class *compote_class = NULL;
+static dev_t _compote_devt;
+
+static u64 compote_name(struct nouveau_drm *nvdrm)
+{
+ struct pci_dev *pdev = nvdrm->dev->pdev;
+
+ u64 name = (u64)pci_domain_nr(pdev->bus) << 32;
+ name |= pdev->bus->number << 16;
+ name |= PCI_SLOT(pdev->devfn) << 8;
+ return name | PCI_FUNC(pdev->devfn);
+}
+
+static void compote_client_fini(struct nouveau_cli *nvclient)
+{
+ if (nvclient->vm)
+ nvkm_vm_ref(NULL, &nvclient->vm, NULL);
+ usif_client_fini(nvclient);
+ nvif_device_fini(&nvclient->device);
+ nvif_client_fini(&nvclient->base);
+}
+
+static int compote_client_init(struct nouveau_cli *nvclient,
+ struct nouveau_drm *nvdrm,
+ const char *sname)
+{
+ u64 device = compote_name(nvdrm);
+ int ret;
+
+ snprintf(nvclient->name, sizeof(nvclient->name), "%s", sname);
+ nvclient->dev = nvdrm->dev;
+ mutex_init(&nvclient->mutex);
+ usif_client_init(nvclient);
+
+ ret = nvif_client_init(&nvdrm->client.base, nvclient->name,
+ device, &nvclient->base);
+ if (ret) {
+ printk(KERN_ERR "nvif_client_init(): %d\n", ret);
+ goto error;
+ }
+
+ ret = nvif_device_init(&nvclient->base.object, 0, NV_DEVICE,
+ &(struct nv_device_v0) {
+ .device = ~0,
+ }, sizeof(struct nv_device_v0),
+ &nvclient->device);
+ if (ret) {
+ printk(KERN_ERR "nvif_device_init(): %d\n", ret);
+ goto error;
+ }
+ return 0;
+
+error:
+ compote_client_fini(nvclient);
+ return ret;
+}
+
+static int compote_open(struct inode *inode, struct file *file)
+{
+ struct compote_file *cfile = kzalloc(sizeof(*cfile), GFP_KERNEL);
+ char name[TASK_COMM_LEN + 8], tmpname[TASK_COMM_LEN];
+ struct compote_device *cdevice;
+ int ret;
+
+ if(cfile == NULL)
+ return -ENOMEM;
+
+ /* need to bring up power immediately if opening device */
+ cdevice = container_of(inode->i_cdev, struct compote_device, cdev);
+ ret = pm_runtime_get_sync(cdevice->nvdrm->dev->dev);
+ if (ret < 0 && ret != -EACCES) {
+ kfree(cfile);
+ return ret;
+ }
+
+ get_task_comm(tmpname, current);
+ snprintf(name, sizeof(name), "%s[%d]", tmpname,
+ pid_nr(get_pid(task_pid(current))));
+
+ cfile->cdevice = cdevice;
+ cfile->file = file;
+
+ ret = compote_client_init(&cfile->nvclient, cdevice->nvdrm, name);
+ if (ret)
+ goto error;
+
+ /*
+ * Too many place in nouveau that rely on 40bits so stick with that for
+ * now.
+ */
+ ret = nvkm_vm_new(nvxx_device(&cdevice->nvdrm->client.device), 0,
+ (1ULL << 40), 0x1000, NULL, &cfile->nvclient.vm);
+ if (ret)
+ goto error_vm;
+ nvxx_client(&cfile->nvclient.base)->vm = cfile->nvclient.vm;
+
+ /* share address_space across all char-devs of a single device */
+ file->f_mapping = cdevice->nvdrm->dev->anon_inode->i_mapping;
+
+ pm_runtime_mark_last_busy(cdevice->nvdrm->dev->dev);
+ pm_runtime_put_autosuspend(cdevice->nvdrm->dev->dev);
+ cfile->nvclient.base.super = false;
+ file->private_data = cfile;
+ return 0;
+
+error_vm:
+ compote_client_fini(&cfile->nvclient);
+error:
+ kfree(cfile);
+
+ pm_runtime_mark_last_busy(cdevice->nvdrm->dev->dev);
+ pm_runtime_put_autosuspend(cdevice->nvdrm->dev->dev);
+
+ return ret;
+}
+
+static int compote_close(struct inode *inode, struct file *file)
+{
+ struct compote_file *cfile = file->private_data;
+ struct compote_device *cdevice;
+
+ if(cfile == NULL)
+ return 0;
+
+ pm_runtime_get_sync(cfile->cdevice->nvdrm->dev->dev);
+
+ compote_client_fini(&cfile->nvclient);
+ cdevice = cfile->cdevice;
+ kfree(cfile);
+
+ pm_runtime_mark_last_busy(cdevice->nvdrm->dev->dev);
+ pm_runtime_put_autosuspend(cdevice->nvdrm->dev->dev);
+
+ return 0;
+}
+
+static long compote_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ return -EINVAL;
+}
+
+static const struct file_operations compote_fops = {
+ .open = compote_open,
+ .release = compote_close,
+ .unlocked_ioctl = compote_ioctl,
+ .owner = THIS_MODULE,
+};
+
+static bool compote_minor_get(int *minor)
+{
+ spin_lock(&compote_minor_lock);
+ *minor = bitmap_find_next_zero_area(compote_minor, 256, 0, 1, 0);
+ if ((*minor) < 256) {
+ bitmap_set(compote_minor, *minor, 1);
+ spin_unlock(&compote_minor_lock);
+ return true;
+ }
+ spin_unlock(&compote_minor_lock);
+ return false;
+}
+
+static void compote_minor_put(int minor)
+{
+ spin_lock(&compote_minor_lock);
+ bitmap_clear(compote_minor, minor, 1);
+ spin_unlock(&compote_minor_lock);
+}
+
+void compote_device_fini(void *compote_device)
+{
+ struct compote_device *cdevice = compote_device;
+
+ if (cdevice == NULL)
+ return;
+
+ if (cdevice->device) {
+ dev_t dev = MKDEV(MAJOR(_compote_devt), cdevice->minor);
+
+ device_destroy(compote_class, dev);
+ cdev_del(&cdevice->cdev);
+ }
+
+ compote_minor_put(cdevice->minor);
+ kfree(cdevice);
+}
+
+void *compote_device_init(struct nouveau_drm *nvdrm)
+{
+ const struct nv_device_info_v0 *devinfo = &nvdrm->client.device.info;
+ struct compote_device *cdevice;
+ dev_t dev;
+ int ret;
+
+ /* Only support maxwell and pascal */
+ switch (devinfo->family) {
+ case NV_DEVICE_INFO_V0_MAXWELL:
+ case NV_DEVICE_INFO_V0_PASCAL:
+ break;
+ default:
+ return NULL;
+ }
+
+ cdevice = kzalloc(sizeof(*cdevice), GFP_KERNEL);
+ if (cdevice == NULL)
+ return NULL;
+
+ cdevice->chipset = devinfo->chipset;
+ cdevice->nvdrm = nvdrm;
+
+ if (!compote_minor_get(&cdevice->minor)) {
+ kfree(cdevice);
+ return NULL;
+ }
+
+ dev = MKDEV(MAJOR(_compote_devt), cdevice->minor);
+ cdev_init(&cdevice->cdev, &compote_fops);
+ cdevice->cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&cdevice->cdev, dev, 1);
+ if (ret) {
+ kfree(cdevice);
+ return NULL;
+ }
+
+ cdevice->device = device_create(compote_class, nvdrm->dev->dev, dev,
+ NULL, "%s%d", COMPOTE_DEVICE_NAME,
+ cdevice->minor);
+ if (cdevice->device == NULL) {
+ cdev_del(&cdevice->cdev);
+ compote_device_fini(cdevice);
+ return NULL;
+ }
+
+ printk(KERN_INFO "%s%d for 0x%02x chipset\n", COMPOTE_DEVICE_NAME,
+ cdevice->minor, cdevice->chipset);
+ return cdevice;
+}
+
+void compote_exit(void)
+{
+ if (!compote_class)
+ return;
+
+ class_destroy(compote_class);
+ unregister_chrdev_region(_compote_devt, 256);
+}
+
+int compote_init(void)
+{
+ int ret;
+
+ ret = alloc_chrdev_region(&_compote_devt, 0, 256, COMPOTE_DEVICE_NAME);
+ if (ret)
+ return ret;
+
+ compote_class = class_create(THIS_MODULE, COMPOTE_DEVICE_NAME);
+ if (IS_ERR(compote_class)) {
+ int ret = PTR_ERR(compote_class);
+
+ unregister_chrdev_region(_compote_devt, 256);
+ compote_class = NULL;
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/compote/compote-interface.h b/drivers/gpu/drm/nouveau/compote/compote-interface.h
new file mode 100644
index 000000000000..f29f9c72432f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/compote/compote-interface.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Authors: Jérôme Glisse <jglisse@redhat.com>
+ */
+#ifndef COMPOTE_INTERFACE_H
+#define COMPOTE_INTERFACE_H
+
+#include <linux/kconfig.h>
+
+#if IS_ENABLED(CONFIG_DRM_COMPOTE)
+int compote_init(void);
+void compote_exit(void);
+void compote_device_fini(void *compote_device);
+void *compote_device_init(struct nouveau_drm *nvdrm);
+
+#else /* IS_ENABLED(CONFIG_DRM_COMPOTE) */
+
+static inline int compote_init(void)
+{
+ return 0;
+}
+
+static inline void compote_exit(void)
+{
+}
+
+static inline void *compote_device_init(struct nouveau_drm *nvdrm)
+{
+ return NULL;
+}
+
+static inline void compote_device_fini(void *compote_device)
+{
+}
+#endif /* IS_ENABLED(CONFIG_DRM_COMPOTE) */
+
+#endif /* COMPOTE_INTERFACE_H */
diff --git a/drivers/gpu/drm/nouveau/compote/compote.h b/drivers/gpu/drm/nouveau/compote/compote.h
new file mode 100644
index 000000000000..40f437703259
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/compote/compote.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Authors: Jérôme Glisse <jglisse@redhat.com>
+ */
+#ifndef COMPOTE_H
+#define COMPOTE_H
+
+#include "nouveau_drv.h"
+
+#define COMPOTE_DEVICE_NAME "compote"
+
+struct compote_device {
+ struct device *device;
+ struct nouveau_drm *nvdrm;
+ struct cdev cdev;
+ int minor;
+ uint8_t chipset;
+};
+
+struct compote_file {
+ struct compote_device *cdevice;
+ struct file *file;
+ struct nouveau_cli nvclient;
+};
+
+#endif /* COMPOTE_H */