| /* |
| * Copyright © 2016 Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| * |
| */ |
| |
| #include <linux/pm_domain.h> |
| #include <linux/pm_runtime.h> |
| |
| #include "mock_engine.h" |
| #include "mock_context.h" |
| #include "mock_request.h" |
| #include "mock_gem_device.h" |
| #include "mock_gem_object.h" |
| #include "mock_gtt.h" |
| #include "mock_uncore.h" |
| |
| void mock_device_flush(struct drm_i915_private *i915) |
| { |
| struct intel_engine_cs *engine; |
| enum intel_engine_id id; |
| |
| lockdep_assert_held(&i915->drm.struct_mutex); |
| |
| for_each_engine(engine, i915, id) |
| mock_engine_flush(engine); |
| |
| i915_retire_requests(i915); |
| GEM_BUG_ON(i915->gt.active_requests); |
| } |
| |
| static void mock_device_release(struct drm_device *dev) |
| { |
| struct drm_i915_private *i915 = to_i915(dev); |
| struct intel_engine_cs *engine; |
| enum intel_engine_id id; |
| |
| mutex_lock(&i915->drm.struct_mutex); |
| mock_device_flush(i915); |
| i915_gem_contexts_lost(i915); |
| mutex_unlock(&i915->drm.struct_mutex); |
| |
| cancel_delayed_work_sync(&i915->gt.retire_work); |
| cancel_delayed_work_sync(&i915->gt.idle_work); |
| i915_gem_drain_workqueue(i915); |
| |
| mutex_lock(&i915->drm.struct_mutex); |
| for_each_engine(engine, i915, id) |
| mock_engine_free(engine); |
| i915_gem_contexts_fini(i915); |
| mutex_unlock(&i915->drm.struct_mutex); |
| |
| drain_workqueue(i915->wq); |
| i915_gem_drain_freed_objects(i915); |
| |
| mutex_lock(&i915->drm.struct_mutex); |
| mock_fini_ggtt(i915); |
| mutex_unlock(&i915->drm.struct_mutex); |
| WARN_ON(!list_empty(&i915->gt.timelines)); |
| |
| destroy_workqueue(i915->wq); |
| |
| kmem_cache_destroy(i915->priorities); |
| kmem_cache_destroy(i915->dependencies); |
| kmem_cache_destroy(i915->requests); |
| kmem_cache_destroy(i915->vmas); |
| kmem_cache_destroy(i915->objects); |
| |
| i915_gemfs_fini(i915); |
| |
| drm_mode_config_cleanup(&i915->drm); |
| |
| drm_dev_fini(&i915->drm); |
| put_device(&i915->drm.pdev->dev); |
| } |
| |
| static struct drm_driver mock_driver = { |
| .name = "mock", |
| .driver_features = DRIVER_GEM, |
| .release = mock_device_release, |
| |
| .gem_close_object = i915_gem_close_object, |
| .gem_free_object_unlocked = i915_gem_free_object, |
| }; |
| |
| static void release_dev(struct device *dev) |
| { |
| struct pci_dev *pdev = to_pci_dev(dev); |
| |
| kfree(pdev); |
| } |
| |
| static void mock_retire_work_handler(struct work_struct *work) |
| { |
| } |
| |
| static void mock_idle_work_handler(struct work_struct *work) |
| { |
| } |
| |
| static int pm_domain_resume(struct device *dev) |
| { |
| return pm_generic_runtime_resume(dev); |
| } |
| |
| static int pm_domain_suspend(struct device *dev) |
| { |
| return pm_generic_runtime_suspend(dev); |
| } |
| |
| static struct dev_pm_domain pm_domain = { |
| .ops = { |
| .runtime_suspend = pm_domain_suspend, |
| .runtime_resume = pm_domain_resume, |
| }, |
| }; |
| |
| struct drm_i915_private *mock_gem_device(void) |
| { |
| struct drm_i915_private *i915; |
| struct pci_dev *pdev; |
| int err; |
| |
| pdev = kzalloc(sizeof(*pdev) + sizeof(*i915), GFP_KERNEL); |
| if (!pdev) |
| goto err; |
| |
| device_initialize(&pdev->dev); |
| pdev->class = PCI_BASE_CLASS_DISPLAY << 16; |
| pdev->dev.release = release_dev; |
| dev_set_name(&pdev->dev, "mock"); |
| dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); |
| |
| #if IS_ENABLED(CONFIG_IOMMU_API) && defined(CONFIG_INTEL_IOMMU) |
| /* hack to disable iommu for the fake device; force identity mapping */ |
| pdev->dev.archdata.iommu = (void *)-1; |
| #endif |
| |
| dev_pm_domain_set(&pdev->dev, &pm_domain); |
| pm_runtime_enable(&pdev->dev); |
| pm_runtime_dont_use_autosuspend(&pdev->dev); |
| if (pm_runtime_enabled(&pdev->dev)) |
| WARN_ON(pm_runtime_get_sync(&pdev->dev)); |
| |
| i915 = (struct drm_i915_private *)(pdev + 1); |
| pci_set_drvdata(pdev, i915); |
| |
| err = drm_dev_init(&i915->drm, &mock_driver, &pdev->dev); |
| if (err) { |
| pr_err("Failed to initialise mock GEM device: err=%d\n", err); |
| goto put_device; |
| } |
| i915->drm.pdev = pdev; |
| i915->drm.dev_private = i915; |
| |
| /* Using the global GTT may ask questions about KMS users, so prepare */ |
| drm_mode_config_init(&i915->drm); |
| |
| mkwrite_device_info(i915)->gen = -1; |
| |
| mkwrite_device_info(i915)->page_sizes = |
| I915_GTT_PAGE_SIZE_4K | |
| I915_GTT_PAGE_SIZE_64K | |
| I915_GTT_PAGE_SIZE_2M; |
| |
| mock_uncore_init(i915); |
| i915_gem_init__mm(i915); |
| |
| init_waitqueue_head(&i915->gpu_error.wait_queue); |
| init_waitqueue_head(&i915->gpu_error.reset_queue); |
| |
| i915->wq = alloc_ordered_workqueue("mock", 0); |
| if (!i915->wq) |
| goto err_drv; |
| |
| mock_init_contexts(i915); |
| |
| INIT_DELAYED_WORK(&i915->gt.retire_work, mock_retire_work_handler); |
| INIT_DELAYED_WORK(&i915->gt.idle_work, mock_idle_work_handler); |
| |
| i915->gt.awake = true; |
| |
| i915->objects = KMEM_CACHE(mock_object, SLAB_HWCACHE_ALIGN); |
| if (!i915->objects) |
| goto err_wq; |
| |
| i915->vmas = KMEM_CACHE(i915_vma, SLAB_HWCACHE_ALIGN); |
| if (!i915->vmas) |
| goto err_objects; |
| |
| i915->requests = KMEM_CACHE(mock_request, |
| SLAB_HWCACHE_ALIGN | |
| SLAB_RECLAIM_ACCOUNT | |
| SLAB_TYPESAFE_BY_RCU); |
| if (!i915->requests) |
| goto err_vmas; |
| |
| i915->dependencies = KMEM_CACHE(i915_dependency, |
| SLAB_HWCACHE_ALIGN | |
| SLAB_RECLAIM_ACCOUNT); |
| if (!i915->dependencies) |
| goto err_requests; |
| |
| i915->priorities = KMEM_CACHE(i915_priolist, SLAB_HWCACHE_ALIGN); |
| if (!i915->priorities) |
| goto err_dependencies; |
| |
| INIT_LIST_HEAD(&i915->gt.timelines); |
| INIT_LIST_HEAD(&i915->gt.active_rings); |
| INIT_LIST_HEAD(&i915->gt.closed_vma); |
| |
| mutex_lock(&i915->drm.struct_mutex); |
| |
| mock_init_ggtt(i915); |
| |
| mkwrite_device_info(i915)->ring_mask = BIT(0); |
| i915->kernel_context = mock_context(i915, NULL); |
| if (!i915->kernel_context) |
| goto err_unlock; |
| |
| i915->engine[RCS] = mock_engine(i915, "mock", RCS); |
| if (!i915->engine[RCS]) |
| goto err_context; |
| |
| mutex_unlock(&i915->drm.struct_mutex); |
| |
| WARN_ON(i915_gemfs_init(i915)); |
| |
| return i915; |
| |
| err_context: |
| i915_gem_contexts_fini(i915); |
| err_unlock: |
| mutex_unlock(&i915->drm.struct_mutex); |
| kmem_cache_destroy(i915->priorities); |
| err_dependencies: |
| kmem_cache_destroy(i915->dependencies); |
| err_requests: |
| kmem_cache_destroy(i915->requests); |
| err_vmas: |
| kmem_cache_destroy(i915->vmas); |
| err_objects: |
| kmem_cache_destroy(i915->objects); |
| err_wq: |
| destroy_workqueue(i915->wq); |
| err_drv: |
| drm_mode_config_cleanup(&i915->drm); |
| drm_dev_fini(&i915->drm); |
| put_device: |
| put_device(&pdev->dev); |
| err: |
| return NULL; |
| } |