1 /*
2 * Copyright © 2008 Red Hat, Inc.
3 * Copyright (c) 2023 Emil Velikov
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Soft-
7 * ware"), to deal in the Software without restriction, including without
8 * limitation the rights to use, copy, modify, merge, publish, distribute,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, provided that the above copyright
11 * notice(s) and this permission notice appear in all copies of the Soft-
12 * ware and that both the above copyright notice(s) and this permission
13 * notice appear in supporting documentation.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
17 * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
18 * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
19 * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE-
20 * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR-
23 * MANCE OF THIS SOFTWARE.
24 *
25 * Except as contained in this notice, the name of a copyright holder shall
26 * not be used in advertising or otherwise to promote the sale, use or
27 * other dealings in this Software without prior written authorization of
28 * the copyright holder.
29 *
30 * Authors:
31 * Kristian Høgsberg ([email protected])
32 */
33
34
35 #define NEED_REPLIES
36 #include <X11/Xlibint.h>
37 #include <X11/extensions/Xext.h>
38 #include <X11/extensions/extutil.h>
39 #include "xf86drm.h"
40 #include "va_dri2.h"
41 #include "va_dri2_priv.h"
42 #include "va_dricommon.h"
43 #include "va_dri2str.h"
44 #include "va_dri2tokens.h"
45
46 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
47
48 #ifndef DRI2DriverDRI
49 #define DRI2DriverDRI 0
50 #endif
51
52 static int
53 VA_DRI2Error(Display *dpy, xError *err, XExtCodes *codes, int *ret_code);
54
55 static VA_DRI2Buffer *
56 VA_DRI2GetBuffers_internal(XExtDisplayInfo *info,
57 Display *dpy, XID drawable,
58 int *width, int *height,
59 unsigned int *attachments,
60 int count,
61 int *outCount);
62
63 static char va_dri2ExtensionName[] = DRI2_NAME;
64 static XExtensionInfo _va_dri2_info_data;
65 static XExtensionInfo *va_dri2Info = &_va_dri2_info_data;
66 static XEXT_GENERATE_CLOSE_DISPLAY(VA_DRI2CloseDisplay, va_dri2Info)
67 static /* const */ XExtensionHooks va_dri2ExtensionHooks = {
68 NULL, /* create_gc */
69 NULL, /* copy_gc */
70 NULL, /* flush_gc */
71 NULL, /* free_gc */
72 NULL, /* create_font */
73 NULL, /* free_font */
74 VA_DRI2CloseDisplay, /* close_display */
75 NULL, /* wire_to_event */
76 NULL, /* event_to_wire */
77 VA_DRI2Error, /* error */
78 NULL, /* error_string */
79 };
80
81 static XEXT_GENERATE_FIND_DISPLAY(DRI2FindDisplay, va_dri2Info,
82 va_dri2ExtensionName,
83 &va_dri2ExtensionHooks,
84 0, NULL)
85
86 static CARD32 _va_resource_x_error_drawable = 0;
87 static Bool _va_resource_x_error_matched = False;
88
89 #define VA_EnterResourceError(drawable) \
90 do { \
91 _va_resource_x_error_drawable = (drawable); \
92 _va_resource_x_error_matched = False; \
93 } while (0)
94
95 #define VA_LeaveResourceError() \
96 do { \
97 _va_resource_x_error_drawable = 0; \
98 } while (0)
99
100 #define VA_ResourceErrorMatched() \
101 (_va_resource_x_error_matched)
102
103 static int
VA_DRI2Error(Display * dpy,xError * err,XExtCodes * codes,int * ret_code)104 VA_DRI2Error(Display *dpy, xError *err, XExtCodes *codes, int *ret_code)
105 {
106 if (_va_resource_x_error_drawable == err->resourceID) {
107 _va_resource_x_error_matched = True;
108 return True;
109 }
110
111 return False;
112 }
113
VA_DRI2QueryExtension(Display * dpy,int * eventBase,int * errorBase)114 Bool VA_DRI2QueryExtension(Display *dpy, int *eventBase, int *errorBase)
115 {
116 XExtDisplayInfo *info = DRI2FindDisplay(dpy);
117
118 if (XextHasExtension(info)) {
119 *eventBase = info->codes->first_event;
120 *errorBase = info->codes->first_error;
121 return True;
122 }
123
124 return False;
125 }
126
VA_DRI2QueryVersion(Display * dpy,int * major,int * minor)127 Bool VA_DRI2QueryVersion(Display *dpy, int *major, int *minor)
128 {
129 XExtDisplayInfo *info = DRI2FindDisplay(dpy);
130 xDRI2QueryVersionReply rep;
131 xDRI2QueryVersionReq *req;
132
133 XextCheckExtension(dpy, info, va_dri2ExtensionName, False);
134
135 LockDisplay(dpy);
136 GetReq(DRI2QueryVersion, req);
137 req->reqType = info->codes->major_opcode;
138 req->dri2ReqType = X_DRI2QueryVersion;
139 req->majorVersion = DRI2_MAJOR;
140 req->minorVersion = DRI2_MINOR;
141 if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
142 UnlockDisplay(dpy);
143 SyncHandle();
144 return False;
145 }
146 *major = rep.majorVersion;
147 *minor = rep.minorVersion;
148 UnlockDisplay(dpy);
149 SyncHandle();
150
151 return True;
152 }
153
VA_DRI2Connect(Display * dpy,XID window,char ** driverName,char ** deviceName)154 Bool VA_DRI2Connect(Display *dpy, XID window,
155 char **driverName, char **deviceName)
156 {
157 XExtDisplayInfo *info = DRI2FindDisplay(dpy);
158 xDRI2ConnectReply rep;
159 xDRI2ConnectReq *req;
160
161 XextCheckExtension(dpy, info, va_dri2ExtensionName, False);
162
163 LockDisplay(dpy);
164 GetReq(DRI2Connect, req);
165 req->reqType = info->codes->major_opcode;
166 req->dri2ReqType = X_DRI2Connect;
167 req->window = window;
168 req->driverType = DRI2DriverDRI;
169 if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
170 UnlockDisplay(dpy);
171 SyncHandle();
172 return False;
173 }
174
175 if (rep.driverNameLength == 0 && rep.deviceNameLength == 0) {
176 UnlockDisplay(dpy);
177 SyncHandle();
178 return False;
179 }
180
181 *driverName = Xmalloc(rep.driverNameLength + 1);
182 if (*driverName == NULL) {
183 _XEatData(dpy,
184 ((rep.driverNameLength + 3) & ~3) +
185 ((rep.deviceNameLength + 3) & ~3));
186 UnlockDisplay(dpy);
187 SyncHandle();
188 return False;
189 }
190 _XReadPad(dpy, *driverName, rep.driverNameLength);
191 (*driverName)[rep.driverNameLength] = '\0';
192
193 *deviceName = Xmalloc(rep.deviceNameLength + 1);
194 if (*deviceName == NULL) {
195 Xfree(*driverName);
196 _XEatData(dpy, ((rep.deviceNameLength + 3) & ~3));
197 UnlockDisplay(dpy);
198 SyncHandle();
199 return False;
200 }
201 _XReadPad(dpy, *deviceName, rep.deviceNameLength);
202 (*deviceName)[rep.deviceNameLength] = '\0';
203
204 UnlockDisplay(dpy);
205 SyncHandle();
206
207 return True;
208 }
209
VA_DRI2Authenticate(Display * dpy,XID window,drm_magic_t magic)210 Bool VA_DRI2Authenticate(Display *dpy, XID window, drm_magic_t magic)
211 {
212 XExtDisplayInfo *info = DRI2FindDisplay(dpy);
213 xDRI2AuthenticateReq *req;
214 xDRI2AuthenticateReply rep;
215
216 XextCheckExtension(dpy, info, va_dri2ExtensionName, False);
217
218 LockDisplay(dpy);
219 GetReq(DRI2Authenticate, req);
220 req->reqType = info->codes->major_opcode;
221 req->dri2ReqType = X_DRI2Authenticate;
222 req->window = window;
223 req->magic = magic;
224
225 if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
226 UnlockDisplay(dpy);
227 SyncHandle();
228 return False;
229 }
230
231 UnlockDisplay(dpy);
232 SyncHandle();
233
234 return rep.authenticated;
235 }
236
VA_DRI2CreateDrawable(Display * dpy,XID drawable)237 void VA_DRI2CreateDrawable(Display *dpy, XID drawable)
238 {
239 XExtDisplayInfo *info = DRI2FindDisplay(dpy);
240 xDRI2CreateDrawableReq *req;
241
242 XextSimpleCheckExtension(dpy, info, va_dri2ExtensionName);
243
244 LockDisplay(dpy);
245 GetReq(DRI2CreateDrawable, req);
246 req->reqType = info->codes->major_opcode;
247 req->dri2ReqType = X_DRI2CreateDrawable;
248 req->drawable = drawable;
249 UnlockDisplay(dpy);
250 SyncHandle();
251 }
252
VA_DRI2DestroyDrawable(Display * dpy,XID drawable)253 void VA_DRI2DestroyDrawable(Display *dpy, XID drawable)
254 {
255 XExtDisplayInfo *info = DRI2FindDisplay(dpy);
256 xDRI2DestroyDrawableReq *req;
257 unsigned int attachement = 0; // FRONT_LEFT
258 VA_DRI2Buffer *buffers;
259
260 XextSimpleCheckExtension(dpy, info, va_dri2ExtensionName);
261
262 XSync(dpy, False);
263
264 LockDisplay(dpy);
265 /*
266 * We have no way of catching DRI2DestroyDrawable errors because
267 * this message doesn't have a defined answer. So we test whether
268 * the drawable is still alive by sending DRIGetBuffers first and
269 * checking whether we get an error.
270 */
271 VA_EnterResourceError(drawable);
272
273 buffers = VA_DRI2GetBuffers_internal(info, dpy, drawable,
274 NULL, NULL,
275 &attachement, 1, NULL);
276 VA_LeaveResourceError();
277 if (buffers)
278 XFree(buffers);
279 if (VA_ResourceErrorMatched()) {
280 UnlockDisplay(dpy);
281 SyncHandle();
282 return;
283 }
284
285 GetReq(DRI2DestroyDrawable, req);
286 req->reqType = info->codes->major_opcode;
287 req->dri2ReqType = X_DRI2DestroyDrawable;
288 req->drawable = drawable;
289 UnlockDisplay(dpy);
290 SyncHandle();
291 }
292
VA_DRI2GetBuffers_internal(XExtDisplayInfo * info,Display * dpy,XID drawable,int * width,int * height,unsigned int * attachments,int count,int * outCount)293 VA_DRI2Buffer *VA_DRI2GetBuffers_internal(XExtDisplayInfo *info,
294 Display *dpy, XID drawable,
295 int *width, int *height,
296 unsigned int *attachments, int count,
297 int *outCount)
298 {
299 xDRI2GetBuffersReply rep;
300 xDRI2GetBuffersReq *req;
301 VA_DRI2Buffer *buffers;
302 xDRI2Buffer repBuffer;
303 CARD32 *p;
304 int i;
305
306 GetReqExtra(DRI2GetBuffers, count * 4, req);
307 req->reqType = info->codes->major_opcode;
308 req->dri2ReqType = X_DRI2GetBuffers;
309 req->drawable = drawable;
310 req->count = count;
311 p = (CARD32 *) &req[1];
312 for (i = 0; i < count; i++)
313 p[i] = attachments[i];
314
315 if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
316 return NULL;
317 }
318
319 if (width)
320 *width = rep.width;
321 if (height)
322 *height = rep.height;
323 if (outCount)
324 *outCount = rep.count;
325
326 buffers = Xmalloc(rep.count * sizeof buffers[0]);
327 if (buffers == NULL) {
328 _XEatData(dpy, rep.count * sizeof repBuffer);
329 return NULL;
330 }
331
332 for (i = 0; i < rep.count; i++) {
333 _XReadPad(dpy, (char *) &repBuffer, sizeof repBuffer);
334 buffers[i].attachment = repBuffer.attachment;
335 buffers[i].name = repBuffer.name;
336 buffers[i].pitch = repBuffer.pitch;
337 buffers[i].cpp = repBuffer.cpp;
338 buffers[i].flags = repBuffer.flags;
339 }
340
341 return buffers;
342 }
343
VA_DRI2GetBuffers(Display * dpy,XID drawable,int * width,int * height,unsigned int * attachments,int count,int * outCount)344 VA_DRI2Buffer *VA_DRI2GetBuffers(Display *dpy, XID drawable,
345 int *width, int *height,
346 unsigned int *attachments, int count,
347 int *outCount)
348 {
349 XExtDisplayInfo *info = DRI2FindDisplay(dpy);
350 VA_DRI2Buffer *buffers;
351
352 XextCheckExtension(dpy, info, va_dri2ExtensionName, False);
353
354 LockDisplay(dpy);
355
356 buffers = VA_DRI2GetBuffers_internal(info, dpy, drawable, width, height,
357 attachments, count, outCount);
358
359 UnlockDisplay(dpy);
360 SyncHandle();
361
362 return buffers;
363 }
364
VA_DRI2CopyRegion(Display * dpy,XID drawable,XserverRegion region,CARD32 dest,CARD32 src)365 void VA_DRI2CopyRegion(Display *dpy, XID drawable, XserverRegion region,
366 CARD32 dest, CARD32 src)
367 {
368 XExtDisplayInfo *info = DRI2FindDisplay(dpy);
369 xDRI2CopyRegionReq *req;
370 xDRI2CopyRegionReply rep;
371
372 XextSimpleCheckExtension(dpy, info, va_dri2ExtensionName);
373
374 LockDisplay(dpy);
375 GetReq(DRI2CopyRegion, req);
376 req->reqType = info->codes->major_opcode;
377 req->dri2ReqType = X_DRI2CopyRegion;
378 req->drawable = drawable;
379 req->region = region;
380 req->dest = dest;
381 req->src = src;
382
383 _XReply(dpy, (xReply *)&rep, 0, xFalse);
384
385 UnlockDisplay(dpy);
386 SyncHandle();
387 }
388
389 static void
load_swap_req(xDRI2SwapBuffersReq * req,CARD64 target,CARD64 divisor,CARD64 remainder)390 load_swap_req(xDRI2SwapBuffersReq *req, CARD64 target, CARD64 divisor,
391 CARD64 remainder)
392 {
393 req->target_msc_hi = target >> 32;
394 req->target_msc_lo = target & 0xffffffff;
395 req->divisor_hi = divisor >> 32;
396 req->divisor_lo = divisor & 0xffffffff;
397 req->remainder_hi = remainder >> 32;
398 req->remainder_lo = remainder & 0xffffffff;
399 }
400
401 static CARD64
vals_to_card64(CARD32 lo,CARD32 hi)402 vals_to_card64(CARD32 lo, CARD32 hi)
403 {
404 return (CARD64)hi << 32 | lo;
405 }
406
VA_DRI2SwapBuffers(Display * dpy,XID drawable,CARD64 target_msc,CARD64 divisor,CARD64 remainder,CARD64 * count)407 void VA_DRI2SwapBuffers(Display *dpy, XID drawable, CARD64 target_msc,
408 CARD64 divisor, CARD64 remainder, CARD64 *count)
409 {
410 XExtDisplayInfo *info = DRI2FindDisplay(dpy);
411 xDRI2SwapBuffersReq *req;
412 xDRI2SwapBuffersReply rep;
413
414 XextSimpleCheckExtension(dpy, info, va_dri2ExtensionName);
415
416 LockDisplay(dpy);
417 GetReq(DRI2SwapBuffers, req);
418 req->reqType = info->codes->major_opcode;
419 req->dri2ReqType = X_DRI2SwapBuffers;
420 req->drawable = drawable;
421 load_swap_req(req, target_msc, divisor, remainder);
422
423 _XReply(dpy, (xReply *)&rep, 0, xFalse);
424
425 *count = vals_to_card64(rep.swap_lo, rep.swap_hi);
426
427 UnlockDisplay(dpy);
428 SyncHandle();
429 }
430
va_DRI2_GetDriverNames(VADisplayContextP pDisplayContext,char ** drivers,unsigned * num_drivers)431 VAStatus va_DRI2_GetDriverNames(
432 VADisplayContextP pDisplayContext,
433 char **drivers,
434 unsigned *num_drivers
435 )
436 {
437 #define MAX_NAMES 2 // Adjust if needed
438
439 static const struct {
440 const char * const dri_driver;
441 const char * const va_driver[MAX_NAMES];
442 } map[] = {
443 { "i965", { "iHD", "i965" } }, // Intel Media and OTC GenX
444 { "iris", { "iHD", "i965" } }, // Intel Media and OTC GenX
445 { "crocus", { "i965" } }, // OTC GenX
446 };
447
448 VADriverContextP ctx = pDisplayContext->pDriverContext;
449 char *dri_driver;
450 unsigned count = 0;
451
452 if (!(va_isDRI2Connected(ctx, &dri_driver) && dri_driver))
453 return VA_STATUS_ERROR_UNKNOWN;
454
455 for (unsigned i = 0; i < ARRAY_SIZE(map); i++) {
456 if (strcmp(map[i].dri_driver, dri_driver) == 0) {
457 const char * const *va_drivers = map[i].va_driver;
458
459 for (; count < MAX_NAMES && va_drivers[count] && count < *num_drivers; count++)
460 drivers[count] = strdup(va_drivers[count]);
461
462 break;
463 }
464 }
465
466 /* Fallback to the dri driver, if there's no va equivalent in the map. */
467 if (!count) {
468 drivers[count] = dri_driver;
469 count++;
470 } else {
471 free(dri_driver);
472 }
473
474 *num_drivers = count;
475
476 return VA_STATUS_SUCCESS;
477 }
478