diff options
Diffstat (limited to 'drivers/gpu/drm/msm/msm_drv.c')
-rw-r--r-- | drivers/gpu/drm/msm/msm_drv.c | 383 |
1 files changed, 185 insertions, 198 deletions
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index c3588dc9e537..9dcc7a596a11 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -11,7 +11,6 @@ #include <linux/of_address.h> #include <linux/uaccess.h> -#include <drm/clients/drm_client_setup.h> #include <drm/drm_drv.h> #include <drm/drm_file.h> #include <drm/drm_ioctl.h> @@ -41,17 +40,12 @@ * - 1.10.0 - Add MSM_SUBMIT_BO_NO_IMPLICIT * - 1.11.0 - Add wait boost (MSM_WAIT_FENCE_BOOST, MSM_PREP_BOOST) * - 1.12.0 - Add MSM_INFO_SET_METADATA and MSM_INFO_GET_METADATA + * - 1.13.0 - Add VM_BIND */ #define MSM_VERSION_MAJOR 1 -#define MSM_VERSION_MINOR 12 +#define MSM_VERSION_MINOR 13 #define MSM_VERSION_PATCHLEVEL 0 -static void msm_deinit_vram(struct drm_device *ddev); - -static char *vram = "16m"; -MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU)"); -module_param(vram, charp, 0); - bool dumpstate; MODULE_PARM_DESC(dumpstate, "Dump KMS state on errors"); module_param(dumpstate, bool, 0600); @@ -60,10 +54,19 @@ static bool modeset = true; MODULE_PARM_DESC(modeset, "Use kernel modesetting [KMS] (1=on (default), 0=disable)"); module_param(modeset, bool, 0600); +static bool separate_gpu_kms; +MODULE_PARM_DESC(separate_gpu_drm, "Use separate DRM device for the GPU (0=single DRM device for both GPU and display (default), 1=two DRM devices)"); +module_param(separate_gpu_kms, bool, 0400); + DECLARE_FAULT_ATTR(fail_gem_alloc); DECLARE_FAULT_ATTR(fail_gem_iova); -static int msm_drm_uninit(struct device *dev) +bool msm_gpu_no_components(void) +{ + return separate_gpu_kms; +} + +static int msm_drm_uninit(struct device *dev, const struct component_ops *gpu_ops) { struct platform_device *pdev = to_platform_device(dev); struct msm_drm_private *priv = platform_get_drvdata(pdev); @@ -79,16 +82,9 @@ static int msm_drm_uninit(struct device *dev) if (ddev->registered) { drm_dev_unregister(ddev); if (priv->kms) - drm_atomic_helper_shutdown(ddev); + msm_drm_kms_unregister(dev); } - /* We must cancel and cleanup any pending vblank enable/disable - * work before msm_irq_uninstall() to avoid work re-enabling an - * irq after uninstall has disabled it. - */ - - flush_workqueue(priv->wq); - msm_gem_shrinker_cleanup(ddev); msm_perf_debugfs_cleanup(priv); @@ -97,120 +93,19 @@ static int msm_drm_uninit(struct device *dev) if (priv->kms) msm_drm_kms_uninit(dev); - msm_deinit_vram(ddev); - - component_unbind_all(dev, ddev); + if (gpu_ops) + gpu_ops->unbind(dev, dev, NULL); + else + component_unbind_all(dev, ddev); ddev->dev_private = NULL; drm_dev_put(ddev); - destroy_workqueue(priv->wq); - return 0; } -bool msm_use_mmu(struct drm_device *dev) -{ - struct msm_drm_private *priv = dev->dev_private; - - /* - * a2xx comes with its own MMU - * On other platforms IOMMU can be declared specified either for the - * MDP/DPU device or for its parent, MDSS device. - */ - return priv->is_a2xx || - device_iommu_mapped(dev->dev) || - device_iommu_mapped(dev->dev->parent); -} - -static int msm_init_vram(struct drm_device *dev) -{ - struct msm_drm_private *priv = dev->dev_private; - struct device_node *node; - unsigned long size = 0; - int ret = 0; - - /* In the device-tree world, we could have a 'memory-region' - * phandle, which gives us a link to our "vram". Allocating - * is all nicely abstracted behind the dma api, but we need - * to know the entire size to allocate it all in one go. There - * are two cases: - * 1) device with no IOMMU, in which case we need exclusive - * access to a VRAM carveout big enough for all gpu - * buffers - * 2) device with IOMMU, but where the bootloader puts up - * a splash screen. In this case, the VRAM carveout - * need only be large enough for fbdev fb. But we need - * exclusive access to the buffer to avoid the kernel - * using those pages for other purposes (which appears - * as corruption on screen before we have a chance to - * load and do initial modeset) - */ - - node = of_parse_phandle(dev->dev->of_node, "memory-region", 0); - if (node) { - struct resource r; - ret = of_address_to_resource(node, 0, &r); - of_node_put(node); - if (ret) - return ret; - size = r.end - r.start + 1; - DRM_INFO("using VRAM carveout: %lx@%pa\n", size, &r.start); - - /* if we have no IOMMU, then we need to use carveout allocator. - * Grab the entire DMA chunk carved out in early startup in - * mach-msm: - */ - } else if (!msm_use_mmu(dev)) { - DRM_INFO("using %s VRAM carveout\n", vram); - size = memparse(vram, NULL); - } - - if (size) { - unsigned long attrs = 0; - void *p; - - priv->vram.size = size; - - drm_mm_init(&priv->vram.mm, 0, (size >> PAGE_SHIFT) - 1); - spin_lock_init(&priv->vram.lock); - - attrs |= DMA_ATTR_NO_KERNEL_MAPPING; - attrs |= DMA_ATTR_WRITE_COMBINE; - - /* note that for no-kernel-mapping, the vaddr returned - * is bogus, but non-null if allocation succeeded: - */ - p = dma_alloc_attrs(dev->dev, size, - &priv->vram.paddr, GFP_KERNEL, attrs); - if (!p) { - DRM_DEV_ERROR(dev->dev, "failed to allocate VRAM\n"); - priv->vram.paddr = 0; - return -ENOMEM; - } - - DRM_DEV_INFO(dev->dev, "VRAM: %08x->%08x\n", - (uint32_t)priv->vram.paddr, - (uint32_t)(priv->vram.paddr + size)); - } - - return ret; -} - -static void msm_deinit_vram(struct drm_device *ddev) -{ - struct msm_drm_private *priv = ddev->dev_private; - unsigned long attrs = DMA_ATTR_NO_KERNEL_MAPPING; - - if (!priv->vram.paddr) - return; - - drm_mm_takedown(&priv->vram.mm); - dma_free_attrs(ddev->dev, priv->vram.size, NULL, priv->vram.paddr, - attrs); -} - -static int msm_drm_init(struct device *dev, const struct drm_driver *drv) +static int msm_drm_init(struct device *dev, const struct drm_driver *drv, + const struct component_ops *gpu_ops) { struct msm_drm_private *priv = dev_get_drvdata(dev); struct drm_device *ddev; @@ -227,12 +122,6 @@ static int msm_drm_init(struct device *dev, const struct drm_driver *drv) ddev->dev_private = priv; priv->dev = ddev; - priv->wq = alloc_ordered_workqueue("msm", 0); - if (!priv->wq) { - ret = -ENOMEM; - goto err_put_dev; - } - INIT_LIST_HEAD(&priv->objects); mutex_init(&priv->obj_lock); @@ -245,6 +134,10 @@ static int msm_drm_init(struct device *dev, const struct drm_driver *drv) drm_gem_lru_init(&priv->lru.willneed, &priv->lru.lock); drm_gem_lru_init(&priv->lru.dontneed, &priv->lru.lock); + /* Initialize stall-on-fault */ + spin_lock_init(&priv->fault_stall_lock); + priv->stall_enabled = true; + /* Teach lockdep about lock ordering wrt. shrinker: */ fs_reclaim_acquire(GFP_KERNEL); might_lock(&priv->lru.lock); @@ -253,19 +146,18 @@ static int msm_drm_init(struct device *dev, const struct drm_driver *drv) if (priv->kms_init) { ret = drmm_mode_config_init(ddev); if (ret) - goto err_destroy_wq; + goto err_put_dev; } - ret = msm_init_vram(ddev); - if (ret) - goto err_destroy_wq; - dma_set_max_seg_size(dev, UINT_MAX); /* Bind all our sub-components: */ - ret = component_bind_all(dev, ddev); + if (gpu_ops) + ret = gpu_ops->bind(dev, dev, NULL); + else + ret = component_bind_all(dev, ddev); if (ret) - goto err_deinit_vram; + goto err_put_dev; ret = msm_gem_shrinker_init(ddev); if (ret) @@ -275,11 +167,6 @@ static int msm_drm_init(struct device *dev, const struct drm_driver *drv) ret = msm_drm_kms_init(dev, drv); if (ret) goto err_msm_uninit; - } else { - /* valid only for the dummy headless case, where of_node=NULL */ - WARN_ON(dev->of_node); - ddev->driver_features &= ~DRIVER_MODESET; - ddev->driver_features &= ~DRIVER_ATOMIC; } ret = drm_dev_register(ddev, 0); @@ -290,22 +177,16 @@ static int msm_drm_init(struct device *dev, const struct drm_driver *drv) if (ret) goto err_msm_uninit; - if (priv->kms_init) { - drm_kms_helper_poll_init(ddev); - drm_client_setup(ddev, NULL); - } + if (priv->kms_init) + msm_drm_kms_post_init(dev); return 0; err_msm_uninit: - msm_drm_uninit(dev); + msm_drm_uninit(dev, gpu_ops); return ret; -err_deinit_vram: - msm_deinit_vram(ddev); -err_destroy_wq: - destroy_workqueue(priv->wq); err_put_dev: drm_dev_put(ddev); @@ -329,11 +210,42 @@ static void load_gpu(struct drm_device *dev) mutex_unlock(&init_lock); } +/** + * msm_context_vm - lazily create the context's VM + * + * @dev: the drm device + * @ctx: the context + * + * The VM is lazily created, so that userspace has a chance to opt-in to having + * a userspace managed VM before the VM is created. + * + * Note that this does not return a reference to the VM. Once the VM is created, + * it exists for the lifetime of the context. + */ +struct drm_gpuvm *msm_context_vm(struct drm_device *dev, struct msm_context *ctx) +{ + static DEFINE_MUTEX(init_lock); + struct msm_drm_private *priv = dev->dev_private; + + /* Once ctx->vm is created it is valid for the lifetime of the context: */ + if (ctx->vm) + return ctx->vm; + + mutex_lock(&init_lock); + if (!ctx->vm) { + ctx->vm = msm_gpu_create_private_vm( + priv->gpu, current, !ctx->userspace_managed_vm); + + } + mutex_unlock(&init_lock); + + return ctx->vm; +} + static int context_init(struct drm_device *dev, struct drm_file *file) { static atomic_t ident = ATOMIC_INIT(0); - struct msm_drm_private *priv = dev->dev_private; - struct msm_file_private *ctx; + struct msm_context *ctx; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -345,7 +257,6 @@ static int context_init(struct drm_device *dev, struct drm_file *file) kref_init(&ctx->ref); msm_submitqueue_init(dev, ctx); - ctx->aspace = msm_gpu_create_private_address_space(priv->gpu, current); file->driver_priv = ctx; ctx->seqno = atomic_inc_return(&ident); @@ -363,23 +274,24 @@ static int msm_open(struct drm_device *dev, struct drm_file *file) return context_init(dev, file); } -static void context_close(struct msm_file_private *ctx) +static void context_close(struct msm_context *ctx) { + ctx->closed = true; msm_submitqueue_close(ctx); - msm_file_private_put(ctx); + msm_context_put(ctx); } static void msm_postclose(struct drm_device *dev, struct drm_file *file) { struct msm_drm_private *priv = dev->dev_private; - struct msm_file_private *ctx = file->driver_priv; + struct msm_context *ctx = file->driver_priv; /* * It is not possible to set sysprof param to non-zero if gpu * is not initialized: */ if (priv->gpu) - msm_file_private_set_sysprof(ctx, priv->gpu, 0); + msm_context_set_sysprof(ctx, priv->gpu, 0); context_close(ctx); } @@ -511,11 +423,14 @@ static int msm_ioctl_gem_info_iova(struct drm_device *dev, uint64_t *iova) { struct msm_drm_private *priv = dev->dev_private; - struct msm_file_private *ctx = file->driver_priv; + struct msm_context *ctx = file->driver_priv; if (!priv->gpu) return -EINVAL; + if (msm_context_is_vmbind(ctx)) + return UERR(EINVAL, dev, "VM_BIND is enabled"); + if (should_fail(&fail_gem_iova, obj->size)) return -ENOMEM; @@ -523,7 +438,7 @@ static int msm_ioctl_gem_info_iova(struct drm_device *dev, * Don't pin the memory here - just get an address so that userspace can * be productive */ - return msm_gem_get_iova(obj, ctx->aspace, iova); + return msm_gem_get_iova(obj, msm_context_vm(dev, ctx), iova); } static int msm_ioctl_gem_info_set_iova(struct drm_device *dev, @@ -531,19 +446,23 @@ static int msm_ioctl_gem_info_set_iova(struct drm_device *dev, uint64_t iova) { struct msm_drm_private *priv = dev->dev_private; - struct msm_file_private *ctx = file->driver_priv; + struct msm_context *ctx = file->driver_priv; + struct drm_gpuvm *vm = msm_context_vm(dev, ctx); if (!priv->gpu) return -EINVAL; + if (msm_context_is_vmbind(ctx)) + return UERR(EINVAL, dev, "VM_BIND is enabled"); + /* Only supported if per-process address space is supported: */ - if (priv->gpu->aspace == ctx->aspace) + if (priv->gpu->vm == vm) return UERR(EOPNOTSUPP, dev, "requires per-process pgtables"); if (should_fail(&fail_gem_iova, obj->size)) return -ENOMEM; - return msm_gem_set_iova(obj, ctx->aspace, iova); + return msm_gem_set_iova(obj, vm, iova); } static int msm_ioctl_gem_info_set_metadata(struct drm_gem_object *obj, @@ -551,6 +470,7 @@ static int msm_ioctl_gem_info_set_metadata(struct drm_gem_object *obj, u32 metadata_size) { struct msm_gem_object *msm_obj = to_msm_bo(obj); + void *new_metadata; void *buf; int ret; @@ -568,8 +488,14 @@ static int msm_ioctl_gem_info_set_metadata(struct drm_gem_object *obj, if (ret) goto out; - msm_obj->metadata = + new_metadata = krealloc(msm_obj->metadata, metadata_size, GFP_KERNEL); + if (!new_metadata) { + ret = -ENOMEM; + goto out; + } + + msm_obj->metadata = new_metadata; msm_obj->metadata_size = metadata_size; memcpy(msm_obj->metadata, buf, metadata_size); @@ -671,7 +597,7 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data, ret = msm_ioctl_gem_info_set_iova(dev, file, obj, args->value); break; case MSM_INFO_GET_FLAGS: - if (obj->import_attach) { + if (drm_gem_is_imported(obj)) { ret = -EINVAL; break; } @@ -868,6 +794,7 @@ static const struct drm_ioctl_desc msm_ioctls[] = { DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_NEW, msm_ioctl_submitqueue_new, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_CLOSE, msm_ioctl_submitqueue_close, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_QUERY, msm_ioctl_submitqueue_query, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_VM_BIND, msm_ioctl_vm_bind, DRM_RENDER_ALLOW), }; static void msm_show_fdinfo(struct drm_printer *p, struct drm_file *file) @@ -889,17 +816,27 @@ static const struct file_operations fops = { .show_fdinfo = drm_show_fdinfo, }; +#define DRIVER_FEATURES_GPU ( \ + DRIVER_GEM | \ + DRIVER_GEM_GPUVA | \ + DRIVER_RENDER | \ + DRIVER_SYNCOBJ | \ + DRIVER_SYNCOBJ_TIMELINE | \ + 0 ) + +#define DRIVER_FEATURES_KMS ( \ + DRIVER_GEM | \ + DRIVER_ATOMIC | \ + DRIVER_MODESET | \ + 0 ) + static const struct drm_driver msm_driver = { - .driver_features = DRIVER_GEM | - DRIVER_RENDER | - DRIVER_ATOMIC | - DRIVER_MODESET | - DRIVER_SYNCOBJ_TIMELINE | - DRIVER_SYNCOBJ, + .driver_features = DRIVER_FEATURES_GPU | DRIVER_FEATURES_KMS, .open = msm_open, .postclose = msm_postclose, .dumb_create = msm_gem_dumb_create, .dumb_map_offset = msm_gem_dumb_map_offset, + .gem_prime_import = msm_gem_prime_import, .gem_prime_import_sg_table = msm_gem_prime_import_sg_table, #ifdef CONFIG_DEBUG_FS .debugfs_init = msm_debugfs_init, @@ -916,6 +853,45 @@ static const struct drm_driver msm_driver = { .patchlevel = MSM_VERSION_PATCHLEVEL, }; +static const struct drm_driver msm_kms_driver = { + .driver_features = DRIVER_FEATURES_KMS, + .open = msm_open, + .postclose = msm_postclose, + .dumb_create = msm_gem_dumb_create, + .dumb_map_offset = msm_gem_dumb_map_offset, + .gem_prime_import_sg_table = msm_gem_prime_import_sg_table, +#ifdef CONFIG_DEBUG_FS + .debugfs_init = msm_debugfs_init, +#endif + MSM_FBDEV_DRIVER_OPS, + .show_fdinfo = msm_show_fdinfo, + .fops = &fops, + .name = "msm-kms", + .desc = "MSM Snapdragon DRM", + .major = MSM_VERSION_MAJOR, + .minor = MSM_VERSION_MINOR, + .patchlevel = MSM_VERSION_PATCHLEVEL, +}; + +static const struct drm_driver msm_gpu_driver = { + .driver_features = DRIVER_FEATURES_GPU, + .open = msm_open, + .postclose = msm_postclose, + .gem_prime_import_sg_table = msm_gem_prime_import_sg_table, +#ifdef CONFIG_DEBUG_FS + .debugfs_init = msm_debugfs_init, +#endif + .show_fdinfo = msm_show_fdinfo, + .ioctls = msm_ioctls, + .num_ioctls = ARRAY_SIZE(msm_ioctls), + .fops = &fops, + .name = "msm", + .desc = "MSM Snapdragon DRM", + .major = MSM_VERSION_MAJOR, + .minor = MSM_VERSION_MINOR, + .patchlevel = MSM_VERSION_PATCHLEVEL, +}; + /* * Componentized driver support: */ @@ -926,7 +902,7 @@ static const struct drm_driver msm_driver = { * is no external component that we need to add since LVDS is within MDP4 * itself. */ -static int add_components_mdp(struct device *master_dev, +static int add_mdp_components(struct device *master_dev, struct component_match **matchptr) { struct device_node *np = master_dev->of_node; @@ -1030,7 +1006,7 @@ static int add_gpu_components(struct device *dev, if (!np) return 0; - if (of_device_is_available(np)) + if (of_device_is_available(np) && adreno_has_gpu(np)) drm_of_component_match_add(dev, matchptr, component_compare_of, np); of_node_put(np); @@ -1040,12 +1016,16 @@ static int add_gpu_components(struct device *dev, static int msm_drm_bind(struct device *dev) { - return msm_drm_init(dev, &msm_driver); + return msm_drm_init(dev, + msm_gpu_no_components() ? + &msm_kms_driver : + &msm_driver, + NULL); } static void msm_drm_unbind(struct device *dev) { - msm_drm_uninit(dev); + msm_drm_uninit(dev, NULL); } const struct component_master_ops msm_drm_ops = { @@ -1071,14 +1051,16 @@ int msm_drv_probe(struct device *master_dev, /* Add mdp components if we have KMS. */ if (kms_init) { - ret = add_components_mdp(master_dev, &match); + ret = add_mdp_components(master_dev, &match); if (ret) return ret; } - ret = add_gpu_components(master_dev, &match); - if (ret) - return ret; + if (!msm_gpu_no_components()) { + ret = add_gpu_components(master_dev, &match); + if (ret) + return ret; + } /* on all devices that I am aware of, iommu's which can map * any address the cpu can see are used: @@ -1094,29 +1076,34 @@ int msm_drv_probe(struct device *master_dev, return 0; } -/* - * Platform driver: - * Used only for headlesss GPU instances - */ - -static int msm_pdev_probe(struct platform_device *pdev) +int msm_gpu_probe(struct platform_device *pdev, + const struct component_ops *ops) { - return msm_drv_probe(&pdev->dev, NULL, NULL); + struct msm_drm_private *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + /* on all devices that I am aware of, iommu's which can map + * any address the cpu can see are used: + */ + ret = dma_set_mask_and_coherent(&pdev->dev, ~0); + if (ret) + return ret; + + return msm_drm_init(&pdev->dev, &msm_gpu_driver, ops); } -static void msm_pdev_remove(struct platform_device *pdev) +void msm_gpu_remove(struct platform_device *pdev, + const struct component_ops *ops) { - component_master_del(&pdev->dev, &msm_drm_ops); + msm_drm_uninit(&pdev->dev, ops); } -static struct platform_driver msm_platform_driver = { - .probe = msm_pdev_probe, - .remove = msm_pdev_remove, - .driver = { - .name = "msm", - }, -}; - static int __init msm_drm_register(void) { if (!modeset) @@ -1131,13 +1118,13 @@ static int __init msm_drm_register(void) adreno_register(); msm_mdp4_register(); msm_mdss_register(); - return platform_driver_register(&msm_platform_driver); + + return 0; } static void __exit msm_drm_unregister(void) { DBG("fini"); - platform_driver_unregister(&msm_platform_driver); msm_mdss_unregister(); msm_mdp4_unregister(); msm_dp_unregister(); |