524 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			524 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright © 2012 Red Hat Inc.
 | |
|  * Copyright 2019 DisplayLink (UK) Ltd.
 | |
|  *
 | |
|  * Permission to use, copy, modify, distribute, and sell this software and its
 | |
|  * documentation for any purpose is hereby granted without fee, provided that
 | |
|  * the above copyright notice appear in all copies and that both that copyright
 | |
|  * notice and this permission notice appear in supporting documentation, and
 | |
|  * that the name of the copyright holders not be used in advertising or
 | |
|  * publicity pertaining to distribution of the software without specific,
 | |
|  * written prior permission.  The copyright holders make no representations
 | |
|  * about the suitability of this software for any purpose.  It is provided "as
 | |
|  * is" without express or implied warranty.
 | |
|  *
 | |
|  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 | |
|  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 | |
|  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 | |
|  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 | |
|  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 | |
|  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 | |
|  * OF THIS SOFTWARE.
 | |
|  *
 | |
|  * Authors: Dave Airlie
 | |
|  */
 | |
| #include <dix-config.h>
 | |
| 
 | |
| #include <X11/Xatom.h>
 | |
| 
 | |
| #include "dix/dix_priv.h"
 | |
| #include "randr/randrstr_priv.h"
 | |
| 
 | |
| #include "swaprep.h"
 | |
| 
 | |
| RESTYPE RRProviderType = 0;
 | |
| 
 | |
| /*
 | |
|  * Initialize provider type error value
 | |
|  */
 | |
| void
 | |
| RRProviderInitErrorValue(void)
 | |
| {
 | |
|     SetResourceTypeErrorValue(RRProviderType, RRErrorBase + BadRRProvider);
 | |
| }
 | |
| 
 | |
| #define ADD_PROVIDER(_pScreen) do {                                 \
 | |
|     pScrPriv = rrGetScrPriv((_pScreen));                            \
 | |
|     if (pScrPriv->provider) {                                   \
 | |
|         providers[count_providers] = pScrPriv->provider->id;    \
 | |
|         if (client->swapped)                                    \
 | |
|             swapl(&providers[count_providers]);                 \
 | |
|         count_providers++;                                      \
 | |
|     }                                                           \
 | |
|     } while(0)
 | |
| 
 | |
| int
 | |
| ProcRRGetProviders (ClientPtr client)
 | |
