xorg_driver.c revision 956cd73c23269e4f76cbfee9076ec6eeb1f3262a
1/*
2 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 *
26 * Author: Alan Hourihane <alanh@tungstengraphics.com>
27 * Author: Jakob Bornecrantz <wallbraker@gmail.com>
28 *
29 */
30
31
32#include "xorg-server.h"
33#include "xf86.h"
34#include "xf86_OSproc.h"
35#include "compiler.h"
36#include "xf86PciInfo.h"
37#include "xf86Pci.h"
38#include "mipointer.h"
39#include "micmap.h"
40#include <X11/extensions/randr.h>
41#include "fb.h"
42#include "edid.h"
43#include "xf86i2c.h"
44#include "xf86Crtc.h"
45#include "miscstruct.h"
46#include "dixstruct.h"
47#include "xf86xv.h"
48#ifndef XSERVER_LIBPCIACCESS
49#error "libpciaccess needed"
50#endif
51
52#include <pciaccess.h>
53
54#include "pipe/p_context.h"
55#include "xorg_tracker.h"
56#include "xorg_winsys.h"
57
58#ifdef HAVE_LIBKMS
59#include "libkms.h"
60#endif
61
62/*
63 * Functions and symbols exported to Xorg via pointers.
64 */
65
66static Bool drv_pre_init(ScrnInfoPtr pScrn, int flags);
67static Bool drv_screen_init(int scrnIndex, ScreenPtr pScreen, int argc,
68			    char **argv);
69static Bool drv_switch_mode(int scrnIndex, DisplayModePtr mode, int flags);
70static void drv_adjust_frame(int scrnIndex, int x, int y, int flags);
71static Bool drv_enter_vt(int scrnIndex, int flags);
72static void drv_leave_vt(int scrnIndex, int flags);
73static void drv_free_screen(int scrnIndex, int flags);
74static ModeStatus drv_valid_mode(int scrnIndex, DisplayModePtr mode, Bool verbose,
75			         int flags);
76
77typedef enum
78{
79    OPTION_SW_CURSOR,
80    OPTION_2D_ACCEL,
81    OPTION_DEBUG_FALLBACK,
82} drv_option_enums;
83
84static const OptionInfoRec drv_options[] = {
85    {OPTION_SW_CURSOR, "SWcursor", OPTV_BOOLEAN, {0}, FALSE},
86    {OPTION_2D_ACCEL, "2DAccel", OPTV_BOOLEAN, {0}, FALSE},
87    {OPTION_DEBUG_FALLBACK, "DebugFallback", OPTV_BOOLEAN, {0}, FALSE},
88    {-1, NULL, OPTV_NONE, {0}, FALSE}
89};
90
91
92/*
93 * Exported Xorg driver functions to winsys
94 */
95
96const OptionInfoRec *
97xorg_tracker_available_options(int chipid, int busid)
98{
99    return drv_options;
100}
101
102void
103xorg_tracker_set_functions(ScrnInfoPtr scrn)
104{
105    scrn->PreInit = drv_pre_init;
106    scrn->ScreenInit = drv_screen_init;
107    scrn->SwitchMode = drv_switch_mode;
108    scrn->AdjustFrame = drv_adjust_frame;
109    scrn->EnterVT = drv_enter_vt;
110    scrn->LeaveVT = drv_leave_vt;
111    scrn->FreeScreen = drv_free_screen;
112    scrn->ValidMode = drv_valid_mode;
113}
114
115Bool
116xorg_tracker_have_modesetting(ScrnInfoPtr pScrn, struct pci_device *device)
117{
118    char *BusID = xalloc(64);
119    sprintf(BusID, "pci:%04x:%02x:%02x.%d",
120	    device->domain, device->bus,
121	    device->dev, device->func);
122
123    if (drmCheckModesettingSupported(BusID)) {
124	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 0,
125		       "Drm modesetting not supported %s\n", BusID);
126	xfree(BusID);
127	return FALSE;
128    }
129
130    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 0,
131		   "Drm modesetting supported on %s\n", BusID);
132
133    xfree(BusID);
134    return TRUE;
135}
136
137
138/*
139 * Internal function definitions
140 */
141
142static Bool drv_init_front_buffer_functions(ScrnInfoPtr pScrn);
143static Bool drv_close_screen(int scrnIndex, ScreenPtr pScreen);
144static Bool drv_save_hw_state(ScrnInfoPtr pScrn);
145static Bool drv_restore_hw_state(ScrnInfoPtr pScrn);
146
147
148/*
149 * Internal functions
150 */
151
152static Bool
153drv_get_rec(ScrnInfoPtr pScrn)
154{
155    if (pScrn->driverPrivate)
156	return TRUE;
157
158    pScrn->driverPrivate = xnfcalloc(1, sizeof(modesettingRec));
159
160    return TRUE;
161}
162
163static void
164drv_free_rec(ScrnInfoPtr pScrn)
165{
166    if (!pScrn)
167	return;
168
169    if (!pScrn->driverPrivate)
170	return;
171
172    xfree(pScrn->driverPrivate);
173
174    pScrn->driverPrivate = NULL;
175}
176
177static void
178drv_probe_ddc(ScrnInfoPtr pScrn, int index)
179{
180    ConfiguredMonitor = NULL;
181}
182
183static Bool
184drv_crtc_resize(ScrnInfoPtr pScrn, int width, int height)
185{
186    modesettingPtr ms = modesettingPTR(pScrn);
187    ScreenPtr pScreen = pScrn->pScreen;
188    int old_width, old_height;
189    PixmapPtr rootPixmap;
190
191    if (width == pScrn->virtualX && height == pScrn->virtualY)
192	return TRUE;
193
194    old_width = pScrn->virtualX;
195    old_height = pScrn->virtualY;
196    pScrn->virtualX = width;
197    pScrn->virtualY = height;
198
199    /* ms->create_front_buffer will remove the old front buffer */
200
201    rootPixmap = pScreen->GetScreenPixmap(pScreen);
202    if (!pScreen->ModifyPixmapHeader(rootPixmap, width, height, -1, -1, -1, NULL))
203	goto error_modify;
204
205    pScrn->displayWidth = rootPixmap->devKind / (rootPixmap->drawable.bitsPerPixel / 8);
206
207    if (ms->create_front_buffer(pScrn) && ms->bind_front_buffer(pScrn))
208	return TRUE;
209
210    /*
211     * This is the error recovery path.
212     */
213
214    if (!pScreen->ModifyPixmapHeader(rootPixmap, old_width, old_height, -1, -1, -1, NULL))
215	FatalError("failed to resize rootPixmap error path\n");
216
217    pScrn->displayWidth = rootPixmap->devKind / (rootPixmap->drawable.bitsPerPixel / 8);
218
219error_modify:
220    pScrn->virtualX = old_width;
221    pScrn->virtualY = old_height;
222
223    if (ms->create_front_buffer(pScrn) && ms->bind_front_buffer(pScrn))
224	return FALSE;
225
226    FatalError("failed to setup old framebuffer\n");
227    return FALSE;
228}
229
230static const xf86CrtcConfigFuncsRec crtc_config_funcs = {
231    .resize = drv_crtc_resize
232};
233
234static Bool
235drv_init_drm(ScrnInfoPtr pScrn)
236{
237    modesettingPtr ms = modesettingPTR(pScrn);
238
239    /* deal with server regeneration */
240    if (ms->fd < 0) {
241	char *BusID;
242
243	BusID = xalloc(64);
244	sprintf(BusID, "PCI:%d:%d:%d",
245		((ms->PciInfo->domain << 8) | ms->PciInfo->bus),
246		ms->PciInfo->dev, ms->PciInfo->func
247	    );
248
249
250	ms->api = drm_api_create();
251	ms->fd = drmOpen(ms->api ? ms->api->driver_name : NULL, BusID);
252	xfree(BusID);
253
254	if (ms->fd >= 0)
255	    return TRUE;
256
257	if (ms->api && ms->api->destroy)
258	    ms->api->destroy(ms->api);
259
260	ms->api = NULL;
261
262	return FALSE;
263    }
264
265    return TRUE;
266}
267
268static Bool
269drv_close_drm(ScrnInfoPtr pScrn)
270{
271    modesettingPtr ms = modesettingPTR(pScrn);
272
273    if (ms->api && ms->api->destroy)
274	ms->api->destroy(ms->api);
275    ms->api = NULL;
276
277    drmClose(ms->fd);
278    ms->fd = -1;
279
280    return TRUE;
281}
282
283static Bool
284drv_init_resource_management(ScrnInfoPtr pScrn)
285{
286    modesettingPtr ms = modesettingPTR(pScrn);
287    /*
288    ScreenPtr pScreen = pScrn->pScreen;
289    PixmapPtr rootPixmap = pScreen->GetScreenPixmap(pScreen);
290    Bool fbAccessDisabled;
291    CARD8 *fbstart;
292     */
293
294    if (ms->screen || ms->kms)
295	return TRUE;
296
297    if (ms->api) {
298	ms->screen = ms->api->create_screen(ms->api, ms->fd, NULL);
299
300	if (ms->screen)
301	    return TRUE;
302
303	if (ms->api->destroy)
304	    ms->api->destroy(ms->api);
305
306	ms->api = NULL;
307    }
308
309#ifdef HAVE_LIBKMS
310    if (!kms_create(ms->fd, &ms->kms))
311	return TRUE;
312#endif
313
314    return FALSE;
315}
316
317static Bool
318drv_close_resource_management(ScrnInfoPtr pScrn)
319{
320    modesettingPtr ms = modesettingPTR(pScrn);
321    int i;
322
323    if (ms->screen) {
324	assert(ms->ctx == NULL);
325
326	for (i = 0; i < XORG_NR_FENCES; i++) {
327	    if (ms->fence[i]) {
328		ms->screen->fence_finish(ms->screen, ms->fence[i], 0);
329		ms->screen->fence_reference(ms->screen, &ms->fence[i], NULL);
330	    }
331	}
332	ms->screen->destroy(ms->screen);
333    }
334    ms->screen = NULL;
335
336#ifdef HAVE_LIBKMS
337    if (ms->kms)
338	kms_destroy(&ms->kms);
339#endif
340
341    return TRUE;
342}
343
344static Bool
345drv_pre_init(ScrnInfoPtr pScrn, int flags)
346{
347    xf86CrtcConfigPtr xf86_config;
348    modesettingPtr ms;
349    rgb defaultWeight = { 0, 0, 0 };
350    EntityInfoPtr pEnt;
351    EntPtr msEnt = NULL;
352    int max_width, max_height;
353    CustomizerPtr cust;
354
355    if (pScrn->numEntities != 1)
356	return FALSE;
357
358    pEnt = xf86GetEntityInfo(pScrn->entityList[0]);
359
360    if (flags & PROBE_DETECT) {
361	drv_probe_ddc(pScrn, pEnt->index);
362	return TRUE;
363    }
364
365    cust = (CustomizerPtr) pScrn->driverPrivate;
366    pScrn->driverPrivate = NULL;
367
368    /* Allocate driverPrivate */
369    if (!drv_get_rec(pScrn))
370	return FALSE;
371
372    ms = modesettingPTR(pScrn);
373    ms->SaveGeneration = -1;
374    ms->pEnt = pEnt;
375    ms->cust = cust;
376
377    pScrn->displayWidth = 640;	       /* default it */
378
379    if (ms->pEnt->location.type != BUS_PCI)
380	return FALSE;
381
382    ms->PciInfo = xf86GetPciInfoForEntity(ms->pEnt->index);
383
384    /* Allocate an entity private if necessary */
385    if (xf86IsEntityShared(pScrn->entityList[0])) {
386	FatalError("Entity");
387#if 0
388	msEnt = xf86GetEntityPrivate(pScrn->entityList[0],
389				     modesettingEntityIndex)->ptr;
390	ms->entityPrivate = msEnt;
391#else
392	(void)msEnt;
393#endif
394    } else
395	ms->entityPrivate = NULL;
396
397    if (xf86IsEntityShared(pScrn->entityList[0])) {
398	if (xf86IsPrimInitDone(pScrn->entityList[0])) {
399	    /* do something */
400	} else {
401	    xf86SetPrimInitDone(pScrn->entityList[0]);
402	}
403    }
404
405    ms->fd = -1;
406    ms->api = NULL;
407    if (!drv_init_drm(pScrn))
408	return FALSE;
409
410    pScrn->monitor = pScrn->confScreen->monitor;
411    pScrn->progClock = TRUE;
412    pScrn->rgbBits = 8;
413
414    if (!xf86SetDepthBpp
415	(pScrn, 0, 0, 0,
416	 PreferConvert24to32 | SupportConvert24to32 | Support32bppFb))
417	return FALSE;
418
419    switch (pScrn->depth) {
420    case 15:
421    case 16:
422    case 24:
423	break;
424    default:
425	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
426		   "Given depth (%d) is not supported by the driver\n",
427		   pScrn->depth);
428	return FALSE;
429    }
430    xf86PrintDepthBpp(pScrn);
431
432    if (!xf86SetWeight(pScrn, defaultWeight, defaultWeight))
433	return FALSE;
434    if (!xf86SetDefaultVisual(pScrn, -1))
435	return FALSE;
436
437    /* Process the options */
438    xf86CollectOptions(pScrn, NULL);
439    if (!(ms->Options = xalloc(sizeof(drv_options))))
440	return FALSE;
441    memcpy(ms->Options, drv_options, sizeof(drv_options));
442    xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, ms->Options);
443
444    /* Allocate an xf86CrtcConfig */
445    xf86CrtcConfigInit(pScrn, &crtc_config_funcs);
446    xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
447
448    max_width = 2048;  /* A very low default */
449    max_height = 2048; /* see screen_init */
450    xf86CrtcSetSizeRange(pScrn, 320, 200, max_width, max_height);
451
452    if (xf86ReturnOptValBool(ms->Options, OPTION_SW_CURSOR, FALSE)) {
453	ms->SWCursor = TRUE;
454    }
455
456    drv_save_hw_state(pScrn);
457
458    xorg_crtc_init(pScrn);
459    xorg_output_init(pScrn);
460
461    if (!xf86InitialConfiguration(pScrn, TRUE)) {
462	xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes.\n");
463	drv_restore_hw_state(pScrn);
464	return FALSE;
465    }
466
467    drv_restore_hw_state(pScrn);
468
469    /*
470     * If the driver can do gamma correction, it should call xf86SetGamma() here.
471     */
472    {
473	Gamma zeros = { 0.0, 0.0, 0.0 };
474
475	if (!xf86SetGamma(pScrn, zeros)) {
476	    return FALSE;
477	}
478    }
479
480    if (pScrn->modes == NULL) {
481	xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No modes.\n");
482	return FALSE;
483    }
484
485    pScrn->currentMode = pScrn->modes;
486
487    /* Set display resolution */
488    xf86SetDpi(pScrn, 0, 0);
489
490    /* Load the required sub modules */
491    if (!xf86LoadSubModule(pScrn, "fb"))
492	return FALSE;
493
494    /* XXX: these aren't needed when we are using libkms */
495    if (!xf86LoadSubModule(pScrn, "exa"))
496	return FALSE;
497
498#ifdef DRI2
499    if (!xf86LoadSubModule(pScrn, "dri2"))
500	return FALSE;
501#endif
502
503    return TRUE;
504}
505
506static Bool
507drv_save_hw_state(ScrnInfoPtr pScrn)
508{
509    /*xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);*/
510
511    return TRUE;
512}
513
514static Bool
515drv_restore_hw_state(ScrnInfoPtr pScrn)
516{
517    /*xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);*/
518
519    return TRUE;
520}
521
522static void drv_block_handler(int i, pointer blockData, pointer pTimeout,
523                              pointer pReadmask)
524{
525    ScreenPtr pScreen = screenInfo.screens[i];
526    modesettingPtr ms = modesettingPTR(xf86Screens[pScreen->myNum]);
527
528    pScreen->BlockHandler = ms->blockHandler;
529    pScreen->BlockHandler(i, blockData, pTimeout, pReadmask);
530    pScreen->BlockHandler = drv_block_handler;
531
532    if (ms->ctx) {
533       int j;
534
535       ms->ctx->flush(ms->ctx, PIPE_FLUSH_RENDER_CACHE, &ms->fence[XORG_NR_FENCES-1]);
536
537       if (ms->fence[0])
538          ms->ctx->screen->fence_finish(ms->ctx->screen, ms->fence[0], 0);
539
540       /* The amount of rendering generated by a block handler can be
541        * quite small.  Let us get a fair way ahead of hardware before
542        * throttling.
543        */
544       for (j = 0; j < XORG_NR_FENCES - 1; j++)
545          ms->screen->fence_reference(ms->screen,
546                                      &ms->fence[j],
547                                      ms->fence[j+1]);
548
549       ms->screen->fence_reference(ms->screen,
550                                   &ms->fence[XORG_NR_FENCES-1],
551                                   NULL);
552    }
553
554
555#ifdef DRM_MODE_FEATURE_DIRTYFB
556    {
557	RegionPtr dirty = DamageRegion(ms->damage);
558	unsigned num_cliprects = REGION_NUM_RECTS(dirty);
559
560	if (num_cliprects) {
561	    drmModeClip *clip = alloca(num_cliprects * sizeof(drmModeClip));
562	    BoxPtr rect = REGION_RECTS(dirty);
563	    int i, ret;
564
565	    /* XXX no need for copy? */
566	    for (i = 0; i < num_cliprects; i++, rect++) {
567		clip[i].x1 = rect->x1;
568		clip[i].y1 = rect->y1;
569		clip[i].x2 = rect->x2;
570		clip[i].y2 = rect->y2;
571	    }
572
573	    /* TODO query connector property to see if this is needed */
574	    ret = drmModeDirtyFB(ms->fd, ms->fb_id, clip, num_cliprects);
575	    if (ret) {
576		debug_printf("%s: failed to send dirty (%i, %s)\n",
577			     __func__, ret, strerror(-ret));
578	    }
579
580	    DamageEmpty(ms->damage);
581	}
582    }
583#endif
584}
585
586static Bool
587drv_create_screen_resources(ScreenPtr pScreen)
588{
589    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
590    modesettingPtr ms = modesettingPTR(pScrn);
591    PixmapPtr rootPixmap;
592    Bool ret;
593
594    ms->noEvict = TRUE;
595
596    pScreen->CreateScreenResources = ms->createScreenResources;
597    ret = pScreen->CreateScreenResources(pScreen);
598    pScreen->CreateScreenResources = drv_create_screen_resources;
599
600    ms->bind_front_buffer(pScrn);
601
602    ms->noEvict = FALSE;
603
604    drv_adjust_frame(pScrn->scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);
605
606#ifdef DRM_MODE_FEATURE_DIRTYFB
607    rootPixmap = pScreen->GetScreenPixmap(pScreen);
608    ms->damage = DamageCreate(NULL, NULL, DamageReportNone, TRUE,
609                              pScreen, rootPixmap);
610
611    if (ms->damage) {
612       DamageRegister(&rootPixmap->drawable, ms->damage);
613
614       xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Damage tracking initialized\n");
615    } else {
616       xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
617                  "Failed to create screen damage record\n");
618       return FALSE;
619    }
620#else
621    (void)rootPixmap;
622#endif
623
624    return ret;
625}
626
627static Bool
628drv_screen_init(int scrnIndex, ScreenPtr pScreen, int argc, char **argv)
629{
630    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
631    modesettingPtr ms = modesettingPTR(pScrn);
632    unsigned max_width, max_height;
633    VisualPtr visual;
634    CustomizerPtr cust = ms->cust;
635
636    if (!drv_init_drm(pScrn)) {
637	FatalError("Could not init DRM");
638	return FALSE;
639    }
640
641    if (!drv_init_resource_management(pScrn)) {
642	FatalError("Could not init resource management (!pipe_screen && !libkms)");
643	return FALSE;
644    }
645
646    if (!drv_init_front_buffer_functions(pScrn)) {
647	FatalError("Could not init front buffer manager");
648	return FALSE;
649    }
650
651    /* get max width and height */
652    {
653	drmModeResPtr res;
654	res = drmModeGetResources(ms->fd);
655	max_width = res->max_width;
656	max_height = res->max_height;
657	drmModeFreeResources(res);
658    }
659
660    if (ms->screen) {
661	float maxf;
662	int max;
663	maxf = ms->screen->get_paramf(ms->screen, PIPE_CAP_MAX_TEXTURE_2D_LEVELS);
664	max = (1 << (int)(maxf - 1.0f));
665	max_width = max < max_width ? max : max_width;
666	max_height = max < max_height ? max : max_height;
667    }
668
669    xf86CrtcSetSizeRange(pScrn, 1, 1, max_width, max_height);
670
671    pScrn->pScreen = pScreen;
672
673    /* HW dependent - FIXME */
674    pScrn->displayWidth = pScrn->virtualX;
675
676    miClearVisualTypes();
677
678    if (!miSetVisualTypes(pScrn->depth,
679			  miGetDefaultVisualMask(pScrn->depth),
680			  pScrn->rgbBits, pScrn->defaultVisual))
681	return FALSE;
682
683    if (!miSetPixmapDepths())
684	return FALSE;
685
686    pScrn->memPhysBase = 0;
687    pScrn->fbOffset = 0;
688
689    if (!fbScreenInit(pScreen, NULL,
690		      pScrn->virtualX, pScrn->virtualY,
691		      pScrn->xDpi, pScrn->yDpi,
692		      pScrn->displayWidth, pScrn->bitsPerPixel))
693	return FALSE;
694
695    if (pScrn->bitsPerPixel > 8) {
696	/* Fixup RGB ordering */
697	visual = pScreen->visuals + pScreen->numVisuals;
698	while (--visual >= pScreen->visuals) {
699	    if ((visual->class | DynamicClass) == DirectColor) {
700		visual->offsetRed = pScrn->offset.red;
701		visual->offsetGreen = pScrn->offset.green;
702		visual->offsetBlue = pScrn->offset.blue;
703		visual->redMask = pScrn->mask.red;
704		visual->greenMask = pScrn->mask.green;
705		visual->blueMask = pScrn->mask.blue;
706	    }
707	}
708    }
709
710    fbPictureInit(pScreen, NULL, 0);
711
712    ms->blockHandler = pScreen->BlockHandler;
713    pScreen->BlockHandler = drv_block_handler;
714    ms->createScreenResources = pScreen->CreateScreenResources;
715    pScreen->CreateScreenResources = drv_create_screen_resources;
716
717    xf86SetBlackWhitePixels(pScreen);
718
719    ms->accelerate_2d = xf86ReturnOptValBool(ms->Options, OPTION_2D_ACCEL, FALSE);
720    ms->debug_fallback = xf86ReturnOptValBool(ms->Options, OPTION_DEBUG_FALLBACK, ms->accelerate_2d);
721
722    if (ms->screen) {
723	ms->exa = xorg_exa_init(pScrn, ms->accelerate_2d);
724
725	xorg_xv_init(pScreen);
726#ifdef DRI2
727	xorg_dri2_init(pScreen);
728#endif
729    }
730
731    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "##################################\n");
732    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "# Usefull debugging info follows #\n");
733    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "##################################\n");
734    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Using %s backend\n",
735	       ms->screen ? "Gallium3D" : "libkms");
736    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "2D Acceleration is %s\n",
737	       ms->screen && ms->accelerate_2d ? "enabled" : "disabled");
738    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Fallback debugging is %s\n",
739	       ms->debug_fallback ? "enabled" : "disabled");
740#ifdef DRI2
741    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "3D Acceleration is %s\n",
742	       ms->screen ? "enabled" : "disabled");
743#else
744    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "3D Acceleration is disabled\n");
745#endif
746    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "##################################\n");
747
748    miInitializeBackingStore(pScreen);
749    xf86SetBackingStore(pScreen);
750    xf86SetSilkenMouse(pScreen);
751    miDCInitialize(pScreen, xf86GetPointerScreenFuncs());
752
753    /* Need to extend HWcursor support to handle mask interleave */
754    if (!ms->SWCursor)
755	xf86_cursors_init(pScreen, 64, 64,
756			  HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_64 |
757			  HARDWARE_CURSOR_ARGB);
758
759    /* Must force it before EnterVT, so we are in control of VT and
760     * later memory should be bound when allocating, e.g rotate_mem */
761    pScrn->vtSema = TRUE;
762
763    pScreen->SaveScreen = xf86SaveScreen;
764    ms->CloseScreen = pScreen->CloseScreen;
765    pScreen->CloseScreen = drv_close_screen;
766
767    if (!xf86CrtcScreenInit(pScreen))
768	return FALSE;
769
770    if (!miCreateDefColormap(pScreen))
771	return FALSE;
772
773    xf86DPMSInit(pScreen, xf86DPMSSet, 0);
774
775    if (serverGeneration == 1)
776	xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);
777
778    if (cust && cust->winsys_screen_init)
779	cust->winsys_screen_init(cust, ms->fd);
780
781    return drv_enter_vt(scrnIndex, 1);
782}
783
784static void
785drv_adjust_frame(int scrnIndex, int x, int y, int flags)
786{
787    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
788    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
789    xf86OutputPtr output = config->output[config->compat_output];
790    xf86CrtcPtr crtc = output->crtc;
791
792    if (crtc && crtc->enabled) {
793	crtc->funcs->set_mode_major(crtc, pScrn->currentMode,
794				    RR_Rotate_0, x, y);
795	crtc->x = output->initial_x + x;
796	crtc->y = output->initial_y + y;
797    }
798}
799
800static void
801drv_free_screen(int scrnIndex, int flags)
802{
803    drv_free_rec(xf86Screens[scrnIndex]);
804}
805
806static void
807drv_leave_vt(int scrnIndex, int flags)
808{
809    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
810    modesettingPtr ms = modesettingPTR(pScrn);
811    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
812    CustomizerPtr cust = ms->cust;
813    int o;
814
815    if (cust && cust->winsys_leave_vt)
816	cust->winsys_leave_vt(cust);
817
818    for (o = 0; o < config->num_crtc; o++) {
819	xf86CrtcPtr crtc = config->crtc[o];
820
821	xorg_crtc_cursor_destroy(crtc);
822
823	if (crtc->rotatedPixmap || crtc->rotatedData) {
824	    crtc->funcs->shadow_destroy(crtc, crtc->rotatedPixmap,
825					crtc->rotatedData);
826	    crtc->rotatedPixmap = NULL;
827	    crtc->rotatedData = NULL;
828	}
829    }
830
831    drmModeRmFB(ms->fd, ms->fb_id);
832    ms->fb_id = -1;
833
834    drv_restore_hw_state(pScrn);
835
836    if (drmDropMaster(ms->fd))
837	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
838		   "drmDropMaster failed: %s\n", strerror(errno));
839
840    pScrn->vtSema = FALSE;
841}
842
843/*
844 * This gets called when gaining control of the VT, and from ScreenInit().
845 */
846static Bool
847drv_enter_vt(int scrnIndex, int flags)
848{
849    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
850    modesettingPtr ms = modesettingPTR(pScrn);
851    CustomizerPtr cust = ms->cust;
852
853    if (drmSetMaster(ms->fd)) {
854	if (errno == EINVAL) {
855	    xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
856		       "drmSetMaster failed: 2.6.29 or newer kernel required for "
857		       "multi-server DRI\n");
858	} else {
859	    xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
860		       "drmSetMaster failed: %s\n", strerror(errno));
861	}
862    }
863
864    /*
865     * Only save state once per server generation since that's what most
866     * drivers do.  Could change this to save state at each VT enter.
867     */
868    if (ms->SaveGeneration != serverGeneration) {
869	ms->SaveGeneration = serverGeneration;
870	drv_save_hw_state(pScrn);
871    }
872
873    if (!ms->create_front_buffer(pScrn))
874	return FALSE;
875
876    if (!flags && !ms->bind_front_buffer(pScrn))
877	return FALSE;
878
879    if (!xf86SetDesiredModes(pScrn))
880	return FALSE;
881
882    if (cust && cust->winsys_enter_vt)
883	cust->winsys_enter_vt(cust);
884
885    return TRUE;
886}
887
888static Bool
889drv_switch_mode(int scrnIndex, DisplayModePtr mode, int flags)
890{
891    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
892
893    return xf86SetSingleMode(pScrn, mode, RR_Rotate_0);
894}
895
896static Bool
897drv_close_screen(int scrnIndex, ScreenPtr pScreen)
898{
899    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
900    modesettingPtr ms = modesettingPTR(pScrn);
901    CustomizerPtr cust = ms->cust;
902
903    if (pScrn->vtSema) {
904	drv_leave_vt(scrnIndex, 0);
905    }
906
907    if (cust && cust->winsys_screen_close)
908	cust->winsys_screen_close(cust);
909
910#ifdef DRI2
911    if (ms->screen)
912	xorg_dri2_close(pScreen);
913#endif
914
915    pScreen->BlockHandler = ms->blockHandler;
916    pScreen->CreateScreenResources = ms->createScreenResources;
917
918#ifdef DRM_MODE_FEATURE_DIRTYFB
919    if (ms->damage) {
920	DamageUnregister(&pScreen->GetScreenPixmap(pScreen)->drawable, ms->damage);
921	DamageDestroy(ms->damage);
922	ms->damage = NULL;
923    }
924#endif
925
926    drmModeRmFB(ms->fd, ms->fb_id);
927    ms->destroy_front_buffer(pScrn);
928
929    if (ms->exa)
930	xorg_exa_close(pScrn);
931    ms->exa = NULL;
932
933    drv_close_resource_management(pScrn);
934
935    drv_close_drm(pScrn);
936
937    pScrn->vtSema = FALSE;
938    pScreen->CloseScreen = ms->CloseScreen;
939    return (*pScreen->CloseScreen) (scrnIndex, pScreen);
940}
941
942static ModeStatus
943drv_valid_mode(int scrnIndex, DisplayModePtr mode, Bool verbose, int flags)
944{
945    return MODE_OK;
946}
947
948
949/*
950 * Front buffer backing store functions.
951 */
952
953static Bool
954drv_destroy_front_buffer_ga3d(ScrnInfoPtr pScrn)
955{
956    modesettingPtr ms = modesettingPTR(pScrn);
957
958    if (!ms->root_bo)
959	return TRUE;
960
961    if (ms->fb_id != -1) {
962	drmModeRmFB(ms->fd, ms->fb_id);
963	ms->fb_id = -1;
964    }
965
966    pipe_texture_reference(&ms->root_texture, NULL);
967    return TRUE;
968}
969
970static Bool
971drv_create_front_buffer_ga3d(ScrnInfoPtr pScrn)
972{
973    modesettingPtr ms = modesettingPTR(pScrn);
974    unsigned handle, stride, fb_id;
975    struct pipe_texture *tex;
976    int ret;
977
978    ms->noEvict = TRUE;
979
980    tex = xorg_exa_create_root_texture(pScrn, pScrn->virtualX, pScrn->virtualY,
981				       pScrn->depth, pScrn->bitsPerPixel);
982
983    if (!tex)
984	return FALSE;
985
986    if (!ms->api->local_handle_from_texture(ms->api, ms->screen,
987					    tex,
988					    &stride,
989					    &handle))
990	goto err_destroy;
991
992    ret = drmModeAddFB(ms->fd,
993		       pScrn->virtualX,
994		       pScrn->virtualY,
995		       pScrn->depth,
996		       pScrn->bitsPerPixel,
997		       stride,
998		       handle,
999		       &fb_id);
1000    if (ret) {
1001	debug_printf("%s: failed to create framebuffer (%i, %s)\n",
1002		     __func__, ret, strerror(-ret));
1003	goto err_destroy;
1004    }
1005
1006    if (!drv_destroy_front_buffer_ga3d(pScrn))
1007	FatalError("%s: failed to take down old framebuffer\n", __func__);
1008
1009    pScrn->frameX0 = 0;
1010    pScrn->frameY0 = 0;
1011    drv_adjust_frame(pScrn->scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);
1012
1013    pipe_texture_reference(&ms->root_texture, tex);
1014    pipe_texture_reference(&tex, NULL);
1015    ms->fb_id = fb_id;
1016
1017    return TRUE;
1018
1019err_destroy:
1020    pipe_texture_reference(&tex, NULL);
1021    return FALSE;
1022}
1023
1024static Bool
1025drv_bind_front_buffer_ga3d(ScrnInfoPtr pScrn)
1026{
1027    modesettingPtr ms = modesettingPTR(pScrn);
1028    ScreenPtr pScreen = pScrn->pScreen;
1029    PixmapPtr rootPixmap = pScreen->GetScreenPixmap(pScreen);
1030    struct pipe_texture *check;
1031
1032    xorg_exa_set_displayed_usage(rootPixmap);
1033    xorg_exa_set_shared_usage(rootPixmap);
1034    xorg_exa_set_texture(rootPixmap, ms->root_texture);
1035    if (!pScreen->ModifyPixmapHeader(rootPixmap, -1, -1, -1, -1, -1, NULL))
1036	FatalError("Couldn't adjust screen pixmap\n");
1037
1038    check = xorg_exa_get_texture(rootPixmap);
1039    if (ms->root_texture != check)
1040	FatalError("Created new root texture\n");
1041
1042    pipe_texture_reference(&check, NULL);
1043    return TRUE;
1044}
1045
1046#ifdef HAVE_LIBKMS
1047static Bool
1048drv_destroy_front_buffer_kms(ScrnInfoPtr pScrn)
1049{
1050    modesettingPtr ms = modesettingPTR(pScrn);
1051    ScreenPtr pScreen = pScrn->pScreen;
1052    PixmapPtr rootPixmap = pScreen->GetScreenPixmap(pScreen);
1053
1054    /* XXX Do something with the rootPixmap.
1055     * This currently works fine but if we are getting crashes in
1056     * the fb functions after VT switches maybe look more into it.
1057     */
1058    (void)rootPixmap;
1059
1060    if (!ms->root_bo)
1061	return TRUE;
1062
1063    if (ms->fb_id != -1) {
1064	drmModeRmFB(ms->fd, ms->fb_id);
1065	ms->fb_id = -1;
1066    }
1067
1068    kms_bo_unmap(ms->root_bo);
1069    kms_bo_destroy(&ms->root_bo);
1070    return TRUE;
1071}
1072
1073static Bool
1074drv_create_front_buffer_kms(ScrnInfoPtr pScrn)
1075{
1076    modesettingPtr ms = modesettingPTR(pScrn);
1077    unsigned handle, stride;
1078    struct kms_bo *bo;
1079    unsigned attr[8];
1080    unsigned fb_id;
1081    int ret;
1082
1083    attr[0] = KMS_BO_TYPE;
1084#ifdef KMS_BO_TYPE_SCANOUT_X8R8G8B8
1085    attr[1] = KMS_BO_TYPE_SCANOUT_X8R8G8B8;
1086#else
1087    attr[1] = KMS_BO_TYPE_SCANOUT;
1088#endif
1089    attr[2] = KMS_WIDTH;
1090    attr[3] = pScrn->virtualX;
1091    attr[4] = KMS_HEIGHT;
1092    attr[5] = pScrn->virtualY;
1093    attr[6] = 0;
1094
1095    if (kms_bo_create(ms->kms, attr, &bo))
1096	return FALSE;
1097
1098    if (kms_bo_get_prop(bo, KMS_PITCH, &stride))
1099	goto err_destroy;
1100
1101    if (kms_bo_get_prop(bo, KMS_HANDLE, &handle))
1102	goto err_destroy;
1103
1104    ret = drmModeAddFB(ms->fd,
1105		       pScrn->virtualX,
1106		       pScrn->virtualY,
1107		       pScrn->depth,
1108		       pScrn->bitsPerPixel,
1109		       stride,
1110		       handle,
1111		       &fb_id);
1112    if (ret) {
1113	debug_printf("%s: failed to create framebuffer (%i, %s)",
1114		     __func__, ret, strerror(-ret));
1115	goto err_destroy;
1116    }
1117
1118    if (!drv_destroy_front_buffer_kms(pScrn))
1119	FatalError("%s: could not takedown old bo", __func__);
1120
1121    pScrn->frameX0 = 0;
1122    pScrn->frameY0 = 0;
1123    drv_adjust_frame(pScrn->scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);
1124    ms->root_bo = bo;
1125    ms->fb_id = fb_id;
1126
1127    return TRUE;
1128
1129err_destroy:
1130    kms_bo_destroy(&bo);
1131    return FALSE;
1132}
1133
1134static Bool
1135drv_bind_front_buffer_kms(ScrnInfoPtr pScrn)
1136{
1137    modesettingPtr ms = modesettingPTR(pScrn);
1138    ScreenPtr pScreen = pScrn->pScreen;
1139    PixmapPtr rootPixmap = pScreen->GetScreenPixmap(pScreen);
1140    unsigned stride;
1141    void *ptr;
1142
1143    if (kms_bo_get_prop(ms->root_bo, KMS_PITCH, &stride))
1144	return FALSE;
1145
1146    if (kms_bo_map(ms->root_bo, &ptr))
1147	goto err_destroy;
1148
1149    pScreen->ModifyPixmapHeader(rootPixmap,
1150				pScrn->virtualX,
1151				pScrn->virtualY,
1152				pScreen->rootDepth,
1153				pScrn->bitsPerPixel,
1154				stride,
1155				ptr);
1156
1157    /* This a hack to work around EnableDisableFBAccess setting the pointer
1158     * the real fix would be to replace pScrn->EnableDisableFBAccess hook
1159     * and set the rootPixmap->devPrivate.ptr to something valid before that.
1160     *
1161     * But in its infinit visdome something uses either this some times before
1162     * that, so our hook doesn't get called before the crash happens.
1163     */
1164    pScrn->pixmapPrivate.ptr = ptr;
1165
1166    return TRUE;
1167
1168err_destroy:
1169    kms_bo_destroy(&ms->root_bo);
1170    return FALSE;
1171}
1172#endif /* HAVE_LIBKMS */
1173
1174static Bool drv_init_front_buffer_functions(ScrnInfoPtr pScrn)
1175{
1176    modesettingPtr ms = modesettingPTR(pScrn);
1177    if (ms->screen) {
1178	ms->destroy_front_buffer = drv_destroy_front_buffer_ga3d;
1179	ms->create_front_buffer = drv_create_front_buffer_ga3d;
1180	ms->bind_front_buffer = drv_bind_front_buffer_ga3d;
1181#ifdef HAVE_LIBKMS
1182    } else if (ms->kms) {
1183	ms->destroy_front_buffer = drv_destroy_front_buffer_kms;
1184	ms->create_front_buffer = drv_create_front_buffer_kms;
1185	ms->bind_front_buffer = drv_bind_front_buffer_kms;
1186#endif
1187    } else
1188	return FALSE;
1189
1190    return TRUE;
1191}
1192
1193CustomizerPtr xorg_customizer(ScrnInfoPtr pScrn)
1194{
1195    return modesettingPTR(pScrn)->cust;
1196}
1197
1198Bool xorg_has_gallium(ScrnInfoPtr pScrn)
1199{
1200    return modesettingPTR(pScrn)->screen != NULL;
1201}
1202
1203/* vim: set sw=4 ts=8 sts=4: */
1204