1a1d2a633SQiang Yu // SPDX-License-Identifier: GPL-2.0 OR MIT 2a1d2a633SQiang Yu /* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */ 3a1d2a633SQiang Yu 4a1d2a633SQiang Yu #include <linux/module.h> 5a1d2a633SQiang Yu #include <linux/of_platform.h> 6a1d2a633SQiang Yu #include <linux/uaccess.h> 7a1d2a633SQiang Yu #include <linux/slab.h> 8a1d2a633SQiang Yu #include <drm/drm_ioctl.h> 9a1d2a633SQiang Yu #include <drm/drm_drv.h> 10a1d2a633SQiang Yu #include <drm/drm_prime.h> 11a1d2a633SQiang Yu #include <drm/lima_drm.h> 12a1d2a633SQiang Yu 13a1d2a633SQiang Yu #include "lima_drv.h" 14a1d2a633SQiang Yu #include "lima_gem.h" 15a1d2a633SQiang Yu #include "lima_vm.h" 16a1d2a633SQiang Yu 17a1d2a633SQiang Yu int lima_sched_timeout_ms; 186aebc51dSQiang Yu uint lima_heap_init_nr_pages = 8; 19*c67a3d4fSQiang Yu uint lima_max_error_tasks; 20a1d2a633SQiang Yu 2147ab1457SErico Nunes MODULE_PARM_DESC(sched_timeout_ms, "task run timeout in ms"); 22a1d2a633SQiang Yu module_param_named(sched_timeout_ms, lima_sched_timeout_ms, int, 0444); 23a1d2a633SQiang Yu 246aebc51dSQiang Yu MODULE_PARM_DESC(heap_init_nr_pages, "heap buffer init number of pages"); 256aebc51dSQiang Yu module_param_named(heap_init_nr_pages, lima_heap_init_nr_pages, uint, 0444); 266aebc51dSQiang Yu 27*c67a3d4fSQiang Yu MODULE_PARM_DESC(max_error_tasks, "max number of error tasks to save"); 28*c67a3d4fSQiang Yu module_param_named(max_error_tasks, lima_max_error_tasks, uint, 0644); 29*c67a3d4fSQiang Yu 30a1d2a633SQiang Yu static int lima_ioctl_get_param(struct drm_device *dev, void *data, struct drm_file *file) 31a1d2a633SQiang Yu { 32a1d2a633SQiang Yu struct drm_lima_get_param *args = data; 33a1d2a633SQiang Yu struct lima_device *ldev = to_lima_dev(dev); 34a1d2a633SQiang Yu 35a1d2a633SQiang Yu if (args->pad) 36a1d2a633SQiang Yu return -EINVAL; 37a1d2a633SQiang Yu 38a1d2a633SQiang Yu switch (args->param) { 39a1d2a633SQiang Yu case DRM_LIMA_PARAM_GPU_ID: 40a1d2a633SQiang Yu switch (ldev->id) { 41a1d2a633SQiang Yu case lima_gpu_mali400: 42a1d2a633SQiang Yu args->value = DRM_LIMA_PARAM_GPU_ID_MALI400; 43a1d2a633SQiang Yu break; 44a1d2a633SQiang Yu case lima_gpu_mali450: 45a1d2a633SQiang Yu args->value = DRM_LIMA_PARAM_GPU_ID_MALI450; 46a1d2a633SQiang Yu break; 47a1d2a633SQiang Yu default: 48a1d2a633SQiang Yu args->value = DRM_LIMA_PARAM_GPU_ID_UNKNOWN; 49a1d2a633SQiang Yu break; 50a1d2a633SQiang Yu } 51a1d2a633SQiang Yu break; 52a1d2a633SQiang Yu 53a1d2a633SQiang Yu case DRM_LIMA_PARAM_NUM_PP: 54a1d2a633SQiang Yu args->value = ldev->pipe[lima_pipe_pp].num_processor; 55a1d2a633SQiang Yu break; 56a1d2a633SQiang Yu 57a1d2a633SQiang Yu case DRM_LIMA_PARAM_GP_VERSION: 58a1d2a633SQiang Yu args->value = ldev->gp_version; 59a1d2a633SQiang Yu break; 60a1d2a633SQiang Yu 61a1d2a633SQiang Yu case DRM_LIMA_PARAM_PP_VERSION: 62a1d2a633SQiang Yu args->value = ldev->pp_version; 63a1d2a633SQiang Yu break; 64a1d2a633SQiang Yu 65a1d2a633SQiang Yu default: 66a1d2a633SQiang Yu return -EINVAL; 67a1d2a633SQiang Yu } 68a1d2a633SQiang Yu 69a1d2a633SQiang Yu return 0; 70a1d2a633SQiang Yu } 71a1d2a633SQiang Yu 72a1d2a633SQiang Yu static int lima_ioctl_gem_create(struct drm_device *dev, void *data, struct drm_file *file) 73a1d2a633SQiang Yu { 74a1d2a633SQiang Yu struct drm_lima_gem_create *args = data; 75a1d2a633SQiang Yu 76a1d2a633SQiang Yu if (args->pad) 77a1d2a633SQiang Yu return -EINVAL; 78a1d2a633SQiang Yu 796aebc51dSQiang Yu if (args->flags & ~(LIMA_BO_FLAG_HEAP)) 80a1d2a633SQiang Yu return -EINVAL; 81a1d2a633SQiang Yu 82a1d2a633SQiang Yu if (args->size == 0) 83a1d2a633SQiang Yu return -EINVAL; 84a1d2a633SQiang Yu 85a1d2a633SQiang Yu return lima_gem_create_handle(dev, file, args->size, args->flags, &args->handle); 86a1d2a633SQiang Yu } 87a1d2a633SQiang Yu 88a1d2a633SQiang Yu static int lima_ioctl_gem_info(struct drm_device *dev, void *data, struct drm_file *file) 89a1d2a633SQiang Yu { 90a1d2a633SQiang Yu struct drm_lima_gem_info *args = data; 91a1d2a633SQiang Yu 92a1d2a633SQiang Yu return lima_gem_get_info(file, args->handle, &args->va, &args->offset); 93a1d2a633SQiang Yu } 94a1d2a633SQiang Yu 95a1d2a633SQiang Yu static int lima_ioctl_gem_submit(struct drm_device *dev, void *data, struct drm_file *file) 96a1d2a633SQiang Yu { 97a1d2a633SQiang Yu struct drm_lima_gem_submit *args = data; 98a1d2a633SQiang Yu struct lima_device *ldev = to_lima_dev(dev); 99a1d2a633SQiang Yu struct lima_drm_priv *priv = file->driver_priv; 100a1d2a633SQiang Yu struct drm_lima_gem_submit_bo *bos; 101a1d2a633SQiang Yu struct lima_sched_pipe *pipe; 102a1d2a633SQiang Yu struct lima_sched_task *task; 103a1d2a633SQiang Yu struct lima_ctx *ctx; 104a1d2a633SQiang Yu struct lima_submit submit = {0}; 105a1d2a633SQiang Yu size_t size; 106a1d2a633SQiang Yu int err = 0; 107a1d2a633SQiang Yu 108a1d2a633SQiang Yu if (args->pipe >= lima_pipe_num || args->nr_bos == 0) 109a1d2a633SQiang Yu return -EINVAL; 110a1d2a633SQiang Yu 111a1d2a633SQiang Yu if (args->flags & ~(LIMA_SUBMIT_FLAG_EXPLICIT_FENCE)) 112a1d2a633SQiang Yu return -EINVAL; 113a1d2a633SQiang Yu 114a1d2a633SQiang Yu pipe = ldev->pipe + args->pipe; 115a1d2a633SQiang Yu if (args->frame_size != pipe->frame_size) 116a1d2a633SQiang Yu return -EINVAL; 117a1d2a633SQiang Yu 118a1d2a633SQiang Yu bos = kvcalloc(args->nr_bos, sizeof(*submit.bos) + sizeof(*submit.lbos), GFP_KERNEL); 119a1d2a633SQiang Yu if (!bos) 120a1d2a633SQiang Yu return -ENOMEM; 121a1d2a633SQiang Yu 122a1d2a633SQiang Yu size = args->nr_bos * sizeof(*submit.bos); 123a1d2a633SQiang Yu if (copy_from_user(bos, u64_to_user_ptr(args->bos), size)) { 124a1d2a633SQiang Yu err = -EFAULT; 125a1d2a633SQiang Yu goto out0; 126a1d2a633SQiang Yu } 127a1d2a633SQiang Yu 128a1d2a633SQiang Yu task = kmem_cache_zalloc(pipe->task_slab, GFP_KERNEL); 129a1d2a633SQiang Yu if (!task) { 130a1d2a633SQiang Yu err = -ENOMEM; 131a1d2a633SQiang Yu goto out0; 132a1d2a633SQiang Yu } 133a1d2a633SQiang Yu 134a1d2a633SQiang Yu task->frame = task + 1; 135a1d2a633SQiang Yu if (copy_from_user(task->frame, u64_to_user_ptr(args->frame), args->frame_size)) { 136a1d2a633SQiang Yu err = -EFAULT; 137a1d2a633SQiang Yu goto out1; 138a1d2a633SQiang Yu } 139a1d2a633SQiang Yu 140a1d2a633SQiang Yu err = pipe->task_validate(pipe, task); 141a1d2a633SQiang Yu if (err) 142a1d2a633SQiang Yu goto out1; 143a1d2a633SQiang Yu 144a1d2a633SQiang Yu ctx = lima_ctx_get(&priv->ctx_mgr, args->ctx); 145a1d2a633SQiang Yu if (!ctx) { 146a1d2a633SQiang Yu err = -ENOENT; 147a1d2a633SQiang Yu goto out1; 148a1d2a633SQiang Yu } 149a1d2a633SQiang Yu 150a1d2a633SQiang Yu submit.pipe = args->pipe; 151a1d2a633SQiang Yu submit.bos = bos; 152a1d2a633SQiang Yu submit.lbos = (void *)bos + size; 153a1d2a633SQiang Yu submit.nr_bos = args->nr_bos; 154a1d2a633SQiang Yu submit.task = task; 155a1d2a633SQiang Yu submit.ctx = ctx; 156a1d2a633SQiang Yu submit.flags = args->flags; 157a1d2a633SQiang Yu submit.in_sync[0] = args->in_sync[0]; 158a1d2a633SQiang Yu submit.in_sync[1] = args->in_sync[1]; 159a1d2a633SQiang Yu submit.out_sync = args->out_sync; 160a1d2a633SQiang Yu 161a1d2a633SQiang Yu err = lima_gem_submit(file, &submit); 162a1d2a633SQiang Yu 163a1d2a633SQiang Yu lima_ctx_put(ctx); 164a1d2a633SQiang Yu out1: 165a1d2a633SQiang Yu if (err) 166a1d2a633SQiang Yu kmem_cache_free(pipe->task_slab, task); 167a1d2a633SQiang Yu out0: 168a1d2a633SQiang Yu kvfree(bos); 169a1d2a633SQiang Yu return err; 170a1d2a633SQiang Yu } 171a1d2a633SQiang Yu 172a1d2a633SQiang Yu static int lima_ioctl_gem_wait(struct drm_device *dev, void *data, struct drm_file *file) 173a1d2a633SQiang Yu { 174a1d2a633SQiang Yu struct drm_lima_gem_wait *args = data; 175a1d2a633SQiang Yu 176a1d2a633SQiang Yu if (args->op & ~(LIMA_GEM_WAIT_READ|LIMA_GEM_WAIT_WRITE)) 177a1d2a633SQiang Yu return -EINVAL; 178a1d2a633SQiang Yu 179a1d2a633SQiang Yu return lima_gem_wait(file, args->handle, args->op, args->timeout_ns); 180a1d2a633SQiang Yu } 181a1d2a633SQiang Yu 182a1d2a633SQiang Yu static int lima_ioctl_ctx_create(struct drm_device *dev, void *data, struct drm_file *file) 183a1d2a633SQiang Yu { 184a1d2a633SQiang Yu struct drm_lima_ctx_create *args = data; 185a1d2a633SQiang Yu struct lima_drm_priv *priv = file->driver_priv; 186a1d2a633SQiang Yu struct lima_device *ldev = to_lima_dev(dev); 187a1d2a633SQiang Yu 188a1d2a633SQiang Yu if (args->_pad) 189a1d2a633SQiang Yu return -EINVAL; 190a1d2a633SQiang Yu 191a1d2a633SQiang Yu return lima_ctx_create(ldev, &priv->ctx_mgr, &args->id); 192a1d2a633SQiang Yu } 193a1d2a633SQiang Yu 194a1d2a633SQiang Yu static int lima_ioctl_ctx_free(struct drm_device *dev, void *data, struct drm_file *file) 195a1d2a633SQiang Yu { 196a1d2a633SQiang Yu struct drm_lima_ctx_create *args = data; 197a1d2a633SQiang Yu struct lima_drm_priv *priv = file->driver_priv; 198a1d2a633SQiang Yu 199a1d2a633SQiang Yu if (args->_pad) 200a1d2a633SQiang Yu return -EINVAL; 201a1d2a633SQiang Yu 202a1d2a633SQiang Yu return lima_ctx_free(&priv->ctx_mgr, args->id); 203a1d2a633SQiang Yu } 204a1d2a633SQiang Yu 205a1d2a633SQiang Yu static int lima_drm_driver_open(struct drm_device *dev, struct drm_file *file) 206a1d2a633SQiang Yu { 207a1d2a633SQiang Yu int err; 208a1d2a633SQiang Yu struct lima_drm_priv *priv; 209a1d2a633SQiang Yu struct lima_device *ldev = to_lima_dev(dev); 210a1d2a633SQiang Yu 211a1d2a633SQiang Yu priv = kzalloc(sizeof(*priv), GFP_KERNEL); 212a1d2a633SQiang Yu if (!priv) 213a1d2a633SQiang Yu return -ENOMEM; 214a1d2a633SQiang Yu 215a1d2a633SQiang Yu priv->vm = lima_vm_create(ldev); 216a1d2a633SQiang Yu if (!priv->vm) { 217a1d2a633SQiang Yu err = -ENOMEM; 218a1d2a633SQiang Yu goto err_out0; 219a1d2a633SQiang Yu } 220a1d2a633SQiang Yu 221a1d2a633SQiang Yu lima_ctx_mgr_init(&priv->ctx_mgr); 222a1d2a633SQiang Yu 223a1d2a633SQiang Yu file->driver_priv = priv; 224a1d2a633SQiang Yu return 0; 225a1d2a633SQiang Yu 226a1d2a633SQiang Yu err_out0: 227a1d2a633SQiang Yu kfree(priv); 228a1d2a633SQiang Yu return err; 229a1d2a633SQiang Yu } 230a1d2a633SQiang Yu 231a1d2a633SQiang Yu static void lima_drm_driver_postclose(struct drm_device *dev, struct drm_file *file) 232a1d2a633SQiang Yu { 233a1d2a633SQiang Yu struct lima_drm_priv *priv = file->driver_priv; 234a1d2a633SQiang Yu 235a1d2a633SQiang Yu lima_ctx_mgr_fini(&priv->ctx_mgr); 236a1d2a633SQiang Yu lima_vm_put(priv->vm); 237a1d2a633SQiang Yu kfree(priv); 238a1d2a633SQiang Yu } 239a1d2a633SQiang Yu 240a1d2a633SQiang Yu static const struct drm_ioctl_desc lima_drm_driver_ioctls[] = { 241921d5732SEmil Velikov DRM_IOCTL_DEF_DRV(LIMA_GET_PARAM, lima_ioctl_get_param, DRM_RENDER_ALLOW), 242921d5732SEmil Velikov DRM_IOCTL_DEF_DRV(LIMA_GEM_CREATE, lima_ioctl_gem_create, DRM_RENDER_ALLOW), 243921d5732SEmil Velikov DRM_IOCTL_DEF_DRV(LIMA_GEM_INFO, lima_ioctl_gem_info, DRM_RENDER_ALLOW), 244921d5732SEmil Velikov DRM_IOCTL_DEF_DRV(LIMA_GEM_SUBMIT, lima_ioctl_gem_submit, DRM_RENDER_ALLOW), 245921d5732SEmil Velikov DRM_IOCTL_DEF_DRV(LIMA_GEM_WAIT, lima_ioctl_gem_wait, DRM_RENDER_ALLOW), 246921d5732SEmil Velikov DRM_IOCTL_DEF_DRV(LIMA_CTX_CREATE, lima_ioctl_ctx_create, DRM_RENDER_ALLOW), 247921d5732SEmil Velikov DRM_IOCTL_DEF_DRV(LIMA_CTX_FREE, lima_ioctl_ctx_free, DRM_RENDER_ALLOW), 248a1d2a633SQiang Yu }; 249a1d2a633SQiang Yu 250d61dd248SQiang Yu DEFINE_DRM_GEM_FOPS(lima_drm_driver_fops); 251a1d2a633SQiang Yu 252d20615f8SQiang Yu /** 253d20615f8SQiang Yu * Changelog: 254d20615f8SQiang Yu * 255d20615f8SQiang Yu * - 1.1.0 - add heap buffer support 256d20615f8SQiang Yu */ 257d20615f8SQiang Yu 258a1d2a633SQiang Yu static struct drm_driver lima_drm_driver = { 2590424fdafSDaniel Vetter .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ, 260a1d2a633SQiang Yu .open = lima_drm_driver_open, 261a1d2a633SQiang Yu .postclose = lima_drm_driver_postclose, 262a1d2a633SQiang Yu .ioctls = lima_drm_driver_ioctls, 263a1d2a633SQiang Yu .num_ioctls = ARRAY_SIZE(lima_drm_driver_ioctls), 264a1d2a633SQiang Yu .fops = &lima_drm_driver_fops, 265a1d2a633SQiang Yu .name = "lima", 266a1d2a633SQiang Yu .desc = "lima DRM", 267d20615f8SQiang Yu .date = "20191231", 268a1d2a633SQiang Yu .major = 1, 269d20615f8SQiang Yu .minor = 1, 270a1d2a633SQiang Yu .patchlevel = 0, 271a1d2a633SQiang Yu 272d61dd248SQiang Yu .gem_create_object = lima_gem_create_object, 273a1d2a633SQiang Yu .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 274d61dd248SQiang Yu .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, 275a1d2a633SQiang Yu .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 276d61dd248SQiang Yu .gem_prime_mmap = drm_gem_prime_mmap, 277a1d2a633SQiang Yu }; 278a1d2a633SQiang Yu 279a1d2a633SQiang Yu static int lima_pdev_probe(struct platform_device *pdev) 280a1d2a633SQiang Yu { 281a1d2a633SQiang Yu struct lima_device *ldev; 282a1d2a633SQiang Yu struct drm_device *ddev; 283a1d2a633SQiang Yu int err; 284a1d2a633SQiang Yu 285a1d2a633SQiang Yu err = lima_sched_slab_init(); 286a1d2a633SQiang Yu if (err) 287a1d2a633SQiang Yu return err; 288a1d2a633SQiang Yu 289a1d2a633SQiang Yu ldev = devm_kzalloc(&pdev->dev, sizeof(*ldev), GFP_KERNEL); 290a1d2a633SQiang Yu if (!ldev) { 291a1d2a633SQiang Yu err = -ENOMEM; 292a1d2a633SQiang Yu goto err_out0; 293a1d2a633SQiang Yu } 294a1d2a633SQiang Yu 295a1d2a633SQiang Yu ldev->pdev = pdev; 296a1d2a633SQiang Yu ldev->dev = &pdev->dev; 297a1d2a633SQiang Yu ldev->id = (enum lima_gpu_id)of_device_get_match_data(&pdev->dev); 298a1d2a633SQiang Yu 299a1d2a633SQiang Yu platform_set_drvdata(pdev, ldev); 300a1d2a633SQiang Yu 301a1d2a633SQiang Yu /* Allocate and initialize the DRM device. */ 302a1d2a633SQiang Yu ddev = drm_dev_alloc(&lima_drm_driver, &pdev->dev); 303a1d2a633SQiang Yu if (IS_ERR(ddev)) 304a1d2a633SQiang Yu return PTR_ERR(ddev); 305a1d2a633SQiang Yu 306a1d2a633SQiang Yu ddev->dev_private = ldev; 307a1d2a633SQiang Yu ldev->ddev = ddev; 308a1d2a633SQiang Yu 309a1d2a633SQiang Yu err = lima_device_init(ldev); 31034e88f9eSKrzysztof Kozlowski if (err) 311a1d2a633SQiang Yu goto err_out1; 312a1d2a633SQiang Yu 313a1d2a633SQiang Yu /* 314a1d2a633SQiang Yu * Register the DRM device with the core and the connectors with 315a1d2a633SQiang Yu * sysfs. 316a1d2a633SQiang Yu */ 317a1d2a633SQiang Yu err = drm_dev_register(ddev, 0); 318a1d2a633SQiang Yu if (err < 0) 319a1d2a633SQiang Yu goto err_out2; 320a1d2a633SQiang Yu 321a1d2a633SQiang Yu return 0; 322a1d2a633SQiang Yu 323a1d2a633SQiang Yu err_out2: 324a1d2a633SQiang Yu lima_device_fini(ldev); 325a1d2a633SQiang Yu err_out1: 326a1d2a633SQiang Yu drm_dev_put(ddev); 327a1d2a633SQiang Yu err_out0: 328a1d2a633SQiang Yu lima_sched_slab_fini(); 329a1d2a633SQiang Yu return err; 330a1d2a633SQiang Yu } 331a1d2a633SQiang Yu 332a1d2a633SQiang Yu static int lima_pdev_remove(struct platform_device *pdev) 333a1d2a633SQiang Yu { 334a1d2a633SQiang Yu struct lima_device *ldev = platform_get_drvdata(pdev); 335a1d2a633SQiang Yu struct drm_device *ddev = ldev->ddev; 336a1d2a633SQiang Yu 337a1d2a633SQiang Yu drm_dev_unregister(ddev); 338a1d2a633SQiang Yu lima_device_fini(ldev); 339a1d2a633SQiang Yu drm_dev_put(ddev); 340a1d2a633SQiang Yu lima_sched_slab_fini(); 341a1d2a633SQiang Yu return 0; 342a1d2a633SQiang Yu } 343a1d2a633SQiang Yu 344a1d2a633SQiang Yu static const struct of_device_id dt_match[] = { 345a1d2a633SQiang Yu { .compatible = "arm,mali-400", .data = (void *)lima_gpu_mali400 }, 346a1d2a633SQiang Yu { .compatible = "arm,mali-450", .data = (void *)lima_gpu_mali450 }, 347a1d2a633SQiang Yu {} 348a1d2a633SQiang Yu }; 349a1d2a633SQiang Yu MODULE_DEVICE_TABLE(of, dt_match); 350a1d2a633SQiang Yu 351a1d2a633SQiang Yu static struct platform_driver lima_platform_driver = { 352a1d2a633SQiang Yu .probe = lima_pdev_probe, 353a1d2a633SQiang Yu .remove = lima_pdev_remove, 354a1d2a633SQiang Yu .driver = { 355a1d2a633SQiang Yu .name = "lima", 356a1d2a633SQiang Yu .of_match_table = dt_match, 357a1d2a633SQiang Yu }, 358a1d2a633SQiang Yu }; 359a1d2a633SQiang Yu 360a1d2a633SQiang Yu static int __init lima_init(void) 361a1d2a633SQiang Yu { 362a1d2a633SQiang Yu return platform_driver_register(&lima_platform_driver); 363a1d2a633SQiang Yu } 364a1d2a633SQiang Yu module_init(lima_init); 365a1d2a633SQiang Yu 366a1d2a633SQiang Yu static void __exit lima_exit(void) 367a1d2a633SQiang Yu { 368a1d2a633SQiang Yu platform_driver_unregister(&lima_platform_driver); 369a1d2a633SQiang Yu } 370a1d2a633SQiang Yu module_exit(lima_exit); 371a1d2a633SQiang Yu 372a1d2a633SQiang Yu MODULE_AUTHOR("Lima Project Developers"); 373a1d2a633SQiang Yu MODULE_DESCRIPTION("Lima DRM Driver"); 374a1d2a633SQiang Yu MODULE_LICENSE("GPL v2"); 375