xorg_crtc.c revision c7c1e5338cd4d47168fd2654ae951955578bef8d
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#include <unistd.h>
32#include <string.h>
33#include <assert.h>
34#include <stdlib.h>
35#include <math.h>
36#include <stdint.h>
37
38#include "xorg-server.h"
39#include <xf86.h>
40#include <xf86i2c.h>
41#include <xf86Crtc.h>
42#include <cursorstr.h>
43#include "xorg_tracker.h"
44#include "xf86Modes.h"
45
46#ifdef HAVE_XEXTPROTO_71
47#include <X11/extensions/dpmsconst.h>
48#else
49#define DPMS_SERVER
50#include <X11/extensions/dpms.h>
51#endif
52
53#include "state_tracker/drm_driver.h"
54#include "util/u_inlines.h"
55#include "util/u_rect.h"
56
57#ifdef HAVE_LIBKMS
58#include "libkms.h"
59#endif
60
61struct crtc_private
62{
63    drmModeCrtcPtr drm_crtc;
64
65    /* hwcursor */
66    struct pipe_resource *cursor_tex;
67    struct kms_bo *cursor_bo;
68
69    unsigned cursor_handle;
70};
71
72static void
73crtc_dpms(xf86CrtcPtr crtc, int mode)
74{
75    /* ScrnInfoPtr pScrn = crtc->scrn; */
76
77    switch (mode) {
78    case DPMSModeOn:
79    case DPMSModeStandby:
80    case DPMSModeSuspend:
81	break;
82    case DPMSModeOff:
83	break;
84    }
85}
86
87static Bool
88crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
89		    Rotation rotation, int x, int y)
90{
91    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
92    modesettingPtr ms = modesettingPTR(crtc->scrn);
93    xf86OutputPtr output = NULL;
94    struct crtc_private *crtcp = crtc->driver_private;
95    drmModeCrtcPtr drm_crtc = crtcp->drm_crtc;
96    drmModeModeInfo drm_mode;
97    int i, ret;
98    unsigned int connector_id;
99
100    for (i = 0; i < config->num_output; output = NULL, i++) {
101	output = config->output[i];
102
103	if (output->crtc == crtc)
104	    break;
105    }
106
107    if (!output)
108	return FALSE;
109
110    connector_id = xorg_output_get_id(output);
111
112    drm_mode.clock = mode->Clock;
113    drm_mode.hdisplay = mode->HDisplay;
114    drm_mode.hsync_start = mode->HSyncStart;
115    drm_mode.hsync_end = mode->HSyncEnd;
116    drm_mode.htotal = mode->HTotal;
117    drm_mode.vdisplay = mode->VDisplay;
118    drm_mode.vsync_start = mode->VSyncStart;
119    drm_mode.vsync_end = mode->VSyncEnd;
120    drm_mode.vtotal = mode->VTotal;
121    drm_mode.flags = mode->Flags;
122    drm_mode.hskew = mode->HSkew;
123    drm_mode.vscan = mode->VScan;
124    drm_mode.vrefresh = mode->VRefresh;
125    if (!mode->name)
126	xf86SetModeDefaultName(mode);
127    strncpy(drm_mode.name, mode->name, DRM_DISPLAY_MODE_LEN - 1);
128    drm_mode.name[DRM_DISPLAY_MODE_LEN - 1] = '\0';
129
130    ret = drmModeSetCrtc(ms->fd, drm_crtc->crtc_id, ms->fb_id, x, y,
131			 &connector_id, 1, &drm_mode);
132
133    if (ret)
134	return FALSE;
135
136    /* Only set gamma when needed, to avoid unneeded delays. */
137#if defined(XF86_CRTC_VERSION) && XF86_CRTC_VERSION >= 3
138    if (!crtc->active)
139#endif
140	crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
141			       crtc->gamma_blue, crtc->gamma_size);
142
143#if defined(XF86_CRTC_VERSION) && XF86_CRTC_VERSION >= 3
144    crtc->active = TRUE;
145#endif
146    crtc->x = x;
147    crtc->y = y;
148    crtc->mode = *mode;
149    crtc->rotation = rotation;
150
151    return TRUE;
152}
153
154static void
155crtc_gamma_set(xf86CrtcPtr crtc, CARD16 * red, CARD16 * green, CARD16 * blue,
156	       int size)
157{
158    modesettingPtr ms = modesettingPTR(crtc->scrn);
159    struct crtc_private *crtcp = crtc->driver_private;
160
161    drmModeCrtcSetGamma(ms->fd, crtcp->drm_crtc->crtc_id, size, red, green, blue);
162}
163
164#if 0 /* Implement and enable to enable rotation and reflection. */
165static void *
166crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
167{
168    /* ScrnInfoPtr pScrn = crtc->scrn; */
169
170    return NULL;
171}
172
173static PixmapPtr
174crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
175{
176    /* ScrnInfoPtr pScrn = crtc->scrn; */
177
178    return NULL;
179}
180
181static void
182crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
183{
184    /* ScrnInfoPtr pScrn = crtc->scrn; */
185}
186
187#endif
188
189/*
190 * Cursor functions
191 */
192
193static void
194crtc_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg)
195{
196    /* XXX: See if this one is needed, as we only support ARGB cursors */
197}
198
199static void
200crtc_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
201{
202    modesettingPtr ms = modesettingPTR(crtc->scrn);
203    struct crtc_private *crtcp = crtc->driver_private;
204
205    drmModeMoveCursor(ms->fd, crtcp->drm_crtc->crtc_id, x, y);
206}
207
208static void
209crtc_load_cursor_argb_ga3d(xf86CrtcPtr crtc, CARD32 * image)
210{
211    unsigned char *ptr;
212    modesettingPtr ms = modesettingPTR(crtc->scrn);
213    struct crtc_private *crtcp = crtc->driver_private;
214    struct pipe_transfer *transfer;
215
216    if (!crtcp->cursor_tex) {
217	struct pipe_resource templat;
218	struct winsys_handle whandle;
219
220	memset(&templat, 0, sizeof(templat));
221	templat.bind |= PIPE_BIND_RENDER_TARGET;
222	templat.bind |= PIPE_BIND_SCANOUT;
223	templat.target = PIPE_TEXTURE_2D;
224	templat.last_level = 0;
225	templat.depth0 = 1;
226	templat.array_size = 1;
227	templat.format = PIPE_FORMAT_B8G8R8A8_UNORM;
228	templat.width0 = 64;
229	templat.height0 = 64;
230
231	memset(&whandle, 0, sizeof(whandle));
232	whandle.type = DRM_API_HANDLE_TYPE_KMS;
233
234	crtcp->cursor_tex = ms->screen->resource_create(ms->screen,
235						       &templat);
236	ms->screen->resource_get_handle(ms->screen, crtcp->cursor_tex, &whandle);
237
238	crtcp->cursor_handle = whandle.handle;
239    }
240
241    transfer = pipe_get_transfer(ms->ctx, crtcp->cursor_tex,
242                                 0, 0,
243                                 PIPE_TRANSFER_WRITE,
244                                 0, 0, 64, 64);
245    ptr = ms->ctx->transfer_map(ms->ctx, transfer);
246    util_copy_rect(ptr, crtcp->cursor_tex->format,
247		   transfer->stride, 0, 0,
248		   64, 64, (void*)image, 64 * 4, 0, 0);
249    ms->ctx->transfer_unmap(ms->ctx, transfer);
250    ms->ctx->transfer_destroy(ms->ctx, transfer);
251
252    if (crtc->cursor_shown)
253	drmModeSetCursor(ms->fd, crtcp->drm_crtc->crtc_id,
254			 crtcp->cursor_handle, 64, 64);
255}
256
257#if HAVE_LIBKMS
258static void
259crtc_load_cursor_argb_kms(xf86CrtcPtr crtc, CARD32 * image)
260{
261    modesettingPtr ms = modesettingPTR(crtc->scrn);
262    struct crtc_private *crtcp = crtc->driver_private;
263    unsigned char *ptr;
264
265    if (!crtcp->cursor_bo) {
266	unsigned attr[8];
267
268	attr[0] = KMS_BO_TYPE;
269#ifdef KMS_BO_TYPE_CURSOR_64X64_A8R8G8B8
270	attr[1] = KMS_BO_TYPE_CURSOR_64X64_A8R8G8B8;
271#else
272	attr[1] = KMS_BO_TYPE_CURSOR;
273#endif
274	attr[2] = KMS_WIDTH;
275	attr[3] = 64;
276	attr[4] = KMS_HEIGHT;
277	attr[5] = 64;
278	attr[6] = 0;
279
280        if (kms_bo_create(ms->kms, attr, &crtcp->cursor_bo))
281	   return;
282
283	if (kms_bo_get_prop(crtcp->cursor_bo, KMS_HANDLE,
284			    &crtcp->cursor_handle))
285	    goto err_bo_destroy;
286    }
287
288    kms_bo_map(crtcp->cursor_bo, (void**)&ptr);
289    memcpy(ptr, image, 64*64*4);
290    kms_bo_unmap(crtcp->cursor_bo);
291
292    if (crtc->cursor_shown)
293	drmModeSetCursor(ms->fd, crtcp->drm_crtc->crtc_id,
294			 crtcp->cursor_handle, 64, 64);
295
296    return;
297
298err_bo_destroy:
299    kms_bo_destroy(&crtcp->cursor_bo);
300}
301#endif
302
303static void
304crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 * image)
305{
306    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
307    modesettingPtr ms = modesettingPTR(crtc->scrn);
308
309    /* Older X servers have cursor reference counting bugs leading to use of
310     * freed memory and consequently random crashes. Should be fixed as of
311     * xserver 1.8, but this workaround shouldn't hurt anyway.
312     */
313    if (config->cursor)
314       config->cursor->refcnt++;
315
316    if (ms->cursor)
317       FreeCursor(ms->cursor, None);
318
319    ms->cursor = config->cursor;
320
321    if (ms->screen)
322	crtc_load_cursor_argb_ga3d(crtc, image);
323#ifdef HAVE_LIBKMS
324    else if (ms->kms)
325	crtc_load_cursor_argb_kms(crtc, image);
326#endif
327}
328
329static void
330crtc_show_cursor(xf86CrtcPtr crtc)
331{
332    modesettingPtr ms = modesettingPTR(crtc->scrn);
333    struct crtc_private *crtcp = crtc->driver_private;
334
335    if (crtcp->cursor_tex || crtcp->cursor_bo)
336	drmModeSetCursor(ms->fd, crtcp->drm_crtc->crtc_id,
337			 crtcp->cursor_handle, 64, 64);
338}
339
340static void
341crtc_hide_cursor(xf86CrtcPtr crtc)
342{
343    modesettingPtr ms = modesettingPTR(crtc->scrn);
344    struct crtc_private *crtcp = crtc->driver_private;
345
346    drmModeSetCursor(ms->fd, crtcp->drm_crtc->crtc_id, 0, 0, 0);
347}
348
349/**
350 * Called at vt leave
351 */
352void
353xorg_crtc_cursor_destroy(xf86CrtcPtr crtc)
354{
355    struct crtc_private *crtcp = crtc->driver_private;
356
357    if (crtcp->cursor_tex)
358	pipe_resource_reference(&crtcp->cursor_tex, NULL);
359#ifdef HAVE_LIBKMS
360    if (crtcp->cursor_bo)
361	kms_bo_destroy(&crtcp->cursor_bo);
362#endif
363}
364
365/*
366 * Misc functions
367 */
368
369static void
370crtc_destroy(xf86CrtcPtr crtc)
371{
372    struct crtc_private *crtcp = crtc->driver_private;
373
374    xorg_crtc_cursor_destroy(crtc);
375
376    drmModeFreeCrtc(crtcp->drm_crtc);
377
378    free(crtcp);
379    crtc->driver_private = NULL;
380}
381
382static const xf86CrtcFuncsRec crtc_funcs = {
383    .dpms = crtc_dpms,
384    .set_mode_major = crtc_set_mode_major,
385
386    .set_cursor_colors = crtc_set_cursor_colors,
387    .set_cursor_position = crtc_set_cursor_position,
388    .show_cursor = crtc_show_cursor,
389    .hide_cursor = crtc_hide_cursor,
390    .load_cursor_argb = crtc_load_cursor_argb,
391
392    .shadow_create = NULL,
393    .shadow_allocate = NULL,
394    .shadow_destroy = NULL,
395
396    .gamma_set = crtc_gamma_set,
397    .destroy = crtc_destroy,
398};
399
400void
401xorg_crtc_init(ScrnInfoPtr pScrn)
402{
403    modesettingPtr ms = modesettingPTR(pScrn);
404    xf86CrtcPtr crtc;
405    drmModeResPtr res;
406    drmModeCrtcPtr drm_crtc = NULL;
407    struct crtc_private *crtcp;
408    int c;
409
410    res = drmModeGetResources(ms->fd);
411    if (res == 0) {
412	ErrorF("Failed drmModeGetResources %d\n", errno);
413	return;
414    }
415
416    for (c = 0; c < res->count_crtcs; c++) {
417	drm_crtc = drmModeGetCrtc(ms->fd, res->crtcs[c]);
418
419	if (!drm_crtc)
420	    continue;
421
422	crtc = xf86CrtcCreate(pScrn, &crtc_funcs);
423	if (crtc == NULL)
424	    goto out;
425
426	crtcp = calloc(1, sizeof(struct crtc_private));
427	if (!crtcp) {
428	    xf86CrtcDestroy(crtc);
429	    goto out;
430	}
431
432	crtcp->drm_crtc = drm_crtc;
433
434	crtc->driver_private = crtcp;
435    }
436
437  out:
438    drmModeFreeResources(res);
439}
440
441/* vim: set sw=4 ts=8 sts=4: */
442