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