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 "qemu/osdep.h"
  20#include "hw/hw.h"
  21#include "ui/console.h"
  22#include "hw/arm/omap.h"
  23#include "framebuffer.h"
  24#include "ui/pixel_ops.h"
  25
  26struct omap_lcd_panel_s {
  27    MemoryRegion *sysmem;
  28    MemoryRegion iomem;
  29    MemoryRegionSection fbsection;
  30    qemu_irq irq;
  31    QemuConsole *con;
  32
  33    int plm;
  34    int tft;
  35    int mono;
  36    int enable;
  37    int width;
  38    int height;
  39    int interrupts;
  40    uint32_t timing[3];
  41    uint32_t subpanel;
  42    uint32_t ctrl;
  43
  44    struct omap_dma_lcd_channel_s *dma;
  45    uint16_t palette[256];
  46    int palette_done;
  47    int frame_done;
  48    int invalidate;
  49    int sync_error;
  50};
  51
  52static void omap_lcd_interrupts(struct omap_lcd_panel_s *s)
  53{
  54    if (s->frame_done && (s->interrupts & 1)) {
  55        qemu_irq_raise(s->irq);
  56        return;
  57    }
  58
  59    if (s->palette_done && (s->interrupts & 2)) {
  60        qemu_irq_raise(s->irq);
  61        return;
  62    }
  63
  64    if (s->sync_error) {
  65        qemu_irq_raise(s->irq);
  66        return;
  67    }
  68
  69    qemu_irq_lower(s->irq);
  70}
  71
  72#define draw_line_func drawfn
  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]  = NULL,
  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]  = NULL,
  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]  = NULL,
  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]  = NULL,
 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]  = NULL,
 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    DisplaySurface *surface = qemu_console_surface(omap_lcd->con);
 119    draw_line_func draw_line;
 120    int size, height, first, last;
 121    int width, linesize, step, bpp, frame_offset;
 122    hwaddr frame_base;
 123
 124    if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable ||
 125        !surface_bits_per_pixel(surface)) {
 126        return;
 127    }
 128
 129    frame_offset = 0;
 130    if (omap_lcd->plm != 2) {
 131        cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[
 132                                  omap_lcd->dma->current_frame],
 133                                 (void *)omap_lcd->palette, 0x200);
 134        switch (omap_lcd->palette[0] >> 12 & 7) {
 135        case 3 ... 7:
 136            frame_offset += 0x200;
 137            break;
 138        default:
 139            frame_offset += 0x20;
 140        }
 141    }
 142
 143    /* Colour depth */
 144    switch ((omap_lcd->palette[0] >> 12) & 7) {
 145    case 1:
 146        draw_line = draw_line_table2[surface_bits_per_pixel(surface)];
 147        bpp = 2;
 148        break;
 149
 150    case 2:
 151        draw_line = draw_line_table4[surface_bits_per_pixel(surface)];
 152        bpp = 4;
 153        break;
 154
 155    case 3:
 156        draw_line = draw_line_table8[surface_bits_per_pixel(surface)];
 157        bpp = 8;
 158        break;
 159
 160    case 4 ... 7:
 161        if (!omap_lcd->tft)
 162            draw_line = draw_line_table12[surface_bits_per_pixel(surface)];
 163        else
 164            draw_line = draw_line_table16[surface_bits_per_pixel(surface)];
 165        bpp = 16;
 166        break;
 167
 168    default:
 169        /* Unsupported at the moment.  */
 170        return;
 171    }
 172
 173    /* Resolution */
 174    width = omap_lcd->width;
 175    if (width != surface_width(surface) ||
 176        omap_lcd->height != surface_height(surface)) {
 177        qemu_console_resize(omap_lcd->con,
 178                            omap_lcd->width, omap_lcd->height);
 179        surface = qemu_console_surface(omap_lcd->con);
 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 (!surface_bits_per_pixel(surface)) {
 205        return;
 206    }
 207
 208    first = 0;
 209    height = omap_lcd->height;
 210    if (omap_lcd->subpanel & (1 << 31)) {
 211        if (omap_lcd->subpanel & (1 << 29))
 212            first = (omap_lcd->subpanel >> 16) & 0x3ff;
 213        else
 214            height = (omap_lcd->subpanel >> 16) & 0x3ff;
 215        /* TODO: fill the rest of the panel with DPD */
 216    }
 217
 218    step = width * bpp >> 3;
 219    linesize = surface_stride(surface);
 220    if (omap_lcd->invalidate) {
 221        framebuffer_update_memory_section(&omap_lcd->fbsection,
 222                                          omap_lcd->sysmem, frame_base,
 223                                          height, step);
 224    }
 225
 226    framebuffer_update_display(surface, &omap_lcd->fbsection,
 227                               width, height,
 228                               step, linesize, 0,
 229                               omap_lcd->invalidate,
 230                               draw_line, omap_lcd->palette,
 231                               &first, &last);
 232
 233    if (first >= 0) {
 234        dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1);
 235    }
 236    omap_lcd->invalidate = 0;
 237}
 238
 239static void omap_invalidate_display(void *opaque) {
 240    struct omap_lcd_panel_s *omap_lcd = opaque;
 241    omap_lcd->invalidate = 1;
 242}
 243
 244static void omap_lcd_update(struct omap_lcd_panel_s *s) {
 245    if (!s->enable) {
 246        s->dma->current_frame = -1;
 247        s->sync_error = 0;
 248        if (s->plm != 1)
 249            s->frame_done = 1;
 250        omap_lcd_interrupts(s);
 251        return;
 252    }
 253
 254    if (s->dma->current_frame == -1) {
 255        s->frame_done = 0;
 256        s->palette_done = 0;
 257        s->dma->current_frame = 0;
 258    }
 259
 260    if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
 261                            s->dma->src_f1_top) ||
 262                    !s->dma->mpu->port[
 263                    s->dma->src].addr_valid(s->dma->mpu,
 264                            s->dma->src_f1_bottom) ||
 265                    (s->dma->dual &&
 266                     (!s->dma->mpu->port[
 267                      s->dma->src].addr_valid(s->dma->mpu,
 268                              s->dma->src_f2_top) ||
 269                      !s->dma->mpu->port[
 270                      s->dma->src].addr_valid(s->dma->mpu,
 271                              s->dma->src_f2_bottom)))) {
 272        s->dma->condition |= 1 << 2;
 273        if (s->dma->interrupts & (1 << 1))
 274            qemu_irq_raise(s->dma->irq);
 275        s->enable = 0;
 276        return;
 277    }
 278
 279    s->dma->phys_framebuffer[0] = s->dma->src_f1_top;
 280    s->dma->phys_framebuffer[1] = s->dma->src_f2_top;
 281
 282    if (s->plm != 2 && !s->palette_done) {
 283        cpu_physical_memory_read(
 284            s->dma->phys_framebuffer[s->dma->current_frame],
 285            (void *)s->palette, 0x200);
 286        s->palette_done = 1;
 287        omap_lcd_interrupts(s);
 288    }
 289}
 290
 291static uint64_t omap_lcdc_read(void *opaque, hwaddr addr,
 292                               unsigned size)
 293{
 294    struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
 295
 296    switch (addr) {
 297    case 0x00:  /* LCD_CONTROL */
 298        return (s->tft << 23) | (s->plm << 20) |
 299                (s->tft << 7) | (s->interrupts << 3) |
 300                (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
 301
 302    case 0x04:  /* LCD_TIMING0 */
 303        return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
 304
 305    case 0x08:  /* LCD_TIMING1 */
 306        return (s->timing[1] << 10) | (s->height - 1);
 307
 308    case 0x0c:  /* LCD_TIMING2 */
 309        return s->timing[2] | 0xfc000000;
 310
 311    case 0x10:  /* LCD_STATUS */
 312        return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
 313
 314    case 0x14:  /* LCD_SUBPANEL */
 315        return s->subpanel;
 316
 317    default:
 318        break;
 319    }
 320    OMAP_BAD_REG(addr);
 321    return 0;
 322}
 323
 324static void omap_lcdc_write(void *opaque, hwaddr addr,
 325                            uint64_t value, unsigned size)
 326{
 327    struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
 328
 329    switch (addr) {
 330    case 0x00:  /* LCD_CONTROL */
 331        s->plm = (value >> 20) & 3;
 332        s->tft = (value >> 7) & 1;
 333        s->interrupts = (value >> 3) & 3;
 334        s->mono = (value >> 1) & 1;
 335        s->ctrl = value & 0x01cff300;
 336        if (s->enable != (value & 1)) {
 337            s->enable = value & 1;
 338            omap_lcd_update(s);
 339        }
 340        break;
 341
 342    case 0x04:  /* LCD_TIMING0 */
 343        s->timing[0] = value >> 10;
 344        s->width = (value & 0x3ff) + 1;
 345        break;
 346
 347    case 0x08:  /* LCD_TIMING1 */
 348        s->timing[1] = value >> 10;
 349        s->height = (value & 0x3ff) + 1;
 350        break;
 351
 352    case 0x0c:  /* LCD_TIMING2 */
 353        s->timing[2] = value;
 354        break;
 355
 356    case 0x10:  /* LCD_STATUS */
 357        break;
 358
 359    case 0x14:  /* LCD_SUBPANEL */
 360        s->subpanel = value & 0xa1ffffff;
 361        break;
 362
 363    default:
 364        OMAP_BAD_REG(addr);
 365    }
 366}
 367
 368static const MemoryRegionOps omap_lcdc_ops = {
 369    .read = omap_lcdc_read,
 370    .write = omap_lcdc_write,
 371    .endianness = DEVICE_NATIVE_ENDIAN,
 372};
 373
 374void omap_lcdc_reset(struct omap_lcd_panel_s *s)
 375{
 376    s->dma->current_frame = -1;
 377    s->plm = 0;
 378    s->tft = 0;
 379    s->mono = 0;
 380    s->enable = 0;
 381    s->width = 0;
 382    s->height = 0;
 383    s->interrupts = 0;
 384    s->timing[0] = 0;
 385    s->timing[1] = 0;
 386    s->timing[2] = 0;
 387    s->subpanel = 0;
 388    s->palette_done = 0;
 389    s->frame_done = 0;
 390    s->sync_error = 0;
 391    s->invalidate = 1;
 392    s->subpanel = 0;
 393    s->ctrl = 0;
 394}
 395
 396static const GraphicHwOps omap_ops = {
 397    .invalidate  = omap_invalidate_display,
 398    .gfx_update  = omap_update_display,
 399};
 400
 401struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
 402                                        hwaddr base,
 403                                        qemu_irq irq,
 404                                        struct omap_dma_lcd_channel_s *dma,
 405                                        omap_clk clk)
 406{
 407    struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1);
 408
 409    s->irq = irq;
 410    s->dma = dma;
 411    s->sysmem = sysmem;
 412    omap_lcdc_reset(s);
 413
 414    memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100);
 415    memory_region_add_subregion(sysmem, base, &s->iomem);
 416
 417    s->con = graphic_console_init(NULL, 0, &omap_ops, s);
 418
 419    return s;
 420}
 421