]> Git Repo - qemu.git/blob - hw/milkymist-tmu2.c
Merge remote branch 'origin/master' into pci
[qemu.git] / hw / 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://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 } __attribute__((packed));
77
78 struct MilkymistTMU2State {
79     SysBusDevice busdev;
80     CharDriverState *chr;
81     qemu_irq irq;
82
83     uint32_t regs[R_MAX];
84
85     Display *dpy;
86     GLXFBConfig glx_fb_config;
87     GLXContext glx_context;
88 };
89 typedef struct MilkymistTMU2State MilkymistTMU2State;
90
91 static const int glx_fbconfig_attr[] = {
92     GLX_GREEN_SIZE, 5,
93     GLX_GREEN_SIZE, 6,
94     GLX_BLUE_SIZE, 5,
95     None
96 };
97
98 static int tmu2_glx_init(MilkymistTMU2State *s)
99 {
100     GLXFBConfig *configs;
101     int nelements;
102
103     s->dpy = XOpenDisplay(NULL); /* FIXME: call XCloseDisplay() */
104     if (s->dpy == NULL) {
105         return 1;
106     }
107
108     configs = glXChooseFBConfig(s->dpy, 0, glx_fbconfig_attr, &nelements);
109     if (configs == NULL) {
110         return 1;
111     }
112
113     s->glx_fb_config = *configs;
114     XFree(configs);
115
116     /* FIXME: call glXDestroyContext() */
117     s->glx_context = glXCreateNewContext(s->dpy, s->glx_fb_config,
118             GLX_RGBA_TYPE, NULL, 1);
119     if (s->glx_context == NULL) {
120         return 1;
121     }
122
123     return 0;
124 }
125
126 static void tmu2_gl_map(struct vertex *mesh, int texhres, int texvres,
127         int hmeshlast, int vmeshlast, int ho, int vo, int sw, int sh)
128 {
129     int x, y;
130     int x0, y0, x1, y1;
131     int u0, v0, u1, v1, u2, v2, u3, v3;
132     double xscale = 1.0 / ((double)(64 * texhres));
133     double yscale = 1.0 / ((double)(64 * texvres));
134
135     glLoadIdentity();
136     glTranslatef(ho, vo, 0);
137     glEnable(GL_TEXTURE_2D);
138     glBegin(GL_QUADS);
139
140     for (y = 0; y < vmeshlast; y++) {
141         y0 = y * sh;
142         y1 = y0 + sh;
143         for (x = 0; x < hmeshlast; x++) {
144             x0 = x * sw;
145             x1 = x0 + sw;
146
147             u0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].x);
148             v0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].y);
149             u1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].x);
150             v1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].y);
151             u2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].x);
152             v2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].y);
153             u3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].x);
154             v3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].y);
155
156             glTexCoord2d(((double)u0) * xscale, ((double)v0) * yscale);
157             glVertex3i(x0, y0, 0);
158             glTexCoord2d(((double)u1) * xscale, ((double)v1) * yscale);
159             glVertex3i(x1, y0, 0);
160             glTexCoord2d(((double)u2) * xscale, ((double)v2) * yscale);
161             glVertex3i(x1, y1, 0);
162             glTexCoord2d(((double)u3) * xscale, ((double)v3) * yscale);
163             glVertex3i(x0, y1, 0);
164         }
165     }
166
167     glEnd();
168 }
169
170 static void tmu2_start(MilkymistTMU2State *s)
171 {
172     int pbuffer_attrib[6] = {
173         GLX_PBUFFER_WIDTH,
174         0,
175         GLX_PBUFFER_HEIGHT,
176         0,
177         GLX_PRESERVED_CONTENTS,
178         True
179     };
180
181     GLXPbuffer pbuffer;
182     GLuint texture;
183     void *fb;
184     target_phys_addr_t fb_len;
185     void *mesh;
186     target_phys_addr_t mesh_len;
187     float m;
188
189     trace_milkymist_tmu2_start();
190
191     /* Create and set up a suitable OpenGL context */
192     pbuffer_attrib[1] = s->regs[R_DSTHRES];
193     pbuffer_attrib[3] = s->regs[R_DSTVRES];
194     pbuffer = glXCreatePbuffer(s->dpy, s->glx_fb_config, pbuffer_attrib);
195     glXMakeContextCurrent(s->dpy, pbuffer, pbuffer, s->glx_context);
196
197     /* Fixup endianness. TODO: would it work on BE hosts? */
198     glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
199     glPixelStorei(GL_PACK_SWAP_BYTES, 1);
200
201     /* Row alignment */
202     glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
203     glPixelStorei(GL_PACK_ALIGNMENT, 2);
204
205     /* Read the QEMU source framebuffer into an OpenGL texture */
206     glGenTextures(1, &texture);
207     glBindTexture(GL_TEXTURE_2D, texture);
208     fb_len = 2*s->regs[R_TEXHRES]*s->regs[R_TEXVRES];
209     fb = cpu_physical_memory_map(s->regs[R_TEXFBUF], &fb_len, 0);
210     if (fb == NULL) {
211         glDeleteTextures(1, &texture);
212         glXMakeContextCurrent(s->dpy, None, None, NULL);
213         glXDestroyPbuffer(s->dpy, pbuffer);
214         return;
215     }
216     glTexImage2D(GL_TEXTURE_2D, 0, 3, s->regs[R_TEXHRES], s->regs[R_TEXVRES],
217             0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, fb);
218     cpu_physical_memory_unmap(fb, fb_len, 0, fb_len);
219
220     /* Set up texturing options */
221     /* WARNING:
222      * Many cases of TMU2 masking are not supported by OpenGL.
223      * We only implement the most common ones:
224      *  - full bilinear filtering vs. nearest texel
225      *  - texture clamping vs. texture wrapping
226      */
227     if ((s->regs[R_TEXHMASK] & 0x3f) > 0x20) {
228         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
229         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
230     } else {
231         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
232         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
233     }
234     if ((s->regs[R_TEXHMASK] >> 6) & s->regs[R_TEXHRES]) {
235         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
236     } else {
237         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
238     }
239     if ((s->regs[R_TEXVMASK] >> 6) & s->regs[R_TEXVRES]) {
240         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
241     } else {
242         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
243     }
244
245     /* Translucency and decay */
246     glEnable(GL_BLEND);
247     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
248     m = (float)(s->regs[R_BRIGHTNESS] + 1) / 64.0f;
249     glColor4f(m, m, m, (float)(s->regs[R_ALPHA] + 1) / 64.0f);
250
251     /* Read the QEMU dest. framebuffer into the OpenGL framebuffer */
252     fb_len = 2 * s->regs[R_DSTHRES] * s->regs[R_DSTVRES];
253     fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 0);
254     if (fb == NULL) {
255         glDeleteTextures(1, &texture);
256         glXMakeContextCurrent(s->dpy, None, None, NULL);
257         glXDestroyPbuffer(s->dpy, pbuffer);
258         return;
259     }
260
261     glDrawPixels(s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB,
262             GL_UNSIGNED_SHORT_5_6_5, fb);
263     cpu_physical_memory_unmap(fb, fb_len, 0, fb_len);
264     glViewport(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES]);
265     glMatrixMode(GL_PROJECTION);
266     glLoadIdentity();
267     glOrtho(0.0, s->regs[R_DSTHRES], 0.0, s->regs[R_DSTVRES], -1.0, 1.0);
268     glMatrixMode(GL_MODELVIEW);
269
270     /* Map the texture */
271     mesh_len = MESH_MAXSIZE*MESH_MAXSIZE*sizeof(struct vertex);
272     mesh = cpu_physical_memory_map(s->regs[R_VERTICESADDR], &mesh_len, 0);
273     if (mesh == NULL) {
274         glDeleteTextures(1, &texture);
275         glXMakeContextCurrent(s->dpy, None, None, NULL);
276         glXDestroyPbuffer(s->dpy, pbuffer);
277         return;
278     }
279
280     tmu2_gl_map((struct vertex *)mesh,
281         s->regs[R_TEXHRES], s->regs[R_TEXVRES],
282         s->regs[R_HMESHLAST], s->regs[R_VMESHLAST],
283         s->regs[R_DSTHOFFSET], s->regs[R_DSTVOFFSET],
284         s->regs[R_DSTSQUAREW], s->regs[R_DSTSQUAREH]);
285     cpu_physical_memory_unmap(mesh, mesh_len, 0, mesh_len);
286
287     /* Write back the OpenGL framebuffer to the QEMU framebuffer */
288     fb_len = 2 * s->regs[R_DSTHRES] * s->regs[R_DSTVRES];
289     fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 1);
290     if (fb == NULL) {
291         glDeleteTextures(1, &texture);
292         glXMakeContextCurrent(s->dpy, None, None, NULL);
293         glXDestroyPbuffer(s->dpy, pbuffer);
294         return;
295     }
296
297     glReadPixels(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB,
298             GL_UNSIGNED_SHORT_5_6_5, fb);
299     cpu_physical_memory_unmap(fb, fb_len, 1, fb_len);
300
301     /* Free OpenGL allocs */
302     glDeleteTextures(1, &texture);
303     glXMakeContextCurrent(s->dpy, None, None, NULL);
304     glXDestroyPbuffer(s->dpy, pbuffer);
305
306     s->regs[R_CTL] &= ~CTL_START_BUSY;
307
308     trace_milkymist_tmu2_pulse_irq();
309     qemu_irq_pulse(s->irq);
310 }
311
312 static uint32_t tmu2_read(void *opaque, target_phys_addr_t addr)
313 {
314     MilkymistTMU2State *s = opaque;
315     uint32_t r = 0;
316
317     addr >>= 2;
318     switch (addr) {
319     case R_CTL:
320     case R_HMESHLAST:
321     case R_VMESHLAST:
322     case R_BRIGHTNESS:
323     case R_CHROMAKEY:
324     case R_VERTICESADDR:
325     case R_TEXFBUF:
326     case R_TEXHRES:
327     case R_TEXVRES:
328     case R_TEXHMASK:
329     case R_TEXVMASK:
330     case R_DSTFBUF:
331     case R_DSTHRES:
332     case R_DSTVRES:
333     case R_DSTHOFFSET:
334     case R_DSTVOFFSET:
335     case R_DSTSQUAREW:
336     case R_DSTSQUAREH:
337     case R_ALPHA:
338         r = s->regs[addr];
339         break;
340
341     default:
342         error_report("milkymist_tmu2: read access to unknown register 0x"
343                 TARGET_FMT_plx, addr << 2);
344         break;
345     }
346
347     trace_milkymist_tmu2_memory_read(addr << 2, r);
348
349     return r;
350 }
351
352 static void tmu2_check_registers(MilkymistTMU2State *s)
353 {
354     if (s->regs[R_BRIGHTNESS] > MAX_BRIGHTNESS) {
355         error_report("milkymist_tmu2: max brightness is %d\n", MAX_BRIGHTNESS);
356     }
357
358     if (s->regs[R_ALPHA] > MAX_ALPHA) {
359         error_report("milkymist_tmu2: max alpha is %d\n", MAX_ALPHA);
360     }
361
362     if (s->regs[R_VERTICESADDR] & 0x07) {
363         error_report("milkymist_tmu2: vertex mesh address has to be 64-bit "
364                 "aligned\n");
365     }
366
367     if (s->regs[R_TEXFBUF] & 0x01) {
368         error_report("milkymist_tmu2: texture buffer address has to be "
369                 "16-bit aligned\n");
370     }
371 }
372
373 static void tmu2_write(void *opaque, target_phys_addr_t addr, uint32_t value)
374 {
375     MilkymistTMU2State *s = opaque;
376
377     trace_milkymist_tmu2_memory_write(addr, value);
378
379     addr >>= 2;
380     switch (addr) {
381     case R_CTL:
382         s->regs[addr] = value;
383         if (value & CTL_START_BUSY) {
384             tmu2_start(s);
385         }
386         break;
387     case R_BRIGHTNESS:
388     case R_HMESHLAST:
389     case R_VMESHLAST:
390     case R_CHROMAKEY:
391     case R_VERTICESADDR:
392     case R_TEXFBUF:
393     case R_TEXHRES:
394     case R_TEXVRES:
395     case R_TEXHMASK:
396     case R_TEXVMASK:
397     case R_DSTFBUF:
398     case R_DSTHRES:
399     case R_DSTVRES:
400     case R_DSTHOFFSET:
401     case R_DSTVOFFSET:
402     case R_DSTSQUAREW:
403     case R_DSTSQUAREH:
404     case R_ALPHA:
405         s->regs[addr] = value;
406         break;
407
408     default:
409         error_report("milkymist_tmu2: write access to unknown register 0x"
410                 TARGET_FMT_plx, addr << 2);
411         break;
412     }
413
414     tmu2_check_registers(s);
415 }
416
417 static CPUReadMemoryFunc * const tmu2_read_fn[] = {
418     NULL,
419     NULL,
420     &tmu2_read,
421 };
422
423 static CPUWriteMemoryFunc * const tmu2_write_fn[] = {
424     NULL,
425     NULL,
426     &tmu2_write,
427 };
428
429 static void milkymist_tmu2_reset(DeviceState *d)
430 {
431     MilkymistTMU2State *s = container_of(d, MilkymistTMU2State, busdev.qdev);
432     int i;
433
434     for (i = 0; i < R_MAX; i++) {
435         s->regs[i] = 0;
436     }
437 }
438
439 static int milkymist_tmu2_init(SysBusDevice *dev)
440 {
441     MilkymistTMU2State *s = FROM_SYSBUS(typeof(*s), dev);
442     int tmu2_regs;
443
444     if (tmu2_glx_init(s)) {
445         return 1;
446     }
447
448     sysbus_init_irq(dev, &s->irq);
449
450     tmu2_regs = cpu_register_io_memory(tmu2_read_fn, tmu2_write_fn, s,
451             DEVICE_NATIVE_ENDIAN);
452     sysbus_init_mmio(dev, R_MAX * 4, tmu2_regs);
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)
This page took 0.052081 seconds and 4 git commands to generate.