aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/tegra
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2014-10-21 07:51:53 -0400
committerThierry Reding <treding@nvidia.com>2014-11-13 10:18:28 -0500
commitc7679306a923c2feb383f709446c1110db1c56e4 (patch)
tree1652c214408efdb374d60fe1bfe8757aa1ca0f58 /drivers/gpu/drm/tegra
parent03a605697658ac7af722764cef3f0fed889d2033 (diff)
drm/tegra: dc: Universal plane support
This allows the primary plane and cursor to be exposed as regular DRM/KMS planes, which is a prerequisite for atomic modesetting and gives userspace more flexibility over controlling them. Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/gpu/drm/tegra')
-rw-r--r--drivers/gpu/drm/tegra/dc.c487
1 files changed, 330 insertions, 157 deletions
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 517a257cccaf..7ef16a2409b1 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -332,11 +332,255 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
332 return 0; 332 return 0;
333} 333}
334 334
335static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, 335static int tegra_window_plane_disable(struct drm_plane *plane)
336 struct drm_framebuffer *fb, int crtc_x, 336{
337 int crtc_y, unsigned int crtc_w, 337 struct tegra_dc *dc = to_tegra_dc(plane->crtc);
338 unsigned int crtc_h, uint32_t src_x, 338 struct tegra_plane *p = to_tegra_plane(plane);
339 uint32_t src_y, uint32_t src_w, uint32_t src_h) 339 u32 value;
340
341 if (!plane->crtc)
342 return 0;
343
344 value = WINDOW_A_SELECT << p->index;
345 tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
346
347 value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
348 value &= ~WIN_ENABLE;
349 tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
350
351 tegra_dc_window_commit(dc, p->index);
352
353 return 0;
354}
355
356static void tegra_plane_destroy(struct drm_plane *plane)
357{
358 struct tegra_plane *p = to_tegra_plane(plane);
359
360 drm_plane_cleanup(plane);
361 kfree(p);
362}
363
364static const u32 tegra_primary_plane_formats[] = {
365 DRM_FORMAT_XBGR8888,
366 DRM_FORMAT_XRGB8888,
367 DRM_FORMAT_RGB565,
368};
369
370static int tegra_primary_plane_update(struct drm_plane *plane,
371 struct drm_crtc *crtc,
372 struct drm_framebuffer *fb, int crtc_x,
373 int crtc_y, unsigned int crtc_w,
374 unsigned int crtc_h, uint32_t src_x,
375 uint32_t src_y, uint32_t src_w,
376 uint32_t src_h)
377{
378 struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
379 struct tegra_plane *p = to_tegra_plane(plane);
380 struct tegra_dc *dc = to_tegra_dc(crtc);
381 struct tegra_dc_window window;
382 int err;
383
384 memset(&window, 0, sizeof(window));
385 window.src.x = src_x >> 16;
386 window.src.y = src_y >> 16;
387 window.src.w = src_w >> 16;
388 window.src.h = src_h >> 16;
389 window.dst.x = crtc_x;
390 window.dst.y = crtc_y;
391 window.dst.w = crtc_w;
392 window.dst.h = crtc_h;
393 window.format = tegra_dc_format(fb->pixel_format, &window.swap);
394 window.bits_per_pixel = fb->bits_per_pixel;
395 window.bottom_up = tegra_fb_is_bottom_up(fb);
396
397 err = tegra_fb_get_tiling(fb, &window.tiling);
398 if (err < 0)
399 return err;
400
401 window.base[0] = bo->paddr + fb->offsets[0];
402 window.stride[0] = fb->pitches[0];
403
404 err = tegra_dc_setup_window(dc, p->index, &window);
405 if (err < 0)
406 return err;
407
408 return 0;
409}
410
411static void tegra_primary_plane_destroy(struct drm_plane *plane)
412{
413 tegra_window_plane_disable(plane);
414 tegra_plane_destroy(plane);
415}
416
417static const struct drm_plane_funcs tegra_primary_plane_funcs = {
418 .update_plane = tegra_primary_plane_update,
419 .disable_plane = tegra_window_plane_disable,
420 .destroy = tegra_primary_plane_destroy,
421};
422
423static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
424 struct tegra_dc *dc)
425{
426 struct tegra_plane *plane;
427 unsigned int num_formats;
428 const u32 *formats;
429 int err;
430
431 plane = kzalloc(sizeof(*plane), GFP_KERNEL);
432 if (!plane)
433 return ERR_PTR(-ENOMEM);
434
435 num_formats = ARRAY_SIZE(tegra_primary_plane_formats);
436 formats = tegra_primary_plane_formats;
437
438 err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe,
439 &tegra_primary_plane_funcs, formats,
440 num_formats, DRM_PLANE_TYPE_PRIMARY);
441 if (err < 0) {
442 kfree(plane);
443 return ERR_PTR(err);
444 }
445
446 return &plane->base;
447}
448
449static const u32 tegra_cursor_plane_formats[] = {
450 DRM_FORMAT_RGBA8888,
451};
452
453static int tegra_cursor_plane_update(struct drm_plane *plane,
454 struct drm_crtc *crtc,
455 struct drm_framebuffer *fb, int crtc_x,
456 int crtc_y, unsigned int crtc_w,
457 unsigned int crtc_h, uint32_t src_x,
458 uint32_t src_y, uint32_t src_w,
459 uint32_t src_h)
460{
461 struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
462 struct tegra_dc *dc = to_tegra_dc(crtc);
463 u32 value = CURSOR_CLIP_DISPLAY;
464
465 /* scaling not supported for cursor */
466 if ((src_w >> 16 != crtc_w) || (src_h >> 16 != crtc_h))
467 return -EINVAL;
468
469 /* only square cursors supported */
470 if (src_w != src_h)
471 return -EINVAL;
472
473 switch (crtc_w) {
474 case 32:
475 value |= CURSOR_SIZE_32x32;
476 break;
477
478 case 64:
479 value |= CURSOR_SIZE_64x64;
480 break;
481
482 case 128:
483 value |= CURSOR_SIZE_128x128;
484 break;
485
486 case 256:
487 value |= CURSOR_SIZE_256x256;
488 break;
489
490 default:
491 return -EINVAL;
492 }
493
494 value |= (bo->paddr >> 10) & 0x3fffff;
495 tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);
496
497#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
498 value = (bo->paddr >> 32) & 0x3;
499 tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
500#endif
501
502 /* enable cursor and set blend mode */
503 value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
504 value |= CURSOR_ENABLE;
505 tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
506
507 value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
508 value &= ~CURSOR_DST_BLEND_MASK;
509 value &= ~CURSOR_SRC_BLEND_MASK;
510 value |= CURSOR_MODE_NORMAL;
511 value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
512 value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
513 value |= CURSOR_ALPHA;
514 tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
515
516 /* position the cursor */
517 value = (crtc_y & 0x3fff) << 16 | (crtc_x & 0x3fff);
518 tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
519
520 /* apply changes */
521 tegra_dc_cursor_commit(dc);
522 tegra_dc_commit(dc);
523
524 return 0;
525}
526
527static int tegra_cursor_plane_disable(struct drm_plane *plane)
528{
529 struct tegra_dc *dc = to_tegra_dc(plane->crtc);
530 u32 value;
531
532 if (!plane->crtc)
533 return 0;
534
535 value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
536 value &= ~CURSOR_ENABLE;
537 tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
538
539 tegra_dc_cursor_commit(dc);
540 tegra_dc_commit(dc);
541
542 return 0;
543}
544
545static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
546 .update_plane = tegra_cursor_plane_update,
547 .disable_plane = tegra_cursor_plane_disable,
548 .destroy = tegra_plane_destroy,
549};
550
551static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
552 struct tegra_dc *dc)
553{
554 struct tegra_plane *plane;
555 unsigned int num_formats;
556 const u32 *formats;
557 int err;
558
559 plane = kzalloc(sizeof(*plane), GFP_KERNEL);
560 if (!plane)
561 return ERR_PTR(-ENOMEM);
562
563 num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
564 formats = tegra_cursor_plane_formats;
565
566 err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe,
567 &tegra_cursor_plane_funcs, formats,
568 num_formats, DRM_PLANE_TYPE_CURSOR);
569 if (err < 0) {
570 kfree(plane);
571 return ERR_PTR(err);
572 }
573
574 return &plane->base;
575}
576
577static int tegra_overlay_plane_update(struct drm_plane *plane,
578 struct drm_crtc *crtc,
579 struct drm_framebuffer *fb, int crtc_x,
580 int crtc_y, unsigned int crtc_w,
581 unsigned int crtc_h, uint32_t src_x,
582 uint32_t src_y, uint32_t src_w,
583 uint32_t src_h)
340{ 584{
341 struct tegra_plane *p = to_tegra_plane(plane); 585 struct tegra_plane *p = to_tegra_plane(plane);
342 struct tegra_dc *dc = to_tegra_dc(crtc); 586 struct tegra_dc *dc = to_tegra_dc(crtc);
@@ -382,43 +626,19 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
382 return tegra_dc_setup_window(dc, p->index, &window); 626 return tegra_dc_setup_window(dc, p->index, &window);
383} 627}
384 628
385static int tegra_plane_disable(struct drm_plane *plane) 629static void tegra_overlay_plane_destroy(struct drm_plane *plane)
386{
387 struct tegra_dc *dc = to_tegra_dc(plane->crtc);
388 struct tegra_plane *p = to_tegra_plane(plane);
389 unsigned long value;
390
391 if (!plane->crtc)
392 return 0;
393
394 value = WINDOW_A_SELECT << p->index;
395 tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
396
397 value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
398 value &= ~WIN_ENABLE;
399 tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
400
401 tegra_dc_window_commit(dc, p->index);
402
403 return 0;
404}
405
406static void tegra_plane_destroy(struct drm_plane *plane)
407{ 630{
408 struct tegra_plane *p = to_tegra_plane(plane); 631 tegra_window_plane_disable(plane);
409 632 tegra_plane_destroy(plane);
410 tegra_plane_disable(plane);
411 drm_plane_cleanup(plane);
412 kfree(p);
413} 633}
414 634
415static const struct drm_plane_funcs tegra_plane_funcs = { 635static const struct drm_plane_funcs tegra_overlay_plane_funcs = {
416 .update_plane = tegra_plane_update, 636 .update_plane = tegra_overlay_plane_update,
417 .disable_plane = tegra_plane_disable, 637 .disable_plane = tegra_window_plane_disable,
418 .destroy = tegra_plane_destroy, 638 .destroy = tegra_overlay_plane_destroy,
419}; 639};
420 640
421static const uint32_t plane_formats[] = { 641static const uint32_t tegra_overlay_plane_formats[] = {
422 DRM_FORMAT_XBGR8888, 642 DRM_FORMAT_XBGR8888,
423 DRM_FORMAT_XRGB8888, 643 DRM_FORMAT_XRGB8888,
424 DRM_FORMAT_RGB565, 644 DRM_FORMAT_RGB565,
@@ -428,27 +648,44 @@ static const uint32_t plane_formats[] = {
428 DRM_FORMAT_YUV422, 648 DRM_FORMAT_YUV422,
429}; 649};
430 650
431static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) 651static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
652 struct tegra_dc *dc,
653 unsigned int index)
432{ 654{
433 unsigned int i; 655 struct tegra_plane *plane;
434 int err = 0; 656 unsigned int num_formats;
657 const u32 *formats;
658 int err;
435 659
436 for (i = 0; i < 2; i++) { 660 plane = kzalloc(sizeof(*plane), GFP_KERNEL);
437 struct tegra_plane *plane; 661 if (!plane)
662 return ERR_PTR(-ENOMEM);
438 663
439 plane = kzalloc(sizeof(*plane), GFP_KERNEL); 664 plane->index = index;
440 if (!plane)
441 return -ENOMEM;
442 665
443 plane->index = 1 + i; 666 num_formats = ARRAY_SIZE(tegra_overlay_plane_formats);
667 formats = tegra_overlay_plane_formats;
444 668
445 err = drm_plane_init(drm, &plane->base, 1 << dc->pipe, 669 err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe,
446 &tegra_plane_funcs, plane_formats, 670 &tegra_overlay_plane_funcs, formats,
447 ARRAY_SIZE(plane_formats), false); 671 num_formats, DRM_PLANE_TYPE_OVERLAY);
448 if (err < 0) { 672 if (err < 0) {
449 kfree(plane); 673 kfree(plane);
450 return err; 674 return ERR_PTR(err);
451 } 675 }
676
677 return &plane->base;
678}
679
680static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
681{
682 struct drm_plane *plane;
683 unsigned int i;
684
685 for (i = 0; i < 2; i++) {
686 plane = tegra_dc_overlay_plane_create(drm, dc, 1 + i);
687 if (IS_ERR(plane))
688 return PTR_ERR(plane);
452 } 689 }
453 690
454 return 0; 691 return 0;
@@ -568,103 +805,6 @@ void tegra_dc_disable_vblank(struct tegra_dc *dc)
568 spin_unlock_irqrestore(&dc->lock, flags); 805 spin_unlock_irqrestore(&dc->lock, flags);
569} 806}
570 807
571static int tegra_dc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file,
572 uint32_t handle, uint32_t width,
573 uint32_t height, int32_t hot_x, int32_t hot_y)
574{
575 unsigned long value = CURSOR_CLIP_DISPLAY;
576 struct tegra_dc *dc = to_tegra_dc(crtc);
577 struct drm_gem_object *gem;
578 struct tegra_bo *bo = NULL;
579
580 if (!dc->soc->supports_cursor)
581 return -ENXIO;
582
583 if (width != height)
584 return -EINVAL;
585
586 switch (width) {
587 case 32:
588 value |= CURSOR_SIZE_32x32;
589 break;
590
591 case 64:
592 value |= CURSOR_SIZE_64x64;
593 break;
594
595 case 128:
596 value |= CURSOR_SIZE_128x128;
597
598 case 256:
599 value |= CURSOR_SIZE_256x256;
600 break;
601
602 default:
603 return -EINVAL;
604 }
605
606 if (handle) {
607 gem = drm_gem_object_lookup(crtc->dev, file, handle);
608 if (!gem)
609 return -ENOENT;
610
611 bo = to_tegra_bo(gem);
612 }
613
614 if (bo) {
615 unsigned long addr = (bo->paddr & 0xfffffc00) >> 10;
616#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
617 unsigned long high = (bo->paddr & 0xfffffffc) >> 32;
618#endif
619
620 tegra_dc_writel(dc, value | addr, DC_DISP_CURSOR_START_ADDR);
621
622#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
623 tegra_dc_writel(dc, high, DC_DISP_CURSOR_START_ADDR_HI);
624#endif
625
626 value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
627 value |= CURSOR_ENABLE;
628 tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
629
630 value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
631 value &= ~CURSOR_DST_BLEND_MASK;
632 value &= ~CURSOR_SRC_BLEND_MASK;
633 value |= CURSOR_MODE_NORMAL;
634 value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
635 value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
636 value |= CURSOR_ALPHA;
637 tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
638 } else {
639 value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
640 value &= ~CURSOR_ENABLE;
641 tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
642 }
643
644 tegra_dc_cursor_commit(dc);
645 tegra_dc_commit(dc);
646
647 return 0;
648}
649
650static int tegra_dc_cursor_move(struct drm_crtc *crtc, int x, int y)
651{
652 struct tegra_dc *dc = to_tegra_dc(crtc);
653 unsigned long value;
654
655 if (!dc->soc->supports_cursor)
656 return -ENXIO;
657
658 value = ((y & 0x3fff) << 16) | (x & 0x3fff);
659 tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
660
661 tegra_dc_cursor_commit(dc);
662 /* XXX: only required on generations earlier than Tegra124? */
663 tegra_dc_commit(dc);
664
665 return 0;
666}
667
668static void tegra_dc_finish_page_flip(struct tegra_dc *dc) 808static void tegra_dc_finish_page_flip(struct tegra_dc *dc)
669{ 809{
670 struct drm_device *drm = dc->base.dev; 810 struct drm_device *drm = dc->base.dev;
@@ -741,8 +881,6 @@ static void tegra_dc_destroy(struct drm_crtc *crtc)
741} 881}
742 882
743static const struct drm_crtc_funcs tegra_crtc_funcs = { 883static const struct drm_crtc_funcs tegra_crtc_funcs = {
744 .cursor_set2 = tegra_dc_cursor_set2,
745 .cursor_move = tegra_dc_cursor_move,
746 .page_flip = tegra_dc_page_flip, 884 .page_flip = tegra_dc_page_flip,
747 .set_config = drm_crtc_helper_set_config, 885 .set_config = drm_crtc_helper_set_config,
748 .destroy = tegra_dc_destroy, 886 .destroy = tegra_dc_destroy,
@@ -756,7 +894,7 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
756 894
757 drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) { 895 drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) {
758 if (plane->crtc == crtc) { 896 if (plane->crtc == crtc) {
759 tegra_plane_disable(plane); 897 tegra_window_plane_disable(plane);
760 plane->crtc = NULL; 898 plane->crtc = NULL;
761 899
762 if (plane->fb) { 900 if (plane->fb) {
@@ -767,6 +905,7 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
767 } 905 }
768 906
769 drm_vblank_off(drm, dc->pipe); 907 drm_vblank_off(drm, dc->pipe);
908 tegra_dc_commit(dc);
770} 909}
771 910
772static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc, 911static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -1293,6 +1432,8 @@ static int tegra_dc_init(struct host1x_client *client)
1293 struct drm_device *drm = dev_get_drvdata(client->parent); 1432 struct drm_device *drm = dev_get_drvdata(client->parent);
1294 struct tegra_dc *dc = host1x_client_to_dc(client); 1433 struct tegra_dc *dc = host1x_client_to_dc(client);
1295 struct tegra_drm *tegra = drm->dev_private; 1434 struct tegra_drm *tegra = drm->dev_private;
1435 struct drm_plane *primary = NULL;
1436 struct drm_plane *cursor = NULL;
1296 int err; 1437 int err;
1297 1438
1298 if (tegra->domain) { 1439 if (tegra->domain) {
@@ -1306,7 +1447,25 @@ static int tegra_dc_init(struct host1x_client *client)
1306 dc->domain = tegra->domain; 1447 dc->domain = tegra->domain;
1307 } 1448 }
1308 1449
1309 drm_crtc_init(drm, &dc->base, &tegra_crtc_funcs); 1450 primary = tegra_dc_primary_plane_create(drm, dc);
1451 if (IS_ERR(primary)) {
1452 err = PTR_ERR(primary);
1453 goto cleanup;
1454 }
1455
1456 if (dc->soc->supports_cursor) {
1457 cursor = tegra_dc_cursor_plane_create(drm, dc);
1458 if (IS_ERR(cursor)) {
1459 err = PTR_ERR(cursor);
1460 goto cleanup;
1461 }
1462 }
1463
1464 err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor,
1465 &tegra_crtc_funcs);
1466 if (err < 0)
1467 goto cleanup;
1468
1310 drm_mode_crtc_set_gamma_size(&dc->base, 256); 1469 drm_mode_crtc_set_gamma_size(&dc->base, 256);
1311 drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); 1470 drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
1312 1471
@@ -1320,12 +1479,12 @@ static int tegra_dc_init(struct host1x_client *client)
1320 err = tegra_dc_rgb_init(drm, dc); 1479 err = tegra_dc_rgb_init(drm, dc);
1321 if (err < 0 && err != -ENODEV) { 1480 if (err < 0 && err != -ENODEV) {
1322 dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); 1481 dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
1323 return err; 1482 goto cleanup;
1324 } 1483 }
1325 1484
1326 err = tegra_dc_add_planes(drm, dc); 1485 err = tegra_dc_add_planes(drm, dc);
1327 if (err < 0) 1486 if (err < 0)
1328 return err; 1487 goto cleanup;
1329 1488
1330 if (IS_ENABLED(CONFIG_DEBUG_FS)) { 1489 if (IS_ENABLED(CONFIG_DEBUG_FS)) {
1331 err = tegra_dc_debugfs_init(dc, drm->primary); 1490 err = tegra_dc_debugfs_init(dc, drm->primary);
@@ -1338,10 +1497,24 @@ static int tegra_dc_init(struct host1x_client *client)
1338 if (err < 0) { 1497 if (err < 0) {
1339 dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq, 1498 dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq,
1340 err); 1499 err);
1341 return err; 1500 goto cleanup;
1342 } 1501 }
1343 1502
1344 return 0; 1503 return 0;
1504
1505cleanup:
1506 if (cursor)
1507 drm_plane_cleanup(cursor);
1508
1509 if (primary)
1510 drm_plane_cleanup(primary);
1511
1512 if (tegra->domain) {
1513 iommu_detach_device(tegra->domain, dc->dev);
1514 dc->domain = NULL;
1515 }
1516
1517 return err;
1345} 1518}
1346 1519
1347static int tegra_dc_exit(struct host1x_client *client) 1520static int tegra_dc_exit(struct host1x_client *client)