diff options
author | Thomas Hellstrom <thellstrom@vmware.com> | 2012-11-20 07:19:36 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2012-11-20 16:47:08 -0500 |
commit | 543831cfc976669b8da963b3e94933e21e051846 (patch) | |
tree | d48ddcdaa3909f9295c24922ee953f44bbdb85de /drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | |
parent | c0951b797e7d0f2c6b0df2c0e18185c72d0cf1a1 (diff) |
drm/vmwgfx: Break out surface and context management to separate files
Add a resource-private header for common resource definitions
Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: Brian Paul <brianp@vmware.com>
Reviewed-by: Dmitry Torokhov <dtor@vmware.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_surface.c')
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 968 |
1 files changed, 968 insertions, 0 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c new file mode 100644 index 000000000000..c4a7bcdabd48 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | |||
@@ -0,0 +1,968 @@ | |||
1 | /************************************************************************** | ||
2 | * | ||
3 | * Copyright © 2009-2012 VMware, Inc., Palo Alto, CA., USA | ||
4 | * All Rights Reserved. | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
7 | * copy of this software and associated documentation files (the | ||
8 | * "Software"), to deal in the Software without restriction, including | ||
9 | * without limitation the rights to use, copy, modify, merge, publish, | ||
10 | * distribute, sub license, and/or sell copies of the Software, and to | ||
11 | * permit persons to whom the Software is furnished to do so, subject to | ||
12 | * the following conditions: | ||
13 | * | ||
14 | * The above copyright notice and this permission notice (including the | ||
15 | * next paragraph) shall be included in all copies or substantial portions | ||
16 | * of the Software. | ||
17 | * | ||
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
21 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, | ||
22 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
23 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | ||
24 | * USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
25 | * | ||
26 | **************************************************************************/ | ||
27 | |||
28 | #include "vmwgfx_drv.h" | ||
29 | #include "vmwgfx_resource_priv.h" | ||
30 | #include <ttm/ttm_placement.h> | ||
31 | |||
32 | /** | ||
33 | * struct vmw_user_surface - User-space visible surface resource | ||
34 | * | ||
35 | * @base: The TTM base object handling user-space visibility. | ||
36 | * @srf: The surface metadata. | ||
37 | * @size: TTM accounting size for the surface. | ||
38 | */ | ||
39 | struct vmw_user_surface { | ||
40 | struct ttm_base_object base; | ||
41 | struct vmw_surface srf; | ||
42 | uint32_t size; | ||
43 | uint32_t backup_handle; | ||
44 | }; | ||
45 | |||
46 | /** | ||
47 | * struct vmw_surface_offset - Backing store mip level offset info | ||
48 | * | ||
49 | * @face: Surface face. | ||
50 | * @mip: Mip level. | ||
51 | * @bo_offset: Offset into backing store of this mip level. | ||
52 | * | ||
53 | */ | ||
54 | struct vmw_surface_offset { | ||
55 | uint32_t face; | ||
56 | uint32_t mip; | ||
57 | uint32_t bo_offset; | ||
58 | }; | ||
59 | |||
60 | static void vmw_user_surface_free(struct vmw_resource *res); | ||
61 | static struct vmw_resource * | ||
62 | vmw_user_surface_base_to_res(struct ttm_base_object *base); | ||
63 | static int vmw_legacy_srf_bind(struct vmw_resource *res, | ||
64 | struct ttm_validate_buffer *val_buf); | ||
65 | static int vmw_legacy_srf_unbind(struct vmw_resource *res, | ||
66 | bool readback, | ||
67 | struct ttm_validate_buffer *val_buf); | ||
68 | static int vmw_legacy_srf_create(struct vmw_resource *res); | ||
69 | static int vmw_legacy_srf_destroy(struct vmw_resource *res); | ||
70 | |||
71 | static const struct vmw_user_resource_conv user_surface_conv = { | ||
72 | .object_type = VMW_RES_SURFACE, | ||
73 | .base_obj_to_res = vmw_user_surface_base_to_res, | ||
74 | .res_free = vmw_user_surface_free | ||
75 | }; | ||
76 | |||
77 | const struct vmw_user_resource_conv *user_surface_converter = | ||
78 | &user_surface_conv; | ||
79 | |||
80 | |||
81 | static uint64_t vmw_user_surface_size; | ||
82 | |||
83 | static const struct vmw_res_func vmw_legacy_surface_func = { | ||
84 | .res_type = vmw_res_surface, | ||
85 | .needs_backup = false, | ||
86 | .may_evict = true, | ||
87 | .type_name = "legacy surfaces", | ||
88 | .backup_placement = &vmw_srf_placement, | ||
89 | .create = &vmw_legacy_srf_create, | ||
90 | .destroy = &vmw_legacy_srf_destroy, | ||
91 | .bind = &vmw_legacy_srf_bind, | ||
92 | .unbind = &vmw_legacy_srf_unbind | ||
93 | }; | ||
94 | |||
95 | /** | ||
96 | * struct vmw_bpp - Bits per pixel info for surface storage size computation. | ||
97 | * | ||
98 | * @bpp: Bits per pixel. | ||
99 | * @s_bpp: Stride bits per pixel. See definition below. | ||
100 | * | ||
101 | */ | ||
102 | struct vmw_bpp { | ||
103 | uint8_t bpp; | ||
104 | uint8_t s_bpp; | ||
105 | }; | ||
106 | |||
107 | /* | ||
108 | * Size table for the supported SVGA3D surface formats. It consists of | ||
109 | * two values. The bpp value and the s_bpp value which is short for | ||
110 | * "stride bits per pixel" The values are given in such a way that the | ||
111 | * minimum stride for the image is calculated using | ||
112 | * | ||
113 | * min_stride = w*s_bpp | ||
114 | * | ||
115 | * and the total memory requirement for the image is | ||
116 | * | ||
117 | * h*min_stride*bpp/s_bpp | ||
118 | * | ||
119 | */ | ||
120 | static const struct vmw_bpp vmw_sf_bpp[] = { | ||
121 | [SVGA3D_FORMAT_INVALID] = {0, 0}, | ||
122 | [SVGA3D_X8R8G8B8] = {32, 32}, | ||
123 | [SVGA3D_A8R8G8B8] = {32, 32}, | ||
124 | [SVGA3D_R5G6B5] = {16, 16}, | ||
125 | [SVGA3D_X1R5G5B5] = {16, 16}, | ||
126 | [SVGA3D_A1R5G5B5] = {16, 16}, | ||
127 | [SVGA3D_A4R4G4B4] = {16, 16}, | ||
128 | [SVGA3D_Z_D32] = {32, 32}, | ||
129 | [SVGA3D_Z_D16] = {16, 16}, | ||
130 | [SVGA3D_Z_D24S8] = {32, 32}, | ||
131 | [SVGA3D_Z_D15S1] = {16, 16}, | ||
132 | [SVGA3D_LUMINANCE8] = {8, 8}, | ||
133 | [SVGA3D_LUMINANCE4_ALPHA4] = {8, 8}, | ||
134 | [SVGA3D_LUMINANCE16] = {16, 16}, | ||
135 | [SVGA3D_LUMINANCE8_ALPHA8] = {16, 16}, | ||
136 | [SVGA3D_DXT1] = {4, 16}, | ||
137 | [SVGA3D_DXT2] = {8, 32}, | ||
138 | [SVGA3D_DXT3] = {8, 32}, | ||
139 | [SVGA3D_DXT4] = {8, 32}, | ||
140 | [SVGA3D_DXT5] = {8, 32}, | ||
141 | [SVGA3D_BUMPU8V8] = {16, 16}, | ||
142 | [SVGA3D_BUMPL6V5U5] = {16, 16}, | ||
143 | [SVGA3D_BUMPX8L8V8U8] = {32, 32}, | ||
144 | [SVGA3D_ARGB_S10E5] = {16, 16}, | ||
145 | [SVGA3D_ARGB_S23E8] = {32, 32}, | ||
146 | [SVGA3D_A2R10G10B10] = {32, 32}, | ||
147 | [SVGA3D_V8U8] = {16, 16}, | ||
148 | [SVGA3D_Q8W8V8U8] = {32, 32}, | ||
149 | [SVGA3D_CxV8U8] = {16, 16}, | ||
150 | [SVGA3D_X8L8V8U8] = {32, 32}, | ||
151 | [SVGA3D_A2W10V10U10] = {32, 32}, | ||
152 | [SVGA3D_ALPHA8] = {8, 8}, | ||
153 | [SVGA3D_R_S10E5] = {16, 16}, | ||
154 | [SVGA3D_R_S23E8] = {32, 32}, | ||
155 | [SVGA3D_RG_S10E5] = {16, 16}, | ||
156 | [SVGA3D_RG_S23E8] = {32, 32}, | ||
157 | [SVGA3D_BUFFER] = {8, 8}, | ||
158 | [SVGA3D_Z_D24X8] = {32, 32}, | ||
159 | [SVGA3D_V16U16] = {32, 32}, | ||
160 | [SVGA3D_G16R16] = {32, 32}, | ||
161 | [SVGA3D_A16B16G16R16] = {64, 64}, | ||
162 | [SVGA3D_UYVY] = {12, 12}, | ||
163 | [SVGA3D_YUY2] = {12, 12}, | ||
164 | [SVGA3D_NV12] = {12, 8}, | ||
165 | [SVGA3D_AYUV] = {32, 32}, | ||
166 | [SVGA3D_BC4_UNORM] = {4, 16}, | ||
167 | [SVGA3D_BC5_UNORM] = {8, 32}, | ||
168 | [SVGA3D_Z_DF16] = {16, 16}, | ||
169 | [SVGA3D_Z_DF24] = {24, 24}, | ||
170 | [SVGA3D_Z_D24S8_INT] = {32, 32} | ||
171 | }; | ||
172 | |||
173 | |||
174 | /** | ||
175 | * struct vmw_surface_dma - SVGA3D DMA command | ||
176 | */ | ||
177 | struct vmw_surface_dma { | ||
178 | SVGA3dCmdHeader header; | ||
179 | SVGA3dCmdSurfaceDMA body; | ||
180 | SVGA3dCopyBox cb; | ||
181 | SVGA3dCmdSurfaceDMASuffix suffix; | ||
182 | }; | ||
183 | |||
184 | /** | ||
185 | * struct vmw_surface_define - SVGA3D Surface Define command | ||
186 | */ | ||
187 | struct vmw_surface_define { | ||
188 | SVGA3dCmdHeader header; | ||
189 | SVGA3dCmdDefineSurface body; | ||
190 | }; | ||
191 | |||
192 | /** | ||
193 | * struct vmw_surface_destroy - SVGA3D Surface Destroy command | ||
194 | */ | ||
195 | struct vmw_surface_destroy { | ||
196 | SVGA3dCmdHeader header; | ||
197 | SVGA3dCmdDestroySurface body; | ||
198 | }; | ||
199 | |||
200 | |||
201 | /** | ||
202 | * vmw_surface_dma_size - Compute fifo size for a dma command. | ||
203 | * | ||
204 | * @srf: Pointer to a struct vmw_surface | ||
205 | * | ||
206 | * Computes the required size for a surface dma command for backup or | ||
207 | * restoration of the surface represented by @srf. | ||
208 | */ | ||
209 | static inline uint32_t vmw_surface_dma_size(const struct vmw_surface *srf) | ||
210 | { | ||
211 | return srf->num_sizes * sizeof(struct vmw_surface_dma); | ||
212 | } | ||
213 | |||
214 | |||
215 | /** | ||
216 | * vmw_surface_define_size - Compute fifo size for a surface define command. | ||
217 | * | ||
218 | * @srf: Pointer to a struct vmw_surface | ||
219 | * | ||
220 | * Computes the required size for a surface define command for the definition | ||
221 | * of the surface represented by @srf. | ||
222 | */ | ||
223 | static inline uint32_t vmw_surface_define_size(const struct vmw_surface *srf) | ||
224 | { | ||
225 | return sizeof(struct vmw_surface_define) + srf->num_sizes * | ||
226 | sizeof(SVGA3dSize); | ||
227 | } | ||
228 | |||
229 | |||
230 | /** | ||
231 | * vmw_surface_destroy_size - Compute fifo size for a surface destroy command. | ||
232 | * | ||
233 | * Computes the required size for a surface destroy command for the destruction | ||
234 | * of a hw surface. | ||
235 | */ | ||
236 | static inline uint32_t vmw_surface_destroy_size(void) | ||
237 | { | ||
238 | return sizeof(struct vmw_surface_destroy); | ||
239 | } | ||
240 | |||
241 | /** | ||
242 | * vmw_surface_destroy_encode - Encode a surface_destroy command. | ||
243 | * | ||
244 | * @id: The surface id | ||
245 | * @cmd_space: Pointer to memory area in which the commands should be encoded. | ||
246 | */ | ||
247 | static void vmw_surface_destroy_encode(uint32_t id, | ||
248 | void *cmd_space) | ||
249 | { | ||
250 | struct vmw_surface_destroy *cmd = (struct vmw_surface_destroy *) | ||
251 | cmd_space; | ||
252 | |||
253 | cmd->header.id = SVGA_3D_CMD_SURFACE_DESTROY; | ||
254 | cmd->header.size = sizeof(cmd->body); | ||
255 | cmd->body.sid = id; | ||
256 | } | ||
257 | |||
258 | /** | ||
259 | * vmw_surface_define_encode - Encode a surface_define command. | ||
260 | * | ||
261 | * @srf: Pointer to a struct vmw_surface object. | ||
262 | * @cmd_space: Pointer to memory area in which the commands should be encoded. | ||
263 | */ | ||
264 | static void vmw_surface_define_encode(const struct vmw_surface *srf, | ||
265 | void *cmd_space) | ||
266 | { | ||
267 | struct vmw_surface_define *cmd = (struct vmw_surface_define *) | ||
268 | cmd_space; | ||
269 | struct drm_vmw_size *src_size; | ||
270 | SVGA3dSize *cmd_size; | ||
271 | uint32_t cmd_len; | ||
272 | int i; | ||
273 | |||
274 | cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize); | ||
275 | |||
276 | cmd->header.id = SVGA_3D_CMD_SURFACE_DEFINE; | ||
277 | cmd->header.size = cmd_len; | ||
278 | cmd->body.sid = srf->res.id; | ||
279 | cmd->body.surfaceFlags = srf->flags; | ||
280 | cmd->body.format = cpu_to_le32(srf->format); | ||
281 | for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) | ||
282 | cmd->body.face[i].numMipLevels = srf->mip_levels[i]; | ||
283 | |||
284 | cmd += 1; | ||
285 | cmd_size = (SVGA3dSize *) cmd; | ||
286 | src_size = srf->sizes; | ||
287 | |||
288 | for (i = 0; i < srf->num_sizes; ++i, cmd_size++, src_size++) { | ||
289 | cmd_size->width = src_size->width; | ||
290 | cmd_size->height = src_size->height; | ||
291 | cmd_size->depth = src_size->depth; | ||
292 | } | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * vmw_surface_dma_encode - Encode a surface_dma command. | ||
297 | * | ||
298 | * @srf: Pointer to a struct vmw_surface object. | ||
299 | * @cmd_space: Pointer to memory area in which the commands should be encoded. | ||
300 | * @ptr: Pointer to an SVGAGuestPtr indicating where the surface contents | ||
301 | * should be placed or read from. | ||
302 | * @to_surface: Boolean whether to DMA to the surface or from the surface. | ||
303 | */ | ||
304 | static void vmw_surface_dma_encode(struct vmw_surface *srf, | ||
305 | void *cmd_space, | ||
306 | const SVGAGuestPtr *ptr, | ||
307 | bool to_surface) | ||
308 | { | ||
309 | uint32_t i; | ||
310 | uint32_t bpp = vmw_sf_bpp[srf->format].bpp; | ||
311 | uint32_t stride_bpp = vmw_sf_bpp[srf->format].s_bpp; | ||
312 | struct vmw_surface_dma *cmd = (struct vmw_surface_dma *)cmd_space; | ||
313 | |||
314 | for (i = 0; i < srf->num_sizes; ++i) { | ||
315 | SVGA3dCmdHeader *header = &cmd->header; | ||
316 | SVGA3dCmdSurfaceDMA *body = &cmd->body; | ||
317 | SVGA3dCopyBox *cb = &cmd->cb; | ||
318 | SVGA3dCmdSurfaceDMASuffix *suffix = &cmd->suffix; | ||
319 | const struct vmw_surface_offset *cur_offset = &srf->offsets[i]; | ||
320 | const struct drm_vmw_size *cur_size = &srf->sizes[i]; | ||
321 | |||
322 | header->id = SVGA_3D_CMD_SURFACE_DMA; | ||
323 | header->size = sizeof(*body) + sizeof(*cb) + sizeof(*suffix); | ||
324 | |||
325 | body->guest.ptr = *ptr; | ||
326 | body->guest.ptr.offset += cur_offset->bo_offset; | ||
327 | body->guest.pitch = (cur_size->width * stride_bpp + 7) >> 3; | ||
328 | body->host.sid = srf->res.id; | ||
329 | body->host.face = cur_offset->face; | ||
330 | body->host.mipmap = cur_offset->mip; | ||
331 | body->transfer = ((to_surface) ? SVGA3D_WRITE_HOST_VRAM : | ||
332 | SVGA3D_READ_HOST_VRAM); | ||
333 | cb->x = 0; | ||
334 | cb->y = 0; | ||
335 | cb->z = 0; | ||
336 | cb->srcx = 0; | ||
337 | cb->srcy = 0; | ||
338 | cb->srcz = 0; | ||
339 | cb->w = cur_size->width; | ||
340 | cb->h = cur_size->height; | ||
341 | cb->d = cur_size->depth; | ||
342 | |||
343 | suffix->suffixSize = sizeof(*suffix); | ||
344 | suffix->maximumOffset = body->guest.pitch*cur_size->height* | ||
345 | cur_size->depth*bpp / stride_bpp; | ||
346 | suffix->flags.discard = 0; | ||
347 | suffix->flags.unsynchronized = 0; | ||
348 | suffix->flags.reserved = 0; | ||
349 | ++cmd; | ||
350 | } | ||
351 | }; | ||
352 | |||
353 | |||
354 | /** | ||
355 | * vmw_hw_surface_destroy - destroy a Device surface | ||
356 | * | ||
357 | * @res: Pointer to a struct vmw_resource embedded in a struct | ||
358 | * vmw_surface. | ||
359 | * | ||
360 | * Destroys a the device surface associated with a struct vmw_surface if | ||
361 | * any, and adjusts accounting and resource count accordingly. | ||
362 | */ | ||
363 | static void vmw_hw_surface_destroy(struct vmw_resource *res) | ||
364 | { | ||
365 | |||
366 | struct vmw_private *dev_priv = res->dev_priv; | ||
367 | struct vmw_surface *srf; | ||
368 | void *cmd; | ||
369 | |||
370 | if (res->id != -1) { | ||
371 | |||
372 | cmd = vmw_fifo_reserve(dev_priv, vmw_surface_destroy_size()); | ||
373 | if (unlikely(cmd == NULL)) { | ||
374 | DRM_ERROR("Failed reserving FIFO space for surface " | ||
375 | "destruction.\n"); | ||
376 | return; | ||
377 | } | ||
378 | |||
379 | vmw_surface_destroy_encode(res->id, cmd); | ||
380 | vmw_fifo_commit(dev_priv, vmw_surface_destroy_size()); | ||
381 | |||
382 | /* | ||
383 | * used_memory_size_atomic, or separate lock | ||
384 | * to avoid taking dev_priv::cmdbuf_mutex in | ||
385 | * the destroy path. | ||
386 | */ | ||
387 | |||
388 | mutex_lock(&dev_priv->cmdbuf_mutex); | ||
389 | srf = vmw_res_to_srf(res); | ||
390 | dev_priv->used_memory_size -= res->backup_size; | ||
391 | mutex_unlock(&dev_priv->cmdbuf_mutex); | ||
392 | } | ||
393 | vmw_3d_resource_dec(dev_priv, false); | ||
394 | } | ||
395 | |||
396 | /** | ||
397 | * vmw_legacy_srf_create - Create a device surface as part of the | ||
398 | * resource validation process. | ||
399 | * | ||
400 | * @res: Pointer to a struct vmw_surface. | ||
401 | * | ||
402 | * If the surface doesn't have a hw id. | ||
403 | * | ||
404 | * Returns -EBUSY if there wasn't sufficient device resources to | ||
405 | * complete the validation. Retry after freeing up resources. | ||
406 | * | ||
407 | * May return other errors if the kernel is out of guest resources. | ||
408 | */ | ||
409 | static int vmw_legacy_srf_create(struct vmw_resource *res) | ||
410 | { | ||
411 | struct vmw_private *dev_priv = res->dev_priv; | ||
412 | struct vmw_surface *srf; | ||
413 | uint32_t submit_size; | ||
414 | uint8_t *cmd; | ||
415 | int ret; | ||
416 | |||
417 | if (likely(res->id != -1)) | ||
418 | return 0; | ||
419 | |||
420 | srf = vmw_res_to_srf(res); | ||
421 | if (unlikely(dev_priv->used_memory_size + res->backup_size >= | ||
422 | dev_priv->memory_size)) | ||
423 | return -EBUSY; | ||
424 | |||
425 | /* | ||
426 | * Alloc id for the resource. | ||
427 | */ | ||
428 | |||
429 | ret = vmw_resource_alloc_id(res); | ||
430 | if (unlikely(ret != 0)) { | ||
431 | DRM_ERROR("Failed to allocate a surface id.\n"); | ||
432 | goto out_no_id; | ||
433 | } | ||
434 | |||
435 | if (unlikely(res->id >= SVGA3D_MAX_SURFACE_IDS)) { | ||
436 | ret = -EBUSY; | ||
437 | goto out_no_fifo; | ||
438 | } | ||
439 | |||
440 | /* | ||
441 | * Encode surface define- commands. | ||
442 | */ | ||
443 | |||
444 | submit_size = vmw_surface_define_size(srf); | ||
445 | cmd = vmw_fifo_reserve(dev_priv, submit_size); | ||
446 | if (unlikely(cmd == NULL)) { | ||
447 | DRM_ERROR("Failed reserving FIFO space for surface " | ||
448 | "creation.\n"); | ||
449 | ret = -ENOMEM; | ||
450 | goto out_no_fifo; | ||
451 | } | ||
452 | |||
453 | vmw_surface_define_encode(srf, cmd); | ||
454 | vmw_fifo_commit(dev_priv, submit_size); | ||
455 | /* | ||
456 | * Surface memory usage accounting. | ||
457 | */ | ||
458 | |||
459 | dev_priv->used_memory_size += res->backup_size; | ||
460 | return 0; | ||
461 | |||
462 | out_no_fifo: | ||
463 | vmw_resource_release_id(res); | ||
464 | out_no_id: | ||
465 | return ret; | ||
466 | } | ||
467 | |||
468 | /** | ||
469 | * vmw_legacy_srf_dma - Copy backup data to or from a legacy surface. | ||
470 | * | ||
471 | * @res: Pointer to a struct vmw_res embedded in a struct | ||
472 | * vmw_surface. | ||
473 | * @val_buf: Pointer to a struct ttm_validate_buffer containing | ||
474 | * information about the backup buffer. | ||
475 | * @bind: Boolean wether to DMA to the surface. | ||
476 | * | ||
477 | * Transfer backup data to or from a legacy surface as part of the | ||
478 | * validation process. | ||
479 | * May return other errors if the kernel is out of guest resources. | ||
480 | * The backup buffer will be fenced or idle upon successful completion, | ||
481 | * and if the surface needs persistent backup storage, the backup buffer | ||
482 | * will also be returned reserved iff @bind is true. | ||
483 | */ | ||
484 | static int vmw_legacy_srf_dma(struct vmw_resource *res, | ||
485 | struct ttm_validate_buffer *val_buf, | ||
486 | bool bind) | ||
487 | { | ||
488 | SVGAGuestPtr ptr; | ||
489 | struct vmw_fence_obj *fence; | ||
490 | uint32_t submit_size; | ||
491 | struct vmw_surface *srf = vmw_res_to_srf(res); | ||
492 | uint8_t *cmd; | ||
493 | struct vmw_private *dev_priv = res->dev_priv; | ||
494 | |||
495 | BUG_ON(val_buf->bo == NULL); | ||
496 | |||
497 | submit_size = vmw_surface_dma_size(srf); | ||
498 | cmd = vmw_fifo_reserve(dev_priv, submit_size); | ||
499 | if (unlikely(cmd == NULL)) { | ||
500 | DRM_ERROR("Failed reserving FIFO space for surface " | ||
501 | "DMA.\n"); | ||
502 | return -ENOMEM; | ||
503 | } | ||
504 | vmw_bo_get_guest_ptr(val_buf->bo, &ptr); | ||
505 | vmw_surface_dma_encode(srf, cmd, &ptr, bind); | ||
506 | |||
507 | vmw_fifo_commit(dev_priv, submit_size); | ||
508 | |||
509 | /* | ||
510 | * Create a fence object and fence the backup buffer. | ||
511 | */ | ||
512 | |||
513 | (void) vmw_execbuf_fence_commands(NULL, dev_priv, | ||
514 | &fence, NULL); | ||
515 | |||
516 | vmw_fence_single_bo(val_buf->bo, fence); | ||
517 | |||
518 | if (likely(fence != NULL)) | ||
519 | vmw_fence_obj_unreference(&fence); | ||
520 | |||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | /** | ||
525 | * vmw_legacy_srf_bind - Perform a legacy surface bind as part of the | ||
526 | * surface validation process. | ||
527 | * | ||
528 | * @res: Pointer to a struct vmw_res embedded in a struct | ||
529 | * vmw_surface. | ||
530 | * @val_buf: Pointer to a struct ttm_validate_buffer containing | ||
531 | * information about the backup buffer. | ||
532 | * | ||
533 | * This function will copy backup data to the surface if the | ||
534 | * backup buffer is dirty. | ||
535 | */ | ||
536 | static int vmw_legacy_srf_bind(struct vmw_resource *res, | ||
537 | struct ttm_validate_buffer *val_buf) | ||
538 | { | ||
539 | if (!res->backup_dirty) | ||
540 | return 0; | ||
541 | |||
542 | return vmw_legacy_srf_dma(res, val_buf, true); | ||
543 | } | ||
544 | |||
545 | |||
546 | /** | ||
547 | * vmw_legacy_srf_unbind - Perform a legacy surface unbind as part of the | ||
548 | * surface eviction process. | ||
549 | * | ||
550 | * @res: Pointer to a struct vmw_res embedded in a struct | ||
551 | * vmw_surface. | ||
552 | * @val_buf: Pointer to a struct ttm_validate_buffer containing | ||
553 | * information about the backup buffer. | ||
554 | * | ||
555 | * This function will copy backup data from the surface. | ||
556 | */ | ||
557 | static int vmw_legacy_srf_unbind(struct vmw_resource *res, | ||
558 | bool readback, | ||
559 | struct ttm_validate_buffer *val_buf) | ||
560 | { | ||
561 | if (unlikely(readback)) | ||
562 | return vmw_legacy_srf_dma(res, val_buf, false); | ||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | /** | ||
567 | * vmw_legacy_srf_destroy - Destroy a device surface as part of a | ||
568 | * resource eviction process. | ||
569 | * | ||
570 | * @res: Pointer to a struct vmw_res embedded in a struct | ||
571 | * vmw_surface. | ||
572 | */ | ||
573 | static int vmw_legacy_srf_destroy(struct vmw_resource *res) | ||
574 | { | ||
575 | struct vmw_private *dev_priv = res->dev_priv; | ||
576 | uint32_t submit_size; | ||
577 | uint8_t *cmd; | ||
578 | |||
579 | BUG_ON(res->id == -1); | ||
580 | |||
581 | /* | ||
582 | * Encode the dma- and surface destroy commands. | ||
583 | */ | ||
584 | |||
585 | submit_size = vmw_surface_destroy_size(); | ||
586 | cmd = vmw_fifo_reserve(dev_priv, submit_size); | ||
587 | if (unlikely(cmd == NULL)) { | ||
588 | DRM_ERROR("Failed reserving FIFO space for surface " | ||
589 | "eviction.\n"); | ||
590 | return -ENOMEM; | ||
591 | } | ||
592 | |||
593 | vmw_surface_destroy_encode(res->id, cmd); | ||
594 | vmw_fifo_commit(dev_priv, submit_size); | ||
595 | |||
596 | /* | ||
597 | * Surface memory usage accounting. | ||
598 | */ | ||
599 | |||
600 | dev_priv->used_memory_size -= res->backup_size; | ||
601 | |||
602 | /* | ||
603 | * Release the surface ID. | ||
604 | */ | ||
605 | |||
606 | vmw_resource_release_id(res); | ||
607 | |||
608 | return 0; | ||
609 | } | ||
610 | |||
611 | |||
612 | /** | ||
613 | * vmw_surface_init - initialize a struct vmw_surface | ||
614 | * | ||
615 | * @dev_priv: Pointer to a device private struct. | ||
616 | * @srf: Pointer to the struct vmw_surface to initialize. | ||
617 | * @res_free: Pointer to a resource destructor used to free | ||
618 | * the object. | ||
619 | */ | ||
620 | static int vmw_surface_init(struct vmw_private *dev_priv, | ||
621 | struct vmw_surface *srf, | ||
622 | void (*res_free) (struct vmw_resource *res)) | ||
623 | { | ||
624 | int ret; | ||
625 | struct vmw_resource *res = &srf->res; | ||
626 | |||
627 | BUG_ON(res_free == NULL); | ||
628 | (void) vmw_3d_resource_inc(dev_priv, false); | ||
629 | ret = vmw_resource_init(dev_priv, res, true, res_free, | ||
630 | &vmw_legacy_surface_func); | ||
631 | |||
632 | if (unlikely(ret != 0)) { | ||
633 | vmw_3d_resource_dec(dev_priv, false); | ||
634 | res_free(res); | ||
635 | return ret; | ||
636 | } | ||
637 | |||
638 | /* | ||
639 | * The surface won't be visible to hardware until a | ||
640 | * surface validate. | ||
641 | */ | ||
642 | |||
643 | vmw_resource_activate(res, vmw_hw_surface_destroy); | ||
644 | return ret; | ||
645 | } | ||
646 | |||
647 | /** | ||
648 | * vmw_user_surface_base_to_res - TTM base object to resource converter for | ||
649 | * user visible surfaces | ||
650 | * | ||
651 | * @base: Pointer to a TTM base object | ||
652 | * | ||
653 | * Returns the struct vmw_resource embedded in a struct vmw_surface | ||
654 | * for the user-visible object identified by the TTM base object @base. | ||
655 | */ | ||
656 | static struct vmw_resource * | ||
657 | vmw_user_surface_base_to_res(struct ttm_base_object *base) | ||
658 | { | ||
659 | return &(container_of(base, struct vmw_user_surface, base)->srf.res); | ||
660 | } | ||
661 | |||
662 | /** | ||
663 | * vmw_user_surface_free - User visible surface resource destructor | ||
664 | * | ||
665 | * @res: A struct vmw_resource embedded in a struct vmw_surface. | ||
666 | */ | ||
667 | static void vmw_user_surface_free(struct vmw_resource *res) | ||
668 | { | ||
669 | struct vmw_surface *srf = vmw_res_to_srf(res); | ||
670 | struct vmw_user_surface *user_srf = | ||
671 | container_of(srf, struct vmw_user_surface, srf); | ||
672 | struct vmw_private *dev_priv = srf->res.dev_priv; | ||
673 | uint32_t size = user_srf->size; | ||
674 | |||
675 | kfree(srf->offsets); | ||
676 | kfree(srf->sizes); | ||
677 | kfree(srf->snooper.image); | ||
678 | ttm_base_object_kfree(user_srf, base); | ||
679 | ttm_mem_global_free(vmw_mem_glob(dev_priv), size); | ||
680 | } | ||
681 | |||
682 | /** | ||
683 | * vmw_user_surface_free - User visible surface TTM base object destructor | ||
684 | * | ||
685 | * @p_base: Pointer to a pointer to a TTM base object | ||
686 | * embedded in a struct vmw_user_surface. | ||
687 | * | ||
688 | * Drops the base object's reference on its resource, and the | ||
689 | * pointer pointed to by *p_base is set to NULL. | ||
690 | */ | ||
691 | static void vmw_user_surface_base_release(struct ttm_base_object **p_base) | ||
692 | { | ||
693 | struct ttm_base_object *base = *p_base; | ||
694 | struct vmw_user_surface *user_srf = | ||
695 | container_of(base, struct vmw_user_surface, base); | ||
696 | struct vmw_resource *res = &user_srf->srf.res; | ||
697 | |||
698 | *p_base = NULL; | ||
699 | vmw_resource_unreference(&res); | ||
700 | } | ||
701 | |||
702 | /** | ||
703 | * vmw_user_surface_destroy_ioctl - Ioctl function implementing | ||
704 | * the user surface destroy functionality. | ||
705 | * | ||
706 | * @dev: Pointer to a struct drm_device. | ||
707 | * @data: Pointer to data copied from / to user-space. | ||
708 | * @file_priv: Pointer to a drm file private structure. | ||
709 | */ | ||
710 | int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data, | ||
711 | struct drm_file *file_priv) | ||
712 | { | ||
713 | struct drm_vmw_surface_arg *arg = (struct drm_vmw_surface_arg *)data; | ||
714 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | ||
715 | |||
716 | return ttm_ref_object_base_unref(tfile, arg->sid, TTM_REF_USAGE); | ||
717 | } | ||
718 | |||
719 | /** | ||
720 | * vmw_user_surface_define_ioctl - Ioctl function implementing | ||
721 | * the user surface define functionality. | ||
722 | * | ||
723 | * @dev: Pointer to a struct drm_device. | ||
724 | * @data: Pointer to data copied from / to user-space. | ||
725 | * @file_priv: Pointer to a drm file private structure. | ||
726 | */ | ||
727 | int vmw_surface_define_ioctl(struct drm_device *dev, void *data, | ||
728 | struct drm_file *file_priv) | ||
729 | { | ||
730 | struct vmw_private *dev_priv = vmw_priv(dev); | ||
731 | struct vmw_user_surface *user_srf; | ||
732 | struct vmw_surface *srf; | ||
733 | struct vmw_resource *res; | ||
734 | struct vmw_resource *tmp; | ||
735 | union drm_vmw_surface_create_arg *arg = | ||
736 | (union drm_vmw_surface_create_arg *)data; | ||
737 | struct drm_vmw_surface_create_req *req = &arg->req; | ||
738 | struct drm_vmw_surface_arg *rep = &arg->rep; | ||
739 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | ||
740 | struct drm_vmw_size __user *user_sizes; | ||
741 | int ret; | ||
742 | int i, j; | ||
743 | uint32_t cur_bo_offset; | ||
744 | struct drm_vmw_size *cur_size; | ||
745 | struct vmw_surface_offset *cur_offset; | ||
746 | uint32_t stride_bpp; | ||
747 | uint32_t bpp; | ||
748 | uint32_t num_sizes; | ||
749 | uint32_t size; | ||
750 | struct vmw_master *vmaster = vmw_master(file_priv->master); | ||
751 | |||
752 | if (unlikely(vmw_user_surface_size == 0)) | ||
753 | vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) + | ||
754 | 128; | ||
755 | |||
756 | num_sizes = 0; | ||
757 | for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) | ||
758 | num_sizes += req->mip_levels[i]; | ||
759 | |||
760 | if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * | ||
761 | DRM_VMW_MAX_MIP_LEVELS) | ||
762 | return -EINVAL; | ||
763 | |||
764 | size = vmw_user_surface_size + 128 + | ||
765 | ttm_round_pot(num_sizes * sizeof(struct drm_vmw_size)) + | ||
766 | ttm_round_pot(num_sizes * sizeof(struct vmw_surface_offset)); | ||
767 | |||
768 | |||
769 | ret = ttm_read_lock(&vmaster->lock, true); | ||
770 | if (unlikely(ret != 0)) | ||
771 | return ret; | ||
772 | |||
773 | ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), | ||
774 | size, false, true); | ||
775 | if (unlikely(ret != 0)) { | ||
776 | if (ret != -ERESTARTSYS) | ||
777 | DRM_ERROR("Out of graphics memory for surface" | ||
778 | " creation.\n"); | ||
779 | goto out_unlock; | ||
780 | } | ||
781 | |||
782 | user_srf = kzalloc(sizeof(*user_srf), GFP_KERNEL); | ||
783 | if (unlikely(user_srf == NULL)) { | ||
784 | ret = -ENOMEM; | ||
785 | goto out_no_user_srf; | ||
786 | } | ||
787 | |||
788 | srf = &user_srf->srf; | ||
789 | res = &srf->res; | ||
790 | |||
791 | srf->flags = req->flags; | ||
792 | srf->format = req->format; | ||
793 | srf->scanout = req->scanout; | ||
794 | |||
795 | memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels)); | ||
796 | srf->num_sizes = num_sizes; | ||
797 | user_srf->size = size; | ||
798 | |||
799 | srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL); | ||
800 | if (unlikely(srf->sizes == NULL)) { | ||
801 | ret = -ENOMEM; | ||
802 | goto out_no_sizes; | ||
803 | } | ||
804 | srf->offsets = kmalloc(srf->num_sizes * sizeof(*srf->offsets), | ||
805 | GFP_KERNEL); | ||
806 | if (unlikely(srf->sizes == NULL)) { | ||
807 | ret = -ENOMEM; | ||
808 | goto out_no_offsets; | ||
809 | } | ||
810 | |||
811 | user_sizes = (struct drm_vmw_size __user *)(unsigned long) | ||
812 | req->size_addr; | ||
813 | |||
814 | ret = copy_from_user(srf->sizes, user_sizes, | ||
815 | srf->num_sizes * sizeof(*srf->sizes)); | ||
816 | if (unlikely(ret != 0)) { | ||
817 | ret = -EFAULT; | ||
818 | goto out_no_copy; | ||
819 | } | ||
820 | |||
821 | srf->base_size = *srf->sizes; | ||
822 | srf->autogen_filter = SVGA3D_TEX_FILTER_NONE; | ||
823 | srf->multisample_count = 1; | ||
824 | |||
825 | cur_bo_offset = 0; | ||
826 | cur_offset = srf->offsets; | ||
827 | cur_size = srf->sizes; | ||
828 | |||
829 | bpp = vmw_sf_bpp[srf->format].bpp; | ||
830 | stride_bpp = vmw_sf_bpp[srf->format].s_bpp; | ||
831 | |||
832 | for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { | ||
833 | for (j = 0; j < srf->mip_levels[i]; ++j) { | ||
834 | uint32_t stride = | ||
835 | (cur_size->width * stride_bpp + 7) >> 3; | ||
836 | |||
837 | cur_offset->face = i; | ||
838 | cur_offset->mip = j; | ||
839 | cur_offset->bo_offset = cur_bo_offset; | ||
840 | cur_bo_offset += stride * cur_size->height * | ||
841 | cur_size->depth * bpp / stride_bpp; | ||
842 | ++cur_offset; | ||
843 | ++cur_size; | ||
844 | } | ||
845 | } | ||
846 | res->backup_size = cur_bo_offset; | ||
847 | |||
848 | if (srf->scanout && | ||
849 | srf->num_sizes == 1 && | ||
850 | srf->sizes[0].width == 64 && | ||
851 | srf->sizes[0].height == 64 && | ||
852 | srf->format == SVGA3D_A8R8G8B8) { | ||
853 | |||
854 | srf->snooper.image = kmalloc(64 * 64 * 4, GFP_KERNEL); | ||
855 | /* clear the image */ | ||
856 | if (srf->snooper.image) { | ||
857 | memset(srf->snooper.image, 0x00, 64 * 64 * 4); | ||
858 | } else { | ||
859 | DRM_ERROR("Failed to allocate cursor_image\n"); | ||
860 | ret = -ENOMEM; | ||
861 | goto out_no_copy; | ||
862 | } | ||
863 | } else { | ||
864 | srf->snooper.image = NULL; | ||
865 | } | ||
866 | srf->snooper.crtc = NULL; | ||
867 | |||
868 | user_srf->base.shareable = false; | ||
869 | user_srf->base.tfile = NULL; | ||
870 | |||
871 | /** | ||
872 | * From this point, the generic resource management functions | ||
873 | * destroy the object on failure. | ||
874 | */ | ||
875 | |||
876 | ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free); | ||
877 | if (unlikely(ret != 0)) | ||
878 | goto out_unlock; | ||
879 | |||
880 | tmp = vmw_resource_reference(&srf->res); | ||
881 | ret = ttm_base_object_init(tfile, &user_srf->base, | ||
882 | req->shareable, VMW_RES_SURFACE, | ||
883 | &vmw_user_surface_base_release, NULL); | ||
884 | |||
885 | if (unlikely(ret != 0)) { | ||
886 | vmw_resource_unreference(&tmp); | ||
887 | vmw_resource_unreference(&res); | ||
888 | goto out_unlock; | ||
889 | } | ||
890 | |||
891 | rep->sid = user_srf->base.hash.key; | ||
892 | vmw_resource_unreference(&res); | ||
893 | |||
894 | ttm_read_unlock(&vmaster->lock); | ||
895 | return 0; | ||
896 | out_no_copy: | ||
897 | kfree(srf->offsets); | ||
898 | out_no_offsets: | ||
899 | kfree(srf->sizes); | ||
900 | out_no_sizes: | ||
901 | ttm_base_object_kfree(user_srf, base); | ||
902 | out_no_user_srf: | ||
903 | ttm_mem_global_free(vmw_mem_glob(dev_priv), size); | ||
904 | out_unlock: | ||
905 | ttm_read_unlock(&vmaster->lock); | ||
906 | return ret; | ||
907 | } | ||
908 | |||
909 | /** | ||
910 | * vmw_user_surface_define_ioctl - Ioctl function implementing | ||
911 | * the user surface reference functionality. | ||
912 | * | ||
913 | * @dev: Pointer to a struct drm_device. | ||
914 | * @data: Pointer to data copied from / to user-space. | ||
915 | * @file_priv: Pointer to a drm file private structure. | ||
916 | */ | ||
917 | int vmw_surface_reference_ioctl(struct drm_device *dev, void *data, | ||
918 | struct drm_file *file_priv) | ||
919 | { | ||
920 | union drm_vmw_surface_reference_arg *arg = | ||
921 | (union drm_vmw_surface_reference_arg *)data; | ||
922 | struct drm_vmw_surface_arg *req = &arg->req; | ||
923 | struct drm_vmw_surface_create_req *rep = &arg->rep; | ||
924 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | ||
925 | struct vmw_surface *srf; | ||
926 | struct vmw_user_surface *user_srf; | ||
927 | struct drm_vmw_size __user *user_sizes; | ||
928 | struct ttm_base_object *base; | ||
929 | int ret = -EINVAL; | ||
930 | |||
931 | base = ttm_base_object_lookup(tfile, req->sid); | ||
932 | if (unlikely(base == NULL)) { | ||
933 | DRM_ERROR("Could not find surface to reference.\n"); | ||
934 | return -EINVAL; | ||
935 | } | ||
936 | |||
937 | if (unlikely(base->object_type != VMW_RES_SURFACE)) | ||
938 | goto out_bad_resource; | ||
939 | |||
940 | user_srf = container_of(base, struct vmw_user_surface, base); | ||
941 | srf = &user_srf->srf; | ||
942 | |||
943 | ret = ttm_ref_object_add(tfile, &user_srf->base, TTM_REF_USAGE, NULL); | ||
944 | if (unlikely(ret != 0)) { | ||
945 | DRM_ERROR("Could not add a reference to a surface.\n"); | ||
946 | goto out_no_reference; | ||
947 | } | ||
948 | |||
949 | rep->flags = srf->flags; | ||
950 | rep->format = srf->format; | ||
951 | memcpy(rep->mip_levels, srf->mip_levels, sizeof(srf->mip_levels)); | ||
952 | user_sizes = (struct drm_vmw_size __user *)(unsigned long) | ||
953 | rep->size_addr; | ||
954 | |||
955 | if (user_sizes) | ||
956 | ret = copy_to_user(user_sizes, srf->sizes, | ||
957 | srf->num_sizes * sizeof(*srf->sizes)); | ||
958 | if (unlikely(ret != 0)) { | ||
959 | DRM_ERROR("copy_to_user failed %p %u\n", | ||
960 | user_sizes, srf->num_sizes); | ||
961 | ret = -EFAULT; | ||
962 | } | ||
963 | out_bad_resource: | ||
964 | out_no_reference: | ||
965 | ttm_base_object_unref(&base); | ||
966 | |||
967 | return ret; | ||
968 | } | ||