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