Annotation of qemu/hw/milkymist-tmu2.c, revision 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.