1/**********************************************************
2 * Copyright 2021 VMware, Inc.
3 * SPDX-License-Identifier: GPL-2.0 OR MIT
4 *
5 * Permission is hereby granted, free of charge, to any person
6 * obtaining a copy of this software and associated documentation
7 * files (the "Software"), to deal in the Software without
8 * restriction, including without limitation the rights to use, copy,
9 * modify, merge, publish, distribute, sublicense, and/or sell copies
10 * of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 **********************************************************/
26
27#ifndef VMW_SURFACE_CACHE_H
28#define VMW_SURFACE_CACHE_H
29
30#include "device_include/svga3d_surfacedefs.h"
31
32#include <drm/vmwgfx_drm.h>
33
34static inline u32 clamped_umul32(u32 a, u32 b)
35{
36 uint64_t tmp = (uint64_t) a*b;
37 return (tmp > (uint64_t) ((u32) -1)) ? (u32) -1 : tmp;
38}
39
40/**
41 * vmw_surface_get_desc - Look up the appropriate SVGA3dSurfaceDesc for the
42 * given format.
43 */
44static inline const SVGA3dSurfaceDesc *
45vmw_surface_get_desc(SVGA3dSurfaceFormat format)
46{
47 if (format < ARRAY_SIZE(g_SVGA3dSurfaceDescs))
48 return &g_SVGA3dSurfaceDescs[format];
49
50 return &g_SVGA3dSurfaceDescs[SVGA3D_FORMAT_INVALID];
51}
52
53/**
54 * vmw_surface_get_mip_size - Given a base level size and the mip level,
55 * compute the size of the mip level.
56 */
57static inline struct drm_vmw_size
58vmw_surface_get_mip_size(struct drm_vmw_size base_level, u32 mip_level)
59{
60 struct drm_vmw_size size = {
61 .width = max_t(u32, base_level.width >> mip_level, 1),
62 .height = max_t(u32, base_level.height >> mip_level, 1),
63 .depth = max_t(u32, base_level.depth >> mip_level, 1)
64 };
65
66 return size;
67}
68
69static inline void
70vmw_surface_get_size_in_blocks(const SVGA3dSurfaceDesc *desc,
71 const struct drm_vmw_size *pixel_size,
72 SVGA3dSize *block_size)
73{
74 block_size->width = __KERNEL_DIV_ROUND_UP(pixel_size->width,
75 desc->blockSize.width);
76 block_size->height = __KERNEL_DIV_ROUND_UP(pixel_size->height,
77 desc->blockSize.height);
78 block_size->depth = __KERNEL_DIV_ROUND_UP(pixel_size->depth,
79 desc->blockSize.depth);
80}
81
82static inline bool
83vmw_surface_is_planar_surface(const SVGA3dSurfaceDesc *desc)
84{
85 return (desc->blockDesc & SVGA3DBLOCKDESC_PLANAR_YUV) != 0;
86}
87
88static inline u32
89vmw_surface_calculate_pitch(const SVGA3dSurfaceDesc *desc,
90 const struct drm_vmw_size *size)
91{
92 u32 pitch;
93 SVGA3dSize blocks;
94
95 vmw_surface_get_size_in_blocks(desc, pixel_size: size, block_size: &blocks);
96
97 pitch = blocks.width * desc->pitchBytesPerBlock;
98
99 return pitch;
100}
101
102/**
103 * vmw_surface_get_image_buffer_size - Calculates image buffer size.
104 *
105 * Return the number of bytes of buffer space required to store one image of a
106 * surface, optionally using the specified pitch.
107 *
108 * If pitch is zero, it is assumed that rows are tightly packed.
109 *
110 * This function is overflow-safe. If the result would have overflowed, instead
111 * we return MAX_UINT32.
112 */
113static inline u32
114vmw_surface_get_image_buffer_size(const SVGA3dSurfaceDesc *desc,
115 const struct drm_vmw_size *size,
116 u32 pitch)
117{
118 SVGA3dSize image_blocks;
119 u32 slice_size, total_size;
120
121 vmw_surface_get_size_in_blocks(desc, pixel_size: size, block_size: &image_blocks);
122
123 if (vmw_surface_is_planar_surface(desc)) {
124 total_size = clamped_umul32(a: image_blocks.width,
125 b: image_blocks.height);
126 total_size = clamped_umul32(a: total_size, b: image_blocks.depth);
127 total_size = clamped_umul32(a: total_size, b: desc->bytesPerBlock);
128 return total_size;
129 }
130
131 if (pitch == 0)
132 pitch = vmw_surface_calculate_pitch(desc, size);
133
134 slice_size = clamped_umul32(a: image_blocks.height, b: pitch);
135 total_size = clamped_umul32(a: slice_size, b: image_blocks.depth);
136
137 return total_size;
138}
139
140/**
141 * vmw_surface_get_serialized_size - Get the serialized size for the image.
142 */
143static inline u32
144vmw_surface_get_serialized_size(SVGA3dSurfaceFormat format,
145 struct drm_vmw_size base_level_size,
146 u32 num_mip_levels,
147 u32 num_layers)
148{
149 const SVGA3dSurfaceDesc *desc = vmw_surface_get_desc(format);
150 u32 total_size = 0;
151 u32 mip;
152
153 for (mip = 0; mip < num_mip_levels; mip++) {
154 struct drm_vmw_size size =
155 vmw_surface_get_mip_size(base_level: base_level_size, mip_level: mip);
156 total_size += vmw_surface_get_image_buffer_size(desc,
157 size: &size, pitch: 0);
158 }
159
160 return total_size * num_layers;
161}
162
163/**
164 * vmw_surface_get_serialized_size_extended - Returns the number of bytes
165 * required for a surface with given parameters. Support for sample count.
166 */
167static inline u32
168vmw_surface_get_serialized_size_extended(SVGA3dSurfaceFormat format,
169 struct drm_vmw_size base_level_size,
170 u32 num_mip_levels,
171 u32 num_layers,
172 u32 num_samples)
173{
174 uint64_t total_size =
175 vmw_surface_get_serialized_size(format,
176 base_level_size,
177 num_mip_levels,
178 num_layers);
179 total_size *= max_t(u32, 1, num_samples);
180
181 return min_t(uint64_t, total_size, (uint64_t)U32_MAX);
182}
183
184/**
185 * vmw_surface_get_pixel_offset - Compute the offset (in bytes) to a pixel
186 * in an image (or volume).
187 *
188 * @width: The image width in pixels.
189 * @height: The image height in pixels
190 */
191static inline u32
192vmw_surface_get_pixel_offset(SVGA3dSurfaceFormat format,
193 u32 width, u32 height,
194 u32 x, u32 y, u32 z)
195{
196 const SVGA3dSurfaceDesc *desc = vmw_surface_get_desc(format);
197 const u32 bw = desc->blockSize.width, bh = desc->blockSize.height;
198 const u32 bd = desc->blockSize.depth;
199 const u32 rowstride = __KERNEL_DIV_ROUND_UP(width, bw) *
200 desc->bytesPerBlock;
201 const u32 imgstride = __KERNEL_DIV_ROUND_UP(height, bh) * rowstride;
202 const u32 offset = (z / bd * imgstride +
203 y / bh * rowstride +
204 x / bw * desc->bytesPerBlock);
205 return offset;
206}
207
208static inline u32
209vmw_surface_get_image_offset(SVGA3dSurfaceFormat format,
210 struct drm_vmw_size baseLevelSize,
211 u32 numMipLevels,
212 u32 face,
213 u32 mip)
214
215{
216 u32 offset;
217 u32 mipChainBytes;
218 u32 mipChainBytesToLevel;
219 u32 i;
220 const SVGA3dSurfaceDesc *desc;
221 struct drm_vmw_size mipSize;
222 u32 bytes;
223
224 desc = vmw_surface_get_desc(format);
225
226 mipChainBytes = 0;
227 mipChainBytesToLevel = 0;
228 for (i = 0; i < numMipLevels; i++) {
229 mipSize = vmw_surface_get_mip_size(base_level: baseLevelSize, mip_level: i);
230 bytes = vmw_surface_get_image_buffer_size(desc, size: &mipSize, pitch: 0);
231 mipChainBytes += bytes;
232 if (i < mip)
233 mipChainBytesToLevel += bytes;
234 }
235
236 offset = mipChainBytes * face + mipChainBytesToLevel;
237
238 return offset;
239}
240
241
242/**
243 * vmw_surface_is_gb_screen_target_format - Is the specified format usable as
244 * a ScreenTarget?
245 * (with just the GBObjects cap-bit
246 * set)
247 * @format: format to queried
248 *
249 * RETURNS:
250 * true if queried format is valid for screen targets
251 */
252static inline bool
253vmw_surface_is_gb_screen_target_format(SVGA3dSurfaceFormat format)
254{
255 return (format == SVGA3D_X8R8G8B8 ||
256 format == SVGA3D_A8R8G8B8 ||
257 format == SVGA3D_R5G6B5 ||
258 format == SVGA3D_X1R5G5B5 ||
259 format == SVGA3D_A1R5G5B5 ||
260 format == SVGA3D_P8);
261}
262
263
264/**
265 * vmw_surface_is_dx_screen_target_format - Is the specified format usable as
266 * a ScreenTarget?
267 * (with DX10 enabled)
268 *
269 * @format: format to queried
270 *
271 * Results:
272 * true if queried format is valid for screen targets
273 */
274static inline bool
275vmw_surface_is_dx_screen_target_format(SVGA3dSurfaceFormat format)
276{
277 return (format == SVGA3D_R8G8B8A8_UNORM ||
278 format == SVGA3D_B8G8R8A8_UNORM ||
279 format == SVGA3D_B8G8R8X8_UNORM);
280}
281
282
283/**
284 * vmw_surface_is_screen_target_format - Is the specified format usable as a
285 * ScreenTarget?
286 * (for some combination of caps)
287 *
288 * @format: format to queried
289 *
290 * Results:
291 * true if queried format is valid for screen targets
292 */
293static inline bool
294vmw_surface_is_screen_target_format(SVGA3dSurfaceFormat format)
295{
296 if (vmw_surface_is_gb_screen_target_format(format)) {
297 return true;
298 }
299 return vmw_surface_is_dx_screen_target_format(format);
300}
301
302/**
303 * struct vmw_surface_mip - Mimpmap level information
304 * @bytes: Bytes required in the backing store of this mipmap level.
305 * @img_stride: Byte stride per image.
306 * @row_stride: Byte stride per block row.
307 * @size: The size of the mipmap.
308 */
309struct vmw_surface_mip {
310 size_t bytes;
311 size_t img_stride;
312 size_t row_stride;
313 struct drm_vmw_size size;
314
315};
316
317/**
318 * struct vmw_surface_cache - Cached surface information
319 * @desc: Pointer to the surface descriptor
320 * @mip: Array of mipmap level information. Valid size is @num_mip_levels.
321 * @mip_chain_bytes: Bytes required in the backing store for the whole chain
322 * of mip levels.
323 * @sheet_bytes: Bytes required in the backing store for a sheet
324 * representing a single sample.
325 * @num_mip_levels: Valid size of the @mip array. Number of mipmap levels in
326 * a chain.
327 * @num_layers: Number of slices in an array texture or number of faces in
328 * a cubemap texture.
329 */
330struct vmw_surface_cache {
331 const SVGA3dSurfaceDesc *desc;
332 struct vmw_surface_mip mip[DRM_VMW_MAX_MIP_LEVELS];
333 size_t mip_chain_bytes;
334 size_t sheet_bytes;
335 u32 num_mip_levels;
336 u32 num_layers;
337};
338
339/**
340 * struct vmw_surface_loc - Surface location
341 * @sheet: The multisample sheet.
342 * @sub_resource: Surface subresource. Defined as layer * num_mip_levels +
343 * mip_level.
344 * @x: X coordinate.
345 * @y: Y coordinate.
346 * @z: Z coordinate.
347 */
348struct vmw_surface_loc {
349 u32 sheet;
350 u32 sub_resource;
351 u32 x, y, z;
352};
353
354/**
355 * vmw_surface_subres - Compute the subresource from layer and mipmap.
356 * @cache: Surface layout data.
357 * @mip_level: The mipmap level.
358 * @layer: The surface layer (face or array slice).
359 *
360 * Return: The subresource.
361 */
362static inline u32 vmw_surface_subres(const struct vmw_surface_cache *cache,
363 u32 mip_level, u32 layer)
364{
365 return cache->num_mip_levels * layer + mip_level;
366}
367
368/**
369 * vmw_surface_setup_cache - Build a surface cache entry
370 * @size: The surface base level dimensions.
371 * @format: The surface format.
372 * @num_mip_levels: Number of mipmap levels.
373 * @num_layers: Number of layers.
374 * @cache: Pointer to a struct vmw_surface_cach object to be filled in.
375 *
376 * Return: Zero on success, -EINVAL on invalid surface layout.
377 */
378static inline int vmw_surface_setup_cache(const struct drm_vmw_size *size,
379 SVGA3dSurfaceFormat format,
380 u32 num_mip_levels,
381 u32 num_layers,
382 u32 num_samples,
383 struct vmw_surface_cache *cache)
384{
385 const SVGA3dSurfaceDesc *desc;
386 u32 i;
387
388 memset(cache, 0, sizeof(*cache));
389 cache->desc = desc = vmw_surface_get_desc(format);
390 cache->num_mip_levels = num_mip_levels;
391 cache->num_layers = num_layers;
392 for (i = 0; i < cache->num_mip_levels; i++) {
393 struct vmw_surface_mip *mip = &cache->mip[i];
394
395 mip->size = vmw_surface_get_mip_size(base_level: *size, mip_level: i);
396 mip->bytes = vmw_surface_get_image_buffer_size
397 (desc, size: &mip->size, pitch: 0);
398 mip->row_stride =
399 __KERNEL_DIV_ROUND_UP(mip->size.width,
400 desc->blockSize.width) *
401 desc->bytesPerBlock * num_samples;
402 if (!mip->row_stride)
403 goto invalid_dim;
404
405 mip->img_stride =
406 __KERNEL_DIV_ROUND_UP(mip->size.height,
407 desc->blockSize.height) *
408 mip->row_stride;
409 if (!mip->img_stride)
410 goto invalid_dim;
411
412 cache->mip_chain_bytes += mip->bytes;
413 }
414 cache->sheet_bytes = cache->mip_chain_bytes * num_layers;
415 if (!cache->sheet_bytes)
416 goto invalid_dim;
417
418 return 0;
419
420invalid_dim:
421 VMW_DEBUG_USER("Invalid surface layout for dirty tracking.\n");
422 return -EINVAL;
423}
424
425/**
426 * vmw_surface_get_loc - Get a surface location from an offset into the
427 * backing store
428 * @cache: Surface layout data.
429 * @loc: Pointer to a struct vmw_surface_loc to be filled in.
430 * @offset: Offset into the surface backing store.
431 */
432static inline void
433vmw_surface_get_loc(const struct vmw_surface_cache *cache,
434 struct vmw_surface_loc *loc,
435 size_t offset)
436{
437 const struct vmw_surface_mip *mip = &cache->mip[0];
438 const SVGA3dSurfaceDesc *desc = cache->desc;
439 u32 layer;
440 int i;
441
442 loc->sheet = offset / cache->sheet_bytes;
443 offset -= loc->sheet * cache->sheet_bytes;
444
445 layer = offset / cache->mip_chain_bytes;
446 offset -= layer * cache->mip_chain_bytes;
447 for (i = 0; i < cache->num_mip_levels; ++i, ++mip) {
448 if (mip->bytes > offset)
449 break;
450 offset -= mip->bytes;
451 }
452
453 loc->sub_resource = vmw_surface_subres(cache, mip_level: i, layer);
454 loc->z = offset / mip->img_stride;
455 offset -= loc->z * mip->img_stride;
456 loc->z *= desc->blockSize.depth;
457 loc->y = offset / mip->row_stride;
458 offset -= loc->y * mip->row_stride;
459 loc->y *= desc->blockSize.height;
460 loc->x = offset / desc->bytesPerBlock;
461 loc->x *= desc->blockSize.width;
462}
463
464/**
465 * vmw_surface_inc_loc - Clamp increment a surface location with one block
466 * size
467 * in each dimension.
468 * @loc: Pointer to a struct vmw_surface_loc to be incremented.
469 *
470 * When computing the size of a range as size = end - start, the range does not
471 * include the end element. However a location representing the last byte
472 * of a touched region in the backing store *is* included in the range.
473 * This function modifies such a location to match the end definition
474 * given as start + size which is the one used in a SVGA3dBox.
475 */
476static inline void
477vmw_surface_inc_loc(const struct vmw_surface_cache *cache,
478 struct vmw_surface_loc *loc)
479{
480 const SVGA3dSurfaceDesc *desc = cache->desc;
481 u32 mip = loc->sub_resource % cache->num_mip_levels;
482 const struct drm_vmw_size *size = &cache->mip[mip].size;
483
484 loc->sub_resource++;
485 loc->x += desc->blockSize.width;
486 if (loc->x > size->width)
487 loc->x = size->width;
488 loc->y += desc->blockSize.height;
489 if (loc->y > size->height)
490 loc->y = size->height;
491 loc->z += desc->blockSize.depth;
492 if (loc->z > size->depth)
493 loc->z = size->depth;
494}
495
496/**
497 * vmw_surface_min_loc - The start location in a subresource
498 * @cache: Surface layout data.
499 * @sub_resource: The subresource.
500 * @loc: Pointer to a struct vmw_surface_loc to be filled in.
501 */
502static inline void
503vmw_surface_min_loc(const struct vmw_surface_cache *cache,
504 u32 sub_resource,
505 struct vmw_surface_loc *loc)
506{
507 loc->sheet = 0;
508 loc->sub_resource = sub_resource;
509 loc->x = loc->y = loc->z = 0;
510}
511
512/**
513 * vmw_surface_min_loc - The end location in a subresource
514 * @cache: Surface layout data.
515 * @sub_resource: The subresource.
516 * @loc: Pointer to a struct vmw_surface_loc to be filled in.
517 *
518 * Following the end definition given in vmw_surface_inc_loc(),
519 * Compute the end location of a surface subresource.
520 */
521static inline void
522vmw_surface_max_loc(const struct vmw_surface_cache *cache,
523 u32 sub_resource,
524 struct vmw_surface_loc *loc)
525{
526 const struct drm_vmw_size *size;
527 u32 mip;
528
529 loc->sheet = 0;
530 loc->sub_resource = sub_resource + 1;
531 mip = sub_resource % cache->num_mip_levels;
532 size = &cache->mip[mip].size;
533 loc->x = size->width;
534 loc->y = size->height;
535 loc->z = size->depth;
536}
537
538
539#endif /* VMW_SURFACE_CACHE_H */
540

source code of linux/drivers/gpu/drm/vmwgfx/vmw_surface_cache.h