]> Git Repo - qemu.git/blob - hw/display/milkymist-tmu2.c
virtio-gpu: clear command and fence queues on reset
[qemu.git] / hw / display / milkymist-tmu2.c
1 /*
2  *  QEMU model of the Milkymist texture mapping unit.
3  *
4  *  Copyright (c) 2010 Michael Walle <[email protected]>
5  *  Copyright (c) 2010 Sebastien Bourdeauducq
6  *                       <[email protected]>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20  *
21  *
22  * Specification available at:
23  *   http://milkymist.walle.cc/socdoc/tmu2.pdf
24  *
25  */
26
27 #include "qemu/osdep.h"
28 #include "hw/hw.h"
29 #include "hw/sysbus.h"
30 #include "trace.h"
31 #include "qapi/error.h"
32 #include "qemu/error-report.h"
33 #include "qapi/error.h"
34 #include "hw/display/milkymist_tmu2.h"
35
36 #include <X11/Xlib.h>
37 #include <epoxy/gl.h>
38 #include <epoxy/glx.h>
39
40 enum {
41     R_CTL = 0,
42     R_HMESHLAST,
43     R_VMESHLAST,
44     R_BRIGHTNESS,
45     R_CHROMAKEY,
46     R_VERTICESADDR,
47     R_TEXFBUF,
48     R_TEXHRES,
49     R_TEXVRES,
50     R_TEXHMASK,
51     R_TEXVMASK,
52     R_DSTFBUF,
53     R_DSTHRES,
54     R_DSTVRES,
55     R_DSTHOFFSET,
56     R_DSTVOFFSET,
57     R_DSTSQUAREW,
58     R_DSTSQUAREH,
59     R_ALPHA,
60     R_MAX
61 };
62
63 enum {
64     CTL_START_BUSY  = (1<<0),
65     CTL_CHROMAKEY   = (1<<1),
66 };
67
68 enum {
69     MAX_BRIGHTNESS = 63,
70     MAX_ALPHA      = 63,
71 };
72
73 enum {
74     MESH_MAXSIZE = 128,
75 };
76
77 struct vertex {
78     int x;
79     int y;
80 } QEMU_PACKED;
81
82 #define TYPE_MILKYMIST_TMU2 "milkymist-tmu2"
83 #define MILKYMIST_TMU2(obj) \
84     OBJECT_CHECK(MilkymistTMU2State, (obj), TYPE_MILKYMIST_TMU2)
85
86 struct MilkymistTMU2State {
87     SysBusDevice parent_obj;
88
89     MemoryRegion regs_region;
90     Chardev *chr;
91     qemu_irq irq;
92
93     uint32_t regs[R_MAX];
94
95     Display *dpy;
96     GLXFBConfig glx_fb_config;
97     GLXContext glx_context;
98 };
99 typedef struct MilkymistTMU2State MilkymistTMU2State;
100
101 static const int glx_fbconfig_attr[] = {
102     GLX_GREEN_SIZE, 5,
103     GLX_GREEN_SIZE, 6,
104     GLX_BLUE_SIZE, 5,
105     None
106 };
107
108 static int tmu2_glx_init(MilkymistTMU2State *s)
109 {
110     GLXFBConfig *configs;
111     int nelements;
112
113     s->dpy = XOpenDisplay(NULL); /* FIXME: call XCloseDisplay() */
114     if (s->dpy == NULL) {
115         return 1;
116     }
117
118     configs = glXChooseFBConfig(s->dpy, 0, glx_fbconfig_attr, &nelements);
119     if (configs == NULL) {
120         return 1;
121     }
122
123     s->glx_fb_config = *configs;
124     XFree(configs);
125
126     /* FIXME: call glXDestroyContext() */
127     s->glx_context = glXCreateNewContext(s->dpy, s->glx_fb_config,
128             GLX_RGBA_TYPE, NULL, 1);
129     if (s->glx_context == NULL) {
130         return 1;
131     }
132
133     return 0;
134 }
135
136 static void tmu2_gl_map(struct vertex *mesh, int texhres, int texvres,
137         int hmeshlast, int vmeshlast, int ho, int vo, int sw, int sh)
138 {
139     int x, y;
140     int x0, y0, x1, y1;
141     int u0, v0, u1, v1, u2, v2, u3, v3;
142     double xscale = 1.0 / ((double)(64 * texhres));
143     double yscale = 1.0 / ((double)(64 * texvres));
144
145     glLoadIdentity();
146     glTranslatef(ho, vo, 0);
147     glEnable(GL_TEXTURE_2D);
148     glBegin(GL_QUADS);
149
150     for (y = 0; y < vmeshlast; y++) {
151         y0 = y * sh;
152         y1 = y0 + sh;
153         for (x = 0; x < hmeshlast; x++) {
154             x0 = x * sw;
155             x1 = x0 + sw;
156
157             u0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].x);
158             v0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].y);
159             u1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].x);
160             v1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].y);
161             u2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].x);
162             v2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].y);
163             u3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].x);
164             v3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].y);
165
166             glTexCoord2d(((double)u0) * xscale, ((double)v0) * yscale);
167             glVertex3i(x0, y0, 0);
168             glTexCoord2d(((double)u1) * xscale, ((double)v1) * yscale);
169             glVertex3i(x1, y0, 0);
170             glTexCoord2d(((double)u2) * xscale, ((double)v2) * yscale);
171             glVertex3i(x1, y1, 0);
172             glTexCoord2d(((double)u3) * xscale, ((double)v3) * yscale);
173             glVertex3i(x0, y1, 0);
174         }
175     }
176
177     glEnd();
178 }
179
180 static void tmu2_start(MilkymistTMU2State *s)
181 {
182     int pbuffer_attrib[6] = {
183         GLX_PBUFFER_WIDTH,
184         0,
185         GLX_PBUFFER_HEIGHT,
186         0,
187         GLX_PRESERVED_CONTENTS,
188         True
189     };
190
191     GLXPbuffer pbuffer;
192     GLuint texture;
193     void *fb;
194     hwaddr fb_len;
195     void *mesh;
196     hwaddr mesh_len;
197     float m;
198
199     trace_milkymist_tmu2_start();
200
201     /* Create and set up a suitable OpenGL context */
202     pbuffer_attrib[1] = s->regs[R_DSTHRES];
203     pbuffer_attrib[3] = s->regs[R_DSTVRES];
204     pbuffer = glXCreatePbuffer(s->dpy, s->glx_fb_config, pbuffer_attrib);
205     glXMakeContextCurrent(s->dpy, pbuffer, pbuffer, s->glx_context);
206
207     /* Fixup endianness. TODO: would it work on BE hosts? */
208     glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
209     glPixelStorei(GL_PACK_SWAP_BYTES, 1);
210
211     /* Row alignment */
212     glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
213     glPixelStorei(GL_PACK_ALIGNMENT, 2);
214
215     /* Read the QEMU source framebuffer into an OpenGL texture */
216     glGenTextures(1, &texture);
217     glBindTexture(GL_TEXTURE_2D, texture);
218     fb_len = 2ULL * s->regs[R_TEXHRES] * s->regs[R_TEXVRES];
219     fb = cpu_physical_memory_map(s->regs[R_TEXFBUF], &fb_len, 0);
220     if (fb == NULL) {
221         glDeleteTextures(1, &texture);
222         glXMakeContextCurrent(s->dpy, None, None, NULL);
223         glXDestroyPbuffer(s->dpy, pbuffer);
224         return;
225     }
226     glTexImage2D(GL_TEXTURE_2D, 0, 3, s->regs[R_TEXHRES], s->regs[R_TEXVRES],
227             0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, fb);
228     cpu_physical_memory_unmap(fb, fb_len, 0, fb_len);
229
230     /* Set up texturing options */
231     /* WARNING:
232      * Many cases of TMU2 masking are not supported by OpenGL.
233      * We only implement the most common ones:
234      *  - full bilinear filtering vs. nearest texel
235      *  - texture clamping vs. texture wrapping
236      */
237     if ((s->regs[R_TEXHMASK] & 0x3f) > 0x20) {
238         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
239         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
240     } else {
241         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
242         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
243     }
244     if ((s->regs[R_TEXHMASK] >> 6) & s->regs[R_TEXHRES]) {
245         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
246     } else {
247         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
248     }
249     if ((s->regs[R_TEXVMASK] >> 6) & s->regs[R_TEXVRES]) {
250         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
251     } else {
252         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
253     }
254
255     /* Translucency and decay */
256     glEnable(GL_BLEND);
257     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
258     m = (float)(s->regs[R_BRIGHTNESS] + 1) / 64.0f;
259     glColor4f(m, m, m, (float)(s->regs[R_ALPHA] + 1) / 64.0f);
260
261     /* Read the QEMU dest. framebuffer into the OpenGL framebuffer */
262     fb_len = 2ULL * s->regs[R_DSTHRES] * s->regs[R_DSTVRES];
263     fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 0);
264     if (fb == NULL) {
265         glDeleteTextures(1, &texture);
266         glXMakeContextCurrent(s->dpy, None, None, NULL);
267         glXDestroyPbuffer(s->dpy, pbuffer);
268         return;
269     }
270
271     glDrawPixels(s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB,
272             GL_UNSIGNED_SHORT_5_6_5, fb);
273     cpu_physical_memory_unmap(fb, fb_len, 0, fb_len);
274     glViewport(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES]);
275     glMatrixMode(GL_PROJECTION);
276     glLoadIdentity();
277     glOrtho(0.0, s->regs[R_DSTHRES], 0.0, s->regs[R_DSTVRES], -1.0, 1.0);
278     glMatrixMode(GL_MODELVIEW);
279
280     /* Map the texture */
281     mesh_len = MESH_MAXSIZE*MESH_MAXSIZE*sizeof(struct vertex);
282     mesh = cpu_physical_memory_map(s->regs[R_VERTICESADDR], &mesh_len, 0);
283     if (mesh == NULL) {
284         glDeleteTextures(1, &texture);
285         glXMakeContextCurrent(s->dpy, None, None, NULL);
286         glXDestroyPbuffer(s->dpy, pbuffer);
287         return;
288     }
289
290     tmu2_gl_map((struct vertex *)mesh,
291         s->regs[R_TEXHRES], s->regs[R_TEXVRES],
292         s->regs[R_HMESHLAST], s->regs[R_VMESHLAST],
293         s->regs[R_DSTHOFFSET], s->regs[R_DSTVOFFSET],
294         s->regs[R_DSTSQUAREW], s->regs[R_DSTSQUAREH]);
295     cpu_physical_memory_unmap(mesh, mesh_len, 0, mesh_len);
296
297     /* Write back the OpenGL framebuffer to the QEMU framebuffer */
298     fb_len = 2ULL * s->regs[R_DSTHRES] * s->regs[R_DSTVRES];
299     fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 1);
300     if (fb == NULL) {
301         glDeleteTextures(1, &texture);
302         glXMakeContextCurrent(s->dpy, None, None, NULL);
303         glXDestroyPbuffer(s->dpy, pbuffer);
304         return;
305     }
306
307     glReadPixels(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB,
308             GL_UNSIGNED_SHORT_5_6_5, fb);
309     cpu_physical_memory_unmap(fb, fb_len, 1, fb_len);
310
311     /* Free OpenGL allocs */
312     glDeleteTextures(1, &texture);
313     glXMakeContextCurrent(s->dpy, None, None, NULL);
314     glXDestroyPbuffer(s->dpy, pbuffer);
315
316     s->regs[R_CTL] &= ~CTL_START_BUSY;
317
318     trace_milkymist_tmu2_pulse_irq();
319     qemu_irq_pulse(s->irq);
320 }
321
322 static uint64_t tmu2_read(void *opaque, hwaddr addr,
323                           unsigned size)
324 {
325     MilkymistTMU2State *s = opaque;
326     uint32_t r = 0;
327
328     addr >>= 2;
329     switch (addr) {
330     case R_CTL:
331     case R_HMESHLAST:
332     case R_VMESHLAST:
333     case R_BRIGHTNESS:
334     case R_CHROMAKEY:
335     case R_VERTICESADDR:
336     case R_TEXFBUF:
337     case R_TEXHRES:
338     case R_TEXVRES:
339     case R_TEXHMASK:
340     case R_TEXVMASK:
341     case R_DSTFBUF:
342     case R_DSTHRES:
343     case R_DSTVRES:
344     case R_DSTHOFFSET:
345     case R_DSTVOFFSET:
346     case R_DSTSQUAREW:
347     case R_DSTSQUAREH:
348     case R_ALPHA:
349         r = s->regs[addr];
350         break;
351
352     default:
353         error_report("milkymist_tmu2: read access to unknown register 0x"
354                 TARGET_FMT_plx, addr << 2);
355         break;
356     }
357
358     trace_milkymist_tmu2_memory_read(addr << 2, r);
359
360     return r;
361 }
362
363 static void tmu2_check_registers(MilkymistTMU2State *s)
364 {
365     if (s->regs[R_BRIGHTNESS] > MAX_BRIGHTNESS) {
366         error_report("milkymist_tmu2: max brightness is %d", MAX_BRIGHTNESS);
367     }
368
369     if (s->regs[R_ALPHA] > MAX_ALPHA) {
370         error_report("milkymist_tmu2: max alpha is %d", MAX_ALPHA);
371     }
372
373     if (s->regs[R_VERTICESADDR] & 0x07) {
374         error_report("milkymist_tmu2: vertex mesh address has to be 64-bit "
375                 "aligned");
376     }
377
378     if (s->regs[R_TEXFBUF] & 0x01) {
379         error_report("milkymist_tmu2: texture buffer address has to be "
380                 "16-bit aligned");
381     }
382 }
383
384 static void tmu2_write(void *opaque, hwaddr addr, uint64_t value,
385                        unsigned size)
386 {
387     MilkymistTMU2State *s = opaque;
388
389     trace_milkymist_tmu2_memory_write(addr, value);
390
391     addr >>= 2;
392     switch (addr) {
393     case R_CTL:
394         s->regs[addr] = value;
395         if (value & CTL_START_BUSY) {
396             tmu2_start(s);
397         }
398         break;
399     case R_BRIGHTNESS:
400     case R_HMESHLAST:
401     case R_VMESHLAST:
402     case R_CHROMAKEY:
403     case R_VERTICESADDR:
404     case R_TEXFBUF:
405     case R_TEXHRES:
406     case R_TEXVRES:
407     case R_TEXHMASK:
408     case R_TEXVMASK:
409     case R_DSTFBUF:
410     case R_DSTHRES:
411     case R_DSTVRES:
412     case R_DSTHOFFSET:
413     case R_DSTVOFFSET:
414     case R_DSTSQUAREW:
415     case R_DSTSQUAREH:
416     case R_ALPHA:
417         s->regs[addr] = value;
418         break;
419
420     default:
421         error_report("milkymist_tmu2: write access to unknown register 0x"
422                 TARGET_FMT_plx, addr << 2);
423         break;
424     }
425
426     tmu2_check_registers(s);
427 }
428
429 static const MemoryRegionOps tmu2_mmio_ops = {
430     .read = tmu2_read,
431     .write = tmu2_write,
432     .valid = {
433         .min_access_size = 4,
434         .max_access_size = 4,
435     },
436     .endianness = DEVICE_NATIVE_ENDIAN,
437 };
438
439 static void milkymist_tmu2_reset(DeviceState *d)
440 {
441     MilkymistTMU2State *s = MILKYMIST_TMU2(d);
442     int i;
443
444     for (i = 0; i < R_MAX; i++) {
445         s->regs[i] = 0;
446     }
447 }
448
449 static void milkymist_tmu2_init(Object *obj)
450 {
451     MilkymistTMU2State *s = MILKYMIST_TMU2(obj);
452     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
453
454     sysbus_init_irq(dev, &s->irq);
455
456     memory_region_init_io(&s->regs_region, obj, &tmu2_mmio_ops, s,
457             "milkymist-tmu2", R_MAX * 4);
458     sysbus_init_mmio(dev, &s->regs_region);
459 }
460
461 static void milkymist_tmu2_realize(DeviceState *dev, Error **errp)
462 {
463     MilkymistTMU2State *s = MILKYMIST_TMU2(dev);
464
465     if (tmu2_glx_init(s)) {
466         error_setg(errp, "tmu2_glx_init failed");
467     }
468 }
469
470 static const VMStateDescription vmstate_milkymist_tmu2 = {
471     .name = "milkymist-tmu2",
472     .version_id = 1,
473     .minimum_version_id = 1,
474     .fields = (VMStateField[]) {
475         VMSTATE_UINT32_ARRAY(regs, MilkymistTMU2State, R_MAX),
476         VMSTATE_END_OF_LIST()
477     }
478 };
479
480 static void milkymist_tmu2_class_init(ObjectClass *klass, void *data)
481 {
482     DeviceClass *dc = DEVICE_CLASS(klass);
483
484     dc->realize = milkymist_tmu2_realize;
485     dc->reset = milkymist_tmu2_reset;
486     dc->vmsd = &vmstate_milkymist_tmu2;
487 }
488
489 static const TypeInfo milkymist_tmu2_info = {
490     .name          = TYPE_MILKYMIST_TMU2,
491     .parent        = TYPE_SYS_BUS_DEVICE,
492     .instance_size = sizeof(MilkymistTMU2State),
493     .instance_init = milkymist_tmu2_init,
494     .class_init    = milkymist_tmu2_class_init,
495 };
496
497 static void milkymist_tmu2_register_types(void)
498 {
499     type_register_static(&milkymist_tmu2_info);
500 }
501
502 type_init(milkymist_tmu2_register_types)
503
504 DeviceState *milkymist_tmu2_create(hwaddr base, qemu_irq irq)
505 {
506     DeviceState *dev;
507     Display *d;
508     GLXFBConfig *configs;
509     int nelements;
510     int ver_major, ver_minor;
511
512     /* check that GLX will work */
513     d = XOpenDisplay(NULL);
514     if (d == NULL) {
515         return NULL;
516     }
517
518     if (!glXQueryVersion(d, &ver_major, &ver_minor)) {
519         /*
520          * Yeah, sometimes getting the GLX version can fail.
521          * Isn't X beautiful?
522          */
523         XCloseDisplay(d);
524         return NULL;
525     }
526
527     if ((ver_major < 1) || ((ver_major == 1) && (ver_minor < 3))) {
528         printf("Your GLX version is %d.%d,"
529           "but TMU emulation needs at least 1.3. TMU disabled.\n",
530           ver_major, ver_minor);
531         XCloseDisplay(d);
532         return NULL;
533     }
534
535     configs = glXChooseFBConfig(d, 0, glx_fbconfig_attr, &nelements);
536     if (configs == NULL) {
537         XCloseDisplay(d);
538         return NULL;
539     }
540
541     XFree(configs);
542     XCloseDisplay(d);
543
544     dev = qdev_create(NULL, TYPE_MILKYMIST_TMU2);
545     qdev_init_nofail(dev);
546     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
547     sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
548
549     return dev;
550 }
This page took 0.050564 seconds and 4 git commands to generate.