1/*
2 * Copyright © 2006 Intel Corporation
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 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * 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 NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 *    Eric Anholt <eric@anholt.net>
25 *
26 */
27
28#ifdef HAVE_DIX_CONFIG_H
29#include <dix-config.h>
30#endif
31
32#include "ephyr.h"
33#include "exa_priv.h"
34#include "fbpict.h"
35
36#define EPHYR_TRACE_DRAW 0
37
38#if EPHYR_TRACE_DRAW
39#define TRACE_DRAW() ErrorF("%s\n", __FUNCTION__);
40#else
41#define TRACE_DRAW() do { } while (0)
42#endif
43
44/* Use some oddball alignments, to expose issues in alignment handling in EXA. */
45#define EPHYR_OFFSET_ALIGN	24
46#define EPHYR_PITCH_ALIGN	24
47
48#define EPHYR_OFFSCREEN_SIZE	(16 * 1024 * 1024)
49#define EPHYR_OFFSCREEN_BASE	(1 * 1024 * 1024)
50
51/**
52 * Forces a real devPrivate.ptr for hidden pixmaps, so that we can call down to
53 * fb functions.
54 */
55static void
56ephyrPreparePipelinedAccess(PixmapPtr pPix, int index)
57{
58    KdScreenPriv(pPix->drawable.pScreen);
59    KdScreenInfo *screen = pScreenPriv->screen;
60    EphyrScrPriv *scrpriv = screen->driver;
61    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
62
63    assert(fakexa->saved_ptrs[index] == NULL);
64    fakexa->saved_ptrs[index] = pPix->devPrivate.ptr;
65
66    if (pPix->devPrivate.ptr != NULL)
67        return;
68
69    pPix->devPrivate.ptr = fakexa->exa->memoryBase + exaGetPixmapOffset(pPix);
70}
71
72/**
73 * Restores the original devPrivate.ptr of the pixmap from before we messed with
74 * it.
75 */
76static void
77ephyrFinishPipelinedAccess(PixmapPtr pPix, int index)
78{
79    KdScreenPriv(pPix->drawable.pScreen);
80    KdScreenInfo *screen = pScreenPriv->screen;
81    EphyrScrPriv *scrpriv = screen->driver;
82    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
83
84    pPix->devPrivate.ptr = fakexa->saved_ptrs[index];
85    fakexa->saved_ptrs[index] = NULL;
86}
87
88/**
89 * Sets up a scratch GC for fbFill, and saves other parameters for the
90 * ephyrSolid implementation.
91 */
92static Bool
93ephyrPrepareSolid(PixmapPtr pPix, int alu, Pixel pm, Pixel fg)
94{
95    ScreenPtr pScreen = pPix->drawable.pScreen;
96
97    KdScreenPriv(pScreen);
98    KdScreenInfo *screen = pScreenPriv->screen;
99    EphyrScrPriv *scrpriv = screen->driver;
100    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
101    ChangeGCVal tmpval[3];
102
103    ephyrPreparePipelinedAccess(pPix, EXA_PREPARE_DEST);
104
105    fakexa->pDst = pPix;
106    fakexa->pGC = GetScratchGC(pPix->drawable.depth, pScreen);
107
108    tmpval[0].val = alu;
109    tmpval[1].val = pm;
110    tmpval[2].val = fg;
111    ChangeGC(NullClient, fakexa->pGC, GCFunction | GCPlaneMask | GCForeground,
112             tmpval);
113
114    ValidateGC(&pPix->drawable, fakexa->pGC);
115
116    TRACE_DRAW();
117
118    return TRUE;
119}
120
121/**
122 * Does an fbFill of the rectangle to be drawn.
123 */
124static void
125ephyrSolid(PixmapPtr pPix, int x1, int y1, int x2, int y2)
126{
127    ScreenPtr pScreen = pPix->drawable.pScreen;
128
129    KdScreenPriv(pScreen);
130    KdScreenInfo *screen = pScreenPriv->screen;
131    EphyrScrPriv *scrpriv = screen->driver;
132    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
133
134    fbFill(&fakexa->pDst->drawable, fakexa->pGC, x1, y1, x2 - x1, y2 - y1);
135}
136
137/**
138 * Cleans up the scratch GC created in ephyrPrepareSolid.
139 */
140static void
141ephyrDoneSolid(PixmapPtr pPix)
142{
143    ScreenPtr pScreen = pPix->drawable.pScreen;
144
145    KdScreenPriv(pScreen);
146    KdScreenInfo *screen = pScreenPriv->screen;
147    EphyrScrPriv *scrpriv = screen->driver;
148    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
149
150    FreeScratchGC(fakexa->pGC);
151
152    ephyrFinishPipelinedAccess(pPix, EXA_PREPARE_DEST);
153}
154
155/**
156 * Sets up a scratch GC for fbCopyArea, and saves other parameters for the
157 * ephyrCopy implementation.
158 */
159static Bool
160ephyrPrepareCopy(PixmapPtr pSrc, PixmapPtr pDst, int dx, int dy, int alu,
161                 Pixel pm)
162{
163    ScreenPtr pScreen = pDst->drawable.pScreen;
164
165    KdScreenPriv(pScreen);
166    KdScreenInfo *screen = pScreenPriv->screen;
167    EphyrScrPriv *scrpriv = screen->driver;
168    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
169    ChangeGCVal tmpval[2];
170
171    ephyrPreparePipelinedAccess(pDst, EXA_PREPARE_DEST);
172    ephyrPreparePipelinedAccess(pSrc, EXA_PREPARE_SRC);
173
174    fakexa->pSrc = pSrc;
175    fakexa->pDst = pDst;
176    fakexa->pGC = GetScratchGC(pDst->drawable.depth, pScreen);
177
178    tmpval[0].val = alu;
179    tmpval[1].val = pm;
180    ChangeGC(NullClient, fakexa->pGC, GCFunction | GCPlaneMask, tmpval);
181
182    ValidateGC(&pDst->drawable, fakexa->pGC);
183
184    TRACE_DRAW();
185
186    return TRUE;
187}
188
189/**
190 * Does an fbCopyArea to take care of the requested copy.
191 */
192static void
193ephyrCopy(PixmapPtr pDst, int srcX, int srcY, int dstX, int dstY, int w, int h)
194{
195    ScreenPtr pScreen = pDst->drawable.pScreen;
196
197    KdScreenPriv(pScreen);
198    KdScreenInfo *screen = pScreenPriv->screen;
199    EphyrScrPriv *scrpriv = screen->driver;
200    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
201
202    fbCopyArea(&fakexa->pSrc->drawable, &fakexa->pDst->drawable, fakexa->pGC,
203               srcX, srcY, w, h, dstX, dstY);
204}
205
206/**
207 * Cleans up the scratch GC created in ephyrPrepareCopy.
208 */
209static void
210ephyrDoneCopy(PixmapPtr pDst)
211{
212    ScreenPtr pScreen = pDst->drawable.pScreen;
213
214    KdScreenPriv(pScreen);
215    KdScreenInfo *screen = pScreenPriv->screen;
216    EphyrScrPriv *scrpriv = screen->driver;
217    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
218
219    FreeScratchGC(fakexa->pGC);
220
221    ephyrFinishPipelinedAccess(fakexa->pSrc, EXA_PREPARE_SRC);
222    ephyrFinishPipelinedAccess(fakexa->pDst, EXA_PREPARE_DEST);
223}
224
225/**
226 * Reports that we can always accelerate the given operation.  This may not be
227 * desirable from an EXA testing standpoint -- testing the fallback paths would
228 * be useful, too.
229 */
230static Bool
231ephyrCheckComposite(int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture,
232                    PicturePtr pDstPicture)
233{
234    /* Exercise the component alpha helper, so fail on this case like a normal
235     * driver
236     */
237    if (pMaskPicture && pMaskPicture->componentAlpha && op == PictOpOver)
238        return FALSE;
239
240    return TRUE;
241}
242
243/**
244 * Saves off the parameters for ephyrComposite.
245 */
246static Bool
247ephyrPrepareComposite(int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture,
248                      PicturePtr pDstPicture, PixmapPtr pSrc, PixmapPtr pMask,
249                      PixmapPtr pDst)
250{
251    KdScreenPriv(pDst->drawable.pScreen);
252    KdScreenInfo *screen = pScreenPriv->screen;
253    EphyrScrPriv *scrpriv = screen->driver;
254    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
255
256    ephyrPreparePipelinedAccess(pDst, EXA_PREPARE_DEST);
257    if (pSrc != NULL)
258        ephyrPreparePipelinedAccess(pSrc, EXA_PREPARE_SRC);
259    if (pMask != NULL)
260        ephyrPreparePipelinedAccess(pMask, EXA_PREPARE_MASK);
261
262    fakexa->op = op;
263    fakexa->pSrcPicture = pSrcPicture;
264    fakexa->pMaskPicture = pMaskPicture;
265    fakexa->pDstPicture = pDstPicture;
266    fakexa->pSrc = pSrc;
267    fakexa->pMask = pMask;
268    fakexa->pDst = pDst;
269
270    TRACE_DRAW();
271
272    return TRUE;
273}
274
275/**
276 * Does an fbComposite to complete the requested drawing operation.
277 */
278static void
279ephyrComposite(PixmapPtr pDst, int srcX, int srcY, int maskX, int maskY,
280               int dstX, int dstY, int w, int h)
281{
282    KdScreenPriv(pDst->drawable.pScreen);
283    KdScreenInfo *screen = pScreenPriv->screen;
284    EphyrScrPriv *scrpriv = screen->driver;
285    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
286
287    fbComposite(fakexa->op, fakexa->pSrcPicture, fakexa->pMaskPicture,
288                fakexa->pDstPicture, srcX, srcY, maskX, maskY, dstX, dstY,
289                w, h);
290}
291
292static void
293ephyrDoneComposite(PixmapPtr pDst)
294{
295    KdScreenPriv(pDst->drawable.pScreen);
296    KdScreenInfo *screen = pScreenPriv->screen;
297    EphyrScrPriv *scrpriv = screen->driver;
298    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
299
300    if (fakexa->pMask != NULL)
301        ephyrFinishPipelinedAccess(fakexa->pMask, EXA_PREPARE_MASK);
302    if (fakexa->pSrc != NULL)
303        ephyrFinishPipelinedAccess(fakexa->pSrc, EXA_PREPARE_SRC);
304    ephyrFinishPipelinedAccess(fakexa->pDst, EXA_PREPARE_DEST);
305}
306
307/**
308 * Does fake acceleration of DownloadFromScren using memcpy.
309 */
310static Bool
311ephyrDownloadFromScreen(PixmapPtr pSrc, int x, int y, int w, int h, char *dst,
312                        int dst_pitch)
313{
314    KdScreenPriv(pSrc->drawable.pScreen);
315    KdScreenInfo *screen = pScreenPriv->screen;
316    EphyrScrPriv *scrpriv = screen->driver;
317    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
318    unsigned char *src;
319    int src_pitch, cpp;
320
321    if (pSrc->drawable.bitsPerPixel < 8)
322        return FALSE;
323
324    ephyrPreparePipelinedAccess(pSrc, EXA_PREPARE_SRC);
325
326    cpp = pSrc->drawable.bitsPerPixel / 8;
327    src_pitch = exaGetPixmapPitch(pSrc);
328    src = fakexa->exa->memoryBase + exaGetPixmapOffset(pSrc);
329    src += y * src_pitch + x * cpp;
330
331    for (; h > 0; h--) {
332        memcpy(dst, src, w * cpp);
333        dst += dst_pitch;
334        src += src_pitch;
335    }
336
337    exaMarkSync(pSrc->drawable.pScreen);
338
339    ephyrFinishPipelinedAccess(pSrc, EXA_PREPARE_SRC);
340
341    return TRUE;
342}
343
344/**
345 * Does fake acceleration of UploadToScreen using memcpy.
346 */
347static Bool
348ephyrUploadToScreen(PixmapPtr pDst, int x, int y, int w, int h, char *src,
349                    int src_pitch)
350{
351    KdScreenPriv(pDst->drawable.pScreen);
352    KdScreenInfo *screen = pScreenPriv->screen;
353    EphyrScrPriv *scrpriv = screen->driver;
354    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
355    unsigned char *dst;
356    int dst_pitch, cpp;
357
358    if (pDst->drawable.bitsPerPixel < 8)
359        return FALSE;
360
361    ephyrPreparePipelinedAccess(pDst, EXA_PREPARE_DEST);
362
363    cpp = pDst->drawable.bitsPerPixel / 8;
364    dst_pitch = exaGetPixmapPitch(pDst);
365    dst = fakexa->exa->memoryBase + exaGetPixmapOffset(pDst);
366    dst += y * dst_pitch + x * cpp;
367
368    for (; h > 0; h--) {
369        memcpy(dst, src, w * cpp);
370        dst += dst_pitch;
371        src += src_pitch;
372    }
373
374    exaMarkSync(pDst->drawable.pScreen);
375
376    ephyrFinishPipelinedAccess(pDst, EXA_PREPARE_DEST);
377
378    return TRUE;
379}
380
381static Bool
382ephyrPrepareAccess(PixmapPtr pPix, int index)
383{
384    /* Make sure we don't somehow end up with a pointer that is in framebuffer
385     * and hasn't been readied for us.
386     */
387    assert(pPix->devPrivate.ptr != NULL);
388
389    return TRUE;
390}
391
392/**
393 * In fakexa, we currently only track whether we have synced to the latest
394 * "accelerated" drawing that has happened or not.  It's not used for anything
395 * yet.
396 */
397static int
398ephyrMarkSync(ScreenPtr pScreen)
399{
400    KdScreenPriv(pScreen);
401    KdScreenInfo *screen = pScreenPriv->screen;
402    EphyrScrPriv *scrpriv = screen->driver;
403    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
404
405    fakexa->is_synced = FALSE;
406
407    return 0;
408}
409
410/**
411 * Assumes that we're waiting on the latest marker.  When EXA gets smarter and
412 * starts using markers in a fine-grained way (for example, waiting on drawing
413 * to required pixmaps to complete, rather than waiting for all drawing to
414 * complete), we'll want to make the ephyrMarkSync/ephyrWaitMarker
415 * implementation fine-grained as well.
416 */
417static void
418ephyrWaitMarker(ScreenPtr pScreen, int marker)
419{
420    KdScreenPriv(pScreen);
421    KdScreenInfo *screen = pScreenPriv->screen;
422    EphyrScrPriv *scrpriv = screen->driver;
423    EphyrFakexaPriv *fakexa = scrpriv->fakexa;
424
425    fakexa->is_synced = TRUE;
426}
427
428/**
429 * This function initializes EXA to use the fake acceleration implementation
430 * which just falls through to software.  The purpose is to have a reliable,
431 * correct driver with which to test changes to the EXA core.
432 */
433Bool
434ephyrDrawInit(ScreenPtr pScreen)
435{
436    KdScreenPriv(pScreen);
437    KdScreenInfo *screen = pScreenPriv->screen;
438    EphyrScrPriv *scrpriv = screen->driver;
439    EphyrPriv *priv = screen->card->driver;
440    EphyrFakexaPriv *fakexa;
441    Bool success;
442
443    fakexa = calloc(1, sizeof(*fakexa));
444    if (fakexa == NULL)
445        return FALSE;
446
447    fakexa->exa = exaDriverAlloc();
448    if (fakexa->exa == NULL) {
449        free(fakexa);
450        return FALSE;
451    }
452
453    fakexa->exa->memoryBase = (CARD8 *) (priv->base);
454    fakexa->exa->memorySize = priv->bytes_per_line * ephyrBufferHeight(screen);
455    fakexa->exa->offScreenBase = priv->bytes_per_line * screen->height;
456
457    /* Since we statically link against EXA, we shouldn't have to be smart about
458     * versioning.
459     */
460    fakexa->exa->exa_major = 2;
461    fakexa->exa->exa_minor = 0;
462
463    fakexa->exa->PrepareSolid = ephyrPrepareSolid;
464    fakexa->exa->Solid = ephyrSolid;
465    fakexa->exa->DoneSolid = ephyrDoneSolid;
466
467    fakexa->exa->PrepareCopy = ephyrPrepareCopy;
468    fakexa->exa->Copy = ephyrCopy;
469    fakexa->exa->DoneCopy = ephyrDoneCopy;
470
471    fakexa->exa->CheckComposite = ephyrCheckComposite;
472    fakexa->exa->PrepareComposite = ephyrPrepareComposite;
473    fakexa->exa->Composite = ephyrComposite;
474    fakexa->exa->DoneComposite = ephyrDoneComposite;
475
476    fakexa->exa->DownloadFromScreen = ephyrDownloadFromScreen;
477    fakexa->exa->UploadToScreen = ephyrUploadToScreen;
478
479    fakexa->exa->MarkSync = ephyrMarkSync;
480    fakexa->exa->WaitMarker = ephyrWaitMarker;
481
482    fakexa->exa->PrepareAccess = ephyrPrepareAccess;
483
484    fakexa->exa->pixmapOffsetAlign = EPHYR_OFFSET_ALIGN;
485    fakexa->exa->pixmapPitchAlign = EPHYR_PITCH_ALIGN;
486
487    fakexa->exa->maxX = 1023;
488    fakexa->exa->maxY = 1023;
489
490    fakexa->exa->flags = EXA_OFFSCREEN_PIXMAPS;
491
492    success = exaDriverInit(pScreen, fakexa->exa);
493    if (success) {
494        ErrorF("Initialized fake EXA acceleration\n");
495        scrpriv->fakexa = fakexa;
496    }
497    else {
498        ErrorF("Failed to initialize EXA\n");
499        free(fakexa->exa);
500        free(fakexa);
501    }
502
503    return success;
504}
505
506void
507ephyrDrawEnable(ScreenPtr pScreen)
508{
509}
510
511void
512ephyrDrawDisable(ScreenPtr pScreen)
513{
514}
515
516void
517ephyrDrawFini(ScreenPtr pScreen)
518{
519}
520
521/**
522 * exaDDXDriverInit is required by the top-level EXA module, and is used by
523 * the xorg DDX to hook in its EnableDisableFB wrapper.  We don't need it, since
524 * we won't be enabling/disabling the FB.
525 */
526void
527exaDDXDriverInit(ScreenPtr pScreen)
528{
529    ExaScreenPriv(pScreen);
530
531    pExaScr->migration = ExaMigrationSmart;
532    pExaScr->checkDirtyCorrectness = TRUE;
533}
534