1/*
2 * Copyright 2008 Red Hat, Inc.
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 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23/* code to handle UMS modesetting */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28#include "qxl.h"
29
30/* These constants govern which modes are reported to X as preferred */
31#define DEFAULT_WIDTH       1024
32#define DEFAULT_HEIGHT       768
33
34static void qxl_update_monitors_config (qxl_screen_t *qxl);
35
36static DisplayModePtr
37screen_create_mode (ScrnInfoPtr pScrn, int width, int height, int type)
38{
39    DisplayModePtr mode;
40
41    mode = xnfcalloc (1, sizeof (DisplayModeRec));
42
43    mode->status = MODE_OK;
44    mode->type = type;
45    mode->HDisplay   = width;
46    mode->HSyncStart = (width * 105 / 100 + 7) & ~7;
47    mode->HSyncEnd   = (width * 115 / 100 + 7) & ~7;
48    mode->HTotal     = (width * 130 / 100 + 7) & ~7;
49    mode->VDisplay   = height;
50    mode->VSyncStart = height + 1;
51    mode->VSyncEnd   = height + 4;
52    mode->VTotal     = height * 1035 / 1000;
53    mode->Clock = mode->HTotal * mode->VTotal * 60 / 1000;
54    mode->Flags = V_NHSYNC | V_PVSYNC;
55
56    xf86SetModeDefaultName (mode);
57    xf86SetModeCrtc (mode, pScrn->adjustFlags); /* needed? xf86-video-modesetting does this */
58
59    return mode;
60}
61
62static DisplayModePtr
63qxl_add_mode (qxl_screen_t *qxl, ScrnInfoPtr pScrn, int width, int height, int type)
64{
65    DisplayModePtr mode;
66
67    mode = screen_create_mode (pScrn, width, height, type);
68    pScrn->modes = qxl->x_modes = xf86ModesAdd (qxl->x_modes, mode);
69
70    return mode;
71}
72
73static int
74check_crtc (qxl_screen_t *qxl)
75{
76    int i, count = 0;
77    xf86CrtcPtr crtc;
78
79    if (qxl->crtcs == NULL) {
80        return 0;
81    }
82
83    for (i = 0 ; i < qxl->num_heads; ++i)
84    {
85	crtc = qxl->crtcs[i];
86
87	if (!crtc->enabled || crtc->mode.CrtcHDisplay == 0 ||
88	    crtc->mode.CrtcVDisplay == 0)
89	{
90	    continue;
91	}
92	count++;
93    }
94
95#if 0
96    if (count == 0)
97    {
98	ErrorF ("check crtc failed, count == 0!!\n");
99	BREAKPOINT ();
100    }
101#endif
102
103    return count;
104}
105
106static void
107qxl_update_monitors_config (qxl_screen_t *qxl)
108{
109    int i;
110    QXLHead *head;
111    xf86CrtcPtr crtc;
112    qxl_output_private *qxl_output;
113    QXLRam * ram = get_ram_header (qxl);
114
115    if (check_crtc (qxl) == 0)
116        return;
117
118    qxl->monitors_config->count = 0;
119    qxl->monitors_config->max_allowed = qxl->num_heads;
120    for (i = 0 ; i < qxl->num_heads; ++i)
121    {
122	head = &qxl->monitors_config->heads[qxl->monitors_config->count];
123	crtc = qxl->crtcs[i];
124	qxl_output = qxl->outputs[i]->driver_private;
125	head->id = i;
126	head->surface_id = 0;
127	head->flags = 0;
128
129	if (!crtc->enabled || crtc->mode.CrtcHDisplay == 0 ||
130	    crtc->mode.CrtcVDisplay == 0)
131	{
132	    head->width = head->height = head->x = head->y = 0;
133	    qxl_output->status = XF86OutputStatusDisconnected;
134	}
135	else
136	{
137	    head->width = crtc->mode.CrtcHDisplay;
138	    head->height = crtc->mode.CrtcVDisplay;
139	    head->x = crtc->x;
140	    head->y = crtc->y;
141	    qxl->monitors_config->count++;
142	    qxl_output->status = XF86OutputStatusConnected;
143	}
144    }
145    /* initialize when actually used, memslots should be initialized by now */
146    if (ram->monitors_config == 0)
147    {
148	ram->monitors_config = physical_address (qxl, qxl->monitors_config,
149	                                         qxl->main_mem_slot);
150    }
151
152    qxl_io_monitors_config_async (qxl);
153}
154
155static Bool
156crtc_set_mode_major (xf86CrtcPtr crtc, DisplayModePtr mode,
157                     Rotation rotation, int x, int y)
158{
159    qxl_crtc_private *crtc_private = crtc->driver_private;
160    qxl_screen_t *    qxl = crtc_private->qxl;
161
162    if (crtc == qxl->crtcs[0] && mode == NULL)
163    {
164	/* disallow disabling of monitor 0 mode */
165	ErrorF ("%s: not allowing crtc 0 disablement\n", __func__);
166	return FALSE;
167    }
168
169    crtc->mode = *mode;
170    crtc->x = x;
171    crtc->y = y;
172    crtc->rotation = rotation;
173#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC (1, 5, 99, 0, 0)
174    crtc->transformPresent = FALSE;
175#endif
176    /* TODO set EDID here */
177    return TRUE;
178}
179
180Bool
181qxl_create_desired_modes (qxl_screen_t *qxl)
182{
183    int i;
184    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR (qxl->pScrn);
185
186    CHECK_POINT ();
187
188    for (i = 0 ; i < config->num_crtc; ++i)
189    {
190	xf86CrtcPtr crtc = config->crtc[i];
191	if (!crtc->enabled)
192	    continue;
193
194	if (!crtc_set_mode_major (
195		crtc, &crtc->desiredMode, crtc->desiredRotation,
196		crtc->desiredX, crtc->desiredY))
197	{
198	    return FALSE;
199	}
200    }
201
202    qxl_update_monitors_config(qxl);
203    return TRUE;
204}
205
206void
207qxl_update_edid (qxl_screen_t *qxl)
208{
209    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR (qxl->pScrn);
210    int i;
211
212    for (i = 0; i < config->num_crtc; ++i)
213    {
214	xf86CrtcPtr crtc = config->crtc[i];
215
216	if (!crtc->enabled)
217	    continue;
218
219	/* TODO set EDID here */
220    }
221}
222
223
224static DisplayModePtr
225qxl_output_get_modes (xf86OutputPtr output)
226{
227    qxl_output_private *qxl_output = output->driver_private;
228    DisplayModePtr      modes = xf86DuplicateModes (qxl_output->qxl->pScrn, qxl_output->qxl->x_modes);
229
230    /* xf86ProbeOutputModes owns this memory */
231    return modes;
232}
233
234static void
235qxl_output_destroy (xf86OutputPtr output)
236{
237    qxl_output_private *qxl_output = output->driver_private;
238
239    xf86DrvMsg (qxl_output->qxl->pScrn->scrnIndex, X_INFO,
240                "%s", __func__);
241}
242
243static void
244qxl_output_dpms (xf86OutputPtr output, int mode)
245{
246}
247
248static void
249qxl_output_create_resources (xf86OutputPtr output)
250{
251}
252
253static Bool
254qxl_output_set_property (xf86OutputPtr output, Atom property,
255                         RRPropertyValuePtr value)
256{
257    /* EDID data is stored in the "EDID" atom property, we must return
258     * TRUE here for that. No penalty to say ok to everything else. */
259    return TRUE;
260}
261
262static Bool
263qxl_output_get_property (xf86OutputPtr output, Atom property)
264{
265    return TRUE;
266}
267
268static xf86OutputStatus
269qxl_output_detect (xf86OutputPtr output)
270{
271    qxl_output_private *qxl_output = output->driver_private;
272
273    return qxl_output->status;
274}
275
276static Bool
277qxl_output_mode_valid (xf86OutputPtr output, DisplayModePtr pModes)
278{
279    return MODE_OK;
280}
281
282static const xf86OutputFuncsRec qxl_output_funcs = {
283    .dpms = qxl_output_dpms,
284    .create_resources = qxl_output_create_resources,
285#ifdef RANDR_12_INTERFACE
286    .set_property = qxl_output_set_property,
287    .get_property = qxl_output_get_property,
288#endif
289    .detect = qxl_output_detect,
290    .mode_valid = qxl_output_mode_valid,
291
292    .get_modes = qxl_output_get_modes,
293    .destroy = qxl_output_destroy
294};
295
296
297static void
298qxl_crtc_dpms (xf86CrtcPtr crtc, int mode)
299{
300}
301
302static Bool
303qxl_crtc_set_mode_major (xf86CrtcPtr crtc, DisplayModePtr mode,
304                         Rotation rotation, int x, int y)
305{
306    qxl_crtc_private *crtc_private = crtc->driver_private;
307    qxl_screen_t *    qxl = crtc_private->qxl;
308
309    CHECK_POINT ();
310
311    if (!crtc_set_mode_major (crtc, mode, rotation, x, y))
312	return FALSE;
313
314    qxl_update_monitors_config (qxl);
315
316    return TRUE;
317}
318
319static void
320qxl_crtc_set_cursor_colors (xf86CrtcPtr crtc, int bg, int fg)
321{
322}
323
324static void
325qxl_crtc_set_cursor_position (xf86CrtcPtr crtc, int x, int y)
326{
327}
328
329static void
330qxl_crtc_load_cursor_argb (xf86CrtcPtr crtc, CARD32 *image)
331{
332}
333
334static void
335qxl_crtc_hide_cursor (xf86CrtcPtr crtc)
336{
337}
338
339static void
340qxl_crtc_show_cursor (xf86CrtcPtr crtc)
341{
342}
343
344static void
345qxl_crtc_gamma_set (xf86CrtcPtr crtc, uint16_t *red, uint16_t *green,
346                    uint16_t *blue, int size)
347{
348}
349
350static void
351qxl_crtc_destroy (xf86CrtcPtr crtc)
352{
353    qxl_crtc_private *crtc_private = crtc->driver_private;
354    qxl_screen_t *    qxl = crtc_private->qxl;
355
356    xf86DrvMsg (qxl->pScrn->scrnIndex, X_INFO, "%s\n", __func__);
357}
358
359static Bool
360qxl_crtc_lock (xf86CrtcPtr crtc)
361{
362    qxl_crtc_private *crtc_private = crtc->driver_private;
363    qxl_screen_t *    qxl = crtc_private->qxl;
364
365    xf86DrvMsg (qxl->pScrn->scrnIndex, X_INFO, "%s\n", __func__);
366    return TRUE;
367}
368
369static void
370qxl_crtc_unlock (xf86CrtcPtr crtc)
371{
372    qxl_crtc_private *crtc_private = crtc->driver_private;
373    qxl_screen_t *    qxl = crtc_private->qxl;
374
375    xf86DrvMsg (qxl->pScrn->scrnIndex, X_INFO, "%s\n", __func__);
376    qxl_update_monitors_config (qxl);
377}
378
379static const xf86CrtcFuncsRec qxl_crtc_funcs = {
380    .dpms = qxl_crtc_dpms,
381    .set_mode_major = qxl_crtc_set_mode_major,
382    .set_cursor_colors = qxl_crtc_set_cursor_colors,
383    .set_cursor_position = qxl_crtc_set_cursor_position,
384    .show_cursor = qxl_crtc_show_cursor,
385    .hide_cursor = qxl_crtc_hide_cursor,
386    .load_cursor_argb = qxl_crtc_load_cursor_argb,
387    .lock = qxl_crtc_lock,
388    .unlock = qxl_crtc_unlock,
389
390    .gamma_set = qxl_crtc_gamma_set,
391    .destroy = qxl_crtc_destroy,
392};
393
394
395static Bool
396qxl_xf86crtc_resize (ScrnInfoPtr scrn, int width, int height)
397{
398    qxl_screen_t *qxl = scrn->driverPrivate;
399
400    xf86DrvMsg (scrn->scrnIndex, X_INFO, "%s: Placeholder resize %dx%d\n",
401                __func__, width, height);
402    if (!qxl_resize_primary (qxl, width, height))
403	return FALSE;
404
405    scrn->virtualX = width;
406    scrn->virtualY = height;
407
408    // when starting, no monitor is enabled, and count == 0
409    // we want to avoid server/client freaking out with temporary config
410    qxl_update_monitors_config (qxl);
411
412    return TRUE;
413}
414
415static const xf86CrtcConfigFuncsRec qxl_xf86crtc_config_funcs = {
416    qxl_xf86crtc_resize
417};
418
419void
420qxl_initialize_x_modes (qxl_screen_t *qxl, ScrnInfoPtr pScrn,
421                        unsigned int *max_x, unsigned int *max_y)
422{
423    int i;
424    int size;
425    int preferred_flag;
426
427    *max_x = *max_y = 0;
428    /* Create a list of modes used by the qxl_output_get_modes */
429    for (i = 0; i < qxl->num_modes; i++)
430    {
431	if (qxl->modes[i].orientation == 0)
432	{
433	    size = qxl->modes[i].y_res * qxl->modes[i].stride;
434	    if (size > qxl->surface0_size)
435	    {
436		ErrorF ("skipping mode %dx%d not fitting in surface0\n",
437		        qxl->modes[i].x_res, qxl->modes[i].y_res);
438		continue;
439	    }
440
441            if (qxl->modes[i].x_res == DEFAULT_WIDTH && qxl->modes[i].y_res == DEFAULT_HEIGHT)
442                preferred_flag = M_T_PREFERRED;
443            else
444                preferred_flag = 0;
445
446	    qxl_add_mode (qxl, pScrn, qxl->modes[i].x_res, qxl->modes[i].y_res,
447	                  M_T_DRIVER | preferred_flag);
448
449	    if (qxl->modes[i].x_res > *max_x)
450		*max_x = qxl->modes[i].x_res;
451	    if (qxl->modes[i].y_res > *max_y)
452		*max_y = qxl->modes[i].y_res;
453	}
454    }
455}
456
457void
458qxl_init_randr (ScrnInfoPtr pScrn, qxl_screen_t *qxl)
459{
460    char                name[32];
461    qxl_output_private *qxl_output;
462    qxl_crtc_private *  qxl_crtc;
463    int                 i;
464    xf86OutputPtr       output;
465
466    xf86CrtcConfigInit (pScrn, &qxl_xf86crtc_config_funcs);
467
468    /* CHECKME: This is actually redundant, it's overwritten by a later call via
469     * xf86InitialConfiguration */
470    xf86CrtcSetSizeRange (pScrn, 320, 200, 8192, 8192);
471
472    qxl->crtcs = xnfcalloc (sizeof (xf86CrtcPtr), qxl->num_heads);
473    qxl->outputs = xnfcalloc (sizeof (xf86OutputPtr), qxl->num_heads);
474
475    for (i = 0 ; i < qxl->num_heads; ++i)
476    {
477	qxl->crtcs[i] = xf86CrtcCreate (pScrn, &qxl_crtc_funcs);
478	if (!qxl->crtcs[i])
479	    xf86DrvMsg (pScrn->scrnIndex, X_ERROR, "failed to create Crtc %d", i);
480
481	qxl_crtc = xnfcalloc (sizeof (qxl_crtc_private), 1);
482	qxl->crtcs[i]->driver_private = qxl_crtc;
483	qxl_crtc->head = i;
484	qxl_crtc->qxl = qxl;
485	snprintf (name, sizeof (name), "qxl-%d", i);
486	qxl->outputs[i] = output = xf86OutputCreate (pScrn, &qxl_output_funcs, name);
487	if (!output)
488	    xf86DrvMsg (pScrn->scrnIndex, X_ERROR, "failed to create Output %d", i);
489
490	output->possible_crtcs = (1 << i); /* bitrange of allowed outputs - do a 1:1 */
491	output->possible_clones = 0; /* TODO: not? */
492	qxl_output = xnfcalloc (sizeof (qxl_output_private), 1);
493	output->driver_private = qxl_output;
494	qxl_output->head = i;
495	qxl_output->qxl = qxl;
496	qxl_output->status = i ? XF86OutputStatusDisconnected : XF86OutputStatusConnected;
497	qxl_crtc->output = output;
498    }
499
500    xf86InitialConfiguration (pScrn, TRUE);
501
502    qxl->virtual_x = pScrn->virtualX;
503    qxl->virtual_y = pScrn->virtualY;
504    /* all crtcs are enabled here, but their mode is 0,
505       resulting monitor config empty atm */
506}
507