diff options
Diffstat (limited to 'src/gallium/state_trackers/xorg/xorg_exa.c')
-rw-r--r-- | src/gallium/state_trackers/xorg/xorg_exa.c | 534 |
1 files changed, 534 insertions, 0 deletions
diff --git a/src/gallium/state_trackers/xorg/xorg_exa.c b/src/gallium/state_trackers/xorg/xorg_exa.c new file mode 100644 index 00000000000..ac0bfc88a4d --- /dev/null +++ b/src/gallium/state_trackers/xorg/xorg_exa.c | |||
@@ -0,0 +1,534 @@ | |||
1 | /* | ||
2 | * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. | ||
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 | * | ||
26 | * Author: Alan Hourihane <alanh@tungstengraphics.com> | ||
27 | * Author: Jakob Bornecrantz <wallbraker@gmail.com> | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | /* FIXME ! */ | ||
32 | #define DRI_DRIVER_PATH "/ISO/X.Org/modular/i386/lib/dri" | ||
33 | |||
34 | #include "xf86.h" | ||
35 | //#include "xf86_OSproc.h" | ||
36 | #include "xorg_tracker.h" | ||
37 | |||
38 | //#include "pipe/p_winsys.h" | ||
39 | #include "pipe/p_format.h" | ||
40 | #include "pipe/p_context.h" | ||
41 | #include "pipe/p_state.h" | ||
42 | #include "pipe/p_inlines.h" | ||
43 | |||
44 | struct exa_entity | ||
45 | { | ||
46 | ExaDriverPtr pExa; | ||
47 | struct pipe_context *ctx; | ||
48 | struct pipe_screen *scrn; | ||
49 | }; | ||
50 | |||
51 | struct PixmapPriv | ||
52 | { | ||
53 | int flags; | ||
54 | struct pipe_texture *tex; | ||
55 | unsigned int color; | ||
56 | struct pipe_surface *src_surf; /* for copies */ | ||
57 | struct pipe_transfer *map_transfer; | ||
58 | }; | ||
59 | |||
60 | /* | ||
61 | * Helper functions | ||
62 | */ | ||
63 | |||
64 | static enum pipe_format | ||
65 | exa_get_pipe_format(int depth) | ||
66 | { | ||
67 | switch (depth) { | ||
68 | case 32: | ||
69 | return PIPE_FORMAT_A8R8G8B8_UNORM; | ||
70 | case 24: | ||
71 | return PIPE_FORMAT_X8R8G8B8_UNORM; | ||
72 | case 16: | ||
73 | return PIPE_FORMAT_R5G6B5_UNORM; | ||
74 | case 15: | ||
75 | return PIPE_FORMAT_A1R5G5B5_UNORM; | ||
76 | case 8: | ||
77 | case 4: | ||
78 | case 1: | ||
79 | return PIPE_FORMAT_A8R8G8B8_UNORM; /* bad bad bad */ | ||
80 | default: | ||
81 | assert(0); | ||
82 | return 0; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * Static exported EXA functions | ||
88 | */ | ||
89 | |||
90 | static void | ||
91 | ExaWaitMarker(ScreenPtr pScreen, int marker) | ||
92 | { | ||
93 | } | ||
94 | |||
95 | static int | ||
96 | ExaMarkSync(ScreenPtr pScreen) | ||
97 | { | ||
98 | return 1; | ||
99 | } | ||
100 | |||
101 | static Bool | ||
102 | ExaPrepareAccess(PixmapPtr pPix, int index) | ||
103 | { | ||
104 | ScreenPtr pScreen = pPix->drawable.pScreen; | ||
105 | ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; | ||
106 | modesettingPtr ms = modesettingPTR(pScrn); | ||
107 | //PixmapPtr rootPixmap = pScreen->GetScreenPixmap(pScreen); | ||
108 | struct exa_entity *exa = ms->exa; | ||
109 | struct PixmapPriv *priv; | ||
110 | //int ret; | ||
111 | |||
112 | priv = exaGetPixmapDriverPrivate(pPix); | ||
113 | |||
114 | if (!priv) | ||
115 | return FALSE; | ||
116 | |||
117 | if (!priv->tex) | ||
118 | return FALSE; | ||
119 | { | ||
120 | priv->map_transfer = | ||
121 | exa->scrn->get_tex_transfer(exa->scrn, priv->tex, 0, 0, 0, | ||
122 | PIPE_TRANSFER_READ_WRITE, | ||
123 | 0, 0, priv->tex->width[0], priv->tex->height[0]); | ||
124 | |||
125 | pPix->devPrivate.ptr = | ||
126 | exa->scrn->transfer_map(exa->scrn, priv->map_transfer); | ||
127 | pPix->devKind = priv->map_transfer->stride; | ||
128 | } | ||
129 | |||
130 | return TRUE; | ||
131 | } | ||
132 | |||
133 | static void | ||
134 | ExaFinishAccess(PixmapPtr pPix, int index) | ||
135 | { | ||
136 | ScreenPtr pScreen = pPix->drawable.pScreen; | ||
137 | ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; | ||
138 | modesettingPtr ms = modesettingPTR(pScrn); | ||
139 | struct exa_entity *exa = ms->exa; | ||
140 | struct PixmapPriv *priv; | ||
141 | priv = exaGetPixmapDriverPrivate(pPix); | ||
142 | |||
143 | if (!priv) | ||
144 | return; | ||
145 | |||
146 | if (!priv->map_transfer) | ||
147 | return; | ||
148 | |||
149 | exa->scrn->transfer_unmap(exa->scrn, priv->map_transfer); | ||
150 | pipe_transfer_reference(&priv->map_transfer, NULL); | ||
151 | |||
152 | } | ||
153 | |||
154 | static void | ||
155 | ExaDone(PixmapPtr pPixmap) | ||
156 | { | ||
157 | ScrnInfoPtr pScrn = xf86Screens[pPixmap->drawable.pScreen->myNum]; | ||
158 | modesettingPtr ms = modesettingPTR(pScrn); | ||
159 | struct PixmapPriv *priv = exaGetPixmapDriverPrivate(pPixmap); | ||
160 | struct exa_entity *exa = ms->exa; | ||
161 | |||
162 | if (!priv) | ||
163 | return; | ||
164 | |||
165 | if (priv->src_surf) | ||
166 | exa->scrn->tex_surface_release(exa->scrn, &priv->src_surf); | ||
167 | priv->src_surf = NULL; | ||
168 | } | ||
169 | |||
170 | static void | ||
171 | ExaDoneComposite(PixmapPtr pPixmap) | ||
172 | { | ||
173 | |||
174 | } | ||
175 | |||
176 | static Bool | ||
177 | ExaPrepareSolid(PixmapPtr pPixmap, int alu, Pixel planeMask, Pixel fg) | ||
178 | { | ||
179 | ScrnInfoPtr pScrn = xf86Screens[pPixmap->drawable.pScreen->myNum]; | ||
180 | modesettingPtr ms = modesettingPTR(pScrn); | ||
181 | struct PixmapPriv *priv = exaGetPixmapDriverPrivate(pPixmap); | ||
182 | struct exa_entity *exa = ms->exa; | ||
183 | |||
184 | if (1) | ||
185 | return FALSE; | ||
186 | |||
187 | if (pPixmap->drawable.depth < 15) | ||
188 | return FALSE; | ||
189 | |||
190 | if (!EXA_PM_IS_SOLID(&pPixmap->drawable, planeMask)) | ||
191 | return FALSE; | ||
192 | |||
193 | if (!priv || !priv->tex) | ||
194 | return FALSE; | ||
195 | |||
196 | if (alu != GXcopy) | ||
197 | return FALSE; | ||
198 | |||
199 | if (!exa->ctx || !exa->ctx->surface_fill) | ||
200 | return FALSE; | ||
201 | |||
202 | priv->color = fg; | ||
203 | |||
204 | return TRUE; | ||
205 | } | ||
206 | |||
207 | static void | ||
208 | ExaSolid(PixmapPtr pPixmap, int x0, int y0, int x1, int y1) | ||
209 | { | ||
210 | ScrnInfoPtr pScrn = xf86Screens[pPixmap->drawable.pScreen->myNum]; | ||
211 | modesettingPtr ms = modesettingPTR(pScrn); | ||
212 | struct exa_entity *exa = ms->exa; | ||
213 | struct PixmapPriv *priv = exaGetPixmapDriverPrivate(pPixmap); | ||
214 | struct pipe_surface *surf = | ||
215 | exa->scrn->get_tex_surface(exa->scrn, priv->tex, 0, 0, 0, | ||
216 | PIPE_BUFFER_USAGE_GPU_READ | | ||
217 | PIPE_BUFFER_USAGE_GPU_WRITE); | ||
218 | |||
219 | exa->ctx->surface_fill(exa->ctx, surf, x0, y0, x1 - x0, y1 - y0, | ||
220 | priv->color); | ||
221 | |||
222 | exa->scrn->tex_surface_release(exa->scrn, &surf); | ||
223 | } | ||
224 | |||
225 | static Bool | ||
226 | ExaPrepareCopy(PixmapPtr pSrcPixmap, PixmapPtr pDstPixmap, int xdir, | ||
227 | int ydir, int alu, Pixel planeMask) | ||
228 | { | ||
229 | ScrnInfoPtr pScrn = xf86Screens[pDstPixmap->drawable.pScreen->myNum]; | ||
230 | modesettingPtr ms = modesettingPTR(pScrn); | ||
231 | struct exa_entity *exa = ms->exa; | ||
232 | struct PixmapPriv *priv = exaGetPixmapDriverPrivate(pDstPixmap); | ||
233 | struct PixmapPriv *src_priv = exaGetPixmapDriverPrivate(pSrcPixmap); | ||
234 | |||
235 | if (1) | ||
236 | return FALSE; | ||
237 | |||
238 | if (alu != GXcopy) | ||
239 | return FALSE; | ||
240 | |||
241 | if (pSrcPixmap->drawable.depth < 15 || pDstPixmap->drawable.depth < 15) | ||
242 | return FALSE; | ||
243 | |||
244 | if (!EXA_PM_IS_SOLID(&pSrcPixmap->drawable, planeMask)) | ||
245 | return FALSE; | ||
246 | |||
247 | if (!priv || !src_priv) | ||
248 | return FALSE; | ||
249 | |||
250 | if (!priv->tex || !src_priv->tex) | ||
251 | return FALSE; | ||
252 | |||
253 | if (!exa->ctx || !exa->ctx->surface_copy) | ||
254 | return FALSE; | ||
255 | |||
256 | priv->src_surf = | ||
257 | exa->scrn->get_tex_surface(exa->scrn, src_priv->tex, 0, 0, 0, | ||
258 | PIPE_BUFFER_USAGE_GPU_READ | | ||
259 | PIPE_BUFFER_USAGE_GPU_WRITE); | ||
260 | |||
261 | return FALSE; | ||
262 | } | ||
263 | |||
264 | static void | ||
265 | ExaCopy(PixmapPtr pDstPixmap, int srcX, int srcY, int dstX, int dstY, | ||
266 | int width, int height) | ||
267 | { | ||
268 | ScrnInfoPtr pScrn = xf86Screens[pDstPixmap->drawable.pScreen->myNum]; | ||
269 | modesettingPtr ms = modesettingPTR(pScrn); | ||
270 | struct exa_entity *exa = ms->exa; | ||
271 | struct PixmapPriv *priv = exaGetPixmapDriverPrivate(pDstPixmap); | ||
272 | struct pipe_surface *surf = | ||
273 | exa->scrn->get_tex_surface(exa->scrn, priv->tex, 0, 0, 0, | ||
274 | PIPE_BUFFER_USAGE_GPU_READ | | ||
275 | PIPE_BUFFER_USAGE_GPU_WRITE); | ||
276 | |||
277 | exa->ctx->surface_copy(exa->ctx, 0, surf, dstX, dstY, priv->src_surf, | ||
278 | srcX, srcY, width, height); | ||
279 | exa->scrn->tex_surface_release(exa->scrn, &surf); | ||
280 | } | ||
281 | |||
282 | static Bool | ||
283 | ExaPrepareComposite(int op, PicturePtr pSrcPicture, | ||
284 | PicturePtr pMaskPicture, PicturePtr pDstPicture, | ||
285 | PixmapPtr pSrc, PixmapPtr pMask, PixmapPtr pDst) | ||
286 | { | ||
287 | return FALSE; | ||
288 | } | ||
289 | |||
290 | #if 0 | ||
291 | static Bool | ||
292 | ExaUploadToScreen(PixmapPtr pDst, int x, int y, int w, int h, char *src, | ||
293 | int src_pitch) | ||
294 | { | ||
295 | ErrorF("UPLOAD\n"); | ||
296 | |||
297 | return FALSE; | ||
298 | } | ||
299 | #endif | ||
300 | |||
301 | static void | ||
302 | ExaComposite(PixmapPtr pDst, int srcX, int srcY, int maskX, int maskY, | ||
303 | int dstX, int dstY, int width, int height) | ||
304 | { | ||
305 | } | ||
306 | |||
307 | static Bool | ||
308 | ExaCheckComposite(int op, | ||
309 | PicturePtr pSrcPicture, PicturePtr pMaskPicture, | ||
310 | PicturePtr pDstPicture) | ||
311 | { | ||
312 | return FALSE; | ||
313 | } | ||
314 | |||
315 | static void * | ||
316 | ExaCreatePixmap(ScreenPtr pScreen, int size, int align) | ||
317 | { | ||
318 | struct PixmapPriv *priv; | ||
319 | |||
320 | priv = xcalloc(1, sizeof(struct PixmapPriv)); | ||
321 | if (!priv) | ||
322 | return NULL; | ||
323 | |||
324 | return priv; | ||
325 | } | ||
326 | |||
327 | static void | ||
328 | ExaDestroyPixmap(ScreenPtr pScreen, void *dPriv) | ||
329 | { | ||
330 | struct PixmapPriv *priv = (struct PixmapPriv *)dPriv; | ||
331 | ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; | ||
332 | modesettingPtr ms = modesettingPTR(pScrn); | ||
333 | struct exa_entity *exa = ms->exa; | ||
334 | |||
335 | if (!priv) | ||
336 | return; | ||
337 | |||
338 | if (priv->tex) | ||
339 | ms->screen->texture_release(exa->scrn, &priv->tex); | ||
340 | |||
341 | xfree(priv); | ||
342 | } | ||
343 | |||
344 | static Bool | ||
345 | ExaPixmapIsOffscreen(PixmapPtr pPixmap) | ||
346 | { | ||
347 | struct PixmapPriv *priv; | ||
348 | |||
349 | priv = exaGetPixmapDriverPrivate(pPixmap); | ||
350 | |||
351 | if (!priv) | ||
352 | return FALSE; | ||
353 | |||
354 | if (priv->tex) | ||
355 | return TRUE; | ||
356 | |||
357 | return FALSE; | ||
358 | } | ||
359 | |||
360 | unsigned | ||
361 | xorg_exa_get_pixmap_handle(PixmapPtr pPixmap) | ||
362 | { | ||
363 | ScreenPtr pScreen = pPixmap->drawable.pScreen; | ||
364 | ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; | ||
365 | modesettingPtr ms = modesettingPTR(pScrn); | ||
366 | struct PixmapPriv *priv; | ||
367 | struct pipe_buffer *buffer = NULL; | ||
368 | unsigned handle; | ||
369 | unsigned stride; | ||
370 | |||
371 | if (!ms->exa) { | ||
372 | FatalError("NO MS->EXA\n"); | ||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | priv = exaGetPixmapDriverPrivate(pPixmap); | ||
377 | |||
378 | if (!priv) { | ||
379 | FatalError("NO PIXMAP PRIVATE\n"); | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | drm_api_hocks.buffer_from_texture(priv->tex, &buffer, &stride); | ||
384 | drm_api_hocks.handle_from_buffer(ms->screen, buffer, &handle); | ||
385 | pipe_buffer_reference(ms->screen, &buffer, NULL); | ||
386 | return handle; | ||
387 | } | ||
388 | |||
389 | static Bool | ||
390 | ExaModifyPixmapHeader(PixmapPtr pPixmap, int width, int height, | ||
391 | int depth, int bitsPerPixel, int devKind, | ||
392 | pointer pPixData) | ||
393 | { | ||
394 | ScreenPtr pScreen = pPixmap->drawable.pScreen; | ||
395 | ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; | ||
396 | struct PixmapPriv *priv = exaGetPixmapDriverPrivate(pPixmap); | ||
397 | modesettingPtr ms = modesettingPTR(pScrn); | ||
398 | //PixmapPtr rootPixmap = pScreen->GetScreenPixmap(pScreen); | ||
399 | struct exa_entity *exa = ms->exa; | ||
400 | |||
401 | if (!priv) | ||
402 | return FALSE; | ||
403 | |||
404 | if (depth <= 0) | ||
405 | depth = pPixmap->drawable.depth; | ||
406 | |||
407 | if (bitsPerPixel <= 0) | ||
408 | bitsPerPixel = pPixmap->drawable.bitsPerPixel; | ||
409 | |||
410 | if (width <= 0) | ||
411 | width = pPixmap->drawable.width; | ||
412 | |||
413 | if (height <= 0) | ||
414 | height = pPixmap->drawable.height; | ||
415 | |||
416 | if (width <= 0 || height <= 0 || depth <= 0) | ||
417 | return FALSE; | ||
418 | |||
419 | miModifyPixmapHeader(pPixmap, width, height, depth, | ||
420 | bitsPerPixel, devKind, NULL); | ||
421 | |||
422 | /* Deal with screen resize */ | ||
423 | if (priv->tex && (priv->tex->width[0] != width || priv->tex->height[0] != height)) { | ||
424 | pipe_texture_reference(&priv->tex, NULL); | ||
425 | } | ||
426 | |||
427 | if (!priv->tex) { | ||
428 | struct pipe_texture template; | ||
429 | |||
430 | memset(&template, 0, sizeof(template)); | ||
431 | template.target = PIPE_TEXTURE_2D; | ||
432 | template.compressed = 0; | ||
433 | template.format = exa_get_pipe_format(depth); | ||
434 | pf_get_block(template.format, &template.block); | ||
435 | template.width[0] = width; | ||
436 | template.height[0] = height; | ||
437 | template.depth[0] = 1; | ||
438 | template.last_level = 0; | ||
439 | template.tex_usage = PIPE_TEXTURE_USAGE_RENDER_TARGET; | ||
440 | priv->tex = exa->scrn->texture_create(exa->scrn, &template); | ||
441 | } | ||
442 | |||
443 | return TRUE; | ||
444 | } | ||
445 | |||
446 | struct pipe_texture * | ||
447 | xorg_exa_get_texture(PixmapPtr pPixmap) | ||
448 | { | ||
449 | struct PixmapPriv *priv = exaGetPixmapDriverPrivate(pPixmap); | ||
450 | struct pipe_texture *tex = NULL; | ||
451 | pipe_texture_reference(&tex, priv->tex); | ||
452 | return tex; | ||
453 | } | ||
454 | |||
455 | void | ||
456 | xorg_exa_close(ScrnInfoPtr pScrn) | ||
457 | { | ||
458 | modesettingPtr ms = modesettingPTR(pScrn); | ||
459 | struct exa_entity *exa = ms->exa; | ||
460 | |||
461 | if (exa->ctx) | ||
462 | exa->ctx->destroy(exa->ctx); | ||
463 | |||
464 | exaDriverFini(pScrn->pScreen); | ||
465 | xfree(exa); | ||
466 | ms->exa = NULL; | ||
467 | } | ||
468 | |||
469 | void * | ||
470 | xorg_exa_init(ScrnInfoPtr pScrn) | ||
471 | { | ||
472 | modesettingPtr ms = modesettingPTR(pScrn); | ||
473 | struct exa_entity *exa; | ||
474 | ExaDriverPtr pExa; | ||
475 | |||
476 | exa = xcalloc(1, sizeof(struct exa_entity)); | ||
477 | if (!exa) | ||
478 | return NULL; | ||
479 | |||
480 | pExa = exaDriverAlloc(); | ||
481 | if (!pExa) { | ||
482 | goto out_err; | ||
483 | } | ||
484 | |||
485 | memset(pExa, 0, sizeof(*pExa)); | ||
486 | pExa->exa_major = 2; | ||
487 | pExa->exa_minor = 2; | ||
488 | pExa->memoryBase = 0; | ||
489 | pExa->memorySize = 0; | ||
490 | pExa->offScreenBase = 0; | ||
491 | pExa->pixmapOffsetAlign = 0; | ||
492 | pExa->pixmapPitchAlign = 1; | ||
493 | pExa->flags = EXA_OFFSCREEN_PIXMAPS | EXA_HANDLES_PIXMAPS; | ||
494 | pExa->maxX = 8191; /* FIXME */ | ||
495 | pExa->maxY = 8191; /* FIXME */ | ||
496 | pExa->WaitMarker = ExaWaitMarker; | ||
497 | pExa->MarkSync = ExaMarkSync; | ||
498 | pExa->PrepareSolid = ExaPrepareSolid; | ||
499 | pExa->Solid = ExaSolid; | ||
500 | pExa->DoneSolid = ExaDone; | ||
501 | pExa->PrepareCopy = ExaPrepareCopy; | ||
502 | pExa->Copy = ExaCopy; | ||
503 | pExa->DoneCopy = ExaDone; | ||
504 | pExa->CheckComposite = ExaCheckComposite; | ||
505 | pExa->PrepareComposite = ExaPrepareComposite; | ||
506 | pExa->Composite = ExaComposite; | ||
507 | pExa->DoneComposite = ExaDoneComposite; | ||
508 | pExa->PixmapIsOffscreen = ExaPixmapIsOffscreen; | ||
509 | pExa->PrepareAccess = ExaPrepareAccess; | ||
510 | pExa->FinishAccess = ExaFinishAccess; | ||
511 | //pExa->UploadToScreen = ExaUploadToScreen; | ||
512 | pExa->UploadToScreen = NULL; | ||
513 | pExa->CreatePixmap = ExaCreatePixmap; | ||
514 | pExa->DestroyPixmap = ExaDestroyPixmap; | ||
515 | pExa->ModifyPixmapHeader = ExaModifyPixmapHeader; | ||
516 | |||
517 | if (!exaDriverInit(pScrn->pScreen, pExa)) { | ||
518 | goto out_err; | ||
519 | } | ||
520 | |||
521 | exa->scrn = ms->screen; | ||
522 | exa->ctx = drm_api_hocks.create_context(exa->scrn); | ||
523 | /* Share context with DRI */ | ||
524 | ms->ctx = exa->ctx; | ||
525 | |||
526 | return (void *)exa; | ||
527 | |||
528 | out_err: | ||
529 | xorg_exa_close(pScrn); | ||
530 | |||
531 | return NULL; | ||
532 | } | ||
533 | |||
534 | /* vim: set sw=4 ts=8 sts=4: */ | ||