1/*
2 * Copyright 2008 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23/** \file qxl_driver.c
24 * \author Adam Jackson <ajax@redhat.com>
25 * \author Søren Sandmann <sandmann@redhat.com>
26 *
27 * This is qxl, a driver for the Qumranet paravirtualized graphics device
28 * in qemu.
29 */
30
31
32#ifdef HAVE_CONFIG_H
33#include "config.h"
34#endif
35
36#include "qxl.h"
37#include "dfps.h"
38#include <spice/protocol.h>
39
40#if HAS_DEVPRIVATEKEYREC
41DevPrivateKeyRec uxa_pixmap_index;
42#else
43int uxa_pixmap_index;
44#endif
45
46static Bool
47qxl_prepare_access (PixmapPtr pixmap, RegionPtr region, uxa_access_t access)
48{
49    return qxl_surface_prepare_access (get_surface (pixmap),
50                                       pixmap, region, access);
51}
52
53static void
54qxl_finish_access (PixmapPtr pixmap)
55{
56    qxl_surface_finish_access (get_surface (pixmap), pixmap);
57}
58
59static Bool
60qxl_pixmap_is_offscreen (PixmapPtr pixmap)
61{
62    return !!get_surface (pixmap);
63}
64
65static Bool
66good_alu_and_pm (DrawablePtr drawable, int alu, Pixel planemask)
67{
68    if (!UXA_PM_IS_SOLID (drawable, planemask))
69	return FALSE;
70
71    if (alu != GXcopy)
72	return FALSE;
73
74    return TRUE;
75}
76
77/*
78 * Solid fill
79 */
80static Bool
81qxl_check_solid (DrawablePtr drawable, int alu, Pixel planemask)
82{
83    if (!good_alu_and_pm (drawable, alu, planemask))
84	return FALSE;
85
86    return TRUE;
87}
88
89static Bool
90qxl_prepare_solid (PixmapPtr pixmap, int alu, Pixel planemask, Pixel fg)
91{
92    qxl_surface_t *surface;
93
94    if (!(surface = get_surface (pixmap)))
95	return FALSE;
96
97    return qxl_surface_prepare_solid (surface, fg);
98}
99
100static void
101qxl_solid (PixmapPtr pixmap, int x1, int y1, int x2, int y2)
102{
103    qxl_surface_solid (get_surface (pixmap), x1, y1, x2, y2);
104}
105
106static void
107qxl_done_solid (PixmapPtr pixmap)
108{
109}
110
111/*
112 * Copy
113 */
114static Bool
115qxl_check_copy (PixmapPtr source, PixmapPtr dest,
116                int alu, Pixel planemask)
117{
118    if (!good_alu_and_pm ((DrawablePtr)source, alu, planemask))
119	return FALSE;
120
121    if (source->drawable.bitsPerPixel != dest->drawable.bitsPerPixel)
122    {
123	ErrorF ("differing bitsperpixel - this shouldn't happen\n");
124	return FALSE;
125    }
126
127    return TRUE;
128}
129
130static Bool
131qxl_prepare_copy (PixmapPtr source, PixmapPtr dest,
132                  int xdir, int ydir, int alu,
133                  Pixel planemask)
134{
135    return qxl_surface_prepare_copy (get_surface (dest), get_surface (source));
136}
137
138static void
139qxl_copy (PixmapPtr dest,
140          int src_x1, int src_y1,
141          int dest_x1, int dest_y1,
142          int width, int height)
143{
144    qxl_surface_copy (get_surface (dest),
145                      src_x1, src_y1,
146                      dest_x1, dest_y1,
147                      width, height);
148}
149
150static void
151qxl_done_copy (PixmapPtr dest)
152{
153}
154
155/*
156 * Composite
157 */
158static Bool
159can_accelerate_picture (qxl_screen_t *qxl, PicturePtr pict)
160{
161    if (!pict)
162	return TRUE;
163
164    if (pict->format != PICT_a8r8g8b8		&&
165	pict->format != PICT_x8r8g8b8		&&
166	pict->format != PICT_a8)
167    {
168        if (qxl->debug_render_fallbacks)
169        {
170            ErrorF ("Image with format %x can't be accelerated \n",
171                    pict->format);
172        }
173
174        return FALSE;
175    }
176
177    if (!pict->pDrawable)
178    {
179        if (qxl->debug_render_fallbacks)
180        {
181            ErrorF ("Source image (of type %d) can't be accelerated\n",
182                    pict->pSourcePict->type);
183        }
184
185	return FALSE;
186    }
187
188    if (pict->transform)
189    {
190	if (pict->transform->matrix[2][0] != 0	||
191	    pict->transform->matrix[2][1] != 0	||
192	    pict->transform->matrix[2][2] != pixman_int_to_fixed (1))
193	{
194            if (qxl->debug_render_fallbacks)
195                ErrorF ("Image with non-affine transform can't be accelerated\n");
196
197	    return FALSE;
198	}
199    }
200
201    if (pict->filter != PictFilterBilinear	&&
202	pict->filter != PictFilterNearest)
203    {
204        if (qxl->debug_render_fallbacks)
205        {
206            ErrorF ("Image with filter type %d can't be accelerated\n",
207                    pict->filter);
208        }
209
210        return FALSE;
211    }
212
213    return TRUE;
214}
215
216#define QXL_HAS_CAP(qxl, cap)						\
217    (((qxl)->rom->client_capabilities[(cap) / 8]) & (1 << ((cap) % 8)))
218
219static Bool
220qxl_has_composite (qxl_screen_t *qxl)
221{
222#ifdef XF86DRM_MODE
223    if (qxl->kms_enabled) {
224#if 0 /* KMS Composite support seems broken - needs better hw support */
225	static Bool result, checked;
226	if (!checked) {
227	    result = qxl_kms_check_cap(qxl, SPICE_DISPLAY_CAP_COMPOSITE);
228	    checked = TRUE;
229	}
230	return result;
231#else
232	return FALSE;
233#endif
234    }
235#endif
236#ifndef XSPICE
237    return
238	qxl->pci->revision >= 4			&&
239	QXL_HAS_CAP (qxl, SPICE_DISPLAY_CAP_COMPOSITE);
240#else
241    /* FIXME */
242    return FALSE;
243#endif
244}
245
246static Bool
247qxl_has_a8_surfaces (qxl_screen_t *qxl)
248{
249#ifdef XF86DRM_MODE
250    if (qxl->kms_enabled) {
251#if 0 /* KMS Composite support seems broken - needs better hw support */
252        static Bool result, checked;
253	if (!checked) {
254            result = qxl_kms_check_cap(qxl, SPICE_DISPLAY_CAP_A8_SURFACE);
255	    checked = TRUE;
256	}
257	return result;
258#else
259	return FALSE;
260#endif
261    }
262#endif
263#ifndef XSPICE
264    if (qxl->pci->revision < 4)
265    {
266        if (qxl->debug_render_fallbacks)
267        {
268            ErrorF ("No a8 surface due to revision being %d, which is < 4\n",
269                    qxl->pci->revision);
270        }
271
272        return FALSE;
273    }
274
275    if (!QXL_HAS_CAP (qxl, SPICE_DISPLAY_CAP_COMPOSITE))
276    {
277        if (qxl->debug_render_fallbacks)
278        {
279            ErrorF ("No composite due to client not providing SPICE_DISPLAY_CAP_A8_SURFACE\n");
280        }
281
282        return FALSE;
283    }
284
285    return TRUE;
286
287#else
288    /* FIXME */
289    return FALSE;
290#endif
291}
292
293static Bool
294qxl_check_composite (int op,
295		     PicturePtr pSrcPicture,
296		     PicturePtr pMaskPicture,
297		     PicturePtr pDstPicture,
298		     int width, int height)
299{
300    int i;
301    ScreenPtr pScreen = pDstPicture->pDrawable->pScreen;
302    ScrnInfoPtr pScrn = xf86ScreenToScrn (pScreen);
303    qxl_screen_t *qxl = pScrn->driverPrivate;
304
305    static const int accelerated_ops[] =
306    {
307	PictOpClear, PictOpSrc, PictOpDst, PictOpOver, PictOpOverReverse,
308	PictOpIn, PictOpInReverse, PictOpOut, PictOpOutReverse,
309	PictOpAtop, PictOpAtopReverse, PictOpXor, PictOpAdd,
310	PictOpSaturate, PictOpMultiply, PictOpScreen, PictOpOverlay,
311	PictOpDarken, PictOpLighten, PictOpColorDodge, PictOpColorBurn,
312	PictOpHardLight, PictOpSoftLight, PictOpDifference, PictOpExclusion,
313	PictOpHSLHue, PictOpHSLSaturation, PictOpHSLColor, PictOpHSLLuminosity,
314    };
315
316    if (!qxl_has_composite (qxl))
317	return FALSE;
318
319    if (!can_accelerate_picture (qxl, pSrcPicture)	||
320	!can_accelerate_picture (qxl, pMaskPicture)	||
321	!can_accelerate_picture (qxl, pDstPicture))
322    {
323	return FALSE;
324    }
325
326    for (i = 0; i < sizeof (accelerated_ops) / sizeof (accelerated_ops[0]); ++i)
327    {
328	if (accelerated_ops[i] == op)
329	    goto found;
330    }
331
332    if (qxl->debug_render_fallbacks)
333        ErrorF ("Compositing operator %d can't be accelerated\n", op);
334
335    return FALSE;
336
337found:
338    return TRUE;
339}
340
341static Bool
342qxl_check_composite_target (PixmapPtr pixmap)
343{
344    return TRUE;
345}
346
347static Bool
348qxl_check_composite_texture (ScreenPtr screen,
349			     PicturePtr pPicture)
350{
351    return TRUE;
352}
353
354static Bool
355qxl_prepare_composite (int op,
356		       PicturePtr pSrcPicture,
357		       PicturePtr pMaskPicture,
358		       PicturePtr pDstPicture,
359		       PixmapPtr pSrc,
360		       PixmapPtr pMask,
361		       PixmapPtr pDst)
362{
363    return qxl_surface_prepare_composite (
364	op, pSrcPicture, pMaskPicture, pDstPicture,
365	get_surface (pSrc),
366	pMask? get_surface (pMask) : NULL,
367	get_surface (pDst));
368}
369
370static void
371qxl_composite (PixmapPtr pDst,
372	       int src_x, int src_y,
373	       int mask_x, int mask_y,
374	       int dst_x, int dst_y,
375	       int width, int height)
376{
377    qxl_surface_composite (
378	get_surface (pDst),
379	src_x, src_y,
380	mask_x, mask_y,
381	dst_x, dst_y, width, height);
382}
383
384static void
385qxl_done_composite (PixmapPtr pDst)
386{
387    ;
388}
389
390static Bool
391qxl_put_image (PixmapPtr pDst, int x, int y, int w, int h,
392               char *src, int src_pitch)
393{
394    qxl_surface_t *surface = get_surface (pDst);
395
396    if (surface)
397	return qxl_surface_put_image (surface, x, y, w, h, src, src_pitch);
398
399    return FALSE;
400}
401
402static void
403qxl_set_screen_pixmap (PixmapPtr pixmap)
404{
405    pixmap->drawable.pScreen->devPrivate = pixmap;
406}
407
408static PixmapPtr
409qxl_create_pixmap (ScreenPtr screen, int w, int h, int depth, unsigned usage)
410{
411    ScrnInfoPtr    scrn = xf86ScreenToScrn (screen);
412    PixmapPtr      pixmap;
413    qxl_screen_t * qxl = scrn->driverPrivate;
414    qxl_surface_t *surface;
415
416    if (w > 32767 || h > 32767)
417	return NULL;
418
419    qxl_surface_cache_sanity_check (qxl->surface_cache);
420
421#if 0
422    ErrorF ("Create pixmap: %d %d @ %d (usage: %d)\n", w, h, depth, usage);
423#endif
424
425    if (qxl->kms_enabled)
426	goto fallback;
427    if (uxa_swapped_out (screen))
428	goto fallback;
429
430    if (depth == 8 && !qxl_has_a8_surfaces (qxl))
431    {
432	/* FIXME: When we detect a _change_ in the property of having a8
433	 * surfaces, we should copy all existing a8 surface to host memory
434	 * and then destroy the ones on the device.
435	 */
436	goto fallback;
437    }
438
439    if (!w || !h)
440      goto fallback;
441
442    surface = qxl->bo_funcs->create_surface (qxl, w, h, depth);
443    if (surface)
444    {
445	/* ErrorF ("   Successfully created surface in video memory\n"); */
446
447	pixmap = fbCreatePixmap (screen, 0, 0, depth, usage);
448
449	screen->ModifyPixmapHeader (pixmap, w, h,
450	                            -1, -1, -1,
451	                            NULL);
452
453#if 0
454	ErrorF ("Create pixmap %p with surface %p\n", pixmap, surface);
455#endif
456	set_surface (pixmap, surface);
457	qxl_surface_set_pixmap (surface, pixmap);
458
459	qxl_surface_cache_sanity_check (qxl->surface_cache);
460    }
461    else
462    {
463#if 0
464	ErrorF ("   Couldn't allocate %d x %d @ %d surface in video memory\n",
465	        w, h, depth);
466#endif
467    fallback:
468	pixmap = fbCreatePixmap (screen, w, h, depth, usage);
469
470#if 0
471	ErrorF ("Create pixmap %p without surface\n", pixmap);
472#endif
473    }
474
475    return pixmap;
476}
477
478static Bool
479qxl_destroy_pixmap (PixmapPtr pixmap)
480{
481    ScreenPtr      screen = pixmap->drawable.pScreen;
482    ScrnInfoPtr    scrn = xf86ScreenToScrn (screen);
483    qxl_screen_t * qxl = scrn->driverPrivate;
484    qxl_surface_t *surface = NULL;
485
486    qxl_surface_cache_sanity_check (qxl->surface_cache);
487
488    if (pixmap->refcnt == 1)
489    {
490	surface = get_surface (pixmap);
491
492#if 0
493	ErrorF ("- Destroy %p (had surface %p)\n", pixmap, surface);
494#endif
495
496	if (surface)
497	{
498	    qxl->bo_funcs->destroy_surface(surface);
499	    set_surface (pixmap, NULL);
500
501	    qxl_surface_cache_sanity_check (qxl->surface_cache);
502	}
503    }
504
505    fbDestroyPixmap (pixmap);
506    return TRUE;
507}
508
509static void
510set_uxa_functions(qxl_screen_t *qxl, ScreenPtr screen)
511{
512    /* Solid fill */
513    qxl->uxa->check_solid = qxl_check_solid;
514    qxl->uxa->prepare_solid = qxl_prepare_solid;
515    qxl->uxa->solid = qxl_solid;
516    qxl->uxa->done_solid = qxl_done_solid;
517
518    /* Copy */
519    qxl->uxa->check_copy = qxl_check_copy;
520    qxl->uxa->prepare_copy = qxl_prepare_copy;
521    qxl->uxa->copy = qxl_copy;
522    qxl->uxa->done_copy = qxl_done_copy;
523
524    /* Composite */
525    qxl->uxa->check_composite = qxl_check_composite;
526    qxl->uxa->check_composite_target = qxl_check_composite_target;
527    qxl->uxa->check_composite_texture = qxl_check_composite_texture;
528    qxl->uxa->prepare_composite = qxl_prepare_composite;
529    qxl->uxa->composite = qxl_composite;
530    qxl->uxa->done_composite = qxl_done_composite;
531
532    /* PutImage */
533    qxl->uxa->put_image = qxl_put_image;
534
535    /* Prepare access */
536    qxl->uxa->prepare_access = qxl_prepare_access;
537    qxl->uxa->finish_access = qxl_finish_access;
538
539    qxl->uxa->pixmap_is_offscreen = qxl_pixmap_is_offscreen;
540
541    screen->SetScreenPixmap = qxl_set_screen_pixmap;
542    screen->CreatePixmap = qxl_create_pixmap;
543    screen->DestroyPixmap = qxl_destroy_pixmap;
544}
545
546Bool
547qxl_uxa_init (qxl_screen_t *qxl, ScreenPtr screen)
548{
549    ScrnInfoPtr scrn = xf86ScreenToScrn (screen);
550
551#if HAS_DIXREGISTERPRIVATEKEY
552    if (!dixRegisterPrivateKey (&uxa_pixmap_index, PRIVATE_PIXMAP, 0))
553	return FALSE;
554#else
555    if (!dixRequestPrivate (&uxa_pixmap_index, 0))
556	return FALSE;
557#endif
558
559    qxl->uxa = uxa_driver_alloc ();
560    if (qxl->uxa == NULL)
561	return FALSE;
562
563    memset (qxl->uxa, 0, sizeof (*qxl->uxa));
564
565    qxl->uxa->uxa_major = 1;
566    qxl->uxa->uxa_minor = 0;
567
568    if (qxl->deferred_fps)
569        dfps_set_uxa_functions(qxl, screen);
570    else
571        set_uxa_functions(qxl, screen);
572
573    if (!uxa_driver_init (screen, qxl->uxa))
574    {
575	xf86DrvMsg (scrn->scrnIndex, X_ERROR,
576	            "UXA initialization failed\n");
577	free (qxl->uxa);
578	return FALSE;
579    }
580
581#if 0
582    uxa_set_fallback_debug (screen, FALSE);
583#endif
584
585#if 0
586    if (!uxa_driver_init (screen, qxl->uxa))
587	return FALSE;
588#endif
589
590    return TRUE;
591}
592