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); |