vmwgfx_saa.c revision 25dbecb6
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
28#include <xorg-server.h>
29#include <xorgVersion.h>
30#include <mi.h>
31#include <fb.h>
32#include <xa_context.h>
33#include "vmwgfx_saa.h"
34#include "vmwgfx_drmi.h"
35#include "vmwgfx_saa_priv.h"
36#include <xf86drmMode.h>
37
38/*
39 * Damage to be added as soon as we attach storage to the pixmap.
40 */
41static Bool
42vmwgfx_pixmap_add_damage(PixmapPtr pixmap)
43{
44    struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap);
45    struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix);
46    DrawablePtr draw = &pixmap->drawable;
47    BoxRec box;
48
49    if (spix->damage)
50	return TRUE;
51
52    if (!saa_add_damage(pixmap))
53	return FALSE;
54
55    box.x1 = 0;
56    box.x2 = draw->width;
57    box.y1 = 0;
58    box.y2 = draw->height;
59
60    if (vpix->hw) {
61	REGION_RESET(draw->pScreen, &spix->dirty_hw, &box);
62	REGION_EMPTY(draw->pScreen, &spix->dirty_shadow);
63    } else {
64	REGION_RESET(draw->pScreen, &spix->dirty_shadow, &box);
65	REGION_EMPTY(draw->pScreen, &spix->dirty_hw);
66    }
67
68    return TRUE;
69}
70
71static void
72vmwgfx_pixmap_remove_damage(PixmapPtr pixmap)
73{
74    struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap);
75    struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix);
76
77    if (!spix->damage || vpix->hw || vpix->gmr || vpix->malloc)
78	return;
79
80#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,14,99,2,0)
81    DamageUnregister(spix->damage);
82#else
83    DamageUnregister(&pixmap->drawable, spix->damage);
84#endif
85
86    DamageDestroy(spix->damage);
87    spix->damage = NULL;
88}
89
90static void
91vmwgfx_pixmap_remove_present(struct vmwgfx_saa_pixmap *vpix)
92{
93    if (vpix->dirty_present)
94	REGION_DESTROY(pixmap->drawable.pScreen, vpix->dirty_present);
95    if (vpix->present_damage)
96	REGION_DESTROY(pixmap->drawable.pScreen, vpix->present_damage);
97    if (vpix->pending_update)
98	REGION_DESTROY(pixmap->drawable.pScreen, vpix->pending_update);
99    if (vpix->pending_present)
100	REGION_DESTROY(pixmap->drawable.pScreen, vpix->pending_present);
101    vpix->dirty_present = NULL;
102    vpix->present_damage = NULL;
103    vpix->pending_update = NULL;
104    vpix->pending_present = NULL;
105}
106
107static Bool
108vmwgfx_pixmap_add_present(PixmapPtr pixmap, Bool present_opt)
109{
110    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
111    ScreenPtr pScreen = pixmap->drawable.pScreen;
112    (void) pScreen;
113
114    if (present_opt) {
115	vpix->dirty_present = REGION_CREATE(pScreen, NULL, 0);
116	if (!vpix->dirty_present)
117	    return FALSE;
118	vpix->present_damage = REGION_CREATE(pScreen, NULL, 0);
119	if (!vpix->present_damage)
120	    goto out_no_present_damage;
121    }
122    vpix->pending_update = REGION_CREATE(pScreen, NULL, 0);
123    if (!vpix->pending_update)
124	goto out_no_pending_update;
125    vpix->pending_present = REGION_CREATE(pScreen, NULL, 0);
126    if (!vpix->pending_present)
127	goto out_no_pending_present;
128
129    return TRUE;
130  out_no_pending_present:
131    REGION_DESTROY(pScreen, vpix->pending_update);
132  out_no_pending_update:
133    if (vpix->present_damage)
134	REGION_DESTROY(pScreen, vpix->present_damage);
135  out_no_present_damage:
136    if (vpix->dirty_present)
137	REGION_DESTROY(pScreen, vpix->dirty_present);
138    return FALSE;
139}
140
141static void
142vmwgfx_pixmap_free_storage(struct vmwgfx_saa_pixmap *vpix)
143{
144    if (!(vpix->backing & VMWGFX_PIX_MALLOC) && vpix->malloc) {
145	free(vpix->malloc);
146	vpix->malloc = NULL;
147    }
148    if (!(vpix->backing & VMWGFX_PIX_SURFACE) && vpix->hw) {
149	xa_surface_destroy(vpix->hw);
150	vpix->hw = NULL;
151    }
152    if (!(vpix->backing & VMWGFX_PIX_GMR) && vpix->gmr) {
153	vmwgfx_dmabuf_destroy(vpix->gmr);
154	vpix->gmr = NULL;
155    }
156}
157
158static Bool
159vmwgfx_pixmap_create_gmr(struct vmwgfx_saa *vsaa, PixmapPtr pixmap)
160{
161    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
162    size_t size;
163    struct vmwgfx_dmabuf *gmr;
164    void *addr;
165
166    if (vpix->gmr)
167	return TRUE;
168
169    size = pixmap->devKind * pixmap->drawable.height;
170    gmr = vmwgfx_dmabuf_alloc(vsaa->drm_fd, size);
171    if (!gmr)
172	return FALSE;
173
174    if (vpix->malloc) {
175
176	addr = vmwgfx_dmabuf_map(gmr);
177	if (!addr)
178	    goto out_no_transfer;
179	memcpy(addr, vpix->malloc, size);
180	vmwgfx_dmabuf_unmap(gmr);
181
182    } else if (!vmwgfx_pixmap_add_damage(pixmap))
183	goto out_no_transfer;
184
185    vpix->backing |= VMWGFX_PIX_GMR;
186    vpix->backing &= ~VMWGFX_PIX_MALLOC;
187    vpix->gmr = gmr;
188
189    vmwgfx_pixmap_free_storage(vpix);
190
191    return TRUE;
192
193  out_no_transfer:
194    vmwgfx_dmabuf_destroy(gmr);
195    return FALSE;
196}
197
198static Bool
199vmwgfx_pixmap_create_sw(struct vmwgfx_saa *vsaa, PixmapPtr pixmap)
200{
201    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
202
203    if (!(vpix->backing & (VMWGFX_PIX_MALLOC | VMWGFX_PIX_GMR)))
204	return FALSE;
205
206    if (!vpix->malloc && (vpix->backing & VMWGFX_PIX_MALLOC)) {
207	vpix->malloc = malloc(pixmap->devKind * pixmap->drawable.height);
208	if (!vpix->malloc)
209	    goto out_no_malloc;
210	if (!vmwgfx_pixmap_add_damage(pixmap))
211	    goto out_no_damage;
212    } else if (vpix->backing & VMWGFX_PIX_GMR)
213	return vmwgfx_pixmap_create_gmr(vsaa, pixmap);
214
215    return TRUE;
216
217  out_no_damage:
218    free(vpix->malloc);
219    vpix->malloc = NULL;
220  out_no_malloc:
221    return FALSE;
222}
223
224
225/**
226 *
227 * Makes sure all presented contents covered by @region are read
228 * back and are present in a valid GMR.
229 */
230
231static Bool
232vmwgfx_pixmap_present_readback(struct vmwgfx_saa *vsaa,
233			       PixmapPtr pixmap,
234			       RegionPtr region)
235{
236    struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap);
237    struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix);
238    RegionRec intersection;
239
240    if (!spix->damage || !REGION_NOTEMPTY(vsaa->pScreen, &spix->dirty_hw) ||
241	!vpix->dirty_present)
242	return TRUE;
243
244    /*
245     * Intersect dirty region with region to be read back, if any.
246     */
247
248    REGION_NULL(vsaa->pScreen, &intersection);
249    REGION_COPY(vsaa->pScreen, &intersection, &spix->dirty_hw);
250    REGION_INTERSECT(vsaa->pScreen, &intersection, &intersection,
251		     vpix->dirty_present);
252
253    if (region)
254	REGION_INTERSECT(vsaa->pScreen, &intersection, &intersection, region);
255
256    if (!REGION_NOTEMPTY(vsaa->pScreen, &intersection))
257	goto out;
258
259    /*
260     * Make really sure there is a GMR to read back to.
261     */
262
263    if (!vmwgfx_pixmap_create_gmr(vsaa, pixmap))
264	goto out_err;
265
266    if (vmwgfx_present_readback(vsaa->drm_fd, vpix->fb_id,
267				&intersection) != 0)
268	goto out_err;
269
270    REGION_SUBTRACT(vsaa->pScreen, &spix->dirty_hw,
271		    &spix->dirty_hw, &intersection);
272  out:
273    REGION_UNINIT(vsaa->pScreen, &intersection);
274    return TRUE;
275
276  out_err:
277    REGION_UNINIT(vsaa->pScreen, &intersection);
278    return FALSE;
279}
280
281static Bool
282vmwgfx_saa_dma(struct vmwgfx_saa *vsaa,
283	       PixmapPtr pixmap,
284	       RegionPtr reg,
285	       Bool to_hw,
286	       int dx,
287	       int dy,
288	       struct xa_surface *srf)
289{
290    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
291
292    if (!srf)
293	srf = vpix->hw;
294
295    if (!srf || (!vpix->gmr && !vpix->malloc))
296	return TRUE;
297
298    if (vpix->gmr && vsaa->can_optimize_dma) {
299	uint32_t handle, dummy;
300
301	if (_xa_surface_handle(srf, &handle, &dummy) != 0)
302	    goto out_err;
303	if (vmwgfx_dma(dx, dy, reg, vpix->gmr, pixmap->devKind, handle,
304		       to_hw) != 0)
305	    goto out_err;
306    } else {
307	uint8_t *data = (uint8_t *) vpix->malloc;
308	int ret;
309
310	if (vpix->gmr) {
311	    data = (uint8_t *) vmwgfx_dmabuf_map(vpix->gmr);
312	    if (!data)
313		goto out_err;
314	}
315
316	if (dx || dy) {
317	    REGION_TRANSLATE(pScreen, reg, dx, dy);
318	    data -= ((dx * pixmap->drawable.bitsPerPixel + 7)/8 +
319		     dy * pixmap->devKind);
320	}
321
322	ret = xa_surface_dma(vsaa->xa_ctx, srf, data, pixmap->devKind,
323			     (int) to_hw,
324			     (struct xa_box *) REGION_RECTS(reg),
325			     REGION_NUM_RECTS(reg));
326	if (to_hw)
327	    xa_context_flush(vsaa->xa_ctx);
328	if (vpix->gmr)
329	    vmwgfx_dmabuf_unmap(vpix->gmr);
330	if (dx || dy)
331	    REGION_TRANSLATE(pScreen, reg, -dx, -dy);
332	if (ret)
333	    goto out_err;
334    }
335    return TRUE;
336  out_err:
337    LogMessage(X_ERROR, "DMA %s surface failed.\n",
338	       to_hw ? "to" : "from");
339    return FALSE;
340}
341
342
343static Bool
344vmwgfx_download_from_hw(struct saa_driver *driver, PixmapPtr pixmap,
345			RegionPtr readback)
346{
347    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver);
348    struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap);
349    struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix);
350
351    RegionRec intersection;
352
353    if (!vmwgfx_pixmap_present_readback(vsaa, pixmap, readback))
354	return FALSE;
355
356    if (!REGION_NOTEMPTY(vsaa->pScreen, &spix->dirty_hw))
357	return TRUE;
358
359    if (!vpix->hw)
360	return TRUE;
361
362    REGION_NULL(vsaa->pScreen, &intersection);
363    REGION_INTERSECT(vsaa->pScreen, &intersection, readback,
364		     &spix->dirty_hw);
365    readback = &intersection;
366
367    if (!vmwgfx_pixmap_create_sw(vsaa, pixmap))
368	goto out_err;
369
370    if (!vmwgfx_saa_dma(vsaa, pixmap, readback, FALSE, 0, 0, NULL))
371	goto out_err;
372    REGION_SUBTRACT(vsaa->pScreen, &spix->dirty_hw, &spix->dirty_hw, readback);
373    REGION_UNINIT(vsaa->pScreen, &intersection);
374
375    return TRUE;
376 out_err:
377    REGION_UNINIT(vsaa->pScreen, &intersection);
378    return FALSE;
379}
380
381
382static Bool
383vmwgfx_upload_to_hw(struct saa_driver *driver, PixmapPtr pixmap,
384		    RegionPtr upload)
385{
386    return vmwgfx_saa_dma(to_vmwgfx_saa(driver), pixmap, upload, TRUE,
387			  0, 0, NULL);
388}
389
390static void
391vmwgfx_release_from_cpu(struct saa_driver *driver, PixmapPtr pixmap, saa_access_t access)
392{
393  //    LogMessage(X_INFO, "Release 0x%08lx access 0x%08x\n",
394  //	       (unsigned long) pixmap, (unsigned) access);
395}
396
397static void *
398vmwgfx_sync_for_cpu(struct saa_driver *driver, PixmapPtr pixmap, saa_access_t access)
399{
400    /*
401     * Errors in this functions will turn up in subsequent map
402     * calls.
403     */
404
405    (void) vmwgfx_pixmap_create_sw(to_vmwgfx_saa(driver), pixmap);
406
407    return NULL;
408}
409
410static void *
411vmwgfx_map(struct saa_driver *driver, PixmapPtr pixmap, saa_access_t access)
412{
413    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
414
415    if (vpix->malloc)
416	return vpix->malloc;
417    else if (vpix->gmr)
418	return vmwgfx_dmabuf_map(vpix->gmr);
419    else
420	return NULL;
421}
422
423static void
424vmwgfx_unmap(struct saa_driver *driver, PixmapPtr pixmap, saa_access_t access)
425{
426    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
427
428    if (vpix->gmr)
429	return vmwgfx_dmabuf_unmap(vpix->gmr);
430
431//    LogMessage(X_INFO, "Unmap 0x%08lx access 0x%08x\n",
432    //       (unsigned long) pixmap, (unsigned) access);
433    ;
434}
435
436static Bool
437vmwgfx_create_pixmap(struct saa_driver *driver, struct saa_pixmap *spix,
438		     int w, int h, int depth,
439		     unsigned int usage_hint, int bpp, int *new_pitch)
440{
441    struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix);
442
443    *new_pitch = ((w * bpp + FB_MASK) >> FB_SHIFT) * sizeof(FbBits);
444
445    WSBMINITLISTHEAD(&vpix->sync_x_head);
446    WSBMINITLISTHEAD(&vpix->scanout_list);
447    WSBMINITLISTHEAD(&vpix->pixmap_list);
448
449    return TRUE;
450}
451
452Bool
453vmwgfx_hw_kill(struct vmwgfx_saa *vsaa,
454	       struct saa_pixmap *spix)
455{
456    struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix);
457
458    if (!vpix->hw)
459	return TRUE;
460
461    /*
462     * Read back any dirty regions from hardware.
463     */
464
465    if (!vmwgfx_download_from_hw(&vsaa->driver, spix->pixmap,
466				 &spix->dirty_hw))
467	return FALSE;
468
469    xa_surface_destroy(vpix->hw);
470    vpix->hw = NULL;
471
472    /*
473     * Remove damage tracking if this is not a scanout pixmap.
474     */
475
476    if (WSBMLISTEMPTY(&vpix->scanout_list))
477	vmwgfx_pixmap_remove_damage(spix->pixmap);
478
479    return TRUE;
480}
481
482void
483vmwgfx_flush_dri2(ScreenPtr pScreen)
484{
485    struct vmwgfx_saa *vsaa =
486	to_vmwgfx_saa(saa_get_driver(pScreen));
487    struct _WsbmListHead *list, *next;
488    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
489
490    if (!pScrn->vtSema)
491	return;
492
493    WSBMLISTFOREACHSAFE(list, next, &vsaa->sync_x_list) {
494	struct vmwgfx_saa_pixmap *vpix =
495	    WSBMLISTENTRY(list, struct vmwgfx_saa_pixmap, sync_x_head);
496	struct saa_pixmap *spix = &vpix->base;
497	PixmapPtr pixmap = spix->pixmap;
498
499	if (vmwgfx_upload_to_hw(&vsaa->driver, pixmap, &spix->dirty_shadow)) {
500	    REGION_EMPTY(vsaa->pScreen, &spix->dirty_shadow);
501	    WSBMLISTDELINIT(list);
502	}
503    }
504}
505
506
507static void
508vmwgfx_destroy_pixmap(struct saa_driver *driver, PixmapPtr pixmap)
509{
510    ScreenPtr pScreen = to_vmwgfx_saa(driver)->pScreen;
511    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
512    (void) pScreen;
513
514    vpix->backing = 0;
515    vmwgfx_pixmap_free_storage(vpix);
516
517    /*
518     * Any damage we've registered has already been removed by the server
519     * at this point. Any attempt to unregister / destroy it will result
520     * in a double free.
521     */
522
523    vmwgfx_pixmap_remove_present(vpix);
524    WSBMLISTDELINIT(&vpix->pixmap_list);
525    WSBMLISTDELINIT(&vpix->sync_x_head);
526}
527
528
529
530/**
531 *
532 * Makes sure we have a surface with valid contents.
533 */
534
535static void
536vmwgfx_copy_stride(uint8_t *dst, uint8_t *src, unsigned int dst_pitch,
537		   unsigned int src_pitch, unsigned int dst_height,
538		   unsigned int src_height)
539{
540    unsigned int i;
541    unsigned int height = (dst_height < src_height) ? dst_height : src_height;
542    unsigned int pitch = (dst_pitch < src_pitch) ? dst_pitch : src_pitch;
543
544    for(i=0; i<height; ++i) {
545	memcpy(dst, src, pitch);
546	dst += dst_pitch;
547	src += src_pitch;
548    }
549}
550
551
552static Bool
553vmwgfx_pix_resize(PixmapPtr pixmap, unsigned int old_pitch,
554		  unsigned int old_height, unsigned int old_width)
555{
556    ScreenPtr pScreen = pixmap->drawable.pScreen;
557    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(saa_get_driver(pScreen));
558    struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap);
559    struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix);
560    DrawablePtr draw = &pixmap->drawable;
561    unsigned int size = pixmap->devKind * draw->height;
562    BoxRec b_box;
563    RegionRec b_reg;
564
565    /*
566     * Ignore copying errors. At worst they will show up as rendering
567     * artefacts.
568     */
569
570    if (vpix->malloc) {
571
572	void *new_malloc = malloc(size);
573	if (!new_malloc)
574	    return FALSE;
575
576	vmwgfx_copy_stride(new_malloc, vpix->malloc, pixmap->devKind,
577			   old_pitch, draw->height,
578			   old_height);
579	free(vpix->malloc);
580	vpix->malloc = new_malloc;
581    }
582
583    if (vpix->gmr) {
584	struct vmwgfx_dmabuf *gmr;
585	void *new_addr;
586	void *old_addr;
587
588	gmr = vmwgfx_dmabuf_alloc(vsaa->drm_fd, size);
589	if (!gmr)
590	    return FALSE;
591
592	new_addr = vmwgfx_dmabuf_map(gmr);
593	old_addr = vmwgfx_dmabuf_map(vpix->gmr);
594
595	if (new_addr && old_addr)
596	    vmwgfx_copy_stride(new_addr, old_addr, pixmap->devKind,
597			       old_pitch, draw->height,
598			       old_height);
599	else
600	    LogMessage(X_ERROR, "Failed pixmap resize copy.\n");
601
602	if (old_addr)
603	    vmwgfx_dmabuf_unmap(vpix->gmr);
604	if (new_addr)
605	    vmwgfx_dmabuf_unmap(gmr);
606	vmwgfx_dmabuf_destroy(vpix->gmr);
607	vpix->gmr = gmr;
608    }
609
610    if (vpix->hw) {
611      if (!vmwgfx_xa_surface_redefine(vpix, vpix->hw, draw->width,
612				      draw->height, draw->depth, xa_type_argb,
613				      xa_format_unknown, vpix->xa_flags, 1))
614	    return FALSE;
615    }
616
617    b_box.x1 = 0;
618    b_box.x2 = draw->width;
619    b_box.y1 = 0;
620    b_box.y2 = draw->height;
621
622    REGION_INIT(pScreen, &b_reg, &b_box, 1);
623    REGION_INTERSECT(pScreen, &spix->dirty_shadow, &spix->dirty_shadow,
624		     &b_reg);
625    REGION_INTERSECT(pScreen, &spix->dirty_hw, &spix->dirty_hw, &b_reg);
626    if (vpix->dirty_present)
627	REGION_INTERSECT(pScreen, vpix->dirty_present, vpix->dirty_present,
628			 &b_reg);
629    if (vpix->pending_update)
630	REGION_INTERSECT(pScreen, vpix->pending_update, vpix->pending_update,
631			 &b_reg);
632    if (vpix->pending_present)
633	REGION_INTERSECT(pScreen, vpix->pending_present,
634			 vpix->pending_present, &b_reg);
635    if (vpix->present_damage)
636	REGION_INTERSECT(pScreen, vpix->present_damage, vpix->present_damage,
637			 &b_reg);
638
639    REGION_UNINIT(pScreen, &b_reg);
640
641    return TRUE;
642}
643
644
645static Bool
646vmwgfx_modify_pixmap_header (PixmapPtr pixmap, int w, int h, int depth,
647			     int bpp, int devkind, void *pixdata)
648{
649    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
650    ScreenPtr pScreen = pixmap->drawable.pScreen;
651    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(saa_get_driver(pScreen));
652    unsigned int old_height;
653    unsigned int old_width;
654    unsigned int old_pitch;
655
656    if (!vpix) {
657	LogMessage(X_ERROR, "Not an SAA pixmap.\n");
658	return FALSE;
659    }
660
661    if (pixdata) {
662	vpix->backing = 0;
663	vmwgfx_pixmap_free_storage(vpix);
664	return FALSE;
665    }
666
667    if (depth <= 0)
668	depth = pixmap->drawable.depth;
669
670    if (bpp <= 0)
671	bpp = pixmap->drawable.bitsPerPixel;
672
673    if (w <= 0)
674	w = pixmap->drawable.width;
675
676    if (h <= 0)
677	h = pixmap->drawable.height;
678
679    if (w <= 0 || h <= 0 || depth <= 0)
680	return FALSE;
681
682    old_height = pixmap->drawable.height;
683    old_width = pixmap->drawable.width;
684    old_pitch = pixmap->devKind;
685
686    if (!miModifyPixmapHeader(pixmap, w, h, depth,
687			      bpp, devkind, NULL))
688	goto out_no_modify;
689
690    if (!vpix->backing)
691	vpix->backing = VMWGFX_PIX_MALLOC;
692
693    vmwgfx_pix_resize(pixmap, old_pitch, old_height, old_width);
694    vmwgfx_pixmap_free_storage(vpix);
695    if (WSBMLISTEMPTY(&vpix->pixmap_list))
696	WSBMLISTADDTAIL(&vpix->pixmap_list, &vsaa->pixmaps);
697
698    return TRUE;
699
700  out_no_modify:
701    return FALSE;
702}
703
704static Bool
705vmwgfx_present_prepare(struct vmwgfx_saa *vsaa,
706		       struct vmwgfx_saa_pixmap *src_vpix,
707		       struct vmwgfx_saa_pixmap *dst_vpix)
708{
709    ScreenPtr pScreen = vsaa->pScreen;
710    unsigned int dummy;
711
712    (void) pScreen;
713    if (src_vpix == dst_vpix || !src_vpix->hw ||
714	_xa_surface_handle(src_vpix->hw, &vsaa->src_handle, &dummy) != 0)
715	return FALSE;
716
717    REGION_NULL(pScreen, &vsaa->present_region);
718    vsaa->diff_valid = FALSE;
719    vsaa->dst_vpix = dst_vpix;
720    vsaa->present_flush(pScreen);
721
722    return TRUE;
723}
724
725/**
726 * Determine whether we should try present copies on this pixmap.
727 */
728
729static Bool
730vmwgfx_is_present_hw(PixmapPtr pixmap)
731{
732    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
733    return (vpix->dirty_present != NULL);
734}
735
736static void
737vmwgfx_check_hw_contents(struct vmwgfx_saa *vsaa,
738			 struct vmwgfx_saa_pixmap *vpix,
739			 RegionPtr region,
740			 Bool *has_dirty_hw,
741			 Bool *has_valid_hw)
742{
743    RegionRec intersection;
744
745
746    if (!vpix->hw) {
747	*has_dirty_hw = FALSE;
748	*has_valid_hw = FALSE;
749	return;
750    }
751
752    if (!region) {
753	*has_dirty_hw = REGION_NOTEMPTY(vsaa->pScreen,
754					&vpix->base.dirty_hw);
755	*has_valid_hw = !REGION_NOTEMPTY(vsaa->pScreen,
756					 &vpix->base.dirty_shadow);
757	return;
758    }
759
760    REGION_NULL(vsaa->pScreen, &intersection);
761    REGION_INTERSECT(vsaa->pScreen, &intersection, &vpix->base.dirty_hw,
762		     region);
763    *has_dirty_hw = REGION_NOTEMPTY(vsaa->pScreen, &intersection);
764    REGION_INTERSECT(vsaa->pScreen, &intersection, &vpix->base.dirty_shadow,
765		     region);
766    *has_valid_hw = !REGION_NOTEMPTY(vsaa->pScreen, &intersection);
767    REGION_UNINIT(vsaa->pScreen, &intersection);
768}
769
770/**
771 * vmwgfx_prefer_gmr: Prefer a dma buffer over malloced memory for software
772 * rendered storage
773 *
774 * @vsaa: Pointer to a struct vmwgfx_saa accelerator.
775 * @pixmap: Pointer to pixmap whose storage preference we want to alter.
776 *
777 * If possible, alter the storage or future storage of the software contents
778 * of this pixmap to be in a DMA buffer rather than in malloced memory.
779 * This function should be called when it's likely that frequent DMA operations
780 * will occur between a surface and the memory holding the software
781 * contents.
782 */
783static void
784vmwgfx_prefer_gmr(struct vmwgfx_saa *vsaa, PixmapPtr pixmap)
785{
786    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
787
788    if (vsaa->can_optimize_dma) {
789	if (vpix->malloc) {
790	    (void) vmwgfx_pixmap_create_gmr(vsaa, pixmap);
791	} else if (vpix->backing & VMWGFX_PIX_MALLOC) {
792	    vpix->backing |= VMWGFX_PIX_GMR;
793	    vpix->backing &= ~VMWGFX_PIX_MALLOC;
794	}
795    }
796}
797
798Bool
799vmwgfx_create_hw(struct vmwgfx_saa *vsaa,
800		 PixmapPtr pixmap,
801		 Bool shared)
802{
803    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
804    struct xa_surface *hw;
805    uint32_t new_flags;
806
807    if (!vsaa->xat)
808	return FALSE;
809
810    if (!shared) {
811	if (vpix->hw)
812	    return TRUE;
813
814	new_flags = (vpix->xa_flags & ~vpix->staging_remove_flags) |
815	    vpix->staging_add_flags | XA_FLAG_SHARED;
816
817	hw = xa_surface_create(vsaa->xat,
818			       pixmap->drawable.width,
819			       pixmap->drawable.height,
820			       0,
821			       xa_type_other,
822			       vpix->staging_format,
823			       new_flags);
824    } else {
825	new_flags = vpix->xa_flags;
826	hw = vpix->hw;
827    }
828
829    if (hw == NULL)
830	return FALSE;
831
832    vpix->xa_flags = new_flags;
833    vpix->hw = hw;
834
835    if (!vmwgfx_pixmap_add_damage(pixmap))
836	goto out_no_damage;
837
838    vpix->backing |= VMWGFX_PIX_SURFACE;
839    vmwgfx_pixmap_free_storage(vpix);
840
841    /*
842     * If there is a HW surface, make sure that the shadow is
843     * (or will be) a GMR, provided we can do fast DMAs from / to it.
844     */
845    vmwgfx_prefer_gmr(vsaa, pixmap);
846
847    return TRUE;
848
849out_no_damage:
850    vpix->hw = NULL;
851    xa_surface_destroy(hw);
852    return FALSE;
853}
854
855
856Bool
857vmwgfx_hw_validate(PixmapPtr pixmap, RegionPtr region)
858{
859    struct vmwgfx_saa *vsaa =
860	to_vmwgfx_saa(saa_get_driver(pixmap->drawable.pScreen));
861    struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap);
862    struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix);
863    RegionRec intersection;
864
865    if (!vmwgfx_pixmap_present_readback(vsaa, pixmap, region))
866	return FALSE;
867
868    REGION_NULL(vsaa->pScreen, &intersection);
869    REGION_COPY(vsaa->pScreen, &intersection, &spix->dirty_shadow);
870
871    if (vpix->dirty_present)
872	REGION_UNION(vsaa->pScreen, &intersection, vpix->dirty_present,
873		     &spix->dirty_shadow);
874
875    if (spix->damage && REGION_NOTEMPTY(vsaa->pScreen, &intersection)) {
876	RegionPtr upload = &intersection;
877
878	/*
879	 * Check whether we need to upload from GMR.
880	 */
881
882	if (region) {
883	    REGION_INTERSECT(vsaa->pScreen, &intersection, region,
884			     &intersection);
885	    upload = &intersection;
886	}
887
888	if (REGION_NOTEMPTY(vsaa->pScreen, upload)) {
889	    Bool ret = vmwgfx_upload_to_hw(&vsaa->driver, pixmap, upload);
890	    if (ret) {
891		REGION_SUBTRACT(vsaa->pScreen, &spix->dirty_shadow,
892				&spix->dirty_shadow, upload);
893		if (vpix->dirty_present)
894		    REGION_SUBTRACT(vsaa->pScreen, vpix->dirty_present,
895				    vpix->dirty_present, upload);
896	    } else {
897		REGION_UNINIT(vsaa->pScreen, &intersection);
898		return FALSE;
899	    }
900	}
901    }
902    REGION_UNINIT(vsaa->pScreen, &intersection);
903    return TRUE;
904}
905
906static Bool
907vmwgfx_copy_prepare(struct saa_driver *driver,
908		    PixmapPtr src_pixmap,
909		    PixmapPtr dst_pixmap,
910		    int dx,
911		    int dy,
912		    int alu,
913		    RegionPtr src_reg,
914		    uint32_t plane_mask)
915{
916    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver);
917    struct vmwgfx_saa_pixmap *src_vpix;
918    struct vmwgfx_saa_pixmap *dst_vpix;
919    Bool has_dirty_hw;
920    Bool has_valid_hw;
921
922    if (!vsaa->xat || !SAA_PM_IS_SOLID(&dst_pixmap->drawable, plane_mask) ||
923	alu != GXcopy || !vsaa->is_master)
924	return FALSE;
925
926    src_vpix = vmwgfx_saa_pixmap(src_pixmap);
927    dst_vpix = vmwgfx_saa_pixmap(dst_pixmap);
928
929    vmwgfx_check_hw_contents(vsaa, src_vpix, src_reg,
930			     &has_dirty_hw, &has_valid_hw);
931
932    if (vmwgfx_is_present_hw(dst_pixmap) &&
933	src_vpix->backing & VMWGFX_PIX_SURFACE) {
934
935	if (!has_dirty_hw && !has_valid_hw)
936	    return FALSE;
937
938	if (!vmwgfx_hw_accel_validate(src_pixmap, 0, 0, 0, src_reg))
939	    return FALSE;
940	if (vmwgfx_present_prepare(vsaa, src_vpix, dst_vpix)) {
941	    vsaa->present_copy = TRUE;
942	    return TRUE;
943	}
944	return FALSE;
945    }
946
947    vsaa->present_copy = FALSE;
948    if (src_vpix != dst_vpix) {
949
950	/*
951	 * Use hardware acceleration either if source is partially only
952	 * in hardware, or if source is entirely in hardware and destination
953	 * has a hardware surface.
954	 */
955
956	if (!has_dirty_hw && !(has_valid_hw && (dst_vpix->hw != NULL)))
957	    return FALSE;
958
959	/*
960	 * Determine surface formats.
961	 */
962
963	if (src_vpix->base.src_format == 0) {
964	    if (!vmwgfx_hw_accel_stage(src_pixmap, 0, XA_FLAG_RENDER_TARGET, 0))
965		return FALSE;
966	} else {
967	    if (PICT_FORMAT_TYPE(src_vpix->base.src_format) != PICT_TYPE_ARGB ||
968		!vmwgfx_hw_composite_src_stage(src_pixmap, src_vpix->base.src_format))
969		return FALSE;
970	}
971
972	if (dst_vpix->base.dst_format == 0) {
973	    if (!vmwgfx_hw_accel_stage(dst_pixmap, 0, XA_FLAG_RENDER_TARGET, 0))
974		return FALSE;
975	} else {
976	    if (PICT_FORMAT_TYPE(dst_vpix->base.dst_format) != PICT_TYPE_ARGB ||
977		!vmwgfx_hw_composite_dst_stage(dst_pixmap, dst_vpix->base.dst_format))
978		return FALSE;
979	}
980
981	/*
982	 * Create hardware surfaces.
983	 */
984
985	if (!vmwgfx_hw_commit(src_pixmap))
986	    return FALSE;
987	if (!vmwgfx_hw_commit(dst_pixmap))
988	    return FALSE;
989
990	/*
991	 * Migrate data.
992	 */
993
994	if (!vmwgfx_hw_validate(src_pixmap, src_reg)) {
995	    xa_copy_done(vsaa->xa_ctx);
996	    xa_context_flush(vsaa->xa_ctx);
997	    return FALSE;
998	}
999
1000	/*
1001	 * Setup copy state.
1002	 */
1003
1004	if (xa_copy_prepare(vsaa->xa_ctx, dst_vpix->hw, src_vpix->hw) !=
1005	    XA_ERR_NONE)
1006	    return FALSE;
1007
1008	return TRUE;
1009    }
1010
1011    return FALSE;
1012}
1013
1014
1015static void
1016vmwgfx_present_done(struct vmwgfx_saa *vsaa)
1017{
1018    ScreenPtr pScreen = vsaa->pScreen;
1019    struct vmwgfx_saa_pixmap *dst_vpix = vsaa->dst_vpix;
1020
1021    (void) pScreen;
1022    if (!vsaa->diff_valid)
1023	return;
1024
1025    (void) vmwgfx_present(vsaa->drm_fd, dst_vpix->fb_id,
1026			  vsaa->xdiff, vsaa->ydiff,
1027			  &vsaa->present_region, vsaa->src_handle);
1028
1029    REGION_TRANSLATE(pScreen, &vsaa->present_region, vsaa->xdiff, vsaa->ydiff);
1030    REGION_UNION(pScreen, dst_vpix->present_damage, dst_vpix->present_damage,
1031		 &vsaa->present_region);
1032    vsaa->diff_valid = FALSE;
1033    REGION_UNINIT(pScreen, &vsaa->present_region);
1034}
1035
1036static void
1037vmwgfx_present_copy(struct vmwgfx_saa *vsaa,
1038		    int src_x,
1039		    int src_y,
1040		    int dst_x,
1041		    int dst_y,
1042		    int w,
1043		    int h)
1044{
1045    int xdiff = dst_x - src_x;
1046    int ydiff = dst_y - src_y;
1047    BoxRec box;
1048    RegionRec reg;
1049
1050    if (vsaa->diff_valid && ((xdiff != vsaa->xdiff) || (ydiff != vsaa->ydiff)))
1051	(void) vmwgfx_present_done(vsaa);
1052
1053    if (!vsaa->diff_valid) {
1054	vsaa->xdiff = xdiff;
1055	vsaa->ydiff = ydiff;
1056	vsaa->diff_valid = TRUE;
1057    }
1058
1059    box.x1 = src_x;
1060    box.x2 = src_x + w;
1061    box.y1 = src_y;
1062    box.y2 = src_y + h;
1063
1064    REGION_INIT(pScreen, &reg, &box, 1);
1065    REGION_UNION(pScreen, &vsaa->present_region, &vsaa->present_region, &reg);
1066    REGION_UNINIT(pScreen, &reg);
1067}
1068
1069static void
1070vmwgfx_copy(struct saa_driver *driver,
1071	    int src_x,
1072	    int src_y,
1073	    int dst_x,
1074	    int dst_y,
1075	    int w,
1076	    int h)
1077{
1078    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver);
1079
1080    if (vsaa->present_copy) {
1081	vmwgfx_present_copy(vsaa, src_x, src_y, dst_x, dst_y, w, h);
1082	return;
1083    }
1084    xa_copy(vsaa->xa_ctx, dst_x, dst_y, src_x, src_y, w, h);
1085}
1086
1087static void
1088vmwgfx_copy_done(struct saa_driver *driver)
1089{
1090    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver);
1091
1092    if (vsaa->present_copy) {
1093	vmwgfx_present_done(vsaa);
1094	return;
1095    }
1096    xa_copy_done(vsaa->xa_ctx);
1097    xa_context_flush(vsaa->xa_ctx);
1098}
1099
1100static Bool
1101vmwgfx_composite_prepare(struct saa_driver *driver, CARD8 op,
1102			 PicturePtr src_pict, PicturePtr mask_pict,
1103			 PicturePtr dst_pict,
1104			 PixmapPtr src_pix, PixmapPtr mask_pix,
1105			 PixmapPtr dst_pix,
1106			 RegionPtr src_region,
1107			 RegionPtr mask_region,
1108			 RegionPtr dst_region)
1109{
1110    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver);
1111    struct vmwgfx_saa_pixmap *src_vpix;
1112    struct vmwgfx_saa_pixmap *dst_vpix;
1113    struct vmwgfx_saa_pixmap *mask_vpix;
1114    Bool tmp_valid_hw;
1115    Bool dirty_hw;
1116    Bool valid_hw;
1117    RegionRec empty;
1118    struct xa_composite *xa_comp;
1119
1120    if (!vsaa->is_master)
1121	return FALSE;
1122
1123    REGION_NULL(pScreen, &empty);
1124
1125    /*
1126     * First we define our migration policy. We accelerate only if there
1127     * are dirty hw regions to be read or if all source data is
1128     * available in hw, and the destination has a hardware surface.
1129     */
1130    dst_vpix = vmwgfx_saa_pixmap(dst_pix);
1131    valid_hw = (dst_vpix->hw != NULL);
1132    if (saa_op_reads_destination(op)) {
1133	vmwgfx_check_hw_contents(vsaa, dst_vpix, dst_region,
1134				 &dirty_hw, &tmp_valid_hw);
1135	valid_hw = (valid_hw && tmp_valid_hw);
1136    } else {
1137	dirty_hw = FALSE;
1138	dst_region = &empty;
1139    }
1140
1141    if (src_pix && !dirty_hw) {
1142	src_vpix = vmwgfx_saa_pixmap(src_pix);
1143	vmwgfx_check_hw_contents(vsaa, src_vpix, src_region,
1144				 &dirty_hw, &tmp_valid_hw);
1145	valid_hw = (valid_hw && tmp_valid_hw);
1146    }
1147
1148    if (mask_pict && mask_pix && !dirty_hw) {
1149	mask_vpix = vmwgfx_saa_pixmap(mask_pix);
1150	vmwgfx_check_hw_contents(vsaa, mask_vpix, mask_region,
1151				 &dirty_hw, &tmp_valid_hw);
1152	valid_hw = (valid_hw && tmp_valid_hw);
1153    }
1154
1155    /*
1156     * In rendercheck mode we try to accelerate all supported
1157     * composite operations.
1158     */
1159
1160    if (!valid_hw && !dirty_hw && !vsaa->rendercheck)
1161	goto out_err;
1162
1163    /*
1164     * Then, setup most of the XA composite state (except hardware surfaces)
1165     * and check whether XA can accelerate.
1166     */
1167
1168    if (!mask_pix)
1169	mask_pict = NULL;
1170    xa_comp = vmwgfx_xa_setup_comp(vsaa->vcomp, op,
1171				   src_pict, mask_pict, dst_pict);
1172    if (!xa_comp)
1173	goto out_err;
1174
1175    if (xa_composite_check_accelerated(xa_comp) != XA_ERR_NONE)
1176	goto out_err;
1177
1178    /*
1179     * Check that we can create the needed hardware surfaces.
1180     */
1181    if (src_pix && !vmwgfx_hw_composite_src_stage(src_pix, src_pict->format))
1182	goto out_err;
1183    if (mask_pict && mask_pix &&
1184	!vmwgfx_hw_composite_src_stage(mask_pix, mask_pict->format))
1185	goto out_err;
1186    if (!vmwgfx_hw_composite_dst_stage(dst_pix, dst_pict->format))
1187	goto out_err;
1188
1189    /*
1190     * Seems OK. Commit the changes, creating hardware surfaces.
1191     */
1192    if (src_pix && !vmwgfx_hw_commit(src_pix))
1193	goto out_err;
1194    if (mask_pict && mask_pix && !vmwgfx_hw_commit(mask_pix))
1195	goto out_err;
1196    if (!vmwgfx_hw_commit(dst_pix))
1197	goto out_err;
1198
1199    /*
1200     * Update the XA state with our hardware surfaces and
1201     * surface formats
1202     */
1203    if (!vmwgfx_xa_update_comp(xa_comp, src_pix, mask_pix, dst_pix))
1204	goto out_err;
1205
1206    /*
1207     * Migrate data to surfaces.
1208     */
1209    if (src_pix && src_region && !vmwgfx_hw_validate(src_pix, NULL))
1210	goto out_err;
1211    if (mask_pict && mask_pix && mask_region &&
1212	!vmwgfx_hw_validate(mask_pix, NULL))
1213	goto out_err;
1214    if (dst_region && !vmwgfx_hw_validate(dst_pix, NULL))
1215	goto out_err;
1216
1217
1218    /*
1219     * Bind the XA state. This must be done after data migration, since
1220     * migration may change the hardware surfaces.
1221     */
1222    if (xa_composite_prepare(vsaa->xa_ctx, xa_comp))
1223	goto out_err;
1224
1225    return TRUE;
1226
1227  out_err:
1228    return FALSE;
1229}
1230
1231static void
1232vmwgfx_composite(struct saa_driver *driver,
1233		 int src_x, int src_y, int mask_x, int mask_y,
1234		 int dst_x, int dst_y,
1235		 int width, int height)
1236{
1237    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver);
1238
1239    xa_composite_rect(vsaa->xa_ctx, src_x, src_y, mask_x, mask_y,
1240		      dst_x, dst_y, width, height);
1241}
1242
1243static void
1244vmwgfx_composite_done(struct saa_driver *driver)
1245{
1246   struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver);
1247
1248   xa_composite_done(vsaa->xa_ctx);
1249   xa_context_flush(vsaa->xa_ctx);
1250}
1251
1252static void
1253vmwgfx_takedown(struct saa_driver *driver)
1254{
1255    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver);
1256
1257    if (vsaa->vcomp)
1258	vmwgfx_free_composite(vsaa->vcomp);
1259    free(vsaa);
1260}
1261
1262/*
1263 * This function call originates from the damage layer (outside SAA)
1264 * to indicate that an operation is complete, and that damage is being
1265 * processed.
1266 */
1267static void
1268vmwgfx_operation_complete(struct saa_driver *driver,
1269			  PixmapPtr pixmap)
1270{
1271    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver);
1272    struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap);
1273    struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix);
1274    ScrnInfoPtr pScrn = xf86ScreenToScrn(vsaa->pScreen);
1275
1276    /*
1277     * Make dri2 drawables up to date, or add them to the flush list
1278     * executed at glxWaitX(). Currently glxWaitX() is broken, so
1279     * we flush immediately, unless we're VT-switched away, in which
1280     * case a flush would deadlock in the kernel.
1281     *
1282     * For pixmaps for which vpix->hw_is_hosted is true, we can explicitly
1283     * inform the compositor when contents has changed, so for those pixmaps
1284     * we defer the upload until the compositor is informed, by putting
1285     * them on the sync_x_list. Note that hw_is_dri2_fronts take precedence.
1286     */
1287    if (vpix->hw && (vpix->hw_is_dri2_fronts || vpix->hw_is_hosted)) {
1288	if (pScrn->vtSema && vpix->hw_is_dri2_fronts &&
1289	    vmwgfx_upload_to_hw(driver, pixmap, &spix->dirty_shadow)) {
1290
1291	    REGION_EMPTY(vsaa->pScreen, &spix->dirty_shadow);
1292	    return;
1293	}
1294
1295	if (WSBMLISTEMPTY(&vpix->sync_x_head))
1296	    WSBMLISTADDTAIL(&vpix->sync_x_head, &vsaa->sync_x_list);
1297    }
1298}
1299
1300/*
1301 * This function is called by SAA to indicate that SAA has
1302 * dirtied a region of a pixmap, either as hw (accelerated) or as
1303 * !hw (not accelerated).
1304 */
1305static Bool
1306vmwgfx_dirty(struct saa_driver *driver, PixmapPtr pixmap,
1307	     Bool hw, RegionPtr damage)
1308{
1309    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver);
1310    struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap);
1311    struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix);
1312
1313    /*
1314     * Return if this is not a scanout pixmap.
1315     */
1316    if (WSBMLISTEMPTY(&vpix->scanout_list))
1317	return TRUE;
1318
1319#if 0
1320    /*
1321     * This code can be enabled to immediately upload scanout sw
1322     * contents to the hw surface. Otherwise this is done
1323     * just before we call the kms update function for the hw
1324     * surface.
1325     */
1326    if (vpix->scanout_hw) {
1327	if (!hw && !vmwgfx_upload_to_hw(&vsaa->driver, pixmap, damage))
1328	    return FALSE;
1329
1330	REGION_SUBTRACT(&vsaa->pScreen, &spix->dirty_shadow,
1331			&spix->dirty_shadow, damage);
1332	hw = TRUE;
1333    }
1334#endif
1335
1336    /*
1337     * Is the new scanout damage hw or sw?
1338     */
1339    if (hw) {
1340	/*
1341	 * Dump pending present into present tracking region.
1342	 */
1343	if (vpix->dirty_present &&
1344	    REGION_NOTEMPTY(vsaa->pScreen, vpix->present_damage)) {
1345	    REGION_UNION(vsaa->pScreen, vpix->dirty_present,
1346			 vpix->dirty_present, damage);
1347	    REGION_EMPTY(vsaa->pScreen, vpix->present_damage);
1348	} else {
1349	    if (REGION_NOTEMPTY(vsaa->pScreen, vpix->pending_update)) {
1350		RegionRec reg;
1351
1352		REGION_NULL(vsaa->pScreen, &reg);
1353		REGION_INTERSECT(vsaa->pScreen, &reg, vpix->pending_update,
1354				 damage);
1355		if (REGION_NOTEMPTY(vsaa->pScreen, &reg))
1356		    vsaa->present_flush(vsaa->pScreen);
1357		REGION_UNINIT(pScreen, &reg);
1358	    }
1359	    REGION_UNION(vsaa->pScreen, vpix->pending_present,
1360			 vpix->pending_present, damage);
1361	    if (vpix->dirty_present)
1362		REGION_SUBTRACT(vsaa->pScreen, vpix->dirty_present,
1363				vpix->dirty_present, damage);
1364	}
1365    } else {
1366	    if (REGION_NOTEMPTY(vsaa->pScreen, vpix->pending_present)) {
1367		RegionRec reg;
1368
1369		REGION_NULL(vsaa->pScreen, &reg);
1370		REGION_INTERSECT(vsaa->pScreen, &reg, vpix->pending_present,
1371				 damage);
1372		if (REGION_NOTEMPTY(vsaa->pScreen, &reg))
1373		    vsaa->present_flush(vsaa->pScreen);
1374		REGION_UNINIT(pScreen, &reg);
1375	    }
1376	    REGION_UNION(vsaa->pScreen, vpix->pending_update,
1377			 vpix->pending_update, damage);
1378	    if (vpix->dirty_present)
1379		REGION_SUBTRACT(vsaa->pScreen, vpix->dirty_present,
1380				vpix->dirty_present, damage);
1381    }
1382
1383    return TRUE;
1384}
1385
1386
1387static const struct saa_driver vmwgfx_saa_driver = {
1388    .saa_major = SAA_VERSION_MAJOR,
1389    .saa_minor = SAA_VERSION_MINOR,
1390    .pixmap_size = sizeof(struct vmwgfx_saa_pixmap),
1391    .damage = vmwgfx_dirty,
1392    .operation_complete = vmwgfx_operation_complete,
1393    .download_from_hw = vmwgfx_download_from_hw,
1394    .release_from_cpu = vmwgfx_release_from_cpu,
1395    .sync_for_cpu = vmwgfx_sync_for_cpu,
1396    .map = vmwgfx_map,
1397    .unmap = vmwgfx_unmap,
1398    .create_pixmap = vmwgfx_create_pixmap,
1399    .destroy_pixmap = vmwgfx_destroy_pixmap,
1400    .modify_pixmap_header = vmwgfx_modify_pixmap_header,
1401    .copy_prepare = vmwgfx_copy_prepare,
1402    .copy = vmwgfx_copy,
1403    .copy_done = vmwgfx_copy_done,
1404    .composite_prepare = vmwgfx_composite_prepare,
1405    .composite = vmwgfx_composite,
1406    .composite_done = vmwgfx_composite_done,
1407    .takedown = vmwgfx_takedown,
1408};
1409
1410
1411Bool
1412vmwgfx_saa_init(ScreenPtr pScreen, int drm_fd, struct xa_tracker *xat,
1413		void (*present_flush)(ScreenPtr pScreen),
1414		Bool direct_presents,
1415		Bool only_hw_presents,
1416		Bool rendercheck,
1417		Bool has_screen_targets)
1418{
1419    struct vmwgfx_saa *vsaa;
1420
1421    vsaa = calloc(1, sizeof(*vsaa));
1422    if (!vsaa)
1423	return FALSE;
1424
1425    if (xat == NULL) {
1426	direct_presents = FALSE;
1427	only_hw_presents = FALSE;
1428	has_screen_targets = FALSE;
1429    }
1430
1431    vsaa->pScreen = pScreen;
1432    vsaa->xat = xat;
1433    if (xat)
1434	vsaa->xa_ctx = xa_context_default(xat);
1435    vsaa->drm_fd = drm_fd;
1436    vsaa->present_flush = present_flush;
1437    vsaa->can_optimize_dma = TRUE;
1438    vsaa->use_present_opt = direct_presents;
1439    vsaa->only_hw_presents = only_hw_presents;
1440    vsaa->rendercheck = rendercheck;
1441    vsaa->is_master = TRUE;
1442    vsaa->known_prime_format = FALSE;
1443    vsaa->has_screen_targets = has_screen_targets;
1444    WSBMINITLISTHEAD(&vsaa->sync_x_list);
1445    WSBMINITLISTHEAD(&vsaa->pixmaps);
1446
1447    vsaa->driver = vmwgfx_saa_driver;
1448    vsaa->vcomp = vmwgfx_alloc_composite();
1449
1450    if (!vsaa->vcomp)
1451	vsaa->driver.composite_prepare = NULL;
1452
1453    if (!saa_driver_init(pScreen, &vsaa->driver))
1454	goto out_no_saa;
1455
1456    return TRUE;
1457  out_no_saa:
1458    free(vsaa);
1459    return FALSE;
1460}
1461
1462/*
1463 * *************************************************************************
1464 * Scanout functions.
1465 * These do not strictly belong here, but we choose to hide the scanout
1466 * pixmap private data in the saa pixmaps. Might want to revisit this.
1467 */
1468
1469/*
1470 * Make sure we flush / update this scanout on next update run.
1471 */
1472
1473void
1474vmwgfx_scanout_refresh(PixmapPtr pixmap)
1475{
1476    ScreenPtr pScreen = pixmap->drawable.pScreen;
1477    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
1478    BoxRec box;
1479
1480    (void) pScreen;
1481    box.x1 = 0;
1482    box.y1 = 0;
1483    box.x2 = pixmap->drawable.width;
1484    box.y2 = pixmap->drawable.height;
1485
1486    REGION_RESET(vsaa->pScreen, vpix->pending_present, &box);
1487    if (vpix->dirty_present)
1488	REGION_SUBTRACT(vsaa->pScreen, vpix->pending_present,
1489			vpix->pending_present, vpix->dirty_present);
1490    REGION_SUBTRACT(vsaa->pScreen, vpix->pending_present,
1491		    vpix->pending_present, &vpix->base.dirty_shadow);
1492    REGION_COPY(vsaa->pScreen, vpix->pending_update,
1493		&vpix->base.dirty_shadow);
1494}
1495
1496/*
1497 * Take a "scanout reference" on a pixmap. If this is the first scanout
1498 * reference, allocate resources needed for scanout, like proper
1499 * damage tracking and kms fbs.
1500 */
1501
1502uint32_t
1503vmwgfx_scanout_ref(struct vmwgfx_screen_entry  *entry,
1504		   Bool scanout_equals_pixmap)
1505{
1506    PixmapPtr pixmap = entry->pixmap;
1507    struct vmwgfx_saa *vsaa =
1508	to_vmwgfx_saa(saa_get_driver(pixmap->drawable.pScreen));
1509    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
1510
1511    if (WSBMLISTEMPTY(&vpix->scanout_list)) {
1512	uint32_t handle, dummy;
1513	unsigned int depth;
1514
1515	vpix->scanout_hw = vsaa->only_hw_presents ||
1516	    (vsaa->has_screen_targets && scanout_equals_pixmap);
1517
1518	if (vpix->scanout_hw) {
1519	    /*
1520	     * The KMS fb will be a HW surface. Create it, add damage
1521	     * and get the handle.
1522	     */
1523	    if (!vmwgfx_hw_accel_validate(pixmap, 0, XA_FLAG_SCANOUT |
1524					  XA_FLAG_RENDER_TARGET, 0, NULL))
1525		goto out_err;
1526	    if (_xa_surface_handle(vpix->hw, &handle, &dummy) != 0)
1527		goto out_err;
1528	    depth = xa_format_depth(xa_surface_format(vpix->hw));
1529
1530	} else {
1531	    /*
1532	     * The KMS fb will be a Guest Memory Region. Create it,
1533	     * add damage and get the handle.
1534	     */
1535	    if (!vmwgfx_pixmap_create_gmr(vsaa, pixmap))
1536		goto out_err;
1537
1538	    handle = vpix->gmr->handle;
1539	    depth = pixmap->drawable.depth;
1540
1541	}
1542
1543        if (!vmwgfx_pixmap_add_present(pixmap, vsaa->use_present_opt))
1544	    goto out_no_present;
1545
1546	if (drmModeAddFB(vsaa->drm_fd,
1547			 pixmap->drawable.width,
1548			 pixmap->drawable.height,
1549			 depth,
1550			 pixmap->drawable.bitsPerPixel,
1551			 pixmap->devKind,
1552			 handle,
1553			 &vpix->fb_id) != 0)
1554	    goto out_no_fb;;
1555    }
1556    pixmap->refcnt += 1;
1557    WSBMLISTADDTAIL(&entry->scanout_head, &vpix->scanout_list);
1558    return vpix->fb_id;
1559
1560  out_no_fb:
1561    vmwgfx_pixmap_remove_present(vpix);
1562  out_no_present:
1563    vmwgfx_pixmap_remove_damage(pixmap);
1564  out_err:
1565    vpix->fb_id = -1;
1566    return -1;
1567}
1568
1569/*
1570 * Free a "scanout reference" on a pixmap. If this was the last scanout
1571 * reference, free pixmap resources needed for scanout, like
1572 * damage tracking and kms fbs.
1573 */
1574void
1575vmwgfx_scanout_unref(struct vmwgfx_screen_entry *entry)
1576{
1577    struct vmwgfx_saa *vsaa;
1578    struct vmwgfx_saa_pixmap *vpix;
1579    PixmapPtr pixmap = entry->pixmap;
1580
1581    if (!pixmap)
1582	return;
1583
1584    vsaa = to_vmwgfx_saa(saa_get_driver(pixmap->drawable.pScreen));
1585    vpix = vmwgfx_saa_pixmap(pixmap);
1586    WSBMLISTDELINIT(&entry->scanout_head);
1587
1588    if (WSBMLISTEMPTY(&vpix->scanout_list)) {
1589	REGION_EMPTY(vsaa->pScreen, vpix->pending_update);
1590	drmModeRmFB(vsaa->drm_fd, vpix->fb_id);
1591	vpix->fb_id = -1;
1592	vmwgfx_pixmap_present_readback(vsaa, pixmap, NULL);
1593	vmwgfx_pixmap_remove_present(vpix);
1594	vmwgfx_pixmap_remove_damage(pixmap);
1595    }
1596
1597    entry->pixmap = NULL;
1598    pixmap->drawable.pScreen->DestroyPixmap(pixmap);
1599}
1600
1601void
1602vmwgfx_saa_set_master(ScreenPtr pScreen)
1603{
1604    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(saa_get_driver(pScreen));
1605
1606    vsaa->is_master = TRUE;
1607    vmwgfx_flush_dri2(pScreen);
1608}
1609
1610void
1611vmwgfx_saa_drop_master(ScreenPtr pScreen)
1612{
1613    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(saa_get_driver(pScreen));
1614    struct _WsbmListHead *list;
1615    struct vmwgfx_saa_pixmap *vpix;
1616    struct saa_pixmap *spix;
1617
1618    WSBMLISTFOREACH(list, &vsaa->pixmaps) {
1619	vpix = WSBMLISTENTRY(list, struct vmwgfx_saa_pixmap, pixmap_list);
1620	spix = &vpix->base;
1621
1622	if (!vpix->hw)
1623	    continue;
1624
1625	(void) vmwgfx_download_from_hw(&vsaa->driver, spix->pixmap,
1626				       &spix->dirty_hw);
1627	REGION_EMPTY(draw->pScreen, &spix->dirty_hw);
1628    }
1629
1630    vsaa->is_master = FALSE;
1631}
1632
1633/*
1634 * *************************************************************************
1635 * Helpers for hosted.
1636 */
1637
1638#if (XA_TRACKER_VERSION_MAJOR >= 2) && defined(HAVE_LIBDRM_2_4_38)
1639
1640/**
1641 * vmwgfx_saa_copy_to_surface - Copy Drawable contents to an external surface.
1642 *
1643 * @pDraw: Pointer to source drawable.
1644 * @surface_fd: Prime file descriptor of external surface to copy to.
1645 * @dst_box: BoxRec describing the destination bounding box.
1646 * @region: Region of drawable to copy. Note: The code assumes that the
1647 * region is relative to the drawable origin, not the underlying pixmap
1648 * origin.
1649 *
1650 * Copies the contents (both software- and accelerated contents) to an
1651 * external surface.
1652 */
1653Bool
1654vmwgfx_saa_copy_to_surface(DrawablePtr pDraw, uint32_t surface_fd,
1655			   const BoxRec *dst_box, RegionPtr region)
1656{
1657    ScreenPtr pScreen = pDraw->pScreen;
1658    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(saa_get_driver(pScreen));
1659    PixmapPtr src;
1660    struct saa_pixmap *spix;
1661    struct vmwgfx_saa_pixmap *vpix;
1662    const BoxRec *box;
1663    int n;
1664    int sx, sy, dx, dy;
1665    struct xa_surface *dst;
1666    uint32_t handle;
1667    Bool ret = TRUE;
1668    RegionRec intersection;
1669    RegionPtr copy_region = region;
1670
1671    if (vmwgfx_prime_fd_to_handle(vsaa->drm_fd, surface_fd, &handle) < 0)
1672	return FALSE;
1673
1674    dst = xa_surface_from_handle(vsaa->xat, pDraw->width, pDraw->height,
1675				 pDraw->depth, xa_type_argb,
1676				 xa_format_unknown,
1677				 XA_FLAG_SHARED | XA_FLAG_RENDER_TARGET,
1678				 handle,
1679				 (pDraw->width * pDraw->bitsPerPixel + 7) / 8);
1680
1681    if (!dst) {
1682	ret = FALSE;
1683	goto out_no_surface;
1684    }
1685
1686    /*
1687     * Assume damage region is relative to the source window.
1688     */
1689    src = saa_get_pixmap(pDraw, &sx, &sy);
1690    sx += pDraw->x;
1691    sy += pDraw->y;
1692    if (sx || sy)
1693	REGION_TRANSLATE(pScreen, region, sx, sy);
1694
1695    dx = dst_box->x1 - sx;
1696    dy = dst_box->y1 - sy;
1697
1698    spix = saa_get_saa_pixmap(src);
1699    vpix = to_vmwgfx_saa_pixmap(spix);
1700
1701    /*
1702     * Make sure software contents of the source pixmap is henceforth put
1703     * in a GMR to avoid the extra copy in the xa DMA.
1704     */
1705    vmwgfx_prefer_gmr(vsaa, src);
1706
1707    /*
1708     * Determine the intersection between software contents and region to copy.
1709     */
1710
1711    if (vsaa->known_prime_format) {
1712	REGION_NULL(pScreen, &intersection);
1713	if (!vpix->hw)
1714	    REGION_COPY(pScreen, &intersection, region);
1715	else if (spix->damage && REGION_NOTEMPTY(pScreen, &spix->dirty_shadow))
1716	    REGION_INTERSECT(pScreen, &intersection, region, &spix->dirty_shadow);
1717
1718	/*
1719	 * DMA software contents directly into the destination. Then subtract
1720	 * the region we've DMA'd from the region to copy.
1721	 */
1722	if (REGION_NOTEMPTY(pScreen, &intersection)) {
1723	    if (vmwgfx_saa_dma(vsaa, src, &intersection, TRUE, dx, dy, dst)) {
1724		REGION_SUBTRACT(pScreen, &intersection, region, &intersection);
1725		copy_region = &intersection;
1726	    }
1727	}
1728    }
1729
1730    if (!REGION_NOTEMPTY(pScreen, copy_region))
1731	goto out_no_copy;
1732
1733    /*
1734     * Copy Hardware contents to the destination
1735     */
1736    box = REGION_RECTS(copy_region);
1737    n = REGION_NUM_RECTS(copy_region);
1738
1739    if (!vmwgfx_hw_accel_validate(src, 0, 0, 0, copy_region)) {
1740	ret = FALSE;
1741	goto out_no_copy;
1742    }
1743
1744    if (xa_copy_prepare(vsaa->xa_ctx, dst, vpix->hw) != XA_ERR_NONE) {
1745	ret = FALSE;
1746	goto out_no_copy;
1747    }
1748
1749    while(n--) {
1750	xa_copy(vsaa->xa_ctx, box->x1 + dx, box->y1 + dy, box->x1, box->y1,
1751		box->x2 - box->x1, box->y2 - box->y1);
1752	box++;
1753    }
1754
1755    xa_copy_done(vsaa->xa_ctx);
1756    xa_context_flush(vsaa->xa_ctx);
1757
1758  out_no_copy:
1759    if (vsaa->known_prime_format)
1760	REGION_UNINIT(pScreen, &intersection);
1761    if (sx || sy)
1762	REGION_TRANSLATE(pScreen, region, -sx, -sy);
1763    xa_surface_unref(dst);
1764  out_no_surface:
1765    vmwgfx_prime_release_handle(vsaa->drm_fd, handle);
1766
1767    return ret;
1768}
1769#endif
1770