1/*
2 * Copyright © 2012 Red Hat.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *
22 * Author: Dave Airlie <airlied@redhat.com>
23 */
24
25/*
26 * This file contains the interfaces to the bus-specific code
27 */
28
29#ifdef HAVE_XORG_CONFIG_H
30#include <xorg-config.h>
31#endif
32
33#ifdef XSERVER_PLATFORM_BUS
34#include <errno.h>
35
36#include <pciaccess.h>
37#include <fcntl.h>
38#include <unistd.h>
39#include "os.h"
40#include "hotplug.h"
41#include "systemd-logind.h"
42
43#include "loaderProcs.h"
44#include "xf86.h"
45#include "xf86_OSproc.h"
46#include "xf86Priv.h"
47#include "xf86str.h"
48#include "xf86Bus.h"
49#include "Pci.h"
50#include "xf86platformBus.h"
51#include "xf86Config.h"
52#include "xf86Crtc.h"
53
54#include "randrstr.h"
55int platformSlotClaimed;
56
57int xf86_num_platform_devices;
58
59struct xf86_platform_device *xf86_platform_devices;
60
61int
62xf86_add_platform_device(struct OdevAttributes *attribs, Bool unowned)
63{
64    xf86_platform_devices = xnfreallocarray(xf86_platform_devices,
65                                            xf86_num_platform_devices + 1,
66                                            sizeof(struct xf86_platform_device));
67
68    xf86_platform_devices[xf86_num_platform_devices].attribs = attribs;
69    xf86_platform_devices[xf86_num_platform_devices].pdev = NULL;
70    xf86_platform_devices[xf86_num_platform_devices].flags =
71        unowned ? XF86_PDEV_UNOWNED : 0;
72
73    xf86_num_platform_devices++;
74    return 0;
75}
76
77int
78xf86_remove_platform_device(int dev_index)
79{
80    int j;
81
82    config_odev_free_attributes(xf86_platform_devices[dev_index].attribs);
83
84    for (j = dev_index; j < xf86_num_platform_devices - 1; j++)
85        memcpy(&xf86_platform_devices[j], &xf86_platform_devices[j + 1], sizeof(struct xf86_platform_device));
86    xf86_num_platform_devices--;
87    return 0;
88}
89
90Bool
91xf86_get_platform_device_unowned(int index)
92{
93    return (xf86_platform_devices[index].flags & XF86_PDEV_UNOWNED) ?
94        TRUE : FALSE;
95}
96
97struct xf86_platform_device *
98xf86_find_platform_device_by_devnum(int major, int minor)
99{
100    int i, attr_major, attr_minor;
101
102    for (i = 0; i < xf86_num_platform_devices; i++) {
103        attr_major = xf86_platform_odev_attributes(i)->major;
104        attr_minor = xf86_platform_odev_attributes(i)->minor;
105        if (attr_major == major && attr_minor == minor)
106            return &xf86_platform_devices[i];
107    }
108    return NULL;
109}
110
111/*
112 * xf86IsPrimaryPlatform() -- return TRUE if primary device
113 * is a platform device and it matches this one.
114 */
115
116static Bool
117xf86IsPrimaryPlatform(struct xf86_platform_device *plat)
118{
119    /* Add max. 1 screen for the IgnorePrimary fallback path */
120    if (xf86ProbeIgnorePrimary && xf86NumScreens == 0)
121        return TRUE;
122
123    if (primaryBus.type == BUS_PLATFORM)
124        return plat == primaryBus.id.plat;
125#ifdef XSERVER_LIBPCIACCESS
126    if (primaryBus.type == BUS_PCI)
127        if (plat->pdev)
128            if (MATCH_PCI_DEVICES(primaryBus.id.pci, plat->pdev))
129                return TRUE;
130#endif
131    return FALSE;
132}
133
134static void
135platform_find_pci_info(struct xf86_platform_device *pd, char *busid)
136{
137    struct pci_slot_match devmatch;
138    struct pci_device *info;
139    struct pci_device_iterator *iter;
140    int ret;
141
142    ret = sscanf(busid, "pci:%04x:%02x:%02x.%u",
143                 &devmatch.domain, &devmatch.bus, &devmatch.dev,
144                 &devmatch.func);
145    if (ret != 4)
146        return;
147
148    iter = pci_slot_match_iterator_create(&devmatch);
149    info = pci_device_next(iter);
150    if (info)
151        pd->pdev = info;
152    pci_iterator_destroy(iter);
153}
154
155static Bool
156xf86_check_platform_slot(const struct xf86_platform_device *pd)
157{
158    int i;
159
160    for (i = 0; i < xf86NumEntities; i++) {
161        const EntityPtr u = xf86Entities[i];
162
163        if (pd->pdev && u->bus.type == BUS_PCI &&
164            MATCH_PCI_DEVICES(pd->pdev, u->bus.id.pci)) {
165            return FALSE;
166        }
167        if ((u->bus.type == BUS_PLATFORM) && (pd == u->bus.id.plat)) {
168            return FALSE;
169        }
170    }
171    return TRUE;
172}
173
174static Bool
175MatchToken(const char *value, struct xorg_list *patterns,
176           int (*compare)(const char *, const char *))
177{
178    const xf86MatchGroup *group;
179
180    /* If there are no patterns, accept the match */
181    if (xorg_list_is_empty(patterns))
182        return TRUE;
183
184    /* If there are patterns but no attribute, reject the match */
185    if (!value)
186        return FALSE;
187
188    /*
189     * Otherwise, iterate the list of patterns ensuring each entry has a
190     * match. Each list entry is a separate Match line of the same type.
191     */
192    xorg_list_for_each_entry(group, patterns, entry) {
193        Bool match = FALSE;
194        char *const *cur;
195
196        for (cur = group->values; *cur; cur++) {
197            if ((*compare)(value, *cur) == 0) {
198                match = TRUE;
199                break;
200            }
201        }
202
203        if (!match)
204            return FALSE;
205    }
206
207    /* All the entries in the list matched the attribute */
208    return TRUE;
209}
210
211static Bool
212OutputClassMatches(const XF86ConfOutputClassPtr oclass,
213                   struct xf86_platform_device *dev)
214{
215    char *driver = dev->attribs->driver;
216
217    if (!MatchToken(driver, &oclass->match_driver, strcmp))
218        return FALSE;
219
220    return TRUE;
221}
222
223static void
224xf86OutputClassDriverList(int index, XF86MatchedDrivers *md)
225{
226    XF86ConfOutputClassPtr cl;
227
228    for (cl = xf86configptr->conf_outputclass_lst; cl; cl = cl->list.next) {
229        if (OutputClassMatches(cl, &xf86_platform_devices[index])) {
230            char *path = xf86_platform_odev_attributes(index)->path;
231
232            xf86Msg(X_INFO, "Applying OutputClass \"%s\" to %s\n",
233                    cl->identifier, path);
234            xf86Msg(X_NONE, "\tloading driver: %s\n", cl->driver);
235
236            xf86AddMatchedDriver(md, cl->driver);
237        }
238    }
239}
240
241/**
242 *  @return The numbers of found devices that match with the current system
243 *  drivers.
244 */
245void
246xf86PlatformMatchDriver(XF86MatchedDrivers *md)
247{
248    int i;
249    struct pci_device *info = NULL;
250    int pass = 0;
251
252    for (pass = 0; pass < 2; pass++) {
253        for (i = 0; i < xf86_num_platform_devices; i++) {
254
255            if (xf86IsPrimaryPlatform(&xf86_platform_devices[i]) && (pass == 1))
256                continue;
257            else if (!xf86IsPrimaryPlatform(&xf86_platform_devices[i]) && (pass == 0))
258                continue;
259
260            xf86OutputClassDriverList(i, md);
261
262            info = xf86_platform_devices[i].pdev;
263#ifdef __linux__
264            if (info)
265                xf86MatchDriverFromFiles(info->vendor_id, info->device_id, md);
266#endif
267
268            if (info != NULL) {
269                xf86VideoPtrToDriverList(info, md);
270            }
271        }
272    }
273}
274
275int
276xf86platformProbe(void)
277{
278    int i;
279    Bool pci = TRUE;
280    XF86ConfOutputClassPtr cl, cl_head = (xf86configptr) ?
281            xf86configptr->conf_outputclass_lst : NULL;
282    char *old_path, *path = NULL;
283
284    config_odev_probe(xf86PlatformDeviceProbe);
285
286    if (!xf86scanpci()) {
287        pci = FALSE;
288    }
289
290    for (i = 0; i < xf86_num_platform_devices; i++) {
291        char *busid = xf86_platform_odev_attributes(i)->busid;
292
293        if (pci && busid && (strncmp(busid, "pci:", 4) == 0)) {
294            platform_find_pci_info(&xf86_platform_devices[i], busid);
295        }
296
297        /*
298         * Deal with OutputClass ModulePath directives, these must be
299         * processed before we do any module loading.
300         */
301        for (cl = cl_head; cl; cl = cl->list.next) {
302            if (!OutputClassMatches(cl, &xf86_platform_devices[i]))
303                continue;
304
305            if (cl->modulepath && xf86ModPathFrom != X_CMDLINE) {
306                old_path = path;
307                XNFasprintf(&path, "%s,%s", cl->modulepath,
308                            path ? path : xf86ModulePath);
309                free(old_path);
310                xf86Msg(X_CONFIG, "OutputClass \"%s\" ModulePath extended to \"%s\"\n",
311                        cl->identifier, path);
312                LoaderSetPath(path);
313            }
314        }
315    }
316
317    free(path);
318
319    /* First see if there is an OutputClass match marking a device as primary */
320    for (i = 0; i < xf86_num_platform_devices; i++) {
321        struct xf86_platform_device *dev = &xf86_platform_devices[i];
322        for (cl = cl_head; cl; cl = cl->list.next) {
323            if (!OutputClassMatches(cl, dev))
324                continue;
325
326            if (xf86CheckBoolOption(cl->option_lst, "PrimaryGPU", FALSE)) {
327                xf86Msg(X_CONFIG, "OutputClass \"%s\" setting %s as PrimaryGPU\n",
328                        cl->identifier, dev->attribs->path);
329                primaryBus.type = BUS_PLATFORM;
330                primaryBus.id.plat = dev;
331                return 0;
332            }
333        }
334    }
335
336    /* Then check for pci_device_is_boot_vga() */
337    for (i = 0; i < xf86_num_platform_devices; i++) {
338        struct xf86_platform_device *dev = &xf86_platform_devices[i];
339
340        if (!dev->pdev)
341            continue;
342
343        pci_device_probe(dev->pdev);
344        if (pci_device_is_boot_vga(dev->pdev)) {
345            primaryBus.type = BUS_PLATFORM;
346            primaryBus.id.plat = dev;
347        }
348    }
349
350    return 0;
351}
352
353void
354xf86MergeOutputClassOptions(int entityIndex, void **options)
355{
356    const EntityPtr entity = xf86Entities[entityIndex];
357    struct xf86_platform_device *dev = NULL;
358    XF86ConfOutputClassPtr cl;
359    XF86OptionPtr classopts;
360    int i = 0;
361
362    switch (entity->bus.type) {
363    case BUS_PLATFORM:
364        dev = entity->bus.id.plat;
365        break;
366    case BUS_PCI:
367        for (i = 0; i < xf86_num_platform_devices; i++) {
368            if (xf86_platform_devices[i].pdev) {
369                if (MATCH_PCI_DEVICES(xf86_platform_devices[i].pdev,
370                                      entity->bus.id.pci)) {
371                    dev = &xf86_platform_devices[i];
372                    break;
373                }
374            }
375        }
376        break;
377    default:
378        xf86Msg(X_DEBUG, "xf86MergeOutputClassOptions unsupported bus type %d\n",
379                entity->bus.type);
380    }
381
382    if (!dev)
383        return;
384
385    for (cl = xf86configptr->conf_outputclass_lst; cl; cl = cl->list.next) {
386        if (!OutputClassMatches(cl, dev) || !cl->option_lst)
387            continue;
388
389        xf86Msg(X_INFO, "Applying OutputClass \"%s\" options to %s\n",
390                cl->identifier, dev->attribs->path);
391
392        classopts = xf86optionListDup(cl->option_lst);
393        *options = xf86optionListMerge(*options, classopts);
394    }
395}
396
397static int
398xf86ClaimPlatformSlot(struct xf86_platform_device * d, DriverPtr drvp,
399                  int chipset, GDevPtr dev, Bool active)
400{
401    EntityPtr p = NULL;
402    int num;
403
404    if (xf86_check_platform_slot(d)) {
405        num = xf86AllocateEntity();
406        p = xf86Entities[num];
407        p->driver = drvp;
408        p->chipset = chipset;
409        p->bus.type = BUS_PLATFORM;
410        p->bus.id.plat = d;
411        p->active = active;
412        p->inUse = FALSE;
413        if (dev)
414            xf86AddDevToEntity(num, dev);
415
416        platformSlotClaimed++;
417        return num;
418    }
419    else
420        return -1;
421}
422
423static int
424xf86UnclaimPlatformSlot(struct xf86_platform_device *d, GDevPtr dev)
425{
426    int i;
427
428    for (i = 0; i < xf86NumEntities; i++) {
429        const EntityPtr p = xf86Entities[i];
430
431        if ((p->bus.type == BUS_PLATFORM) && (p->bus.id.plat == d)) {
432            if (dev)
433                xf86RemoveDevFromEntity(i, dev);
434            platformSlotClaimed--;
435            p->bus.type = BUS_NONE;
436            return 0;
437        }
438    }
439    return 0;
440}
441
442
443#define END_OF_MATCHES(m)                                               \
444    (((m).vendor_id == 0) && ((m).device_id == 0) && ((m).subvendor_id == 0))
445
446static Bool doPlatformProbe(struct xf86_platform_device *dev, DriverPtr drvp,
447                            GDevPtr gdev, int flags, intptr_t match_data)
448{
449    Bool foundScreen = FALSE;
450    int entity;
451
452    if (gdev && gdev->screen == 0 && !xf86_check_platform_slot(dev))
453        return FALSE;
454
455    entity = xf86ClaimPlatformSlot(dev, drvp, 0,
456                                   gdev, gdev ? gdev->active : 0);
457
458    if ((entity == -1) && gdev && (gdev->screen > 0)) {
459        unsigned nent;
460
461        for (nent = 0; nent < xf86NumEntities; nent++) {
462            EntityPtr pEnt = xf86Entities[nent];
463
464            if (pEnt->bus.type != BUS_PLATFORM)
465                continue;
466            if (pEnt->bus.id.plat == dev) {
467                entity = nent;
468                xf86AddDevToEntity(nent, gdev);
469                break;
470            }
471        }
472    }
473    if (entity != -1) {
474        if ((dev->flags & XF86_PDEV_SERVER_FD) && (!drvp->driverFunc ||
475                !drvp->driverFunc(NULL, SUPPORTS_SERVER_FDS, NULL))) {
476            systemd_logind_release_fd(dev->attribs->major, dev->attribs->minor, dev->attribs->fd);
477            dev->attribs->fd = -1;
478            dev->flags &= ~XF86_PDEV_SERVER_FD;
479        }
480
481        if (drvp->platformProbe(drvp, entity, flags, dev, match_data))
482            foundScreen = TRUE;
483        else
484            xf86UnclaimPlatformSlot(dev, gdev);
485    }
486    return foundScreen;
487}
488
489static Bool
490probeSingleDevice(struct xf86_platform_device *dev, DriverPtr drvp, GDevPtr gdev, int flags)
491{
492    int k;
493    Bool foundScreen = FALSE;
494    struct pci_device *pPci;
495    const struct pci_id_match *const devices = drvp->supported_devices;
496
497    if (dev->pdev && devices) {
498        int device_id = dev->pdev->device_id;
499        pPci = dev->pdev;
500        for (k = 0; !END_OF_MATCHES(devices[k]); k++) {
501            if (PCI_ID_COMPARE(devices[k].vendor_id, pPci->vendor_id)
502                && PCI_ID_COMPARE(devices[k].device_id, device_id)
503                && ((devices[k].device_class_mask & pPci->device_class)
504                    ==  devices[k].device_class)) {
505                foundScreen = doPlatformProbe(dev, drvp, gdev, flags, devices[k].match_data);
506                if (foundScreen)
507                    break;
508            }
509        }
510    }
511    else if (dev->pdev && !devices)
512        return FALSE;
513    else
514        foundScreen = doPlatformProbe(dev, drvp, gdev, flags, 0);
515    return foundScreen;
516}
517
518static Bool
519isGPUDevice(GDevPtr gdev)
520{
521    int i;
522
523    for (i = 0; i < gdev->myScreenSection->num_gpu_devices; i++) {
524        if (gdev == gdev->myScreenSection->gpu_devices[i])
525            return TRUE;
526    }
527
528    return FALSE;
529}
530
531int
532xf86platformProbeDev(DriverPtr drvp)
533{
534    Bool foundScreen = FALSE;
535    GDevPtr *devList;
536    const unsigned numDevs = xf86MatchDevice(drvp->driverName, &devList);
537    int i, j;
538
539    /* find the main device or any device specified in xorg.conf */
540    for (i = 0; i < numDevs; i++) {
541        const char *devpath;
542
543        /* skip inactive devices */
544        if (!devList[i]->active)
545            continue;
546
547        /* This is specific to modesetting. */
548        devpath = xf86FindOptionValue(devList[i]->options, "kmsdev");
549
550        for (j = 0; j < xf86_num_platform_devices; j++) {
551            if (devpath && *devpath) {
552                if (strcmp(xf86_platform_devices[j].attribs->path, devpath) == 0)
553                    break;
554            } else if (devList[i]->busID && *devList[i]->busID) {
555                if (xf86PlatformDeviceCheckBusID(&xf86_platform_devices[j], devList[i]->busID))
556                    break;
557            }
558            else {
559                /* for non-seat0 servers assume first device is the master */
560                if (ServerIsNotSeat0()) {
561                    break;
562                } else {
563                    /* Accept the device if the driver is simpledrm */
564                    if (strcmp(xf86_platform_devices[j].attribs->driver, "simpledrm") == 0)
565                        break;
566                }
567
568                if (xf86IsPrimaryPlatform(&xf86_platform_devices[j]))
569                    break;
570            }
571        }
572
573        if (j == xf86_num_platform_devices)
574             continue;
575
576        foundScreen = probeSingleDevice(&xf86_platform_devices[j], drvp, devList[i],
577                                        isGPUDevice(devList[i]) ? PLATFORM_PROBE_GPU_SCREEN : 0);
578    }
579
580    free(devList);
581
582    return foundScreen;
583}
584
585int
586xf86platformAddGPUDevices(DriverPtr drvp)
587{
588    Bool foundScreen = FALSE;
589    GDevPtr *devList;
590    int j;
591
592    if (!drvp->platformProbe)
593        return FALSE;
594
595    xf86MatchDevice(drvp->driverName, &devList);
596
597    /* if autoaddgpu devices is enabled then go find any unclaimed platform
598     * devices and add them as GPU screens */
599    if (xf86Info.autoAddGPU) {
600        for (j = 0; j < xf86_num_platform_devices; j++) {
601            if (probeSingleDevice(&xf86_platform_devices[j], drvp,
602                                  devList ?  devList[0] : NULL,
603                                  PLATFORM_PROBE_GPU_SCREEN))
604                foundScreen = TRUE;
605        }
606    }
607
608    free(devList);
609
610    return foundScreen;
611}
612
613int
614xf86platformAddDevice(int index)
615{
616    int i, old_screens, scr_index, scrnum;
617    DriverPtr drvp = NULL;
618    screenLayoutPtr layout;
619    static const char *hotplug_driver_name = "modesetting";
620
621    if (!xf86Info.autoAddGPU)
622        return -1;
623
624    /* force load the driver for now */
625    xf86LoadOneModule(hotplug_driver_name, NULL);
626
627    for (i = 0; i < xf86NumDrivers; i++) {
628        if (!xf86DriverList[i])
629            continue;
630
631        if (!strcmp(xf86DriverList[i]->driverName, hotplug_driver_name)) {
632            drvp = xf86DriverList[i];
633            break;
634        }
635    }
636    if (i == xf86NumDrivers)
637        return -1;
638
639    old_screens = xf86NumGPUScreens;
640    doPlatformProbe(&xf86_platform_devices[index], drvp, NULL,
641                    PLATFORM_PROBE_GPU_SCREEN, 0);
642    if (old_screens == xf86NumGPUScreens)
643        return -1;
644    i = old_screens;
645
646    for (layout = xf86ConfigLayout.screens; layout->screen != NULL;
647         layout++) {
648        xf86GPUScreens[i]->confScreen = layout->screen;
649        break;
650    }
651
652    if (xf86GPUScreens[i]->PreInit &&
653        xf86GPUScreens[i]->PreInit(xf86GPUScreens[i], 0))
654        xf86GPUScreens[i]->configured = TRUE;
655
656    if (!xf86GPUScreens[i]->configured) {
657        ErrorF("hotplugged device %d didn't configure\n", i);
658        xf86DeleteScreen(xf86GPUScreens[i]);
659        return -1;
660    }
661
662   scr_index = AddGPUScreen(xf86GPUScreens[i]->ScreenInit, 0, NULL);
663   if (scr_index == -1) {
664       xf86DeleteScreen(xf86GPUScreens[i]);
665       xf86UnclaimPlatformSlot(&xf86_platform_devices[index], NULL);
666       xf86NumGPUScreens = old_screens;
667       return -1;
668   }
669   dixSetPrivate(&xf86GPUScreens[i]->pScreen->devPrivates,
670                 xf86ScreenKey, xf86GPUScreens[i]);
671
672   CreateScratchPixmapsForScreen(xf86GPUScreens[i]->pScreen);
673
674   if (xf86GPUScreens[i]->pScreen->CreateScreenResources &&
675       !(*xf86GPUScreens[i]->pScreen->CreateScreenResources) (xf86GPUScreens[i]->pScreen)) {
676       RemoveGPUScreen(xf86GPUScreens[i]->pScreen);
677       xf86DeleteScreen(xf86GPUScreens[i]);
678       xf86UnclaimPlatformSlot(&xf86_platform_devices[index], NULL);
679       xf86NumGPUScreens = old_screens;
680       return -1;
681   }
682   /* attach unbound to the configured protocol screen (or 0) */
683   scrnum = xf86GPUScreens[i]->confScreen->screennum;
684   AttachUnboundGPU(xf86Screens[scrnum]->pScreen, xf86GPUScreens[i]->pScreen);
685   if (xf86Info.autoBindGPU)
686       RRProviderAutoConfigGpuScreen(xf86ScrnToScreen(xf86GPUScreens[i]),
687                                     xf86ScrnToScreen(xf86Screens[scrnum]));
688
689   RRResourcesChanged(xf86Screens[scrnum]->pScreen);
690   RRTellChanged(xf86Screens[scrnum]->pScreen);
691
692   return 0;
693}
694
695void
696xf86platformRemoveDevice(int index)
697{
698    EntityPtr entity;
699    int ent_num, i, j, scrnum;
700    Bool found;
701
702    for (ent_num = 0; ent_num < xf86NumEntities; ent_num++) {
703        entity = xf86Entities[ent_num];
704        if (entity->bus.type == BUS_PLATFORM &&
705            entity->bus.id.plat == &xf86_platform_devices[index])
706            break;
707    }
708    if (ent_num == xf86NumEntities)
709        goto out;
710
711    found = FALSE;
712    for (i = 0; i < xf86NumGPUScreens; i++) {
713        for (j = 0; j < xf86GPUScreens[i]->numEntities; j++)
714            if (xf86GPUScreens[i]->entityList[j] == ent_num) {
715                found = TRUE;
716                break;
717            }
718        if (found)
719            break;
720    }
721    if (!found) {
722        ErrorF("failed to find screen to remove\n");
723        goto out;
724    }
725
726    scrnum = xf86GPUScreens[i]->confScreen->screennum;
727
728    xf86GPUScreens[i]->pScreen->CloseScreen(xf86GPUScreens[i]->pScreen);
729
730    RemoveGPUScreen(xf86GPUScreens[i]->pScreen);
731    xf86DeleteScreen(xf86GPUScreens[i]);
732
733    xf86UnclaimPlatformSlot(&xf86_platform_devices[index], NULL);
734
735    xf86_remove_platform_device(index);
736
737    RRResourcesChanged(xf86Screens[scrnum]->pScreen);
738    RRTellChanged(xf86Screens[scrnum]->pScreen);
739 out:
740    return;
741}
742
743/* called on return from VT switch to find any new devices */
744void xf86platformVTProbe(void)
745{
746    int i;
747
748    for (i = 0; i < xf86_num_platform_devices; i++) {
749        if (!(xf86_platform_devices[i].flags & XF86_PDEV_UNOWNED))
750            continue;
751
752        xf86_platform_devices[i].flags &= ~XF86_PDEV_UNOWNED;
753        xf86PlatformReprobeDevice(i, xf86_platform_devices[i].attribs);
754    }
755}
756
757void xf86platformPrimary(void)
758{
759    /* use the first platform device as a fallback */
760    if (primaryBus.type == BUS_NONE) {
761        xf86Msg(X_INFO, "no primary bus or device found\n");
762
763        if (xf86_num_platform_devices > 0) {
764            primaryBus.id.plat = &xf86_platform_devices[0];
765            primaryBus.type = BUS_PLATFORM;
766
767            xf86Msg(X_NONE, "\tfalling back to %s\n", primaryBus.id.plat->attribs->syspath);
768        }
769    }
770}
771#endif
772