Commit | Line | Data |
---|---|---|
f64122c1 DA |
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 | |
8002db63 DA |
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, | |
f64122c1 | 57 | struct qxl_release *release, |
8002db63 DA |
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) | |
f64122c1 | 87 | { |
8002db63 DA |
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; | |
f64122c1 DA |
109 | struct qxl_image *image; |
110 | struct qxl_data_chunk *chunk; | |
111 | int i; | |
112 | int chunk_stride; | |
113 | int linesize = width * depth / 8; | |
8002db63 | 114 | struct qxl_bo *chunk_bo, *image_bo; |
f64122c1 DA |
115 | void *ptr; |
116 | /* Chunk */ | |
117 | /* FIXME: Check integer overflow */ | |
118 | /* TODO: variable number of chunks */ | |
8002db63 DA |
119 | |
120 | drv_chunk = list_first_entry(&dimage->chunk_list, struct qxl_drm_chunk, head); | |
121 | ||
122 | chunk_bo = drv_chunk->bo; | |
f64122c1 DA |
123 | chunk_stride = stride; /* TODO: should use linesize, but it renders |
124 | wrong (check the bitmaps are sent correctly | |
125 | first) */ | |
8002db63 | 126 | |
f64122c1 DA |
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; | |
408799eb | 139 | |
f64122c1 DA |
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 { | |
1b000494 | 166 | unsigned int page_base, page_offset, out_offset; |
408799eb | 167 | |
f64122c1 DA |
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); | |
f64122c1 DA |
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 | } | |
f7ed28e1 | 189 | qxl_bo_vunmap_locked(chunk_bo); |
f64122c1 | 190 | |
8002db63 DA |
191 | image_bo = dimage->bo; |
192 | ptr = qxl_bo_kmap_atomic_page(qdev, image_bo, 0); | |
f64122c1 DA |
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"); | |
5b5703db VA |
215 | qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr); |
216 | return -EINVAL; | |
f64122c1 DA |
217 | } |
218 | image->u.bitmap.flags = QXL_BITMAP_TOP_DOWN; | |
219 | image->u.bitmap.x = width; | |
220 | image->u.bitmap.y = height; | |
221 | image->u.bitmap.stride = chunk_stride; | |
222 | image->u.bitmap.palette = 0; | |
223 | image->u.bitmap.data = qxl_bo_physical_address(qdev, chunk_bo, 0); | |
f64122c1 | 224 | |
8002db63 | 225 | qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr); |
f64122c1 DA |
226 | |
227 | return 0; | |
228 | } | |
229 | ||
8002db63 | 230 | int qxl_image_init(struct qxl_device *qdev, |
f64122c1 | 231 | struct qxl_release *release, |
8002db63 | 232 | struct qxl_drm_image *dimage, |
f64122c1 DA |
233 | const uint8_t *data, |
234 | int x, int y, int width, int height, | |
235 | int depth, int stride) | |
236 | { | |
237 | data += y * stride + x * (depth / 8); | |
8002db63 | 238 | return qxl_image_init_helper(qdev, release, dimage, data, |
f64122c1 DA |
239 | width, height, depth, 0, stride); |
240 | } |