diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/compote')
-rw-r--r-- | drivers/gpu/drm/nouveau/compote/Kbuild | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/compote/compote-driver.c | 299 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/compote/compote-interface.h | 48 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/compote/compote.h | 37 |
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 */ |