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
35static 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
43static 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 */
62Bool
63vmwgfx_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 */
96enum xa_formats
97vmwgfx_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 */
117Bool
118vmwgfx_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 */
154static inline Bool
155vmwgfx_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 */
170Bool
171vmwgfx_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 */
205Bool
206vmwgfx_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 */
269static enum xa_formats
270vmwgfx_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 */
293Bool
294vmwgfx_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 */
337Bool
338vmwgfx_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 */
393Bool
394vmwgfx_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 */
409Bool
410vmwgfx_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