diff options
Diffstat (limited to 'drivers/gpu')
-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; |