Annotation of qemu/hw/milkymist-tmu2.c, revision 1.1.1.1

1.1       root        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", MAX_BRIGHTNESS);
                    356:     }
                    357: 
                    358:     if (s->regs[R_ALPHA] > MAX_ALPHA) {
                    359:         error_report("milkymist_tmu2: max alpha is %d", 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");
                    365:     }
                    366: 
                    367:     if (s->regs[R_TEXFBUF] & 0x01) {
                    368:         error_report("milkymist_tmu2: texture buffer address has to be "
                    369:                 "16-bit aligned");
                    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)

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.