]> Pileus Git - ~andy/linux/blobdiff - drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drm/vmwgfx: Implement a buffer object synccpu ioctl.
[~andy/linux] / drivers / gpu / drm / vmwgfx / vmwgfx_drv.c
index 0508f93b9795fbc2b4ccf9c8f34ea62383a7e138..fb56676ed3eeb3fbb58412a69a20cd19e841ed74 100644 (file)
@@ -32,6 +32,7 @@
 #include <drm/ttm/ttm_bo_driver.h>
 #include <drm/ttm/ttm_object.h>
 #include <drm/ttm/ttm_module.h>
+#include <linux/dma_remapping.h>
 
 #define VMWGFX_DRIVER_NAME "vmwgfx"
 #define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices"
 #define DRM_IOCTL_VMW_UPDATE_LAYOUT                            \
        DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UPDATE_LAYOUT,       \
                 struct drm_vmw_update_layout_arg)
+#define DRM_IOCTL_VMW_CREATE_SHADER                            \
+       DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_CREATE_SHADER,      \
+                struct drm_vmw_shader_create_arg)
+#define DRM_IOCTL_VMW_UNREF_SHADER                             \
+       DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_SHADER,        \
+                struct drm_vmw_shader_arg)
+#define DRM_IOCTL_VMW_GB_SURFACE_CREATE                                \
+       DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_GB_SURFACE_CREATE,  \
+                union drm_vmw_gb_surface_create_arg)
+#define DRM_IOCTL_VMW_GB_SURFACE_REF                           \
+       DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_GB_SURFACE_REF,     \
+                union drm_vmw_gb_surface_reference_arg)
+#define DRM_IOCTL_VMW_SYNCCPU                                  \
+       DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_SYNCCPU,             \
+                struct drm_vmw_synccpu_arg)
 
 /**
  * The core DRM version of this macro doesn't account for
@@ -176,6 +192,21 @@ static const struct drm_ioctl_desc vmw_ioctls[] = {
        VMW_IOCTL_DEF(VMW_UPDATE_LAYOUT,
                      vmw_kms_update_layout_ioctl,
                      DRM_MASTER | DRM_UNLOCKED),
+       VMW_IOCTL_DEF(VMW_CREATE_SHADER,
+                     vmw_shader_define_ioctl,
+                     DRM_AUTH | DRM_UNLOCKED),
+       VMW_IOCTL_DEF(VMW_UNREF_SHADER,
+                     vmw_shader_destroy_ioctl,
+                     DRM_AUTH | DRM_UNLOCKED),
+       VMW_IOCTL_DEF(VMW_GB_SURFACE_CREATE,
+                     vmw_gb_surface_define_ioctl,
+                     DRM_AUTH | DRM_UNLOCKED),
+       VMW_IOCTL_DEF(VMW_GB_SURFACE_REF,
+                     vmw_gb_surface_reference_ioctl,
+                     DRM_AUTH | DRM_UNLOCKED),
+       VMW_IOCTL_DEF(VMW_SYNCCPU,
+                     vmw_user_dmabuf_synccpu_ioctl,
+                     DRM_AUTH | DRM_UNLOCKED),
 };
 
 static struct pci_device_id vmw_pci_id_list[] = {
@@ -185,6 +216,10 @@ static struct pci_device_id vmw_pci_id_list[] = {
 MODULE_DEVICE_TABLE(pci, vmw_pci_id_list);
 
 static int enable_fbdev = IS_ENABLED(CONFIG_DRM_VMWGFX_FBCON);
+static int vmw_force_iommu;
+static int vmw_restrict_iommu;
+static int vmw_force_coherent;
+static int vmw_restrict_dma_mask;
 
 static int vmw_probe(struct pci_dev *, const struct pci_device_id *);
 static void vmw_master_init(struct vmw_master *);
@@ -193,6 +228,15 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
 
 MODULE_PARM_DESC(enable_fbdev, "Enable vmwgfx fbdev");
 module_param_named(enable_fbdev, enable_fbdev, int, 0600);
+MODULE_PARM_DESC(force_dma_api, "Force using the DMA API for TTM pages");
+module_param_named(force_dma_api, vmw_force_iommu, int, 0600);
+MODULE_PARM_DESC(restrict_iommu, "Try to limit IOMMU usage for TTM pages");
+module_param_named(restrict_iommu, vmw_restrict_iommu, int, 0600);
+MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages");
+module_param_named(force_coherent, vmw_force_coherent, int, 0600);
+MODULE_PARM_DESC(restrict_dma_mask, "Restrict DMA mask to 44 bits with IOMMU");
+module_param_named(restrict_dma_mask, vmw_restrict_dma_mask, int, 0600);
+
 
 static void vmw_print_capabilities(uint32_t capabilities)
 {
@@ -229,6 +273,12 @@ static void vmw_print_capabilities(uint32_t capabilities)
                DRM_INFO("  GMR2.\n");
        if (capabilities & SVGA_CAP_SCREEN_OBJECT_2)
                DRM_INFO("  Screen Object 2.\n");
+       if (capabilities & SVGA_CAP_COMMAND_BUFFERS)
+               DRM_INFO("  Command Buffers.\n");
+       if (capabilities & SVGA_CAP_CMD_BUFFERS_2)
+               DRM_INFO("  Command Buffers 2.\n");
+       if (capabilities & SVGA_CAP_GBOBJECTS)
+               DRM_INFO("  Guest Backed Resources.\n");
 }
 
 
@@ -307,6 +357,14 @@ static int vmw_request_device(struct vmw_private *dev_priv)
                return ret;
        }
        vmw_fence_fifo_up(dev_priv->fman);
+       if (dev_priv->has_mob) {
+               ret = vmw_otables_setup(dev_priv);
+               if (unlikely(ret != 0)) {
+                       DRM_ERROR("Unable to initialize "
+                                 "guest Memory OBjects.\n");
+                       goto out_no_mob;
+               }
+       }
        ret = vmw_dummy_query_bo_create(dev_priv);
        if (unlikely(ret != 0))
                goto out_no_query_bo;
@@ -315,6 +373,9 @@ static int vmw_request_device(struct vmw_private *dev_priv)
        return 0;
 
 out_no_query_bo:
+       if (dev_priv->has_mob)
+               vmw_otables_takedown(dev_priv);
+out_no_mob:
        vmw_fence_fifo_down(dev_priv->fman);
        vmw_fifo_release(dev_priv, &dev_priv->fifo);
        return ret;
@@ -330,10 +391,13 @@ static void vmw_release_device(struct vmw_private *dev_priv)
        BUG_ON(dev_priv->pinned_bo != NULL);
 
        ttm_bo_unref(&dev_priv->dummy_query_bo);
+       if (dev_priv->has_mob)
+               vmw_otables_takedown(dev_priv);
        vmw_fence_fifo_down(dev_priv->fman);
        vmw_fifo_release(dev_priv, &dev_priv->fifo);
 }
 
+
 /**
  * Increase the 3d resource refcount.
  * If the count was prevously zero, initialize the fifo, switching to svga
@@ -427,12 +491,112 @@ static void vmw_get_initial_size(struct vmw_private *dev_priv)
        dev_priv->initial_height = height;
 }
 
+/**
+ * vmw_dma_select_mode - Determine how DMA mappings should be set up for this
+ * system.
+ *
+ * @dev_priv: Pointer to a struct vmw_private
+ *
+ * This functions tries to determine the IOMMU setup and what actions
+ * need to be taken by the driver to make system pages visible to the
+ * device.
+ * If this function decides that DMA is not possible, it returns -EINVAL.
+ * The driver may then try to disable features of the device that require
+ * DMA.
+ */
+static int vmw_dma_select_mode(struct vmw_private *dev_priv)
+{
+       static const char *names[vmw_dma_map_max] = {
+               [vmw_dma_phys] = "Using physical TTM page addresses.",
+               [vmw_dma_alloc_coherent] = "Using coherent TTM pages.",
+               [vmw_dma_map_populate] = "Keeping DMA mappings.",
+               [vmw_dma_map_bind] = "Giving up DMA mappings early."};
+#ifdef CONFIG_X86
+       const struct dma_map_ops *dma_ops = get_dma_ops(dev_priv->dev->dev);
+
+#ifdef CONFIG_INTEL_IOMMU
+       if (intel_iommu_enabled) {
+               dev_priv->map_mode = vmw_dma_map_populate;
+               goto out_fixup;
+       }
+#endif
+
+       if (!(vmw_force_iommu || vmw_force_coherent)) {
+               dev_priv->map_mode = vmw_dma_phys;
+               DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]);
+               return 0;
+       }
+
+       dev_priv->map_mode = vmw_dma_map_populate;
+
+       if (dma_ops->sync_single_for_cpu)
+               dev_priv->map_mode = vmw_dma_alloc_coherent;
+#ifdef CONFIG_SWIOTLB
+       if (swiotlb_nr_tbl() == 0)
+               dev_priv->map_mode = vmw_dma_map_populate;
+#endif
+
+#ifdef CONFIG_INTEL_IOMMU
+out_fixup:
+#endif
+       if (dev_priv->map_mode == vmw_dma_map_populate &&
+           vmw_restrict_iommu)
+               dev_priv->map_mode = vmw_dma_map_bind;
+
+       if (vmw_force_coherent)
+               dev_priv->map_mode = vmw_dma_alloc_coherent;
+
+#if !defined(CONFIG_SWIOTLB) && !defined(CONFIG_INTEL_IOMMU)
+       /*
+        * No coherent page pool
+        */
+       if (dev_priv->map_mode == vmw_dma_alloc_coherent)
+               return -EINVAL;
+#endif
+
+#else /* CONFIG_X86 */
+       dev_priv->map_mode = vmw_dma_map_populate;
+#endif /* CONFIG_X86 */
+
+       DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]);
+
+       return 0;
+}
+
+/**
+ * vmw_dma_masks - set required page- and dma masks
+ *
+ * @dev: Pointer to struct drm-device
+ *
+ * With 32-bit we can only handle 32 bit PFNs. Optionally set that
+ * restriction also for 64-bit systems.
+ */
+#ifdef CONFIG_INTEL_IOMMU
+static int vmw_dma_masks(struct vmw_private *dev_priv)
+{
+       struct drm_device *dev = dev_priv->dev;
+
+       if (intel_iommu_enabled &&
+           (sizeof(unsigned long) == 4 || vmw_restrict_dma_mask)) {
+               DRM_INFO("Restricting DMA addresses to 44 bits.\n");
+               return dma_set_mask(dev->dev, DMA_BIT_MASK(44));
+       }
+       return 0;
+}
+#else
+static int vmw_dma_masks(struct vmw_private *dev_priv)
+{
+       return 0;
+}
+#endif
+
 static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
 {
        struct vmw_private *dev_priv;
        int ret;
        uint32_t svga_id;
        enum vmw_res_type i;
+       bool refuse_dma = false;
 
        dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
        if (unlikely(dev_priv == NULL)) {
@@ -481,6 +645,11 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
        }
 
        dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES);
