diff options
| -rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_g2d.c | 160 |
1 files changed, 123 insertions, 37 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 7c1aac3871da..1a022dc4188e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c | |||
| @@ -96,8 +96,6 @@ | |||
| 96 | #define G2D_CMDLIST_POOL_SIZE (G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM) | 96 | #define G2D_CMDLIST_POOL_SIZE (G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM) |
| 97 | #define G2D_CMDLIST_DATA_NUM (G2D_CMDLIST_SIZE / sizeof(u32) - 2) | 97 | #define G2D_CMDLIST_DATA_NUM (G2D_CMDLIST_SIZE / sizeof(u32) - 2) |
| 98 | 98 | ||
| 99 | #define MAX_BUF_ADDR_NR 6 | ||
| 100 | |||
| 101 | /* maximum buffer pool size of userptr is 64MB as default */ | 99 | /* maximum buffer pool size of userptr is 64MB as default */ |
| 102 | #define MAX_POOL (64 * 1024 * 1024) | 100 | #define MAX_POOL (64 * 1024 * 1024) |
| 103 | 101 | ||
| @@ -106,6 +104,17 @@ enum { | |||
| 106 | BUF_TYPE_USERPTR, | 104 | BUF_TYPE_USERPTR, |
| 107 | }; | 105 | }; |
| 108 | 106 | ||
| 107 | enum g2d_reg_type { | ||
| 108 | REG_TYPE_NONE = -1, | ||
| 109 | REG_TYPE_SRC, | ||
| 110 | REG_TYPE_SRC_PLANE2, | ||
| 111 | REG_TYPE_DST, | ||
| 112 | REG_TYPE_DST_PLANE2, | ||
| 113 | REG_TYPE_PAT, | ||
| 114 | REG_TYPE_MSK, | ||
| 115 | MAX_REG_TYPE_NR | ||
| 116 | }; | ||
| 117 | |||
| 109 | /* cmdlist data structure */ | 118 | /* cmdlist data structure */ |
| 110 | struct g2d_cmdlist { | 119 | struct g2d_cmdlist { |
| 111 | u32 head; | 120 | u32 head; |
| @@ -113,6 +122,22 @@ struct g2d_cmdlist { | |||
| 113 | u32 last; /* last data offset */ | 122 | u32 last; /* last data offset */ |
| 114 | }; | 123 | }; |
| 115 | 124 | ||
| 125 | /* | ||
| 126 | * A structure of buffer information | ||
| 127 | * | ||
| 128 | * @map_nr: manages the number of mapped buffers | ||
| 129 | * @reg_types: stores regitster type in the order of requested command | ||
| 130 | * @handles: stores buffer handle in its reg_type position | ||
| 131 | * @types: stores buffer type in its reg_type position | ||
| 132 | * | ||
| 133 | */ | ||
| 134 | struct g2d_buf_info { | ||
| 135 | unsigned int map_nr; | ||
| 136 | enum g2d_reg_type reg_types[MAX_REG_TYPE_NR]; | ||
| 137 | unsigned long handles[MAX_REG_TYPE_NR]; | ||
| 138 | unsigned int types[MAX_REG_TYPE_NR]; | ||
| 139 | }; | ||
| 140 | |||
| 116 | struct drm_exynos_pending_g2d_event { | 141 | struct drm_exynos_pending_g2d_event { |
| 117 | struct drm_pending_event base; | 142 | struct drm_pending_event base; |
| 118 | struct drm_exynos_g2d_event event; | 143 | struct drm_exynos_g2d_event event; |
| @@ -134,10 +159,8 @@ struct g2d_cmdlist_userptr { | |||
| 134 | struct g2d_cmdlist_node { | 159 | struct g2d_cmdlist_node { |
| 135 | struct list_head list; | 160 | struct list_head list; |
| 136 | struct g2d_cmdlist *cmdlist; | 161 | struct g2d_cmdlist *cmdlist; |
| 137 | unsigned int map_nr; | ||
| 138 | unsigned long handles[MAX_BUF_ADDR_NR]; | ||
| 139 | unsigned int buf_type[MAX_BUF_ADDR_NR]; | ||
| 140 | dma_addr_t dma_addr; | 162 | dma_addr_t dma_addr; |
| 163 | struct g2d_buf_info buf_info; | ||
| 141 | 164 | ||
| 142 | struct drm_exynos_pending_g2d_event *event; | 165 | struct drm_exynos_pending_g2d_event *event; |
| 143 | }; | 166 | }; |
| @@ -187,6 +210,7 @@ static int g2d_init_cmdlist(struct g2d_data *g2d) | |||
| 187 | struct exynos_drm_subdrv *subdrv = &g2d->subdrv; | 210 | struct exynos_drm_subdrv *subdrv = &g2d->subdrv; |
| 188 | int nr; | 211 | int nr; |
| 189 | int ret; | 212 | int ret; |
| 213 | struct g2d_buf_info *buf_info; | ||
| 190 | 214 | ||
| 191 | init_dma_attrs(&g2d->cmdlist_dma_attrs); | 215 | init_dma_attrs(&g2d->cmdlist_dma_attrs); |
| 192 | dma_set_attr(DMA_ATTR_WRITE_COMBINE, &g2d->cmdlist_dma_attrs); | 216 | dma_set_attr(DMA_ATTR_WRITE_COMBINE, &g2d->cmdlist_dma_attrs); |
| @@ -208,11 +232,17 @@ static int g2d_init_cmdlist(struct g2d_data *g2d) | |||
| 208 | } | 232 | } |
| 209 | 233 | ||
| 210 | for (nr = 0; nr < G2D_CMDLIST_NUM; nr++) { | 234 | for (nr = 0; nr < G2D_CMDLIST_NUM; nr++) { |
| 235 | unsigned int i; | ||
| 236 | |||
| 211 | node[nr].cmdlist = | 237 | node[nr].cmdlist = |
| 212 | g2d->cmdlist_pool_virt + nr * G2D_CMDLIST_SIZE; | 238 | g2d->cmdlist_pool_virt + nr * G2D_CMDLIST_SIZE; |
| 213 | node[nr].dma_addr = | 239 | node[nr].dma_addr = |
| 214 | g2d->cmdlist_pool + nr * G2D_CMDLIST_SIZE; | 240 | g2d->cmdlist_pool + nr * G2D_CMDLIST_SIZE; |
| 215 | 241 | ||
| 242 | buf_info = &node[nr].buf_info; | ||
| 243 | for (i = 0; i < MAX_REG_TYPE_NR; i++) | ||
| 244 | buf_info->reg_types[i] = REG_TYPE_NONE; | ||
| 245 | |||
| 216 | list_add_tail(&node[nr].list, &g2d->free_cmdlist); | 246 | list_add_tail(&node[nr].list, &g2d->free_cmdlist); |
| 217 | } | 247 | } |
| 218 | 248 | ||
| @@ -507,36 +537,80 @@ static void g2d_userptr_free_all(struct drm_device *drm_dev, | |||
| 507 | g2d->current_pool = 0; | 537 | g2d->current_pool = 0; |
| 508 | } | 538 | } |
| 509 | 539 | ||
| 540 | static enum g2d_reg_type g2d_get_reg_type(int reg_offset) | ||
| 541 | { | ||
| 542 | enum g2d_reg_type reg_type; | ||
| 543 | |||
| 544 | switch (reg_offset) { | ||
| 545 | case G2D_SRC_BASE_ADDR: | ||
| 546 | reg_type = REG_TYPE_SRC; | ||
| 547 | break; | ||
| 548 | case G2D_SRC_PLANE2_BASE_ADDR: | ||
| 549 | reg_type = REG_TYPE_SRC_PLANE2; | ||
| 550 | break; | ||
| 551 | case G2D_DST_BASE_ADDR: | ||
| 552 | reg_type = REG_TYPE_DST; | ||
| 553 | break; | ||
| 554 | case G2D_DST_PLANE2_BASE_ADDR: | ||
| 555 | reg_type = REG_TYPE_DST_PLANE2; | ||
| 556 | break; | ||
| 557 | case G2D_PAT_BASE_ADDR: | ||
| 558 | reg_type = REG_TYPE_PAT; | ||
| 559 | break; | ||
| 560 | case G2D_MSK_BASE_ADDR: | ||
| 561 | reg_type = REG_TYPE_MSK; | ||
| 562 | break; | ||
| 563 | default: | ||
| 564 | reg_type = REG_TYPE_NONE; | ||
| 565 | DRM_ERROR("Unknown register offset![%d]\n", reg_offset); | ||
| 566 | break; | ||
| 567 | }; | ||
| 568 | |||
| 569 | return reg_type; | ||
| 570 | } | ||
| 571 | |||
| 510 | static int g2d_map_cmdlist_gem(struct g2d_data *g2d, | 572 | static int g2d_map_cmdlist_gem(struct g2d_data *g2d, |
| 511 | struct g2d_cmdlist_node *node, | 573 | struct g2d_cmdlist_node *node, |
| 512 | struct drm_device *drm_dev, | 574 | struct drm_device *drm_dev, |
| 513 | struct drm_file *file) | 575 | struct drm_file *file) |
| 514 | { | 576 | { |
| 515 | struct g2d_cmdlist *cmdlist = node->cmdlist; | 577 | struct g2d_cmdlist *cmdlist = node->cmdlist; |
| 578 | struct g2d_buf_info *buf_info = &node->buf_info; | ||
| 516 | int offset; | 579 | int offset; |
| 580 | int ret; | ||
| 517 | int i; | 581 | int i; |
| 518 | 582 | ||
| 519 | for (i = 0; i < node->map_nr; i++) { | 583 | for (i = 0; i < buf_info->map_nr; i++) { |
| 584 | enum g2d_reg_type reg_type; | ||
| 585 | int reg_pos; | ||
| 520 | unsigned long handle; | 586 | unsigned long handle; |
| 521 | dma_addr_t *addr; | 587 | dma_addr_t *addr; |
| 522 | 588 | ||
| 523 | offset = cmdlist->last - (i * 2 + 1); | 589 | reg_pos = cmdlist->last - 2 * (i + 1); |
| 524 | handle = cmdlist->data[offset]; | 590 | |
| 591 | offset = cmdlist->data[reg_pos]; | ||
| 592 | handle = cmdlist->data[reg_pos + 1]; | ||
| 593 | |||
| 594 | reg_type = g2d_get_reg_type(offset); | ||
| 595 | if (reg_type == REG_TYPE_NONE) { | ||
| 596 | ret = -EFAULT; | ||
| 597 | goto err; | ||
| 598 | } | ||
| 525 | 599 | ||
| 526 | if (node->buf_type[i] == BUF_TYPE_GEM) { | 600 | if (buf_info->types[reg_type] == BUF_TYPE_GEM) { |
| 527 | addr = exynos_drm_gem_get_dma_addr(drm_dev, handle, | 601 | addr = exynos_drm_gem_get_dma_addr(drm_dev, handle, |
| 528 | file); | 602 | file); |
| 529 | if (IS_ERR(addr)) { | 603 | if (IS_ERR(addr)) { |
| 530 | node->map_nr = i; | 604 | ret = -EFAULT; |
| 531 | return -EFAULT; | 605 | goto err; |
| 532 | } | 606 | } |
| 533 | } else { | 607 | } else { |
| 534 | struct drm_exynos_g2d_userptr g2d_userptr; | 608 | struct drm_exynos_g2d_userptr g2d_userptr; |
| 535 | 609 | ||
| 536 | if (copy_from_user(&g2d_userptr, (void __user *)handle, | 610 | if (copy_from_user(&g2d_userptr, (void __user *)handle, |
| 537 | sizeof(struct drm_exynos_g2d_userptr))) { | 611 | sizeof(struct drm_exynos_g2d_userptr))) { |
| 538 | node->map_nr = i; | 612 | ret = -EFAULT; |
| 539 | return -EFAULT; | 613 | goto err; |
| 540 | } | 614 | } |
| 541 | 615 | ||
| 542 | addr = g2d_userptr_get_dma_addr(drm_dev, | 616 | addr = g2d_userptr_get_dma_addr(drm_dev, |
| @@ -545,16 +619,21 @@ static int g2d_map_cmdlist_gem(struct g2d_data *g2d, | |||
| 545 | file, | 619 | file, |
| 546 | &handle); | 620 | &handle); |
| 547 | if (IS_ERR(addr)) { | 621 | if (IS_ERR(addr)) { |
| 548 | node->map_nr = i; | 622 | ret = -EFAULT; |
| 549 | return -EFAULT; | 623 | goto err; |
| 550 | } | 624 | } |
| 551 | } | 625 | } |
| 552 | 626 | ||
| 553 | cmdlist->data[offset] = *addr; | 627 | cmdlist->data[reg_pos + 1] = *addr; |
| 554 | node->handles[i] = handle; | 628 | buf_info->reg_types[i] = reg_type; |
| 629 | buf_info->handles[reg_type] = handle; | ||
| 555 | } | 630 | } |
| 556 | 631 | ||
| 557 | return 0; | 632 | return 0; |
| 633 | |||
| 634 | err: | ||
| 635 | buf_info->map_nr = i; | ||
| 636 | return ret; | ||
| 558 | } | 637 | } |
| 559 | 638 | ||
| 560 | static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d, | 639 | static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d, |
| @@ -562,23 +641,30 @@ static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d, | |||
| 562 | struct drm_file *filp) | 641 | struct drm_file *filp) |
| 563 | { | 642 | { |
| 564 | struct exynos_drm_subdrv *subdrv = &g2d->subdrv; | 643 | struct exynos_drm_subdrv *subdrv = &g2d->subdrv; |
| 644 | struct g2d_buf_info *buf_info = &node->buf_info; | ||
| 565 | int i; | 645 | int i; |
| 566 | 646 | ||
| 567 | for (i = 0; i < node->map_nr; i++) { | 647 | for (i = 0; i < buf_info->map_nr; i++) { |
| 568 | unsigned long handle = node->handles[i]; | 648 | enum g2d_reg_type reg_type; |
| 649 | unsigned long handle; | ||
| 650 | |||
| 651 | reg_type = buf_info->reg_types[i]; | ||
| 569 | 652 | ||
| 570 | if (node->buf_type[i] == BUF_TYPE_GEM) | 653 | handle = buf_info->handles[reg_type]; |
| 654 | |||
| 655 | if (buf_info->types[reg_type] == BUF_TYPE_GEM) | ||
| 571 | exynos_drm_gem_put_dma_addr(subdrv->drm_dev, handle, | 656 | exynos_drm_gem_put_dma_addr(subdrv->drm_dev, handle, |
| 572 | filp); | 657 | filp); |
| 573 | else | 658 | else |
| 574 | g2d_userptr_put_dma_addr(subdrv->drm_dev, handle, | 659 | g2d_userptr_put_dma_addr(subdrv->drm_dev, handle, |
| 575 | false); | 660 | false); |
| 576 | 661 | ||
| 577 | node->handles[i] = 0; | 662 | buf_info->reg_types[i] = REG_TYPE_NONE; |
| 578 | node->buf_type[i] = 0; | 663 | buf_info->handles[reg_type] = 0; |
| 664 | buf_info->types[reg_type] = 0; | ||
| 579 | } | 665 | } |
| 580 | 666 | ||
| 581 | node->map_nr = 0; | 667 | buf_info->map_nr = 0; |
| 582 | } | 668 | } |
| 583 | 669 | ||
| 584 | static void g2d_dma_start(struct g2d_data *g2d, | 670 | static void g2d_dma_start(struct g2d_data *g2d, |
| @@ -721,20 +807,12 @@ static int g2d_check_reg_offset(struct device *dev, | |||
| 721 | int i; | 807 | int i; |
| 722 | 808 | ||
| 723 | for (i = 0; i < nr; i++) { | 809 | for (i = 0; i < nr; i++) { |
| 724 | index = cmdlist->last - 2 * (i + 1); | 810 | struct g2d_buf_info *buf_info = &node->buf_info; |
| 811 | enum g2d_reg_type reg_type; | ||
| 725 | 812 | ||
| 726 | if (for_addr) { | 813 | index = cmdlist->last - 2 * (i + 1); |
| 727 | /* check userptr buffer type. */ | ||
| 728 | reg_offset = (cmdlist->data[index] & | ||
| 729 | ~0x7fffffff) >> 31; | ||
| 730 | if (reg_offset) { | ||
| 731 | node->buf_type[i] = BUF_TYPE_USERPTR; | ||
| 732 | cmdlist->data[index] &= ~G2D_BUF_USERPTR; | ||
| 733 | } | ||
| 734 | } | ||
| 735 | 814 | ||
| 736 | reg_offset = cmdlist->data[index] & ~0xfffff000; | 815 | reg_offset = cmdlist->data[index] & ~0xfffff000; |
| 737 | |||
| 738 | if (reg_offset < G2D_VALID_START || reg_offset > G2D_VALID_END) | 816 | if (reg_offset < G2D_VALID_START || reg_offset > G2D_VALID_END) |
| 739 | goto err; | 817 | goto err; |
| 740 | if (reg_offset % 4) | 818 | if (reg_offset % 4) |
| @@ -750,8 +828,16 @@ static int g2d_check_reg_offset(struct device *dev, | |||
| 750 | if (!for_addr) | 828 | if (!for_addr) |
| 751 | goto err; | 829 | goto err; |
| 752 | 830 | ||
| 753 | if (node->buf_type[i] != BUF_TYPE_USERPTR) | 831 | reg_type = g2d_get_reg_type(reg_offset); |
| 754 | node->buf_type[i] = BUF_TYPE_GEM; | 832 | if (reg_type == REG_TYPE_NONE) |
| 833 | goto err; | ||
| 834 | |||
| 835 | /* check userptr buffer type. */ | ||
| 836 | if ((cmdlist->data[index] & ~0x7fffffff) >> 31) { | ||
| 837 | buf_info->types[reg_type] = BUF_TYPE_USERPTR; | ||
| 838 | cmdlist->data[index] &= ~G2D_BUF_USERPTR; | ||
| 839 | } else | ||
| 840 | buf_info->types[reg_type] = BUF_TYPE_GEM; | ||
| 755 | break; | 841 | break; |
| 756 | default: | 842 | default: |
| 757 | if (for_addr) | 843 | if (for_addr) |
| @@ -898,7 +984,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, | |||
| 898 | if (ret < 0) | 984 | if (ret < 0) |
| 899 | goto err_free_event; | 985 | goto err_free_event; |
| 900 | 986 | ||
| 901 | node->map_nr = req->cmd_buf_nr; | 987 | node->buf_info.map_nr = req->cmd_buf_nr; |
| 902 | if (req->cmd_buf_nr) { | 988 | if (req->cmd_buf_nr) { |
| 903 | struct drm_exynos_g2d_cmd *cmd_buf; | 989 | struct drm_exynos_g2d_cmd *cmd_buf; |
| 904 | 990 | ||
