qemu/hw/audio/milkymist-ac97.c
<<
>>
Prefs
   1/*
   2 *  QEMU model of the Milkymist System Controller.
   3 *
   4 *  Copyright (c) 2010 Michael Walle <michael@walle.cc>
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2 of the License, or (at your option) any later version.
  10 *
  11 * This library 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 GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 *
  19 *
  20 * Specification available at:
  21 *   http://www.milkymist.org/socdoc/ac97.pdf
  22 */
  23
  24#include "hw/hw.h"
  25#include "hw/sysbus.h"
  26#include "trace.h"
  27#include "audio/audio.h"
  28#include "qemu/error-report.h"
  29
  30enum {
  31    R_AC97_CTRL = 0,
  32    R_AC97_ADDR,
  33    R_AC97_DATAOUT,
  34    R_AC97_DATAIN,
  35    R_D_CTRL,
  36    R_D_ADDR,
  37    R_D_REMAINING,
  38    R_RESERVED,
  39    R_U_CTRL,
  40    R_U_ADDR,
  41    R_U_REMAINING,
  42    R_MAX
  43};
  44
  45enum {
  46    AC97_CTRL_RQEN  = (1<<0),
  47    AC97_CTRL_WRITE = (1<<1),
  48};
  49
  50enum {
  51    CTRL_EN = (1<<0),
  52};
  53
  54#define TYPE_MILKYMIST_AC97 "milkymist-ac97"
  55#define MILKYMIST_AC97(obj) \
  56    OBJECT_CHECK(MilkymistAC97State, (obj), TYPE_MILKYMIST_AC97)
  57
  58struct MilkymistAC97State {
  59    SysBusDevice parent_obj;
  60
  61    MemoryRegion regs_region;
  62
  63    QEMUSoundCard card;
  64    SWVoiceIn *voice_in;
  65    SWVoiceOut *voice_out;
  66
  67    uint32_t regs[R_MAX];
  68
  69    qemu_irq crrequest_irq;
  70    qemu_irq crreply_irq;
  71    qemu_irq dmar_irq;
  72    qemu_irq dmaw_irq;
  73};
  74typedef struct MilkymistAC97State MilkymistAC97State;
  75
  76static void update_voices(MilkymistAC97State *s)
  77{
  78    if (s->regs[R_D_CTRL] & CTRL_EN) {
  79        AUD_set_active_out(s->voice_out, 1);
  80    } else {
  81        AUD_set_active_out(s->voice_out, 0);
  82    }
  83
  84    if (s->regs[R_U_CTRL] & CTRL_EN) {
  85        AUD_set_active_in(s->voice_in, 1);
  86    } else {
  87        AUD_set_active_in(s->voice_in, 0);
  88    }
  89}
  90
  91static uint64_t ac97_read(void *opaque, hwaddr addr,
  92                          unsigned size)
  93{
  94    MilkymistAC97State *s = opaque;
  95    uint32_t r = 0;
  96
  97    addr >>= 2;
  98    switch (addr) {
  99    case R_AC97_CTRL:
 100    case R_AC97_ADDR:
 101    case R_AC97_DATAOUT:
 102    case R_AC97_DATAIN:
 103    case R_D_CTRL:
 104    case R_D_ADDR:
 105    case R_D_REMAINING:
 106    case R_U_CTRL:
 107    case R_U_ADDR:
 108    case R_U_REMAINING:
 109        r = s->regs[addr];
 110        break;
 111
 112    default:
 113        error_report("milkymist_ac97: read access to unknown register 0x"
 114                TARGET_FMT_plx, addr << 2);
 115        break;
 116    }
 117
 118    trace_milkymist_ac97_memory_read(addr << 2, r);
 119
 120    return r;
 121}
 122
 123static void ac97_write(void *opaque, hwaddr addr, uint64_t value,
 124                       unsigned size)
 125{
 126    MilkymistAC97State *s = opaque;
 127
 128    trace_milkymist_ac97_memory_write(addr, value);
 129
 130    addr >>= 2;
 131    switch (addr) {
 132    case R_AC97_CTRL:
 133        /* always raise an IRQ according to the direction */
 134        if (value & AC97_CTRL_RQEN) {
 135            if (value & AC97_CTRL_WRITE) {
 136                trace_milkymist_ac97_pulse_irq_crrequest();
 137                qemu_irq_pulse(s->crrequest_irq);
 138            } else {
 139                trace_milkymist_ac97_pulse_irq_crreply();
 140                qemu_irq_pulse(s->crreply_irq);
 141            }
 142        }
 143
 144        /* RQEN is self clearing */
 145        s->regs[addr] = value & ~AC97_CTRL_RQEN;
 146        break;
 147    case R_D_CTRL:
 148    case R_U_CTRL:
 149        s->regs[addr] = value;
 150        update_voices(s);
 151        break;
 152    case R_AC97_ADDR:
 153    case R_AC97_DATAOUT:
 154    case R_AC97_DATAIN:
 155    case R_D_ADDR:
 156    case R_D_REMAINING:
 157    case R_U_ADDR:
 158    case R_U_REMAINING:
 159        s->regs[addr] = value;
 160        break;
 161
 162    default:
 163        error_report("milkymist_ac97: write access to unknown register 0x"
 164                TARGET_FMT_plx, addr);
 165        break;
 166    }
 167
 168}
 169
 170static const MemoryRegionOps ac97_mmio_ops = {
 171    .read = ac97_read,
 172    .write = ac97_write,
 173    .valid = {
 174        .min_access_size = 4,
 175        .max_access_size = 4,
 176    },
 177    .endianness = DEVICE_NATIVE_ENDIAN,
 178};
 179
 180static void ac97_in_cb(void *opaque, int avail_b)
 181{
 182    MilkymistAC97State *s = opaque;
 183    uint8_t buf[4096];
 184    uint32_t remaining = s->regs[R_U_REMAINING];
 185    int temp = audio_MIN(remaining, avail_b);
 186    uint32_t addr = s->regs[R_U_ADDR];
 187    int transferred = 0;
 188
 189    trace_milkymist_ac97_in_cb(avail_b, remaining);
 190
 191    /* prevent from raising an IRQ */
 192    if (temp == 0) {
 193        return;
 194    }
 195
 196    while (temp) {
 197        int acquired, to_copy;
 198
 199        to_copy = audio_MIN(temp, sizeof(buf));
 200        acquired = AUD_read(s->voice_in, buf, to_copy);
 201        if (!acquired) {
 202            break;
 203        }
 204
 205        cpu_physical_memory_write(addr, buf, acquired);
 206
 207        temp -= acquired;
 208        addr += acquired;
 209        transferred += acquired;
 210    }
 211
 212    trace_milkymist_ac97_in_cb_transferred(transferred);
 213
 214    s->regs[R_U_ADDR] = addr;
 215    s->regs[R_U_REMAINING] -= transferred;
 216
 217    if ((s->regs[R_U_CTRL] & CTRL_EN) && (s->regs[R_U_REMAINING] == 0)) {
 218        trace_milkymist_ac97_pulse_irq_dmaw();
 219        qemu_irq_pulse(s->dmaw_irq);
 220    }
 221}
 222
 223static void ac97_out_cb(void *opaque, int free_b)
 224{
 225    MilkymistAC97State *s = opaque;
 226    uint8_t buf[4096];
 227    uint32_t remaining = s->regs[R_D_REMAINING];
 228    int temp = audio_MIN(remaining, free_b);
 229    uint32_t addr = s->regs[R_D_ADDR];
 230    int transferred = 0;
 231
 232    trace_milkymist_ac97_out_cb(free_b, remaining);
 233
 234    /* prevent from raising an IRQ */
 235    if (temp == 0) {
 236        return;
 237    }
 238
 239    while (temp) {
 240        int copied, to_copy;
 241
 242        to_copy = audio_MIN(temp, sizeof(buf));
 243        cpu_physical_memory_read(addr, buf, to_copy);
 244        copied = AUD_write(s->voice_out, buf, to_copy);
 245        if (!copied) {
 246            break;
 247        }
 248        temp -= copied;
 249        addr += copied;
 250        transferred += copied;
 251    }
 252
 253    trace_milkymist_ac97_out_cb_transferred(transferred);
 254
 255    s->regs[R_D_ADDR] = addr;
 256    s->regs[R_D_REMAINING] -= transferred;
 257
 258    if ((s->regs[R_D_CTRL] & CTRL_EN) && (s->regs[R_D_REMAINING] == 0)) {
 259        trace_milkymist_ac97_pulse_irq_dmar();
 260        qemu_irq_pulse(s->dmar_irq);
 261    }
 262}
 263
 264static void milkymist_ac97_reset(DeviceState *d)
 265{
 266    MilkymistAC97State *s = MILKYMIST_AC97(d);
 267    int i;
 268
 269    for (i = 0; i < R_MAX; i++) {
 270        s->regs[i] = 0;
 271    }
 272
 273    AUD_set_active_in(s->voice_in, 0);
 274    AUD_set_active_out(s->voice_out, 0);
 275}
 276
 277static int ac97_post_load(void *opaque, int version_id)
 278{
 279    MilkymistAC97State *s = opaque;
 280
 281    update_voices(s);
 282
 283    return 0;
 284}
 285
 286static int milkymist_ac97_init(SysBusDevice *dev)
 287{
 288    MilkymistAC97State *s = MILKYMIST_AC97(dev);
 289
 290    struct audsettings as;
 291    sysbus_init_irq(dev, &s->crrequest_irq);
 292    sysbus_init_irq(dev, &s->crreply_irq);
 293    sysbus_init_irq(dev, &s->dmar_irq);
 294    sysbus_init_irq(dev, &s->dmaw_irq);
 295
 296    AUD_register_card("Milkymist AC'97", &s->card);
 297
 298    as.freq = 48000;
 299    as.nchannels = 2;
 300    as.fmt = AUD_FMT_S16;
 301    as.endianness = 1;
 302
 303    s->voice_in = AUD_open_in(&s->card, s->voice_in,
 304            "mm_ac97.in", s, ac97_in_cb, &as);
 305    s->voice_out = AUD_open_out(&s->card, s->voice_out,
 306            "mm_ac97.out", s, ac97_out_cb, &as);
 307
 308    memory_region_init_io(&s->regs_region, OBJECT(s), &ac97_mmio_ops, s,
 309            "milkymist-ac97", R_MAX * 4);
 310    sysbus_init_mmio(dev, &s->regs_region);
 311
 312    return 0;
 313}
 314
 315static const VMStateDescription vmstate_milkymist_ac97 = {
 316    .name = "milkymist-ac97",
 317    .version_id = 1,
 318    .minimum_version_id = 1,
 319    .minimum_version_id_old = 1,
 320    .post_load = ac97_post_load,
 321    .fields      = (VMStateField[]) {
 322        VMSTATE_UINT32_ARRAY(regs, MilkymistAC97State, R_MAX),
 323        VMSTATE_END_OF_LIST()
 324    }
 325};
 326
 327static void milkymist_ac97_class_init(ObjectClass *klass, void *data)
 328{
 329    DeviceClass *dc = DEVICE_CLASS(klass);
 330    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 331
 332    k->init = milkymist_ac97_init;
 333    dc->reset = milkymist_ac97_reset;
 334    dc->vmsd = &vmstate_milkymist_ac97;
 335}
 336
 337static const TypeInfo milkymist_ac97_info = {
 338    .name          = TYPE_MILKYMIST_AC97,
 339    .parent        = TYPE_SYS_BUS_DEVICE,
 340    .instance_size = sizeof(MilkymistAC97State),
 341    .class_init    = milkymist_ac97_class_init,
 342};
 343
 344static void milkymist_ac97_register_types(void)
 345{
 346    type_register_static(&milkymist_ac97_info);
 347}
 348
 349type_init(milkymist_ac97_register_types)
 350