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