qemu

Форк
0
/
via-ac97.c 
538 строк · 15.8 Кб
1
/*
2
 * VIA south bridges sound support
3
 *
4
 * Copyright (c) 2022-2023 BALATON Zoltan
5
 *
6
 * This work is licensed under the GNU GPL license version 2 or later.
7
 */
8

9
/*
10
 * TODO: This is only a basic implementation of one audio playback channel
11
 *       more functionality should be added here.
12
 */
13

14
#include "qemu/osdep.h"
15
#include "qemu/log.h"
16
#include "hw/isa/vt82c686.h"
17
#include "ac97.h"
18
#include "trace.h"
19

20
#define CLEN_IS_EOL(x)  ((x)->clen & BIT(31))
21
#define CLEN_IS_FLAG(x) ((x)->clen & BIT(30))
22
#define CLEN_IS_STOP(x) ((x)->clen & BIT(29))
23
#define CLEN_LEN(x)     ((x)->clen & 0xffffff)
24

25
#define STAT_ACTIVE BIT(7)
26
#define STAT_PAUSED BIT(6)
27
#define STAT_TRIG   BIT(3)
28
#define STAT_STOP   BIT(2)
29
#define STAT_EOL    BIT(1)
30
#define STAT_FLAG   BIT(0)
31

32
#define CNTL_START  BIT(7)
33
#define CNTL_TERM   BIT(6)
34
#define CNTL_PAUSE  BIT(3)
35

36
static void open_voice_out(ViaAC97State *s);
37

38
static uint16_t codec_rates[] = { 8000, 11025, 16000, 22050, 32000, 44100,
39
                                  48000 };
40

41
#define CODEC_REG(s, o)  ((s)->codec_regs[(o) / 2])
42
#define CODEC_VOL(vol, mask)  ((255 * ((vol) & mask)) / mask)
43

44
static void codec_volume_set_out(ViaAC97State *s)
45
{
46
    int lvol, rvol, mute;
47

48
    lvol = 255 - CODEC_VOL(CODEC_REG(s, AC97_Master_Volume_Mute) >> 8, 0x1f);
49
    lvol *= 255 - CODEC_VOL(CODEC_REG(s, AC97_PCM_Out_Volume_Mute) >> 8, 0x1f);
50
    lvol /= 255;
51
    rvol = 255 - CODEC_VOL(CODEC_REG(s, AC97_Master_Volume_Mute), 0x1f);
52
    rvol *= 255 - CODEC_VOL(CODEC_REG(s, AC97_PCM_Out_Volume_Mute), 0x1f);
53
    rvol /= 255;
54
    mute = CODEC_REG(s, AC97_Master_Volume_Mute) >> MUTE_SHIFT;
55
    mute |= CODEC_REG(s, AC97_PCM_Out_Volume_Mute) >> MUTE_SHIFT;
56
    AUD_set_volume_out(s->vo, mute, lvol, rvol);
57
}
58

59
static void codec_reset(ViaAC97State *s)
60
{
61
    memset(s->codec_regs, 0, sizeof(s->codec_regs));
62
    CODEC_REG(s, AC97_Reset) = 0x6a90;
63
    CODEC_REG(s, AC97_Master_Volume_Mute) = 0x8000;
64
    CODEC_REG(s, AC97_Headphone_Volume_Mute) = 0x8000;
65
    CODEC_REG(s, AC97_Master_Volume_Mono_Mute) = 0x8000;
66
    CODEC_REG(s, AC97_Phone_Volume_Mute) = 0x8008;
67
    CODEC_REG(s, AC97_Mic_Volume_Mute) = 0x8008;
68
    CODEC_REG(s, AC97_Line_In_Volume_Mute) = 0x8808;
69
    CODEC_REG(s, AC97_CD_Volume_Mute) = 0x8808;
70
    CODEC_REG(s, AC97_Video_Volume_Mute) = 0x8808;
71
    CODEC_REG(s, AC97_Aux_Volume_Mute) = 0x8808;
72
    CODEC_REG(s, AC97_PCM_Out_Volume_Mute) = 0x8808;
73
    CODEC_REG(s, AC97_Record_Gain_Mute) = 0x8000;
74
    CODEC_REG(s, AC97_Powerdown_Ctrl_Stat) = 0x000f;
75
    CODEC_REG(s, AC97_Extended_Audio_ID) = 0x0a05;
76
    CODEC_REG(s, AC97_Extended_Audio_Ctrl_Stat) = 0x0400;
77
    CODEC_REG(s, AC97_PCM_Front_DAC_Rate) = 48000;
78
    CODEC_REG(s, AC97_PCM_LR_ADC_Rate) = 48000;
79
    /* Sigmatel 9766 (STAC9766) */
80
    CODEC_REG(s, AC97_Vendor_ID1) = 0x8384;
81
    CODEC_REG(s, AC97_Vendor_ID2) = 0x7666;
82
}
83

