]> rtime.felk.cvut.cz Git - lisovros/qemu_apohw.git/blob - hw/milkymist-tmu2.c
20110e5a8392bc3257d2a15432cfd15fbd3345e6
[lisovros/qemu_apohw.git] / hw / milkymist-tmu2.c
1 /*
2  *  QEMU model of the Milkymist texture mapping unit.
3  *
4  *  Copyright (c) 2010 Michael Walle <michael@walle.cc>
5  *  Copyright (c) 2010 Sebastien Bourdeauducq
6  *                       <sebastien.bourdeauducq@lekernel.net>
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://www.milkymist.org/socdoc/tmu2.pdf
24  *
25  */
26
27 #include "hw.h"
28 #include "sysbus.h"
29 #include "trace.h"
30 #include "qemu-error.h"
31
32 #include <X11/Xlib.h>
33 #include <GL/gl.h>
34 #include <GL/glx.h>
35
36 enum {
37     R_CTL = 0,
38     R_HMESHLAST,
39     R_VMESHLAST,
40     R_BRIGHTNESS,
41     R_CHROMAKEY,
42     R_VERTICESADDR,
43     R_TEXFBUF,
44     R_TEXHRES,
45     R_TEXVRES,
46     R_TEXHMASK,
47     R_TEXVMASK,
48     R_DSTFBUF,
49     R_DSTHRES,
50     R_DSTVRES,
51     R_DSTHOFFSET,
52     R_DSTVOFFSET,
53     R_DSTSQUAREW,
54     R_DSTSQUAREH,
55     R_ALPHA,
56     R_MAX
57 };
58
59 enum {
60     CTL_START_BUSY  = (1<<0),
61     CTL_CHROMAKEY   = (1<<1),
62 };
63
64 enum {
65     MAX_BRIGHTNESS = 63,
66     MAX_ALPHA      = 63,
67 };
68
69 enum {
70     MESH_MAXSIZE = 128,
71 };
72
73 struct vertex {
74     int x;
75     int y;
76 } QEMU_PACKED;
77
78 struct MilkymistTMU2State {
79     SysBusDevice busdev;
80     MemoryRegion regs_region;
81     CharDriverState *chr;
82     qemu_irq irq;
83
84     uint32_t regs[R_MAX];
85
86     Display *dpy;
87     GLXFBConfig glx_fb_config;
88     GLXContext glx_context;
89 };
90 typedef struct MilkymistTMU2State MilkymistTMU2State;
91
92 static const int glx_fbconfig_attr[] = {
93     GLX_GREEN_SIZE, 5,
94     GLX_GREEN_SIZE, 6,
95     GLX_BLUE_SIZE, 5,
96     None
97 };
98
99 static int tmu2_glx_init(MilkymistTMU2State *s)
100 {
101     GLXFBConfig *configs;
102     int nelements;
103
104     s->dpy = XOpenDisplay(NULL); /* FIXME: call XCloseDisplay() */
105     if (s->dpy == NULL) {
106         return 1;
107     }
108
109     configs = glXChooseFBConfig(s->dpy, 0, glx_fbconfig_attr, &nelements);
110     if (configs == NULL) {
111         return 1;
112     }
113
114     s->glx_fb_config = *configs;
115     XFree(configs);
116
117     /* FIXME: call glXDestroyContext() */
118     s->glx_context = glXCreateNewContext(s->dpy, s->glx_fb_config,
119             GLX_RGBA_TYPE, NULL, 1);
120     if (s->glx_context == NULL) {
121         return 1;
122     }
123
124     return 0;
125 }
126
127 static void tmu2_gl_map(struct vertex *mesh, int texhres, int texvres,
128         int hmeshlast, int vmeshlast, int ho, int vo, int sw, int sh)
129 {
130     int x, y;
131     int x0, y0, x1, y1;
132     int u0, v0, u1, v1, u2, v2, u3, v3;
133     double xscale = 1.0 / ((double)(64 * texhres));
134     double yscale = 1.0 / ((double)(64 * texvres));
135
136     glLoadIdentity();
137     glTranslatef(ho, vo, 0);
138     glEnable(GL_TEXTURE_2D);
139     glBegin(GL_QUADS);
140
141     for (y = 0; y < vmeshlast; y++) {
142         y0 = y * sh;
143         y1 = y0 + sh;
144         for (x = 0; x < hmeshlast; x++) {
145             x0 = x * sw;
146             x1 = x0 + sw;
147
148             u0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].x);
149             v0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].y);
150             u1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].x);
151             v1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].y);
152             u2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].x);
153             v2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].y);
154             u3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].x);
155             v3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].y);
156
157             glTexCoord2d(((double)u0) * xscale, ((double)v0) * yscale);
158             glVertex3i(x0, y0, 0);
159             glTexCoord2d(((double)u1) * xscale, ((double)v1) * yscale);
160             glVertex3i(x1, y0, 0);
161             glTexCoord2d(((double)u2) * xscale, ((double)v2) * yscale);
162             glVertex3i(x1, y1, 0);
163             glTexCoord2d(((double)u3) * xscale, ((double)v3) * yscale);
164             glVertex3i(x0, y1, 0);
165         }
166     }
167
168     glEnd();
169 }
170
171 static void tmu2_start(MilkymistTMU2State *s)
172 {
173     int pbuffer_attrib[6] = {
174         GLX_PBUFFER_WIDTH,
175         0,
176         GLX_PBUFFER_HEIGHT,
177         0,
178         GLX_PRESERVED_CONTENTS,
179         True
180     };
181
182     GLXPbuffer pbuffer;
183     GLuint texture;
184     void *fb;
185     target_phys_addr_t fb_len;
186     void *mesh;
187     target_phys_addr_t mesh_len;
188     float m;
189
190     trace_milkymist_tmu2_start();
191
192     /* Create and set up a suitable OpenGL context */
193     pbuffer_attrib[1] = s->regs[R_DSTHRES];
194     pbuffer_attrib[3] = s->regs[R_DSTVRES];
195     pbuffer = glXCreatePbuffer(s->dpy, s->glx_fb_config, pbuffer_attrib);
196     glXMakeContextCurrent(s->dpy, pbuffer, pbuffer, s->glx_context);
197
198     /* Fixup endianness. TODO: would it work on BE hosts? */
199     glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
200     glPixelStorei(GL_PACK_SWAP_BYTES, 1);
201
202     /* Row alignment */
203     glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
204     glPixelStorei(GL_PACK_ALIGNMENT, 2);
205
206     /* Read the QEMU source framebuffer into an OpenGL texture */
207     glGenTextures(1, &texture);
208     glBindTexture(GL_TEXTURE_2D, texture);
209     fb_len = 2*s->regs[R_TEXHRES]*s->regs[R_TEXVRES];
210     fb = cpu_physical_memory_map(s->regs[R_TEXFBUF], &fb_len, 0);
211     if (fb == NULL) {
212         glDeleteTextures(1, &texture);
213         glXMakeContextCurrent(s->dpy, None, None, NULL);
214         glXDestroyPbuffer(s->dpy, pbuffer);
215         return;
216     }
217     glTexImage2D(GL_TEXTURE_2D, 0, 3, s->regs[R_TEXHRES], s->regs[R_TEXVRES],
218             0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, fb);
219     cpu_physical_memory_unmap(fb, fb_len, 0, fb_len);
220
221     /* Set up texturing options */
222     /* WARNING:
223      * Many cases of TMU2 masking are not supported by OpenGL.
224      * We only implement the most common ones:
225      *  - full bilinear filtering vs. nearest texel
226      *  - texture clamping vs. texture wrapping
227      */
228     if ((s->regs[R_TEXHMASK] & 0x3f) > 0x20) {
229         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
230         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
231     } else {
232         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
233         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
234     }
235     if ((s->regs[R_TEXHMASK] >> 6) & s->regs[R_TEXHRES]) {
236         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
237     } else {
238         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
239     }
240     if ((s->regs[R_TEXVMASK] >> 6) & s->regs[R_TEXVRES]) {
241         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
242     } else {
243         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
244     }
245
246     /* Translucency and decay */
247     glEnable(GL_BLEND);
248     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
249     m = (float)(s->regs[R_BRIGHTNESS] + 1) / 64.0f;
250     glColor4f(m, m, m, (float)(s->regs[R_ALPHA] + 1) / 64.0f);
251
252     /* Read the QEMU dest. framebuffer into the OpenGL framebuffer */
253     fb_len = 2 * s->regs[R_DSTHRES] * s->regs[R_DSTVRES];
254     fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 0);
255     if (fb == NULL) {
256         glDeleteTextures(1, &texture);
257         glXMakeContextCurrent(s->dpy, None, None, NULL);
258         glXDestroyPbuffer(s->dpy, pbuffer);
259         return;
260     }
261
262     glDrawPixels(s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB,
263             GL_UNSIGNED_SHORT_5_6_5, fb);
264     cpu_physical_memory_unmap(fb, fb_len, 0, fb_len);
265     glViewport(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES]);
266     glMatrixMode(GL_PROJECTION);
267     glLoadIdentity();
268     glOrtho(0.0, s->regs[R_DSTHRES], 0.0, s->regs[R_DSTVRES], -1.0, 1.0);
269     glMatrixMode(GL_MODELVIEW);
270
271     /* Map the texture */
272     mesh_len = MESH_MAXSIZE*MESH_MAXSIZE*sizeof(struct vertex);
273     mesh = cpu_physical_memory_map(s->regs[R_VERTICESADDR], &mesh_len, 0);
274     if (mesh == NULL) {
275         glDeleteTextures(1, &texture);
276         glXMakeContextCurrent(s->dpy, None, None, NULL);
277         glXDestroyPbuffer(s->dpy, pbuffer);
278         return;
279     }
280
281     tmu2_gl_map((struct vertex *)mesh,
282         s->regs[R_TEXHRES], s->regs[R_TEXVRES],
283         s->regs[R_HMESHLAST], s->regs[R_VMESHLAST],
284         s->regs[R_DSTHOFFSET], s->regs[R_DSTVOFFSET],
285         s->regs[R_DSTSQUAREW], s->regs[R_DSTSQUAREH]);
286     cpu_physical_memory_unmap(mesh, mesh_len, 0, mesh_len);
287
288     /* Write back the OpenGL framebuffer to the QEMU framebuffer */
289     fb_len = 2 * s->regs[R_DSTHRES] * s->regs[R_DSTVRES];
290     fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 1);
291     if (fb == NULL) {
292         glDeleteTextures(1, &texture);
293         glXMakeContextCurrent(s->dpy, None, None, NULL);
294         glXDestroyPbuffer(s->dpy, pbuffer);
295         return;
296     }
297
298     glReadPixels(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB,
299             GL_UNSIGNED_SHORT_5_6_5, fb);
300     cpu_physical_memory_unmap(fb, fb_len, 1, fb_len);
301
302     /* Free OpenGL allocs */
303     glDeleteTextures(1, &texture);
304     glXMakeContextCurrent(s->dpy, None, None, NULL);
305     glXDestroyPbuffer(s->dpy, pbuffer);
306
307     s->regs[R_CTL] &= ~CTL_START_BUSY;
308
309     trace_milkymist_tmu2_pulse_irq();
310     qemu_irq_pulse(s->irq);
311 }
312
313 static uint64_t tmu2_read(void *opaque, target_phys_addr_t addr,
314                           unsigned size)
315 {
316     MilkymistTMU2State *s = opaque;
317     uint32_t r = 0;
318
319     addr >>= 2;
320     switch (addr) {
321     case R_CTL:
322     case R_HMESHLAST:
323     case R_VMESHLAST:
324     case R_BRIGHTNESS:
325     case R_CHROMAKEY:
326     case R_VERTICESADDR:
327     case R_TEXFBUF:
328     case R_TEXHRES:
329     case R_TEXVRES:
330     case R_TEXHMASK:
331     case R_TEXVMASK:
332     case R_DSTFBUF:
333     case R_DSTHRES:
334     case R_DSTVRES:
335     case R_DSTHOFFSET:
336     case R_DSTVOFFSET:
337     case R_DSTSQUAREW:
338     case R_DSTSQUAREH:
339     case R_ALPHA:
340         r = s->regs[addr];
341         break;
342
343     default:
344         error_report("milkymist_tmu2: read access to unknown register 0x"
345                 TARGET_FMT_plx, addr << 2);
346         break;
347     }
348
349     trace_milkymist_tmu2_memory_read(addr << 2, r);
350
351     return r;
352 }
353
354 static void tmu2_check_registers(MilkymistTMU2State *s)
355 {
356     if (s->regs[R_BRIGHTNESS] > MAX_BRIGHTNESS) {
357         error_report("milkymist_tmu2: max brightness is %d", MAX_BRIGHTNESS);
358     }
359
360     if (s->regs[R_ALPHA] > MAX_ALPHA) {
361         error_report("milkymist_tmu2: max alpha is %d", MAX_ALPHA);
362     }
363
364     if (s->regs[R_VERTICESADDR] & 0x07) {
365         error_report("milkymist_tmu2: vertex mesh address has to be 64-bit "
366                 "aligned");
367     }
368
369     if (s->regs[R_TEXFBUF] & 0x01) {
370         error_report("milkymist_tmu2: texture buffer address has to be "
371                 "16-bit aligned");
372     }
373 }
374
375 static void tmu2_write(void *opaque, target_phys_addr_t addr, uint64_t value,
376                        unsigned size)
377 {
378     MilkymistTMU2State *s = opaque;
379
380     trace_milkymist_tmu2_memory_write(addr, value);
381
382     addr >>= 2;
383     switch (addr) {
384     case R_CTL:
385         s->regs[addr] = value;
386         if (value & CTL_START_BUSY) {
387             tmu2_start(s);
388         }
389         break;
390     case R_BRIGHTNESS:
391     case R_HMESHLAST:
392     case R_VMESHLAST:
393     case R_CHROMAKEY:
394     case R_VERTICESADDR:
395     case R_TEXFBUF:
396     case R_TEXHRES:
397     case R_TEXVRES:
398     case R_TEXHMASK:
399     case R_TEXVMASK:
400     case R_DSTFBUF:
401     case R_DSTHRES:
402     case R_DSTVRES:
403     case R_DSTHOFFSET:
404     case R_DSTVOFFSET:
405     case R_DSTSQUAREW:
406     case R_DSTSQUAREH:
407     case R_ALPHA:
408         s->regs[addr] = value;
409         break;
410
411     default:
412         error_report("milkymist_tmu2: write access to unknown register 0x"
413                 TARGET_FMT_plx, addr << 2);
414         break;
415     }
416
417     tmu2_check_registers(s);
418 }
419
420 static const MemoryRegionOps tmu2_mmio_ops = {
421     .read = tmu2_read,
422     .write = tmu2_write,
423     .valid = {
424         .min_access_size = 4,
425         .max_access_size = 4,
426     },
427     .endianness = DEVICE_NATIVE_ENDIAN,
428 };
429
430 static void milkymist_tmu2_reset(DeviceState *d)
431 {
432     MilkymistTMU2State *s = container_of(d, MilkymistTMU2State, busdev.qdev);
433     int i;
434
435     for (i = 0; i < R_MAX; i++) {
436         s->regs[i] = 0;
437     }
438 }
439
440 static int milkymist_tmu2_init(SysBusDevice *dev)
441 {
442     MilkymistTMU2State *s = FROM_SYSBUS(typeof(*s), dev);
443
444     if (tmu2_glx_init(s)) {
445         return 1;
446     }
447
448     sysbus_init_irq(dev, &s->irq);
449
450     memory_region_init_io(&s->regs_region, &tmu2_mmio_ops, s,
451             "milkymist-tmu2", R_MAX * 4);
452     sysbus_init_mmio(dev, &s->regs_region);
453
454     return 0;
455 }
456
457 static const VMStateDescription vmstate_milkymist_tmu2 = {
458     .name = "milkymist-tmu2",
459     .version_id = 1,
460     .minimum_version_id = 1,
461     .minimum_version_id_old = 1,
462     .fields      = (VMStateField[]) {
463         VMSTATE_UINT32_ARRAY(regs, MilkymistTMU2State, R_MAX),
464         VMSTATE_END_OF_LIST()
465     }
466 };
467
468 static SysBusDeviceInfo milkymist_tmu2_info = {
469     .init = milkymist_tmu2_init,
470     .qdev.name  = "milkymist-tmu2",
471     .qdev.size  = sizeof(MilkymistTMU2State),
472     .qdev.vmsd  = &vmstate_milkymist_tmu2,
473     .qdev.reset = milkymist_tmu2_reset,
474 };
475
476 static void milkymist_tmu2_register(void)
477 {
478     sysbus_register_withprop(&milkymist_tmu2_info);
479 }
480
481 device_init(milkymist_tmu2_register)