|
|
1.1 ! root 1: /* ! 2: * OMAP LCD controller. ! 3: * ! 4: * Copyright (C) 2006-2007 Andrzej Zaborowski <[email protected]> ! 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.