+       ret = vmw_dma_select_mode(dev_priv);
+       if (unlikely(ret != 0)) {
+               DRM_INFO("Restricting capabilities due to IOMMU setup.\n");
+               refuse_dma = true;
+       }
 
        dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE);
        dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE);
@@ -489,14 +658,9 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
 
        vmw_get_initial_size(dev_priv);
 
-       if (dev_priv->capabilities & SVGA_CAP_GMR) {
-               dev_priv->max_gmr_descriptors =
-                       vmw_read(dev_priv,
-                                SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH);
+       if (dev_priv->capabilities & SVGA_CAP_GMR2) {
                dev_priv->max_gmr_ids =
                        vmw_read(dev_priv, SVGA_REG_GMR_MAX_IDS);
-       }
-       if (dev_priv->capabilities & SVGA_CAP_GMR2) {
                dev_priv->max_gmr_pages =
                        vmw_read(dev_priv, SVGA_REG_GMRS_MAX_PAGES);
                dev_priv->memory_size =
@@ -509,23 +673,40 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
                 */
                dev_priv->memory_size = 512*1024*1024;
        }
+       dev_priv->max_mob_pages = 0;
+       if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) {
+               uint64_t mem_size =
+                       vmw_read(dev_priv,
+                                SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB);
+
+               dev_priv->max_mob_pages = mem_size * 1024 / PAGE_SIZE;
+               dev_priv->prim_bb_mem =
+                       vmw_read(dev_priv,
+                                SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM);
+       } else
+               dev_priv->prim_bb_mem = dev_priv->vram_size;
+
+       ret = vmw_dma_masks(dev_priv);
+       if (unlikely(ret != 0))
+               goto out_err0;
+
+       if (unlikely(dev_priv->prim_bb_mem < dev_priv->vram_size))
+               dev_priv->prim_bb_mem = dev_priv->vram_size;
 
        mutex_unlock(&dev_priv->hw_mutex);
 
        vmw_print_capabilities(dev_priv->capabilities);
 
