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