diff options
Diffstat (limited to 'drivers/gpu/drm/tegra/dc.c')
| -rw-r--r-- | drivers/gpu/drm/tegra/dc.c | 596 |
1 files changed, 416 insertions, 180 deletions
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 054a79f143ae..3367960286a6 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c | |||
| @@ -9,17 +9,23 @@ | |||
| 9 | 9 | ||
| 10 | #include <linux/clk.h> | 10 | #include <linux/clk.h> |
| 11 | #include <linux/debugfs.h> | 11 | #include <linux/debugfs.h> |
| 12 | #include <linux/iommu.h> | ||
| 12 | #include <linux/reset.h> | 13 | #include <linux/reset.h> |
| 13 | 14 | ||
| 15 | #include <soc/tegra/pmc.h> | ||
| 16 | |||
| 14 | #include "dc.h" | 17 | #include "dc.h" |
| 15 | #include "drm.h" | 18 | #include "drm.h" |
| 16 | #include "gem.h" | 19 | #include "gem.h" |
| 17 | 20 | ||
| 21 | #include <drm/drm_plane_helper.h> | ||
| 22 | |||
| 18 | struct tegra_dc_soc_info { | 23 | struct tegra_dc_soc_info { |
| 19 | bool supports_interlacing; | 24 | bool supports_interlacing; |
| 20 | bool supports_cursor; | 25 | bool supports_cursor; |
| 21 | bool supports_block_linear; | 26 | bool supports_block_linear; |
| 22 | unsigned int pitch_align; | 27 | unsigned int pitch_align; |
| 28 | bool has_powergate; | ||
| 23 | }; | 29 | }; |
| 24 | 30 | ||
| 25 | struct tegra_plane { | 31 | struct tegra_plane { |
| @@ -32,6 +38,26 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane) | |||
| 32 | return container_of(plane, struct tegra_plane, base); | 38 | return container_of(plane, struct tegra_plane, base); |
| 33 | } | 39 | } |
| 34 | 40 | ||
| 41 | static void tegra_dc_window_commit(struct tegra_dc *dc, unsigned int index) | ||
| 42 | { | ||
| 43 | u32 value = WIN_A_ACT_REQ << index; | ||
| 44 | |||
| 45 | tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL); | ||
| 46 | tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); | ||
| 47 | } | ||
| 48 | |||
| 49 | static void tegra_dc_cursor_commit(struct tegra_dc *dc) | ||
| 50 | { | ||
| 51 | tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | ||
| 52 | tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 53 | } | ||
| 54 | |||
| 55 | static void tegra_dc_commit(struct tegra_dc *dc) | ||
| 56 | { | ||
| 57 | tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | ||
| 58 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 59 | } | ||
| 60 | |||
| 35 | static unsigned int tegra_dc_format(uint32_t format, uint32_t *swap) | 61 | static unsigned int tegra_dc_format(uint32_t format, uint32_t *swap) |
| 36 | { | 62 | { |
| 37 | /* assume no swapping of fetched data */ | 63 | /* assume no swapping of fetched data */ |
| @@ -303,17 +329,260 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, | |||
| 303 | break; | 329 | break; |
| 304 | } | 330 | } |
| 305 | 331 | ||
| 306 | tegra_dc_writel(dc, WIN_A_UPDATE << index, DC_CMD_STATE_CONTROL); | 332 | tegra_dc_window_commit(dc, index); |
| 307 | tegra_dc_writel(dc, WIN_A_ACT_REQ << index, DC_CMD_STATE_CONTROL); | 333 | |
| 334 | return 0; | ||
| 335 | } | ||
| 336 | |||
| 337 | static int tegra_window_plane_disable(struct drm_plane *plane) | ||
| 338 | { | ||
| 339 | struct tegra_dc *dc = to_tegra_dc(plane->crtc); | ||
| 340 | struct tegra_plane *p = to_tegra_plane(plane); | ||
| 341 | u32 value; | ||
| 342 | |||
| 343 | if (!plane->crtc) | ||
| 344 | return 0; | ||
| 345 | |||
| 346 | value = WINDOW_A_SELECT << p->index; | ||
| 347 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 348 | |||
| 349 | value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); | ||
| 350 | value &= ~WIN_ENABLE; | ||
| 351 | tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); | ||
| 352 | |||
| 353 | tegra_dc_window_commit(dc, p->index); | ||
| 354 | |||
| 355 | return 0; | ||
| 356 | } | ||
| 357 | |||
| 358 | static void tegra_plane_destroy(struct drm_plane *plane) | ||
| 359 | { | ||
| 360 | struct tegra_plane *p = to_tegra_plane(plane); | ||
| 361 | |||
| 362 | drm_plane_cleanup(plane); | ||
| 363 | kfree(p); | ||
| 364 | } | ||
| 365 | |||
| 366 | static const u32 tegra_primary_plane_formats[] = { | ||
| 367 | DRM_FORMAT_XBGR8888, | ||
| 368 | DRM_FORMAT_XRGB8888, | ||
| 369 | DRM_FORMAT_RGB565, | ||
| 370 | }; | ||
| 371 | |||
| 372 | static int tegra_primary_plane_update(struct drm_plane *plane, | ||
| 373 | struct drm_crtc *crtc, | ||
| 374 | struct drm_framebuffer *fb, int crtc_x, | ||
| 375 | int crtc_y, unsigned int crtc_w, | ||
| 376 | unsigned int crtc_h, uint32_t src_x, | ||
| 377 | uint32_t src_y, uint32_t src_w, | ||
| 378 | uint32_t src_h) | ||
| 379 | { | ||
| 380 | struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); | ||
| 381 | struct tegra_plane *p = to_tegra_plane(plane); | ||
| 382 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
| 383 | struct tegra_dc_window window; | ||
| 384 | int err; | ||
| 385 | |||
| 386 | memset(&window, 0, sizeof(window)); | ||
| 387 | window.src.x = src_x >> 16; | ||
| 388 | window.src.y = src_y >> 16; | ||
| 389 | window.src.w = src_w >> 16; | ||
| 390 | window.src.h = src_h >> 16; | ||
| 391 | window.dst.x = crtc_x; | ||
| 392 | window.dst.y = crtc_y; | ||
| 393 | window.dst.w = crtc_w; | ||
| 394 | window.dst.h = crtc_h; | ||
| 395 | window.format = tegra_dc_format(fb->pixel_format, &window.swap); | ||
| 396 | window.bits_per_pixel = fb->bits_per_pixel; | ||
| 397 | window.bottom_up = tegra_fb_is_bottom_up(fb); | ||
| 398 | |||
| 399 | err = tegra_fb_get_tiling(fb, &window.tiling); | ||
| 400 | if (err < 0) | ||
| 401 | return err; | ||
| 402 | |||
| 403 | window.base[0] = bo->paddr + fb->offsets[0]; | ||
| 404 | window.stride[0] = fb->pitches[0]; | ||
| 405 | |||
| 406 | err = tegra_dc_setup_window(dc, p->index, &window); | ||
| 407 | if (err < 0) | ||
| 408 | return err; | ||
| 409 | |||
| 410 | return 0; | ||
| 411 | } | ||
| 412 | |||
| 413 | static void tegra_primary_plane_destroy(struct drm_plane *plane) | ||
| 414 | { | ||
| 415 | tegra_window_plane_disable(plane); | ||
| 416 | tegra_plane_destroy(plane); | ||
| 417 | } | ||
| 418 | |||
| 419 | static const struct drm_plane_funcs tegra_primary_plane_funcs = { | ||
| 420 | .update_plane = tegra_primary_plane_update, | ||
| 421 | .disable_plane = tegra_window_plane_disable, | ||
| 422 | .destroy = tegra_primary_plane_destroy, | ||
| 423 | }; | ||
| 424 | |||
| 425 | static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, | ||
| 426 | struct tegra_dc *dc) | ||
| 427 | { | ||
| 428 | struct tegra_plane *plane; | ||
| 429 | unsigned int num_formats; | ||
| 430 | const u32 *formats; | ||
| 431 | int err; | ||
| 432 | |||
| 433 | plane = kzalloc(sizeof(*plane), GFP_KERNEL); | ||
| 434 | if (!plane) | ||
| 435 | return ERR_PTR(-ENOMEM); | ||
| 436 | |||
| 437 | num_formats = ARRAY_SIZE(tegra_primary_plane_formats); | ||
| 438 | formats = tegra_primary_plane_formats; | ||
| 439 | |||
| 440 | err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, | ||
| 441 | &tegra_primary_plane_funcs, formats, | ||
| 442 | num_formats, DRM_PLANE_TYPE_PRIMARY); | ||
| 443 | if (err < 0) { | ||
| 444 | kfree(plane); | ||
| 445 | return ERR_PTR(err); | ||
| 446 | } | ||
| 447 | |||
| 448 | return &plane->base; | ||
| 449 | } | ||
| 450 | |||
| 451 | static const u32 tegra_cursor_plane_formats[] = { | ||
| 452 | DRM_FORMAT_RGBA8888, | ||
| 453 | }; | ||
| 454 | |||
| 455 | static int tegra_cursor_plane_update(struct drm_plane *plane, | ||
| 456 | struct drm_crtc *crtc, | ||
| 457 | struct drm_framebuffer *fb, int crtc_x, | ||
| 458 | int crtc_y, unsigned int crtc_w, | ||
| 459 | unsigned int crtc_h, uint32_t src_x, | ||
| 460 | uint32_t src_y, uint32_t src_w, | ||
| 461 | uint32_t src_h) | ||
| 462 | { | ||
| 463 | struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); | ||
| 464 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
| 465 | u32 value = CURSOR_CLIP_DISPLAY; | ||
| 466 | |||
| 467 | /* scaling not supported for cursor */ | ||
| 468 | if ((src_w >> 16 != crtc_w) || (src_h >> 16 != crtc_h)) | ||
| 469 | return -EINVAL; | ||
| 470 | |||
| 471 | /* only square cursors supported */ | ||
| 472 | if (src_w != src_h) | ||
| 473 | return -EINVAL; | ||
| 474 | |||
| 475 | switch (crtc_w) { | ||
| 476 | case 32: | ||
| 477 | value |= CURSOR_SIZE_32x32; | ||
| 478 | break; | ||
| 479 | |||
| 480 | case 64: | ||
| 481 | value |= CURSOR_SIZE_64x64; | ||
| 482 | break; | ||
| 483 | |||
| 484 | case 128: | ||
| 485 | value |= CURSOR_SIZE_128x128; | ||
| 486 | break; | ||
| 487 | |||
| 488 | case 256: | ||
| 489 | value |= CURSOR_SIZE_256x256; | ||
| 490 | break; | ||
| 491 | |||
| 492 | default: | ||
| 493 | return -EINVAL; | ||
| 494 | } | ||
| 495 | |||
| 496 | value |= (bo->paddr >> 10) & 0x3fffff; | ||
| 497 | tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR); | ||
| 498 | |||
| 499 | #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT | ||
| 500 | value = (bo->paddr >> 32) & 0x3; | ||
| 501 | tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI); | ||
| 502 | #endif | ||
| 503 | |||
| 504 | /* enable cursor and set blend mode */ | ||
| 505 | value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); | ||
| 506 | value |= CURSOR_ENABLE; | ||
| 507 | tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); | ||
| 508 | |||
| 509 | value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL); | ||
| 510 | value &= ~CURSOR_DST_BLEND_MASK; | ||
| 511 | value &= ~CURSOR_SRC_BLEND_MASK; | ||
| 512 | value |= CURSOR_MODE_NORMAL; | ||
| 513 | value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC; | ||
| 514 | value |= CURSOR_SRC_BLEND_K1_TIMES_SRC; | ||
| 515 | value |= CURSOR_ALPHA; | ||
| 516 | tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL); | ||
| 517 | |||
| 518 | /* position the cursor */ | ||
| 519 | value = (crtc_y & 0x3fff) << 16 | (crtc_x & 0x3fff); | ||
| 520 | tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION); | ||
| 521 | |||
| 522 | /* apply changes */ | ||
| 523 | tegra_dc_cursor_commit(dc); | ||
| 524 | tegra_dc_commit(dc); | ||
| 525 | |||
| 526 | return 0; | ||
| 527 | } | ||
| 528 | |||
| 529 | static int tegra_cursor_plane_disable(struct drm_plane *plane) | ||
| 530 | { | ||
| 531 | struct tegra_dc *dc = to_tegra_dc(plane->crtc); | ||
| 532 | u32 value; | ||
| 533 | |||
| 534 | if (!plane->crtc) | ||
| 535 | return 0; | ||
| 536 | |||
| 537 | value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); | ||
| 538 | value &= ~CURSOR_ENABLE; | ||
| 539 | tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); | ||
| 540 | |||
| 541 | tegra_dc_cursor_commit(dc); | ||
| 542 | tegra_dc_commit(dc); | ||
| 308 | 543 | ||
| 309 | return 0; | 544 | return 0; |
| 310 | } | 545 | } |
| 311 | 546 | ||
| 312 | static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, | 547 | static const struct drm_plane_funcs tegra_cursor_plane_funcs = { |
| 313 | struct drm_framebuffer *fb, int crtc_x, | 548 | .update_plane = tegra_cursor_plane_update, |
| 314 | int crtc_y, unsigned int crtc_w, | 549 | .disable_plane = tegra_cursor_plane_disable, |
| 315 | unsigned int crtc_h, uint32_t src_x, | 550 | .destroy = tegra_plane_destroy, |
| 316 | uint32_t src_y, uint32_t src_w, uint32_t src_h) | 551 | }; |
| 552 | |||
| 553 | static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, | ||
| 554 | struct tegra_dc *dc) | ||
| 555 | { | ||
| 556 | struct tegra_plane *plane; | ||
| 557 | unsigned int num_formats; | ||
| 558 | const u32 *formats; | ||
| 559 | int err; | ||
| 560 | |||
| 561 | plane = kzalloc(sizeof(*plane), GFP_KERNEL); | ||
| 562 | if (!plane) | ||
| 563 | return ERR_PTR(-ENOMEM); | ||
| 564 | |||
| 565 | num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); | ||
| 566 | formats = tegra_cursor_plane_formats; | ||
| 567 | |||
| 568 | err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, | ||
| 569 | &tegra_cursor_plane_funcs, formats, | ||
| 570 | num_formats, DRM_PLANE_TYPE_CURSOR); | ||
| 571 | if (err < 0) { | ||
| 572 | kfree(plane); | ||
| 573 | return ERR_PTR(err); | ||
| 574 | } | ||
| 575 | |||
| 576 | return &plane->base; | ||
| 577 | } | ||
| 578 | |||
| 579 | static int tegra_overlay_plane_update(struct drm_plane *plane, | ||
| 580 | struct drm_crtc *crtc, | ||
| 581 | struct drm_framebuffer *fb, int crtc_x, | ||
| 582 | int crtc_y, unsigned int crtc_w, | ||
| 583 | unsigned int crtc_h, uint32_t src_x, | ||
| 584 | uint32_t src_y, uint32_t src_w, | ||
| 585 | uint32_t src_h) | ||
| 317 | { | 586 | { |
| 318 | struct tegra_plane *p = to_tegra_plane(plane); | 587 | struct tegra_plane *p = to_tegra_plane(plane); |
| 319 | struct tegra_dc *dc = to_tegra_dc(crtc); | 588 | struct tegra_dc *dc = to_tegra_dc(crtc); |
| @@ -359,44 +628,19 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, | |||
| 359 | return tegra_dc_setup_window(dc, p->index, &window); | 628 | return tegra_dc_setup_window(dc, p->index, &window); |
| 360 | } | 629 | } |
| 361 | 630 | ||
| 362 | static int tegra_plane_disable(struct drm_plane *plane) | 631 | static void tegra_overlay_plane_destroy(struct drm_plane *plane) |
| 363 | { | 632 | { |
| 364 | struct tegra_dc *dc = to_tegra_dc(plane->crtc); | 633 | tegra_window_plane_disable(plane); |
| 365 | struct tegra_plane *p = to_tegra_plane(plane); | 634 | tegra_plane_destroy(plane); |
| 366 | unsigned long value; | ||
| 367 | |||
| 368 | if (!plane->crtc) | ||
| 369 | return 0; | ||
| 370 | |||
| 371 | value = WINDOW_A_SELECT << p->index; | ||
| 372 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); | ||
| 373 | |||
| 374 | value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); | ||
| 375 | value &= ~WIN_ENABLE; | ||
| 376 | tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); | ||
| 377 | |||
| 378 | tegra_dc_writel(dc, WIN_A_UPDATE << p->index, DC_CMD_STATE_CONTROL); | ||
| 379 | tegra_dc_writel(dc, WIN_A_ACT_REQ << p->index, DC_CMD_STATE_CONTROL); | ||
| 380 | |||
| 381 | return 0; | ||
| 382 | } | ||
| 383 | |||
| 384 | static void tegra_plane_destroy(struct drm_plane *plane) | ||
| 385 | { | ||
| 386 | struct tegra_plane *p = to_tegra_plane(plane); | ||
| 387 | |||
| 388 | tegra_plane_disable(plane); | ||
| 389 | drm_plane_cleanup(plane); | ||
| 390 | kfree(p); | ||
| 391 | } | 635 | } |
| 392 | 636 | ||
| 393 | static const struct drm_plane_funcs tegra_plane_funcs = { | 637 | static const struct drm_plane_funcs tegra_overlay_plane_funcs = { |
| 394 | .update_plane = tegra_plane_update, | 638 | .update_plane = tegra_overlay_plane_update, |
| 395 | .disable_plane = tegra_plane_disable, | 639 | .disable_plane = tegra_window_plane_disable, |
| 396 | .destroy = tegra_plane_destroy, | 640 | .destroy = tegra_overlay_plane_destroy, |
| 397 | }; | 641 | }; |
| 398 | 642 | ||
| 399 | static const uint32_t plane_formats[] = { | 643 | static const uint32_t tegra_overlay_plane_formats[] = { |
| 400 | DRM_FORMAT_XBGR8888, | 644 | DRM_FORMAT_XBGR8888, |
| 401 | DRM_FORMAT_XRGB8888, | 645 | DRM_FORMAT_XRGB8888, |
| 402 | DRM_FORMAT_RGB565, | 646 | DRM_FORMAT_RGB565, |
| @@ -406,27 +650,44 @@ static const uint32_t plane_formats[] = { | |||
| 406 | DRM_FORMAT_YUV422, | 650 | DRM_FORMAT_YUV422, |
| 407 | }; | 651 | }; |
| 408 | 652 | ||
| 409 | static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) | 653 | static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, |
| 654 | struct tegra_dc *dc, | ||
| 655 | unsigned int index) | ||
| 410 | { | 656 | { |
| 411 | unsigned int i; | 657 | struct tegra_plane *plane; |
| 412 | int err = 0; | 658 | unsigned int num_formats; |
| 659 | const u32 *formats; | ||
| 660 | int err; | ||
| 413 | 661 | ||
| 414 | for (i = 0; i < 2; i++) { | 662 | plane = kzalloc(sizeof(*plane), GFP_KERNEL); |
| 415 | struct tegra_plane *plane; | 663 | if (!plane) |
| 664 | return ERR_PTR(-ENOMEM); | ||
| 416 | 665 | ||
| 417 | plane = kzalloc(sizeof(*plane), GFP_KERNEL); | 666 | plane->index = index; |
| 418 | if (!plane) | ||
| 419 | return -ENOMEM; | ||
| 420 | 667 | ||
| 421 | plane->index = 1 + i; | 668 | num_formats = ARRAY_SIZE(tegra_overlay_plane_formats); |
| 669 | formats = tegra_overlay_plane_formats; | ||
| 422 | 670 | ||
| 423 | err = drm_plane_init(drm, &plane->base, 1 << dc->pipe, | 671 | err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, |
| 424 | &tegra_plane_funcs, plane_formats, | 672 | &tegra_overlay_plane_funcs, formats, |
| 425 | ARRAY_SIZE(plane_formats), false); | 673 | num_formats, DRM_PLANE_TYPE_OVERLAY); |
| 426 | if (err < 0) { | 674 | if (err < 0) { |
| 427 | kfree(plane); | 675 | kfree(plane); |
| 428 | return err; | 676 | return ERR_PTR(err); |
| 429 | } | 677 | } |
| 678 | |||
| 679 | return &plane->base; | ||
| 680 | } | ||
| 681 | |||
| 682 | static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) | ||
| 683 | { | ||
| 684 | struct drm_plane *plane; | ||
| 685 | unsigned int i; | ||
| 686 | |||
| 687 | for (i = 0; i < 2; i++) { | ||
| 688 | plane = tegra_dc_overlay_plane_create(drm, dc, 1 + i); | ||
| 689 | if (IS_ERR(plane)) | ||
| 690 | return PTR_ERR(plane); | ||
| 430 | } | 691 | } |
| 431 | 692 | ||
| 432 | return 0; | 693 | return 0; |
| @@ -513,10 +774,8 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, | |||
| 513 | tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET); | 774 | tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET); |
| 514 | tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET); | 775 | tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET); |
| 515 | 776 | ||
| 516 | value = GENERAL_UPDATE | WIN_A_UPDATE; | ||
| 517 | tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); | ||
| 518 | |||
| 519 | value = GENERAL_ACT_REQ | WIN_A_ACT_REQ; | 777 | value = GENERAL_ACT_REQ | WIN_A_ACT_REQ; |
| 778 | tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL); | ||
| 520 | tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); | 779 | tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); |
| 521 | 780 | ||
| 522 | return 0; | 781 | return 0; |
| @@ -548,109 +807,6 @@ void tegra_dc_disable_vblank(struct tegra_dc *dc) | |||
| 548 | spin_unlock_irqrestore(&dc->lock, flags); | 807 | spin_unlock_irqrestore(&dc->lock, flags); |
| 549 | } | 808 | } |
| 550 | 809 | ||
| 551 | static int tegra_dc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file, | ||
| 552 | uint32_t handle, uint32_t width, | ||
| 553 | uint32_t height, int32_t hot_x, int32_t hot_y) | ||
| 554 | { | ||
| 555 | unsigned long value = CURSOR_CLIP_DISPLAY; | ||
| 556 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
| 557 | struct drm_gem_object *gem; | ||
| 558 | struct tegra_bo *bo = NULL; | ||
| 559 | |||
| 560 | if (!dc->soc->supports_cursor) | ||
| 561 | return -ENXIO; | ||
| 562 | |||
| 563 | if (width != height) | ||
| 564 | return -EINVAL; | ||
| 565 | |||
| 566 | switch (width) { | ||
| 567 | case 32: | ||
| 568 | value |= CURSOR_SIZE_32x32; | ||
| 569 | break; | ||
| 570 | |||
| 571 | case 64: | ||
| 572 | value |= CURSOR_SIZE_64x64; | ||
| 573 | break; | ||
| 574 | |||
| 575 | case 128: | ||
| 576 | value |= CURSOR_SIZE_128x128; | ||
| 577 | |||
| 578 | case 256: | ||
| 579 | value |= CURSOR_SIZE_256x256; | ||
| 580 | break; | ||
| 581 | |||
| 582 | default: | ||
| 583 | return -EINVAL; | ||
| 584 | } | ||
| 585 | |||
| 586 | if (handle) { | ||
| 587 | gem = drm_gem_object_lookup(crtc->dev, file, handle); | ||
| 588 | if (!gem) | ||
| 589 | return -ENOENT; | ||
| 590 | |||
| 591 | bo = to_tegra_bo(gem); | ||
| 592 | } | ||
| 593 | |||
| 594 | if (bo) { | ||
| 595 | unsigned long addr = (bo->paddr & 0xfffffc00) >> 10; | ||
| 596 | #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT | ||
| 597 | unsigned long high = (bo->paddr & 0xfffffffc) >> 32; | ||
| 598 | #endif | ||
| 599 | |||
| 600 | tegra_dc_writel(dc, value | addr, DC_DISP_CURSOR_START_ADDR); | ||
| 601 | |||
| 602 | #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT | ||
| 603 | tegra_dc_writel(dc, high, DC_DISP_CURSOR_START_ADDR_HI); | ||
| 604 | #endif | ||
| 605 | |||
| 606 | value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); | ||
| 607 | value |= CURSOR_ENABLE; | ||
| 608 | tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); | ||
| 609 | |||
| 610 | value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL); | ||
| 611 | value &= ~CURSOR_DST_BLEND_MASK; | ||
| 612 | value &= ~CURSOR_SRC_BLEND_MASK; | ||
| 613 | value |= CURSOR_MODE_NORMAL; | ||
| 614 | value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC; | ||
| 615 | value |= CURSOR_SRC_BLEND_K1_TIMES_SRC; | ||
| 616 | value |= CURSOR_ALPHA; | ||
| 617 | tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL); | ||
| 618 | } else { | ||
| 619 | value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); | ||
| 620 | value &= ~CURSOR_ENABLE; | ||
| 621 | tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); | ||
| 622 | } | ||
| 623 | |||
| 624 | tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | ||
| 625 | tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 626 | |||
| 627 | tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | ||
| 628 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 629 | |||
| 630 | return 0; | ||
| 631 | } | ||
| 632 | |||
| 633 | static int tegra_dc_cursor_move(struct drm_crtc *crtc, int x, int y) | ||
| 634 | { | ||
| 635 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
| 636 | unsigned long value; | ||
| 637 | |||
| 638 | if (!dc->soc->supports_cursor) | ||
| 639 | return -ENXIO; | ||
| 640 | |||
| 641 | value = ((y & 0x3fff) << 16) | (x & 0x3fff); | ||
| 642 | tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION); | ||
| 643 | |||
| 644 | tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | ||
| 645 | tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 646 | |||
| 647 | /* XXX: only required on generations earlier than Tegra124? */ | ||
| 648 | tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | ||
| 649 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
| 650 | |||
| 651 | return 0; | ||
| 652 | } | ||
| 653 | |||
| 654 | static void tegra_dc_finish_page_flip(struct tegra_dc *dc) | 810 | static void tegra_dc_finish_page_flip(struct tegra_dc *dc) |
| 655 | { | 811 | { |
| 656 | struct drm_device *drm = dc->base.dev; | 812 | struct drm_device *drm = dc->base.dev; |
| @@ -727,8 +883,6 @@ static void tegra_dc_destroy(struct drm_crtc *crtc) | |||
| 727 | } | 883 | } |
| 728 | 884 | ||
| 729 | static const struct drm_crtc_funcs tegra_crtc_funcs = { | 885 | static const struct drm_crtc_funcs tegra_crtc_funcs = { |
| 730 | .cursor_set2 = tegra_dc_cursor_set2, | ||
| 731 | .cursor_move = tegra_dc_cursor_move, | ||
| 732 | .page_flip = tegra_dc_page_flip, | 886 | .page_flip = tegra_dc_page_flip, |
| 733 | .set_config = drm_crtc_helper_set_config, | 887 | .set_config = drm_crtc_helper_set_config, |
| 734 | .destroy = tegra_dc_destroy, | 888 | .destroy = tegra_dc_destroy, |
| @@ -736,12 +890,13 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = { | |||
| 736 | 890 | ||
| 737 | static void tegra_crtc_disable(struct drm_crtc *crtc) | 891 | static void tegra_crtc_disable(struct drm_crtc *crtc) |
| 738 | { | 892 | { |
| 893 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
| 739 | struct drm_device *drm = crtc->dev; | 894 | struct drm_device *drm = crtc->dev; |
| 740 | struct drm_plane *plane; | 895 | struct drm_plane *plane; |
| 741 | 896 | ||
| 742 | drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) { | 897 | drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) { |
| 743 | if (plane->crtc == crtc) { | 898 | if (plane->crtc == crtc) { |
| 744 | tegra_plane_disable(plane); | 899 | tegra_window_plane_disable(plane); |
| 745 | plane->crtc = NULL; | 900 | plane->crtc = NULL; |
| 746 | 901 | ||
| 747 | if (plane->fb) { | 902 | if (plane->fb) { |
| @@ -752,6 +907,7 @@ static void tegra_crtc_disable(struct drm_crtc *crtc) | |||
| 752 | } | 907 | } |
| 753 | 908 | ||
| 754 | drm_crtc_vblank_off(crtc); | 909 | drm_crtc_vblank_off(crtc); |
| 910 | tegra_dc_commit(dc); | ||
| 755 | } | 911 | } |
| 756 | 912 | ||
| 757 | static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc, | 913 | static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc, |
| @@ -934,15 +1090,9 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc) | |||
| 934 | static void tegra_crtc_commit(struct drm_crtc *crtc) | 1090 | static void tegra_crtc_commit(struct drm_crtc *crtc) |
| 935 | { | 1091 | { |
| 936 | struct tegra_dc *dc = to_tegra_dc(crtc); | 1092 | struct tegra_dc *dc = to_tegra_dc(crtc); |
| 937 | unsigned long value; | ||
| 938 | |||
| 939 | value = GENERAL_UPDATE | WIN_A_UPDATE; | ||
| 940 | tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); | ||
| 941 | |||
| 942 | value = GENERAL_ACT_REQ | WIN_A_ACT_REQ; | ||
| 943 | tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); | ||
| 944 | 1093 | ||
| 945 | drm_crtc_vblank_on(crtc); | 1094 | drm_crtc_vblank_on(crtc); |
| 1095 | tegra_dc_commit(dc); | ||
| 946 | } | 1096 | } |
| 947 | 1097 | ||
| 948 | static void tegra_crtc_load_lut(struct drm_crtc *crtc) | 1098 | static void tegra_crtc_load_lut(struct drm_crtc *crtc) |
| @@ -996,7 +1146,7 @@ static int tegra_dc_show_regs(struct seq_file *s, void *data) | |||
| 996 | struct tegra_dc *dc = node->info_ent->data; | 1146 | struct tegra_dc *dc = node->info_ent->data; |
| 997 | 1147 | ||
| 998 | #define DUMP_REG(name) \ | 1148 | #define DUMP_REG(name) \ |
| 999 | seq_printf(s, "%-40s %#05x %08lx\n", #name, name, \ | 1149 | seq_printf(s, "%-40s %#05x %08x\n", #name, name, \ |
| 1000 | tegra_dc_readl(dc, name)) | 1150 | tegra_dc_readl(dc, name)) |
| 1001 | 1151 | ||
| 1002 | DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT); | 1152 | DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT); |
| @@ -1284,9 +1434,40 @@ static int tegra_dc_init(struct host1x_client *client) | |||
| 1284 | struct drm_device *drm = dev_get_drvdata(client->parent); | 1434 | struct drm_device *drm = dev_get_drvdata(client->parent); |
| 1285 | struct tegra_dc *dc = host1x_client_to_dc(client); | 1435 | struct tegra_dc *dc = host1x_client_to_dc(client); |
| 1286 | struct tegra_drm *tegra = drm->dev_private; | 1436 | struct tegra_drm *tegra = drm->dev_private; |
| 1437 | struct drm_plane *primary = NULL; | ||
| 1438 | struct drm_plane *cursor = NULL; | ||
| 1287 | int err; | 1439 | int err; |
| 1288 | 1440 | ||
| 1289 | drm_crtc_init(drm, &dc->base, &tegra_crtc_funcs); | 1441 | if (tegra->domain) { |
| 1442 | err = iommu_attach_device(tegra->domain, dc->dev); | ||
| 1443 | if (err < 0) { | ||
| 1444 | dev_err(dc->dev, "failed to attach to domain: %d\n", | ||
| 1445 | err); | ||
| 1446 | return err; | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | dc->domain = tegra->domain; | ||
| 1450 | } | ||
| 1451 | |||
| 1452 | primary = tegra_dc_primary_plane_create(drm, dc); | ||
| 1453 | if (IS_ERR(primary)) { | ||
| 1454 | err = PTR_ERR(primary); | ||
| 1455 | goto cleanup; | ||
| 1456 | } | ||
| 1457 | |||
| 1458 | if (dc->soc->supports_cursor) { | ||
| 1459 | cursor = tegra_dc_cursor_plane_create(drm, dc); | ||
| 1460 | if (IS_ERR(cursor)) { | ||
| 1461 | err = PTR_ERR(cursor); | ||
| 1462 | goto cleanup; | ||
| 1463 | } | ||
| 1464 | } | ||
| 1465 | |||
| 1466 | err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor, | ||
| 1467 | &tegra_crtc_funcs); | ||
| 1468 | if (err < 0) | ||
| 1469 | goto cleanup; | ||
| 1470 | |||
| 1290 | drm_mode_crtc_set_gamma_size(&dc->base, 256); | 1471 | drm_mode_crtc_set_gamma_size(&dc->base, 256); |
| 1291 | drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); | 1472 | drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); |
| 1292 | 1473 | ||
| @@ -1300,12 +1481,12 @@ static int tegra_dc_init(struct host1x_client *client) | |||
| 1300 | err = tegra_dc_rgb_init(drm, dc); | 1481 | err = tegra_dc_rgb_init(drm, dc); |
| 1301 | if (err < 0 && err != -ENODEV) { | 1482 | if (err < 0 && err != -ENODEV) { |
| 1302 | dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); | 1483 | dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); |
| 1303 | return err; | 1484 | goto cleanup; |
| 1304 | } | 1485 | } |
| 1305 | 1486 | ||
| 1306 | err = tegra_dc_add_planes(drm, dc); | 1487 | err = tegra_dc_add_planes(drm, dc); |
| 1307 | if (err < 0) | 1488 | if (err < 0) |
| 1308 | return err; | 1489 | goto cleanup; |
| 1309 | 1490 | ||
| 1310 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { | 1491 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { |
| 1311 | err = tegra_dc_debugfs_init(dc, drm->primary); | 1492 | err = tegra_dc_debugfs_init(dc, drm->primary); |
| @@ -1318,10 +1499,24 @@ static int tegra_dc_init(struct host1x_client *client) | |||
| 1318 | if (err < 0) { | 1499 | if (err < 0) { |
| 1319 | dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq, | 1500 | dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq, |
| 1320 | err); | 1501 | err); |
| 1321 | return err; | 1502 | goto cleanup; |
| 1322 | } | 1503 | } |
| 1323 | 1504 | ||
| 1324 | return 0; | 1505 | return 0; |
| 1506 | |||
| 1507 | cleanup: | ||
| 1508 | if (cursor) | ||
| 1509 | drm_plane_cleanup(cursor); | ||
| 1510 | |||
| 1511 | if (primary) | ||
| 1512 | drm_plane_cleanup(primary); | ||
| 1513 | |||
| 1514 | if (tegra->domain) { | ||
| 1515 | iommu_detach_device(tegra->domain, dc->dev); | ||
| 1516 | dc->domain = NULL; | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | return err; | ||
| 1325 | } | 1520 | } |
| 1326 | 1521 | ||
| 1327 | static int tegra_dc_exit(struct host1x_client *client) | 1522 | static int tegra_dc_exit(struct host1x_client *client) |
| @@ -1343,6 +1538,11 @@ static int tegra_dc_exit(struct host1x_client *client) | |||
| 1343 | return err; | 1538 | return err; |
| 1344 | } | 1539 | } |
| 1345 | 1540 | ||
| 1541 | if (dc->domain) { | ||
| 1542 | iommu_detach_device(dc->domain, dc->dev); | ||
| 1543 | dc->domain = NULL; | ||
| 1544 | } | ||
| 1545 | |||
| 1346 | return 0; | 1546 | return 0; |
| 1347 | } | 1547 | } |
| 1348 | 1548 | ||
| @@ -1356,6 +1556,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { | |||
| 1356 | .supports_cursor = false, | 1556 | .supports_cursor = false, |
| 1357 | .supports_block_linear = false, | 1557 | .supports_block_linear = false, |
| 1358 | .pitch_align = 8, | 1558 | .pitch_align = 8, |
| 1559 | .has_powergate = false, | ||
| 1359 | }; | 1560 | }; |
| 1360 | 1561 | ||
| 1361 | static const struct tegra_dc_soc_info tegra30_dc_soc_info = { | 1562 | static const struct tegra_dc_soc_info tegra30_dc_soc_info = { |
| @@ -1363,6 +1564,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { | |||
| 1363 | .supports_cursor = false, | 1564 | .supports_cursor = false, |
| 1364 | .supports_block_linear = false, | 1565 | .supports_block_linear = false, |
| 1365 | .pitch_align = 8, | 1566 | .pitch_align = 8, |
| 1567 | .has_powergate = false, | ||
| 1366 | }; | 1568 | }; |
| 1367 | 1569 | ||
| 1368 | static const struct tegra_dc_soc_info tegra114_dc_soc_info = { | 1570 | static const struct tegra_dc_soc_info tegra114_dc_soc_info = { |
| @@ -1370,6 +1572,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { | |||
| 1370 | .supports_cursor = false, | 1572 | .supports_cursor = false, |
| 1371 | .supports_block_linear = false, | 1573 | .supports_block_linear = false, |
| 1372 | .pitch_align = 64, | 1574 | .pitch_align = 64, |
| 1575 | .has_powergate = true, | ||
| 1373 | }; | 1576 | }; |
| 1374 | 1577 | ||
| 1375 | static const struct tegra_dc_soc_info tegra124_dc_soc_info = { | 1578 | static const struct tegra_dc_soc_info tegra124_dc_soc_info = { |
| @@ -1377,6 +1580,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { | |||
| 1377 | .supports_cursor = true, | 1580 | .supports_cursor = true, |
| 1378 | .supports_block_linear = true, | 1581 | .supports_block_linear = true, |
| 1379 | .pitch_align = 64, | 1582 | .pitch_align = 64, |
| 1583 | .has_powergate = true, | ||
| 1380 | }; | 1584 | }; |
| 1381 | 1585 | ||
| 1382 | static const struct of_device_id tegra_dc_of_match[] = { | 1586 | static const struct of_device_id tegra_dc_of_match[] = { |
| @@ -1384,6 +1588,9 @@ static const struct of_device_id tegra_dc_of_match[] = { | |||
| 1384 | .compatible = "nvidia,tegra124-dc", | 1588 | .compatible = "nvidia,tegra124-dc", |
| 1385 | .data = &tegra124_dc_soc_info, | 1589 | .data = &tegra124_dc_soc_info, |
| 1386 | }, { | 1590 | }, { |
| 1591 | .compatible = "nvidia,tegra114-dc", | ||
| 1592 | .data = &tegra114_dc_soc_info, | ||
| 1593 | }, { | ||
| 1387 | .compatible = "nvidia,tegra30-dc", | 1594 | .compatible = "nvidia,tegra30-dc", |
| 1388 | .data = &tegra30_dc_soc_info, | 1595 | .data = &tegra30_dc_soc_info, |
| 1389 | }, { | 1596 | }, { |
| @@ -1466,9 +1673,34 @@ static int tegra_dc_probe(struct platform_device *pdev) | |||
| 1466 | return PTR_ERR(dc->rst); | 1673 | return PTR_ERR(dc->rst); |
| 1467 | } | 1674 | } |
| 1468 | 1675 | ||
| 1469 | err = clk_prepare_enable(dc->clk); | 1676 | if (dc->soc->has_powergate) { |
| 1470 | if (err < 0) | 1677 | if (dc->pipe == 0) |
| 1471 | return err; | 1678 | dc->powergate = TEGRA_POWERGATE_DIS; |
| 1679 | else | ||
| 1680 | dc->powergate = TEGRA_POWERGATE_DISB; | ||
| 1681 | |||
| 1682 | err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk, | ||
| 1683 | dc->rst); | ||
| 1684 | if (err < 0) { | ||
| 1685 | dev_err(&pdev->dev, "failed to power partition: %d\n", | ||
| 1686 | err); | ||
| 1687 | return err; | ||
| 1688 | } | ||
| 1689 | } else { | ||
| 1690 | err = clk_prepare_enable(dc->clk); | ||
| 1691 | if (err < 0) { | ||
| 1692 | dev_err(&pdev->dev, "failed to enable clock: %d\n", | ||
| 1693 | err); | ||
| 1694 | return err; | ||
| 1695 | } | ||
| 1696 | |||
| 1697 | err = reset_control_deassert(dc->rst); | ||
| 1698 | if (err < 0) { | ||
| 1699 | dev_err(&pdev->dev, "failed to deassert reset: %d\n", | ||
| 1700 | err); | ||
| 1701 | return err; | ||
| 1702 | } | ||
| 1703 | } | ||
| 1472 | 1704 | ||
| 1473 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1705 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 1474 | dc->regs = devm_ioremap_resource(&pdev->dev, regs); | 1706 | dc->regs = devm_ioremap_resource(&pdev->dev, regs); |
| @@ -1522,6 +1754,10 @@ static int tegra_dc_remove(struct platform_device *pdev) | |||
| 1522 | } | 1754 | } |
| 1523 | 1755 | ||
| 1524 | reset_control_assert(dc->rst); | 1756 | reset_control_assert(dc->rst); |
| 1757 | |||
| 1758 | if (dc->soc->has_powergate) | ||
| 1759 | tegra_powergate_power_off(dc->powergate); | ||
| 1760 | |||
| 1525 | clk_disable_unprepare(dc->clk); | 1761 | clk_disable_unprepare(dc->clk); |
| 1526 | 1762 | ||
| 1527 | return 0; | 1763 | return 0; |