-       if (dev_priv->capabilities & SVGA_CAP_GMR) {
+       if (dev_priv->capabilities & SVGA_CAP_GMR2) {
                DRM_INFO("Max GMR ids is %u\n",
                         (unsigned)dev_priv->max_gmr_ids);
-               DRM_INFO("Max GMR descriptors is %u\n",
-                        (unsigned)dev_priv->max_gmr_descriptors);
-       }
-       if (dev_priv->capabilities & SVGA_CAP_GMR2) {
                DRM_INFO("Max number of GMR pages is %u\n",
                         (unsigned)dev_priv->max_gmr_pages);
                DRM_INFO("Max dedicated hypervisor surface memory is %u kiB\n",
                         (unsigned)dev_priv->memory_size / 1024);
        }
+       DRM_INFO("Maximum display memory size is %u kiB\n",
+                dev_priv->prim_bb_mem / 1024);
        DRM_INFO("VRAM at 0x%08x size is %u kiB\n",
                 dev_priv->vram_start, dev_priv->vram_size / 1024);
        DRM_INFO("MMIO at 0x%08x size is %u kiB\n",
@@ -558,13 +739,24 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
        }
 
        dev_priv->has_gmr = true;
-       if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR,
-                          dev_priv->max_gmr_ids) != 0) {
+       if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) ||
+           refuse_dma || ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR,
+                                        VMW_PL_GMR) != 0) {
                DRM_INFO("No GMR memory available. "
                         "Graphics memory resources are very limited.\n");
                dev_priv->has_gmr = false;
        }
 