84
static uint16_t codec_read(ViaAC97State *s, uint8_t addr)
85
{
86
    return CODEC_REG(s, addr);
87
}
88

89
static void codec_write(ViaAC97State *s, uint8_t addr, uint16_t val)
90
{
91
    trace_via_ac97_codec_write(addr, val);
92
    switch (addr) {
93
    case AC97_Reset:
94
        codec_reset(s);
95
        return;
96
    case AC97_Master_Volume_Mute:
97
    case AC97_PCM_Out_Volume_Mute:
98
        if (addr == AC97_Master_Volume_Mute) {
99
            if (val & BIT(13)) {
100
                val |= 0x1f00;
101
            }
102
            if (val & BIT(5)) {
103
                val |= 0x1f;
104
            }
105
        }
106
        CODEC_REG(s, addr) = val & 0x9f1f;
107
        codec_volume_set_out(s);
108
        return;
109
    case AC97_Extended_Audio_Ctrl_Stat:
110
        CODEC_REG(s, addr) &= ~EACS_VRA;
111
        CODEC_REG(s, addr) |= val & EACS_VRA;
112
        if (!(val & EACS_VRA)) {
113
            CODEC_REG(s, AC97_PCM_Front_DAC_Rate) = 48000;
114
            CODEC_REG(s, AC97_PCM_LR_ADC_Rate) = 48000;
115
            open_voice_out(s);
116
        }
117
        return;
118
    case AC97_PCM_Front_DAC_Rate:
119
    case AC97_PCM_LR_ADC_Rate:
120
        if (CODEC_REG(s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) {
121
            int i;
122
            uint16_t rate = val;
123

124
            for (i = 0; i < ARRAY_SIZE(codec_rates) - 1; i++) {
125
                if (rate < codec_rates[i] +
126
                    (codec_rates[i + 1] - codec_rates[i]) / 2) {
127
                    rate = codec_rates[i];
128
                    break;
129
                }
130
            }
131
            if (rate > 48000) {
132
                rate = 48000;
133
            }
134
            CODEC_REG(s, addr) = rate;
135
            open_voice_out(s);
136
        }
137
        return;
138
    case AC97_Powerdown_Ctrl_Stat:
139
        CODEC_REG(s, addr) = (val & 0xff00) | (CODEC_REG(s, addr) & 0xff);
140
        return;
141
    case AC97_Extended_Audio_ID:
142
    case AC97_Vendor_ID1:
143
    case AC97_Vendor_ID2:
144
        /* Read only registers */
145
        return;
146
    default:
147
        qemu_log_mask(LOG_UNIMP,
148
                      "via-ac97: Unimplemented codec register 0x%x\n", addr);
149
        CODEC_REG(s, addr) = val;
150
    }
151
}
152

153
static void fetch_sgd(ViaAC97SGDChannel *c, PCIDevice *d)
154
{
155
    uint32_t b[2];
156

157
    if (c->curr < c->base) {
158
        c->curr = c->base;
159
    }
160
    if (unlikely(pci_dma_read(d, c->curr, b, sizeof(b)) != MEMTX_OK)) {
161
        qemu_log_mask(LOG_GUEST_ERROR,
162
                      "via-ac97: DMA error reading SGD table\n");
163
        return;
164
    }
165
    c->addr = le32_to_cpu(b[0]);
166
    c->clen = le32_to_cpu(b[1]);
167
    trace_via_ac97_sgd_fetch(c->curr, c->addr, CLEN_IS_STOP(c) ? 'S' : '-',
168
                             CLEN_IS_EOL(c) ? 'E' : '-',
169
                             CLEN_IS_FLAG(c) ? 'F' : '-', CLEN_LEN(c));
170
}
171

172
static void out_cb(void *opaque, int avail)
173
{
174
    ViaAC97State *s = opaque;
175
    ViaAC97SGDChannel *c = &s->aur;
176
    int temp, to_copy, copied;
177
    bool stop = false;
178
    uint8_t tmpbuf[4096];
179

180
    if (c->stat & STAT_PAUSED) {
181
        return;
182
    }
183
    c->stat |= STAT_ACTIVE;
184
    while (avail && !stop) {
185
        if (!c->clen) {
186
            fetch_sgd(c, &s->dev);
187
        }
188
        temp = MIN(CLEN_LEN(c), avail);
189
        while (temp) {
190
            to_copy = MIN(temp, sizeof(tmpbuf));
191
            pci_dma_read(&s->dev, c->addr, tmpbuf, to_copy);
192
            copied = AUD_write(s->vo, tmpbuf, to_copy);
193
            if (!copied) {
194
                stop = true;
195
                break;
196
            }
197
            temp -= copied;
198
            avail -= copied;
199
            c->addr += copied;
200
            c->clen -= copied;
201
        }
202
        if (CLEN_LEN(c) == 0) {
203
            c->curr += 8;
204
            if (CLEN_IS_EOL(c)) {
205
                c->stat |= STAT_EOL;
206
                if (c->type & CNTL_START) {
207
                    c->curr = c->base;
208
                    c->stat |= STAT_PAUSED;
209
                } else {
210
                    c->stat &= ~STAT_ACTIVE;
211
                    AUD_set_active_out(s->vo, 0);
212
                }
213
                if (c->type & STAT_EOL) {
214
                    via_isa_set_irq(&s->dev, 0, 1);
215
                }
216
            }
217
            if (CLEN_IS_FLAG(c)) {
218
                c->stat |= STAT_FLAG;
219
                c->stat |= STAT_PAUSED;
220
                if (c->type & STAT_FLAG) {
221
                    via_isa_set_irq(&s->dev, 0, 1);
222
                }
223
            }
224
            if (CLEN_IS_STOP(c)) {
225
                c->stat |= STAT_STOP;
226
                c->stat |= STAT_PAUSED;
227
            }
228
            c->clen = 0;
229
            stop = true;
230
        }
231
    }
232
}
233

234
static void open_voice_out(ViaAC97State *s)
235
{
236
    struct audsettings as = {
237
        .freq = CODEC_REG(s, AC97_PCM_Front_DAC_Rate),
238
        .nchannels = s->aur.type & BIT(4) ? 2 : 1,
239
        .fmt = s->aur.type & BIT(5) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_S8,
240
        .endianness = 0,
241
    };
242
    s->vo = AUD_open_out(&s->card, s->vo, "via-ac97.out", s, out_cb, &as);
243
}
244

245
static uint64_t sgd_read(void *opaque, hwaddr addr, unsigned size)
246
{
247
    ViaAC97State *s = opaque;
248
    uint64_t val = 0;
249

250
    switch (addr) {
251
    case 0:
252
        val = s->aur.stat;
253
        if (s->aur.type & CNTL_START) {
254
            val |= STAT_TRIG;
255
        }
256
        break;
257
    case 1:
258
        val = s->aur.stat & STAT_PAUSED ? BIT(3) : 0;
259
        break;
260
    case 2:
261
        val = s->aur.type;
262
        break;
263
    case 4:
264
        val = s->aur.curr;
265
        break;
266
    case 0xc:
267
        val = CLEN_LEN(&s->aur);
268
        break;
269
    case 0x10:
270
        /* silence unimplemented log message that happens at every IRQ */
271
        break;
272
    case 0x80:
273
        val = s->ac97_cmd;
274
        break;
275
    case 0x84:
276
        val = s->aur.stat & STAT_FLAG;
277
        if (s->aur.stat & STAT_EOL) {
278
            val |= BIT(4);
279
        }
280
        if (s->aur.stat & STAT_STOP) {
281
            val |= BIT(8);
282
        }
283
        if (s->aur.stat & STAT_ACTIVE) {
284
            val |= BIT(12);
285
        }
286
        break;
287
    default:
288
        qemu_log_mask(LOG_UNIMP, "via-ac97: Unimplemented register read 0x%"
289
                      HWADDR_PRIx"\n", addr);
290
    }
291
    trace_via_ac97_sgd_read(addr, size, val);
292
    return val;
293
}
294

295
static void sgd_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
296
{
297
    ViaAC97State *s = opaque;
298

299
    trace_via_ac97_sgd_write(addr, size, val);
300
    switch (addr) {
301
    case 0:
302
        if (val & STAT_STOP) {
303
            s->aur.stat &= ~STAT_PAUSED;
304
        }
305
        if (val & STAT_EOL) {
306
            s->aur.stat &= ~(STAT_EOL | STAT_PAUSED);
307
            if (s->aur.type & STAT_EOL) {
308
                via_isa_set_irq(&s->dev, 0, 0);
309
            }
310
        }
311
        if (val & STAT_FLAG) {
312
            s->aur.stat &= ~(STAT_FLAG | STAT_PAUSED);
313
            if (s->aur.type & STAT_FLAG) {
314
                via_isa_set_irq(&s->dev, 0, 0);
315
            }
316
        }
317
        break;
318
    case 1:
319
        if (val & CNTL_START) {
320
            AUD_set_active_out(s->vo, 1);
321
            s->aur.stat = STAT_ACTIVE;
322
        }
323
        if (val & CNTL_TERM) {
324
            AUD_set_active_out(s->vo, 0);
325
            s->aur.stat &= ~(STAT_ACTIVE | STAT_PAUSED);
326
            s->aur.clen = 0;
327
        }
328
        if (val & CNTL_PAUSE) {
329
            AUD_set_active_out(s->vo, 0);
330
            s->aur.stat &= ~STAT_ACTIVE;
331
            s->aur.stat |= STAT_PAUSED;
332
        } else if (!(val & CNTL_PAUSE) && (s->aur.stat & STAT_PAUSED)) {
333
            AUD_set_active_out(s->vo, 1);
334
            s->aur.stat |= STAT_ACTIVE;
335
            s->aur.stat &= ~STAT_PAUSED;
336
        }
337
        break;
338
    case 2:
339
    {
340
        uint32_t oldval = s->aur.type;
341
        s->aur.type = val;
342
        if ((oldval & 0x30) != (val & 0x30)) {
343
            open_voice_out(s);
344
        }
345
        break;
346
    }
347
    case 4:
348
        s->aur.base = val & ~1ULL;
349
        s->aur.curr = s->aur.base;
350
        break;
351
    case 0x80:
352
        if (val >> 30) {
353
            /* we only have primary codec */
354
            break;
355
        }
356
        if (val & BIT(23)) { /* read reg */
357
            s->ac97_cmd = val & 0xc0ff0000ULL;
358
            s->ac97_cmd |= codec_read(s, (val >> 16) & 0x7f);
359
            s->ac97_cmd |= BIT(25); /* data valid */
360
        } else {
361
            s->ac97_cmd = val & 0xc0ffffffULL;
362
            codec_write(s, (val >> 16) & 0x7f, val);
363
        }
364
        break;
365
    case 0xc:
366
    case 0x84:
367
        /* Read only */
368
        break;
369
    default:
370
        qemu_log_mask(LOG_UNIMP, "via-ac97: Unimplemented register write 0x%"
371
                      HWADDR_PRIx"\n", addr);
372
    }
373
}
374

375
static const MemoryRegionOps sgd_ops = {
376
    .read = sgd_read,
377
    .write = sgd_write,
378
    .endianness = DEVICE_LITTLE_ENDIAN,
379
};
380

381
static uint64_t fm_read(void *opaque, hwaddr addr, unsigned size)
382
{
383
    qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d\n", __func__, addr, size);
384
    return 0;
385
}
386

387
static void fm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
388
{
389
    qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d <= 0x%"PRIX64"\n",
390
                  __func__, addr, size, val);
391
}
392

