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