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#include "hw/hw.h"
  20#include "ui/console.h"
  21#include "hw/arm/omap.h"
  22#include "framebuffer.h"
  23#include "ui/pixel_ops.h"
  24
  25struct omap_lcd_panel_s {
  26    MemoryRegion *sysmem;
  27    MemoryRegion iomem;
  28    MemoryRegionSection fbsection;
  29    qemu_irq irq;
  30    QemuConsole *con;
  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
  51static 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#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
  82static 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
 114static void omap_update_display(void *opaque)
 115{
 116    struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque;
 117    DisplaySurface *surface = qemu_console_surface(omap_lcd->con);
 118    draw_line_func draw_line;
 119    int size, height, first, last;
 120    int width, linesize, step, bpp, frame_offset;
 121    hwaddr frame_base;
 122
 123    if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable ||
 124        !surface_bits_per_pixel(surface)) {
 125        return;
 126    }
 127
 128    frame_offset = 0;
 129    if (omap_lcd->plm != 2) {
 130        cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[
 131                                  omap_lcd->dma->current_frame],
 132                                 (void *)omap_lcd->palette, 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[surface_bits_per_pixel(surface)];
 146        bpp = 2;
 147        break;
 148
 149    case 2:
 150        draw_line = draw_line_table4[surface_bits_per_pixel(surface)];
 151        bpp = 4;
 152        break;
 153
 154    case 3:
 155        draw_line = draw_line_table8[surface_bits_per_pixel(surface)];
 156        bpp = 8;
 157        break;
 158
 159    case 4 ... 7:
 160        if (!omap_lcd->tft)
 161            draw_line = draw_line_table12[surface_bits_per_pixel(surface)];
 162        else
 163            draw_line = draw_line_table16[surface_bits_per_pixel(surface)];
 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 != surface_width(surface) ||
 175        omap_lcd->height != surface_height(surface)) {
 176        qemu_console_resize(omap_lcd->con,
 177                            omap_lcd->width, omap_lcd->height);
 178        surface = qemu_console_surface(omap_lcd->con);
 179        omap_lcd->invalidate = 1;
 180    }
 181
 182    if (omap_lcd->dma->current_frame == 0)
 183        size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
 184    else
 185        size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
 186
 187    if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
 188        omap_lcd->sync_error = 1;
 189        omap_lcd_interrupts(omap_lcd);
 190        omap_lcd->enable = 0;
 191        return;
 192    }
 193
 194    /* Content */
 195    frame_base = omap_lcd->dma->phys_framebuffer[
 196            omap_lcd->dma->current_frame] + frame_offset;
 197    omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
 198    if (omap_lcd->dma->interrupts & 1)
 199        qemu_irq_raise(omap_lcd->dma->irq);
 200    if (omap_lcd->dma->dual)
 201        omap_lcd->dma->current_frame ^= 1;
 202
 203    if (!surface_bits_per_pixel(surface)) {
 204        return;
 205    }
 206
 207    first = 0;
 208    height = omap_lcd->height;
 209    if (omap_lcd->subpanel & (1 << 31)) {
 210        if (omap_lcd->subpanel & (1 << 29))
 211            first = (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
 217    step = width * bpp >> 3;
 218    linesize = surface_stride(surface);
 219    if (omap_lcd->invalidate) {
 220        framebuffer_update_memory_section(&omap_lcd->fbsection,
 221                                          omap_lcd->sysmem, frame_base,
 222                                          height, step);
 223    }
 224
 225    framebuffer_update_display(surface, &omap_lcd->fbsection,
 226                               width, height,
 227                               step, linesize, 0,
 228                               omap_lcd->invalidate,
 229                               draw_line, omap_lcd->palette,
 230                               &first, &last);
 231
 232    if (first >= 0) {
 233        dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1);
 234    }
 235    omap_lcd->invalidate = 0;
 236}
 237
 238static void omap_invalidate_display(void *opaque) {
 239    struct omap_lcd_panel_s *omap_lcd = opaque;
 240    omap_lcd->invalidate = 1;
 241}
 242
 243static void omap_lcd_update(struct omap_lcd_panel_s *s) {
 244    if (!s->enable) {
 245        s->dma->current_frame = -1;
 246        s->sync_error = 0;
 247        if (s->plm != 1)
 248            s->frame_done = 1;
 249        omap_lcd_interrupts(s);
 250        return;
 251    }
 252
 253    if (s->dma->current_frame == -1) {
 254        s->frame_done = 0;
 255        s->palette_done = 0;
 256        s->dma->current_frame = 0;
 257    }
 258
 259    if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
 260                            s->dma->src_f1_top) ||
 261                    !s->dma->mpu->port[
 262                    s->dma->src].addr_valid(s->dma->mpu,
 263                            s->dma->src_f1_bottom) ||
 264                    (s->dma->dual &&
 265                     (!s->dma->mpu->port[
 266                      s->dma->src].addr_valid(s->dma->mpu,
 267                              s->dma->src_f2_top) ||
 268                      !s->dma->mpu->port[
 269                      s->dma->src].addr_valid(s->dma->mpu,
 270                              s->dma->src_f2_bottom)))) {
 271        s->dma->condition |= 1 << 2;
 272        if (s->dma->interrupts & (1 << 1))
 273            qemu_irq_raise(s->dma->irq);
 274        s->enable = 0;
 275        return;
 276    }
 277
 278    s->dma->phys_framebuffer[0] = s->dma->src_f1_top;
 279    s->dma->phys_framebuffer[1] = s->dma->src_f2_top;
 280
 281    if (s->plm != 2 && !s->palette_done) {
 282        cpu_physical_memory_read(
 283            s->dma->phys_framebuffer[s->dma->current_frame],
 284            (void *)s->palette, 0x200);
 285        s->palette_done = 1;
 286        omap_lcd_interrupts(s);
 287    }
 288}
 289
 290static uint64_t omap_lcdc_read(void *opaque, hwaddr addr,
 291                               unsigned size)
 292{
 293    struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
 294
 295    switch (addr) {
 296    case 0x00:  /* LCD_CONTROL */
 297        return (s->tft << 23) | (s->plm << 20) |
 298                (s->tft << 7) | (s->interrupts << 3) |
 299                (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
 300
 301    case 0x04:  /* LCD_TIMING0 */
 302        return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
 303
 304    case 0x08:  /* LCD_TIMING1 */
 305        return (s->timing[1] << 10) | (s->height - 1);
 306
 307    case 0x0c:  /* LCD_TIMING2 */
 308        return s->timing[2] | 0xfc000000;
 309
 310    case 0x10:  /* LCD_STATUS */
 311        return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
 312
 313    case 0x14:  /* LCD_SUBPANEL */
 314        return s->subpanel;
 315
 316    default:
 317        break;
 318    }
 319    OMAP_BAD_REG(addr);
 320    return 0;
 321}
 322
 323static void omap_lcdc_write(void *opaque, hwaddr addr,
 324                            uint64_t value, unsigned size)
 325{
 326    struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
 327
 328    switch (addr) {
 329    case 0x00:  /* LCD_CONTROL */
 330        s->plm = (value >> 20) & 3;
 331        s->tft = (value >> 7) & 1;
 332        s->interrupts = (value >> 3) & 3;
 333        s->mono = (value >> 1) & 1;
 334        s->ctrl = value & 0x01cff300;
 335        if (s->enable != (value & 1)) {
 336            s->enable = value & 1;
 337            omap_lcd_update(s);
 338        }
 339        break;
 340
 341    case 0x04:  /* LCD_TIMING0 */
 342        s->timing[0] = value >> 10;
 343        s->width = (value & 0x3ff) + 1;
 344        break;
 345
 346    case 0x08:  /* LCD_TIMING1 */
 347        s->timing[1] = value >> 10;
 348        s->height = (value & 0x3ff) + 1;
 349        break;
 350
 351    case 0x0c:  /* LCD_TIMING2 */
 352        s->timing[2] = value;
 353        break;
 354
 355    case 0x10:  /* LCD_STATUS */
 356        break;
 357
 358    case 0x14:  /* LCD_SUBPANEL */
 359        s->subpanel = value & 0xa1ffffff;
 360        break;
 361
 362    default:
 363        OMAP_BAD_REG(addr);
 364    }
 365}
 366
 367static const MemoryRegionOps omap_lcdc_ops = {
 368    .read = omap_lcdc_read,
 369    .write = omap_lcdc_write,
 370    .endianness = DEVICE_NATIVE_ENDIAN,
 371};
 372
 373void omap_lcdc_reset(struct omap_lcd_panel_s *s)
 374{
 375    s->dma->current_frame = -1;
 376    s->plm = 0;
 377    s->tft = 0;
 378    s->mono = 0;
 379    s->enable = 0;
 380    s->width = 0;
 381    s->height = 0;
 382    s->interrupts = 0;
 383    s->timing[0] = 0;
 384    s->timing[1] = 0;
 385    s->timing[2] = 0;
 386    s->subpanel = 0;
 387    s->palette_done = 0;
 388    s->frame_done = 0;
 389    s->sync_error = 0;
 390    s->invalidate = 1;
 391    s->subpanel = 0;
 392    s->ctrl = 0;
 393}
 394
 395static const GraphicHwOps omap_ops = {
 396    .invalidate  = omap_invalidate_display,
 397    .gfx_update  = omap_update_display,
 398};
 399
 400struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
 401                                        hwaddr base,
 402                                        qemu_irq irq,
 403                                        struct omap_dma_lcd_channel_s *dma,
 404                                        omap_clk clk)
 405{
 406    struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1);
 407
 408    s->irq = irq;
 409    s->dma = dma;
 410    s->sysmem = sysmem;
 411    omap_lcdc_reset(s);
 412
 413    memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100);
 414    memory_region_add_subregion(sysmem, base, &s->iomem);
 415
 416    s->con = graphic_console_init(NULL, 0, &omap_ops, s);
 417
 418    return s;
 419}
 420