lx_exa.c revision f29dbc25
1/*
2 * Copyright (c) 2007-2008 Advanced Micro Devices, 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 * 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 shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *
22 * Neither the name of the Advanced Micro Devices, Inc. nor the names of its
23 * contributors may be used to endorse or promote products derived from this
24 * software without specific prior written permission.
25 */
26
27/* TODO:
28   Support a8 as a source or destination?
29   convert !a8 or !a4 masks?
30   support multiple pass operations?
31*/
32
33/* To support PictOptAdd with a mask */
34
35#ifdef HAVE_CONFIG_H
36#include "config.h"
37#endif
38
39#include "xf86.h"
40#include "exa.h"
41
42#include "geode.h"
43#include "cim_defs.h"
44#include "cim_regs.h"
45
46#include "geode_blend.h"
47
48#define F(x)    IntToxFixed(x)
49#define I(x)    xFixedToInt(x)
50
51static const struct exa_format_t
52{
53    int exa;
54    int bpp;
55    int fmt;
56    int alphabits;
57} lx_exa_formats[] = {
58    {
59    PICT_a8r8g8b8, 32, CIMGP_SOURCE_FMT_8_8_8_8, 8}, {
60    PICT_x8r8g8b8, 32, CIMGP_SOURCE_FMT_8_8_8_8, 0}, {
61    PICT_x8b8g8r8, 32, CIMGP_SOURCE_FMT_32BPP_BGR, 0}, {
62    PICT_a4r4g4b4, 16, CIMGP_SOURCE_FMT_4_4_4_4, 4}, {
63    PICT_a1r5g5b5, 16, CIMGP_SOURCE_FMT_1_5_5_5, 1}, {
64    PICT_r5g6b5, 16, CIMGP_SOURCE_FMT_0_5_6_5, 0}, {
65    PICT_b5g6r5, 16, CIMGP_SOURCE_FMT_16BPP_BGR, 0}, {
66    PICT_x1r5g5b5, 16, CIMGP_SOURCE_FMT_1_5_5_5, 0}, {
67    PICT_x1b5g5r5, 16, CIMGP_SOURCE_FMT_15BPP_BGR, 0}, {
68    PICT_r3g3b2, 8, CIMGP_SOURCE_FMT_3_3_2, 0}
69};
70
71/* This is a chunk of memory we use for scratch space */
72
73#define COMP_TYPE_MASK 0
74#define COMP_TYPE_ONEPASS 1
75#define COMP_TYPE_TWOPASS 3
76#define COMP_TYPE_ROTATE  5
77
78static struct
79{
80    int type;
81
82    unsigned int srcOffset;
83    unsigned int srcPitch;
84    unsigned int srcBpp;
85    unsigned int srcWidth, srcHeight;
86    PixmapPtr srcPixmap;
87
88    unsigned int srcColor;
89    int op;
90    int repeat;
91    unsigned int fourBpp;
92    unsigned int bufferOffset;
93    struct exa_format_t *srcFormat;
94    struct exa_format_t *dstFormat;
95
96    int rotate;
97    PictTransform *transform;
98
99} exaScratch;
100
101static const int SDfn[16] = {
102    0x00, 0x88, 0x44, 0xCC, 0x22, 0xAA, 0x66, 0xEE,
103    0x11, 0x99, 0x55, 0xDD, 0x33, 0xBB, 0x77, 0xFF
104};
105
106static const int SDfn_PM[16] = {
107    0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
108    0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA
109};
110
111/* These functions check to see if we can safely prefetch the memory
112 * for the blt, or if we have to wait the previous blt to complete.
113 * One function is for the fill, and the other is for the copy because
114 * they have different requirements based on ROP
115 */
116
117static int lx0 = -1, ly0 = -1, lx1 = -1, ly1 = -1;
118
119static int
120lx_fill_flags(int x0, int y0, int w, int h, int rop)
121{
122    int x1 = x0 + w, y1 = y0 + h;
123    int n = ((rop ^ (rop >> 1)) & 0x55) == 0 ||	/* no dst */
124	x0 >= lx1 || y0 >= ly1 ||      /* rght/below */
125	x1 <= lx0 || y1 <= ly0 ?       /* left/above */
126	0 : CIMGP_BLTFLAGS_HAZARD;
127
128    lx0 = x0;
129    ly0 = y0;
130    lx1 = x1;
131    ly1 = y1;
132
133    return n;
134}
135
136static int
137lx_copy_flags(int x0, int y0, int x1, int y1, int w, int h, int rop)
138{
139    int x2 = x1 + w, y2 = y1 + h;
140
141    /* dst not hazzard and src not hazzard */
142    int n = (((rop ^ (rop >> 1)) & 0x55) == 0 ||
143	x1 >= lx1 || y1 >= ly1 ||
144	x2 <= lx0 || y2 <= ly0) &&
145	(((rop ^ (rop >> 2)) & 0x33) == 0 ||
146	x0 >= lx1 || y0 >= ly1 ||
147	x0 + w <= lx0 || y0 + h <= ly0) ? 0 : CIMGP_BLTFLAGS_HAZARD;
148
149    lx0 = x1;
150    ly0 = y1;
151    lx1 = x2;
152    ly1 = y2;
153
154    return n;
155}
156
157/* These are borrowed from the exa engine - they should be made global
158   and available to drivers, but until then....
159*/
160
161/* exaGetPixelFromRGBA (exa_render.c) */
162
163static Bool
164_GetPixelFromRGBA(CARD32 * pixel,
165    CARD16 red, CARD16 green, CARD16 blue, CARD16 alpha, CARD32 format)
166{
167    int rbits, bbits, gbits, abits;
168    int rshift, bshift, gshift, ashift;
169
170    *pixel = 0;
171
172    if (!PICT_FORMAT_COLOR(format))
173	return FALSE;
174
175    rbits = PICT_FORMAT_R(format);
176    gbits = PICT_FORMAT_G(format);
177    bbits = PICT_FORMAT_B(format);
178    abits = PICT_FORMAT_A(format);
179
180    if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ARGB) {
181	bshift = 0;
182	gshift = bbits;
183	rshift = gshift + gbits;
184	ashift = rshift + rbits;
185    } else {			       /* PICT_TYPE_ABGR */
186	rshift = 0;
187	gshift = rbits;
188	bshift = gshift + gbits;
189	ashift = bshift + bbits;
190    }
191
192    *pixel |= (blue >> (16 - bbits)) << bshift;
193    *pixel |= (red >> (16 - rbits)) << rshift;
194    *pixel |= (green >> (16 - gbits)) << gshift;
195    *pixel |= (alpha >> (16 - abits)) << ashift;
196
197    return TRUE;
198}
199
200/* exaGetRGBAFromPixel (exa_render.c) */
201
202static Bool
203_GetRGBAFromPixel(CARD32 pixel,
204    CARD16 * red,
205    CARD16 * green, CARD16 * blue, CARD16 * alpha, CARD32 format)
206{
207    int rbits, bbits, gbits, abits;
208    int rshift, bshift, gshift, ashift;
209
210    if (!PICT_FORMAT_COLOR(format))
211	return FALSE;
212
213    rbits = PICT_FORMAT_R(format);
214    gbits = PICT_FORMAT_G(format);
215    bbits = PICT_FORMAT_B(format);
216    abits = PICT_FORMAT_A(format);
217
218    if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ARGB) {
219	bshift = 0;
220	gshift = bbits;
221	rshift = gshift + gbits;
222	ashift = rshift + rbits;
223    } else {			       /* PICT_TYPE_ABGR */
224	rshift = 0;
225	gshift = rbits;
226	bshift = gshift + gbits;
227	ashift = bshift + bbits;
228    }
229
230    *red = ((pixel >> rshift) & ((1 << rbits) - 1)) << (16 - rbits);
231    while (rbits < 16) {
232	*red |= *red >> rbits;
233	rbits <<= 1;
234    }
235
236    *green = ((pixel >> gshift) & ((1 << gbits) - 1)) << (16 - gbits);
237    while (gbits < 16) {
238	*green |= *green >> gbits;
239	gbits <<= 1;
240    }
241
242    *blue = ((pixel >> bshift) & ((1 << bbits) - 1)) << (16 - bbits);
243    while (bbits < 16) {
244	*blue |= *blue >> bbits;
245	bbits <<= 1;
246    }
247
248    if (abits) {
249	*alpha = ((pixel >> ashift) & ((1 << abits) - 1)) << (16 - abits);
250	while (abits < 16) {
251	    *alpha |= *alpha >> abits;
252	    abits <<= 1;
253	}
254    } else
255	*alpha = 0xffff;
256
257    return TRUE;
258}
259
260static unsigned int
261lx_get_source_color(PixmapPtr pSrc, int srcFormat, int dstFormat)
262{
263    CARD32 in, out;
264    CARD16 red = 0, green = 0, blue = 0, alpha = 0;
265
266    /* Stall to avoid a race with the upload function */
267    /* for 1.4 and newer, the problem will be resolved within
268     * exaGetPixmapFirstPixel, so this should be adjusted so
269     * the stall isn't run needlessly
270     */
271
272    gp_wait_until_idle();
273    in = exaGetPixmapFirstPixel(pSrc);
274
275    _GetRGBAFromPixel(in, &red, &blue, &green, &alpha, srcFormat);
276    _GetPixelFromRGBA(&out, red, blue, green, alpha, dstFormat);
277
278    return out;
279}
280
281static Bool
282lx_prepare_solid(PixmapPtr pxMap, int alu, Pixel planemask, Pixel fg)
283{
284    int pitch = exaGetPixmapPitch(pxMap);
285    int op = (planemask == ~0U) ? SDfn[alu] : SDfn_PM[alu];
286
287    gp_declare_blt(0);
288    gp_set_bpp(pxMap->drawable.bitsPerPixel);
289
290    gp_set_raster_operation(op);
291
292    if (planemask != ~0U)
293	gp_set_solid_pattern(planemask);
294
295    exaScratch.op = op;
296
297    gp_set_solid_source(fg);
298
299    gp_set_strides(pitch, pitch);
300    gp_write_parameters();
301    return TRUE;
302}
303
304static void
305lx_do_solid(PixmapPtr pxMap, int x1, int y1, int x2, int y2)
306{
307    int bpp = (pxMap->drawable.bitsPerPixel + 7) / 8;
308    int pitch = exaGetPixmapPitch(pxMap);
309    unsigned int offset =
310	exaGetPixmapOffset(pxMap) + (pitch * y1) + (bpp * x1);
311
312    gp_declare_blt(lx_fill_flags(x1, y1, x2 - x1, y2 - y1, exaScratch.op));
313    gp_pattern_fill(offset, x2 - x1, y2 - y1);
314}
315
316static Bool
317lx_prepare_copy(PixmapPtr pxSrc, PixmapPtr pxDst, int dx, int dy,
318    int alu, Pixel planemask)
319{
320    int dpitch = exaGetPixmapPitch(pxDst);
321    int op = (planemask == ~0U) ? SDfn[alu] : SDfn_PM[alu];
322
323    gp_declare_blt(0);
324    gp_set_bpp(pxDst->drawable.bitsPerPixel);
325
326    gp_set_raster_operation(op);
327
328    if (planemask != ~0U)
329	gp_set_solid_pattern(planemask);
330
331    exaScratch.srcOffset = exaGetPixmapOffset(pxSrc);
332    exaScratch.srcPitch = exaGetPixmapPitch(pxSrc);
333    exaScratch.srcBpp = (pxSrc->drawable.bitsPerPixel + 7) / 8;
334
335    exaScratch.op = op;
336
337    gp_set_strides(dpitch, exaScratch.srcPitch);
338    gp_write_parameters();
339    return TRUE;
340}
341
342static void
343lx_do_copy(PixmapPtr pxDst, int srcX, int srcY,
344    int dstX, int dstY, int w, int h)
345{
346    int dstBpp = (pxDst->drawable.bitsPerPixel + 7) / 8;
347    int dstPitch = exaGetPixmapPitch(pxDst);
348    unsigned int srcOffset, dstOffset;
349    int flags = 0;
350
351    gp_declare_blt(lx_copy_flags(srcX, srcY, dstX, dstY, w, h,
352	    exaScratch.op));
353
354    srcOffset = exaScratch.srcOffset + (exaScratch.srcPitch * srcY) +
355	(exaScratch.srcBpp) * srcX;
356
357    dstOffset = exaGetPixmapOffset(pxDst) +
358	(dstPitch * dstY) + (dstBpp * dstX);
359
360    if (dstX > srcX)
361	flags |= CIMGP_NEGXDIR;
362
363    if (dstY > srcY)
364	flags |= CIMGP_NEGYDIR;
365
366    gp_screen_to_screen_blt(dstOffset, srcOffset, w, h, flags);
367}
368
369/* Composite operations
370
371These are the simplest - one pass operations - if there is no format or
372mask, the we can make these happen pretty fast
373
374                       Operation  Type  Channel   Alpha
375PictOpClear            0          2     0         3
376PictOpSrc              0          3     0         3
377PictOpDst              0          3     1         3
378PictOpOver             2          0     0         3
379PictOpOverReverse      2          0     1         3
380PictOpIn               0          1     0         3
381PictOpInReverse        0          1     1         3
382PictOpOut              1          0     0         3
383PictOpOutReverse       1          0     1         3
384PictOpAdd              2          2     0         3
385
386The following require multiple passes
387PictOpAtop
388PictOpXor
389*/
390
391struct blend_ops_t
392{
393    int operation;
394    int type;
395    int channel;
396} lx_alpha_ops[] = {
397    /* PictOpClear */
398    {
399    CIMGP_ALPHA_TIMES_A, CIMGP_CONSTANT_ALPHA, CIMGP_CHANNEL_A_SOURCE}, {
400    },
401	/* PictOpSrc */
402    {
403    CIMGP_ALPHA_TIMES_A, CIMGP_ALPHA_EQUALS_ONE, CIMGP_CHANNEL_A_SOURCE}, {
404    },
405	/* PictOpDst */
406    {
407    CIMGP_ALPHA_TIMES_A, CIMGP_ALPHA_EQUALS_ONE, CIMGP_CHANNEL_A_DEST}, {
408    },
409	/* PictOpOver */
410    {
411    CIMGP_ALPHA_A_PLUS_BETA_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_SOURCE},
412    {
413    },
414	/* PictOpOverReverse */
415    {
416    CIMGP_ALPHA_A_PLUS_BETA_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_DEST}, {
417    },
418	/* PictOpIn */
419    {
420    CIMGP_ALPHA_TIMES_A, CIMGP_CHANNEL_B_ALPHA, CIMGP_CHANNEL_A_SOURCE}, {
421    },
422	/* PictOpInReverse */
423    {
424    CIMGP_ALPHA_TIMES_A, CIMGP_CHANNEL_B_ALPHA, CIMGP_CHANNEL_A_DEST}, {
425    },
426	/* PictOpOut */
427    {
428    CIMGP_BETA_TIMES_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_SOURCE}, {
429    },
430	/* PictOpOutReverse */
431    {
432    CIMGP_BETA_TIMES_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_DEST}, {
433    },
434	/* SrcAtop */
435    {
436    CIMGP_ALPHA_TIMES_A, CIMGP_CHANNEL_B_ALPHA, CIMGP_CHANNEL_A_DEST}, {
437    CIMGP_BETA_TIMES_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_SOURCE},
438	/* SrcAtopReverse */
439    {
440    CIMGP_ALPHA_TIMES_A, CIMGP_CHANNEL_B_ALPHA, CIMGP_CHANNEL_A_SOURCE}, {
441    CIMGP_BETA_TIMES_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_DEST},
442	/* Xor */
443    {
444    CIMGP_BETA_TIMES_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_SOURCE}, {
445    CIMGP_BETA_TIMES_B, CIMGP_CHANNEL_A_ALPHA, CIMGP_CHANNEL_A_SOURCE},
446	/* PictOpAdd */
447    {
448    CIMGP_A_PLUS_BETA_B, CIMGP_CONSTANT_ALPHA, CIMGP_CHANNEL_A_SOURCE}, {
449    }
450};
451
452#define ARRAY_SIZE(a) (sizeof((a)) / (sizeof(*(a))))
453
454static const struct exa_format_t *
455lx_get_format(PicturePtr p)
456{
457    int i;
458    unsigned int format = p->format;
459
460    for (i = 0; i < ARRAY_SIZE(lx_exa_formats); i++) {
461
462	if (lx_exa_formats[i].bpp < PICT_FORMAT_BPP(format))
463	    break;
464	else if (lx_exa_formats[i].bpp != PICT_FORMAT_BPP(format))
465	    continue;
466
467	if (lx_exa_formats[i].exa == format)
468	    return (&lx_exa_formats[i]);
469    }
470
471    return NULL;
472}
473
474static Bool
475lx_process_transform(PicturePtr pSrc)
476{
477    PictTransformPtr t = pSrc->transform;
478    xFixed c0 = t->matrix[0][0];
479    xFixed s0 = t->matrix[0][1];
480    xFixed s1 = t->matrix[1][0];
481    xFixed c1 = t->matrix[1][1];
482
483    /* If the transform doesn't have any rotation
484     * or scaling components, then just grab the
485     * translate coordinates */
486
487    if (t->matrix[0][0] == 0 &&
488	t->matrix[0][1] == 0 &&
489	t->matrix[1][0] == 0 && t->matrix[1][1] == 0) {
490	exaScratch.transform = pSrc->transform;
491	return TRUE;
492    }
493
494    /* Otherwise, see if this is a simple
495     * rotate transform - if it isn't, then
496     * we have to punt back to software */
497
498    if (t->matrix[2][2] != F(1))
499	return FALSE;
500
501    /* The rotate matrix looks like this:
502     * [ cos X   -sin x
503     * sin X   cos X ]
504     *
505     * Where X is the angle.  We do a simple
506     * check first - if [0,0] != [1,1], then
507     * scaling was specified too, and we can
508     * bail, and if [0,1] != -[1,1] then this
509     * isn't scaling that we can handle.
510     */
511
512    if ((c0 != c1) || (s0 != -s1))
513	return FALSE;
514
515    /* Now, figure out what angle we want - we
516     * can only accelerate right angle rotations,
517     * so this turns into an easy set of if statements */
518
519    if (c0 == F(1) && s1 == F(0))
520	exaScratch.rotate = RR_Rotate_0;
521    else if (c0 == F(0) && s1 == F(1))
522	exaScratch.rotate = RR_Rotate_90;
523    else if (c0 == F(-1) && s1 == F(0))
524	exaScratch.rotate = RR_Rotate_180;
525    else if (c0 == F(0) && s1 == F(-1))
526	exaScratch.rotate = RR_Rotate_270;
527    else
528	return FALSE;
529
530    exaScratch.transform = pSrc->transform;
531
532    return TRUE;
533}
534
535static Bool
536lx_check_composite(int op, PicturePtr pSrc, PicturePtr pMsk, PicturePtr pDst)
537{
538    GeodeRec *pGeode = GEODEPTR_FROM_PICTURE(pDst);
539
540    /* Check that the operation is supported */
541
542    if (op > PictOpAdd)
543	return FALSE;
544
545    if (usesPasses(op)) {
546	if (pGeode->exaBfrOffset == 0 || !pMsk)
547	    return FALSE;
548    }
549
550    if (pMsk && op == PictOpAdd)
551	return FALSE;
552
553    /* Check that the filter matches what we support */
554
555    switch (pSrc->filter) {
556    case PictFilterNearest:
557    case PictFilterFast:
558    case PictFilterGood:
559    case PictFilterBest:
560	break;
561
562    default:
563	/* WE don't support bilinear or convolution filters */
564	return FALSE;
565    }
566
567    /* We don't support any mask transforms */
568    if (pMsk && pMsk->transform)
569	return FALSE;
570
571    /* Keep an eye out for source rotation transforms - those we can
572     * do something about */
573
574    exaScratch.rotate = RR_Rotate_0;
575    exaScratch.transform = NULL;
576
577    if (pSrc->transform && !lx_process_transform(pSrc))
578	return FALSE;
579
580    /* XXX - I don't understand PICT_a8 enough - so I'm punting */
581
582    if (pSrc->format == PICT_a8 || pDst->format == PICT_a8)
583	return FALSE;
584
585    if (pMsk && op != PictOpClear) {
586	/* We can only do masks with a 8bpp or a 4bpp mask */
587	if (pMsk->format != PICT_a8 && pMsk->format != PICT_a4)
588	    return FALSE;
589    }
590
591    return TRUE;
592}
593
594static Bool
595lx_prepare_composite(int op, PicturePtr pSrc, PicturePtr pMsk,
596    PicturePtr pDst, PixmapPtr pxSrc, PixmapPtr pxMsk, PixmapPtr pxDst)
597{
598    GeodeRec *pGeode = GEODEPTR_FROM_PIXMAP(pxDst);
599    const struct exa_format_t *srcFmt, *dstFmt;
600
601    /* Get the formats for the source and destination */
602
603    if ((srcFmt = lx_get_format(pSrc)) == NULL) {
604	ErrorF("EXA: Invalid source format %x\n", pSrc->format);
605	return FALSE;
606    }
607
608    if ((dstFmt = lx_get_format(pDst)) == NULL) {
609	ErrorF("EXA:  Invalid destination format %x\n", pDst->format);
610	return FALSE;
611    }
612
613    /* Make sure operations that need alpha bits have them */
614    /* If a mask is enabled, the alpha will come from there */
615
616    if (!pMsk && (!srcFmt->alphabits && usesSrcAlpha(op)))
617	return FALSE;
618
619    if (!pMsk && (!dstFmt->alphabits && usesDstAlpha(op)))
620	return FALSE;
621
622    /* FIXME:  See a way around this! */
623
624    if (srcFmt->alphabits == 0 && dstFmt->alphabits != 0)
625	return FALSE;
626
627    /* If this is a rotate operation, then make sure the src and dst
628     * formats are the same */
629
630    if (exaScratch.rotate != RR_Rotate_0 && srcFmt != dstFmt) {
631	ErrorF("EXA: Can't rotate and convert formats at the same time\n");
632	return FALSE;
633    }
634
635    /* Set up the scratch buffer with the information we need */
636
637    exaScratch.srcFormat = (struct exa_format_t *)srcFmt;
638    exaScratch.dstFormat = (struct exa_format_t *)dstFmt;
639    exaScratch.op = op;
640    exaScratch.repeat = pSrc->repeat;
641    exaScratch.bufferOffset = pGeode->exaBfrOffset;
642
643    if (pMsk && op != PictOpClear) {
644	struct blend_ops_t *opPtr = &lx_alpha_ops[op * 2];
645	int direction = (opPtr->channel == CIMGP_CHANNEL_A_SOURCE) ? 0 : 1;
646
647	/* Direction 0 indicates src->dst, 1 indiates dst->src */
648
649	if (((direction == 0) && (pxSrc->drawable.bitsPerPixel < 16)) ||
650	    ((direction == 1) && (pxDst->drawable.bitsPerPixel < 16))) {
651	    ErrorF("Can't do mask blending with less then 16bpp\n");
652	    return FALSE;
653	}
654
655	/* Get the source color */
656
657	if (direction == 0)
658	    exaScratch.srcColor = lx_get_source_color(pxSrc, pSrc->format,
659		pDst->format);
660	else
661	    exaScratch.srcColor = lx_get_source_color(pxDst, pDst->format,
662		pSrc->format);
663
664	/* FIXME:  What to do here? */
665
666	if (pSrc->pDrawable->width != 1 || pSrc->pDrawable->height != 1)
667	    return FALSE;
668
669	/* Save off the info we need (reuse the source values to save space) */
670
671	exaScratch.type = COMP_TYPE_MASK;
672
673	exaScratch.srcOffset = exaGetPixmapOffset(pxMsk);
674	exaScratch.srcPitch = exaGetPixmapPitch(pxMsk);
675	exaScratch.srcBpp = (pxMsk->drawable.bitsPerPixel + 7) / 8;
676
677	exaScratch.srcWidth = pMsk->pDrawable->width;
678	exaScratch.srcHeight = pMsk->pDrawable->height;
679
680	/* Flag to indicate if this a 8BPP or a 4BPP mask */
681	exaScratch.fourBpp = (pxMsk->drawable.bitsPerPixel == 4) ? 1 : 0;
682
683	/* If the direction is reversed, then remember the source */
684
685	if (direction == 1)
686	    exaScratch.srcPixmap = pxSrc;
687    } else {
688	if (usesPasses(op))
689	    exaScratch.type = COMP_TYPE_TWOPASS;
690	else if (exaScratch.rotate != RR_Rotate_0)
691	    exaScratch.type = COMP_TYPE_ROTATE;
692	else
693	    exaScratch.type = COMP_TYPE_ONEPASS;
694
695	exaScratch.srcOffset = exaGetPixmapOffset(pxSrc);
696	exaScratch.srcPitch = exaGetPixmapPitch(pxSrc);
697	exaScratch.srcBpp = (pxSrc->drawable.bitsPerPixel + 7) / 8;
698
699	exaScratch.srcWidth = pSrc->pDrawable->width;
700	exaScratch.srcHeight = pSrc->pDrawable->height;
701    }
702
703    return TRUE;
704}
705
706static int
707lx_get_bpp_from_format(int format)
708{
709
710    switch (format) {
711    case CIMGP_SOURCE_FMT_8_8_8_8:
712    case CIMGP_SOURCE_FMT_32BPP_BGR:
713	return 32;
714
715    case CIMGP_SOURCE_FMT_4_4_4_4:
716	return 12;
717
718    case CIMGP_SOURCE_FMT_0_5_6_5:
719    case CIMGP_SOURCE_FMT_16BPP_BGR:
720	return 16;
721
722    case CIMGP_SOURCE_FMT_1_5_5_5:
723    case CIMGP_SOURCE_FMT_15BPP_BGR:
724	return 15;
725
726    case CIMGP_SOURCE_FMT_3_3_2:
727	return 8;
728    }
729
730    return 0;
731}
732
733/* BGR needs to be set in the source for it to take - so adjust the source
734 * to enable BGR if the two formats are different, and disable it if they
735 * are the same
736 */
737
738static void
739lx_set_source_format(int srcFormat, int dstFormat)
740{
741    if (!(srcFormat & 0x10) && (dstFormat & 0x10))
742	gp_set_source_format(srcFormat | 0x10);
743    else if ((srcFormat & 0x10) && (dstFormat & 0x10))
744	gp_set_source_format(srcFormat & ~0x10);
745    else
746	gp_set_source_format(srcFormat);
747}
748
749/* If we are converting colors and we need the channel A alpha,
750 * then use a special alpha type that preserves the alpha before
751 * converting the format
752 */
753
754static inline int
755get_op_type(struct exa_format_t *src, struct exa_format_t *dst, int type)
756{
757    return (type == CIMGP_CHANNEL_A_ALPHA &&
758	src->alphabits != dst->alphabits) ? CIMGP_CONVERTED_ALPHA : type;
759}
760
761/* Note - this is the preferred onepass method.  The other will remain
762 * ifdefed out until such time that we are sure its not needed
763 */
764
765static void
766lx_composite_onepass(PixmapPtr pxDst, unsigned long dstOffset,
767    unsigned long srcOffset, int width, int height)
768{
769    struct blend_ops_t *opPtr;
770    int apply, type;
771
772    opPtr = &lx_alpha_ops[exaScratch.op * 2];
773
774    apply = (exaScratch.dstFormat->alphabits != 0 &&
775	exaScratch.srcFormat->alphabits != 0) ?
776	CIMGP_APPLY_BLEND_TO_ALL : CIMGP_APPLY_BLEND_TO_RGB;
777
778    gp_declare_blt(0);
779    gp_set_bpp(lx_get_bpp_from_format(exaScratch.dstFormat->fmt));
780    gp_set_strides(exaGetPixmapPitch(pxDst), exaScratch.srcPitch);
781
782    lx_set_source_format(exaScratch.srcFormat->fmt,
783	exaScratch.dstFormat->fmt);
784
785    type =
786	get_op_type(exaScratch.srcFormat, exaScratch.dstFormat, opPtr->type);
787
788    gp_set_alpha_operation(opPtr->operation, type, opPtr->channel, apply, 0);
789
790    gp_screen_to_screen_convert(dstOffset, srcOffset, width, height, 0);
791}
792
793/* This function handles the multipass blend functions */
794
795static void
796lx_composite_multipass(PixmapPtr pxDst, unsigned long dstOffset,
797    unsigned long srcOffset, int width, int height)
798{
799    struct blend_ops_t *opPtr;
800    int sbpp = lx_get_bpp_from_format(exaScratch.srcFormat->fmt);
801    int apply, type;
802
803    /* Wait until the GP is idle - this will ensure that the scratch buffer
804     * isn't occupied */
805
806    gp_wait_until_idle();
807
808    /* Copy the destination to the scratch buffer, and convert it to the
809     * source format */
810
811    gp_declare_blt(0);
812
813    gp_set_bpp(sbpp);
814    gp_set_source_format(exaScratch.dstFormat->fmt);
815    gp_set_raster_operation(0xCC);
816    gp_set_strides(exaScratch.srcPitch, exaGetPixmapPitch(pxDst));
817    gp_screen_to_screen_convert(exaScratch.bufferOffset, dstOffset,
818	width, height, 0);
819
820    /* Do the first blend from the source to the scratch buffer */
821
822    gp_declare_blt(CIMGP_BLTFLAGS_HAZARD);
823    gp_set_bpp(sbpp);
824    gp_set_source_format(exaScratch.srcFormat->fmt);
825    gp_set_strides(exaScratch.srcPitch, exaScratch.srcPitch);
826
827    opPtr = &lx_alpha_ops[exaScratch.op * 2];
828
829    apply = (exaScratch.srcFormat->alphabits == 0) ?
830	CIMGP_APPLY_BLEND_TO_RGB : CIMGP_APPLY_BLEND_TO_ALL;
831
832    /* If we're destroying the source alpha bits, then make sure we
833     * use the alpha before the color conversion
834     */
835
836    gp_screen_to_screen_blt(exaScratch.bufferOffset, srcOffset, width, height,
837	0);
838
839    /* Finally, do the second blend back to the destination */
840
841    opPtr = &lx_alpha_ops[(exaScratch.op * 2) + 1];
842
843    apply = (exaScratch.dstFormat->alphabits == 0) ?
844	CIMGP_APPLY_BLEND_TO_RGB : CIMGP_APPLY_BLEND_TO_ALL;
845
846    gp_declare_blt(CIMGP_BLTFLAGS_HAZARD);
847    gp_set_bpp(lx_get_bpp_from_format(exaScratch.dstFormat->fmt));
848
849    lx_set_source_format(exaScratch.srcFormat->fmt,
850	exaScratch.dstFormat->fmt);
851
852    type =
853	get_op_type(exaScratch.srcFormat, exaScratch.dstFormat, opPtr->type);
854
855    gp_set_alpha_operation(opPtr->operation, type, opPtr->channel, apply, 0);
856
857    gp_screen_to_screen_convert(dstOffset, exaScratch.bufferOffset,
858	width, height, 0);
859}
860
861static void
862lx_composite_rotate(PixmapPtr pxDst, unsigned long dstOffset,
863    unsigned int srcOffset, int width, int height)
864{
865    int degrees = 0;
866
867    gp_declare_blt(0);
868    gp_set_bpp(lx_get_bpp_from_format(exaScratch.dstFormat->fmt));
869    gp_set_strides(exaGetPixmapPitch(pxDst), exaScratch.srcPitch);
870
871    lx_set_source_format(exaScratch.srcFormat->fmt,
872	exaScratch.dstFormat->fmt);
873
874    gp_set_raster_operation(0xCC);
875
876    /* RandR rotation is counter-clockwise, our rotation
877     * is clockwise, so adjust the numbers accordingly */
878
879    switch (exaScratch.rotate) {
880    case RR_Rotate_90:
881	degrees = 270;
882	break;
883    case RR_Rotate_180:
884	degrees = 180;
885	break;
886    case RR_Rotate_270:
887	degrees = 90;
888	break;
889    }
890
891    gp_rotate_blt(dstOffset, srcOffset, width, height, degrees);
892}
893
894static void
895lx_do_composite_mask(PixmapPtr pxDst, unsigned long dstOffset,
896    unsigned int maskOffset, int width, int height)
897{
898    struct blend_ops_t *opPtr = &lx_alpha_ops[exaScratch.op * 2];
899
900    gp_declare_blt(0);
901
902    gp_set_source_format(exaScratch.srcFormat->fmt);
903    gp_set_strides(exaGetPixmapPitch(pxDst), exaScratch.srcPitch);
904    gp_set_bpp(lx_get_bpp_from_format(exaScratch.dstFormat->fmt));
905    gp_set_solid_source(exaScratch.srcColor);
906
907    gp_blend_mask_blt(dstOffset, 0, width, height, maskOffset,
908	exaScratch.srcPitch, opPtr->operation, exaScratch.fourBpp);
909}
910
911#define GetPixmapOffset(px, x, y) ( exaGetPixmapOffset((px)) + \
912  (exaGetPixmapPitch((px)) * (y)) + \
913  ((((px)->drawable.bitsPerPixel + 7) / 8) * (x)) )
914
915#define GetSrcOffset(_x, _y) (exaScratch.srcOffset + ((_y) * exaScratch.srcPitch) + \
916			      ((_x) * exaScratch.srcBpp))
917
918static void
919transformPoint(PictTransform * t, xPointFixed * point)
920{
921    PictVector v;
922
923    v.vector[0] = point->x;
924    v.vector[1] = point->y;
925    v.vector[2] = xFixed1;
926
927    if (t != NULL)
928	PictureTransformPoint(t, &v);
929
930    point->x = v.vector[0];
931    point->y = v.vector[1];
932}
933
934static void
935lx_do_composite(PixmapPtr pxDst, int srcX, int srcY, int maskX,
936    int maskY, int dstX, int dstY, int width, int height)
937{
938    struct blend_ops_t *opPtr = &lx_alpha_ops[exaScratch.op * 2];
939
940    unsigned int dstOffset, srcOffset = 0;
941
942    xPointFixed srcPoint;
943
944    int opX = dstX;
945    int opY = dstY;
946    int opWidth = width;
947    int opHeight = height;
948
949    /* Transform the source coordinates */
950
951    if (exaScratch.type == COMP_TYPE_MASK) {
952	srcPoint.x = F(maskX);
953	srcPoint.y = F(maskY);
954    } else {
955	srcPoint.x = F(srcX);
956	srcPoint.y = F(srcY);
957    }
958
959    /* srcX, srcY point to the upper right side of the bounding box
960     * in the unrotated coordinate space.  Depending on the orientation,
961     * we have to translate the coordinates to point to the origin of
962     * the rectangle in the source pixmap */
963
964    switch (exaScratch.rotate) {
965    case RR_Rotate_270:
966	srcPoint.x += F(width);
967
968	opWidth = height;
969	opHeight = width;
970	break;
971
972    case RR_Rotate_180:
973	srcPoint.x += F(width);
974	srcPoint.y += F(height);
975
976	srcX += width;
977	srcY += height;
978	break;
979
980    case RR_Rotate_90:
981	srcPoint.y += F(height);
982
983	opWidth = height;
984	opHeight = width;
985	break;
986    }
987
988    transformPoint(exaScratch.transform, &srcPoint);
989
990    /* Adjust the point to fit into the pixmap */
991
992    if (I(srcPoint.x) < 0) {
993	opWidth += I(srcPoint.x);
994	srcPoint.x = F(0);
995    }
996
997    if (I(srcPoint.y) < 0) {
998	opHeight += I(srcPoint.y);
999	srcPoint.y = F(0);
1000    }
1001
1002    srcOffset = GetSrcOffset(I(srcPoint.x), I(srcPoint.y));
1003
1004    if (exaScratch.srcWidth < opWidth)
1005	opWidth = exaScratch.srcWidth;
1006
1007    if (exaScratch.srcHeight < opHeight)
1008	opHeight = exaScratch.srcHeight;
1009
1010    while (1) {
1011
1012	dstOffset = GetPixmapOffset(pxDst, opX, opY);
1013
1014	switch (exaScratch.type) {
1015
1016	case COMP_TYPE_MASK:{
1017		int direction =
1018		    (opPtr->channel == CIMGP_CHANNEL_A_SOURCE) ? 0 : 1;
1019
1020		if (direction == 1) {
1021		    dstOffset =
1022			GetPixmapOffset(exaScratch.srcPixmap, opX, opY);
1023		    lx_do_composite_mask(exaScratch.srcPixmap, dstOffset,
1024			srcOffset, opWidth, opHeight);
1025		} else {
1026		    lx_do_composite_mask(pxDst, dstOffset, srcOffset, opWidth,
1027			opHeight);
1028		}
1029	    }
1030	    break;
1031
1032	case COMP_TYPE_ONEPASS:
1033	    lx_composite_onepass(pxDst, dstOffset, srcOffset, opWidth,
1034		opHeight);
1035	    break;
1036
1037	case COMP_TYPE_TWOPASS:
1038	    lx_composite_multipass(pxDst, dstOffset, srcOffset, opWidth,
1039		opHeight);
1040
1041	case COMP_TYPE_ROTATE:
1042	    lx_composite_rotate(pxDst, dstOffset, srcOffset, opWidth,
1043		opHeight);
1044	    break;
1045	}
1046
1047	if (!exaScratch.repeat)
1048	    break;
1049
1050	opX += opWidth;
1051
1052	if (opX >= dstX + width) {
1053	    opX = dstX;
1054	    opY += opHeight;
1055
1056	    if (opY >= dstY + height)
1057		break;
1058	}
1059
1060	opWidth = ((dstX + width) - opX) > exaScratch.srcWidth ?
1061	    exaScratch.srcWidth : (dstX + width) - opX;
1062	opHeight = ((dstY + height) - opY) > exaScratch.srcHeight ?
1063	    exaScratch.srcHeight : (dstY + height) - opY;
1064    }
1065}
1066
1067static void
1068lx_wait_marker(ScreenPtr PScreen, int marker)
1069{
1070    gp_wait_until_idle();
1071}
1072
1073static void
1074lx_done(PixmapPtr ptr)
1075{
1076}
1077
1078#if 0
1079static void
1080lx_upload_to_screen(PixmapPtr pxDst, int x, int y, int w, int h,
1081    char *src, int src_pitch)
1082{
1083    GeodeRec *pGeode = GEODEPTR_FROM_PIXMAP(pxDst);
1084    int dst_pitch = exaGetPixmapPitch(pxDst);
1085    int cpp = (pxDst->drawable.bitsPerPixel + 7) / 8;
1086
1087    char *dst;
1088    int offset = exaGetPixmapOffset(pxDst);
1089
1090    dst = (char *)(pGeode->FBBase + offset + (y * dst_pitch) + (x * cpp));
1091    int i;
1092
1093    for (i = 0; i < h; i++) {
1094	memcpy(dst, src, w * cpp);
1095	dst += dst_pitch;
1096	src += src_pitch;
1097    }
1098}
1099#endif
1100
1101#if EXA_VERSION_MINOR >= 2
1102
1103static Bool
1104lx_exa_pixmap_is_offscreen(PixmapPtr pPixmap)
1105{
1106    ScrnInfoPtr pScrni = xf86Screens[pPixmap->drawable.pScreen->myNum];
1107    GeodeRec *pGeode = GEODEPTR(pScrni);
1108    void *start = (void *)(pGeode->FBBase);
1109    void *end =
1110	(void *)(pGeode->FBBase + pGeode->offscreenStart +
1111	pGeode->offscreenSize);
1112
1113    if ((void *)pPixmap->devPrivate.ptr >= start &&
1114	(void *)pPixmap->devPrivate.ptr < end)
1115	return TRUE;
1116
1117    return FALSE;
1118}
1119
1120#endif
1121
1122Bool
1123LXExaInit(ScreenPtr pScreen)
1124{
1125    ScrnInfoPtr pScrni = xf86Screens[pScreen->myNum];
1126    GeodeRec *pGeode = GEODEPTR(pScrni);
1127    ExaDriverPtr pExa = pGeode->pExa;
1128
1129    pExa->exa_major = EXA_VERSION_MAJOR;
1130    pExa->exa_minor = EXA_VERSION_MINOR;
1131
1132    pExa->WaitMarker = lx_wait_marker;
1133
1134    pExa->PrepareSolid = lx_prepare_solid;
1135    pExa->Solid = lx_do_solid;
1136    pExa->DoneSolid = lx_done;
1137
1138    pExa->PrepareCopy = lx_prepare_copy;
1139    pExa->Copy = lx_do_copy;
1140    pExa->DoneCopy = lx_done;
1141
1142    /* Composite */
1143    pExa->CheckComposite = lx_check_composite;
1144    pExa->PrepareComposite = lx_prepare_composite;
1145    pExa->Composite = lx_do_composite;
1146    pExa->DoneComposite = lx_done;
1147    //pExa->UploadToScreen =  lx_upload_to_screen;
1148
1149#if EXA_VERSION_MINOR >= 2
1150    pExa->PixmapIsOffscreen = lx_exa_pixmap_is_offscreen;
1151#endif
1152
1153    //pExa->flags = EXA_OFFSCREEN_PIXMAPS;
1154
1155    return exaDriverInit(pScreen, pGeode->pExa);
1156}
1157