diff options
Diffstat (limited to 'drivers/gpu/drm/msm/msm_drv.c')
| -rw-r--r-- | drivers/gpu/drm/msm/msm_drv.c | 246 |
1 files changed, 245 insertions, 1 deletions
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index b5ae0dbe1eb8..864c9773636b 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | */ | 16 | */ |
| 17 | 17 | ||
| 18 | #include "msm_drv.h" | 18 | #include "msm_drv.h" |
| 19 | #include "msm_gpu.h" | ||
| 19 | 20 | ||
| 20 | #include <mach/iommu.h> | 21 | #include <mach/iommu.h> |
| 21 | 22 | ||
| @@ -135,6 +136,7 @@ static int msm_unload(struct drm_device *dev) | |||
| 135 | { | 136 | { |
| 136 | struct msm_drm_private *priv = dev->dev_private; | 137 | struct msm_drm_private *priv = dev->dev_private; |
| 137 | struct msm_kms *kms = priv->kms; | 138 | struct msm_kms *kms = priv->kms; |
| 139 | struct msm_gpu *gpu = priv->gpu; | ||
| 138 | 140 | ||
| 139 | drm_kms_helper_poll_fini(dev); | 141 | drm_kms_helper_poll_fini(dev); |
| 140 | drm_mode_config_cleanup(dev); | 142 | drm_mode_config_cleanup(dev); |
| @@ -152,6 +154,12 @@ static int msm_unload(struct drm_device *dev) | |||
| 152 | kms->funcs->destroy(kms); | 154 | kms->funcs->destroy(kms); |
| 153 | } | 155 | } |
| 154 | 156 | ||
| 157 | if (gpu) { | ||
| 158 | mutex_lock(&dev->struct_mutex); | ||
| 159 | gpu->funcs->pm_suspend(gpu); | ||
| 160 | gpu->funcs->destroy(gpu); | ||
| 161 | mutex_unlock(&dev->struct_mutex); | ||
| 162 | } | ||
| 155 | 163 | ||
| 156 | dev->dev_private = NULL; | 164 | dev->dev_private = NULL; |
| 157 | 165 | ||
| @@ -176,6 +184,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags) | |||
| 176 | dev->dev_private = priv; | 184 | dev->dev_private = priv; |
| 177 | 185 | ||
| 178 | priv->wq = alloc_ordered_workqueue("msm", 0); | 186 | priv->wq = alloc_ordered_workqueue("msm", 0); |
| 187 | init_waitqueue_head(&priv->fence_event); | ||
| 179 | 188 | ||
| 180 | INIT_LIST_HEAD(&priv->inactive_list); | 189 | INIT_LIST_HEAD(&priv->inactive_list); |
| 181 | 190 | ||
| @@ -240,12 +249,70 @@ fail: | |||
| 240 | return ret; | 249 | return ret; |
| 241 | } | 250 | } |
| 242 | 251 | ||
| 252 | static void load_gpu(struct drm_device *dev) | ||
| 253 | { | ||
| 254 | struct msm_drm_private *priv = dev->dev_private; | ||
| 255 | struct msm_gpu *gpu; | ||
| 256 | |||
| 257 | if (priv->gpu) | ||
| 258 | return; | ||
| 259 | |||
| 260 | mutex_lock(&dev->struct_mutex); | ||
| 261 | gpu = a3xx_gpu_init(dev); | ||
| 262 | if (IS_ERR(gpu)) { | ||
| 263 | dev_warn(dev->dev, "failed to load a3xx gpu\n"); | ||
| 264 | gpu = NULL; | ||
| 265 | /* not fatal */ | ||
| 266 | } | ||
| 267 | mutex_unlock(&dev->struct_mutex); | ||
| 268 | |||
| 269 | if (gpu) { | ||
| 270 | int ret; | ||
| 271 | gpu->funcs->pm_resume(gpu); | ||
| 272 | ret = gpu->funcs->hw_init(gpu); | ||
| 273 | if (ret) { | ||
| 274 | dev_err(dev->dev, "gpu hw init failed: %d\n", ret); | ||
| 275 | gpu->funcs->destroy(gpu); | ||
| 276 | gpu = NULL; | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | priv->gpu = gpu; | ||
| 281 | } | ||
| 282 | |||
| 283 | static int msm_open(struct drm_device *dev, struct drm_file *file) | ||
| 284 | { | ||
| 285 | struct msm_file_private *ctx; | ||
| 286 | |||
| 287 | /* For now, load gpu on open.. to avoid the requirement of having | ||
| 288 | * firmware in the initrd. | ||
| 289 | */ | ||
| 290 | load_gpu(dev); | ||
| 291 | |||
| 292 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | ||
| 293 | if (!ctx) | ||
| 294 | return -ENOMEM; | ||
| 295 | |||
| 296 | file->driver_priv = ctx; | ||
| 297 | |||
| 298 | return 0; | ||
| 299 | } | ||
| 300 | |||
| 243 | static void msm_preclose(struct drm_device *dev, struct drm_file *file) | 301 | static void msm_preclose(struct drm_device *dev, struct drm_file *file) |
| 244 | { | 302 | { |
| 245 | struct msm_drm_private *priv = dev->dev_private; | 303 | struct msm_drm_private *priv = dev->dev_private; |
| 304 | struct msm_file_private *ctx = file->driver_priv; | ||
| 246 | struct msm_kms *kms = priv->kms; | 305 | struct msm_kms *kms = priv->kms; |
| 306 | |||
| 247 | if (kms) | 307 | if (kms) |
| 248 | kms->funcs->preclose(kms, file); | 308 | kms->funcs->preclose(kms, file); |
| 309 | |||
| 310 | mutex_lock(&dev->struct_mutex); | ||
| 311 | if (ctx == priv->lastctx) | ||
| 312 | priv->lastctx = NULL; | ||
| 313 | mutex_unlock(&dev->struct_mutex); | ||
| 314 | |||
| 315 | kfree(ctx); | ||
| 249 | } | 316 | } |
| 250 | 317 | ||
| 251 | static void msm_lastclose(struct drm_device *dev) | 318 | static void msm_lastclose(struct drm_device *dev) |
| @@ -316,11 +383,30 @@ static void msm_disable_vblank(struct drm_device *dev, int crtc_id) | |||
| 316 | */ | 383 | */ |
| 317 | 384 | ||
| 318 | #ifdef CONFIG_DEBUG_FS | 385 | #ifdef CONFIG_DEBUG_FS |
| 386 | static int msm_gpu_show(struct drm_device *dev, struct seq_file *m) | ||
| 387 | { | ||
| 388 | struct msm_drm_private *priv = dev->dev_private; | ||
| 389 | struct msm_gpu *gpu = priv->gpu; | ||
| 390 | |||
| 391 | if (gpu) { | ||
| 392 | seq_printf(m, "%s Status:\n", gpu->name); | ||
| 393 | gpu->funcs->show(gpu, m); | ||
| 394 | } | ||
| 395 | |||
| 396 | return 0; | ||
| 397 | } | ||
| 398 | |||
| 319 | static int msm_gem_show(struct drm_device *dev, struct seq_file *m) | 399 | static int msm_gem_show(struct drm_device *dev, struct seq_file *m) |
| 320 | { | 400 | { |
| 321 | struct msm_drm_private *priv = dev->dev_private; | 401 | struct msm_drm_private *priv = dev->dev_private; |
| 402 | struct msm_gpu *gpu = priv->gpu; | ||
| 403 | |||
| 404 | if (gpu) { | ||
| 405 | seq_printf(m, "Active Objects (%s):\n", gpu->name); | ||
| 406 | msm_gem_describe_objects(&gpu->active_list, m); | ||
| 407 | } | ||
| 322 | 408 | ||
| 323 | seq_printf(m, "All Objects:\n"); | 409 | seq_printf(m, "Inactive Objects:\n"); |
| 324 | msm_gem_describe_objects(&priv->inactive_list, m); | 410 | msm_gem_describe_objects(&priv->inactive_list, m); |
| 325 | 411 | ||
| 326 | return 0; | 412 | return 0; |
| @@ -375,6 +461,7 @@ static int show_locked(struct seq_file *m, void *arg) | |||
| 375 | } | 461 | } |
| 376 | 462 | ||
| 377 | static struct drm_info_list msm_debugfs_list[] = { | 463 | static struct drm_info_list msm_debugfs_list[] = { |
| 464 | {"gpu", show_locked, 0, msm_gpu_show}, | ||
| 378 | {"gem", show_locked, 0, msm_gem_show}, | 465 | {"gem", show_locked, 0, msm_gem_show}, |
| 379 | { "mm", show_locked, 0, msm_mm_show }, | 466 | { "mm", show_locked, 0, msm_mm_show }, |
| 380 | { "fb", show_locked, 0, msm_fb_show }, | 467 | { "fb", show_locked, 0, msm_fb_show }, |
| @@ -404,6 +491,158 @@ static void msm_debugfs_cleanup(struct drm_minor *minor) | |||
| 404 | } | 491 | } |
| 405 | #endif | 492 | #endif |
| 406 | 493 | ||
| 494 | /* | ||
| 495 | * Fences: | ||
| 496 | */ | ||
| 497 | |||
| 498 | int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence, | ||
| 499 | struct timespec *timeout) | ||
| 500 | { | ||
| 501 | struct msm_drm_private *priv = dev->dev_private; | ||
| 502 | unsigned long timeout_jiffies = timespec_to_jiffies(timeout); | ||
| 503 | unsigned long start_jiffies = jiffies; | ||
| 504 | unsigned long remaining_jiffies; | ||
| 505 | int ret; | ||
| 506 | |||
| 507 | if (time_after(start_jiffies, timeout_jiffies)) | ||
| 508 | remaining_jiffies = 0; | ||
| 509 | else | ||
| 510 | remaining_jiffies = timeout_jiffies - start_jiffies; | ||
| 511 | |||
| 512 | ret = wait_event_interruptible_timeout(priv->fence_event, | ||
| 513 | priv->completed_fence >= fence, | ||
| 514 | remaining_jiffies); | ||
| 515 | if (ret == 0) { | ||
| 516 | DBG("timeout waiting for fence: %u (completed: %u)", | ||
| 517 | fence, priv->completed_fence); | ||
| 518 | ret = -ETIMEDOUT; | ||
| 519 | } else if (ret != -ERESTARTSYS) { | ||
| 520 | ret = 0; | ||
| 521 | } | ||
| 522 | |||
| 523 | return ret; | ||
| 524 | } | ||
| 525 | |||
| 526 | /* call under struct_mutex */ | ||
| 527 | void msm_update_fence(struct drm_device *dev, uint32_t fence) | ||
| 528 | { | ||
| 529 | struct msm_drm_private *priv = dev->dev_private; | ||
| 530 | |||
| 531 | if (fence > priv->completed_fence) { | ||
| 532 | priv->completed_fence = fence; | ||
| 533 | wake_up_all(&priv->fence_event); | ||
| 534 | } | ||
| 535 | } | ||
| 536 | |||
| 537 | /* | ||
| 538 | * DRM ioctls: | ||
| 539 | */ | ||
| 540 | |||
| 541 | static int msm_ioctl_get_param(struct drm_device *dev, void *data, | ||
| 542 | struct drm_file *file) | ||
| 543 | { | ||
| 544 | struct msm_drm_private *priv = dev->dev_private; | ||
| 545 | struct drm_msm_param *args = data; | ||
| 546 | struct msm_gpu *gpu; | ||
| 547 | |||
| 548 | /* for now, we just have 3d pipe.. eventually this would need to | ||
| 549 | * be more clever to dispatch to appropriate gpu module: | ||
| 550 | */ | ||
| 551 | if (args->pipe != MSM_PIPE_3D0) | ||
| 552 | return -EINVAL; | ||
| 553 | |||
| 554 | gpu = priv->gpu; | ||
| 555 | |||
| 556 | if (!gpu) | ||
| 557 | return -ENXIO; | ||
| 558 | |||
| 559 | return gpu->funcs->get_param(gpu, args->param, &args->value); | ||
| 560 | } | ||
| 561 | |||
| 562 | static int msm_ioctl_gem_new(struct drm_device *dev, void *data, | ||
| 563 | struct drm_file *file) | ||
| 564 | { | ||
| 565 | struct drm_msm_gem_new *args = data; | ||
| 566 | return msm_gem_new_handle(dev, file, args->size, | ||
| 567 | args->flags, &args->handle); | ||
| 568 | } | ||
| 569 | |||
| 570 | #define TS(t) ((struct timespec){ .tv_sec = (t).tv_sec, .tv_nsec = (t).tv_nsec }) | ||
| 571 | |||
| 572 | static int msm_ioctl_gem_cpu_prep(struct drm_device *dev, void *data, | ||
| 573 | struct drm_file *file) | ||
| 574 | { | ||
| 575 | struct drm_msm_gem_cpu_prep *args = data; | ||
| 576 | struct drm_gem_object *obj; | ||
| 577 | int ret; | ||
| 578 | |||
| 579 | obj = drm_gem_object_lookup(dev, file, args->handle); | ||
| 580 | if (!obj) | ||
| 581 | return -ENOENT; | ||
| 582 | |||
| 583 | ret = msm_gem_cpu_prep(obj, args->op, &TS(args->timeout)); | ||
| 584 | |||
| 585 | drm_gem_object_unreference_unlocked(obj); | ||
| 586 | |||
| 587 | return ret; | ||
| 588 | } | ||
| 589 | |||
| 590 | static int msm_ioctl_gem_cpu_fini(struct drm_device *dev, void *data, | ||
| 591 | struct drm_file *file) | ||
| 592 | { | ||
| 593 | struct drm_msm_gem_cpu_fini *args = data; | ||
| 594 | struct drm_gem_object *obj; | ||
| 595 | int ret; | ||
| 596 | |||
| 597 | obj = drm_gem_object_lookup(dev, file, args->handle); | ||
| 598 | if (!obj) | ||
| 599 | return -ENOENT; | ||
| 600 | |||
| 601 | ret = msm_gem_cpu_fini(obj); | ||
| 602 | |||
| 603 | drm_gem_object_unreference_unlocked(obj); | ||
| 604 | |||
| 605 | return ret; | ||
| 606 | } | ||
| 607 | |||
| 608 | static int msm_ioctl_gem_info(struct drm_device *dev, void *data, | ||
| 609 | struct drm_file *file) | ||
| 610 | { | ||
| 611 | struct drm_msm_gem_info *args = data; | ||
| 612 | struct drm_gem_object *obj; | ||
| 613 | int ret = 0; | ||
| 614 | |||
| 615 | if (args->pad) | ||
| 616 | return -EINVAL; | ||
| 617 | |||
| 618 | obj = drm_gem_object_lookup(dev, file, args->handle); | ||
| 619 | if (!obj) | ||
| 620 | return -ENOENT; | ||
| 621 | |||
| 622 | args->offset = msm_gem_mmap_offset(obj); | ||
| 623 | |||
| 624 | drm_gem_object_unreference_unlocked(obj); | ||
| 625 | |||
| 626 | return ret; | ||
| 627 | } | ||
| 628 | |||
| 629 | static int msm_ioctl_wait_fence(struct drm_device *dev, void *data, | ||
| 630 | struct drm_file *file) | ||
| 631 | { | ||
| 632 | struct drm_msm_wait_fence *args = data; | ||
| 633 | return msm_wait_fence_interruptable(dev, args->fence, &TS(args->timeout)); | ||
| 634 | } | ||
| 635 | |||
| 636 | static const struct drm_ioctl_desc msm_ioctls[] = { | ||
| 637 | DRM_IOCTL_DEF_DRV(MSM_GET_PARAM, msm_ioctl_get_param, DRM_UNLOCKED|DRM_AUTH), | ||
| 638 | DRM_IOCTL_DEF_DRV(MSM_GEM_NEW, msm_ioctl_gem_new, DRM_UNLOCKED|DRM_AUTH), | ||
| 639 | DRM_IOCTL_DEF_DRV(MSM_GEM_INFO, msm_ioctl_gem_info, DRM_UNLOCKED|DRM_AUTH), | ||
| 640 | DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH), | ||
| 641 | DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH), | ||
| 642 | DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT, msm_ioctl_gem_submit, DRM_UNLOCKED|DRM_AUTH), | ||
| 643 | DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE, msm_ioctl_wait_fence, DRM_UNLOCKED|DRM_AUTH), | ||
| 644 | }; | ||
| 645 | |||
| 407 | static const struct vm_operations_struct vm_ops = { | 646 | static const struct vm_operations_struct vm_ops = { |
| 408 | .fault = msm_gem_fault, | 647 | .fault = msm_gem_fault, |
| 409 | .open = drm_gem_vm_open, | 648 | .open = drm_gem_vm_open, |
| @@ -428,6 +667,7 @@ static struct drm_driver msm_driver = { | |||
| 428 | .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, | 667 | .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, |
| 429 | .load = msm_load, | 668 | .load = msm_load, |
| 430 | .unload = msm_unload, | 669 | .unload = msm_unload, |
| 670 | .open = msm_open, | ||
| 431 | .preclose = msm_preclose, | 671 | .preclose = msm_preclose, |
| 432 | .lastclose = msm_lastclose, | 672 | .lastclose = msm_lastclose, |
| 433 | .irq_handler = msm_irq, | 673 | .irq_handler = msm_irq, |
| @@ -446,6 +686,8 @@ static struct drm_driver msm_driver = { | |||
| 446 | .debugfs_init = msm_debugfs_init, | 686 | .debugfs_init = msm_debugfs_init, |
| 447 | .debugfs_cleanup = msm_debugfs_cleanup, | 687 | .debugfs_cleanup = msm_debugfs_cleanup, |
| 448 | #endif | 688 | #endif |
| 689 | .ioctls = msm_ioctls, | ||
| 690 | .num_ioctls = DRM_MSM_NUM_IOCTLS, | ||
| 449 | .fops = &fops, | 691 | .fops = &fops, |
| 450 | .name = "msm", | 692 | .name = "msm", |
| 451 | .desc = "MSM Snapdragon DRM", | 693 | .desc = "MSM Snapdragon DRM", |
| @@ -514,6 +756,7 @@ static int __init msm_drm_register(void) | |||
| 514 | { | 756 | { |
| 515 | DBG("init"); | 757 | DBG("init"); |
| 516 | hdmi_register(); | 758 | hdmi_register(); |
| 759 | a3xx_register(); | ||
| 517 | return platform_driver_register(&msm_platform_driver); | 760 | return platform_driver_register(&msm_platform_driver); |
| 518 | } | 761 | } |
| 519 | 762 | ||
| @@ -522,6 +765,7 @@ static void __exit msm_drm_unregister(void) | |||
| 522 | DBG("fini"); | 765 | DBG("fini"); |
| 523 | platform_driver_unregister(&msm_platform_driver); | 766 | platform_driver_unregister(&msm_platform_driver); |
| 524 | hdmi_unregister(); | 767 | hdmi_unregister(); |
| 768 | a3xx_unregister(); | ||
| 525 | } | 769 | } |
| 526 | 770 | ||
| 527 | module_init(msm_drm_register); | 771 | module_init(msm_drm_register); |
