Annotation of qemu/hw/omap_lcdc.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * OMAP LCD controller.
                      3:  *
                      4:  * Copyright (C) 2006-2007 Andrzej Zaborowski  <balrog@zabor.org>
                      5:  *
                      6:  * This program is free software; you can redistribute it and/or
                      7:  * modify it under the terms of the GNU General Public License as
                      8:  * published by the Free Software Foundation; either version 2 of
                      9:  * the License, or (at your option) any later version.
                     10:  *
                     11:  * This program is distributed in the hope that it will be useful,
                     12:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
                     13:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     14:  * GNU General Public License for more details.
                     15:  *
                     16:  * You should have received a copy of the GNU General Public License
                     17:  * along with this program; if not, write to the Free Software
                     18:  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
                     19:  * MA 02111-1307 USA
                     20:  */
                     21: #include "hw.h"
                     22: #include "console.h"
                     23: #include "omap.h"
                     24: 
                     25: struct omap_lcd_panel_s {
                     26:     target_phys_addr_t base;
                     27:     qemu_irq irq;
                     28:     DisplayState *state;
                     29:     ram_addr_t imif_base;
                     30:     ram_addr_t emiff_base;
                     31: 
                     32:     int plm;
                     33:     int tft;
                     34:     int mono;
                     35:     int enable;
                     36:     int width;
                     37:     int height;
                     38:     int interrupts;
                     39:     uint32_t timing[3];
                     40:     uint32_t subpanel;
                     41:     uint32_t ctrl;
                     42: 
                     43:     struct omap_dma_lcd_channel_s *dma;
                     44:     uint16_t palette[256];
                     45:     int palette_done;
                     46:     int frame_done;
                     47:     int invalidate;
                     48:     int sync_error;
                     49: };
                     50: 
                     51: static void omap_lcd_interrupts(struct omap_lcd_panel_s *s)
                     52: {
                     53:     if (s->frame_done && (s->interrupts & 1)) {
                     54:         qemu_irq_raise(s->irq);
                     55:         return;
                     56:     }
                     57: 
                     58:     if (s->palette_done && (s->interrupts & 2)) {
                     59:         qemu_irq_raise(s->irq);
                     60:         return;
                     61:     }
                     62: 
                     63:     if (s->sync_error) {
                     64:         qemu_irq_raise(s->irq);
                     65:         return;
                     66:     }
                     67: 
                     68:     qemu_irq_lower(s->irq);
                     69: }
                     70: 
                     71: #include "pixel_ops.h"
                     72: 
                     73: typedef void draw_line_func(
                     74:                 uint8_t *d, const uint8_t *s, int width, const uint16_t *pal);
                     75: 
                     76: #define DEPTH 8
                     77: #include "omap_lcd_template.h"
                     78: #define DEPTH 15
                     79: #include "omap_lcd_template.h"
                     80: #define DEPTH 16
                     81: #include "omap_lcd_template.h"
                     82: #define DEPTH 32
                     83: #include "omap_lcd_template.h"
                     84: 
                     85: static draw_line_func *draw_line_table2[33] = {
                     86:     [0 ... 32] = 0,
                     87:     [8]                = draw_line2_8,
                     88:     [15]       = draw_line2_15,
                     89:     [16]       = draw_line2_16,
                     90:     [32]       = draw_line2_32,
                     91: }, *draw_line_table4[33] = {
                     92:     [0 ... 32] = 0,
                     93:     [8]                = draw_line4_8,
                     94:     [15]       = draw_line4_15,
                     95:     [16]       = draw_line4_16,
                     96:     [32]       = draw_line4_32,
                     97: }, *draw_line_table8[33] = {
                     98:     [0 ... 32] = 0,
                     99:     [8]                = draw_line8_8,
                    100:     [15]       = draw_line8_15,
                    101:     [16]       = draw_line8_16,
                    102:     [32]       = draw_line8_32,
                    103: }, *draw_line_table12[33] = {
                    104:     [0 ... 32] = 0,
                    105:     [8]                = draw_line12_8,
                    106:     [15]       = draw_line12_15,
                    107:     [16]       = draw_line12_16,
                    108:     [32]       = draw_line12_32,
                    109: }, *draw_line_table16[33] = {
                    110:     [0 ... 32] = 0,
                    111:     [8]                = draw_line16_8,
                    112:     [15]       = draw_line16_15,
                    113:     [16]       = draw_line16_16,
                    114:     [32]       = draw_line16_32,
                    115: };
                    116: 
                    117: static void omap_update_display(void *opaque)
                    118: {
                    119:     struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque;
                    120:     draw_line_func *draw_line;
                    121:     int size, dirty[2], minline, maxline, height;
                    122:     int line, width, linesize, step, bpp, frame_offset;
                    123:     ram_addr_t frame_base, scanline, newline, x;
                    124:     uint8_t *s, *d;
                    125: 
                    126:     if (!omap_lcd || omap_lcd->plm == 1 ||
                    127:                     !omap_lcd->enable || !omap_lcd->state->depth)
                    128:         return;
                    129: 
                    130:     frame_offset = 0;
                    131:     if (omap_lcd->plm != 2) {
                    132:         memcpy(omap_lcd->palette, phys_ram_base +
                    133:                         omap_lcd->dma->phys_framebuffer[
                    134:                         omap_lcd->dma->current_frame], 0x200);
                    135:         switch (omap_lcd->palette[0] >> 12 & 7) {
                    136:         case 3 ... 7:
                    137:             frame_offset += 0x200;
                    138:             break;
                    139:         default:
                    140:             frame_offset += 0x20;
                    141:         }
                    142:     }
                    143: 
                    144:     /* Colour depth */
                    145:     switch ((omap_lcd->palette[0] >> 12) & 7) {
                    146:     case 1:
                    147:         draw_line = draw_line_table2[omap_lcd->state->depth];
                    148:         bpp = 2;
                    149:         break;
                    150: 
                    151:     case 2:
                    152:         draw_line = draw_line_table4[omap_lcd->state->depth];
                    153:         bpp = 4;
                    154:         break;
                    155: 
                    156:     case 3:
                    157:         draw_line = draw_line_table8[omap_lcd->state->depth];
                    158:         bpp = 8;
                    159:         break;
                    160: 
                    161:     case 4 ... 7:
                    162:         if (!omap_lcd->tft)
                    163:             draw_line = draw_line_table12[omap_lcd->state->depth];
                    164:         else
                    165:             draw_line = draw_line_table16[omap_lcd->state->depth];
                    166:         bpp = 16;
                    167:         break;
                    168: 
                    169:     default:
                    170:         /* Unsupported at the moment.  */
                    171:         return;
                    172:     }
                    173: 
                    174:     /* Resolution */
                    175:     width = omap_lcd->width;
                    176:     if (width != omap_lcd->state->width ||
                    177:             omap_lcd->height != omap_lcd->state->height) {
                    178:         dpy_resize(omap_lcd->state,
                    179:                 omap_lcd->width, omap_lcd->height);
                    180:         omap_lcd->invalidate = 1;
                    181:     }
                    182: 
                    183:     if (omap_lcd->dma->current_frame == 0)
                    184:         size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
                    185:     else
                    186:         size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
                    187: 
                    188:     if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
                    189:         omap_lcd->sync_error = 1;
                    190:         omap_lcd_interrupts(omap_lcd);
                    191:         omap_lcd->enable = 0;
                    192:         return;
                    193:     }
                    194: 
                    195:     /* Content */
                    196:     frame_base = omap_lcd->dma->phys_framebuffer[
                    197:             omap_lcd->dma->current_frame] + frame_offset;
                    198:     omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
                    199:     if (omap_lcd->dma->interrupts & 1)
                    200:         qemu_irq_raise(omap_lcd->dma->irq);
                    201:     if (omap_lcd->dma->dual)
                    202:         omap_lcd->dma->current_frame ^= 1;
                    203: 
                    204:     if (!omap_lcd->state->depth)
                    205:         return;
                    206: 
                    207:     line = 0;
                    208:     height = omap_lcd->height;
                    209:     if (omap_lcd->subpanel & (1 << 31)) {
                    210:         if (omap_lcd->subpanel & (1 << 29))
                    211:             line = (omap_lcd->subpanel >> 16) & 0x3ff;
                    212:         else
                    213:             height = (omap_lcd->subpanel >> 16) & 0x3ff;
                    214:         /* TODO: fill the rest of the panel with DPD */
                    215:     }
                    216:     step = width * bpp >> 3;
                    217:     scanline = frame_base + step * line;
                    218:     s = (uint8_t *) (phys_ram_base + scanline);
                    219:     d = omap_lcd->state->data;
                    220:     linesize = omap_lcd->state->linesize;
                    221: 
                    222:     dirty[0] = dirty[1] =
                    223:             cpu_physical_memory_get_dirty(scanline, VGA_DIRTY_FLAG);
                    224:     minline = height;
                    225:     maxline = line;
                    226:     for (; line < height; line ++) {
                    227:         newline = scanline + step;
                    228:         for (x = scanline + TARGET_PAGE_SIZE; x < newline;
                    229:                         x += TARGET_PAGE_SIZE) {
                    230:             dirty[1] = cpu_physical_memory_get_dirty(x, VGA_DIRTY_FLAG);
                    231:             dirty[0] |= dirty[1];
                    232:         }
                    233:         if (dirty[0] || omap_lcd->invalidate) {
                    234:             draw_line(d, s, width, omap_lcd->palette);
                    235:             if (line < minline)
                    236:                 minline = line;
                    237:             maxline = line + 1;
                    238:         }
                    239:         scanline = newline;
                    240:         dirty[0] = dirty[1];
                    241:         s += step;
                    242:         d += linesize;
                    243:     }
                    244: 
                    245:     if (maxline >= minline) {
                    246:         dpy_update(omap_lcd->state, 0, minline, width, maxline);
                    247:         cpu_physical_memory_reset_dirty(frame_base + step * minline,
                    248:                         frame_base + step * maxline, VGA_DIRTY_FLAG);
                    249:     }
                    250: }
                    251: 
                    252: static int ppm_save(const char *filename, uint8_t *data,
                    253:                 int w, int h, int linesize)
                    254: {
                    255:     FILE *f;
                    256:     uint8_t *d, *d1;
                    257:     unsigned int v;
                    258:     int y, x, bpp;
                    259: 
                    260:     f = fopen(filename, "wb");
                    261:     if (!f)
                    262:         return -1;
                    263:     fprintf(f, "P6\n%d %d\n%d\n", w, h, 255);
                    264:     d1 = data;
                    265:     bpp = linesize / w;
                    266:     for (y = 0; y < h; y ++) {
                    267:         d = d1;
                    268:         for (x = 0; x < w; x ++) {
                    269:             v = *(uint32_t *) d;
                    270:             switch (bpp) {
                    271:             case 2:
                    272:                 fputc((v >> 8) & 0xf8, f);
                    273:                 fputc((v >> 3) & 0xfc, f);
                    274:                 fputc((v << 3) & 0xf8, f);
                    275:                 break;
                    276:             case 3:
                    277:             case 4:
                    278:             default:
                    279:                 fputc((v >> 16) & 0xff, f);
                    280:                 fputc((v >> 8) & 0xff, f);
                    281:                 fputc((v) & 0xff, f);
                    282:                 break;
                    283:             }
                    284:             d += bpp;
                    285:         }
                    286:         d1 += linesize;
                    287:     }
                    288:     fclose(f);
                    289:     return 0;
                    290: }
                    291: 
                    292: static void omap_screen_dump(void *opaque, const char *filename) {
                    293:     struct omap_lcd_panel_s *omap_lcd = opaque;
                    294:     omap_update_display(opaque);
                    295:     if (omap_lcd && omap_lcd->state->data)
                    296:         ppm_save(filename, omap_lcd->state->data,
                    297:                 omap_lcd->width, omap_lcd->height,
                    298:                 omap_lcd->state->linesize);
                    299: }
                    300: 
                    301: static void omap_invalidate_display(void *opaque) {
                    302:     struct omap_lcd_panel_s *omap_lcd = opaque;
                    303:     omap_lcd->invalidate = 1;
                    304: }
                    305: 
                    306: static void omap_lcd_update(struct omap_lcd_panel_s *s) {
                    307:     if (!s->enable) {
                    308:         s->dma->current_frame = -1;
                    309:         s->sync_error = 0;
                    310:         if (s->plm != 1)
                    311:             s->frame_done = 1;
                    312:         omap_lcd_interrupts(s);
                    313:         return;
                    314:     }
                    315: 
                    316:     if (s->dma->current_frame == -1) {
                    317:         s->frame_done = 0;
                    318:         s->palette_done = 0;
                    319:         s->dma->current_frame = 0;
                    320:     }
                    321: 
                    322:     if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
                    323:                             s->dma->src_f1_top) ||
                    324:                     !s->dma->mpu->port[
                    325:                     s->dma->src].addr_valid(s->dma->mpu,
                    326:                             s->dma->src_f1_bottom) ||
                    327:                     (s->dma->dual &&
                    328:                      (!s->dma->mpu->port[
                    329:                       s->dma->src].addr_valid(s->dma->mpu,
                    330:                               s->dma->src_f2_top) ||
                    331:                       !s->dma->mpu->port[
                    332:                       s->dma->src].addr_valid(s->dma->mpu,
                    333:                               s->dma->src_f2_bottom)))) {
                    334:         s->dma->condition |= 1 << 2;
                    335:         if (s->dma->interrupts & (1 << 1))
                    336:             qemu_irq_raise(s->dma->irq);
                    337:         s->enable = 0;
                    338:         return;
                    339:     }
                    340: 
                    341:      if (s->dma->src == imif) {
                    342:         /* Framebuffers are in SRAM */
                    343:         s->dma->phys_framebuffer[0] = s->imif_base +
                    344:                 s->dma->src_f1_top - OMAP_IMIF_BASE;
                    345: 
                    346:         s->dma->phys_framebuffer[1] = s->imif_base +
                    347:                 s->dma->src_f2_top - OMAP_IMIF_BASE;
                    348:     } else {
                    349:         /* Framebuffers are in RAM */
                    350:         s->dma->phys_framebuffer[0] = s->emiff_base +
                    351:                 s->dma->src_f1_top - OMAP_EMIFF_BASE;
                    352: 
                    353:         s->dma->phys_framebuffer[1] = s->emiff_base +
                    354:                 s->dma->src_f2_top - OMAP_EMIFF_BASE;
                    355:     }
                    356: 
                    357:     if (s->plm != 2 && !s->palette_done) {
                    358:         memcpy(s->palette, phys_ram_base +
                    359:                 s->dma->phys_framebuffer[s->dma->current_frame], 0x200);
                    360:         s->palette_done = 1;
                    361:         omap_lcd_interrupts(s);
                    362:     }
                    363: }
                    364: 
                    365: static uint32_t omap_lcdc_read(void *opaque, target_phys_addr_t addr)
                    366: {
                    367:     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
                    368:     int offset = addr - s->base;
                    369: 
                    370:     switch (offset) {
                    371:     case 0x00: /* LCD_CONTROL */
                    372:         return (s->tft << 23) | (s->plm << 20) |
                    373:                 (s->tft << 7) | (s->interrupts << 3) |
                    374:                 (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
                    375: 
                    376:     case 0x04: /* LCD_TIMING0 */
                    377:         return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
                    378: 
                    379:     case 0x08: /* LCD_TIMING1 */
                    380:         return (s->timing[1] << 10) | (s->height - 1);
                    381: 
                    382:     case 0x0c: /* LCD_TIMING2 */
                    383:         return s->timing[2] | 0xfc000000;
                    384: 
                    385:     case 0x10: /* LCD_STATUS */
                    386:         return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
                    387: 
                    388:     case 0x14: /* LCD_SUBPANEL */
                    389:         return s->subpanel;
                    390: 
                    391:     default:
                    392:         break;
                    393:     }
                    394:     OMAP_BAD_REG(addr);
                    395:     return 0;
                    396: }
                    397: 
                    398: static void omap_lcdc_write(void *opaque, target_phys_addr_t addr,
                    399:                 uint32_t value)
                    400: {
                    401:     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
                    402:     int offset = addr - s->base;
                    403: 
                    404:     switch (offset) {
                    405:     case 0x00: /* LCD_CONTROL */
                    406:         s->plm = (value >> 20) & 3;
                    407:         s->tft = (value >> 7) & 1;
                    408:         s->interrupts = (value >> 3) & 3;
                    409:         s->mono = (value >> 1) & 1;
                    410:         s->ctrl = value & 0x01cff300;
                    411:         if (s->enable != (value & 1)) {
                    412:             s->enable = value & 1;
                    413:             omap_lcd_update(s);
                    414:         }
                    415:         break;
                    416: 
                    417:     case 0x04: /* LCD_TIMING0 */
                    418:         s->timing[0] = value >> 10;
                    419:         s->width = (value & 0x3ff) + 1;
                    420:         break;
                    421: 
                    422:     case 0x08: /* LCD_TIMING1 */
                    423:         s->timing[1] = value >> 10;
                    424:         s->height = (value & 0x3ff) + 1;
                    425:         break;
                    426: 
                    427:     case 0x0c: /* LCD_TIMING2 */
                    428:         s->timing[2] = value;
                    429:         break;
                    430: 
                    431:     case 0x10: /* LCD_STATUS */
                    432:         break;
                    433: 
                    434:     case 0x14: /* LCD_SUBPANEL */
                    435:         s->subpanel = value & 0xa1ffffff;
                    436:         break;
                    437: 
                    438:     default:
                    439:         OMAP_BAD_REG(addr);
                    440:     }
                    441: }
                    442: 
                    443: static CPUReadMemoryFunc *omap_lcdc_readfn[] = {
                    444:     omap_lcdc_read,
                    445:     omap_lcdc_read,
                    446:     omap_lcdc_read,
                    447: };
                    448: 
                    449: static CPUWriteMemoryFunc *omap_lcdc_writefn[] = {
                    450:     omap_lcdc_write,
                    451:     omap_lcdc_write,
                    452:     omap_lcdc_write,
                    453: };
                    454: 
                    455: void omap_lcdc_reset(struct omap_lcd_panel_s *s)
                    456: {
                    457:     s->dma->current_frame = -1;
                    458:     s->plm = 0;
                    459:     s->tft = 0;
                    460:     s->mono = 0;
                    461:     s->enable = 0;
                    462:     s->width = 0;
                    463:     s->height = 0;
                    464:     s->interrupts = 0;
                    465:     s->timing[0] = 0;
                    466:     s->timing[1] = 0;
                    467:     s->timing[2] = 0;
                    468:     s->subpanel = 0;
                    469:     s->palette_done = 0;
                    470:     s->frame_done = 0;
                    471:     s->sync_error = 0;
                    472:     s->invalidate = 1;
                    473:     s->subpanel = 0;
                    474:     s->ctrl = 0;
                    475: }
                    476: 
                    477: struct omap_lcd_panel_s *omap_lcdc_init(target_phys_addr_t base, qemu_irq irq,
                    478:                 struct omap_dma_lcd_channel_s *dma, DisplayState *ds,
                    479:                 ram_addr_t imif_base, ram_addr_t emiff_base, omap_clk clk)
                    480: {
                    481:     int iomemtype;
                    482:     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *)
                    483:             qemu_mallocz(sizeof(struct omap_lcd_panel_s));
                    484: 
                    485:     s->irq = irq;
                    486:     s->dma = dma;
                    487:     s->base = base;
                    488:     s->state = ds;
                    489:     s->imif_base = imif_base;
                    490:     s->emiff_base = emiff_base;
                    491:     omap_lcdc_reset(s);
                    492: 
                    493:     iomemtype = cpu_register_io_memory(0, omap_lcdc_readfn,
                    494:                     omap_lcdc_writefn, s);
                    495:     cpu_register_physical_memory(s->base, 0x100, iomemtype);
                    496: 
                    497:     graphic_console_init(ds, omap_update_display,
                    498:                     omap_invalidate_display, omap_screen_dump, s);
                    499: 
                    500:     return s;
                    501: }

unix.superglobalmegacorp.com