File:  [Qemu by Fabrice Bellard] / qemu / hw / omap_lcdc.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:39:29 2018 UTC (2 years, 11 months ago) by root
Branches: qemu, MAIN
CVS tags: qemu0130, qemu0125, qemu0124, qemu0123, qemu0122, qemu0121, qemu0120, HEAD
qemu 0.12.0

    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 along
   17:  * with this program; if not, see <http://www.gnu.org/licenses/>.
   18:  */
   19: #include "hw.h"
   20: #include "console.h"
   21: #include "omap.h"
   22: #include "framebuffer.h"
   23: 
   24: struct omap_lcd_panel_s {
   25:     qemu_irq irq;
   26:     DisplayState *state;
   27:     ram_addr_t imif_base;
   28:     ram_addr_t emiff_base;
   29: 
   30:     int plm;
   31:     int tft;
   32:     int mono;
   33:     int enable;
   34:     int width;
   35:     int height;
   36:     int interrupts;
   37:     uint32_t timing[3];
   38:     uint32_t subpanel;
   39:     uint32_t ctrl;
   40: 
   41:     struct omap_dma_lcd_channel_s *dma;
   42:     uint16_t palette[256];
   43:     int palette_done;
   44:     int frame_done;
   45:     int invalidate;
   46:     int sync_error;
   47: };
   48: 
   49: static void omap_lcd_interrupts(struct omap_lcd_panel_s *s)
   50: {
   51:     if (s->frame_done && (s->interrupts & 1)) {
   52:         qemu_irq_raise(s->irq);
   53:         return;
   54:     }
   55: 
   56:     if (s->palette_done && (s->interrupts & 2)) {
   57:         qemu_irq_raise(s->irq);
   58:         return;
   59:     }
   60: 
   61:     if (s->sync_error) {
   62:         qemu_irq_raise(s->irq);
   63:         return;
   64:     }
   65: 
   66:     qemu_irq_lower(s->irq);
   67: }
   68: 
   69: #include "pixel_ops.h"
   70: 
   71: #define draw_line_func drawfn
   72: 
   73: #define DEPTH 8
   74: #include "omap_lcd_template.h"
   75: #define DEPTH 15
   76: #include "omap_lcd_template.h"
   77: #define DEPTH 16
   78: #include "omap_lcd_template.h"
   79: #define DEPTH 32
   80: #include "omap_lcd_template.h"
   81: 
   82: static draw_line_func draw_line_table2[33] = {
   83:     [0 ... 32]	= NULL,
   84:     [8]		= draw_line2_8,
   85:     [15]	= draw_line2_15,
   86:     [16]	= draw_line2_16,
   87:     [32]	= draw_line2_32,
   88: }, draw_line_table4[33] = {
   89:     [0 ... 32]	= NULL,
   90:     [8]		= draw_line4_8,
   91:     [15]	= draw_line4_15,
   92:     [16]	= draw_line4_16,
   93:     [32]	= draw_line4_32,
   94: }, draw_line_table8[33] = {
   95:     [0 ... 32]	= NULL,
   96:     [8]		= draw_line8_8,
   97:     [15]	= draw_line8_15,
   98:     [16]	= draw_line8_16,
   99:     [32]	= draw_line8_32,
  100: }, draw_line_table12[33] = {
  101:     [0 ... 32]	= NULL,
  102:     [8]		= draw_line12_8,
  103:     [15]	= draw_line12_15,
  104:     [16]	= draw_line12_16,
  105:     [32]	= draw_line12_32,
  106: }, draw_line_table16[33] = {
  107:     [0 ... 32]	= NULL,
  108:     [8]		= draw_line16_8,
  109:     [15]	= draw_line16_15,
  110:     [16]	= draw_line16_16,
  111:     [32]	= draw_line16_32,
  112: };
  113: 
  114: static void omap_update_display(void *opaque)
  115: {
  116:     struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque;
  117:     draw_line_func draw_line;
  118:     int size, height, first, last;
  119:     int width, linesize, step, bpp, frame_offset;
  120:     target_phys_addr_t frame_base;
  121: 
  122:     if (!omap_lcd || omap_lcd->plm == 1 ||
  123:                     !omap_lcd->enable || !ds_get_bits_per_pixel(omap_lcd->state))
  124:         return;
  125: 
  126:     frame_offset = 0;
  127:     if (omap_lcd->plm != 2) {
  128:         cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[
  129:                                   omap_lcd->dma->current_frame],
  130:                                  (void *)omap_lcd->palette, 0x200);
  131:         switch (omap_lcd->palette[0] >> 12 & 7) {
  132:         case 3 ... 7:
  133:             frame_offset += 0x200;
  134:             break;
  135:         default:
  136:             frame_offset += 0x20;
  137:         }
  138:     }
  139: 
  140:     /* Colour depth */
  141:     switch ((omap_lcd->palette[0] >> 12) & 7) {
  142:     case 1:
  143:         draw_line = draw_line_table2[ds_get_bits_per_pixel(omap_lcd->state)];
  144:         bpp = 2;
  145:         break;
  146: 
  147:     case 2:
  148:         draw_line = draw_line_table4[ds_get_bits_per_pixel(omap_lcd->state)];
  149:         bpp = 4;
  150:         break;
  151: 
  152:     case 3:
  153:         draw_line = draw_line_table8[ds_get_bits_per_pixel(omap_lcd->state)];
  154:         bpp = 8;
  155:         break;
  156: 
  157:     case 4 ... 7:
  158:         if (!omap_lcd->tft)
  159:             draw_line = draw_line_table12[ds_get_bits_per_pixel(omap_lcd->state)];
  160:         else
  161:             draw_line = draw_line_table16[ds_get_bits_per_pixel(omap_lcd->state)];
  162:         bpp = 16;
  163:         break;
  164: 
  165:     default:
  166:         /* Unsupported at the moment.  */
  167:         return;
  168:     }
  169: 
  170:     /* Resolution */
  171:     width = omap_lcd->width;
  172:     if (width != ds_get_width(omap_lcd->state) ||
  173:             omap_lcd->height != ds_get_height(omap_lcd->state)) {
  174:         qemu_console_resize(omap_lcd->state,
  175:                             omap_lcd->width, omap_lcd->height);
  176:         omap_lcd->invalidate = 1;
  177:     }
  178: 
  179:     if (omap_lcd->dma->current_frame == 0)
  180:         size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
  181:     else
  182:         size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
  183: 
  184:     if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
  185:         omap_lcd->sync_error = 1;
  186:         omap_lcd_interrupts(omap_lcd);
  187:         omap_lcd->enable = 0;
  188:         return;
  189:     }
  190: 
  191:     /* Content */
  192:     frame_base = omap_lcd->dma->phys_framebuffer[
  193:             omap_lcd->dma->current_frame] + frame_offset;
  194:     omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
  195:     if (omap_lcd->dma->interrupts & 1)
  196:         qemu_irq_raise(omap_lcd->dma->irq);
  197:     if (omap_lcd->dma->dual)
  198:         omap_lcd->dma->current_frame ^= 1;
  199: 
  200:     if (!ds_get_bits_per_pixel(omap_lcd->state))
  201:         return;
  202: 
  203:     first = 0;
  204:     height = omap_lcd->height;
  205:     if (omap_lcd->subpanel & (1 << 31)) {
  206:         if (omap_lcd->subpanel & (1 << 29))
  207:             first = (omap_lcd->subpanel >> 16) & 0x3ff;
  208:         else
  209:             height = (omap_lcd->subpanel >> 16) & 0x3ff;
  210:         /* TODO: fill the rest of the panel with DPD */
  211:     }
  212: 
  213:     step = width * bpp >> 3;
  214:     linesize = ds_get_linesize(omap_lcd->state);
  215:     framebuffer_update_display(omap_lcd->state,
  216:                                frame_base, width, height,
  217:                                step, linesize, 0,
  218:                                omap_lcd->invalidate,
  219:                                draw_line, omap_lcd->palette,
  220:                                &first, &last);
  221:     if (first >= 0) {
  222:         dpy_update(omap_lcd->state, 0, first, width, last - first + 1);
  223:     }
  224:     omap_lcd->invalidate = 0;
  225: }
  226: 
  227: static int ppm_save(const char *filename, uint8_t *data,
  228:                 int w, int h, int linesize)
  229: {
  230:     FILE *f;
  231:     uint8_t *d, *d1;
  232:     unsigned int v;
  233:     int y, x, bpp;
  234: 
  235:     f = fopen(filename, "wb");
  236:     if (!f)
  237:         return -1;
  238:     fprintf(f, "P6\n%d %d\n%d\n", w, h, 255);
  239:     d1 = data;
  240:     bpp = linesize / w;
  241:     for (y = 0; y < h; y ++) {
  242:         d = d1;
  243:         for (x = 0; x < w; x ++) {
  244:             v = *(uint32_t *) d;
  245:             switch (bpp) {
  246:             case 2:
  247:                 fputc((v >> 8) & 0xf8, f);
  248:                 fputc((v >> 3) & 0xfc, f);
  249:                 fputc((v << 3) & 0xf8, f);
  250:                 break;
  251:             case 3:
  252:             case 4:
  253:             default:
  254:                 fputc((v >> 16) & 0xff, f);
  255:                 fputc((v >> 8) & 0xff, f);
  256:                 fputc((v) & 0xff, f);
  257:                 break;
  258:             }
  259:             d += bpp;
  260:         }
  261:         d1 += linesize;
  262:     }
  263:     fclose(f);
  264:     return 0;
  265: }
  266: 
  267: static void omap_screen_dump(void *opaque, const char *filename) {
  268:     struct omap_lcd_panel_s *omap_lcd = opaque;
  269:     omap_update_display(opaque);
  270:     if (omap_lcd && ds_get_data(omap_lcd->state))
  271:         ppm_save(filename, ds_get_data(omap_lcd->state),
  272:                 omap_lcd->width, omap_lcd->height,
  273:                 ds_get_linesize(omap_lcd->state));
  274: }
  275: 
  276: static void omap_invalidate_display(void *opaque) {
  277:     struct omap_lcd_panel_s *omap_lcd = opaque;
  278:     omap_lcd->invalidate = 1;
  279: }
  280: 
  281: static void omap_lcd_update(struct omap_lcd_panel_s *s) {
  282:     if (!s->enable) {
  283:         s->dma->current_frame = -1;
  284:         s->sync_error = 0;
  285:         if (s->plm != 1)
  286:             s->frame_done = 1;
  287:         omap_lcd_interrupts(s);
  288:         return;
  289:     }
  290: 
  291:     if (s->dma->current_frame == -1) {
  292:         s->frame_done = 0;
  293:         s->palette_done = 0;
  294:         s->dma->current_frame = 0;
  295:     }
  296: 
  297:     if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
  298:                             s->dma->src_f1_top) ||
  299:                     !s->dma->mpu->port[
  300:                     s->dma->src].addr_valid(s->dma->mpu,
  301:                             s->dma->src_f1_bottom) ||
  302:                     (s->dma->dual &&
  303:                      (!s->dma->mpu->port[
  304:                       s->dma->src].addr_valid(s->dma->mpu,
  305:                               s->dma->src_f2_top) ||
  306:                       !s->dma->mpu->port[
  307:                       s->dma->src].addr_valid(s->dma->mpu,
  308:                               s->dma->src_f2_bottom)))) {
  309:         s->dma->condition |= 1 << 2;
  310:         if (s->dma->interrupts & (1 << 1))
  311:             qemu_irq_raise(s->dma->irq);
  312:         s->enable = 0;
  313:         return;
  314:     }
  315: 
  316:     s->dma->phys_framebuffer[0] = s->dma->src_f1_top;
  317:     s->dma->phys_framebuffer[1] = s->dma->src_f2_top;
  318: 
  319:     if (s->plm != 2 && !s->palette_done) {
  320:         cpu_physical_memory_read(
  321:             s->dma->phys_framebuffer[s->dma->current_frame],
  322:             (void *)s->palette, 0x200);
  323:         s->palette_done = 1;
  324:         omap_lcd_interrupts(s);
  325:     }
  326: }
  327: 
  328: static uint32_t omap_lcdc_read(void *opaque, target_phys_addr_t addr)
  329: {
  330:     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
  331: 
  332:     switch (addr) {
  333:     case 0x00:	/* LCD_CONTROL */
  334:         return (s->tft << 23) | (s->plm << 20) |
  335:                 (s->tft << 7) | (s->interrupts << 3) |
  336:                 (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
  337: 
  338:     case 0x04:	/* LCD_TIMING0 */
  339:         return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
  340: 
  341:     case 0x08:	/* LCD_TIMING1 */
  342:         return (s->timing[1] << 10) | (s->height - 1);
  343: 
  344:     case 0x0c:	/* LCD_TIMING2 */
  345:         return s->timing[2] | 0xfc000000;
  346: 
  347:     case 0x10:	/* LCD_STATUS */
  348:         return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
  349: 
  350:     case 0x14:	/* LCD_SUBPANEL */
  351:         return s->subpanel;
  352: 
  353:     default:
  354:         break;
  355:     }
  356:     OMAP_BAD_REG(addr);
  357:     return 0;
  358: }
  359: 
  360: static void omap_lcdc_write(void *opaque, target_phys_addr_t addr,
  361:                 uint32_t value)
  362: {
  363:     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
  364: 
  365:     switch (addr) {
  366:     case 0x00:	/* LCD_CONTROL */
  367:         s->plm = (value >> 20) & 3;
  368:         s->tft = (value >> 7) & 1;
  369:         s->interrupts = (value >> 3) & 3;
  370:         s->mono = (value >> 1) & 1;
  371:         s->ctrl = value & 0x01cff300;
  372:         if (s->enable != (value & 1)) {
  373:             s->enable = value & 1;
  374:             omap_lcd_update(s);
  375:         }
  376:         break;
  377: 
  378:     case 0x04:	/* LCD_TIMING0 */
  379:         s->timing[0] = value >> 10;
  380:         s->width = (value & 0x3ff) + 1;
  381:         break;
  382: 
  383:     case 0x08:	/* LCD_TIMING1 */
  384:         s->timing[1] = value >> 10;
  385:         s->height = (value & 0x3ff) + 1;
  386:         break;
  387: 
  388:     case 0x0c:	/* LCD_TIMING2 */
  389:         s->timing[2] = value;
  390:         break;
  391: 
  392:     case 0x10:	/* LCD_STATUS */
  393:         break;
  394: 
  395:     case 0x14:	/* LCD_SUBPANEL */
  396:         s->subpanel = value & 0xa1ffffff;
  397:         break;
  398: 
  399:     default:
  400:         OMAP_BAD_REG(addr);
  401:     }
  402: }
  403: 
  404: static CPUReadMemoryFunc * const omap_lcdc_readfn[] = {
  405:     omap_lcdc_read,
  406:     omap_lcdc_read,
  407:     omap_lcdc_read,
  408: };
  409: 
  410: static CPUWriteMemoryFunc * const omap_lcdc_writefn[] = {
  411:     omap_lcdc_write,
  412:     omap_lcdc_write,
  413:     omap_lcdc_write,
  414: };
  415: 
  416: void omap_lcdc_reset(struct omap_lcd_panel_s *s)
  417: {
  418:     s->dma->current_frame = -1;
  419:     s->plm = 0;
  420:     s->tft = 0;
  421:     s->mono = 0;
  422:     s->enable = 0;
  423:     s->width = 0;
  424:     s->height = 0;
  425:     s->interrupts = 0;
  426:     s->timing[0] = 0;
  427:     s->timing[1] = 0;
  428:     s->timing[2] = 0;
  429:     s->subpanel = 0;
  430:     s->palette_done = 0;
  431:     s->frame_done = 0;
  432:     s->sync_error = 0;
  433:     s->invalidate = 1;
  434:     s->subpanel = 0;
  435:     s->ctrl = 0;
  436: }
  437: 
  438: struct omap_lcd_panel_s *omap_lcdc_init(target_phys_addr_t base, qemu_irq irq,
  439:                 struct omap_dma_lcd_channel_s *dma,
  440:                 ram_addr_t imif_base, ram_addr_t emiff_base, omap_clk clk)
  441: {
  442:     int iomemtype;
  443:     struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *)
  444:             qemu_mallocz(sizeof(struct omap_lcd_panel_s));
  445: 
  446:     s->irq = irq;
  447:     s->dma = dma;
  448:     s->imif_base = imif_base;
  449:     s->emiff_base = emiff_base;
  450:     omap_lcdc_reset(s);
  451: 
  452:     iomemtype = cpu_register_io_memory(omap_lcdc_readfn,
  453:                     omap_lcdc_writefn, s);
  454:     cpu_register_physical_memory(base, 0x100, iomemtype);
  455: 
  456:     s->state = graphic_console_init(omap_update_display,
  457:                                     omap_invalidate_display,
  458:                                     omap_screen_dump, NULL, s);
  459: 
  460:     return s;
  461: }

unix.superglobalmegacorp.com