1 /*
2  * Copyright 2011 VMWare, Inc.
3  * All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sub license, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the
14  * next paragraph) shall be included in all copies or substantial portions
15  * of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20  * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
21  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  * Author: Thomas Hellstrom <thellstrom@vmware.com>
26  */
27 #ifdef _HAVE_CONFIG_H_
28 #include "config.h"
29 #endif
30 
31 #include <xorg-server.h>
32 #include "vmwgfx_saa_priv.h"
33 
34 
35 static const enum xa_surface_type vmwgfx_stype_map[] = {
36   [PICT_TYPE_OTHER] = xa_type_other,
37   [PICT_TYPE_A] = xa_type_a,
38   [PICT_TYPE_ARGB] = xa_type_argb,
39   [PICT_TYPE_ABGR] = xa_type_abgr,
40   [PICT_TYPE_BGRA] = xa_type_bgra
41 };
42 
43 static const unsigned int vmwgfx_stype_map_size =
44     sizeof(vmwgfx_stype_map) / sizeof(enum xa_surface_type);
45 
46 /**
47  * vmwgfx_xa_surface_redefine - wrapper around xa_surface_redefine
48  *
49  * @vpix: Pointer to the struct vmwgfx_saa_pixmap the surface is attached to.
50  * @srf: The surface.
51  * @width: New width.
52  * @height: New height.
53  * @depth: New pixel depth.
54  * @stype: New surface type.
55  * @rgb_format: New rgb format.
56  * @new_flags: New surface flags.
57  * @copy_contents: Copy contents if new backing store is allocated.
58  *
59  * This is a wrapper that prints out an error message if the backing store
60  * of an active scanout surface is changed.
61  */
62 Bool
vmwgfx_xa_surface_redefine(struct vmwgfx_saa_pixmap * vpix,struct xa_surface * srf,int width,int height,int depth,enum xa_surface_type stype,enum xa_formats rgb_format,unsigned int new_flags,int copy_contents)63 vmwgfx_xa_surface_redefine(struct vmwgfx_saa_pixmap *vpix,
64 			   struct xa_surface *srf,
65 			   int width,
66 			   int height,
67 			   int depth,
68 			   enum xa_surface_type stype,
69 			   enum xa_formats rgb_format,
70 			   unsigned int new_flags,
71 			   int copy_contents)
72 {
73     uint32_t handle, new_handle, dummy;
74     Bool have_handle = FALSE;
75 
76     if (!WSBMLISTEMPTY(&vpix->scanout_list))
77 	have_handle = (_xa_surface_handle(srf, &handle, &dummy) == XA_ERR_NONE);
78 
79     if (xa_surface_redefine(srf, width, height, depth, stype, rgb_format,
80 			    new_flags, copy_contents) != XA_ERR_NONE)
81 	return FALSE;
82 
83     if (!WSBMLISTEMPTY(&vpix->scanout_list) && have_handle &&
84 	_xa_surface_handle(srf, &new_handle, &dummy) == XA_ERR_NONE &&
85 	new_handle != handle) {
86 	LogMessage(X_ERROR, "Changed active scanout surface handle.\n");
87     }
88 
89     return TRUE;
90 }
91 
92 
93 /*
94  * Create an xa format from a PICT format.
95  */
96 enum xa_formats
vmwgfx_xa_format(enum _PictFormatShort format)97 vmwgfx_xa_format(enum _PictFormatShort format)
98 {
99     uint32_t ptype = PICT_FORMAT_TYPE(format);
100 
101     if (ptype >= vmwgfx_stype_map_size ||
102 	vmwgfx_stype_map[ptype] == 0 ||
103 	vmwgfx_stype_map[ptype] == xa_type_other)
104 	return xa_format_unknown;
105 
106     return xa_format(PICT_FORMAT_BPP(format),
107 		     vmwgfx_stype_map[ptype],
108 		     PICT_FORMAT_A(format),
109 		     PICT_FORMAT_R(format),
110 		     PICT_FORMAT_G(format),
111 		     PICT_FORMAT_B(format));
112 }
113 
114 /*
115  * Choose formats and flags for a dri2 surface.
116  */
117 Bool
vmwgfx_hw_dri2_stage(PixmapPtr pixmap,unsigned int depth)118 vmwgfx_hw_dri2_stage(PixmapPtr pixmap, unsigned int depth)
119 {
120     struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
121     enum xa_formats format;
122 
123     if (depth == 0)
124 	depth = pixmap->drawable.depth;
125 
126     switch(depth) {
127     case 32:
128 	format = xa_format_a8r8g8b8;
129 	break;
130     case 24:
131         format = xa_format_x8r8g8b8;
132 	break;
133     case 16:
134 	format = xa_format_r5g6b5;
135 	break;
136     case 15:
137 	format = xa_format_x1r5g5b5;
138 	break;
139     default:
140 	return FALSE;
141     }
142 
143     vpix->staging_format = format;
144     vpix->staging_remove_flags = 0;
145     vpix->staging_add_flags = XA_FLAG_RENDER_TARGET | XA_FLAG_SHARED;
146 
147     return TRUE;
148 }
149 
150 /*
151  * Is composite old format compatible? Only difference is that old format
152  * has more alpha bits?
153  */
154 static inline Bool
vmwgfx_old_format_compatible(enum xa_formats format,enum xa_formats old_format)155 vmwgfx_old_format_compatible(enum xa_formats format,
156 			     enum xa_formats old_format)
157 {
158     return (format == old_format ||
159 	    (xa_format_type(format) == xa_format_type(old_format) &&
160 	     xa_format_a(format) <= xa_format_a(old_format) &&
161 	     xa_format_r(format) == xa_format_r(old_format) &&
162 	     xa_format_g(format) == xa_format_g(old_format) &&
163 	     xa_format_b(format) == xa_format_b(old_format)));
164 }
165 
166 
167 /*
168  * Choose format and flags for a composite dst surface.
169  */
170 Bool
vmwgfx_hw_composite_dst_stage(PixmapPtr pixmap,enum _PictFormatShort pict_format)171 vmwgfx_hw_composite_dst_stage(PixmapPtr pixmap,
172 			      enum _PictFormatShort pict_format)
173 {
174     struct vmwgfx_saa *vsaa =
175 	to_vmwgfx_saa(saa_get_driver(pixmap->drawable.pScreen));
176     struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
177     enum xa_formats format = vmwgfx_xa_format(pict_format);
178 
179     /*
180      * Check if we can reuse old hardware format.
181      */
182     if (vpix->hw) {
183 	enum xa_formats old_format = xa_surface_format(vpix->hw);
184 
185 	if (vmwgfx_old_format_compatible(format, old_format))
186 	    format = old_format;
187     }
188 
189     if (xa_format_check_supported(vsaa->xat, format,
190 				  vpix->xa_flags | XA_FLAG_RENDER_TARGET) !=
191 	XA_ERR_NONE) {
192 	return FALSE;
193     }
194 
195     vpix->staging_format = format;
196     vpix->staging_remove_flags = 0;
197     vpix->staging_add_flags = XA_FLAG_RENDER_TARGET | XA_FLAG_SHARED;
198 
199     return TRUE;
200 }
201 
202 /*
203  * Choose format and flags for a composite src surface.
204  */
205 Bool
vmwgfx_hw_composite_src_stage(PixmapPtr pixmap,enum _PictFormatShort pict_format)206 vmwgfx_hw_composite_src_stage(PixmapPtr pixmap,
207 			      enum _PictFormatShort pict_format)
208 {
209     struct vmwgfx_saa *vsaa =
210 	to_vmwgfx_saa(saa_get_driver(pixmap->drawable.pScreen));
211     struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
212     enum xa_formats format = vmwgfx_xa_format(pict_format);
213     enum xa_formats swizzle_format = xa_format_unknown;
214     enum xa_surface_type ftype;
215 
216     if (format == xa_format_unknown)
217 	return FALSE;
218 
219     ftype = xa_format_type(format);
220     if (ftype == xa_type_abgr) {
221 
222 	swizzle_format = xa_format(xa_format_bpp(format),
223 				   xa_type_argb,
224 				   xa_format_a(format),
225 				   xa_format_r(format),
226 				   xa_format_g(format),
227 				   xa_format_b(format));
228     }
229 
230     /*
231      * Check if we can reuse old format.
232      */
233 
234     if (vpix->hw) {
235 	enum xa_formats old_format = xa_surface_format(vpix->hw);
236 
237 	if (vmwgfx_old_format_compatible(format, old_format) ||
238 	    (swizzle_format != xa_format_unknown &&
239 	     vmwgfx_old_format_compatible(swizzle_format, old_format))) {
240 	    format = old_format;
241 	    goto have_format;
242 	}
243     }
244 
245     if (swizzle_format != xa_format_unknown &&
246 	xa_format_check_supported(vsaa->xat, swizzle_format, vpix->xa_flags) ==
247 	XA_ERR_NONE) {
248 	format = swizzle_format;
249 	goto have_format;
250     }
251 
252     if (xa_format_check_supported(vsaa->xat, format, vpix->xa_flags) ==
253 	XA_ERR_NONE) {
254 	goto have_format;
255     }
256 
257     return FALSE;
258   have_format:
259     vpix->staging_format = format;
260     vpix->staging_remove_flags = 0;
261     vpix->staging_add_flags = 0;
262 
263     return TRUE;
264 }
265 
266 /*
267  * Choose accel format given depth.
268  */
269 static enum xa_formats
vmwgfx_choose_accel_format(unsigned int depth)270 vmwgfx_choose_accel_format(unsigned int depth)
271 {
272     switch(depth) {
273     case 32:
274 	return xa_format_a8r8g8b8;
275     case 24:
276 	return xa_format_x8r8g8b8;
277     case 16:
278 	return xa_format_r5g6b5;
279     case 15:
280 	return xa_format_x1r5g5b5;
281     case 8:
282 	return xa_format_a8;
283     default:
284 	break;
285     }
286     return xa_format_unknown;
287 }
288 
289 
290 /*
291  * Determine xa format and flags for an ordinary accel surface.
292  */
293 Bool
vmwgfx_hw_accel_stage(PixmapPtr pixmap,unsigned int depth,uint32_t add_flags,uint32_t remove_flags)294 vmwgfx_hw_accel_stage(PixmapPtr pixmap, unsigned int depth,
295 		      uint32_t add_flags, uint32_t remove_flags)
296 {
297     struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
298     enum xa_formats format = xa_format_unknown;
299 
300     if (depth == 0)
301 	depth = pixmap->drawable.depth;
302 
303     if (vpix->hw) {
304 	enum xa_formats old_format = xa_surface_format(vpix->hw);
305 	enum xa_surface_type ftype = xa_format_type(old_format);
306 
307 	if (ftype != xa_type_argb &&
308 	    ftype != xa_type_a) {
309 	    LogMessage(X_ERROR,
310 		       "Acceleration fallback due to strange hw format.\n");
311 	    return FALSE;
312 	}
313 
314 	if (xa_format_depth(old_format) == depth ||
315 	    (xa_format_depth(old_format) == 32 &&
316 	     depth == 24))
317 	    format = old_format;
318     }
319 
320     if (format == xa_format_unknown)
321 	format = vmwgfx_choose_accel_format(depth);
322 
323     if (format == xa_format_unknown)
324 	return FALSE;
325 
326     vpix->staging_add_flags = add_flags;
327     vpix->staging_remove_flags = remove_flags;
328     vpix->staging_format = format;
329 
330     return TRUE;
331 }
332 
333 /*
334  * Create a surface with a format and flags determined by one of
335  * the staging functions.
336  */
337 Bool
vmwgfx_hw_commit(PixmapPtr pixmap)338 vmwgfx_hw_commit(PixmapPtr pixmap)
339 {
340     struct vmwgfx_saa *vsaa =
341 	to_vmwgfx_saa(saa_get_driver(pixmap->drawable.pScreen));
342     struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap);
343     struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix);
344     enum xa_formats format = vpix->staging_format;
345 
346     if (vpix->hw) {
347 	enum xa_formats old_format = xa_surface_format(vpix->hw);
348 
349 	if (vpix->staging_format != old_format) {
350 	    if (xa_format_type(format) != xa_format_type(old_format) ||
351 		xa_format_r(format) != xa_format_r(old_format) ||
352 		xa_format_g(format) != xa_format_g(old_format) ||
353 		xa_format_b(format) != xa_format_b(old_format)) {
354 
355 		LogMessage(X_INFO, "Killing old hw surface.\n");
356 
357 		if (!vmwgfx_hw_kill(vsaa, spix))
358 		    return FALSE;
359 	    }
360 	}
361     }
362 
363     if (vpix->hw) {
364 	uint32_t new_flags;
365 
366 	new_flags = (vpix->xa_flags & ~vpix->staging_remove_flags) |
367 	    vpix->staging_add_flags | XA_FLAG_SHARED;
368 
369 	if (vpix->staging_format != xa_surface_format(vpix->hw))
370 	    LogMessage(X_INFO, "Changing hardware format.\n");
371 
372 	if (!vmwgfx_xa_surface_redefine(vpix,
373 					vpix->hw,
374 					pixmap->drawable.width,
375 					pixmap->drawable.height,
376 					0,
377 					xa_type_other,
378 					vpix->staging_format,
379 					new_flags, 1) != XA_ERR_NONE)
380 	    return FALSE;
381 	vpix->xa_flags = new_flags;
382     } else if (!vmwgfx_create_hw(vsaa, pixmap, FALSE))
383 	return FALSE;
384 
385     return TRUE;
386 }
387 
388 /*
389  * Create an accel surface if there is none, and make sure the region
390  * given by @region is valid. If @region is NULL, the whole surface
391  * will be valid. This is a utility convenience function only.
392  */
393 Bool
vmwgfx_hw_accel_validate(PixmapPtr pixmap,unsigned int depth,uint32_t add_flags,uint32_t remove_flags,RegionPtr region)394 vmwgfx_hw_accel_validate(PixmapPtr pixmap, unsigned int depth,
395 			 uint32_t add_flags, uint32_t remove_flags,
396 			 RegionPtr region)
397 {
398     return (vmwgfx_hw_accel_stage(pixmap, depth, add_flags, remove_flags) &&
399 	    vmwgfx_hw_commit(pixmap) &&
400 	    vmwgfx_hw_validate(pixmap, region));
401 }
402 
403 
404 /*
405  * Create a dri2 surface if there is none,
406  * and make sure the whole surfade is valid.
407  * This is a utility convenience function only.
408  */
409 Bool
vmwgfx_hw_dri2_validate(PixmapPtr pixmap,unsigned int depth)410 vmwgfx_hw_dri2_validate(PixmapPtr pixmap, unsigned int depth)
411 {
412     struct vmwgfx_saa *vsaa =
413 	to_vmwgfx_saa(saa_get_driver(pixmap->drawable.pScreen));
414 
415     if (!vsaa->is_master)
416 	    return FALSE;
417 
418     return (vmwgfx_hw_dri2_stage(pixmap, depth) &&
419 	    vmwgfx_hw_commit(pixmap) &&
420 	    vmwgfx_hw_validate(pixmap, NULL));
421 }
422