qemu/hw/display/milkymist-vgafb.c
<<
>>
Prefs
   1
   2/*
   3 *  QEMU model of the Milkymist VGA framebuffer.
   4 *
   5 *  Copyright (c) 2010-2012 Michael Walle <michael@walle.cc>
   6 *
   7 * This library is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU Lesser General Public
   9 * License as published by the Free Software Foundation; either
  10 * version 2 of the License, or (at your option) any later version.
  11 *
  12 * This library is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 * Lesser General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU Lesser General Public
  18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  19 *
  20 *
  21 * Specification available at:
  22 *   http://www.milkymist.org/socdoc/vgafb.pdf
  23 */
  24
  25#include "qemu/osdep.h"
  26#include "hw/hw.h"
  27#include "hw/sysbus.h"
  28#include "trace.h"
  29#include "ui/console.h"
  30#include "framebuffer.h"
  31#include "ui/pixel_ops.h"
  32#include "qemu/error-report.h"
  33
  34#define BITS 8
  35#include "milkymist-vgafb_template.h"
  36#define BITS 15
  37#include "milkymist-vgafb_template.h"
  38#define BITS 16
  39#include "milkymist-vgafb_template.h"
  40#define BITS 24
  41#include "milkymist-vgafb_template.h"
  42#define BITS 32
  43#include "milkymist-vgafb_template.h"
  44
  45enum {
  46    R_CTRL = 0,
  47    R_HRES,
  48    R_HSYNC_START,
  49    R_HSYNC_END,
  50    R_HSCAN,
  51    R_VRES,
  52    R_VSYNC_START,
  53    R_VSYNC_END,
  54    R_VSCAN,
  55    R_BASEADDRESS,
  56    R_BASEADDRESS_ACT,
  57    R_BURST_COUNT,
  58    R_DDC,
  59    R_SOURCE_CLOCK,
  60    R_MAX
  61};
  62
  63enum {
  64    CTRL_RESET = (1<<0),
  65};
  66
  67#define TYPE_MILKYMIST_VGAFB "milkymist-vgafb"
  68#define MILKYMIST_VGAFB(obj) \
  69    OBJECT_CHECK(MilkymistVgafbState, (obj), TYPE_MILKYMIST_VGAFB)
  70
  71struct MilkymistVgafbState {
  72    SysBusDevice parent_obj;
  73
  74    MemoryRegion regs_region;
  75    MemoryRegionSection fbsection;
  76    QemuConsole *con;
  77
  78    int invalidate;
  79    uint32_t fb_offset;
  80    uint32_t fb_mask;
  81
  82    uint32_t regs[R_MAX];
  83};
  84typedef struct MilkymistVgafbState MilkymistVgafbState;
  85
  86static int vgafb_enabled(MilkymistVgafbState *s)
  87{
  88    return !(s->regs[R_CTRL] & CTRL_RESET);
  89}
  90
  91static void vgafb_update_display(void *opaque)
  92{
  93    MilkymistVgafbState *s = opaque;
  94    SysBusDevice *sbd;
  95    DisplaySurface *surface = qemu_console_surface(s->con);
  96    int src_width;
  97    int first = 0;
  98    int last = 0;
  99    drawfn fn;
 100
 101    if (!vgafb_enabled(s)) {
 102        return;
 103    }
 104
 105    sbd = SYS_BUS_DEVICE(s);
 106    int dest_width = s->regs[R_HRES];
 107
 108    switch (surface_bits_per_pixel(surface)) {
 109    case 0:
 110        return;
 111    case 8:
 112        fn = draw_line_8;
 113        break;
 114    case 15:
 115        fn = draw_line_15;
 116        dest_width *= 2;
 117        break;
 118    case 16:
 119        fn = draw_line_16;
 120        dest_width *= 2;
 121        break;
 122    case 24:
 123        fn = draw_line_24;
 124        dest_width *= 3;
 125        break;
 126    case 32:
 127        fn = draw_line_32;
 128        dest_width *= 4;
 129        break;
 130    default:
 131        hw_error("milkymist_vgafb: bad color depth\n");
 132        break;
 133    }
 134
 135    src_width = s->regs[R_HRES] * 2;
 136    if (s->invalidate) {
 137        framebuffer_update_memory_section(&s->fbsection,
 138                                          sysbus_address_space(sbd),
 139                                          s->regs[R_BASEADDRESS] + s->fb_offset,
 140                                          s->regs[R_VRES], src_width);
 141    }
 142
 143    framebuffer_update_display(surface, &s->fbsection,
 144                               s->regs[R_HRES],
 145                               s->regs[R_VRES],
 146                               src_width,
 147                               dest_width,
 148                               0,
 149                               s->invalidate,
 150                               fn,
 151                               NULL,
 152                               &first, &last);
 153
 154    if (first >= 0) {
 155        dpy_gfx_update(s->con, 0, first, s->regs[R_HRES], last - first + 1);
 156    }
 157    s->invalidate = 0;
 158}
 159
 160static void vgafb_invalidate_display(void *opaque)
 161{
 162    MilkymistVgafbState *s = opaque;
 163    s->invalidate = 1;
 164}
 165
 166static void vgafb_resize(MilkymistVgafbState *s)
 167{
 168    if (!vgafb_enabled(s)) {
 169        return;
 170    }
 171
 172    qemu_console_resize(s->con, s->regs[R_HRES], s->regs[R_VRES]);
 173    s->invalidate = 1;
 174}
 175
 176static uint64_t vgafb_read(void *opaque, hwaddr addr,
 177                           unsigned size)
 178{
 179    MilkymistVgafbState *s = opaque;
 180    uint32_t r = 0;
 181
 182    addr >>= 2;
 183    switch (addr) {
 184    case R_CTRL:
 185    case R_HRES:
 186    case R_HSYNC_START:
 187    case R_HSYNC_END:
 188    case R_HSCAN:
 189    case R_VRES:
 190    case R_VSYNC_START:
 191    case R_VSYNC_END:
 192    case R_VSCAN:
 193    case R_BASEADDRESS:
 194    case R_BURST_COUNT:
 195    case R_DDC:
 196    case R_SOURCE_CLOCK:
 197        r = s->regs[addr];
 198    break;
 199    case R_BASEADDRESS_ACT:
 200        r = s->regs[R_BASEADDRESS];
 201    break;
 202
 203    default:
 204        error_report("milkymist_vgafb: read access to unknown register 0x"
 205                TARGET_FMT_plx, addr << 2);
 206        break;
 207    }
 208
 209    trace_milkymist_vgafb_memory_read(addr << 2, r);
 210
 211    return r;
 212}
 213
 214static void vgafb_write(void *opaque, hwaddr addr, uint64_t value,
 215                        unsigned size)
 216{
 217    MilkymistVgafbState *s = opaque;
 218
 219    trace_milkymist_vgafb_memory_write(addr, value);
 220
 221    addr >>= 2;
 222    switch (addr) {
 223    case R_CTRL:
 224        s->regs[addr] = value;
 225        vgafb_resize(s);
 226        break;
 227    case R_HSYNC_START:
 228    case R_HSYNC_END:
 229    case R_HSCAN:
 230    case R_VSYNC_START:
 231    case R_VSYNC_END:
 232    case R_VSCAN:
 233    case R_BURST_COUNT:
 234    case R_DDC:
 235    case R_SOURCE_CLOCK:
 236        s->regs[addr] = value;
 237        break;
 238    case R_BASEADDRESS:
 239        if (value & 0x1f) {
 240            error_report("milkymist_vgafb: framebuffer base address have to "
 241                     "be 32 byte aligned");
 242            break;
 243        }
 244        s->regs[addr] = value & s->fb_mask;
 245        s->invalidate = 1;
 246        break;
 247    case R_HRES:
 248    case R_VRES:
 249        s->regs[addr] = value;
 250        vgafb_resize(s);
 251        break;
 252    case R_BASEADDRESS_ACT:
 253        error_report("milkymist_vgafb: write to read-only register 0x"
 254                TARGET_FMT_plx, addr << 2);
 255        break;
 256
 257    default:
 258        error_report("milkymist_vgafb: write access to unknown register 0x"
 259                TARGET_FMT_plx, addr << 2);
 260        break;
 261    }
 262}
 263
 264static const MemoryRegionOps vgafb_mmio_ops = {
 265    .read = vgafb_read,
 266    .write = vgafb_write,
 267    .valid = {
 268        .min_access_size = 4,
 269        .max_access_size = 4,
 270    },
 271    .endianness = DEVICE_NATIVE_ENDIAN,
 272};
 273
 274static void milkymist_vgafb_reset(DeviceState *d)
 275{
 276    MilkymistVgafbState *s = MILKYMIST_VGAFB(d);
 277    int i;
 278
 279    for (i = 0; i < R_MAX; i++) {
 280        s->regs[i] = 0;
 281    }
 282
 283    /* defaults */
 284    s->regs[R_CTRL] = CTRL_RESET;
 285    s->regs[R_HRES] = 640;
 286    s->regs[R_VRES] = 480;
 287    s->regs[R_BASEADDRESS] = 0;
 288}
 289
 290static const GraphicHwOps vgafb_ops = {
 291    .invalidate  = vgafb_invalidate_display,
 292    .gfx_update  = vgafb_update_display,
 293};
 294
 295static int milkymist_vgafb_init(SysBusDevice *dev)
 296{
 297    MilkymistVgafbState *s = MILKYMIST_VGAFB(dev);
 298
 299    memory_region_init_io(&s->regs_region, OBJECT(s), &vgafb_mmio_ops, s,
 300            "milkymist-vgafb", R_MAX * 4);
 301    sysbus_init_mmio(dev, &s->regs_region);
 302
 303    s->con = graphic_console_init(DEVICE(dev), 0, &vgafb_ops, s);
 304
 305    return 0;
 306}
 307
 308static int vgafb_post_load(void *opaque, int version_id)
 309{
 310    vgafb_invalidate_display(opaque);
 311    return 0;
 312}
 313
 314static const VMStateDescription vmstate_milkymist_vgafb = {
 315    .name = "milkymist-vgafb",
 316    .version_id = 1,
 317    .minimum_version_id = 1,
 318    .post_load = vgafb_post_load,
 319    .fields = (VMStateField[]) {
 320        VMSTATE_UINT32_ARRAY(regs, MilkymistVgafbState, R_MAX),
 321        VMSTATE_END_OF_LIST()
 322    }
 323};
 324
 325static Property milkymist_vgafb_properties[] = {
 326    DEFINE_PROP_UINT32("fb_offset", MilkymistVgafbState, fb_offset, 0x0),
 327    DEFINE_PROP_UINT32("fb_mask", MilkymistVgafbState, fb_mask, 0xffffffff),
 328    DEFINE_PROP_END_OF_LIST(),
 329};
 330
 331static void milkymist_vgafb_class_init(ObjectClass *klass, void *data)
 332{
 333    DeviceClass *dc = DEVICE_CLASS(klass);
 334    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 335
 336    k->init = milkymist_vgafb_init;
 337    dc->reset = milkymist_vgafb_reset;
 338    dc->vmsd = &vmstate_milkymist_vgafb;
 339    dc->props = milkymist_vgafb_properties;
 340}
 341
 342static const TypeInfo milkymist_vgafb_info = {
 343    .name          = TYPE_MILKYMIST_VGAFB,
 344    .parent        = TYPE_SYS_BUS_DEVICE,
 345    .instance_size = sizeof(MilkymistVgafbState),
 346    .class_init    = milkymist_vgafb_class_init,
 347};
 348
 349static void milkymist_vgafb_register_types(void)
 350{
 351    type_register_static(&milkymist_vgafb_info);
 352}
 353
 354type_init(milkymist_vgafb_register_types)
 355