perf/core: Replace zero-length array with flexible-array
[linux-2.6-microblaze.git] / drivers / gpu / drm / qxl / qxl_image.c
1 /*
2  * Copyright 2013 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: Dave Airlie
23  *          Alon Levy
24  */
25
26 #include <linux/gfp.h>
27 #include <linux/slab.h>
28
29 #include "qxl_drv.h"
30 #include "qxl_object.h"
31
32 static int
33 qxl_allocate_chunk(struct qxl_device *qdev,
34                    struct qxl_release *release,
35                    struct qxl_drm_image *image,
36                    unsigned int chunk_size)
37 {
38         struct qxl_drm_chunk *chunk;
39         int ret;
40
41         chunk = kmalloc(sizeof(struct qxl_drm_chunk), GFP_KERNEL);
42         if (!chunk)
43                 return -ENOMEM;
44
45         ret = qxl_alloc_bo_reserved(qdev, release, chunk_size, &chunk->bo);
46         if (ret) {
47                 kfree(chunk);
48                 return ret;
49         }
50
51         list_add_tail(&chunk->head, &image->chunk_list);
52         return 0;
53 }
54
55 int
56 qxl_image_alloc_objects(struct qxl_device *qdev,
57                         struct qxl_release *release,
58                         struct qxl_drm_image **image_ptr,
59                         int height, int stride)
60 {
61         struct qxl_drm_image *image;
62         int ret;
63
64         image = kmalloc(sizeof(struct qxl_drm_image), GFP_KERNEL);
65         if (!image)
66                 return -ENOMEM;
67
68         INIT_LIST_HEAD(&image->chunk_list);
69
70         ret = qxl_alloc_bo_reserved(qdev, release, sizeof(struct qxl_image), &image->bo);
71         if (ret) {
72                 kfree(image);
73                 return ret;
74         }
75
76         ret = qxl_allocate_chunk(qdev, release, image, sizeof(struct qxl_data_chunk) + stride * height);
77         if (ret) {
78                 qxl_bo_unref(&image->bo);
79                 kfree(image);
80                 return ret;
81         }
82         *image_ptr = image;
83         return 0;
84 }
85
86 void qxl_image_free_objects(struct qxl_device *qdev, struct qxl_drm_image *dimage)
87 {
88         struct qxl_drm_chunk *chunk, *tmp;
89
90         list_for_each_entry_safe(chunk, tmp, &dimage->chunk_list, head) {
91                 qxl_bo_unref(&chunk->bo);
92                 kfree(chunk);
93         }
94
95         qxl_bo_unref(&dimage->bo);
96         kfree(dimage);
97 }
98
99 static int
100 qxl_image_init_helper(struct qxl_device *qdev,
101                       struct qxl_release *release,
102                       struct qxl_drm_image *dimage,
103                       const uint8_t *data,
104                       int width, int height,
105                       int depth, unsigned int hash,
106                       int stride)
107 {
108         struct qxl_drm_chunk *drv_chunk;
109         struct qxl_image *image;
110         struct qxl_data_chunk *chunk;
111         int i;
112         int chunk_stride;
113         int linesize = width * depth / 8;
114         struct qxl_bo *chunk_bo, *image_bo;
115         void *ptr;
116         /* Chunk */
117         /* FIXME: Check integer overflow */
118         /* TODO: variable number of chunks */
119
120         drv_chunk = list_first_entry(&dimage->chunk_list, struct qxl_drm_chunk, head);
121
122         chunk_bo = drv_chunk->bo;
123         chunk_stride = stride; /* TODO: should use linesize, but it renders
124                                   wrong (check the bitmaps are sent correctly
125                                   first) */
126
127         ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, 0);
128         chunk = ptr;
129         chunk->data_size = height * chunk_stride;
130         chunk->prev_chunk = 0;
131         chunk->next_chunk = 0;
132         qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
133
134         {
135                 void *k_data, *i_data;
136                 int remain;
137                 int page;
138                 int size;
139
140                 if (stride == linesize && chunk_stride == stride) {
141                         remain = linesize * height;
142                         page = 0;
143                         i_data = (void *)data;
144
145                         while (remain > 0) {
146                                 ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page << PAGE_SHIFT);
147
148                                 if (page == 0) {
149                                         chunk = ptr;
150                                         k_data = chunk->data;
151                                         size = PAGE_SIZE - offsetof(struct qxl_data_chunk, data);
152                                 } else {
153                                         k_data = ptr;
154                                         size = PAGE_SIZE;
155                                 }
156                                 size = min(size, remain);
157
158                                 memcpy(k_data, i_data, size);
159
160                                 qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
161                                 i_data += size;
162                                 remain -= size;
163                                 page++;
164                         }
165                 } else {
166                         unsigned int page_base, page_offset, out_offset;
167
168                         for (i = 0 ; i < height ; ++i) {
169                                 i_data = (void *)data + i * stride;
170                                 remain = linesize;
171                                 out_offset = offsetof(struct qxl_data_chunk, data) + i * chunk_stride;
172
173                                 while (remain > 0) {
174                                         page_base = out_offset & PAGE_MASK;
175                                         page_offset = offset_in_page(out_offset);
176                                         size = min((int)(PAGE_SIZE - page_offset), remain);
177
178                                         ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page_base);
179                                         k_data = ptr + page_offset;
180                                         memcpy(k_data, i_data, size);
181                                         qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
182                                         remain -= size;
183                                         i_data += size;
184                                         out_offset += size;
185                                 }
186                         }
187                 }
188         }
189         qxl_bo_kunmap(chunk_bo);
190
191         image_bo = dimage->bo;
192         ptr = qxl_bo_kmap_atomic_page(qdev, image_bo, 0);
193         image = ptr;
194
195         image->descriptor.id = 0;
196         image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
197
198         image->descriptor.flags = 0;
199         image->descriptor.width = width;
200         image->descriptor.height = height;
201
202         switch (depth) {
203         case 1:
204                 /* TODO: BE? check by arch? */
205                 image->u.bitmap.format = SPICE_BITMAP_FMT_1BIT_BE;
206                 break;
207         case 24:
208                 image->u.bitmap.format = SPICE_BITMAP_FMT_24BIT;
209                 break;
210         case 32:
211                 image->u.bitmap.format = SPICE_BITMAP_FMT_32BIT;
212                 break;
213         default:
214                 DRM_ERROR("unsupported image bit depth\n");
215                 return -EINVAL; /* TODO: cleanup */
216         }
217         image->u.bitmap.flags = QXL_BITMAP_TOP_DOWN;
218         image->u.bitmap.x = width;
219         image->u.bitmap.y = height;
220         image->u.bitmap.stride = chunk_stride;
221         image->u.bitmap.palette = 0;
222         image->u.bitmap.data = qxl_bo_physical_address(qdev, chunk_bo, 0);
223
224         qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr);
225
226         return 0;
227 }
228
229 int qxl_image_init(struct qxl_device *qdev,
230                      struct qxl_release *release,
231                      struct qxl_drm_image *dimage,
232                      const uint8_t *data,
233                      int x, int y, int width, int height,
234                      int depth, int stride)
235 {
236         data += y * stride + x * (depth / 8);
237         return qxl_image_init_helper(qdev, release, dimage, data,
238                                        width, height, depth, 0, stride);
239 }