393
static const MemoryRegionOps fm_ops = {
394
    .read = fm_read,
395
    .write = fm_write,
396
    .endianness = DEVICE_LITTLE_ENDIAN,
397
};
398

399
static uint64_t midi_read(void *opaque, hwaddr addr, unsigned size)
400
{
401
    qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d\n", __func__, addr, size);
402
    return 0;
403
}
404

405
static void midi_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
406
{
407
    qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d <= 0x%"PRIX64"\n",
408
                  __func__, addr, size, val);
409
}
410

411
static const MemoryRegionOps midi_ops = {
412
    .read = midi_read,
413
    .write = midi_write,
414
    .endianness = DEVICE_LITTLE_ENDIAN,
415
};
416

417
static void via_ac97_reset(DeviceState *dev)
418
{
419
    ViaAC97State *s = VIA_AC97(dev);
420

421
    codec_reset(s);
422
}
423

424
static void via_ac97_realize(PCIDevice *pci_dev, Error **errp)
425
{
426
    ViaAC97State *s = VIA_AC97(pci_dev);
427
    Object *o = OBJECT(s);
428

429
    if (!AUD_register_card ("via-ac97", &s->card, errp)) {
430
        return;
431
    }
432

433
    /*
434
     * Command register Bus Master bit is documented to be fixed at 0 but it's
435
     * needed for PCI DMA to work in QEMU. The pegasos2 firmware writes 0 here
436
     * and the AmigaOS driver writes 1 only enabling IO bit which works on
437
     * real hardware. So set it here and fix it to 1 to allow DMA.
438
     */
439
    pci_set_word(pci_dev->config + PCI_COMMAND, PCI_COMMAND_MASTER);
440
    pci_set_word(pci_dev->wmask + PCI_COMMAND, PCI_COMMAND_IO);
441
    pci_set_word(pci_dev->config + PCI_STATUS,
442
                 PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_MEDIUM);
443
    pci_set_long(pci_dev->config + PCI_INTERRUPT_PIN, 0x03);
444
    pci_set_byte(pci_dev->config + 0x40, 1); /* codec ready */
445

446
    memory_region_init_io(&s->sgd, o, &sgd_ops, s, "via-ac97.sgd", 256);
447
    pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->sgd);
