1 /*
2  * Copyright © 2011 Intel Corporation.
3  *             2012 Advanced Micro Devices, Inc.
4  *
5  * Permission is hereby granted, free of charge, to any person
6  * obtaining a copy of this software and associated documentation
7  * files (the "Software"), to deal in the Software without
8  * restriction, including without limitation the rights to use, copy,
9  * modify, merge, publish, distribute, sublicense, and/or sell copies
10  * of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including
14  * the next paragraph) shall be included in all copies or substantial
15  * portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include <xf86.h>
32 
33 #include "radeon.h"
34 #include "radeon_bo_helper.h"
35 #include "radeon_glamor.h"
36 
37 DevPrivateKeyRec glamor_pixmap_index;
38 
39 void
radeon_glamor_exchange_buffers(PixmapPtr src,PixmapPtr dst)40 radeon_glamor_exchange_buffers(PixmapPtr src,
41 			       PixmapPtr dst)
42 {
43 	RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(dst->drawable.pScreen));
44 
45 	if (!info->use_glamor)
46 		return;
47 	glamor_egl_exchange_buffers(src, dst);
48 }
49 
50 Bool
radeon_glamor_create_screen_resources(ScreenPtr screen)51 radeon_glamor_create_screen_resources(ScreenPtr screen)
52 {
53 	PixmapPtr screen_pixmap = screen->GetScreenPixmap(screen);
54 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
55 	RADEONInfoPtr info = RADEONPTR(scrn);
56 
57 	if (!info->use_glamor)
58 		return TRUE;
59 
60 #ifdef HAVE_GLAMOR_GLYPHS_INIT
61 	if (!glamor_glyphs_init(screen))
62 		return FALSE;
63 #endif
64 
65 	return radeon_glamor_create_textured_pixmap(screen_pixmap,
66 						    info->front_buffer);
67 }
68 
69 
70 Bool
radeon_glamor_pre_init(ScrnInfoPtr scrn)71 radeon_glamor_pre_init(ScrnInfoPtr scrn)
72 {
73 	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
74 	RADEONInfoPtr info = RADEONPTR(scrn);
75 	pointer glamor_module;
76 	CARD32 version;
77 	const char *s;
78 
79 	if (!info->dri2.available)
80 		return FALSE;
81 
82 	s = xf86GetOptValString(info->Options, OPTION_ACCELMETHOD);
83 	if (!s) {
84 		if (xorgGetVersion() >= XORG_VERSION_NUMERIC(1,18,3,0,0)) {
85 			if (info->ChipFamily < CHIP_FAMILY_R600)
86 				return FALSE;
87 		} else {
88 			if (info->ChipFamily < CHIP_FAMILY_TAHITI)
89 				return FALSE;
90 		}
91 	}
92 
93 	if (s && strcasecmp(s, "glamor") != 0) {
94 		if (info->ChipFamily >= CHIP_FAMILY_TAHITI)
95 			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
96 				   "EXA not supported, using glamor\n");
97 		else
98 			return FALSE;
99 	}
100 
101 	if (info->ChipFamily < CHIP_FAMILY_R300) {
102 		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
103 			   "glamor requires R300 or higher GPU, disabling.\n");
104 		return FALSE;
105 	}
106 
107 	if (info->ChipFamily < CHIP_FAMILY_RV515) {
108 		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
109 			   "glamor may not work (well) with GPUs < RV515.\n");
110 	}
111 
112 #if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,20,99,0,0)
113 	if (scrn->depth < 24) {
114 #else
115 	if (scrn->depth < 15) {
116 #endif
117 		xf86DrvMsg(scrn->scrnIndex, s ? X_ERROR : X_WARNING,
118 			   "Depth %d not supported with glamor, disabling\n",
119 			   scrn->depth);
120 		return FALSE;
121 	}
122 
123 	if (scrn->depth == 30 &&
124 	    xorgGetVersion() < XORG_VERSION_NUMERIC(1,19,99,1,0)) {
125 		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
126 			   "Depth 30 is not supported by GLAMOR with Xorg < "
127 			   "1.19.99.1\n");
128 		return FALSE;
129 	}
130 
131 #if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,15,0,0,0)
132 	if (!xf86LoaderCheckSymbol("glamor_egl_init")) {
133 		xf86DrvMsg(scrn->scrnIndex, s ? X_ERROR : X_WARNING,
134 			   "glamor requires Load \"glamoregl\" in "
135 			   "Section \"Module\", disabling.\n");
136 		return FALSE;
137 	}
138 #endif
139 
140 	info->gbm = gbm_create_device(pRADEONEnt->fd);
141 	if (!info->gbm) {
142 		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
143 			   "gbm_create_device returned NULL\n");
144 		return FALSE;
145 	}
146 
147 	/* Load glamor module */
148 	if ((glamor_module = xf86LoadSubModule(scrn, GLAMOR_EGL_MODULE_NAME))) {
149 		version = xf86GetModuleVersion(glamor_module);
150 		if (version < MODULE_VERSION_NUMERIC(0,3,1)) {
151 			xf86DrvMsg(scrn->scrnIndex, X_ERROR,
152 			"Incompatible glamor version, required >= 0.3.0.\n");
153 			return FALSE;
154 		} else {
155 			if (glamor_egl_init(scrn, pRADEONEnt->fd)) {
156 				xf86DrvMsg(scrn->scrnIndex, X_INFO,
157 					   "glamor detected, initialising EGL layer.\n");
158 			} else {
159 				xf86DrvMsg(scrn->scrnIndex, X_ERROR,
160 					   "glamor detected, failed to initialize EGL.\n");
161 				return FALSE;
162 			}
163 		}
164 	} else {
165 		xf86DrvMsg(scrn->scrnIndex, X_ERROR, "glamor not available\n");
166 		return FALSE;
167 	}
168 
169 	info->use_glamor = TRUE;
170 
171 	return TRUE;
172 }
173 
174 Bool
175 radeon_glamor_create_textured_pixmap(PixmapPtr pixmap, struct radeon_buffer *bo)
176 {
177 	ScrnInfoPtr scrn = xf86ScreenToScrn(pixmap->drawable.pScreen);
178 	RADEONInfoPtr info = RADEONPTR(scrn);
179 
180 	if (!info->use_glamor)
181 		return TRUE;
182 
183 	if (bo->flags & RADEON_BO_FLAGS_GBM) {
184 		return glamor_egl_create_textured_pixmap_from_gbm_bo(pixmap,
185 								     bo->bo.gbm
186 #if XORG_VERSION_CURRENT > XORG_VERSION_NUMERIC(1,19,99,903,0)
187 								     , FALSE
188 #endif
189 								     );
190 	} else {
191 		return glamor_egl_create_textured_pixmap(pixmap,
192 							 bo->bo.radeon->handle,
193 							 pixmap->devKind);
194 	}
195 }
196 
197 static Bool radeon_glamor_destroy_pixmap(PixmapPtr pixmap)
198 {
199 #ifndef HAVE_GLAMOR_EGL_DESTROY_TEXTURED_PIXMAP
200 	ScreenPtr screen = pixmap->drawable.pScreen;
201 	RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(screen));
202 	Bool ret;
203 #endif
204 
205 	if (pixmap->refcnt == 1) {
206 #ifdef HAVE_GLAMOR_EGL_DESTROY_TEXTURED_PIXMAP
207 		glamor_egl_destroy_textured_pixmap(pixmap);
208 #endif
209 		radeon_set_pixmap_bo(pixmap, NULL);
210 	}
211 
212 #ifdef HAVE_GLAMOR_EGL_DESTROY_TEXTURED_PIXMAP
213 	fbDestroyPixmap(pixmap);
214 	return TRUE;
215 #else
216 	screen->DestroyPixmap = info->glamor.SavedDestroyPixmap;
217 	ret = screen->DestroyPixmap(pixmap);
218 	info->glamor.SavedDestroyPixmap = screen->DestroyPixmap;
219 	screen->DestroyPixmap = radeon_glamor_destroy_pixmap;
220 
221 	return ret;
222 #endif
223 }
224 
225 static PixmapPtr
226 radeon_glamor_create_pixmap(ScreenPtr screen, int w, int h, int depth,
227 			unsigned usage)
228 {
229 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
230 	RADEONInfoPtr info = RADEONPTR(scrn);
231 	struct radeon_pixmap *priv;
232 	PixmapPtr pixmap, new_pixmap = NULL;
233 
234 	if (!xf86GetPixFormat(scrn, depth))
235 		return NULL;
236 
237 	if (!RADEON_CREATE_PIXMAP_SHARED(usage)) {
238 		if (info->shadow_primary) {
239 			if (usage != CREATE_PIXMAP_USAGE_BACKING_PIXMAP)
240 				return fbCreatePixmap(screen, w, h, depth, usage);
241 		} else {
242 			pixmap = glamor_create_pixmap(screen, w, h, depth, usage);
243 			if (pixmap)
244 			    return pixmap;
245 		}
246 	}
247 
248 	if (w > 32767 || h > 32767)
249 		return NullPixmap;
250 
251 	if (depth == 1)
252 		return fbCreatePixmap(screen, w, h, depth, usage);
253 
254 	if (usage == CREATE_PIXMAP_USAGE_GLYPH_PICTURE && w <= 32 && h <= 32)
255 		return fbCreatePixmap(screen, w, h, depth, usage);
256 
257 	pixmap = fbCreatePixmap(screen, 0, 0, depth, usage);
258 	if (pixmap == NullPixmap)
259 		return pixmap;
260 
261 	if (w && h) {
262 		int stride;
263 
264 		priv = calloc(1, sizeof (struct radeon_pixmap));
265 		if (!priv)
266 			goto fallback_pixmap;
267 
268 		priv->bo = radeon_alloc_pixmap_bo(scrn, w, h, depth, usage,
269 						  pixmap->drawable.bitsPerPixel,
270 						  &stride, NULL,
271 						  &priv->tiling_flags);
272 		if (!priv->bo)
273 			goto fallback_priv;
274 
275 		radeon_set_pixmap_private(pixmap, priv);
276 
277 		screen->ModifyPixmapHeader(pixmap, w, h, 0, 0, stride, NULL);
278 
279 		if (!radeon_glamor_create_textured_pixmap(pixmap, priv->bo))
280 			goto fallback_glamor;
281 
282 		pixmap->devPrivate.ptr = NULL;
283 	}
284 
285 	return pixmap;
286 
287 fallback_glamor:
288 	if (RADEON_CREATE_PIXMAP_SHARED(usage)) {
289 	/* XXX need further work to handle the DRI2 failure case.
290 	 * Glamor don't know how to handle a BO only pixmap. Put
291 	 * a warning indicator here.
292 	 */
293 		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
294 			   "Failed to create textured DRI2/PRIME pixmap.");
295 
296 		radeon_glamor_destroy_pixmap(pixmap);
297 		return NullPixmap;
298 	}
299 	/* Create textured pixmap failed means glamor failed to
300 	 * create a texture from current BO for some reasons. We turn
301 	 * to create a new glamor pixmap and clean up current one.
302 	 * One thing need to be noted, this new pixmap doesn't
303 	 * has a priv and bo attached to it. It's glamor's responsbility
304 	 * to take care of it. Glamor will mark this new pixmap as a
305 	 * texture only pixmap and will never fallback to DDX layer
306 	 * afterwards.
307 	 */
308 	new_pixmap = glamor_create_pixmap(screen, w, h,	depth, usage);
309 	radeon_buffer_unref(&priv->bo);
310 fallback_priv:
311 	free(priv);
312 fallback_pixmap:
313 	fbDestroyPixmap(pixmap);
314 	if (new_pixmap)
315 		return new_pixmap;
316 	else
317 		return fbCreatePixmap(screen, w, h, depth, usage);
318 }
319 
320 PixmapPtr
321 radeon_glamor_set_pixmap_bo(DrawablePtr drawable, PixmapPtr pixmap)
322 {
323 	PixmapPtr old = get_drawable_pixmap(drawable);
324 	ScreenPtr screen = drawable->pScreen;
325 	struct radeon_pixmap *priv = radeon_get_pixmap_private(pixmap);
326 	GCPtr gc;
327 
328 	/* With a glamor pixmap, 2D pixmaps are created in texture
329 	 * and without a static BO attached to it. To support DRI,
330 	 * we need to create a new textured-drm pixmap and
331 	 * need to copy the original content to this new textured-drm
332 	 * pixmap, and then convert the old pixmap to a coherent
333 	 * textured-drm pixmap which has a valid BO attached to it
334 	 * and also has a valid texture, thus both glamor and DRI2
335 	 * can access it.
336 	 *
337 	 */
338 
339 	/* Copy the current contents of the pixmap to the bo. */
340 	gc = GetScratchGC(drawable->depth, screen);
341 	if (gc) {
342 		ValidateGC(&pixmap->drawable, gc);
343 		gc->ops->CopyArea(&old->drawable, &pixmap->drawable,
344 				  gc,
345 				  0, 0,
346 				  old->drawable.width,
347 				  old->drawable.height, 0, 0);
348 		FreeScratchGC(gc);
349 	}
350 
351 	/* And redirect the pixmap to the new bo (for 3D). */
352 	glamor_egl_exchange_buffers(old, pixmap);
353 	radeon_set_pixmap_private(pixmap, radeon_get_pixmap_private(old));
354 	radeon_set_pixmap_private(old, priv);
355 
356 	screen->ModifyPixmapHeader(old,
357 				   old->drawable.width,
358 				   old->drawable.height,
359 				   0, 0, pixmap->devKind, NULL);
360 	old->devPrivate.ptr = NULL;
361 
362 	screen->DestroyPixmap(pixmap);
363 
364 	return old;
365 }
366 
367 
368 static Bool
369 radeon_glamor_share_pixmap_backing(PixmapPtr pixmap, ScreenPtr slave,
370 				   void **handle_p)
371 {
372 	ScreenPtr screen = pixmap->drawable.pScreen;
373 	CARD16 stride;
374 	CARD32 size;
375 	int fd;
376 
377 	if ((radeon_get_pixmap_tiling_flags(pixmap) &
378 	     RADEON_TILING_MASK) != RADEON_TILING_LINEAR) {
379 		PixmapPtr linear;
380 
381 		/* We don't want to re-allocate the screen pixmap as
382 		 * linear, to avoid trouble with page flipping
383 		 */
384 		if (screen->GetScreenPixmap(screen) == pixmap)
385 			return FALSE;
386 
387 		linear = screen->CreatePixmap(screen, pixmap->drawable.width,
388 					      pixmap->drawable.height,
389 					      pixmap->drawable.depth,
390 					      CREATE_PIXMAP_USAGE_SHARED);
391 		if (!linear)
392 			return FALSE;
393 
394 		radeon_glamor_set_pixmap_bo(&pixmap->drawable, linear);
395 	}
396 
397 	fd = glamor_fd_from_pixmap(screen, pixmap, &stride, &size);
398 	if (fd < 0)
399 		return FALSE;
400 
401 	*handle_p = (void *)(long)fd;
402 	return TRUE;
403 }
404 
405 static Bool
406 radeon_glamor_set_shared_pixmap_backing(PixmapPtr pixmap, void *handle)
407 {
408 	ScreenPtr screen = pixmap->drawable.pScreen;
409 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
410 	int ihandle = (int)(long)handle;
411 
412 	if (!radeon_set_shared_pixmap_backing(pixmap, handle, NULL))
413 		return FALSE;
414 
415 	if (ihandle != -1 &&
416 	    !radeon_glamor_create_textured_pixmap(pixmap,
417 						  radeon_get_pixmap_bo(pixmap))) {
418 		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
419 			   "Failed to get PRIME drawable for glamor pixmap.\n");
420 		return FALSE;
421 	}
422 
423 	screen->ModifyPixmapHeader(pixmap,
424 				   pixmap->drawable.width,
425 				   pixmap->drawable.height,
426 				   0, 0, 0, NULL);
427 
428 	return TRUE;
429 }
430 
431 
432 Bool
433 radeon_glamor_init(ScreenPtr screen)
434 {
435 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
436 	RADEONInfoPtr info = RADEONPTR(scrn);
437 #ifdef RENDER
438 #ifdef HAVE_FBGLYPHS
439 	UnrealizeGlyphProcPtr SavedUnrealizeGlyph = NULL;
440 #endif
441 	PictureScreenPtr ps = NULL;
442 
443 	if (info->shadow_primary) {
444 		ps = GetPictureScreenIfSet(screen);
445 
446 		if (ps) {
447 #ifdef HAVE_FBGLYPHS
448 			SavedUnrealizeGlyph = ps->UnrealizeGlyph;
449 #endif
450 			info->glamor.SavedGlyphs = ps->Glyphs;
451 			info->glamor.SavedTriangles = ps->Triangles;
452 			info->glamor.SavedTrapezoids = ps->Trapezoids;
453 		}
454 	}
455 #endif /* RENDER */
456 
457 	if (!glamor_init(screen, GLAMOR_USE_EGL_SCREEN | GLAMOR_USE_SCREEN |
458 			 GLAMOR_USE_PICTURE_SCREEN | GLAMOR_INVERTED_Y_AXIS |
459 			 GLAMOR_NO_DRI3)) {
460 		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
461 			   "Failed to initialize glamor.\n");
462 		return FALSE;
463 	}
464 
465 	if (!glamor_egl_init_textured_pixmap(screen)) {
466 		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
467 			   "Failed to initialize textured pixmap of screen for glamor.\n");
468 		return FALSE;
469 	}
470 
471 	if (!dixRegisterPrivateKey(&glamor_pixmap_index, PRIVATE_PIXMAP, 0))
472 		return FALSE;
473 
474 	if (info->shadow_primary)
475 		radeon_glamor_screen_init(screen);
476 
477 #if defined(RENDER) && defined(HAVE_FBGLYPHS)
478 	/* For ShadowPrimary, we need fbUnrealizeGlyph instead of
479 	 * glamor_unrealize_glyph
480 	 */
481 	if (ps)
482 		ps->UnrealizeGlyph = SavedUnrealizeGlyph;
483 #endif
484 
485 	info->glamor.SavedCreatePixmap = screen->CreatePixmap;
486 	screen->CreatePixmap = radeon_glamor_create_pixmap;
487 	info->glamor.SavedDestroyPixmap = screen->DestroyPixmap;
488 	screen->DestroyPixmap = radeon_glamor_destroy_pixmap;
489 	info->glamor.SavedSharePixmapBacking = screen->SharePixmapBacking;
490 	screen->SharePixmapBacking = radeon_glamor_share_pixmap_backing;
491 	info->glamor.SavedSetSharedPixmapBacking = screen->SetSharedPixmapBacking;
492 	screen->SetSharedPixmapBacking = radeon_glamor_set_shared_pixmap_backing;
493 
494 	xf86DrvMsg(scrn->scrnIndex, X_INFO,
495 		   "Use GLAMOR acceleration.\n");
496 	return TRUE;
497 }
498 
499 void
500 radeon_glamor_fini(ScreenPtr screen)
501 {
502 	RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(screen));
503 
504 	if (!info->use_glamor)
505 		return;
506 
507 	screen->CreatePixmap = info->glamor.SavedCreatePixmap;
508 	screen->DestroyPixmap = info->glamor.SavedDestroyPixmap;
509 	screen->SharePixmapBacking = info->glamor.SavedSharePixmapBacking;
510 	screen->SetSharedPixmapBacking = info->glamor.SavedSetSharedPixmapBacking;
511 }
512 
513 XF86VideoAdaptorPtr radeon_glamor_xv_init(ScreenPtr pScreen, int num_adapt)
514 {
515 	return glamor_xv_init(pScreen, num_adapt);
516 }
517