cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

marvell_88w8618.c (8761B)


      1/*
      2 * Marvell 88w8618 audio emulation extracted from
      3 * Marvell MV88w8618 / Freecom MusicPal emulation.
      4 *
      5 * Copyright (c) 2008 Jan Kiszka
      6 *
      7 * This code is licensed under the GNU GPL v2.
      8 *
      9 * Contributions after 2012-01-13 are licensed under the terms of the
     10 * GNU GPL, version 2 or (at your option) any later version.
     11 */
     12
     13#include "qemu/osdep.h"
     14#include "hw/sysbus.h"
     15#include "migration/vmstate.h"
     16#include "hw/irq.h"
     17#include "hw/qdev-properties.h"
     18#include "hw/audio/wm8750.h"
     19#include "audio/audio.h"
     20#include "qapi/error.h"
     21#include "qemu/module.h"
     22#include "qom/object.h"
     23
     24#define MP_AUDIO_SIZE           0x00001000
     25
     26/* Audio register offsets */
     27#define MP_AUDIO_PLAYBACK_MODE  0x00
     28#define MP_AUDIO_CLOCK_DIV      0x18
     29#define MP_AUDIO_IRQ_STATUS     0x20
     30#define MP_AUDIO_IRQ_ENABLE     0x24
     31#define MP_AUDIO_TX_START_LO    0x28
     32#define MP_AUDIO_TX_THRESHOLD   0x2C
     33#define MP_AUDIO_TX_STATUS      0x38
     34#define MP_AUDIO_TX_START_HI    0x40
     35
     36/* Status register and IRQ enable bits */
     37#define MP_AUDIO_TX_HALF        (1 << 6)
     38#define MP_AUDIO_TX_FULL        (1 << 7)
     39
     40/* Playback mode bits */
     41#define MP_AUDIO_16BIT_SAMPLE   (1 << 0)
     42#define MP_AUDIO_PLAYBACK_EN    (1 << 7)
     43#define MP_AUDIO_CLOCK_24MHZ    (1 << 9)
     44#define MP_AUDIO_MONO           (1 << 14)
     45
     46OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_audio_state, MV88W8618_AUDIO)
     47
     48struct mv88w8618_audio_state {
     49    SysBusDevice parent_obj;
     50
     51    MemoryRegion iomem;
     52    qemu_irq irq;
     53    uint32_t playback_mode;
     54    uint32_t status;
     55    uint32_t irq_enable;
     56    uint32_t phys_buf;
     57    uint32_t target_buffer;
     58    uint32_t threshold;
     59    uint32_t play_pos;
     60    uint32_t last_free;
     61    uint32_t clock_div;
     62    void *wm;
     63};
     64
     65static void mv88w8618_audio_callback(void *opaque, int free_out, int free_in)
     66{
     67    mv88w8618_audio_state *s = opaque;
     68    int16_t *codec_buffer;
     69    int8_t buf[4096];
     70    int8_t *mem_buffer;
     71    int pos, block_size;
     72
     73    if (!(s->playback_mode & MP_AUDIO_PLAYBACK_EN)) {
     74        return;
     75    }
     76    if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE) {
     77        free_out <<= 1;
     78    }
     79    if (!(s->playback_mode & MP_AUDIO_MONO)) {
     80        free_out <<= 1;
     81    }
     82    block_size = s->threshold / 2;
     83    if (free_out - s->last_free < block_size) {
     84        return;
     85    }
     86    if (block_size > 4096) {
     87        return;
     88    }
     89    cpu_physical_memory_read(s->target_buffer + s->play_pos, buf, block_size);
     90    mem_buffer = buf;
     91    if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE) {
     92        if (s->playback_mode & MP_AUDIO_MONO) {
     93            codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1);
     94            for (pos = 0; pos < block_size; pos += 2) {
     95                *codec_buffer++ = *(int16_t *)mem_buffer;
     96                *codec_buffer++ = *(int16_t *)mem_buffer;
     97                mem_buffer += 2;
     98            }
     99        } else {
    100            memcpy(wm8750_dac_buffer(s->wm, block_size >> 2),
    101                   (uint32_t *)mem_buffer, block_size);
    102        }
    103    } else {
    104        if (s->playback_mode & MP_AUDIO_MONO) {
    105            codec_buffer = wm8750_dac_buffer(s->wm, block_size);
    106            for (pos = 0; pos < block_size; pos++) {
    107                *codec_buffer++ = cpu_to_le16(256 * *mem_buffer);
    108                *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);
    109            }
    110        } else {
    111            codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1);
    112            for (pos = 0; pos < block_size; pos += 2) {
    113                *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);
    114                *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);
    115            }
    116        }
    117    }
    118    wm8750_dac_commit(s->wm);
    119
    120    s->last_free = free_out - block_size;
    121
    122    if (s->play_pos == 0) {
    123        s->status |= MP_AUDIO_TX_HALF;
    124        s->play_pos = block_size;
    125    } else {
    126        s->status |= MP_AUDIO_TX_FULL;
    127        s->play_pos = 0;
    128    }
    129
    130    if (s->status & s->irq_enable) {
    131        qemu_irq_raise(s->irq);
    132    }
    133}
    134
    135static void mv88w8618_audio_clock_update(mv88w8618_audio_state *s)
    136{
    137    int rate;
    138
    139    if (s->playback_mode & MP_AUDIO_CLOCK_24MHZ) {
    140        rate = 24576000 / 64; /* 24.576MHz */
    141    } else {
    142        rate = 11289600 / 64; /* 11.2896MHz */
    143    }
    144    rate /= ((s->clock_div >> 8) & 0xff) + 1;
    145
    146    wm8750_set_bclk_in(s->wm, rate);
    147}
    148
    149static uint64_t mv88w8618_audio_read(void *opaque, hwaddr offset,
    150                                    unsigned size)
    151{
    152    mv88w8618_audio_state *s = opaque;
    153
    154    switch (offset) {
    155    case MP_AUDIO_PLAYBACK_MODE:
    156        return s->playback_mode;
    157
    158    case MP_AUDIO_CLOCK_DIV:
    159        return s->clock_div;
    160
    161    case MP_AUDIO_IRQ_STATUS:
    162        return s->status;
    163
    164    case MP_AUDIO_IRQ_ENABLE:
    165        return s->irq_enable;
    166
    167    case MP_AUDIO_TX_STATUS:
    168        return s->play_pos >> 2;
    169
    170    default:
    171        return 0;
    172    }
    173}
    174
    175static void mv88w8618_audio_write(void *opaque, hwaddr offset,
    176                                  uint64_t value, unsigned size)
    177{
    178    mv88w8618_audio_state *s = opaque;
    179
    180    switch (offset) {
    181    case MP_AUDIO_PLAYBACK_MODE:
    182        if (value & MP_AUDIO_PLAYBACK_EN &&
    183            !(s->playback_mode & MP_AUDIO_PLAYBACK_EN)) {
    184            s->status = 0;
    185            s->last_free = 0;
    186            s->play_pos = 0;
    187        }
    188        s->playback_mode = value;
    189        mv88w8618_audio_clock_update(s);
    190        break;
    191
    192    case MP_AUDIO_CLOCK_DIV:
    193        s->clock_div = value;
    194        s->last_free = 0;
    195        s->play_pos = 0;
    196        mv88w8618_audio_clock_update(s);
    197        break;
    198
    199    case MP_AUDIO_IRQ_STATUS:
    200        s->status &= ~value;
    201        break;
    202
    203    case MP_AUDIO_IRQ_ENABLE:
    204        s->irq_enable = value;
    205        if (s->status & s->irq_enable) {
    206            qemu_irq_raise(s->irq);
    207        }
    208        break;
    209
    210    case MP_AUDIO_TX_START_LO:
    211        s->phys_buf = (s->phys_buf & 0xFFFF0000) | (value & 0xFFFF);
    212        s->target_buffer = s->phys_buf;
    213        s->play_pos = 0;
    214        s->last_free = 0;
    215        break;
    216
    217    case MP_AUDIO_TX_THRESHOLD:
    218        s->threshold = (value + 1) * 4;
    219        break;
    220
    221    case MP_AUDIO_TX_START_HI:
    222        s->phys_buf = (s->phys_buf & 0xFFFF) | (value << 16);
    223        s->target_buffer = s->phys_buf;
    224        s->play_pos = 0;
    225        s->last_free = 0;
    226        break;
    227    }
    228}
    229
    230static void mv88w8618_audio_reset(DeviceState *d)
    231{
    232    mv88w8618_audio_state *s = MV88W8618_AUDIO(d);
    233
    234    s->playback_mode = 0;
    235    s->status = 0;
    236    s->irq_enable = 0;
    237    s->clock_div = 0;
    238    s->threshold = 0;
    239    s->phys_buf = 0;
    240}
    241
    242static const MemoryRegionOps mv88w8618_audio_ops = {
    243    .read = mv88w8618_audio_read,
    244    .write = mv88w8618_audio_write,
    245    .endianness = DEVICE_NATIVE_ENDIAN,
    246};
    247
    248static void mv88w8618_audio_init(Object *obj)
    249{
    250    SysBusDevice *dev = SYS_BUS_DEVICE(obj);
    251    mv88w8618_audio_state *s = MV88W8618_AUDIO(dev);
    252
    253    sysbus_init_irq(dev, &s->irq);
    254
    255    memory_region_init_io(&s->iomem, obj, &mv88w8618_audio_ops, s,
    256                          "audio", MP_AUDIO_SIZE);
    257    sysbus_init_mmio(dev, &s->iomem);
    258
    259    object_property_add_link(OBJECT(dev), "wm8750", TYPE_WM8750,
    260                             (Object **) &s->wm,
    261                             qdev_prop_allow_set_link_before_realize,
    262                             0);
    263}
    264
    265static void mv88w8618_audio_realize(DeviceState *dev, Error **errp)
    266{
    267    mv88w8618_audio_state *s = MV88W8618_AUDIO(dev);
    268
    269    wm8750_data_req_set(s->wm, mv88w8618_audio_callback, s);
    270}
    271
    272static const VMStateDescription mv88w8618_audio_vmsd = {
    273    .name = "mv88w8618_audio",
    274    .version_id = 1,
    275    .minimum_version_id = 1,
    276    .fields = (VMStateField[]) {
    277        VMSTATE_UINT32(playback_mode, mv88w8618_audio_state),
    278        VMSTATE_UINT32(status, mv88w8618_audio_state),
    279        VMSTATE_UINT32(irq_enable, mv88w8618_audio_state),
    280        VMSTATE_UINT32(phys_buf, mv88w8618_audio_state),
    281        VMSTATE_UINT32(target_buffer, mv88w8618_audio_state),
    282        VMSTATE_UINT32(threshold, mv88w8618_audio_state),
    283        VMSTATE_UINT32(play_pos, mv88w8618_audio_state),
    284        VMSTATE_UINT32(last_free, mv88w8618_audio_state),
    285        VMSTATE_UINT32(clock_div, mv88w8618_audio_state),
    286        VMSTATE_END_OF_LIST()
    287    }
    288};
    289
    290static void mv88w8618_audio_class_init(ObjectClass *klass, void *data)
    291{
    292    DeviceClass *dc = DEVICE_CLASS(klass);
    293
    294    dc->realize = mv88w8618_audio_realize;
    295    dc->reset = mv88w8618_audio_reset;
    296    dc->vmsd = &mv88w8618_audio_vmsd;
    297    dc->user_creatable = false;
    298}
    299
    300static const TypeInfo mv88w8618_audio_info = {
    301    .name          = TYPE_MV88W8618_AUDIO,
    302    .parent        = TYPE_SYS_BUS_DEVICE,
    303    .instance_size = sizeof(mv88w8618_audio_state),
    304    .instance_init = mv88w8618_audio_init,
    305    .class_init    = mv88w8618_audio_class_init,
    306};
    307
    308static void mv88w8618_register_types(void)
    309{
    310    type_register_static(&mv88w8618_audio_info);
    311}
    312
    313type_init(mv88w8618_register_types)