qemu

Форк
0
/
marvell_88w8618.c 
313 строк · 8.6 Кб
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

46
OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_audio_state, MV88W8618_AUDIO)
47

48
struct 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

65
static 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

135
static 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

149
static 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

175
static 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

230
static 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

242
static const MemoryRegionOps mv88w8618_audio_ops = {
243
    .read = mv88w8618_audio_read,
244
    .write = mv88w8618_audio_write,
245
    .endianness = DEVICE_NATIVE_ENDIAN,
246
};
247

248
static 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

265
static 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

272
static const VMStateDescription mv88w8618_audio_vmsd = {
273
    .name = "mv88w8618_audio",
274
    .version_id = 1,
275
    .minimum_version_id = 1,
276
    .fields = (const 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

290
static 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

300
static 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

308
static void mv88w8618_register_types(void)
309
{
310
    type_register_static(&mv88w8618_audio_info);
311
}
312

313
type_init(mv88w8618_register_types)
314

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.