+       if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) {
+               dev_priv->has_mob = true;
+               if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_MOB,
+                                  VMW_PL_MOB) != 0) {
+                       DRM_INFO("No MOB memory available. "
+                                "3D will be disabled.\n");
+                       dev_priv->has_mob = false;
+               }
+       }
+
        dev_priv->mmio_mtrr = arch_phys_wc_add(dev_priv->mmio_start,
                                               dev_priv->mmio_size);
 
@@ -587,7 +779,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
        }
 
        dev_priv->tdev = ttm_object_device_init
-           (dev_priv->mem_global_ref.object, 12);
+               (dev_priv->mem_global_ref.object, 12, &vmw_prime_dmabuf_ops);
 
        if (unlikely(dev_priv->tdev == NULL)) {
                DRM_ERROR("Unable to initialize TTM object management.\n");
@@ -667,6 +859,8 @@ out_err4:
        iounmap(dev_priv->mmio_virt);
 out_err3:
        arch_phys_wc_del(dev_priv->mmio_mtrr);
+       if (dev_priv->has_mob)
+               (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB);
        if (dev_priv->has_gmr)
                (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);
        (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
@@ -711,6 +905,8 @@ static int vmw_driver_unload(struct drm_device *dev)
        ttm_object_device_release(&dev_priv->tdev);
        iounmap(dev_priv->mmio_virt);
        arch_phys_wc_del(dev_priv->mmio_mtrr);
+       if (dev_priv->has_mob)
+               (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB);
        if (dev_priv->has_gmr)
                (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);
        (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
@@ -1120,7 +1316,7 @@ static const struct file_operations vmwgfx_driver_fops = {
 
 static struct drm_driver driver = {
        .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED |
-       DRIVER_MODESET,
+       DRIVER_MODESET | DRIVER_PRIME,
        .load = vmw_driver_load,
        .unload = vmw_driver_unload,
        .lastclose = vmw_lastclose,
@@ -1145,6 +1341,9 @@ static struct drm_driver driver = {
        .dumb_map_offset = vmw_dumb_map_offset,
        .dumb_destroy = vmw_dumb_destroy,
 
+       .prime_fd_to_handle = vmw_prime_fd_to_handle,
+       .prime_handle_to_fd = vmw_prime_handle_to_fd,
+
        .fops = &vmwgfx_driver_fops,
        .name = VMWGFX_DRIVER_NAME,
        .desc = VMWGFX_DRIVER_DESC,