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