qemu/hw/display/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, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "qemu/osdep.h"
  21#include "hw/irq.h"
  22#include "ui/console.h"
  23#include "hw/arm/omap.h"
  24#include "framebuffer.h"
  25#include "ui/pixel_ops.h"
  26
  27struct omap_lcd_panel_s {
  28    MemoryRegion *sysmem;
  29    MemoryRegion iomem;
  30    MemoryRegionSection fbsection;
  31    qemu_irq irq;
  32    QemuConsole *con;
  33
  34    int plm;
  35    int tft;
  36    int mono;
  37    int enable;
  38    int width;
  39    int height;
  40    int interrupts;
  41    uint32_t timing[3];
  42    uint32_t subpanel;
  43    uint32_t ctrl;
  44
  45    struct omap_dma_lcd_channel_s *dma;
  46    uint16_t palette[256];
  47    int palette_done;
  48    int frame_done;
  49    int invalidate;
  50    int sync_error;
  51};
  52
  53static void omap_lcd_interrupts(struct omap_lcd_panel_s *s)
  54{
  55    if (s->frame_done && (s->interrupts & 1)) {
  56        qemu_irq_raise(s->irq);
  57        return;
  58    }
  59
  60    if (s->palette_done && (s->interrupts & 2)) {
  61        qemu_irq_raise(s->irq);
  62        return;
  63    }
  64
  65    if (s->sync_error) {
  66        qemu_irq_raise(s->irq);
  67        return;
  68    }
  69
  70    qemu_irq_lower(s->irq);
  71}
  72
  73/*
  74 * 2-bit colour
  75 */
  76static void draw_line2_32(void *opaque, uint8_t *d, const uint8_t *s,
  77                          int width, int deststep)
  78{
  79    uint16_t *pal = opaque;
  80    uint8_t v, r, g, b;
  81
  82    do {
  83        v = ldub_p((void *) s);
  84        r = (pal[v & 3] >> 4) & 0xf0;
  85        g = pal[v & 3] & 0xf0;
  86        b = (pal[v & 3] << 4) & 0xf0;
  87        ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
  88        d += 4;
  89        v >>= 2;
  90        r = (pal[v & 3] >> 4) & 0xf0;
  91        g = pal[v & 3] & 0xf0;
  92        b = (pal[v & 3] << 4) & 0xf0;
  93        ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
  94        d += 4;
  95        v >>= 2;
  96        r = (pal[v & 3] >> 4) & 0xf0;
  97        g = pal[v & 3] & 0xf0;
  98        b = (pal[v & 3] << 4) & 0xf0;
  99        ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
 100        d += 4;
 101        v >>= 2;
 102        r = (pal[v & 3] >> 4) & 0xf0;
 103        g = pal[v & 3] & 0xf0;
 104        b = (pal[v & 3] << 4) & 0xf0;
 105        ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
 106        d += 4;
 107        s++;
 108        width -= 4;
 109    } while (width > 0);
 110}
 111
 112/*
 113 * 4-bit colour
 114 */
 115static void draw_line4_32(void *opaque, uint8_t *d, const uint8_t *s,
 116                          int width, int deststep)
 117{
 118    uint16_t *pal = opaque;
 119    uint8_t v, r, g, b;
 120
 121    do {
 122        v = ldub_p((void *) s);
 123        r = (pal[v & 0xf] >> 4) & 0xf0;
 124        g = pal[v & 0xf] & 0xf0;
 125        b = (pal[v & 0xf] << 4) & 0xf0;
 126        ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
 127        d += 4;
 128        v >>= 4;
 129        r = (pal[v & 0xf] >> 4) & 0xf0;
 130        g = pal[v & 0xf] & 0xf0;
 131        b = (pal[v & 0xf] << 4) & 0xf0;
 132        ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
 133        d += 4;
 134        s++;
 135        width -= 2;
 136    } while (width > 0);
 137}
 138
 139/*
 140 * 8-bit colour
 141 */
 142static void draw_line8_32(void *opaque, uint8_t *d, const uint8_t *s,
 143                          int width, int deststep)
 144{
 145    uint16_t *pal = opaque;
 146    uint8_t v, r, g, b;
 147
 148    do {
 149        v = ldub_p((void *) s);
 150        r = (pal[v] >> 4) & 0xf0;
 151        g = pal[v] & 0xf0;
 152        b = (pal[v] << 4) & 0xf0;
 153        ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
 154        s++;
 155        d += 4;
 156    } while (-- width != 0);
 157}
 158
 159/*
 160 * 12-bit colour
 161 */
 162static void draw_line12_32(void *opaque, uint8_t *d, const uint8_t *s,
 163                           int width, int deststep)
 164{
 165    uint16_t v;
 166    uint8_t r, g, b;
 167
 168    do {
 169        v = lduw_le_p((void *) s);
 170        r = (v >> 4) & 0xf0;
 171        g = v & 0xf0;
 172        b = (v << 4) & 0xf0;
 173        ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
 174        s += 2;
 175        d += 4;
 176    } while (-- width != 0);
 177}
 178
 179/*
 180 * 16-bit colour
 181 */
 182static void draw_line16_32(void *opaque, uint8_t *d, const uint8_t *s,
 183                           int width, int deststep)
 184{
 185    uint16_t v;
 186    uint8_t r, g, b;
 187
 188    do {
 189        v = lduw_le_p((void *) s);
 190        r = (v >> 8) & 0xf8;
 191        g = (v >> 3) & 0xfc;
 192        b = (v << 3) & 0xf8;
 193        ((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
 194        s += 2;
 195        d += 4;
 196    } while (-- width != 0);
 197}
 198
 199static void omap_update_display(void *opaque)
 200{
 201    struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque;
 202    DisplaySurface *surface;
 203    drawfn draw_line;
 204    int size, height, first, last;
 205    int width, linesize, step, bpp, frame_offset;
 206    hwaddr frame_base;
 207
 208    if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable) {
 209        return;
 210    }
 211
 212    surface = qemu_console_surface(omap_lcd->con);
 213    if (!surface_bits_per_pixel(surface)) {
 214        return;
 215    }
 216
 217    frame_offset = 0;
 218    if (omap_lcd->plm != 2) {
 219        cpu_physical_memory_read(
 220                omap_lcd->dma->phys_framebuffer[omap_lcd->dma->current_frame],
 221                omap_lcd->palette, 0x200);
 222        switch (omap_lcd->palette[0] >> 12 & 7) {
 223        case 3 ... 7:
 224            frame_offset += 0x200;
 225            break;
 226        default:
 227            frame_offset += 0x20;
 228        }
 229    }
 230
 231    /* Colour depth */
 232    switch ((omap_lcd->palette[0] >> 12) & 7) {
 233    case 1:
 234        draw_line = draw_line2_32;
 235        bpp = 2;
 236        break;
 237
 238    case 2:
 239        draw_line = draw_line4_32;
 240        bpp = 4;
 241        break;
 242
 243    case 3:
 244        draw_line = draw_line8_32;
 245        bpp = 8;
 246        break;
 247
 248    case 4 ... 7:
 249        if (!omap_lcd->tft)
 250            draw_line = draw_line12_32;
 251        else
 252            draw_line = draw_line16_32;
 253        bpp = 16;
 254        break;
 255
 256    default:
 257        /* Unsupported at the moment.  */
 258        return;
 259    }
 260
 261    /* Resolution */
 262    width = omap_lcd->width;
 263    if (width != surface_width(surface) ||
 264        omap_lcd->height != surface_height(surface)) {
 265        qemu_console_resize(omap_lcd->con,
 266                            omap_lcd->width, omap_lcd->height);
 267        surface = qemu_console_surface(omap_lcd->con);
 268        omap_lcd->invalidate = 1;
 269    }
 270
 271    if (omap_lcd->dma->current_frame == 0)
 272        size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
 273    else
 274        size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
 275
 276    if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
 277        omap_lcd->sync_error = 1;
 278        omap_lcd_interrupts(omap_lcd);
 279        omap_lcd->enable = 0;
 280        return;
 281    }
 282
 283    /* Content */
 284    frame_base = omap_lcd->dma->phys_framebuffer[
 285            omap_lcd->dma->current_frame] + frame_offset;
 286    omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
 287    if (omap_lcd->dma->interrupts & 1)
 288        qemu_irq_raise(omap_lcd->dma->irq);
 289    if (omap_lcd->dma->dual)
 290        omap_lcd->dma->current_frame ^= 1;
 291
 292    if (!surface_bits_per_pixel(surface)) {
 293        return;
 294    }
 295
 296    first = 0;
 297    height = omap_lcd->height;
 298    if (omap_lcd->subpanel & (1 << 31)) {
 299        if (omap_lcd->subpanel & (1 << 29))
 300            first = (omap_lcd->subpanel >> 16) & 0x3ff;
 301        else
 302            height = (omap_lcd->subpanel >> 16) & 0x3ff;
 303        /* TODO: fill the rest of the panel with DPD */
 304    }
 305
 306    step = width * bpp >> 3;
 307    linesize = surface_stride(surface);
 308    if (omap_lcd->invalidate) {
 309        framebuffer_update_memory_section(&omap_lcd->fbsection,
 310                                          omap_lcd->sysmem, frame_base,
 311                                          height, step);
 312    }
 313
 314    framebuffer_update_display(surface, &omap_lcd->fbsection,
 315                               width, height,
 316                               step, linesize, 0,
 317                               omap_lcd->invalidate,
 318                               draw_line, omap_lcd->palette,
 319                               &first, &last);
 320
 321    if (first >= 0) {
 322        dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1);
 323    }
 324    omap_lcd->invalidate = 0;
 325}
 326
 327static void omap_invalidate_display(void *opaque) {
 328    struct omap_lcd_panel_s *omap_lcd = opaque;
 329    omap_lcd->invalidate = 1;
 330}
 331
 332static void omap_lcd_update(struct omap_lcd_panel_s *s) {
 333    if (!s->enable) {
 334        s->dma->current_frame = -1;
 335        s->sync_error = 0;
 336        if (s->plm != 1)
 337            s->frame_done = 1;
 338        omap_lcd_interrupts(s);
 339        return;
 340    }
 341
 342    if (s->dma->current_frame == -1) {
 343        s->frame_done = 0;
 344        s->palette_done = 0;
 345        s->dma->current_frame = 0;
 346    }
 347
 348    if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
 349                            s->dma->src_f1_top) ||
 350                    !s->dma->mpu->port[
 351                    s->dma->src].addr_valid(s->dma->mpu,
 352                            s->dma->src_f1_bottom) ||
 353                    (s->dma->dual &&
 354                     (!s->dma->mpu->port[
 355                      s->dma->src].addr_valid(s->dma->mpu,
 356                              s->dma->src_f2_top) ||
 357                      !s->dma->mpu->port[
 358                      s->dma->src].addr_valid(s->dma->mpu,
 359                              s->dma->src_f2_bottom)))) {
 360        s->dma->condition |= 1 << 2;
 361        if (s->dma->interrupts & (1 << 1))
 362            qemu_irq_raise(s->dma->irq);
 363        s->enable = 0;
 364        return;
 365    }
 366
 367    s->dma->phys_framebuffer[0] = s->dma->src_f1_top;
 368    s->dma->phys_framebuffer[1] = s->dma->src_f2_top;
 369
 370    if (s->plm != 2 && !s->palette_done) {
 371        cpu_physical_memory_read(
 372                            s->dma->phys_framebuffer[s->dma->current_frame],
 373                            s->palette, 0x200);
 374        s->palette_done = 1;
 375        omap_lcd_interrupts(s);
 376    }
 377}
 378
 379static uint64_t omap_lcdc_read(void *opaque, hwaddr addr,
 380                               unsigned size)
 381{
 382    struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
 383
 384    switch (addr) {
 385    case 0x00:  /* LCD_CONTROL */
 386        return (s->tft << 23) | (s->plm << 20) |
 387                (s->tft << 7) | (s->interrupts << 3) |
 388                (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
 389
 390    case 0x04:  /* LCD_TIMING0 */
 391        return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
 392
 393    case 0x08:  /* LCD_TIMING1 */
 394        return (s->timing[1] << 10) | (s->height - 1);
 395
 396    case 0x0c:  /* LCD_TIMING2 */
 397        return s->timing[2] | 0xfc000000;
 398
 399    case 0x10:  /* LCD_STATUS */
 400        return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
 401
 402    case 0x14:  /* LCD_SUBPANEL */
 403        return s->subpanel;
 404
 405    default:
 406        break;
 407    }
 408    OMAP_BAD_REG(addr);
 409    return 0;
 410}
 411
 412static void omap_lcdc_write(void *opaque, hwaddr addr,
 413                            uint64_t value, unsigned size)
 414{
 415    struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
 416
 417    switch (addr) {
 418    case 0x00:  /* LCD_CONTROL */
 419        s->plm = (value >> 20) & 3;
 420        s->tft = (value >> 7) & 1;
 421        s->interrupts = (value >> 3) & 3;
 422        s->mono = (value >> 1) & 1;
 423        s->ctrl = value & 0x01cff300;
 424        if (s->enable != (value & 1)) {
 425            s->enable = value & 1;
 426            omap_lcd_update(s);
 427        }
 428        break;
 429
 430    case 0x04:  /* LCD_TIMING0 */
 431        s->timing[0] = value >> 10;
 432        s->width = (value & 0x3ff) + 1;
 433        break;
 434
 435    case 0x08:  /* LCD_TIMING1 */
 436        s->timing[1] = value >> 10;
 437        s->height = (value & 0x3ff) + 1;
 438        break;
 439
 440    case 0x0c:  /* LCD_TIMING2 */
 441        s->timing[2] = value;
 442        break;
 443
 444    case 0x10:  /* LCD_STATUS */
 445        break;
 446
 447    case 0x14:  /* LCD_SUBPANEL */
 448        s->subpanel = value & 0xa1ffffff;
 449        break;
 450
 451    default:
 452        OMAP_BAD_REG(addr);
 453    }
 454}
 455
 456static const MemoryRegionOps omap_lcdc_ops = {
 457    .read = omap_lcdc_read,
 458    .write = omap_lcdc_write,
 459    .endianness = DEVICE_NATIVE_ENDIAN,
 460};
 461
 462void omap_lcdc_reset(struct omap_lcd_panel_s *s)
 463{
 464    s->dma->current_frame = -1;
 465    s->plm = 0;
 466    s->tft = 0;
 467    s->mono = 0;
 468    s->enable = 0;
 469    s->width = 0;
 470    s->height = 0;
 471    s->interrupts = 0;
 472    s->timing[0] = 0;
 473    s->timing[1] = 0;
 474    s->timing[2] = 0;
 475    s->subpanel = 0;
 476    s->palette_done = 0;
 477    s->frame_done = 0;
 478    s->sync_error = 0;
 479    s->invalidate = 1;
 480    s->subpanel = 0;
 481    s->ctrl = 0;
 482}
 483
 484static const GraphicHwOps omap_ops = {
 485    .invalidate  = omap_invalidate_display,
 486    .gfx_update  = omap_update_display,
 487};
 488
 489struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
 490                                        hwaddr base,
 491                                        qemu_irq irq,
 492                                        struct omap_dma_lcd_channel_s *dma,
 493                                        omap_clk clk)
 494{
 495    struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1);
 496
 497    s->irq = irq;
 498    s->dma = dma;
 499    s->sysmem = sysmem;
 500    omap_lcdc_reset(s);
 501
 502    memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100);
 503    memory_region_add_subregion(sysmem, base, &s->iomem);
 504
 505    s->con = graphic_console_init(NULL, 0, &omap_ops, s);
 506
 507    return s;
 508}
 509