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 "qemu/osdep.h"
  25#include "hw/hw.h"
  26#include "hw/sysbus.h"
  27#include "trace.h"
  28#include "audio/audio.h"
  29#include "qemu/error-report.h"
  30
  31enum {
  32    R_AC97_CTRL = 0,
  33    R_AC97_ADDR,
  34    R_AC97_DATAOUT,
  35    R_AC97_DATAIN,
  36    R_D_CTRL,
  37    R_D_ADDR,
  38    R_D_REMAINING,
  39    R_RESERVED,
  40    R_U_CTRL,
  41    R_U_ADDR,
  42    R_U_REMAINING,
  43    R_MAX
  44};
  45
  46enum {
  47    AC97_CTRL_RQEN  = (1<<0),
  48    AC97_CTRL_WRITE = (1<<1),
  49};
  50
  51enum {
  52    CTRL_EN = (1<<0),
  53};
  54
  55#define TYPE_MILKYMIST_AC97 "milkymist-ac97"
  56#define MILKYMIST_AC97(obj) \
  57    OBJECT_CHECK(MilkymistAC97State, (obj), TYPE_MILKYMIST_AC97)
  58
  59struct MilkymistAC97State {
  60    SysBusDevice parent_obj;
  61
  62    MemoryRegion regs_region;
  63
  64    QEMUSoundCard card;
  65    SWVoiceIn *voice_in;
  66    SWVoiceOut *voice_out;
  67
  68    uint32_t regs[R_MAX];
  69
  70    qemu_irq crrequest_irq;
  71    qemu_irq crreply_irq;
  72    qemu_irq dmar_irq;
  73    qemu_irq dmaw_irq;
  74};
  75typedef struct MilkymistAC97State MilkymistAC97State;
  76
  77static void update_voices(MilkymistAC97State *s)
  78{
  79    if (s->regs[R_D_CTRL] & CTRL_EN) {
  80        AUD_set_active_out(s->voice_out, 1);
  81    } else {
  82        AUD_set_active_out(s->voice_out, 0);
  83    }
  84
  85    if (s->regs[R_U_CTRL] & CTRL_EN) {
  86        AUD_set_active_in(s->voice_in, 1);
  87    } else {
  88        AUD_set_active_in(s->voice_in, 0);
  89    }
  90}
  91
  92static uint64_t ac97_read(void *opaque, hwaddr addr,
  93                          unsigned size)
  94{
  95    MilkymistAC97State *s = opaque;
  96    uint32_t r = 0;
  97
  98    addr >>= 2;
  99    switch (addr) {
 100    case R_AC97_CTRL:
 101    case R_AC97_ADDR:
 102    case R_AC97_DATAOUT:
 103    case R_AC97_DATAIN:
 104    case R_D_CTRL:
 105    case R_D_ADDR:
 106    case R_D_REMAINING:
 107    case R_U_CTRL:
 108    case R_U_ADDR:
 109    case R_U_REMAINING:
 110        r = s->regs[addr];
 111        break;
 112
 113    default:
 114        error_report("milkymist_ac97: read access to unknown register 0x"
 115                TARGET_FMT_plx, addr << 2);
 116        break;
 117    }
 118
 119    trace_milkymist_ac97_memory_read(addr << 2, r);
 120
 121    return r;
 122}
 123
 124static void ac97_write(void *opaque, hwaddr addr, uint64_t value,
 125                       unsigned size)
 126{
 127    MilkymistAC97State *s = opaque;
 128
 129    trace_milkymist_ac97_memory_write(addr, value);
 130
 131    addr >>= 2;
 132    switch (addr) {
 133    case R_AC97_CTRL:
 134        /* always raise an IRQ according to the direction */
 135        if (value & AC97_CTRL_RQEN) {
 136            if (value & AC97_CTRL_WRITE) {
 137                trace_milkymist_ac97_pulse_irq_crrequest();
 138                qemu_irq_pulse(s->crrequest_irq);
 139            } else {
 140                trace_milkymist_ac97_pulse_irq_crreply();
 141                qemu_irq_pulse(s->crreply_irq);
 142            }
 143        }
 144
 145        /* RQEN is self clearing */
 146        s->regs[addr] = value & ~AC97_CTRL_RQEN;
 147        break;
 148    case R_D_CTRL:
 149    case R_U_CTRL:
 150        s->regs[addr] = value;
 151        update_voices(s);
 152        break;
 153    case R_AC97_ADDR:
 154    case R_AC97_DATAOUT:
 155    case R_AC97_DATAIN:
 156    case R_D_ADDR:
 157    case R_D_REMAINING:
 158    case R_U_ADDR:
 159    case R_U_REMAINING:
 160        s->regs[addr] = value;
 161        break;
 162
 163    default:
 164        error_report("milkymist_ac97: write access to unknown register 0x"
 165                TARGET_FMT_plx, addr);
 166        break;
 167    }
 168
 169}
 170
 171static const MemoryRegionOps ac97_mmio_ops = {
 172    .read = ac97_read,
 173    .write = ac97_write,
 174    .valid = {
 175        .min_access_size = 4,
 176        .max_access_size = 4,
 177    },
 178    .endianness = DEVICE_NATIVE_ENDIAN,
 179};
 180
 181static void ac97_in_cb(void *opaque, int avail_b)
 182{
 183    MilkymistAC97State *s = opaque;
 184    uint8_t buf[4096];
 185    uint32_t remaining = s->regs[R_U_REMAINING];
 186    int temp = audio_MIN(remaining, avail_b);
 187    uint32_t addr = s->regs[R_U_ADDR];
 188    int transferred = 0;
 189
 190    trace_milkymist_ac97_in_cb(avail_b, remaining);
 191
 192    /* prevent from raising an IRQ */
 193    if (temp == 0) {
 194        return;
 195    }
 196
 197    while (temp) {
 198        int acquired, to_copy;
 199
 200        to_copy = audio_MIN(temp, sizeof(buf));
 201        acquired = AUD_read(s->voice_in, buf, to_copy);
 202        if (!acquired) {
 203            break;
 204        }
 205
 206        cpu_physical_memory_write(addr, buf, acquired);
 207
 208        temp -= acquired;
 209        addr += acquired;
 210        transferred += acquired;
 211    }
 212
 213    trace_milkymist_ac97_in_cb_transferred(transferred);
 214
 215    s->regs[R_U_ADDR] = addr;
 216    s->regs[R_U_REMAINING] -= transferred;
 217
 218    if ((s->regs[R_U_CTRL] & CTRL_EN) && (s->regs[R_U_REMAINING] == 0)) {
 219        trace_milkymist_ac97_pulse_irq_dmaw();
 220        qemu_irq_pulse(s->dmaw_irq);
 221    }
 222}
 223
 224static void ac97_out_cb(void *opaque, int free_b)
 225{
 226    MilkymistAC97State *s = opaque;
 227    uint8_t buf[4096];
 228    uint32_t remaining = s->regs[R_D_REMAINING];
 229    int temp = audio_MIN(remaining, free_b);
 230    uint32_t addr = s->regs[R_D_ADDR];
 231    int transferred = 0;
 232
 233    trace_milkymist_ac97_out_cb(free_b, remaining);
 234
 235    /* prevent from raising an IRQ */
 236    if (temp == 0) {
 237        return;
 238    }
 239
 240    while (temp) {
 241        int copied, to_copy;
 242
 243        to_copy = audio_MIN(temp, sizeof(buf));
 244        cpu_physical_memory_read(addr, buf, to_copy);
 245        copied = AUD_write(s->voice_out, buf, to_copy);
 246        if (!copied) {
 247            break;
 248        }
 249        temp -= copied;
 250        addr += copied;
 251        transferred += copied;
 252    }
 253
 254    trace_milkymist_ac97_out_cb_transferred(transferred);
 255
 256    s->regs[R_D_ADDR] = addr;
 257    s->regs[R_D_REMAINING] -= transferred;
 258
 259    if ((s->regs[R_D_CTRL] & CTRL_EN) && (s->regs[R_D_REMAINING] == 0)) {
 260        trace_milkymist_ac97_pulse_irq_dmar();
 261        qemu_irq_pulse(s->dmar_irq);
 262    }
 263}
 264
 265static void milkymist_ac97_reset(DeviceState *d)
 266{
 267    MilkymistAC97State *s = MILKYMIST_AC97(d);
 268    int i;
 269
 270    for (i = 0; i < R_MAX; i++) {
 271        s->regs[i] = 0;
 272    }
 273
 274    AUD_set_active_in(s->voice_in, 0);
 275    AUD_set_active_out(s->voice_out, 0);
 276}
 277
 278static int ac97_post_load(void *opaque, int version_id)
 279{
 280    MilkymistAC97State *s = opaque;
 281
 282    update_voices(s);
 283
 284    return 0;
 285}
 286
 287static int milkymist_ac97_init(SysBusDevice *dev)
 288{
 289    MilkymistAC97State *s = MILKYMIST_AC97(dev);
 290
 291    struct audsettings as;
 292    sysbus_init_irq(dev, &s->crrequest_irq);
 293    sysbus_init_irq(dev, &s->crreply_irq);
 294    sysbus_init_irq(dev, &s->dmar_irq);
 295    sysbus_init_irq(dev, &s->dmaw_irq);
 296
 297    AUD_register_card("Milkymist AC'97", &s->card);
 298
 299    as.freq = 48000;
 300    as.nchannels = 2;
 301    as.fmt = AUD_FMT_S16;
 302    as.endianness = 1;
 303
 304    s->voice_in = AUD_open_in(&s->card, s->voice_in,
 305            "mm_ac97.in", s, ac97_in_cb, &as);
 306    s->voice_out = AUD_open_out(&s->card, s->voice_out,
 307            "mm_ac97.out", s, ac97_out_cb, &as);
 308
 309    memory_region_init_io(&s->regs_region, OBJECT(s), &ac97_mmio_ops, s,
 310            "milkymist-ac97", R_MAX * 4);
 311    sysbus_init_mmio(dev, &s->regs_region);
 312
 313    return 0;
 314}
 315
 316static const VMStateDescription vmstate_milkymist_ac97 = {
 317    .name = "milkymist-ac97",
 318    .version_id = 1,
 319    .minimum_version_id = 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