448
    memory_region_init_io(&s->fm, o, &fm_ops, s, "via-ac97.fm", 4);
449
    pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->fm);
450
    memory_region_init_io(&s->midi, o, &midi_ops, s, "via-ac97.midi", 4);
451
    pci_register_bar(pci_dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &s->midi);
452
}
453

454
static void via_ac97_exit(PCIDevice *dev)
455
{
456
    ViaAC97State *s = VIA_AC97(dev);
457

458
    AUD_close_out(&s->card, s->vo);
459
    AUD_remove_card(&s->card);
460
}
461

462
static Property via_ac97_properties[] = {
463
    DEFINE_AUDIO_PROPERTIES(ViaAC97State, card),
464
    DEFINE_PROP_END_OF_LIST(),
465
};
466

467
static void via_ac97_class_init(ObjectClass *klass, void *data)
468
{
469
    DeviceClass *dc = DEVICE_CLASS(klass);
470
    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
471

472
    k->realize = via_ac97_realize;
473
    k->exit = via_ac97_exit;
474
    k->vendor_id = PCI_VENDOR_ID_VIA;
475
    k->device_id = PCI_DEVICE_ID_VIA_AC97;
476
    k->revision = 0x50;
477
    k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
478
    device_class_set_props(dc, via_ac97_properties);
479
    set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
480
    dc->desc = "VIA AC97";
481
    dc->reset = via_ac97_reset;
482
    /* Reason: Part of a south bridge chip */
483
    dc->user_creatable = false;
484
}
485

