diff options
| -rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_g2d.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 1a022dc4188e..47a493c8a71f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c | |||
| @@ -48,8 +48,14 @@ | |||
| 48 | 48 | ||
| 49 | /* registers for base address */ | 49 | /* registers for base address */ |
| 50 | #define G2D_SRC_BASE_ADDR 0x0304 | 50 | #define G2D_SRC_BASE_ADDR 0x0304 |
| 51 | #define G2D_SRC_COLOR_MODE 0x030C | ||
| 52 | #define G2D_SRC_LEFT_TOP 0x0310 | ||
| 53 | #define G2D_SRC_RIGHT_BOTTOM 0x0314 | ||
| 51 | #define G2D_SRC_PLANE2_BASE_ADDR 0x0318 | 54 | #define G2D_SRC_PLANE2_BASE_ADDR 0x0318 |
| 52 | #define G2D_DST_BASE_ADDR 0x0404 | 55 | #define G2D_DST_BASE_ADDR 0x0404 |
| 56 | #define G2D_DST_COLOR_MODE 0x040C | ||
| 57 | #define G2D_DST_LEFT_TOP 0x0410 | ||
| 58 | #define G2D_DST_RIGHT_BOTTOM 0x0414 | ||
| 53 | #define G2D_DST_PLANE2_BASE_ADDR 0x0418 | 59 | #define G2D_DST_PLANE2_BASE_ADDR 0x0418 |
| 54 | #define G2D_PAT_BASE_ADDR 0x0500 | 60 | #define G2D_PAT_BASE_ADDR 0x0500 |
| 55 | #define G2D_MSK_BASE_ADDR 0x0520 | 61 | #define G2D_MSK_BASE_ADDR 0x0520 |
| @@ -91,6 +97,22 @@ | |||
| 91 | #define G2D_START_NHOLT (1 << 1) | 97 | #define G2D_START_NHOLT (1 << 1) |
| 92 | #define G2D_START_BITBLT (1 << 0) | 98 | #define G2D_START_BITBLT (1 << 0) |
| 93 | 99 | ||
| 100 | /* buffer color format */ | ||
| 101 | #define G2D_FMT_XRGB8888 0 | ||
| 102 | #define G2D_FMT_ARGB8888 1 | ||
| 103 | #define G2D_FMT_RGB565 2 | ||
| 104 | #define G2D_FMT_XRGB1555 3 | ||
| 105 | #define G2D_FMT_ARGB1555 4 | ||
| 106 | #define G2D_FMT_XRGB4444 5 | ||
| 107 | #define G2D_FMT_ARGB4444 6 | ||
| 108 | #define G2D_FMT_PACKED_RGB888 7 | ||
| 109 | #define G2D_FMT_A8 11 | ||
| 110 | #define G2D_FMT_L8 12 | ||
| 111 | |||
| 112 | /* buffer valid length */ | ||
| 113 | #define G2D_LEN_MIN 1 | ||
| 114 | #define G2D_LEN_MAX 8000 | ||
| 115 | |||
| 94 | #define G2D_CMDLIST_SIZE (PAGE_SIZE / 4) | 116 | #define G2D_CMDLIST_SIZE (PAGE_SIZE / 4) |
| 95 | #define G2D_CMDLIST_NUM 64 | 117 | #define G2D_CMDLIST_NUM 64 |
| 96 | #define G2D_CMDLIST_POOL_SIZE (G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM) | 118 | #define G2D_CMDLIST_POOL_SIZE (G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM) |
| @@ -123,12 +145,31 @@ struct g2d_cmdlist { | |||
| 123 | }; | 145 | }; |
| 124 | 146 | ||
| 125 | /* | 147 | /* |
| 148 | * A structure of buffer description | ||
| 149 | * | ||
| 150 | * @format: color format | ||
| 151 | * @left_x: the x coordinates of left top corner | ||
| 152 | * @top_y: the y coordinates of left top corner | ||
| 153 | * @right_x: the x coordinates of right bottom corner | ||
| 154 | * @bottom_y: the y coordinates of right bottom corner | ||
| 155 | * | ||
| 156 | */ | ||
| 157 | struct g2d_buf_desc { | ||
| 158 | unsigned int format; | ||
| 159 | unsigned int left_x; | ||
| 160 | unsigned int top_y; | ||
| 161 | unsigned int right_x; | ||
| 162 | unsigned int bottom_y; | ||
| 163 | }; | ||
| 164 | |||
| 165 | /* | ||
| 126 | * A structure of buffer information | 166 | * A structure of buffer information |
| 127 | * | 167 | * |
| 128 | * @map_nr: manages the number of mapped buffers | 168 | * @map_nr: manages the number of mapped buffers |
| 129 | * @reg_types: stores regitster type in the order of requested command | 169 | * @reg_types: stores regitster type in the order of requested command |
| 130 | * @handles: stores buffer handle in its reg_type position | 170 | * @handles: stores buffer handle in its reg_type position |
| 131 | * @types: stores buffer type in its reg_type position | 171 | * @types: stores buffer type in its reg_type position |
| 172 | * @descs: stores buffer description in its reg_type position | ||
| 132 | * | 173 | * |
| 133 | */ | 174 | */ |
| 134 | struct g2d_buf_info { | 175 | struct g2d_buf_info { |
| @@ -136,6 +177,7 @@ struct g2d_buf_info { | |||
| 136 | enum g2d_reg_type reg_types[MAX_REG_TYPE_NR]; | 177 | enum g2d_reg_type reg_types[MAX_REG_TYPE_NR]; |
| 137 | unsigned long handles[MAX_REG_TYPE_NR]; | 178 | unsigned long handles[MAX_REG_TYPE_NR]; |
| 138 | unsigned int types[MAX_REG_TYPE_NR]; | 179 | unsigned int types[MAX_REG_TYPE_NR]; |
| 180 | struct g2d_buf_desc descs[MAX_REG_TYPE_NR]; | ||
| 139 | }; | 181 | }; |
| 140 | 182 | ||
| 141 | struct drm_exynos_pending_g2d_event { | 183 | struct drm_exynos_pending_g2d_event { |
| @@ -543,12 +585,18 @@ static enum g2d_reg_type g2d_get_reg_type(int reg_offset) | |||
| 543 | 585 | ||
| 544 | switch (reg_offset) { | 586 | switch (reg_offset) { |
| 545 | case G2D_SRC_BASE_ADDR: | 587 | case G2D_SRC_BASE_ADDR: |
| 588 | case G2D_SRC_COLOR_MODE: | ||
| 589 | case G2D_SRC_LEFT_TOP: | ||
| 590 | case G2D_SRC_RIGHT_BOTTOM: | ||
| 546 | reg_type = REG_TYPE_SRC; | 591 | reg_type = REG_TYPE_SRC; |
| 547 | break; | 592 | break; |
| 548 | case G2D_SRC_PLANE2_BASE_ADDR: | 593 | case G2D_SRC_PLANE2_BASE_ADDR: |
| 549 | reg_type = REG_TYPE_SRC_PLANE2; | 594 | reg_type = REG_TYPE_SRC_PLANE2; |
| 550 | break; | 595 | break; |
| 551 | case G2D_DST_BASE_ADDR: | 596 | case G2D_DST_BASE_ADDR: |
| 597 | case G2D_DST_COLOR_MODE: | ||
| 598 | case G2D_DST_LEFT_TOP: | ||
| 599 | case G2D_DST_RIGHT_BOTTOM: | ||
| 552 | reg_type = REG_TYPE_DST; | 600 | reg_type = REG_TYPE_DST; |
| 553 | break; | 601 | break; |
| 554 | case G2D_DST_PLANE2_BASE_ADDR: | 602 | case G2D_DST_PLANE2_BASE_ADDR: |
| @@ -569,6 +617,69 @@ static enum g2d_reg_type g2d_get_reg_type(int reg_offset) | |||
| 569 | return reg_type; | 617 | return reg_type; |
| 570 | } | 618 | } |
| 571 | 619 | ||
| 620 | static unsigned long g2d_get_buf_bpp(unsigned int format) | ||
| 621 | { | ||
| 622 | unsigned long bpp; | ||
| 623 | |||
| 624 | switch (format) { | ||
| 625 | case G2D_FMT_XRGB8888: | ||
| 626 | case G2D_FMT_ARGB8888: | ||
| 627 | bpp = 4; | ||
| 628 | break; | ||
| 629 | case G2D_FMT_RGB565: | ||
| 630 | case G2D_FMT_XRGB1555: | ||
| 631 | case G2D_FMT_ARGB1555: | ||
| 632 | case G2D_FMT_XRGB4444: | ||
| 633 | case G2D_FMT_ARGB4444: | ||
| 634 | bpp = 2; | ||
| 635 | break; | ||
| 636 | case G2D_FMT_PACKED_RGB888: | ||
| 637 | bpp = 3; | ||
| 638 | break; | ||
| 639 | default: | ||
| 640 | bpp = 1; | ||
| 641 | break; | ||
| 642 | } | ||
| 643 | |||
| 644 | return bpp; | ||
| 645 | } | ||
| 646 | |||
| 647 | static bool g2d_check_buf_desc_is_valid(struct g2d_buf_desc *buf_desc, | ||
| 648 | enum g2d_reg_type reg_type, | ||
| 649 | unsigned long size) | ||
| 650 | { | ||
| 651 | unsigned int width, height; | ||
| 652 | unsigned long area; | ||
| 653 | |||
| 654 | /* | ||
| 655 | * check source and destination buffers only. | ||
| 656 | * so the others are always valid. | ||
| 657 | */ | ||
| 658 | if (reg_type != REG_TYPE_SRC && reg_type != REG_TYPE_DST) | ||
| 659 | return true; | ||
| 660 | |||
| 661 | width = buf_desc->right_x - buf_desc->left_x; | ||
| 662 | if (width < G2D_LEN_MIN || width > G2D_LEN_MAX) { | ||
| 663 | DRM_ERROR("width[%u] is out of range!\n", width); | ||
| 664 | return false; | ||
| 665 | } | ||
| 666 | |||
| 667 | height = buf_desc->bottom_y - buf_desc->top_y; | ||
| 668 | if (height < G2D_LEN_MIN || height > G2D_LEN_MAX) { | ||
| 669 | DRM_ERROR("height[%u] is out of range!\n", height); | ||
| 670 | return false; | ||
| 671 | } | ||
| 672 | |||
| 673 | area = (unsigned long)width * (unsigned long)height * | ||
| 674 | g2d_get_buf_bpp(buf_desc->format); | ||
| 675 | if (area > size) { | ||
| 676 | DRM_ERROR("area[%lu] is out of range[%lu]!\n", area, size); | ||
| 677 | return false; | ||
| 678 | } | ||
| 679 | |||
| 680 | return true; | ||
| 681 | } | ||
| 682 | |||
| 572 | static int g2d_map_cmdlist_gem(struct g2d_data *g2d, | 683 | static int g2d_map_cmdlist_gem(struct g2d_data *g2d, |
| 573 | struct g2d_cmdlist_node *node, | 684 | struct g2d_cmdlist_node *node, |
| 574 | struct drm_device *drm_dev, | 685 | struct drm_device *drm_dev, |
| @@ -581,6 +692,7 @@ static int g2d_map_cmdlist_gem(struct g2d_data *g2d, | |||
| 581 | int i; | 692 | int i; |
| 582 | 693 | ||
| 583 | for (i = 0; i < buf_info->map_nr; i++) { | 694 | for (i = 0; i < buf_info->map_nr; i++) { |
| 695 | struct g2d_buf_desc *buf_desc; | ||
| 584 | enum g2d_reg_type reg_type; | 696 | enum g2d_reg_type reg_type; |
| 585 | int reg_pos; | 697 | int reg_pos; |
| 586 | unsigned long handle; | 698 | unsigned long handle; |
| @@ -597,7 +709,23 @@ static int g2d_map_cmdlist_gem(struct g2d_data *g2d, | |||
| 597 | goto err; | 709 | goto err; |
| 598 | } | 710 | } |
| 599 | 711 | ||
| 712 | buf_desc = &buf_info->descs[reg_type]; | ||
| 713 | |||
| 600 | if (buf_info->types[reg_type] == BUF_TYPE_GEM) { | 714 | if (buf_info->types[reg_type] == BUF_TYPE_GEM) { |
| 715 | unsigned long size; | ||
| 716 | |||
| 717 | size = exynos_drm_gem_get_size(drm_dev, handle, file); | ||
| 718 | if (!size) { | ||
| 719 | ret = -EFAULT; | ||
| 720 | goto err; | ||
| 721 | } | ||
| 722 | |||
| 723 | if (!g2d_check_buf_desc_is_valid(buf_desc, reg_type, | ||
| 724 | size)) { | ||
| 725 | ret = -EFAULT; | ||
| 726 | goto err; | ||
| 727 | } | ||
| 728 | |||
| 601 | addr = exynos_drm_gem_get_dma_addr(drm_dev, handle, | 729 | addr = exynos_drm_gem_get_dma_addr(drm_dev, handle, |
| 602 | file); | 730 | file); |
| 603 | if (IS_ERR(addr)) { | 731 | if (IS_ERR(addr)) { |
| @@ -613,6 +741,12 @@ static int g2d_map_cmdlist_gem(struct g2d_data *g2d, | |||
| 613 | goto err; | 741 | goto err; |
| 614 | } | 742 | } |
| 615 | 743 | ||
| 744 | if (!g2d_check_buf_desc_is_valid(buf_desc, reg_type, | ||
| 745 | g2d_userptr.size)) { | ||
| 746 | ret = -EFAULT; | ||
| 747 | goto err; | ||
| 748 | } | ||
| 749 | |||
| 616 | addr = g2d_userptr_get_dma_addr(drm_dev, | 750 | addr = g2d_userptr_get_dma_addr(drm_dev, |
| 617 | g2d_userptr.userptr, | 751 | g2d_userptr.userptr, |
| 618 | g2d_userptr.size, | 752 | g2d_userptr.size, |
| @@ -645,11 +779,13 @@ static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d, | |||
| 645 | int i; | 779 | int i; |
| 646 | 780 | ||
| 647 | for (i = 0; i < buf_info->map_nr; i++) { | 781 | for (i = 0; i < buf_info->map_nr; i++) { |
| 782 | struct g2d_buf_desc *buf_desc; | ||
| 648 | enum g2d_reg_type reg_type; | 783 | enum g2d_reg_type reg_type; |
| 649 | unsigned long handle; | 784 | unsigned long handle; |
| 650 | 785 | ||
| 651 | reg_type = buf_info->reg_types[i]; | 786 | reg_type = buf_info->reg_types[i]; |
| 652 | 787 | ||
| 788 | buf_desc = &buf_info->descs[reg_type]; | ||
| 653 | handle = buf_info->handles[reg_type]; | 789 | handle = buf_info->handles[reg_type]; |
| 654 | 790 | ||
| 655 | if (buf_info->types[reg_type] == BUF_TYPE_GEM) | 791 | if (buf_info->types[reg_type] == BUF_TYPE_GEM) |
| @@ -662,6 +798,7 @@ static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d, | |||
| 662 | buf_info->reg_types[i] = REG_TYPE_NONE; | 798 | buf_info->reg_types[i] = REG_TYPE_NONE; |
| 663 | buf_info->handles[reg_type] = 0; | 799 | buf_info->handles[reg_type] = 0; |
| 664 | buf_info->types[reg_type] = 0; | 800 | buf_info->types[reg_type] = 0; |
| 801 | memset(buf_desc, 0x00, sizeof(*buf_desc)); | ||
| 665 | } | 802 | } |
| 666 | 803 | ||
| 667 | buf_info->map_nr = 0; | 804 | buf_info->map_nr = 0; |
| @@ -808,7 +945,9 @@ static int g2d_check_reg_offset(struct device *dev, | |||
| 808 | 945 | ||
| 809 | for (i = 0; i < nr; i++) { | 946 | for (i = 0; i < nr; i++) { |
| 810 | struct g2d_buf_info *buf_info = &node->buf_info; | 947 | struct g2d_buf_info *buf_info = &node->buf_info; |
| 948 | struct g2d_buf_desc *buf_desc; | ||
| 811 | enum g2d_reg_type reg_type; | 949 | enum g2d_reg_type reg_type; |
| 950 | unsigned long value; | ||
| 812 | 951 | ||
| 813 | index = cmdlist->last - 2 * (i + 1); | 952 | index = cmdlist->last - 2 * (i + 1); |
| 814 | 953 | ||
| @@ -839,6 +978,50 @@ static int g2d_check_reg_offset(struct device *dev, | |||
| 839 | } else | 978 | } else |
| 840 | buf_info->types[reg_type] = BUF_TYPE_GEM; | 979 | buf_info->types[reg_type] = BUF_TYPE_GEM; |
| 841 | break; | 980 | break; |
| 981 | case G2D_SRC_COLOR_MODE: | ||
| 982 | case G2D_DST_COLOR_MODE: | ||
| 983 | if (for_addr) | ||
| 984 | goto err; | ||
| 985 | |||
| 986 | reg_type = g2d_get_reg_type(reg_offset); | ||
| 987 | if (reg_type == REG_TYPE_NONE) | ||
| 988 | goto err; | ||
| 989 | |||
| 990 | buf_desc = &buf_info->descs[reg_type]; | ||
| 991 | value = cmdlist->data[index + 1]; | ||
| 992 | |||
| 993 | buf_desc->format = value & 0xf; | ||
| 994 | break; | ||
| 995 | case G2D_SRC_LEFT_TOP: | ||
| 996 | case G2D_DST_LEFT_TOP: | ||
| 997 | if (for_addr) | ||
| 998 | goto err; | ||
| 999 | |||
| 1000 | reg_type = g2d_get_reg_type(reg_offset); | ||
| 1001 | if (reg_type == REG_TYPE_NONE) | ||
| 1002 | goto err; | ||
| 1003 | |||
| 1004 | buf_desc = &buf_info->descs[reg_type]; | ||
| 1005 | value = cmdlist->data[index + 1]; | ||
| 1006 | |||
| 1007 | buf_desc->left_x = value & 0x1fff; | ||
| 1008 | buf_desc->top_y = (value & 0x1fff0000) >> 16; | ||
| 1009 | break; | ||
| 1010 | case G2D_SRC_RIGHT_BOTTOM: | ||
| 1011 | case G2D_DST_RIGHT_BOTTOM: | ||
| 1012 | if (for_addr) | ||
| 1013 | goto err; | ||
| 1014 | |||
| 1015 | reg_type = g2d_get_reg_type(reg_offset); | ||
| 1016 | if (reg_type == REG_TYPE_NONE) | ||
| 1017 | goto err; | ||
| 1018 | |||
| 1019 | buf_desc = &buf_info->descs[reg_type]; | ||
| 1020 | value = cmdlist->data[index + 1]; | ||
| 1021 | |||
| 1022 | buf_desc->right_x = value & 0x1fff; | ||
| 1023 | buf_desc->bottom_y = (value & 0x1fff0000) >> 16; | ||
| 1024 | break; | ||
| 842 | default: | 1025 | default: |
| 843 | if (for_addr) | 1026 | if (for_addr) |
| 844 | goto err; | 1027 | goto err; |
