1/* 2 * Copyright © 2012 Red Hat Inc. 3 * Copyright 2019 DisplayLink (UK) Ltd. 4 * 5 * Permission to use, copy, modify, distribute, and sell this software and its 6 * documentation for any purpose is hereby granted without fee, provided that 7 * the above copyright notice appear in all copies and that both that copyright 8 * notice and this permission notice appear in supporting documentation, and 9 * that the name of the copyright holders not be used in advertising or 10 * publicity pertaining to distribution of the software without specific, 11 * written prior permission. The copyright holders make no representations 12 * about the suitability of this software for any purpose. It is provided "as 13 * is" without express or implied warranty. 14 * 15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 21 * OF THIS SOFTWARE. 22 * 23 * Authors: Dave Airlie 24 */ 25 26#include "randrstr.h" 27#include "swaprep.h" 28 29#include <X11/Xatom.h> 30 31RESTYPE RRProviderType = 0; 32 33/* 34 * Initialize provider type error value 35 */ 36void 37RRProviderInitErrorValue(void) 38{ 39 SetResourceTypeErrorValue(RRProviderType, RRErrorBase + BadRRProvider); 40} 41 42#define ADD_PROVIDER(_pScreen) do { \ 43 pScrPriv = rrGetScrPriv((_pScreen)); \ 44 if (pScrPriv->provider) { \ 45 providers[count_providers] = pScrPriv->provider->id; \ 46 if (client->swapped) \ 47 swapl(&providers[count_providers]); \ 48 count_providers++; \ 49 } \ 50 } while(0) 51 52int 53ProcRRGetProviders (ClientPtr client) 54{ 55 REQUEST(xRRGetProvidersReq); 56 xRRGetProvidersReply rep; 57 WindowPtr pWin; 58 ScreenPtr pScreen; 59 rrScrPrivPtr pScrPriv; 60 int rc; 61 CARD8 *extra; 62 unsigned int extraLen; 63 RRProvider *providers; 64 int total_providers = 0, count_providers = 0; 65 ScreenPtr iter; 66 67 REQUEST_SIZE_MATCH(xRRGetProvidersReq); 68 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); 69 if (rc != Success) 70 return rc; 71 72 pScreen = pWin->drawable.pScreen; 73 74 pScrPriv = rrGetScrPriv(pScreen); 75 76 if (pScrPriv->provider) 77 total_providers++; 78 xorg_list_for_each_entry(iter, &pScreen->secondary_list, secondary_head) { 79 pScrPriv = rrGetScrPriv(iter); 80 total_providers += pScrPriv->provider ? 1 : 0; 81 } 82 83 pScrPriv = rrGetScrPriv(pScreen); 84 85 if (!pScrPriv) 86 { 87 rep = (xRRGetProvidersReply) { 88 .type = X_Reply, 89 .sequenceNumber = client->sequence, 90 .length = 0, 91 .timestamp = currentTime.milliseconds, 92 .nProviders = 0 93 }; 94 extra = NULL; 95 extraLen = 0; 96 } else { 97 rep = (xRRGetProvidersReply) { 98 .type = X_Reply, 99 .sequenceNumber = client->sequence, 100 .timestamp = pScrPriv->lastSetTime.milliseconds, 101 .nProviders = total_providers, 102 .length = total_providers 103 }; 104 extraLen = rep.length << 2; 105 if (extraLen) { 106 extra = malloc(extraLen); 107 if (!extra) 108 return BadAlloc; 109 } else 110 extra = NULL; 111 112 providers = (RRProvider *)extra; 113 ADD_PROVIDER(pScreen); 114 xorg_list_for_each_entry(iter, &pScreen->secondary_list, secondary_head) { 115 ADD_PROVIDER(iter); 116 } 117 } 118 119 if (client->swapped) { 120 swaps(&rep.sequenceNumber); 121 swapl(&rep.length); 122 swapl(&rep.timestamp); 123 swaps(&rep.nProviders); 124 } 125 WriteToClient(client, sizeof(xRRGetProvidersReply), (char *)&rep); 126 if (extraLen) 127 { 128 WriteToClient (client, extraLen, (char *) extra); 129 free(extra); 130 } 131 return Success; 132} 133 134int 135ProcRRGetProviderInfo (ClientPtr client) 136{ 137 REQUEST(xRRGetProviderInfoReq); 138 xRRGetProviderInfoReply rep; 139 rrScrPrivPtr pScrPriv, pScrProvPriv; 140 RRProviderPtr provider; 141 ScreenPtr pScreen; 142 CARD8 *extra; 143 unsigned int extraLen = 0; 144 RRCrtc *crtcs; 145 RROutput *outputs; 146 int i; 147 char *name; 148 ScreenPtr provscreen; 149 RRProvider *providers; 150 uint32_t *prov_cap; 151 152 REQUEST_SIZE_MATCH(xRRGetProviderInfoReq); 153 VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess); 154 155 pScreen = provider->pScreen; 156 pScrPriv = rrGetScrPriv(pScreen); 157 158 rep = (xRRGetProviderInfoReply) { 159 .type = X_Reply, 160 .status = RRSetConfigSuccess, 161 .sequenceNumber = client->sequence, 162 .length = 0, 163 .capabilities = provider->capabilities, 164 .nameLength = provider->nameLength, 165 .timestamp = pScrPriv->lastSetTime.milliseconds, 166 .nCrtcs = pScrPriv->numCrtcs, 167 .nOutputs = pScrPriv->numOutputs, 168 .nAssociatedProviders = 0 169 }; 170 171 /* count associated providers */ 172 if (provider->offload_sink) 173 rep.nAssociatedProviders++; 174 if (provider->output_source && 175 provider->output_source != provider->offload_sink) 176 rep.nAssociatedProviders++; 177 xorg_list_for_each_entry(provscreen, &pScreen->secondary_list, secondary_head) { 178 if (provscreen->is_output_secondary || provscreen->is_offload_secondary) 179 rep.nAssociatedProviders++; 180 } 181 182 rep.length = (pScrPriv->numCrtcs + pScrPriv->numOutputs + 183 (rep.nAssociatedProviders * 2) + bytes_to_int32(rep.nameLength)); 184 185 extraLen = rep.length << 2; 186 if (extraLen) { 187 extra = malloc(extraLen); 188 if (!extra) 189 return BadAlloc; 190 } 191 else 192 extra = NULL; 193 194 crtcs = (RRCrtc *)extra; 195 outputs = (RROutput *)(crtcs + rep.nCrtcs); 196 providers = (RRProvider *)(outputs + rep.nOutputs); 197 prov_cap = (unsigned int *)(providers + rep.nAssociatedProviders); 198 name = (char *)(prov_cap + rep.nAssociatedProviders); 199 200 for (i = 0; i < pScrPriv->numCrtcs; i++) { 201 crtcs[i] = pScrPriv->crtcs[i]->id; 202 if (client->swapped) 203 swapl(&crtcs[i]); 204 } 205 206 for (i = 0; i < pScrPriv->numOutputs; i++) { 207 outputs[i] = pScrPriv->outputs[i]->id; 208 if (client->swapped) 209 swapl(&outputs[i]); 210 } 211 212 i = 0; 213 if (provider->offload_sink) { 214 providers[i] = provider->offload_sink->id; 215 if (client->swapped) 216 swapl(&providers[i]); 217 prov_cap[i] = RR_Capability_SinkOffload; 218 if (client->swapped) 219 swapl(&prov_cap[i]); 220 i++; 221 } 222 if (provider->output_source) { 223 providers[i] = provider->output_source->id; 224 if (client->swapped) 225 swapl(&providers[i]); 226 prov_cap[i] = RR_Capability_SourceOutput; 227 swapl(&prov_cap[i]); 228 i++; 229 } 230 xorg_list_for_each_entry(provscreen, &pScreen->secondary_list, secondary_head) { 231 if (!provscreen->is_output_secondary && !provscreen->is_offload_secondary) 232 continue; 233 pScrProvPriv = rrGetScrPriv(provscreen); 234 providers[i] = pScrProvPriv->provider->id; 235 if (client->swapped) 236 swapl(&providers[i]); 237 prov_cap[i] = 0; 238 if (provscreen->is_output_secondary) 239 prov_cap[i] |= RR_Capability_SinkOutput; 240 if (provscreen->is_offload_secondary) 241 prov_cap[i] |= RR_Capability_SourceOffload; 242 if (client->swapped) 243 swapl(&prov_cap[i]); 244 i++; 245 } 246 247 memcpy(name, provider->name, rep.nameLength); 248 if (client->swapped) { 249 swaps(&rep.sequenceNumber); 250 swapl(&rep.length); 251 swapl(&rep.capabilities); 252 swaps(&rep.nCrtcs); 253 swaps(&rep.nOutputs); 254 swaps(&rep.nameLength); 255 } 256 WriteToClient(client, sizeof(xRRGetProviderInfoReply), (char *)&rep); 257 if (extraLen) 258 { 259 WriteToClient (client, extraLen, (char *) extra); 260 free(extra); 261 } 262 return Success; 263} 264 265static void 266RRInitPrimeSyncProps(ScreenPtr pScreen) 267{ 268 /* 269 * TODO: When adding support for different sources for different outputs, 270 * make sure this sets up the output properties only on outputs associated 271 * with the correct source provider. 272 */ 273 274 rrScrPrivPtr pScrPriv = rrGetScrPriv(pScreen); 275 276 const char *syncStr = PRIME_SYNC_PROP; 277 Atom syncProp = MakeAtom(syncStr, strlen(syncStr), TRUE); 278 279 int defaultVal = TRUE; 280 INT32 validVals[2] = {FALSE, TRUE}; 281 282 int i; 283 for (i = 0; i < pScrPriv->numOutputs; i++) { 284 if (!RRQueryOutputProperty(pScrPriv->outputs[i], syncProp)) { 285 RRConfigureOutputProperty(pScrPriv->outputs[i], syncProp, 286 TRUE, FALSE, FALSE, 287 2, &validVals[0]); 288 RRChangeOutputProperty(pScrPriv->outputs[i], syncProp, XA_INTEGER, 289 8, PropModeReplace, 1, &defaultVal, 290 FALSE, FALSE); 291 } 292 } 293} 294 295static void 296RRFiniPrimeSyncProps(ScreenPtr pScreen) 297{ 298 /* 299 * TODO: When adding support for different sources for different outputs, 300 * make sure this tears down the output properties only on outputs 301 * associated with the correct source provider. 302 */ 303 304 rrScrPrivPtr pScrPriv = rrGetScrPriv(pScreen); 305 int i; 306 307 const char *syncStr = PRIME_SYNC_PROP; 308 Atom syncProp = MakeAtom(syncStr, strlen(syncStr), FALSE); 309 if (syncProp == None) 310 return; 311 312 for (i = 0; i < pScrPriv->numOutputs; i++) { 313 RRDeleteOutputProperty(pScrPriv->outputs[i], syncProp); 314 } 315} 316 317int 318ProcRRSetProviderOutputSource(ClientPtr client) 319{ 320 REQUEST(xRRSetProviderOutputSourceReq); 321 rrScrPrivPtr pScrPriv; 322 RRProviderPtr provider, source_provider = NULL; 323 ScreenPtr pScreen; 324 325 REQUEST_SIZE_MATCH(xRRSetProviderOutputSourceReq); 326 327 VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess); 328 329 if (!(provider->capabilities & RR_Capability_SinkOutput)) 330 return BadValue; 331 332 if (stuff->source_provider) { 333 VERIFY_RR_PROVIDER(stuff->source_provider, source_provider, DixReadAccess); 334 335 if (!(source_provider->capabilities & RR_Capability_SourceOutput)) 336 return BadValue; 337 } 338 339 pScreen = provider->pScreen; 340 pScrPriv = rrGetScrPriv(pScreen); 341 342 if (!pScreen->isGPU) 343 return BadValue; 344 345 pScrPriv->rrProviderSetOutputSource(pScreen, provider, source_provider); 346 347 RRInitPrimeSyncProps(pScreen); 348 349 provider->changed = TRUE; 350 RRSetChanged(pScreen); 351 352 RRTellChanged (pScreen); 353 354 return Success; 355} 356 357int 358ProcRRSetProviderOffloadSink(ClientPtr client) 359{ 360 REQUEST(xRRSetProviderOffloadSinkReq); 361 rrScrPrivPtr pScrPriv; 362 RRProviderPtr provider, sink_provider = NULL; 363 ScreenPtr pScreen; 364 365 REQUEST_SIZE_MATCH(xRRSetProviderOffloadSinkReq); 366 367 VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess); 368 if (!(provider->capabilities & RR_Capability_SourceOffload)) 369 return BadValue; 370 if (!provider->pScreen->isGPU) 371 return BadValue; 372 373 if (stuff->sink_provider) { 374 VERIFY_RR_PROVIDER(stuff->sink_provider, sink_provider, DixReadAccess); 375 if (!(sink_provider->capabilities & RR_Capability_SinkOffload)) 376 return BadValue; 377 } 378 pScreen = provider->pScreen; 379 pScrPriv = rrGetScrPriv(pScreen); 380 381 pScrPriv->rrProviderSetOffloadSink(pScreen, provider, sink_provider); 382 383 provider->changed = TRUE; 384 RRSetChanged(pScreen); 385 386 RRTellChanged (pScreen); 387 388 return Success; 389} 390 391RRProviderPtr 392RRProviderCreate(ScreenPtr pScreen, const char *name, 393 int nameLength) 394{ 395 RRProviderPtr provider; 396 rrScrPrivPtr pScrPriv; 397 398 pScrPriv = rrGetScrPriv(pScreen); 399 400 provider = calloc(1, sizeof(RRProviderRec) + nameLength + 1); 401 if (!provider) 402 return NULL; 403 404 provider->id = FakeClientID(0); 405 provider->pScreen = pScreen; 406 provider->name = (char *) (provider + 1); 407 provider->nameLength = nameLength; 408 memcpy(provider->name, name, nameLength); 409 provider->name[nameLength] = '\0'; 410 provider->changed = FALSE; 411 412 if (!AddResource (provider->id, RRProviderType, (void *) provider)) 413 return NULL; 414 pScrPriv->provider = provider; 415 return provider; 416} 417 418/* 419 * Destroy a provider at shutdown 420 */ 421void 422RRProviderDestroy (RRProviderPtr provider) 423{ 424 RRFiniPrimeSyncProps(provider->pScreen); 425 FreeResource (provider->id, 0); 426} 427 428void 429RRProviderSetCapabilities(RRProviderPtr provider, uint32_t capabilities) 430{ 431 provider->capabilities = capabilities; 432} 433 434static int 435RRProviderDestroyResource (void *value, XID pid) 436{ 437 RRProviderPtr provider = (RRProviderPtr)value; 438 ScreenPtr pScreen = provider->pScreen; 439 440 if (pScreen) 441 { 442 rrScrPriv(pScreen); 443 444 if (pScrPriv->rrProviderDestroy) 445 (*pScrPriv->rrProviderDestroy)(pScreen, provider); 446 pScrPriv->provider = NULL; 447 } 448 free(provider); 449 return 1; 450} 451 452Bool 453RRProviderInit(void) 454{ 455 RRProviderType = CreateNewResourceType(RRProviderDestroyResource, "Provider"); 456 if (!RRProviderType) 457 return FALSE; 458 459 return TRUE; 460} 461 462extern _X_EXPORT Bool 463RRProviderLookup(XID id, RRProviderPtr *provider_p) 464{ 465 int rc = dixLookupResourceByType((void **)provider_p, id, 466 RRProviderType, NullClient, DixReadAccess); 467 if (rc == Success) 468 return TRUE; 469 return FALSE; 470} 471 472void 473RRDeliverProviderEvent(ClientPtr client, WindowPtr pWin, RRProviderPtr provider) 474{ 475 ScreenPtr pScreen = pWin->drawable.pScreen; 476 477 rrScrPriv(pScreen); 478 479 xRRProviderChangeNotifyEvent pe = { 480 .type = RRNotify + RREventBase, 481 .subCode = RRNotify_ProviderChange, 482 .timestamp = pScrPriv->lastSetTime.milliseconds, 483 .window = pWin->drawable.id, 484 .provider = provider->id 485 }; 486 487 WriteEventsToClient(client, 1, (xEvent *) &pe); 488} 489 490void 491RRProviderAutoConfigGpuScreen(ScreenPtr pScreen, ScreenPtr primaryScreen) 492{ 493 rrScrPrivPtr pScrPriv; 494 rrScrPrivPtr primaryPriv; 495 RRProviderPtr provider; 496 RRProviderPtr primary_provider; 497 498 /* Bail out if RandR wasn't initialized. */ 499 if (!dixPrivateKeyRegistered(rrPrivKey)) 500 return; 501 502 pScrPriv = rrGetScrPriv(pScreen); 503 primaryPriv = rrGetScrPriv(primaryScreen); 504 505 provider = pScrPriv->provider; 506 primary_provider = primaryPriv->provider; 507 508 if (!provider || !primary_provider) 509 return; 510 511 if ((provider->capabilities & RR_Capability_SinkOutput) && 512 (primary_provider->capabilities & RR_Capability_SourceOutput)) { 513 pScrPriv->rrProviderSetOutputSource(pScreen, provider, primary_provider); 514 RRInitPrimeSyncProps(pScreen); 515 516 primaryPriv->configChanged = TRUE; 517 RRSetChanged(primaryScreen); 518 } 519 520 if ((provider->capabilities & RR_Capability_SourceOffload) && 521 (primary_provider->capabilities & RR_Capability_SinkOffload)) 522 pScrPriv->rrProviderSetOffloadSink(pScreen, provider, primary_provider); 523} 524