| {
 | |
|     REQUEST(xRRGetProvidersReq);
 | |
|     WindowPtr pWin;
 | |
|     ScreenPtr pScreen;
 | |
|     rrScrPrivPtr pScrPriv;
 | |
|     int rc;
 | |
|     CARD8 *extra;
 | |
|     unsigned int extraLen;
 | |
|     RRProvider *providers;
 | |
|     int total_providers = 0, count_providers = 0;
 | |
|     ScreenPtr iter;
 | |
| 
 | |
|     REQUEST_SIZE_MATCH(xRRGetProvidersReq);
 | |
|     rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
 | |
|     if (rc != Success)
 | |
|         return rc;
 | |
| 
 | |
|     pScreen = pWin->drawable.pScreen;
 | |
| 
 | |
|     pScrPriv = rrGetScrPriv(pScreen);
 | |
| 
 | |
|     if (pScrPriv->provider)
 | |
|         total_providers++;
 | |
|     xorg_list_for_each_entry(iter, &pScreen->secondary_list, secondary_head) {
 | |
|         pScrPriv = rrGetScrPriv(iter);
 | |
|         total_providers += pScrPriv->provider ? 1 : 0;
 | |
|     }
 | |
| 
 | |
|     pScrPriv = rrGetScrPriv(pScreen);
 | |
| 
 | |
|     if (!pScrPriv)
 | |
|     {
 | |
|         xRRGetProvidersReply rep = {
 | |
|             .type = X_Reply,
 | |
|             .sequenceNumber = client->sequence,
 | |
|             .timestamp = currentTime.milliseconds,
 | |
|         };
 | |
|         WriteToClient(client, sizeof(rep), &rep);
 | |
|         return Success;
 | |
|     }
 | |
| 
 | |
|     extraLen = total_providers * sizeof(CARD32);
 | |
| 
 | |
|     xRRGetProvidersReply rep = {
 | |
|         .type = X_Reply,
 | |
|         .sequenceNumber = client->sequence,
 | |
|         .timestamp = pScrPriv->lastSetTime.milliseconds,
 | |
|         .nProviders = total_providers,
 | |
|         .length = bytes_to_int32(extraLen),
 | |
|     };
 | |
| 
 | |
|     if (extraLen) {
 | |
|         extra = malloc(extraLen);
 | |
|         if (!extra)
 | |
|             return BadAlloc;
 | |
|     } else
 | |
|         extra = NULL;
 | |
| 
 | |
|     providers = (RRProvider *)extra;
 | |
|     ADD_PROVIDER(pScreen);
 | |
|     xorg_list_for_each_entry(iter, &pScreen->secondary_list, secondary_head) {
 | |
|         ADD_PROVIDER(iter);
 | |
|     }
 | |
| 
 | |
|     if (client->swapped) {
 | |
|         swaps(&rep.sequenceNumber);
 | |
|         swapl(&rep.length);
 | |
|         swapl(&rep.timestamp);
 | |
|         swaps(&rep.nProviders);
 | |
|     }
 | |
|     WriteToClient(client, sizeof(xRRGetProvidersReply), (char *)&rep);
 | |
|     if (extraLen)
 | |
|     {
 | |
|         WriteToClient (client, extraLen, (char *) extra);
 | |
|         free(extra);
 | |
|     }
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| int
 | |
| ProcRRGetProviderInfo (ClientPtr client)
 | |
| {
 | |
|     REQUEST(xRRGetProviderInfoReq);
 | |
|     rrScrPrivPtr pScrPriv, pScrProvPriv;
 | |
|     RRProviderPtr provider;
 | |
|     ScreenPtr pScreen;
 | |
|     CARD8 *extra;
 | |
|     unsigned int extraLen = 0;
 | |
|     RRCrtc *crtcs;
 | |
|     RROutput *outputs;
 | |
|     int i;
 | |
|     char *name;
 | |
|     ScreenPtr provscreen;
 | |
|     RRProvider *providers;
 | |
|     uint32_t *prov_cap;
 | |
| 
 | |
|     REQUEST_SIZE_MATCH(xRRGetProviderInfoReq);
 | |
|     VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
 | |
| 
 | |
|     pScreen = provider->pScreen;
 | |
|     pScrPriv = rrGetScrPriv(pScreen);
 | |
| 
 | |
|     xRRGetProviderInfoReply rep = {
 | |
|         .type = X_Reply,
 | |
|         .status = RRSetConfigSuccess,
 | |
|         .sequenceNumber = client->sequence,
 | |
|         .capabilities = provider->capabilities,
 | |
|         .nameLength = provider->nameLength,
 | |
|         .timestamp = pScrPriv->lastSetTime.milliseconds,
 | |
|         .nCrtcs = pScrPriv->numCrtcs,
 | |
|         .nOutputs = pScrPriv->numOutputs,
 | |
|     };
 | |
| 
 | |
|     /* count associated providers */
 | |
|     if (provider->offload_sink)
 | |
|         rep.nAssociatedProviders++;
 | |
|     if (provider->output_source &&
 | |
|             provider->output_source != provider->offload_sink)
 | |
|         rep.nAssociatedProviders++;
 | |
|     xorg_list_for_each_entry(provscreen, &pScreen->secondary_list, secondary_head) {
 | |
|         if (provscreen->is_output_secondary || provscreen->is_offload_secondary)
 | |
|             rep.nAssociatedProviders++;
 | |
|     }
 | |
| 
 | |
|     rep.length = (pScrPriv->numCrtcs + pScrPriv->numOutputs +
 | |
|                   (rep.nAssociatedProviders * 2) + bytes_to_int32(rep.nameLength));
 | |
| 
 | |
|     extraLen = rep.length << 2;
 | |
|     if (extraLen) {
 | |
|         extra = malloc(extraLen);
 | |
|         if (!extra)
 | |
|             return BadAlloc;
 | |
|     }
 | |
|     else
 | |
|         extra = NULL;
 | |
| 
 | |
|     crtcs = (RRCrtc *)extra;
 | |
|     outputs = (RROutput *)(crtcs + rep.nCrtcs);
 | |
|     providers = (RRProvider *)(outputs + rep.nOutputs);
 | |
|     prov_cap = (unsigned int *)(providers + rep.nAssociatedProviders);
 | |
|     name = (char *)(prov_cap + rep.nAssociatedProviders);
 | |
| 
 | |
|     for (i = 0; i < pScrPriv->numCrtcs; i++) {
 | |
|         crtcs[i] = pScrPriv->crtcs[i]->id;
 | |
|         if (client->swapped)
 | |
|             swapl(&crtcs[i]);
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < pScrPriv->numOutputs; i++) {
 | |
|         outputs[i] = pScrPriv->outputs[i]->id;
 | |
|         if (client->swapped)
 | |
|             swapl(&outputs[i]);
 | |
|     }
 | |
| 
 | |
|     i = 0;
 | |
|     if (provider->offload_sink) {
 | |
|         providers[i] = provider->offload_sink->id;
 | |
|         if (client->swapped)
 | |
|             swapl(&providers[i]);
 | |
|         prov_cap[i] = RR_Capability_SinkOffload;
 | |
|         if (client->swapped)
 | |
|             swapl(&prov_cap[i]);
 | |
|         i++;
 | |
|     }
 | |
|     if (provider->output_source) {
 | |
|         providers[i] = provider->output_source->id;
 | |
|         prov_cap[i] = RR_Capability_SourceOutput;
 | |
|         if (client->swapped) {
 | |
|             swapl(&providers[i]);
 | |
|             swapl(&prov_cap[i]);
 | |
|         }
 | |
|         i++;
 | |
|     }
 | |
|     xorg_list_for_each_entry(provscreen, &pScreen->secondary_list, secondary_head) {
 | |
|         if (!provscreen->is_output_secondary && !provscreen->is_offload_secondary)
 | |
|             continue;
 | |
|         pScrProvPriv = rrGetScrPriv(provscreen);
 | |
|         providers[i] = pScrProvPriv->provider->id;
 | |
|         if (client->swapped)
 | |
|             swapl(&providers[i]);
 | |
|         prov_cap[i] = 0;
 | |
|         if (provscreen->is_output_secondary)
 | |
|             prov_cap[i] |= RR_Capability_SinkOutput;
 | |
|         if (provscreen->is_offload_secondary)
 | |
|             prov_cap[i] |= RR_Capability_SourceOffload;
 | |
|         if (client->swapped)
 | |
|             swapl(&prov_cap[i]);
 | |
|         i++;
 | |
|     }
 | |
| 
 | |
|     memcpy(name, provider->name, rep.nameLength);
 | |
|     if (client->swapped) {
 | |
|         swaps(&rep.sequenceNumber);
 | |
|         swapl(&rep.length);
 | |
|         swapl(&rep.capabilities);
 | |
|         swaps(&rep.nCrtcs);
 | |
|         swaps(&rep.nOutputs);
 | |
|         swaps(&rep.nameLength);
 | |
|     }
 | |
|     WriteToClient(client, sizeof(xRRGetProviderInfoReply), (char *)&rep);
 | |
|     if (extraLen)
 | |
|     {
 | |
|         WriteToClient (client, extraLen, (char *) extra);
 | |
|         free(extra);
 | |
|     }
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| static void
 | |
| RRInitPrimeSyncProps(ScreenPtr pScreen)
 | |
| {
 | |
|     /*
 | |
|      * TODO: When adding support for different sources for different outputs,
 | |
|      * make sure this sets up the output properties only on outputs associated
 | |
|      * with the correct source provider.
 | |
|      */
 | |
| 
 | |
|     rrScrPrivPtr pScrPriv = rrGetScrPriv(pScreen);
 | |
| 
 | |
|     const char *syncStr = PRIME_SYNC_PROP;
 | |
|     Atom syncProp = MakeAtom(syncStr, strlen(syncStr), TRUE);
 | |
| 
 | |
|     int defaultVal = TRUE;
 | |
|     INT32 validVals[2] = {FALSE, TRUE};
 | |
| 
 | |
|     int i;
 | |
|     for (i = 0; i < pScrPriv->numOutputs; i++) {
 | |
|         if (!RRQueryOutputProperty(pScrPriv->outputs[i], syncProp)) {
 | |
|             RRConfigureOutputProperty(pScrPriv->outputs[i], syncProp,
 | |
|                                       TRUE, FALSE, FALSE,
 | |
|                                       2, &validVals[0]);
 | |
|             RRChangeOutputProperty(pScrPriv->outputs[i], syncProp, XA_INTEGER,
 | |
|                                    8, PropModeReplace, 1, &defaultVal,
 | |
|                                    FALSE, FALSE);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| RRFiniPrimeSyncProps(ScreenPtr pScreen)
 | |
| {
 | |
|     /*
 | |
|      * TODO: When adding support for different sources for different outputs,
 | |
|      * make sure this tears down the output properties only on outputs
 | |
|      * associated with the correct source provider.
 | |
|      */
 | |
| 
 | |
|     rrScrPrivPtr pScrPriv = rrGetScrPriv(pScreen);
 | |
|     int i;
 | |
| 
 | |
|     const char *syncStr = PRIME_SYNC_PROP;
 | |
|     Atom syncProp = MakeAtom(syncStr, strlen(syncStr), FALSE);
 | |
|     if (syncProp == None)
 | |
|         return;
 | |
| 
 | |
|     for (i = 0; i < pScrPriv->numOutputs; i++) {
 | |
|         RRDeleteOutputProperty(pScrPriv->outputs[i], syncProp);
 | |
|     }
 | |
| }
 | |
| 
 | |
| int
 | |
| ProcRRSetProviderOutputSource(ClientPtr client)
 | |
| {
 | |
|     REQUEST(xRRSetProviderOutputSourceReq);
 | |
|     rrScrPrivPtr pScrPriv;
 | |
|     RRProviderPtr provider, source_provider = NULL;
 | |
|     ScreenPtr pScreen;
 | |
| 
 | |
|     REQUEST_SIZE_MATCH(xRRSetProviderOutputSourceReq);
 | |
| 
 | |
|     VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
 | |
| 
 | |
|     if (!(provider->capabilities & RR_Capability_SinkOutput))
 | |
|         return BadValue;
 | |
| 
 | |
|     if (stuff->source_provider) {
 | |
|         VERIFY_RR_PROVIDER(stuff->source_provider, source_provider, DixReadAccess);
 | |
| 
 | |
|         if (!(source_provider->capabilities & RR_Capability_SourceOutput))
 | |
|             return BadValue;
 | |
|     }
 | |
| 
 | |
|     pScreen = provider->pScreen;
 | |
|     pScrPriv = rrGetScrPriv(pScreen);
 | |
| 
 | |
|     if (!pScreen->isGPU)
 | |
|         return BadValue;
 | |
| 
 | |
|     pScrPriv->rrProviderSetOutputSource(pScreen, provider, source_provider);
 | |
| 
 | |
|     RRInitPrimeSyncProps(pScreen);
 | |
| 
 | |
|     provider->changed = TRUE;
 | |
|     RRSetChanged(pScreen);
 | |
| 
 | |
|     RRTellChanged (pScreen);
 | |
| 
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| int
 | |
| ProcRRSetProviderOffloadSink(ClientPtr client)
 | |
| {
 | |
|     REQUEST(xRRSetProviderOffloadSinkReq);
 | |
|     rrScrPrivPtr pScrPriv;
 | |
|     RRProviderPtr provider, sink_provider = NULL;
 | |
|     ScreenPtr pScreen;
 | |
| 
 | |
|     REQUEST_SIZE_MATCH(xRRSetProviderOffloadSinkReq);
 | |
| 
 | |
|     VERIFY_RR_PROVIDER(stuff->provider, provider, DixReadAccess);
 | |
|     if (!(provider->capabilities & RR_Capability_SourceOffload))
 | |
|         return BadValue;
 | |
|     if (!provider->pScreen->isGPU)
 | |
|         return BadValue;
 | |
| 
 | |
|     if (stuff->sink_provider) {
 | |
|         VERIFY_RR_PROVIDER(stuff->sink_provider, sink_provider, DixReadAccess);
 | |
|         if (!(sink_provider->capabilities & RR_Capability_SinkOffload))
 | |
|             return BadValue;
 | |
|     }
 | |
|     pScreen = provider->pScreen;
 | |
|     pScrPriv = rrGetScrPriv(pScreen);
 | |
| 
 | |
|     pScrPriv->rrProviderSetOffloadSink(pScreen, provider, sink_provider);
 | |
| 
 | |
|     provider->changed = TRUE;
 | |
|     RRSetChanged(pScreen);
 | |
| 
 | |
|     RRTellChanged (pScreen);
 | |
| 
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| RRProviderPtr
 | |
| RRProviderCreate(ScreenPtr pScreen, const char *name,
 | |
|                  int nameLength)
 | |
| {
 | |
|     RRProviderPtr provider;
 | |
|     rrScrPrivPtr pScrPriv;
 | |
| 
 | |
|     pScrPriv = rrGetScrPriv(pScreen);
 | |
| 
 | |
|     provider = calloc(1, sizeof(RRProviderRec) + nameLength + 1);
 | |
|     if (!provider)
 | |
|         return NULL;
 | |
| 
 | |
|     provider->id = FakeClientID(0);
 | |
|     provider->pScreen = pScreen;
 | |
|     provider->name = (char *) (provider + 1);
 | |
|     provider->nameLength = nameLength;
 | |
|     memcpy(provider->name, name, nameLength);
 | |
|     provider->name[nameLength] = '\0';
 | |
|     provider->changed = FALSE;
 | |
| 
 | |
|     if (!AddResource (provider->id, RRProviderType, (void *) provider))
 | |
|         return NULL;
 | |
|     pScrPriv->provider = provider;
 | |
|     return provider;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Destroy a provider at shutdown
 | |
|  */
 | |
| void
 | |
| RRProviderDestroy (RRProviderPtr provider)
 | |
| {
 | |
|     RRFiniPrimeSyncProps(provider->pScreen);
 | |
|     FreeResource (provider->id, 0);
 | |
| }
 | |
| 
 | |
| void
 | |
| RRProviderSetCapabilities(RRProviderPtr provider, uint32_t capabilities)
 | |
| {
 | |
|     provider->capabilities = capabilities;
 | |
| }
 | |
| 
 | |
| static int
 | |
| RRProviderDestroyResource (void *value, XID pid)
 | |
| {
 | |
|     RRProviderPtr provider = (RRProviderPtr)value;
 | |
|     ScreenPtr pScreen = provider->pScreen;
 | |
| 
 | |
|     if (pScreen)
 | |
|     {
 | |
|         rrScrPriv(pScreen);
 | |
| 
 | |
|         if (pScrPriv->rrProviderDestroy)
 | |
|             (*pScrPriv->rrProviderDestroy)(pScreen, provider);
 | |
|         pScrPriv->provider = NULL;
 | |
|     }
 | |
|     free(provider);
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| RRProviderInit(void)
 | |
| {
 | |
|     RRProviderType = CreateNewResourceType(RRProviderDestroyResource, "Provider");
 | |
|     if (!RRProviderType)
 | |
|         return FALSE;
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| RRProviderLookup(XID id, RRProviderPtr *provider_p)
 | |
| {
 | |
|     int rc = dixLookupResourceByType((void **)provider_p, id,
 | |
|                                    RRProviderType, NullClient, DixReadAccess);
 | |
|     if (rc == Success)
 | |
|         return TRUE;
 | |
|     return FALSE;
 | |
| }
 | |
| 
 | |
| void
 | |
| RRDeliverProviderEvent(ClientPtr client, WindowPtr pWin, RRProviderPtr provider)
 | |
| {
 | |
|     ScreenPtr pScreen = pWin->drawable.pScreen;
 | |
| 
 | |
|     rrScrPriv(pScreen);
 | |
| 
 | |
|     xRRProviderChangeNotifyEvent pe = {
 | |
|         .type = RRNotify + RREventBase,
 | |
|         .subCode = RRNotify_ProviderChange,
 | |
|         .timestamp = pScrPriv->lastSetTime.milliseconds,
 | |
|         .window = pWin->drawable.id,
 | |
|         .provider = provider->id
 | |
|     };
 | |
| 
 | |
|     WriteEventsToClient(client, 1, (xEvent *) &pe);
 | |
| }
 | |
| 
 | |
| void
 | |
| RRProviderAutoConfigGpuScreen(ScreenPtr pScreen, ScreenPtr primaryScreen)
 | |
| {
 | |
|     rrScrPrivPtr pScrPriv;
 | |
|     rrScrPrivPtr primaryPriv;
 | |
|     RRProviderPtr provider;
 | |
|     RRProviderPtr primary_provider;
 | |
| 
 | |
|     /* Bail out if RandR wasn't initialized. */
 | |
|     if (!dixPrivateKeyRegistered(rrPrivKey))
 | |
|         return;
 | |
| 
 | |
|     pScrPriv = rrGetScrPriv(pScreen);
 | |
|     primaryPriv = rrGetScrPriv(primaryScreen);
 | |
| 
 | |
|     provider = pScrPriv->provider;
 | |
|     primary_provider = primaryPriv->provider;
 | |
| 
 | |
|     if (!provider || !primary_provider)
 | |
|         return;
 | |
| 
 | |
|     if ((provider->capabilities & RR_Capability_SinkOutput) &&
 | |
|         (primary_provider->capabilities & RR_Capability_SourceOutput)) {
 | |
|         pScrPriv->rrProviderSetOutputSource(pScreen, provider, primary_provider);
 | |
|         RRInitPrimeSyncProps(pScreen);
 | |
| 
 | |
|         primaryPriv->configChanged = TRUE;
 | |
|         RRSetChanged(primaryScreen);
 | |
|     }
 | |
| 
 | |
|     if ((provider->capabilities & RR_Capability_SourceOffload) &&
 | |
|         (primary_provider->capabilities & RR_Capability_SinkOffload))
 | |
|         pScrPriv->rrProviderSetOffloadSink(pScreen, provider, primary_provider);
 | |
| }
 |