1/*
2 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
3 * Copyright 2011 VMWare, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 *
27 * Author: Alan Hourihane <alanh@tungstengraphics.com>
28 * Author: Jakob Bornecrantz <wallbraker@gmail.com>
29 * Author: Thomas Hellstrom <thellstrom@vmware.com>
30 */
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
35#include <unistd.h>
36#include <string.h>
37#include <assert.h>
38#include <stdlib.h>
39#include <math.h>
40#include <stdint.h>
41
42#include "xorg-server.h"
43#include <xf86.h>
44#include <xf86i2c.h>
45#include <xf86Crtc.h>
46#include <cursorstr.h>
47#include "vmwgfx_driver.h"
48#include "xf86Modes.h"
49#include "vmwgfx_saa.h"
50
51#ifdef HAVE_XEXTPROTO_71
52#include <X11/extensions/dpmsconst.h>
53#else
54#define DPMS_SERVER
55#include <X11/extensions/dpms.h>
56#endif
57
58struct crtc_private
59{
60    drmModeCrtcPtr drm_crtc;
61
62    /* hwcursor */
63    struct vmwgfx_dmabuf *cursor_bo;
64    uint32_t scanout_id;
65    unsigned cursor_handle;
66
67    /* Scanout info for pixmaps */
68    struct vmwgfx_screen_entry entry;
69};
70
71static void
72crtc_dpms(xf86CrtcPtr crtc, int mode)
73{
74    struct crtc_private *crtcp = crtc->driver_private;
75    /* ScrnInfoPtr pScrn = crtc->scrn; */
76
77    switch (mode) {
78    case DPMSModeOn:
79    case DPMSModeStandby:
80    case DPMSModeSuspend:
81	break;
82    case DPMSModeOff:
83
84      /*
85       * The xf86 modesetting code uses DPMS off to turn off
86       * crtcs that are not enabled. However, the DPMS code does the same.
87       * We assume, that if we get this call with the crtc not enabled,
88       * it's a permanent switch off which will only be reversed by a
89       * major modeset.
90       *
91       * If it's a DPMS switch off, (crtc->enabled == TRUE),
92       * the crtc may be turned on again by
93       * another dpms call, so don't release the scanout pixmap ref.
94       */
95	if (!crtc->enabled && crtcp->entry.pixmap) {
96	    vmwgfx_scanout_unref(&crtcp->entry);
97	}
98	break;
99    }
100}
101
102/*
103 * Disable outputs and crtcs and drop the scanout reference from
104 * scanout pixmaps. This will essentially free all kms fb allocations.
105 */
106
107void
108vmwgfx_disable_scanout(ScrnInfoPtr pScrn)
109{
110    int i;
111    Bool save_enabled;
112    xf86CrtcPtr crtc;
113    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
114
115    xf86DPMSSet(pScrn, DPMSModeOff, 0);
116    for (i=0; i < config->num_crtc; ++i) {
117	crtc = config->crtc[i];
118	save_enabled = crtc->enabled;
119	crtc->enabled = FALSE;
120	crtc_dpms(crtc, DPMSModeOff);
121	crtc->enabled = save_enabled;
122    }
123    xf86RotateFreeShadow(pScrn);
124}
125
126static Bool
127vmwgfx_scanout_equals_pixmap(DisplayModePtr mode, PixmapPtr pixmap,
128			     int x, int y)
129{
130    return x == 0 && y == 0;
131/*
132 * Mode test is disabled for now, since the X server has a tendency to first
133 * change the pixmap dimensions, then change the mode, keeping the pixmap.
134 * This would lead to a lot of false non-equals, or flickering if we were to
135 * kill the drm fb in between.
136 * However, currently we prefer false equals as long as x == 0 and y == 0.
137 * The false equals will then typically correspond to the primary screen in a
138 * multimon setup. Let's live with that for now.
139 */
140#if 0
141	&& mode->HDisplay == pixmap->drawable.width &&
142	mode->VDisplay == pixmap->drawable.height;
143#endif
144}
145
146static Bool
147crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
148		    Rotation rotation, int x, int y)
149{
150    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
151    modesettingPtr ms = modesettingPTR(crtc->scrn);
152    ScreenPtr pScreen = crtc->scrn->pScreen;
153    xf86OutputPtr output = NULL;
154    struct crtc_private *crtcp = crtc->driver_private;
155    drmModeCrtcPtr drm_crtc = crtcp->drm_crtc;
156    drmModeModeInfo drm_mode;
157    int i, ret;
158    unsigned int connector_id;
159    PixmapPtr pixmap;
160
161    for (i = 0; i < config->num_output; output = NULL, i++) {
162	output = config->output[i];
163
164	if (output->crtc == crtc)
165	    break;
166    }
167
168    if (!output) {
169	LogMessage(X_ERROR, "No output for this crtc.\n");
170	return FALSE;
171    }
172
173    connector_id = xorg_output_get_id(output);
174
175    memset(&drm_mode, 0, sizeof(drm_mode));
176    drm_mode.clock = mode->Clock;
177    drm_mode.hdisplay = mode->HDisplay;
178    drm_mode.hsync_start = mode->HSyncStart;
179    drm_mode.hsync_end = mode->HSyncEnd;
180    drm_mode.htotal = mode->HTotal;
181    drm_mode.vdisplay = mode->VDisplay;
182    drm_mode.vsync_start = mode->VSyncStart;
183    drm_mode.vsync_end = mode->VSyncEnd;
184    drm_mode.vtotal = mode->VTotal;
185    drm_mode.flags = mode->Flags;
186    drm_mode.hskew = mode->HSkew;
187    drm_mode.vscan = mode->VScan;
188    drm_mode.vrefresh = mode->VRefresh;
189    if (!mode->name)
190	xf86SetModeDefaultName(mode);
191    strncpy(drm_mode.name, mode->name, DRM_DISPLAY_MODE_LEN - 1);
192    drm_mode.name[DRM_DISPLAY_MODE_LEN - 1] = '\0';
193
194    /*
195     * Check if we need to scanout from something else than the root
196     * pixmap. In that case, xf86CrtcRotate will take care of allocating
197     * new opaque scanout buffer data "crtc->rotatedData".
198     * However, it will not wrap
199     * that data into pixmaps until the first rotated damage composite.
200     * In out case, the buffer data is actually already a pixmap.
201     */
202
203    if (!xf86CrtcRotate(crtc))
204	return FALSE;
205
206    if (crtc->transform_in_use && crtc->rotatedData) {
207	x = 0;
208	y = 0;
209	pixmap = (PixmapPtr) crtc->rotatedData;
210    } else
211	pixmap = pScreen->GetScreenPixmap(pScreen);
212
213    if (crtcp->entry.pixmap != pixmap) {
214	if (crtcp->entry.pixmap)
215	    vmwgfx_scanout_unref(&crtcp->entry);
216
217	crtcp->entry.pixmap = pixmap;
218	crtcp->scanout_id = vmwgfx_scanout_ref
219	    (&crtcp->entry, vmwgfx_scanout_equals_pixmap(mode, pixmap, x, y));
220	if (crtcp->scanout_id == -1) {
221	    crtcp->entry.pixmap = NULL;
222	    LogMessage(X_ERROR, "Failed to convert pixmap to scanout.\n");
223	    return FALSE;
224	}
225    }
226    ret = drmModeSetCrtc(ms->fd, drm_crtc->crtc_id, crtcp->scanout_id, x, y,
227			 &connector_id, 1, &drm_mode);
228    if (ret)
229	return FALSE;
230
231    vmwgfx_scanout_refresh(pixmap);
232
233    /* Only set gamma when needed, to avoid unneeded delays. */
234#if defined(XF86_CRTC_VERSION) && XF86_CRTC_VERSION >= 3
235    if (!crtc->active && crtc->version >= 3)
236	crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
237			       crtc->gamma_blue, crtc->gamma_size);
238    crtc->active = TRUE;
239#endif
240
241    /*
242     * Strictly, this needs to be done only once per configuration change,
243     * not once per crtc, but there's no better place to put this. Since
244     * Intel wrote the crtc code, let's do what the xf86-video-intel driver
245     * does.
246     */
247#if (GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 23)
248    if (pScreen)
249	xf86_reload_cursors(pScreen);
250#endif
251
252    return TRUE;
253}
254
255static void
256crtc_gamma_set(xf86CrtcPtr crtc, CARD16 * red, CARD16 * green, CARD16 * blue,
257	       int size)
258{
259    modesettingPtr ms = modesettingPTR(crtc->scrn);
260    struct crtc_private *crtcp = crtc->driver_private;
261
262    drmModeCrtcSetGamma(ms->fd, crtcp->drm_crtc->crtc_id, size, red, green, blue);
263}
264
265static void *
266crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
267{
268    ScreenPtr pScreen = crtc->scrn->pScreen;
269    PixmapPtr rootpix = pScreen->GetScreenPixmap(pScreen);
270
271    /*
272     * Use the same depth as for the root pixmap.
273     * The associated kms fb will be created on demand once this pixmap
274     * is used as scanout by a crtc.
275     */
276
277    return pScreen->CreatePixmap(pScreen, width, height,
278				 rootpix->drawable.depth, 0);
279}
280
281static PixmapPtr
282crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
283{
284    return (PixmapPtr) data;
285}
286
287static void
288crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
289{
290    ScreenPtr pScreen;
291
292    if (rotate_pixmap == NULL)
293        return;
294
295    pScreen = rotate_pixmap->drawable.pScreen;
296    pScreen->DestroyPixmap(rotate_pixmap);
297}
298
299
300/*
301 * Cursor functions
302 */
303
304static void
305crtc_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg)
306{
307    /* XXX: See if this one is needed, as we only support ARGB cursors */
308}
309
310static void
311crtc_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
312{
313    modesettingPtr ms = modesettingPTR(crtc->scrn);
314    struct crtc_private *crtcp = crtc->driver_private;
315
316    /* Seems like newer X servers try to move cursors without images */
317    if (!crtcp->cursor_bo)
318	return;
319
320    drmModeMoveCursor(ms->fd, crtcp->drm_crtc->crtc_id, x, y);
321}
322
323static void
324crtc_load_cursor_argb_kms(xf86CrtcPtr crtc, CARD32 * image)
325{
326    modesettingPtr ms = modesettingPTR(crtc->scrn);
327    struct crtc_private *crtcp = crtc->driver_private;
328    unsigned char *ptr;
329    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
330    CursorPtr c = config->cursor;
331
332    if (vmwgfx_cursor_bypass(ms->fd, c->bits->xhot, c->bits->yhot) != 0) {
333	xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
334		   "Failed to set VMWare cursor bypass.\n");
335    }
336
337    if (!crtcp->cursor_bo) {
338	size_t size = 64*64*4;
339        crtcp->cursor_bo = vmwgfx_dmabuf_alloc(ms->fd, size);
340	if (!crtcp->cursor_bo) {
341	    xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
342		       "Failed to create a dmabuf for cursor.\n");
343	    return;
344	}
345	crtcp->cursor_handle = crtcp->cursor_bo->handle;
346    }
347
348    ptr = vmwgfx_dmabuf_map(crtcp->cursor_bo);
349    if (ptr) {
350	memcpy(ptr, image, 64*64*4);
351	vmwgfx_dmabuf_unmap(crtcp->cursor_bo);
352    } else {
353	xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
354		   "Failed to map cursor dmabuf.\n");
355    }
356
357    if (crtc->cursor_shown)
358	drmModeSetCursor(ms->fd, crtcp->drm_crtc->crtc_id,
359			 crtcp->cursor_handle, 64, 64);
360
361    return;
362}
363
364static void
365crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 * image)
366{
367    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
368    modesettingPtr ms = modesettingPTR(crtc->scrn);
369
370    /* Older X servers have cursor reference counting bugs leading to use of
371     * freed memory and consequently random crashes. Should be fixed as of
372     * xserver 1.8, but this workaround shouldn't hurt anyway.
373     */
374    if (config->cursor)
375       config->cursor->refcnt++;
376
377    if (ms->cursor)
378       FreeCursor(ms->cursor, None);
379
380    ms->cursor = config->cursor;
381    crtc_load_cursor_argb_kms(crtc, image);
382}
383
384static void
385crtc_show_cursor(xf86CrtcPtr crtc)
386{
387    modesettingPtr ms = modesettingPTR(crtc->scrn);
388    struct crtc_private *crtcp = crtc->driver_private;
389
390    if (crtcp->cursor_bo)
391	drmModeSetCursor(ms->fd, crtcp->drm_crtc->crtc_id,
392			 crtcp->cursor_handle, 64, 64);
393}
394
395static void
396crtc_hide_cursor(xf86CrtcPtr crtc)
397{
398    modesettingPtr ms = modesettingPTR(crtc->scrn);
399    struct crtc_private *crtcp = crtc->driver_private;
400
401    drmModeSetCursor(ms->fd, crtcp->drm_crtc->crtc_id, 0, 0, 0);
402}
403
404/**
405 * Called at vt leave
406 */
407void
408xorg_crtc_cursor_destroy(xf86CrtcPtr crtc)
409{
410    struct crtc_private *crtcp = crtc->driver_private;
411
412    if (crtcp->cursor_bo) {
413	vmwgfx_dmabuf_destroy(crtcp->cursor_bo);
414	crtcp->cursor_bo = NULL;
415    }
416}
417
418/*
419 * Misc functions
420 */
421
422static void
423crtc_destroy(xf86CrtcPtr crtc)
424{
425    struct crtc_private *crtcp = crtc->driver_private;
426
427    if (!WSBMLISTEMPTY(&crtcp->entry.scanout_head))
428	vmwgfx_scanout_unref(&crtcp->entry);
429
430    xorg_crtc_cursor_destroy(crtc);
431
432    drmModeFreeCrtc(crtcp->drm_crtc);
433
434    free(crtcp);
435    crtc->driver_private = NULL;
436}
437
438static const xf86CrtcFuncsRec crtc_funcs = {
439    .dpms = crtc_dpms,
440    .set_mode_major = crtc_set_mode_major,
441
442    .set_cursor_colors = crtc_set_cursor_colors,
443    .set_cursor_position = crtc_set_cursor_position,
444    .show_cursor = crtc_show_cursor,
445    .hide_cursor = crtc_hide_cursor,
446    .load_cursor_argb = crtc_load_cursor_argb,
447
448    .shadow_create = crtc_shadow_create,
449    .shadow_allocate = crtc_shadow_allocate,
450    .shadow_destroy = crtc_shadow_destroy,
451
452    .gamma_set = crtc_gamma_set,
453    .destroy = crtc_destroy,
454};
455
456void
457xorg_crtc_init(ScrnInfoPtr pScrn)
458{
459    modesettingPtr ms = modesettingPTR(pScrn);
460    xf86CrtcPtr crtc;
461    drmModeResPtr res;
462    drmModeCrtcPtr drm_crtc = NULL;
463    struct crtc_private *crtcp;
464    int c;
465
466    res = drmModeGetResources(ms->fd);
467    if (res == 0) {
468	ErrorF("Failed drmModeGetResources %d\n", errno);
469	return;
470    }
471
472    for (c = 0; c < res->count_crtcs; c++) {
473	drm_crtc = drmModeGetCrtc(ms->fd, res->crtcs[c]);
474
475	if (!drm_crtc)
476	    continue;
477
478	crtc = xf86CrtcCreate(pScrn, &crtc_funcs);
479	if (crtc == NULL)
480	    goto out;
481
482	crtcp = calloc(1, sizeof(struct crtc_private));
483	if (!crtcp) {
484	    xf86CrtcDestroy(crtc);
485	    goto out;
486	}
487
488	crtcp->drm_crtc = drm_crtc;
489	crtcp->entry.pixmap = NULL;
490	WSBMINITLISTHEAD(&crtcp->entry.scanout_head);
491
492	crtc->driver_private = crtcp;
493    }
494
495  out:
496    drmModeFreeResources(res);
497}
498
499PixmapPtr
500crtc_get_scanout(xf86CrtcPtr crtc)
501{
502    struct crtc_private *crtcp = crtc->driver_private;
503    return crtcp->entry.pixmap;
504}
505
506/* vim: set sw=4 ts=8 sts=4: */
507