rrprovider.c revision a035e2b2
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
30RESTYPE RRProviderType = 0;
31
32/*
33 * Initialize provider type error value
34 */
35void
36RRProviderInitErrorValue(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
51int
52ProcRRGetProviders (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
133int
134ProcRRGetProviderInfo (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
264static void
265RRInitPrimeSyncProps(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
294static void
295RRFiniPrimeSyncProps(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
316int
317ProcRRSetProviderOutputSource(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
356int
357ProcRRSetProviderOffloadSink(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
390RRProviderPtr
391RRProviderCreate(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 */
420void
421RRProviderDestroy (RRProviderPtr provider)
422{
423    RRFiniPrimeSyncProps(provider->pScreen);
424    FreeResource (provider->id, 0);
425}
426
427void
428RRProviderSetCapabilities(RRProviderPtr provider, uint32_t capabilities)
429{
430    provider->capabilities = capabilities;
431}
432
433static int
434RRProviderDestroyResource (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
451Bool
452RRProviderInit(void)
453{
454    RRProviderType = CreateNewResourceType(RRProviderDestroyResource, "Provider");
455    if (!RRProviderType)
456        return FALSE;
457
458    return TRUE;
459}
460
461extern _X_EXPORT Bool
462RRProviderLookup(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
471void
472RRDeliverProviderEvent(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