486
static const TypeInfo via_ac97_info = {
487
    .name          = TYPE_VIA_AC97,
488
    .parent        = TYPE_PCI_DEVICE,
489
    .instance_size = sizeof(ViaAC97State),
490
    .class_init    = via_ac97_class_init,
491
    .interfaces = (InterfaceInfo[]) {
492
        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
493
        { },
494
    },
495
};
496

497
static void via_mc97_realize(PCIDevice *pci_dev, Error **errp)
498
{
499
    pci_set_word(pci_dev->config + PCI_COMMAND,
500
                 PCI_COMMAND_INVALIDATE | PCI_COMMAND_VGA_PALETTE);
501
    pci_set_word(pci_dev->config + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
502
    pci_set_long(pci_dev->config + PCI_INTERRUPT_PIN, 0x03);
503
}
504

505
static void via_mc97_class_init(ObjectClass *klass, void *data)
506
{
507
    DeviceClass *dc = DEVICE_CLASS(klass);
508
    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
509

510
    k->realize = via_mc97_realize;
511
    k->vendor_id = PCI_VENDOR_ID_VIA;
512
    k->device_id = PCI_DEVICE_ID_VIA_MC97;
513
    k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
514
    k->revision = 0x30;
515
    set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
516
    dc->desc = "VIA MC97";
517
    /* Reason: Part of a south bridge chip */
518
    dc->user_creatable = false;
519
}
520

521
static const TypeInfo via_mc97_info = {
522
    .name          = TYPE_VIA_MC97,
523
    .parent        = TYPE_PCI_DEVICE,
524
    .instance_size = sizeof(PCIDevice),
525
    .class_init    = via_mc97_class_init,
526
    .interfaces = (InterfaceInfo[]) {
527
        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
528
        { },
529
    },
530
};
531

532
static void via_ac97_register_types(void)
533
{
534
    type_register_static(&via_ac97_info);
535
    type_register_static(&via_mc97_info);
536
}
537

538
type_init(via_ac97_register_types)
539

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

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

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

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