1d514b0f3Smrg/*
2d514b0f3Smrg * Copyright 2011 Red Hat, Inc.
3d514b0f3Smrg *
4d514b0f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5d514b0f3Smrg * copy of this software and associated documentation files (the "Software"),
6d514b0f3Smrg * to deal in the Software without restriction, including without limitation
7d514b0f3Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub
8d514b0f3Smrg * license, and/or sell copies of the Software, and to permit persons to whom
9d514b0f3Smrg * the Software is furnished to do so, subject to the following conditions:
10d514b0f3Smrg *
11d514b0f3Smrg * The above copyright notice and this permission notice (including the next
12d514b0f3Smrg * paragraph) shall be included in all copies or substantial portions of the
13d514b0f3Smrg * Software.
14d514b0f3Smrg *
15d514b0f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16d514b0f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17d514b0f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
18d514b0f3Smrg * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19d514b0f3Smrg * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20d514b0f3Smrg * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21d514b0f3Smrg */
22d514b0f3Smrg#ifdef HAVE_CONFIG_H
23d514b0f3Smrg#include "config.h"
24d514b0f3Smrg#endif
25d514b0f3Smrg
26d514b0f3Smrg#include <pthread.h>
27d514b0f3Smrg#include <sched.h>
28d514b0f3Smrg
29d514b0f3Smrg#include <spice.h>
30d514b0f3Smrg
31d514b0f3Smrg#include "qxl.h"
32d514b0f3Smrg#include "spiceqxl_io_port.h"
33d514b0f3Smrg
34d514b0f3Smrg/* TODO: taken from qemu qxl.c, try to remove duplication */
35d514b0f3Smrg#undef SPICE_RING_PROD_ITEM
36d514b0f3Smrg#define SPICE_RING_PROD_ITEM(r, ret) {                                  \
37d514b0f3Smrg        typeof(r) start = r;                                            \
38d514b0f3Smrg        typeof(r) end = r + 1;                                          \
39d514b0f3Smrg        uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r);           \
40d514b0f3Smrg        typeof(&(r)->items[prod]) m_item = &(r)->items[prod];           \
41d514b0f3Smrg        if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
42d514b0f3Smrg            abort();                                                    \
43d514b0f3Smrg        }                                                               \
44d514b0f3Smrg        ret = &m_item->el;                                              \
45d514b0f3Smrg    }
46d514b0f3Smrg
47d514b0f3Smrg
48d514b0f3Smrgstatic int spiceqxl_io_port_debug_level = -1;
49d514b0f3Smrg
50d514b0f3Smrgstatic void __attribute__ ((format (printf, 2, 3))) dprint(int _level, const char *_fmt, ...)
51d514b0f3Smrg{
52d514b0f3Smrg    if (spiceqxl_io_port_debug_level == -1) {
53d514b0f3Smrg        if (getenv("XSPICE_IO_PORT_DEBUG_LEVEL")) {
54d514b0f3Smrg            spiceqxl_io_port_debug_level = atoi(
55d514b0f3Smrg                getenv("XSPICE_IO_PORT_DEBUG_LEVEL"));
56d514b0f3Smrg        } else {
57d514b0f3Smrg            spiceqxl_io_port_debug_level = 0;
58d514b0f3Smrg        }
59d514b0f3Smrg    }
60d514b0f3Smrg    if (spiceqxl_io_port_debug_level >= _level) {
61d514b0f3Smrg        va_list ap;
62d514b0f3Smrg        va_start(ap, _fmt);
63d514b0f3Smrg        vfprintf(stderr, _fmt, ap);
64d514b0f3Smrg        va_end(ap);
65d514b0f3Smrg    }
66d514b0f3Smrg}
67d514b0f3Smrg
68d514b0f3Smrgvoid xspice_init_qxl_ram(qxl_screen_t *qxl)
69d514b0f3Smrg{
70d514b0f3Smrg    QXLRam *ram = get_ram_header(qxl);
71d514b0f3Smrg    uint64_t *item;
72d514b0f3Smrg
73d514b0f3Smrg    ram->magic       = QXL_RAM_MAGIC;
74d514b0f3Smrg    ram->int_pending = 0;
75d514b0f3Smrg    ram->int_mask    = 0;
76d514b0f3Smrg    SPICE_RING_INIT(&ram->cmd_ring);
77d514b0f3Smrg    SPICE_RING_INIT(&ram->cursor_ring);
78d514b0f3Smrg    SPICE_RING_INIT(&ram->release_ring);
79d514b0f3Smrg    SPICE_RING_PROD_ITEM(&ram->release_ring, item);
80d514b0f3Smrg    *item = 0;
81d514b0f3Smrg}
82d514b0f3Smrg
83d514b0f3Smrgstatic void qxl_reset_state(qxl_screen_t *qxl)
84d514b0f3Smrg{
85d514b0f3Smrg    QXLRam *ram = get_ram_header(qxl);
86d514b0f3Smrg
87d514b0f3Smrg    assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring));
88d514b0f3Smrg    assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring));
89d514b0f3Smrg    qxl->shadow_rom.update_id = 0;
90d514b0f3Smrg    *qxl->rom = qxl->shadow_rom;
91d514b0f3Smrg    xspice_init_qxl_ram(qxl);
92d514b0f3Smrg    qxl->num_free_res = 0;
93d514b0f3Smrg    qxl->last_release = NULL;
94d514b0f3Smrg    // TODO - dirty ?
95d514b0f3Smrg    //memset(&qxl->ssd.dirty, 0, sizeof(qxl->ssd.dirty));
96d514b0f3Smrg}
97d514b0f3Smrg
98d514b0f3Smrgstatic void qxl_check_state(qxl_screen_t *qxl)
99d514b0f3Smrg{
100d514b0f3Smrg    QXLRam *ram = get_ram_header(qxl);
101d514b0f3Smrg
102d514b0f3Smrg    assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring));
103d514b0f3Smrg    assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring));
104d514b0f3Smrg}
105d514b0f3Smrg
106d514b0f3Smrgstatic void qxl_soft_reset(qxl_screen_t *qxl)
107d514b0f3Smrg{
108d514b0f3Smrg    dprint(1, "%s:\n", __FUNCTION__);
109d514b0f3Smrg    qxl_check_state(qxl);
110d514b0f3Smrg}
111d514b0f3Smrg
112d514b0f3Smrgstatic void qxl_reset_surfaces(qxl_screen_t *qxl)
113d514b0f3Smrg{
114d514b0f3Smrg    dprint(1, "%s:\n", __FUNCTION__);
115d514b0f3Smrg    spice_qxl_destroy_surfaces(&qxl->display_sin);
116d514b0f3Smrg    // TODO - do we have guest_surfaces?
117d514b0f3Smrg    //memset(&d->guest_surfaces.cmds, 0, sizeof(d->guest_surfaces.cmds));
118d514b0f3Smrg}
119d514b0f3Smrg
120d514b0f3Smrgstatic void qxl_hard_reset(qxl_screen_t *qxl)
121d514b0f3Smrg{
122d514b0f3Smrg    dprint(1, "%s: start\n", __FUNCTION__);
123d514b0f3Smrg
124d514b0f3Smrg    spice_qxl_reset_cursor(&qxl->display_sin);
125d514b0f3Smrg    spice_qxl_reset_image_cache(&qxl->display_sin);
126d514b0f3Smrg    qxl_reset_surfaces(qxl);
127d514b0f3Smrg
128d514b0f3Smrg    qxl_reset_state(qxl);
129d514b0f3Smrg    qxl_soft_reset(qxl);
130d514b0f3Smrg
131d514b0f3Smrg    dprint(1, "%s: done\n", __FUNCTION__);
132d514b0f3Smrg}
133d514b0f3Smrg
134d514b0f3Smrgstatic void qxl_create_guest_primary(qxl_screen_t *qxl)
135d514b0f3Smrg{
136d514b0f3Smrg    QXLDevSurfaceCreate surface;
137d514b0f3Smrg    QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
138d514b0f3Smrg
139d514b0f3Smrg    dprint(1, "%s: %dx%d\n", __FUNCTION__, sc->width, sc->height);
140d514b0f3Smrg
141d514b0f3Smrg    surface.format     = sc->format;
142d514b0f3Smrg    surface.height     = sc->height;
143d514b0f3Smrg    surface.mem        = sc->mem;
144d514b0f3Smrg    surface.position   = sc->position;
145d514b0f3Smrg    surface.stride     = sc->stride;
146d514b0f3Smrg    surface.width      = sc->width;
147d514b0f3Smrg    surface.type       = sc->type;
148d514b0f3Smrg    surface.flags      = sc->flags;
149d514b0f3Smrg
150d514b0f3Smrg    surface.mouse_mode = TRUE;
151d514b0f3Smrg    surface.group_id   = 0;
152d514b0f3Smrg    qxl->cmdflags = 0;
153d514b0f3Smrg    spice_qxl_create_primary_surface(&qxl->display_sin, 0, &surface);
154d514b0f3Smrg}
155d514b0f3Smrg
156d514b0f3Smrgstatic void qxl_destroy_primary(qxl_screen_t *qxl)
157d514b0f3Smrg{
158d514b0f3Smrg    dprint(1, "%s\n", __FUNCTION__);
159d514b0f3Smrg
160d514b0f3Smrg    spice_qxl_destroy_primary_surface(&qxl->display_sin, 0);
161d514b0f3Smrg}
162d514b0f3Smrg
163d514b0f3Smrg
164d514b0f3Smrgstatic void qxl_set_mode(qxl_screen_t *qxl, int modenr)
165d514b0f3Smrg{
166d514b0f3Smrg    struct QXLMode *mode = qxl->modes + modenr;
167d514b0f3Smrg    uint64_t devmem = pointer_to_u64(qxl->ram);
168d514b0f3Smrg    QXLSurfaceCreate surface = {
169d514b0f3Smrg        .width      = mode->x_res,
170d514b0f3Smrg        .height     = mode->y_res,
171d514b0f3Smrg        .stride     = -mode->x_res * 4,
172d514b0f3Smrg        .format     = SPICE_SURFACE_FMT_32_xRGB,
173d514b0f3Smrg        .flags      = 0,
174d514b0f3Smrg        .mouse_mode = TRUE,
175d514b0f3Smrg        .mem        = devmem + qxl->shadow_rom.draw_area_offset,
176d514b0f3Smrg    };
177d514b0f3Smrg
178d514b0f3Smrg    dprint(1, "%s: mode %d  [ %d x %d @ %d bpp devmem 0x%llx ]\n", __FUNCTION__,
179d514b0f3Smrg           modenr, mode->x_res, mode->y_res, mode->bits, (unsigned long long) devmem);
180d514b0f3Smrg    qxl_hard_reset(qxl);
181d514b0f3Smrg
182d514b0f3Smrg    qxl->guest_primary.surface = surface;
183d514b0f3Smrg    qxl_create_guest_primary(qxl);
184d514b0f3Smrg
185d514b0f3Smrg    qxl->cmdflags = QXL_COMMAND_FLAG_COMPAT;
186d514b0f3Smrg#ifdef QXL_COMMAND_FLAG_COMPAT_16BPP /* new in spice 0.6.1 */
187d514b0f3Smrg    if (mode->bits == 16) {
188d514b0f3Smrg        qxl->cmdflags |= QXL_COMMAND_FLAG_COMPAT_16BPP;
189d514b0f3Smrg    }
190d514b0f3Smrg#endif
191d514b0f3Smrg    qxl->shadow_rom.mode = modenr;
192d514b0f3Smrg    qxl->rom->mode = modenr;
193d514b0f3Smrg}
194d514b0f3Smrg
195d514b0f3Smrg/* called from Xorg thread - not worker thread! */
196d514b0f3Smrgvoid ioport_write(qxl_screen_t *qxl, uint32_t io_port, uint32_t val)
197d514b0f3Smrg{
198d514b0f3Smrg    QXLRam *header = get_ram_header(qxl);
199d514b0f3Smrg
200d514b0f3Smrg    if (!qxl->worker_running) {
201d514b0f3Smrg        return;
202d514b0f3Smrg    }
203d514b0f3Smrg
204d514b0f3Smrg    switch (io_port) {
205d514b0f3Smrg    case QXL_IO_UPDATE_AREA:
206d514b0f3Smrg    {
207d514b0f3Smrg        QXLRect update = *(QXLRect*)&header->update_area;
208d514b0f3Smrg        spice_qxl_update_area(&qxl->display_sin, header->update_surface,
209d514b0f3Smrg                                   &update, NULL, 0, 0);
210d514b0f3Smrg        break;
211d514b0f3Smrg    }
212d514b0f3Smrg    case QXL_IO_NOTIFY_CMD:
213d514b0f3Smrg        spice_qxl_wakeup(&qxl->display_sin);
214d514b0f3Smrg        break;
215d514b0f3Smrg    case QXL_IO_NOTIFY_CURSOR:
216d514b0f3Smrg        spice_qxl_wakeup(&qxl->display_sin);
217d514b0f3Smrg        break;
218d514b0f3Smrg    case QXL_IO_UPDATE_IRQ:
219d514b0f3Smrg        /* qxl_set_irq(d); */
220d514b0f3Smrg        printf("QXL_IO_UPDATE_IRQ not implemented\n");
221d514b0f3Smrg        break;
222d514b0f3Smrg    case QXL_IO_NOTIFY_OOM:
223d514b0f3Smrg        if (!SPICE_RING_IS_EMPTY(&header->release_ring)) {
224d514b0f3Smrg            break;
225d514b0f3Smrg        }
226d514b0f3Smrg        sched_yield();
227d514b0f3Smrg        if (!SPICE_RING_IS_EMPTY(&header->release_ring)) {
228d514b0f3Smrg            break;
229d514b0f3Smrg        }
230d514b0f3Smrg        spice_qxl_oom(&qxl->display_sin);
231d514b0f3Smrg        break;
232d514b0f3Smrg    case QXL_IO_SET_MODE:
233d514b0f3Smrg        dprint(1, "QXL_SET_MODE %d\n", val);
234d514b0f3Smrg        qxl_set_mode(qxl, val);
235d514b0f3Smrg        break;
236d514b0f3Smrg    case QXL_IO_LOG:
237d514b0f3Smrg        fprintf(stderr, "qxl/guest: %s", header->log_buf);
238d514b0f3Smrg        break;
239d514b0f3Smrg    case QXL_IO_RESET:
240d514b0f3Smrg        dprint(1, "QXL_IO_RESET\n");
241d514b0f3Smrg        qxl_hard_reset(qxl);
242d514b0f3Smrg        break;
243d514b0f3Smrg    case QXL_IO_MEMSLOT_ADD:
244d514b0f3Smrg        dprint(1, "QXL_IO_MEMSLOT_ADD - should not be called (this is Xspice)\n");
245d514b0f3Smrg        break;
246d514b0f3Smrg    case QXL_IO_MEMSLOT_DEL:
247d514b0f3Smrg        dprint(1, "QXL_IO_MEMSLOT_DEL - should not be called (this is Xspice)\n");
248d514b0f3Smrg        break;
249d514b0f3Smrg    case QXL_IO_CREATE_PRIMARY:
250d514b0f3Smrg        assert(val == 0);
251d514b0f3Smrg        dprint(1, "QXL_IO_CREATE_PRIMARY\n");
252d514b0f3Smrg        qxl->guest_primary.surface =
253d514b0f3Smrg            *(QXLSurfaceCreate*)&header->create_surface;
254d514b0f3Smrg        qxl_create_guest_primary(qxl);
255d514b0f3Smrg        break;
256d514b0f3Smrg    case QXL_IO_DESTROY_PRIMARY:
257d514b0f3Smrg        assert(val == 0);
258d514b0f3Smrg        dprint(1, "QXL_IO_DESTROY_PRIMARY\n");
259d514b0f3Smrg        qxl_destroy_primary(qxl);
260d514b0f3Smrg        break;
261d514b0f3Smrg    case QXL_IO_DESTROY_SURFACE_WAIT:
262d514b0f3Smrg        spice_qxl_destroy_surface_wait(&qxl->display_sin, val);
263d514b0f3Smrg        break;
264d514b0f3Smrg    case QXL_IO_DESTROY_ALL_SURFACES:
265d514b0f3Smrg        spice_qxl_destroy_surfaces(&qxl->display_sin);
266d514b0f3Smrg        break;
267d514b0f3Smrg    case QXL_IO_FLUSH_SURFACES_ASYNC:
268d514b0f3Smrg        fprintf(stderr, "ERROR: async callback Unimplemented\n");
269d514b0f3Smrg        spice_qxl_flush_surfaces_async(&qxl->display_sin, 0);
270d514b0f3Smrg        break;
271d514b0f3Smrg    default:
272d514b0f3Smrg        fprintf(stderr, "%s: ioport=0x%x, abort()\n", __FUNCTION__, io_port);
273d514b0f3Smrg        abort();
274d514b0f3Smrg    }
275d514b0f3Smrg}
276d514b0f3Smrg
277