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

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;
1.1.1.2   root       76: } QEMU_PACKED;
1.1       root       77: 
                     78: struct MilkymistTMU2State {
                     79:     SysBusDevice busdev;
1.1.1.2   root       80:     MemoryRegion regs_region;
1.1       root       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: 
1.1.1.2   root      313: static uint64_t tmu2_read(void *opaque, target_phys_addr_t addr,
                    314:                           unsigned size)
1.1       root      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: 
1.1.1.2   root      375: static void tmu2_write(void *opaque, target_phys_addr_t addr, uint64_t value,
                    376:                        unsigned size)
1.1       root      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: 
1.1.1.2   root      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,
1.1       root      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: 
1.1.1.2   root      450:     memory_region_init_io(&s->regs_region, &tmu2_mmio_ops, s,
                    451:             "milkymist-tmu2", R_MAX * 4);
1.1.1.3 ! root      452:     sysbus_init_mmio(dev, &s->regs_region);
1.1       root      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: 
1.1.1.3 ! root      468: static void milkymist_tmu2_class_init(ObjectClass *klass, void *data)
        !           469: {
        !           470:     DeviceClass *dc = DEVICE_CLASS(klass);
        !           471:     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
        !           472: 
        !           473:     k->init = milkymist_tmu2_init;
        !           474:     dc->reset = milkymist_tmu2_reset;
        !           475:     dc->vmsd = &vmstate_milkymist_tmu2;
        !           476: }
        !           477: 
        !           478: static TypeInfo milkymist_tmu2_info = {
        !           479:     .name          = "milkymist-tmu2",
        !           480:     .parent        = TYPE_SYS_BUS_DEVICE,
        !           481:     .instance_size = sizeof(MilkymistTMU2State),
        !           482:     .class_init    = milkymist_tmu2_class_init,
1.1       root      483: };
                    484: 
1.1.1.3 ! root      485: static void milkymist_tmu2_register_types(void)
1.1       root      486: {
1.1.1.3 ! root      487:     type_register_static(&milkymist_tmu2_info);
1.1       root      488: }
                    489: 
1.1.1.3 ! root      490: type_init(milkymist_tmu2_register_types)

unix.superglobalmegacorp.com

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