149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini * Copyright (C) 2006 InnoTek Systemberatung GmbH
349ab747fSPaolo Bonzini *
449ab747fSPaolo Bonzini * This file is part of VirtualBox Open Source Edition (OSE), as
549ab747fSPaolo Bonzini * available from http://www.virtualbox.org. This file is free software;
649ab747fSPaolo Bonzini * you can redistribute it and/or modify it under the terms of the GNU
749ab747fSPaolo Bonzini * General Public License as published by the Free Software Foundation,
849ab747fSPaolo Bonzini * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
949ab747fSPaolo Bonzini * distribution. VirtualBox OSE is distributed in the hope that it will
1049ab747fSPaolo Bonzini * be useful, but WITHOUT ANY WARRANTY of any kind.
1149ab747fSPaolo Bonzini *
1249ab747fSPaolo Bonzini * If you received this file as part of a commercial VirtualBox
1349ab747fSPaolo Bonzini * distribution, then only the terms of your commercial VirtualBox
1449ab747fSPaolo Bonzini * license agreement apply instead of the previous paragraph.
1549ab747fSPaolo Bonzini *
1649ab747fSPaolo Bonzini * Contributions after 2012-01-13 are licensed under the terms of the
1749ab747fSPaolo Bonzini * GNU GPL, version 2 or (at your option) any later version.
1849ab747fSPaolo Bonzini */
1949ab747fSPaolo Bonzini
206086a565SPeter Maydell #include "qemu/osdep.h"
218a824e4dSEduardo Habkost #include "hw/audio/soundhw.h"
2249ab747fSPaolo Bonzini #include "audio/audio.h"
23edf5ca5dSMarkus Armbruster #include "hw/pci/pci_device.h"
24a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
25d6454270SMarkus Armbruster #include "migration/vmstate.h"
260b8fa32fSMarkus Armbruster #include "qemu/module.h"
2749ab747fSPaolo Bonzini #include "sysemu/dma.h"
28db1015e9SEduardo Habkost #include "qom/object.h"
29c272a724SBALATON Zoltan #include "ac97.h"
3049ab747fSPaolo Bonzini
3149ab747fSPaolo Bonzini #define SOFT_VOLUME
3249ab747fSPaolo Bonzini #define SR_FIFOE 16 /* rwc */
3349ab747fSPaolo Bonzini #define SR_BCIS 8 /* rwc */
3449ab747fSPaolo Bonzini #define SR_LVBCI 4 /* rwc */
3549ab747fSPaolo Bonzini #define SR_CELV 2 /* ro */
3649ab747fSPaolo Bonzini #define SR_DCH 1 /* ro */
3749ab747fSPaolo Bonzini #define SR_VALID_MASK ((1 << 5) - 1)
3849ab747fSPaolo Bonzini #define SR_WCLEAR_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
3949ab747fSPaolo Bonzini #define SR_RO_MASK (SR_DCH | SR_CELV)
4049ab747fSPaolo Bonzini #define SR_INT_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
4149ab747fSPaolo Bonzini
4249ab747fSPaolo Bonzini #define CR_IOCE 16 /* rw */
4349ab747fSPaolo Bonzini #define CR_FEIE 8 /* rw */
4449ab747fSPaolo Bonzini #define CR_LVBIE 4 /* rw */
4549ab747fSPaolo Bonzini #define CR_RR 2 /* rw */
4649ab747fSPaolo Bonzini #define CR_RPBM 1 /* rw */
4749ab747fSPaolo Bonzini #define CR_VALID_MASK ((1 << 5) - 1)
4849ab747fSPaolo Bonzini #define CR_DONT_CLEAR_MASK (CR_IOCE | CR_FEIE | CR_LVBIE)
4949ab747fSPaolo Bonzini
5049ab747fSPaolo Bonzini #define GC_WR 4 /* rw */
5149ab747fSPaolo Bonzini #define GC_CR 2 /* rw */
5249ab747fSPaolo Bonzini #define GC_VALID_MASK ((1 << 6) - 1)
5349ab747fSPaolo Bonzini
5449ab747fSPaolo Bonzini #define GS_MD3 (1 << 17) /* rw */
5549ab747fSPaolo Bonzini #define GS_AD3 (1 << 16) /* rw */
5649ab747fSPaolo Bonzini #define GS_RCS (1 << 15) /* rwc */
5749ab747fSPaolo Bonzini #define GS_B3S12 (1 << 14) /* ro */
5849ab747fSPaolo Bonzini #define GS_B2S12 (1 << 13) /* ro */
5949ab747fSPaolo Bonzini #define GS_B1S12 (1 << 12) /* ro */
6049ab747fSPaolo Bonzini #define GS_S1R1 (1 << 11) /* rwc */
6149ab747fSPaolo Bonzini #define GS_S0R1 (1 << 10) /* rwc */
6249ab747fSPaolo Bonzini #define GS_S1CR (1 << 9) /* ro */
6349ab747fSPaolo Bonzini #define GS_S0CR (1 << 8) /* ro */
6449ab747fSPaolo Bonzini #define GS_MINT (1 << 7) /* ro */
6549ab747fSPaolo Bonzini #define GS_POINT (1 << 6) /* ro */
6649ab747fSPaolo Bonzini #define GS_PIINT (1 << 5) /* ro */
6749ab747fSPaolo Bonzini #define GS_RSRVD ((1 << 4) | (1 << 3))
6849ab747fSPaolo Bonzini #define GS_MOINT (1 << 2) /* ro */
6949ab747fSPaolo Bonzini #define GS_MIINT (1 << 1) /* ro */
7049ab747fSPaolo Bonzini #define GS_GSCI 1 /* rwc */
7149ab747fSPaolo Bonzini #define GS_RO_MASK (GS_B3S12 | \
7249ab747fSPaolo Bonzini GS_B2S12 | \
7349ab747fSPaolo Bonzini GS_B1S12 | \
7449ab747fSPaolo Bonzini GS_S1CR | \
7549ab747fSPaolo Bonzini GS_S0CR | \
7649ab747fSPaolo Bonzini GS_MINT | \
7749ab747fSPaolo Bonzini GS_POINT | \
7849ab747fSPaolo Bonzini GS_PIINT | \
7949ab747fSPaolo Bonzini GS_RSRVD | \
8049ab747fSPaolo Bonzini GS_MOINT | \
8149ab747fSPaolo Bonzini GS_MIINT)
8249ab747fSPaolo Bonzini #define GS_VALID_MASK ((1 << 18) - 1)
8349ab747fSPaolo Bonzini #define GS_WCLEAR_MASK (GS_RCS | GS_S1R1 | GS_S0R1 | GS_GSCI)
8449ab747fSPaolo Bonzini
8549ab747fSPaolo Bonzini #define BD_IOC (1 << 31)
8649ab747fSPaolo Bonzini #define BD_BUP (1 << 30)
8749ab747fSPaolo Bonzini
88417d430eSLi Qiang #define TYPE_AC97 "AC97"
898063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(AC97LinkState, AC97)
90417d430eSLi Qiang
9149ab747fSPaolo Bonzini #define REC_MASK 7
9249ab747fSPaolo Bonzini enum {
9349ab747fSPaolo Bonzini REC_MIC = 0,
9449ab747fSPaolo Bonzini REC_CD,
9549ab747fSPaolo Bonzini REC_VIDEO,
9649ab747fSPaolo Bonzini REC_AUX,
9749ab747fSPaolo Bonzini REC_LINE_IN,
9849ab747fSPaolo Bonzini REC_STEREO_MIX,
9949ab747fSPaolo Bonzini REC_MONO_MIX,
10049ab747fSPaolo Bonzini REC_PHONE
10149ab747fSPaolo Bonzini };
10249ab747fSPaolo Bonzini
10349ab747fSPaolo Bonzini typedef struct BD {
10449ab747fSPaolo Bonzini uint32_t addr;
10549ab747fSPaolo Bonzini uint32_t ctl_len;
10649ab747fSPaolo Bonzini } BD;
10749ab747fSPaolo Bonzini
10849ab747fSPaolo Bonzini typedef struct AC97BusMasterRegs {
10949ab747fSPaolo Bonzini uint32_t bdbar; /* rw 0 */
11049ab747fSPaolo Bonzini uint8_t civ; /* ro 0 */
11149ab747fSPaolo Bonzini uint8_t lvi; /* rw 0 */
11249ab747fSPaolo Bonzini uint16_t sr; /* rw 1 */
11349ab747fSPaolo Bonzini uint16_t picb; /* ro 0 */
11449ab747fSPaolo Bonzini uint8_t piv; /* ro 0 */
11549ab747fSPaolo Bonzini uint8_t cr; /* rw 0 */
11649ab747fSPaolo Bonzini unsigned int bd_valid;
11749ab747fSPaolo Bonzini BD bd;
11849ab747fSPaolo Bonzini } AC97BusMasterRegs;
11949ab747fSPaolo Bonzini
120db1015e9SEduardo Habkost struct AC97LinkState {
12149ab747fSPaolo Bonzini PCIDevice dev;
12249ab747fSPaolo Bonzini QEMUSoundCard card;
12349ab747fSPaolo Bonzini uint32_t glob_cnt;
12449ab747fSPaolo Bonzini uint32_t glob_sta;
12549ab747fSPaolo Bonzini uint32_t cas;
12649ab747fSPaolo Bonzini uint32_t last_samp;
12749ab747fSPaolo Bonzini AC97BusMasterRegs bm_regs[3];
12849ab747fSPaolo Bonzini uint8_t mixer_data[256];
12949ab747fSPaolo Bonzini SWVoiceIn *voice_pi;
13049ab747fSPaolo Bonzini SWVoiceOut *voice_po;
13149ab747fSPaolo Bonzini SWVoiceIn *voice_mc;
13249ab747fSPaolo Bonzini int invalid_freq[3];
13349ab747fSPaolo Bonzini uint8_t silence[128];
13449ab747fSPaolo Bonzini int bup_flag;
13549ab747fSPaolo Bonzini MemoryRegion io_nam;
13649ab747fSPaolo Bonzini MemoryRegion io_nabm;
137db1015e9SEduardo Habkost };
13849ab747fSPaolo Bonzini
13949ab747fSPaolo Bonzini enum {
14049ab747fSPaolo Bonzini BUP_SET = 1,
14149ab747fSPaolo Bonzini BUP_LAST = 2
14249ab747fSPaolo Bonzini };
14349ab747fSPaolo Bonzini
14449ab747fSPaolo Bonzini #ifdef DEBUG_AC97
14549ab747fSPaolo Bonzini #define dolog(...) AUD_log("ac97", __VA_ARGS__)
14649ab747fSPaolo Bonzini #else
14749ab747fSPaolo Bonzini #define dolog(...)
14849ab747fSPaolo Bonzini #endif
14949ab747fSPaolo Bonzini
15049ab747fSPaolo Bonzini #define MKREGS(prefix, start) \
15149ab747fSPaolo Bonzini enum { \
15249ab747fSPaolo Bonzini prefix ## _BDBAR = start, \
15349ab747fSPaolo Bonzini prefix ## _CIV = start + 4, \
15449ab747fSPaolo Bonzini prefix ## _LVI = start + 5, \
15549ab747fSPaolo Bonzini prefix ## _SR = start + 6, \
15649ab747fSPaolo Bonzini prefix ## _PICB = start + 8, \
15749ab747fSPaolo Bonzini prefix ## _PIV = start + 10, \
15849ab747fSPaolo Bonzini prefix ## _CR = start + 11 \
15949ab747fSPaolo Bonzini }
16049ab747fSPaolo Bonzini
16149ab747fSPaolo Bonzini enum {
16249ab747fSPaolo Bonzini PI_INDEX = 0,
16349ab747fSPaolo Bonzini PO_INDEX,
16449ab747fSPaolo Bonzini MC_INDEX,
16549ab747fSPaolo Bonzini LAST_INDEX
16649ab747fSPaolo Bonzini };
16749ab747fSPaolo Bonzini
16849ab747fSPaolo Bonzini MKREGS(PI, PI_INDEX * 16);
16949ab747fSPaolo Bonzini MKREGS(PO, PO_INDEX * 16);
17049ab747fSPaolo Bonzini MKREGS(MC, MC_INDEX * 16);
17149ab747fSPaolo Bonzini
17249ab747fSPaolo Bonzini enum {
17349ab747fSPaolo Bonzini GLOB_CNT = 0x2c,
17449ab747fSPaolo Bonzini GLOB_STA = 0x30,
17549ab747fSPaolo Bonzini CAS = 0x34
17649ab747fSPaolo Bonzini };
17749ab747fSPaolo Bonzini
17849ab747fSPaolo Bonzini #define GET_BM(index) (((index) >> 4) & 3)
17949ab747fSPaolo Bonzini
18049ab747fSPaolo Bonzini static void po_callback(void *opaque, int free);
18149ab747fSPaolo Bonzini static void pi_callback(void *opaque, int avail);
18249ab747fSPaolo Bonzini static void mc_callback(void *opaque, int avail);
18349ab747fSPaolo Bonzini
fetch_bd(AC97LinkState * s,AC97BusMasterRegs * r)18449ab747fSPaolo Bonzini static void fetch_bd(AC97LinkState *s, AC97BusMasterRegs *r)
18549ab747fSPaolo Bonzini {
18649ab747fSPaolo Bonzini uint8_t b[8];
18749ab747fSPaolo Bonzini
18849ab747fSPaolo Bonzini pci_dma_read(&s->dev, r->bdbar + r->civ * 8, b, 8);
18949ab747fSPaolo Bonzini r->bd_valid = 1;
19049ab747fSPaolo Bonzini r->bd.addr = le32_to_cpu(*(uint32_t *) &b[0]) & ~3;
19149ab747fSPaolo Bonzini r->bd.ctl_len = le32_to_cpu(*(uint32_t *) &b[4]);
19249ab747fSPaolo Bonzini r->picb = r->bd.ctl_len & 0xffff;
193ab9f0f7dSBALATON Zoltan dolog("bd %2d addr=0x%x ctl=0x%06x len=0x%x(%d bytes)\n",
19449ab747fSPaolo Bonzini r->civ, r->bd.addr, r->bd.ctl_len >> 16,
195ab9f0f7dSBALATON Zoltan r->bd.ctl_len & 0xffff, (r->bd.ctl_len & 0xffff) << 1);
19649ab747fSPaolo Bonzini }
19749ab747fSPaolo Bonzini
update_sr(AC97LinkState * s,AC97BusMasterRegs * r,uint32_t new_sr)19849ab747fSPaolo Bonzini static void update_sr(AC97LinkState *s, AC97BusMasterRegs *r, uint32_t new_sr)
19949ab747fSPaolo Bonzini {
20049ab747fSPaolo Bonzini int event = 0;
20149ab747fSPaolo Bonzini int level = 0;
20249ab747fSPaolo Bonzini uint32_t new_mask = new_sr & SR_INT_MASK;
20349ab747fSPaolo Bonzini uint32_t old_mask = r->sr & SR_INT_MASK;
20449ab747fSPaolo Bonzini uint32_t masks[] = {GS_PIINT, GS_POINT, GS_MINT};
20549ab747fSPaolo Bonzini
20649ab747fSPaolo Bonzini if (new_mask ^ old_mask) {
20749ab747fSPaolo Bonzini /** @todo is IRQ deasserted when only one of status bits is cleared? */
20849ab747fSPaolo Bonzini if (!new_mask) {
20949ab747fSPaolo Bonzini event = 1;
21049ab747fSPaolo Bonzini level = 0;
211ab9f0f7dSBALATON Zoltan } else {
21249ab747fSPaolo Bonzini if ((new_mask & SR_LVBCI) && (r->cr & CR_LVBIE)) {
21349ab747fSPaolo Bonzini event = 1;
21449ab747fSPaolo Bonzini level = 1;
21549ab747fSPaolo Bonzini }
21649ab747fSPaolo Bonzini if ((new_mask & SR_BCIS) && (r->cr & CR_IOCE)) {
21749ab747fSPaolo Bonzini event = 1;
21849ab747fSPaolo Bonzini level = 1;
21949ab747fSPaolo Bonzini }
22049ab747fSPaolo Bonzini }
22149ab747fSPaolo Bonzini }
22249ab747fSPaolo Bonzini
22349ab747fSPaolo Bonzini r->sr = new_sr;
22449ab747fSPaolo Bonzini
225ab9f0f7dSBALATON Zoltan dolog("IOC%d LVB%d sr=0x%x event=%d level=%d\n",
226ab9f0f7dSBALATON Zoltan r->sr & SR_BCIS, r->sr & SR_LVBCI, r->sr, event, level);
22749ab747fSPaolo Bonzini
228ab9f0f7dSBALATON Zoltan if (!event) {
22949ab747fSPaolo Bonzini return;
230ab9f0f7dSBALATON Zoltan }
23149ab747fSPaolo Bonzini
23249ab747fSPaolo Bonzini if (level) {
23349ab747fSPaolo Bonzini s->glob_sta |= masks[r - s->bm_regs];
23449ab747fSPaolo Bonzini dolog("set irq level=1\n");
2359e64f8a3SMarcel Apfelbaum pci_irq_assert(&s->dev);
236ab9f0f7dSBALATON Zoltan } else {
23749ab747fSPaolo Bonzini s->glob_sta &= ~masks[r - s->bm_regs];
23849ab747fSPaolo Bonzini dolog("set irq level=0\n");
2399e64f8a3SMarcel Apfelbaum pci_irq_deassert(&s->dev);
24049ab747fSPaolo Bonzini }
24149ab747fSPaolo Bonzini }
24249ab747fSPaolo Bonzini
voice_set_active(AC97LinkState * s,int bm_index,int on)24349ab747fSPaolo Bonzini static void voice_set_active(AC97LinkState *s, int bm_index, int on)
24449ab747fSPaolo Bonzini {
24549ab747fSPaolo Bonzini switch (bm_index) {
24649ab747fSPaolo Bonzini case PI_INDEX:
24749ab747fSPaolo Bonzini AUD_set_active_in(s->voice_pi, on);
24849ab747fSPaolo Bonzini break;
24949ab747fSPaolo Bonzini
25049ab747fSPaolo Bonzini case PO_INDEX:
25149ab747fSPaolo Bonzini AUD_set_active_out(s->voice_po, on);
25249ab747fSPaolo Bonzini break;
25349ab747fSPaolo Bonzini
25449ab747fSPaolo Bonzini case MC_INDEX:
25549ab747fSPaolo Bonzini AUD_set_active_in(s->voice_mc, on);
25649ab747fSPaolo Bonzini break;
25749ab747fSPaolo Bonzini
25849ab747fSPaolo Bonzini default:
25949ab747fSPaolo Bonzini AUD_log("ac97", "invalid bm_index(%d) in voice_set_active", bm_index);
26049ab747fSPaolo Bonzini break;
26149ab747fSPaolo Bonzini }
26249ab747fSPaolo Bonzini }
26349ab747fSPaolo Bonzini
reset_bm_regs(AC97LinkState * s,AC97BusMasterRegs * r)26449ab747fSPaolo Bonzini static void reset_bm_regs(AC97LinkState *s, AC97BusMasterRegs *r)
26549ab747fSPaolo Bonzini {
26649ab747fSPaolo Bonzini dolog("reset_bm_regs\n");
26749ab747fSPaolo Bonzini r->bdbar = 0;
26849ab747fSPaolo Bonzini r->civ = 0;
26949ab747fSPaolo Bonzini r->lvi = 0;
27049ab747fSPaolo Bonzini /** todo do we need to do that? */
27149ab747fSPaolo Bonzini update_sr(s, r, SR_DCH);
27249ab747fSPaolo Bonzini r->picb = 0;
27349ab747fSPaolo Bonzini r->piv = 0;
27449ab747fSPaolo Bonzini r->cr = r->cr & CR_DONT_CLEAR_MASK;
27549ab747fSPaolo Bonzini r->bd_valid = 0;
27649ab747fSPaolo Bonzini
27749ab747fSPaolo Bonzini voice_set_active(s, r - s->bm_regs, 0);
27849ab747fSPaolo Bonzini memset(s->silence, 0, sizeof(s->silence));
27949ab747fSPaolo Bonzini }
28049ab747fSPaolo Bonzini
mixer_store(AC97LinkState * s,uint32_t i,uint16_t v)28149ab747fSPaolo Bonzini static void mixer_store(AC97LinkState *s, uint32_t i, uint16_t v)
28249ab747fSPaolo Bonzini {
28349ab747fSPaolo Bonzini if (i + 2 > sizeof(s->mixer_data)) {
28449ab747fSPaolo Bonzini dolog("mixer_store: index %d out of bounds %zd\n",
28549ab747fSPaolo Bonzini i, sizeof(s->mixer_data));
28649ab747fSPaolo Bonzini return;
28749ab747fSPaolo Bonzini }
28849ab747fSPaolo Bonzini
28949ab747fSPaolo Bonzini s->mixer_data[i + 0] = v & 0xff;
29049ab747fSPaolo Bonzini s->mixer_data[i + 1] = v >> 8;
29149ab747fSPaolo Bonzini }
29249ab747fSPaolo Bonzini
mixer_load(AC97LinkState * s,uint32_t i)29349ab747fSPaolo Bonzini static uint16_t mixer_load(AC97LinkState *s, uint32_t i)
29449ab747fSPaolo Bonzini {
29549ab747fSPaolo Bonzini uint16_t val = 0xffff;
29649ab747fSPaolo Bonzini
29749ab747fSPaolo Bonzini if (i + 2 > sizeof(s->mixer_data)) {
29849ab747fSPaolo Bonzini dolog("mixer_load: index %d out of bounds %zd\n",
29949ab747fSPaolo Bonzini i, sizeof(s->mixer_data));
300ab9f0f7dSBALATON Zoltan } else {
30149ab747fSPaolo Bonzini val = s->mixer_data[i + 0] | (s->mixer_data[i + 1] << 8);
30249ab747fSPaolo Bonzini }
30349ab747fSPaolo Bonzini
30449ab747fSPaolo Bonzini return val;
30549ab747fSPaolo Bonzini }
30649ab747fSPaolo Bonzini
open_voice(AC97LinkState * s,int index,int freq)30749ab747fSPaolo Bonzini static void open_voice(AC97LinkState *s, int index, int freq)
30849ab747fSPaolo Bonzini {
30949ab747fSPaolo Bonzini struct audsettings as;
31049ab747fSPaolo Bonzini
31149ab747fSPaolo Bonzini as.freq = freq;
31249ab747fSPaolo Bonzini as.nchannels = 2;
31385bc5852SKővágó, Zoltán as.fmt = AUDIO_FORMAT_S16;
31449ab747fSPaolo Bonzini as.endianness = 0;
31549ab747fSPaolo Bonzini
31649ab747fSPaolo Bonzini if (freq > 0) {
31749ab747fSPaolo Bonzini s->invalid_freq[index] = 0;
31849ab747fSPaolo Bonzini switch (index) {
31949ab747fSPaolo Bonzini case PI_INDEX:
32049ab747fSPaolo Bonzini s->voice_pi = AUD_open_in(
32149ab747fSPaolo Bonzini &s->card,
32249ab747fSPaolo Bonzini s->voice_pi,
32349ab747fSPaolo Bonzini "ac97.pi",
32449ab747fSPaolo Bonzini s,
32549ab747fSPaolo Bonzini pi_callback,
32649ab747fSPaolo Bonzini &as
32749ab747fSPaolo Bonzini );
32849ab747fSPaolo Bonzini break;
32949ab747fSPaolo Bonzini
33049ab747fSPaolo Bonzini case PO_INDEX:
33149ab747fSPaolo Bonzini s->voice_po = AUD_open_out(
33249ab747fSPaolo Bonzini &s->card,
33349ab747fSPaolo Bonzini s->voice_po,
33449ab747fSPaolo Bonzini "ac97.po",
33549ab747fSPaolo Bonzini s,
33649ab747fSPaolo Bonzini po_callback,
33749ab747fSPaolo Bonzini &as
33849ab747fSPaolo Bonzini );
33949ab747fSPaolo Bonzini break;
34049ab747fSPaolo Bonzini
34149ab747fSPaolo Bonzini case MC_INDEX:
34249ab747fSPaolo Bonzini s->voice_mc = AUD_open_in(
34349ab747fSPaolo Bonzini &s->card,
34449ab747fSPaolo Bonzini s->voice_mc,
34549ab747fSPaolo Bonzini "ac97.mc",
34649ab747fSPaolo Bonzini s,
34749ab747fSPaolo Bonzini mc_callback,
34849ab747fSPaolo Bonzini &as
34949ab747fSPaolo Bonzini );
35049ab747fSPaolo Bonzini break;
35149ab747fSPaolo Bonzini }
352ab9f0f7dSBALATON Zoltan } else {
35349ab747fSPaolo Bonzini s->invalid_freq[index] = freq;
35449ab747fSPaolo Bonzini switch (index) {
35549ab747fSPaolo Bonzini case PI_INDEX:
35649ab747fSPaolo Bonzini AUD_close_in(&s->card, s->voice_pi);
35749ab747fSPaolo Bonzini s->voice_pi = NULL;
35849ab747fSPaolo Bonzini break;
35949ab747fSPaolo Bonzini
36049ab747fSPaolo Bonzini case PO_INDEX:
36149ab747fSPaolo Bonzini AUD_close_out(&s->card, s->voice_po);
36249ab747fSPaolo Bonzini s->voice_po = NULL;
36349ab747fSPaolo Bonzini break;
36449ab747fSPaolo Bonzini
36549ab747fSPaolo Bonzini case MC_INDEX:
36649ab747fSPaolo Bonzini AUD_close_in(&s->card, s->voice_mc);
36749ab747fSPaolo Bonzini s->voice_mc = NULL;
36849ab747fSPaolo Bonzini break;
36949ab747fSPaolo Bonzini }
37049ab747fSPaolo Bonzini }
37149ab747fSPaolo Bonzini }
37249ab747fSPaolo Bonzini
reset_voices(AC97LinkState * s,uint8_t active[LAST_INDEX])37349ab747fSPaolo Bonzini static void reset_voices(AC97LinkState *s, uint8_t active[LAST_INDEX])
37449ab747fSPaolo Bonzini {
37549ab747fSPaolo Bonzini uint16_t freq;
37649ab747fSPaolo Bonzini
37749ab747fSPaolo Bonzini freq = mixer_load(s, AC97_PCM_LR_ADC_Rate);
37849ab747fSPaolo Bonzini open_voice(s, PI_INDEX, freq);
37949ab747fSPaolo Bonzini AUD_set_active_in(s->voice_pi, active[PI_INDEX]);
38049ab747fSPaolo Bonzini
38149ab747fSPaolo Bonzini freq = mixer_load(s, AC97_PCM_Front_DAC_Rate);
38249ab747fSPaolo Bonzini open_voice(s, PO_INDEX, freq);
38349ab747fSPaolo Bonzini AUD_set_active_out(s->voice_po, active[PO_INDEX]);
38449ab747fSPaolo Bonzini
38549ab747fSPaolo Bonzini freq = mixer_load(s, AC97_MIC_ADC_Rate);
38649ab747fSPaolo Bonzini open_voice(s, MC_INDEX, freq);
38749ab747fSPaolo Bonzini AUD_set_active_in(s->voice_mc, active[MC_INDEX]);
38849ab747fSPaolo Bonzini }
38949ab747fSPaolo Bonzini
get_volume(uint16_t vol,uint16_t mask,int inverse,int * mute,uint8_t * lvol,uint8_t * rvol)39049ab747fSPaolo Bonzini static void get_volume(uint16_t vol, uint16_t mask, int inverse,
39149ab747fSPaolo Bonzini int *mute, uint8_t *lvol, uint8_t *rvol)
39249ab747fSPaolo Bonzini {
39349ab747fSPaolo Bonzini *mute = (vol >> MUTE_SHIFT) & 1;
39449ab747fSPaolo Bonzini *rvol = (255 * (vol & mask)) / mask;
39549ab747fSPaolo Bonzini *lvol = (255 * ((vol >> 8) & mask)) / mask;
39649ab747fSPaolo Bonzini
39749ab747fSPaolo Bonzini if (inverse) {
39849ab747fSPaolo Bonzini *rvol = 255 - *rvol;
39949ab747fSPaolo Bonzini *lvol = 255 - *lvol;
40049ab747fSPaolo Bonzini }
40149ab747fSPaolo Bonzini }
40249ab747fSPaolo Bonzini
update_combined_volume_out(AC97LinkState * s)40349ab747fSPaolo Bonzini static void update_combined_volume_out(AC97LinkState *s)
40449ab747fSPaolo Bonzini {
40549ab747fSPaolo Bonzini uint8_t lvol, rvol, plvol, prvol;
40649ab747fSPaolo Bonzini int mute, pmute;
40749ab747fSPaolo Bonzini
40849ab747fSPaolo Bonzini get_volume(mixer_load(s, AC97_Master_Volume_Mute), 0x3f, 1,
40949ab747fSPaolo Bonzini &mute, &lvol, &rvol);
41049ab747fSPaolo Bonzini get_volume(mixer_load(s, AC97_PCM_Out_Volume_Mute), 0x1f, 1,
41149ab747fSPaolo Bonzini &pmute, &plvol, &prvol);
41249ab747fSPaolo Bonzini
41349ab747fSPaolo Bonzini mute = mute | pmute;
41449ab747fSPaolo Bonzini lvol = (lvol * plvol) / 255;
41549ab747fSPaolo Bonzini rvol = (rvol * prvol) / 255;
41649ab747fSPaolo Bonzini
41749ab747fSPaolo Bonzini AUD_set_volume_out(s->voice_po, mute, lvol, rvol);
41849ab747fSPaolo Bonzini }
41949ab747fSPaolo Bonzini
update_volume_in(AC97LinkState * s)42049ab747fSPaolo Bonzini static void update_volume_in(AC97LinkState *s)
42149ab747fSPaolo Bonzini {
42249ab747fSPaolo Bonzini uint8_t lvol, rvol;
42349ab747fSPaolo Bonzini int mute;
42449ab747fSPaolo Bonzini
42549ab747fSPaolo Bonzini get_volume(mixer_load(s, AC97_Record_Gain_Mute), 0x0f, 0,
42649ab747fSPaolo Bonzini &mute, &lvol, &rvol);
42749ab747fSPaolo Bonzini
42849ab747fSPaolo Bonzini AUD_set_volume_in(s->voice_pi, mute, lvol, rvol);
42949ab747fSPaolo Bonzini }
43049ab747fSPaolo Bonzini
set_volume(AC97LinkState * s,int index,uint32_t val)43149ab747fSPaolo Bonzini static void set_volume(AC97LinkState *s, int index, uint32_t val)
43249ab747fSPaolo Bonzini {
43349ab747fSPaolo Bonzini switch (index) {
43449ab747fSPaolo Bonzini case AC97_Master_Volume_Mute:
43549ab747fSPaolo Bonzini val &= 0xbf3f;
43649ab747fSPaolo Bonzini mixer_store(s, index, val);
43749ab747fSPaolo Bonzini update_combined_volume_out(s);
43849ab747fSPaolo Bonzini break;
43949ab747fSPaolo Bonzini case AC97_PCM_Out_Volume_Mute:
44049ab747fSPaolo Bonzini val &= 0x9f1f;
44149ab747fSPaolo Bonzini mixer_store(s, index, val);
44249ab747fSPaolo Bonzini update_combined_volume_out(s);
44349ab747fSPaolo Bonzini break;
44449ab747fSPaolo Bonzini case AC97_Record_Gain_Mute:
44549ab747fSPaolo Bonzini val &= 0x8f0f;
44649ab747fSPaolo Bonzini mixer_store(s, index, val);
44749ab747fSPaolo Bonzini update_volume_in(s);
44849ab747fSPaolo Bonzini break;
44949ab747fSPaolo Bonzini }
45049ab747fSPaolo Bonzini }
45149ab747fSPaolo Bonzini
record_select(AC97LinkState * s,uint32_t val)45249ab747fSPaolo Bonzini static void record_select(AC97LinkState *s, uint32_t val)
45349ab747fSPaolo Bonzini {
45449ab747fSPaolo Bonzini uint8_t rs = val & REC_MASK;
45549ab747fSPaolo Bonzini uint8_t ls = (val >> 8) & REC_MASK;
45649ab747fSPaolo Bonzini mixer_store(s, AC97_Record_Select, rs | (ls << 8));
45749ab747fSPaolo Bonzini }
45849ab747fSPaolo Bonzini
mixer_reset(AC97LinkState * s)45949ab747fSPaolo Bonzini static void mixer_reset(AC97LinkState *s)
46049ab747fSPaolo Bonzini {
46149ab747fSPaolo Bonzini uint8_t active[LAST_INDEX];
46249ab747fSPaolo Bonzini
46349ab747fSPaolo Bonzini dolog("mixer_reset\n");
46449ab747fSPaolo Bonzini memset(s->mixer_data, 0, sizeof(s->mixer_data));
46549ab747fSPaolo Bonzini memset(active, 0, sizeof(active));
46649ab747fSPaolo Bonzini mixer_store(s, AC97_Reset, 0x0000); /* 6940 */
46749ab747fSPaolo Bonzini mixer_store(s, AC97_Headphone_Volume_Mute, 0x0000);
46849ab747fSPaolo Bonzini mixer_store(s, AC97_Master_Volume_Mono_Mute, 0x0000);
46949ab747fSPaolo Bonzini mixer_store(s, AC97_Master_Tone_RL, 0x0000);
47049ab747fSPaolo Bonzini mixer_store(s, AC97_PC_BEEP_Volume_Mute, 0x0000);
47149ab747fSPaolo Bonzini mixer_store(s, AC97_Phone_Volume_Mute, 0x0000);
47249ab747fSPaolo Bonzini mixer_store(s, AC97_Mic_Volume_Mute, 0x0000);
47349ab747fSPaolo Bonzini mixer_store(s, AC97_Line_In_Volume_Mute, 0x0000);
47449ab747fSPaolo Bonzini mixer_store(s, AC97_CD_Volume_Mute, 0x0000);
47549ab747fSPaolo Bonzini mixer_store(s, AC97_Video_Volume_Mute, 0x0000);
47649ab747fSPaolo Bonzini mixer_store(s, AC97_Aux_Volume_Mute, 0x0000);
47749ab747fSPaolo Bonzini mixer_store(s, AC97_Record_Gain_Mic_Mute, 0x0000);
47849ab747fSPaolo Bonzini mixer_store(s, AC97_General_Purpose, 0x0000);
47949ab747fSPaolo Bonzini mixer_store(s, AC97_3D_Control, 0x0000);
48049ab747fSPaolo Bonzini mixer_store(s, AC97_Powerdown_Ctrl_Stat, 0x000f);
48149ab747fSPaolo Bonzini
48249ab747fSPaolo Bonzini /*
48349ab747fSPaolo Bonzini * Sigmatel 9700 (STAC9700)
48449ab747fSPaolo Bonzini */
48549ab747fSPaolo Bonzini mixer_store(s, AC97_Vendor_ID1, 0x8384);
48649ab747fSPaolo Bonzini mixer_store(s, AC97_Vendor_ID2, 0x7600); /* 7608 */
48749ab747fSPaolo Bonzini
48849ab747fSPaolo Bonzini mixer_store(s, AC97_Extended_Audio_ID, 0x0809);
48949ab747fSPaolo Bonzini mixer_store(s, AC97_Extended_Audio_Ctrl_Stat, 0x0009);
49049ab747fSPaolo Bonzini mixer_store(s, AC97_PCM_Front_DAC_Rate, 0xbb80);
49149ab747fSPaolo Bonzini mixer_store(s, AC97_PCM_Surround_DAC_Rate, 0xbb80);
49249ab747fSPaolo Bonzini mixer_store(s, AC97_PCM_LFE_DAC_Rate, 0xbb80);
49349ab747fSPaolo Bonzini mixer_store(s, AC97_PCM_LR_ADC_Rate, 0xbb80);
49449ab747fSPaolo Bonzini mixer_store(s, AC97_MIC_ADC_Rate, 0xbb80);
49549ab747fSPaolo Bonzini
49649ab747fSPaolo Bonzini record_select(s, 0);
49749ab747fSPaolo Bonzini set_volume(s, AC97_Master_Volume_Mute, 0x8000);
49849ab747fSPaolo Bonzini set_volume(s, AC97_PCM_Out_Volume_Mute, 0x8808);
49949ab747fSPaolo Bonzini set_volume(s, AC97_Record_Gain_Mute, 0x8808);
50049ab747fSPaolo Bonzini
50149ab747fSPaolo Bonzini reset_voices(s, active);
50249ab747fSPaolo Bonzini }
50349ab747fSPaolo Bonzini
50449ab747fSPaolo Bonzini /**
50549ab747fSPaolo Bonzini * Native audio mixer
50649ab747fSPaolo Bonzini * I/O Reads
50749ab747fSPaolo Bonzini */
nam_readb(void * opaque,uint32_t addr)50849ab747fSPaolo Bonzini static uint32_t nam_readb(void *opaque, uint32_t addr)
50949ab747fSPaolo Bonzini {
51049ab747fSPaolo Bonzini AC97LinkState *s = opaque;
511ab9f0f7dSBALATON Zoltan dolog("U nam readb 0x%x\n", addr);
51249ab747fSPaolo Bonzini s->cas = 0;
51349ab747fSPaolo Bonzini return ~0U;
51449ab747fSPaolo Bonzini }
51549ab747fSPaolo Bonzini
nam_readw(void * opaque,uint32_t addr)51649ab747fSPaolo Bonzini static uint32_t nam_readw(void *opaque, uint32_t addr)
51749ab747fSPaolo Bonzini {
51849ab747fSPaolo Bonzini AC97LinkState *s = opaque;
51949ab747fSPaolo Bonzini s->cas = 0;
520dba2b294SBALATON Zoltan return mixer_load(s, addr);
52149ab747fSPaolo Bonzini }
52249ab747fSPaolo Bonzini
nam_readl(void * opaque,uint32_t addr)52349ab747fSPaolo Bonzini static uint32_t nam_readl(void *opaque, uint32_t addr)
52449ab747fSPaolo Bonzini {
52549ab747fSPaolo Bonzini AC97LinkState *s = opaque;
526ab9f0f7dSBALATON Zoltan dolog("U nam readl 0x%x\n", addr);
52749ab747fSPaolo Bonzini s->cas = 0;
52849ab747fSPaolo Bonzini return ~0U;
52949ab747fSPaolo Bonzini }
53049ab747fSPaolo Bonzini
53149ab747fSPaolo Bonzini /**
53249ab747fSPaolo Bonzini * Native audio mixer
53349ab747fSPaolo Bonzini * I/O Writes
53449ab747fSPaolo Bonzini */
nam_writeb(void * opaque,uint32_t addr,uint32_t val)53549ab747fSPaolo Bonzini static void nam_writeb(void *opaque, uint32_t addr, uint32_t val)
53649ab747fSPaolo Bonzini {
53749ab747fSPaolo Bonzini AC97LinkState *s = opaque;
538ab9f0f7dSBALATON Zoltan dolog("U nam writeb 0x%x <- 0x%x\n", addr, val);
53949ab747fSPaolo Bonzini s->cas = 0;
54049ab747fSPaolo Bonzini }
54149ab747fSPaolo Bonzini
nam_writew(void * opaque,uint32_t addr,uint32_t val)54249ab747fSPaolo Bonzini static void nam_writew(void *opaque, uint32_t addr, uint32_t val)
54349ab747fSPaolo Bonzini {
54449ab747fSPaolo Bonzini AC97LinkState *s = opaque;
545dba2b294SBALATON Zoltan
54649ab747fSPaolo Bonzini s->cas = 0;
547dba2b294SBALATON Zoltan switch (addr) {
54849ab747fSPaolo Bonzini case AC97_Reset:
54949ab747fSPaolo Bonzini mixer_reset(s);
55049ab747fSPaolo Bonzini break;
55149ab747fSPaolo Bonzini case AC97_Powerdown_Ctrl_Stat:
55249ab747fSPaolo Bonzini val &= ~0x800f;
553dba2b294SBALATON Zoltan val |= mixer_load(s, addr) & 0xf;
554dba2b294SBALATON Zoltan mixer_store(s, addr, val);
55549ab747fSPaolo Bonzini break;
55649ab747fSPaolo Bonzini case AC97_PCM_Out_Volume_Mute:
55749ab747fSPaolo Bonzini case AC97_Master_Volume_Mute:
55849ab747fSPaolo Bonzini case AC97_Record_Gain_Mute:
559dba2b294SBALATON Zoltan set_volume(s, addr, val);
56049ab747fSPaolo Bonzini break;
56149ab747fSPaolo Bonzini case AC97_Record_Select:
56249ab747fSPaolo Bonzini record_select(s, val);
56349ab747fSPaolo Bonzini break;
56449ab747fSPaolo Bonzini case AC97_Vendor_ID1:
56549ab747fSPaolo Bonzini case AC97_Vendor_ID2:
566ab9f0f7dSBALATON Zoltan dolog("Attempt to write vendor ID to 0x%x\n", val);
56749ab747fSPaolo Bonzini break;
56849ab747fSPaolo Bonzini case AC97_Extended_Audio_ID:
569ab9f0f7dSBALATON Zoltan dolog("Attempt to write extended audio ID to 0x%x\n", val);
57049ab747fSPaolo Bonzini break;
57149ab747fSPaolo Bonzini case AC97_Extended_Audio_Ctrl_Stat:
57249ab747fSPaolo Bonzini if (!(val & EACS_VRA)) {
57349ab747fSPaolo Bonzini mixer_store(s, AC97_PCM_Front_DAC_Rate, 0xbb80);
57449ab747fSPaolo Bonzini mixer_store(s, AC97_PCM_LR_ADC_Rate, 0xbb80);
57549ab747fSPaolo Bonzini open_voice(s, PI_INDEX, 48000);
57649ab747fSPaolo Bonzini open_voice(s, PO_INDEX, 48000);
57749ab747fSPaolo Bonzini }
57849ab747fSPaolo Bonzini if (!(val & EACS_VRM)) {
57949ab747fSPaolo Bonzini mixer_store(s, AC97_MIC_ADC_Rate, 0xbb80);
58049ab747fSPaolo Bonzini open_voice(s, MC_INDEX, 48000);
58149ab747fSPaolo Bonzini }
582ab9f0f7dSBALATON Zoltan dolog("Setting extended audio control to 0x%x\n", val);
58349ab747fSPaolo Bonzini mixer_store(s, AC97_Extended_Audio_Ctrl_Stat, val);
58449ab747fSPaolo Bonzini break;
58549ab747fSPaolo Bonzini case AC97_PCM_Front_DAC_Rate:
58649ab747fSPaolo Bonzini if (mixer_load(s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) {
587dba2b294SBALATON Zoltan mixer_store(s, addr, val);
58849ab747fSPaolo Bonzini dolog("Set front DAC rate to %d\n", val);
58949ab747fSPaolo Bonzini open_voice(s, PO_INDEX, val);
590ab9f0f7dSBALATON Zoltan } else {
591ab9f0f7dSBALATON Zoltan dolog("Attempt to set front DAC rate to %d, but VRA is not set\n",
59249ab747fSPaolo Bonzini val);
59349ab747fSPaolo Bonzini }
59449ab747fSPaolo Bonzini break;
59549ab747fSPaolo Bonzini case AC97_MIC_ADC_Rate:
59649ab747fSPaolo Bonzini if (mixer_load(s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRM) {
597dba2b294SBALATON Zoltan mixer_store(s, addr, val);
59849ab747fSPaolo Bonzini dolog("Set MIC ADC rate to %d\n", val);
59949ab747fSPaolo Bonzini open_voice(s, MC_INDEX, val);
600ab9f0f7dSBALATON Zoltan } else {
601ab9f0f7dSBALATON Zoltan dolog("Attempt to set MIC ADC rate to %d, but VRM is not set\n",
60249ab747fSPaolo Bonzini val);
60349ab747fSPaolo Bonzini }
60449ab747fSPaolo Bonzini break;
60549ab747fSPaolo Bonzini case AC97_PCM_LR_ADC_Rate:
60649ab747fSPaolo Bonzini if (mixer_load(s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) {
607dba2b294SBALATON Zoltan mixer_store(s, addr, val);
60849ab747fSPaolo Bonzini dolog("Set front LR ADC rate to %d\n", val);
60949ab747fSPaolo Bonzini open_voice(s, PI_INDEX, val);
610ab9f0f7dSBALATON Zoltan } else {
61149ab747fSPaolo Bonzini dolog("Attempt to set LR ADC rate to %d, but VRA is not set\n",
61249ab747fSPaolo Bonzini val);
61349ab747fSPaolo Bonzini }
61449ab747fSPaolo Bonzini break;
61549ab747fSPaolo Bonzini case AC97_Headphone_Volume_Mute:
61649ab747fSPaolo Bonzini case AC97_Master_Volume_Mono_Mute:
61749ab747fSPaolo Bonzini case AC97_Master_Tone_RL:
61849ab747fSPaolo Bonzini case AC97_PC_BEEP_Volume_Mute:
61949ab747fSPaolo Bonzini case AC97_Phone_Volume_Mute:
62049ab747fSPaolo Bonzini case AC97_Mic_Volume_Mute:
62149ab747fSPaolo Bonzini case AC97_Line_In_Volume_Mute:
62249ab747fSPaolo Bonzini case AC97_CD_Volume_Mute:
62349ab747fSPaolo Bonzini case AC97_Video_Volume_Mute:
62449ab747fSPaolo Bonzini case AC97_Aux_Volume_Mute:
62549ab747fSPaolo Bonzini case AC97_Record_Gain_Mic_Mute:
62649ab747fSPaolo Bonzini case AC97_General_Purpose:
62749ab747fSPaolo Bonzini case AC97_3D_Control:
62849ab747fSPaolo Bonzini case AC97_Sigmatel_Analog:
62949ab747fSPaolo Bonzini case AC97_Sigmatel_Dac2Invert:
63049ab747fSPaolo Bonzini /* None of the features in these regs are emulated, so they are RO */
63149ab747fSPaolo Bonzini break;
63249ab747fSPaolo Bonzini default:
633ab9f0f7dSBALATON Zoltan dolog("U nam writew 0x%x <- 0x%x\n", addr, val);
634dba2b294SBALATON Zoltan mixer_store(s, addr, val);
63549ab747fSPaolo Bonzini break;
63649ab747fSPaolo Bonzini }
63749ab747fSPaolo Bonzini }
63849ab747fSPaolo Bonzini
nam_writel(void * opaque,uint32_t addr,uint32_t val)63949ab747fSPaolo Bonzini static void nam_writel(void *opaque, uint32_t addr, uint32_t val)
64049ab747fSPaolo Bonzini {
64149ab747fSPaolo Bonzini AC97LinkState *s = opaque;
642ab9f0f7dSBALATON Zoltan dolog("U nam writel 0x%x <- 0x%x\n", addr, val);
64349ab747fSPaolo Bonzini s->cas = 0;
64449ab747fSPaolo Bonzini }
64549ab747fSPaolo Bonzini
64649ab747fSPaolo Bonzini /**
64749ab747fSPaolo Bonzini * Native audio bus master
64849ab747fSPaolo Bonzini * I/O Reads
64949ab747fSPaolo Bonzini */
nabm_readb(void * opaque,uint32_t addr)65049ab747fSPaolo Bonzini static uint32_t nabm_readb(void *opaque, uint32_t addr)
65149ab747fSPaolo Bonzini {
65249ab747fSPaolo Bonzini AC97LinkState *s = opaque;
65349ab747fSPaolo Bonzini AC97BusMasterRegs *r = NULL;
65449ab747fSPaolo Bonzini uint32_t val = ~0U;
65549ab747fSPaolo Bonzini
656dba2b294SBALATON Zoltan switch (addr) {
65749ab747fSPaolo Bonzini case CAS:
65849ab747fSPaolo Bonzini dolog("CAS %d\n", s->cas);
65949ab747fSPaolo Bonzini val = s->cas;
66049ab747fSPaolo Bonzini s->cas = 1;
66149ab747fSPaolo Bonzini break;
66249ab747fSPaolo Bonzini case PI_CIV:
66349ab747fSPaolo Bonzini case PO_CIV:
66449ab747fSPaolo Bonzini case MC_CIV:
665dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
66649ab747fSPaolo Bonzini val = r->civ;
667dba2b294SBALATON Zoltan dolog("CIV[%d] -> 0x%x\n", GET_BM(addr), val);
66849ab747fSPaolo Bonzini break;
66949ab747fSPaolo Bonzini case PI_LVI:
67049ab747fSPaolo Bonzini case PO_LVI:
67149ab747fSPaolo Bonzini case MC_LVI:
672dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
67349ab747fSPaolo Bonzini val = r->lvi;
674dba2b294SBALATON Zoltan dolog("LVI[%d] -> 0x%x\n", GET_BM(addr), val);
67549ab747fSPaolo Bonzini break;
67649ab747fSPaolo Bonzini case PI_PIV:
67749ab747fSPaolo Bonzini case PO_PIV:
67849ab747fSPaolo Bonzini case MC_PIV:
679dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
68049ab747fSPaolo Bonzini val = r->piv;
681dba2b294SBALATON Zoltan dolog("PIV[%d] -> 0x%x\n", GET_BM(addr), val);
68249ab747fSPaolo Bonzini break;
68349ab747fSPaolo Bonzini case PI_CR:
68449ab747fSPaolo Bonzini case PO_CR:
68549ab747fSPaolo Bonzini case MC_CR:
686dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
68749ab747fSPaolo Bonzini val = r->cr;
688dba2b294SBALATON Zoltan dolog("CR[%d] -> 0x%x\n", GET_BM(addr), val);
68949ab747fSPaolo Bonzini break;
69049ab747fSPaolo Bonzini case PI_SR:
69149ab747fSPaolo Bonzini case PO_SR:
69249ab747fSPaolo Bonzini case MC_SR:
693dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
69449ab747fSPaolo Bonzini val = r->sr & 0xff;
695dba2b294SBALATON Zoltan dolog("SRb[%d] -> 0x%x\n", GET_BM(addr), val);
69649ab747fSPaolo Bonzini break;
69749ab747fSPaolo Bonzini default:
698ab9f0f7dSBALATON Zoltan dolog("U nabm readb 0x%x -> 0x%x\n", addr, val);
69949ab747fSPaolo Bonzini break;
70049ab747fSPaolo Bonzini }
70149ab747fSPaolo Bonzini return val;
70249ab747fSPaolo Bonzini }
70349ab747fSPaolo Bonzini
nabm_readw(void * opaque,uint32_t addr)70449ab747fSPaolo Bonzini static uint32_t nabm_readw(void *opaque, uint32_t addr)
70549ab747fSPaolo Bonzini {
70649ab747fSPaolo Bonzini AC97LinkState *s = opaque;
70749ab747fSPaolo Bonzini AC97BusMasterRegs *r = NULL;
70849ab747fSPaolo Bonzini uint32_t val = ~0U;
70949ab747fSPaolo Bonzini
710dba2b294SBALATON Zoltan switch (addr) {
71149ab747fSPaolo Bonzini case PI_SR:
71249ab747fSPaolo Bonzini case PO_SR:
71349ab747fSPaolo Bonzini case MC_SR:
714dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
71549ab747fSPaolo Bonzini val = r->sr;
716dba2b294SBALATON Zoltan dolog("SR[%d] -> 0x%x\n", GET_BM(addr), val);
71749ab747fSPaolo Bonzini break;
71849ab747fSPaolo Bonzini case PI_PICB:
71949ab747fSPaolo Bonzini case PO_PICB:
72049ab747fSPaolo Bonzini case MC_PICB:
721dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
72249ab747fSPaolo Bonzini val = r->picb;
723dba2b294SBALATON Zoltan dolog("PICB[%d] -> 0x%x\n", GET_BM(addr), val);
72449ab747fSPaolo Bonzini break;
72549ab747fSPaolo Bonzini default:
726ab9f0f7dSBALATON Zoltan dolog("U nabm readw 0x%x -> 0x%x\n", addr, val);
72749ab747fSPaolo Bonzini break;
72849ab747fSPaolo Bonzini }
72949ab747fSPaolo Bonzini return val;
73049ab747fSPaolo Bonzini }
73149ab747fSPaolo Bonzini
nabm_readl(void * opaque,uint32_t addr)73249ab747fSPaolo Bonzini static uint32_t nabm_readl(void *opaque, uint32_t addr)
73349ab747fSPaolo Bonzini {
73449ab747fSPaolo Bonzini AC97LinkState *s = opaque;
73549ab747fSPaolo Bonzini AC97BusMasterRegs *r = NULL;
73649ab747fSPaolo Bonzini uint32_t val = ~0U;
73749ab747fSPaolo Bonzini
738dba2b294SBALATON Zoltan switch (addr) {
73949ab747fSPaolo Bonzini case PI_BDBAR:
74049ab747fSPaolo Bonzini case PO_BDBAR:
74149ab747fSPaolo Bonzini case MC_BDBAR:
742dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
74349ab747fSPaolo Bonzini val = r->bdbar;
744dba2b294SBALATON Zoltan dolog("BMADDR[%d] -> 0x%x\n", GET_BM(addr), val);
74549ab747fSPaolo Bonzini break;
74649ab747fSPaolo Bonzini case PI_CIV:
74749ab747fSPaolo Bonzini case PO_CIV:
74849ab747fSPaolo Bonzini case MC_CIV:
749dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
75049ab747fSPaolo Bonzini val = r->civ | (r->lvi << 8) | (r->sr << 16);
751dba2b294SBALATON Zoltan dolog("CIV LVI SR[%d] -> 0x%x, 0x%x, 0x%x\n", GET_BM(addr),
75249ab747fSPaolo Bonzini r->civ, r->lvi, r->sr);
75349ab747fSPaolo Bonzini break;
75449ab747fSPaolo Bonzini case PI_PICB:
75549ab747fSPaolo Bonzini case PO_PICB:
75649ab747fSPaolo Bonzini case MC_PICB:
757dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
75849ab747fSPaolo Bonzini val = r->picb | (r->piv << 16) | (r->cr << 24);
759dba2b294SBALATON Zoltan dolog("PICB PIV CR[%d] -> 0x%x 0x%x 0x%x 0x%x\n", GET_BM(addr),
76049ab747fSPaolo Bonzini val, r->picb, r->piv, r->cr);
76149ab747fSPaolo Bonzini break;
76249ab747fSPaolo Bonzini case GLOB_CNT:
76349ab747fSPaolo Bonzini val = s->glob_cnt;
764ab9f0f7dSBALATON Zoltan dolog("glob_cnt -> 0x%x\n", val);
76549ab747fSPaolo Bonzini break;
76649ab747fSPaolo Bonzini case GLOB_STA:
76749ab747fSPaolo Bonzini val = s->glob_sta | GS_S0CR;
768ab9f0f7dSBALATON Zoltan dolog("glob_sta -> 0x%x\n", val);
76949ab747fSPaolo Bonzini break;
77049ab747fSPaolo Bonzini default:
771ab9f0f7dSBALATON Zoltan dolog("U nabm readl 0x%x -> 0x%x\n", addr, val);
77249ab747fSPaolo Bonzini break;
77349ab747fSPaolo Bonzini }
77449ab747fSPaolo Bonzini return val;
77549ab747fSPaolo Bonzini }
77649ab747fSPaolo Bonzini
77749ab747fSPaolo Bonzini /**
77849ab747fSPaolo Bonzini * Native audio bus master
77949ab747fSPaolo Bonzini * I/O Writes
78049ab747fSPaolo Bonzini */
nabm_writeb(void * opaque,uint32_t addr,uint32_t val)78149ab747fSPaolo Bonzini static void nabm_writeb(void *opaque, uint32_t addr, uint32_t val)
78249ab747fSPaolo Bonzini {
78349ab747fSPaolo Bonzini AC97LinkState *s = opaque;
78449ab747fSPaolo Bonzini AC97BusMasterRegs *r = NULL;
785dba2b294SBALATON Zoltan
786dba2b294SBALATON Zoltan switch (addr) {
78749ab747fSPaolo Bonzini case PI_LVI:
78849ab747fSPaolo Bonzini case PO_LVI:
78949ab747fSPaolo Bonzini case MC_LVI:
790dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
79149ab747fSPaolo Bonzini if ((r->cr & CR_RPBM) && (r->sr & SR_DCH)) {
79249ab747fSPaolo Bonzini r->sr &= ~(SR_DCH | SR_CELV);
79349ab747fSPaolo Bonzini r->civ = r->piv;
79449ab747fSPaolo Bonzini r->piv = (r->piv + 1) % 32;
79549ab747fSPaolo Bonzini fetch_bd(s, r);
79649ab747fSPaolo Bonzini }
79749ab747fSPaolo Bonzini r->lvi = val % 32;
798dba2b294SBALATON Zoltan dolog("LVI[%d] <- 0x%x\n", GET_BM(addr), val);
79949ab747fSPaolo Bonzini break;
80049ab747fSPaolo Bonzini case PI_CR:
80149ab747fSPaolo Bonzini case PO_CR:
80249ab747fSPaolo Bonzini case MC_CR:
803dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
80449ab747fSPaolo Bonzini if (val & CR_RR) {
80549ab747fSPaolo Bonzini reset_bm_regs(s, r);
806ab9f0f7dSBALATON Zoltan } else {
80749ab747fSPaolo Bonzini r->cr = val & CR_VALID_MASK;
80849ab747fSPaolo Bonzini if (!(r->cr & CR_RPBM)) {
80949ab747fSPaolo Bonzini voice_set_active(s, r - s->bm_regs, 0);
81049ab747fSPaolo Bonzini r->sr |= SR_DCH;
811ab9f0f7dSBALATON Zoltan } else {
81249ab747fSPaolo Bonzini r->civ = r->piv;
81349ab747fSPaolo Bonzini r->piv = (r->piv + 1) % 32;
81449ab747fSPaolo Bonzini fetch_bd(s, r);
81549ab747fSPaolo Bonzini r->sr &= ~SR_DCH;
81649ab747fSPaolo Bonzini voice_set_active(s, r - s->bm_regs, 1);
81749ab747fSPaolo Bonzini }
81849ab747fSPaolo Bonzini }
819dba2b294SBALATON Zoltan dolog("CR[%d] <- 0x%x (cr 0x%x)\n", GET_BM(addr), val, r->cr);
82049ab747fSPaolo Bonzini break;
82149ab747fSPaolo Bonzini case PI_SR:
82249ab747fSPaolo Bonzini case PO_SR:
82349ab747fSPaolo Bonzini case MC_SR:
824dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
82549ab747fSPaolo Bonzini r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
82649ab747fSPaolo Bonzini update_sr(s, r, r->sr & ~(val & SR_WCLEAR_MASK));
827dba2b294SBALATON Zoltan dolog("SR[%d] <- 0x%x (sr 0x%x)\n", GET_BM(addr), val, r->sr);
82849ab747fSPaolo Bonzini break;
82949ab747fSPaolo Bonzini default:
830ab9f0f7dSBALATON Zoltan dolog("U nabm writeb 0x%x <- 0x%x\n", addr, val);
83149ab747fSPaolo Bonzini break;
83249ab747fSPaolo Bonzini }
83349ab747fSPaolo Bonzini }
83449ab747fSPaolo Bonzini
nabm_writew(void * opaque,uint32_t addr,uint32_t val)83549ab747fSPaolo Bonzini static void nabm_writew(void *opaque, uint32_t addr, uint32_t val)
83649ab747fSPaolo Bonzini {
83749ab747fSPaolo Bonzini AC97LinkState *s = opaque;
83849ab747fSPaolo Bonzini AC97BusMasterRegs *r = NULL;
839dba2b294SBALATON Zoltan
840dba2b294SBALATON Zoltan switch (addr) {
84149ab747fSPaolo Bonzini case PI_SR:
84249ab747fSPaolo Bonzini case PO_SR:
84349ab747fSPaolo Bonzini case MC_SR:
844dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
84549ab747fSPaolo Bonzini r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
84649ab747fSPaolo Bonzini update_sr(s, r, r->sr & ~(val & SR_WCLEAR_MASK));
847dba2b294SBALATON Zoltan dolog("SR[%d] <- 0x%x (sr 0x%x)\n", GET_BM(addr), val, r->sr);
84849ab747fSPaolo Bonzini break;
84949ab747fSPaolo Bonzini default:
850ab9f0f7dSBALATON Zoltan dolog("U nabm writew 0x%x <- 0x%x\n", addr, val);
85149ab747fSPaolo Bonzini break;
85249ab747fSPaolo Bonzini }
85349ab747fSPaolo Bonzini }
85449ab747fSPaolo Bonzini
nabm_writel(void * opaque,uint32_t addr,uint32_t val)85549ab747fSPaolo Bonzini static void nabm_writel(void *opaque, uint32_t addr, uint32_t val)
85649ab747fSPaolo Bonzini {
85749ab747fSPaolo Bonzini AC97LinkState *s = opaque;
85849ab747fSPaolo Bonzini AC97BusMasterRegs *r = NULL;
859dba2b294SBALATON Zoltan
860dba2b294SBALATON Zoltan switch (addr) {
86149ab747fSPaolo Bonzini case PI_BDBAR:
86249ab747fSPaolo Bonzini case PO_BDBAR:
86349ab747fSPaolo Bonzini case MC_BDBAR:
864dba2b294SBALATON Zoltan r = &s->bm_regs[GET_BM(addr)];
86549ab747fSPaolo Bonzini r->bdbar = val & ~3;
866dba2b294SBALATON Zoltan dolog("BDBAR[%d] <- 0x%x (bdbar 0x%x)\n", GET_BM(addr), val, r->bdbar);
86749ab747fSPaolo Bonzini break;
86849ab747fSPaolo Bonzini case GLOB_CNT:
869dafea9e2SBALATON Zoltan /* TODO: Handle WR or CR being set (warm/cold reset requests) */
870ab9f0f7dSBALATON Zoltan if (!(val & (GC_WR | GC_CR))) {
87149ab747fSPaolo Bonzini s->glob_cnt = val & GC_VALID_MASK;
872ab9f0f7dSBALATON Zoltan }
873ab9f0f7dSBALATON Zoltan dolog("glob_cnt <- 0x%x (glob_cnt 0x%x)\n", val, s->glob_cnt);
87449ab747fSPaolo Bonzini break;
87549ab747fSPaolo Bonzini case GLOB_STA:
87649ab747fSPaolo Bonzini s->glob_sta &= ~(val & GS_WCLEAR_MASK);
87749ab747fSPaolo Bonzini s->glob_sta |= (val & ~(GS_WCLEAR_MASK | GS_RO_MASK)) & GS_VALID_MASK;
878ab9f0f7dSBALATON Zoltan dolog("glob_sta <- 0x%x (glob_sta 0x%x)\n", val, s->glob_sta);
87949ab747fSPaolo Bonzini break;
88049ab747fSPaolo Bonzini default:
881ab9f0f7dSBALATON Zoltan dolog("U nabm writel 0x%x <- 0x%x\n", addr, val);
88249ab747fSPaolo Bonzini break;
88349ab747fSPaolo Bonzini }
88449ab747fSPaolo Bonzini }
88549ab747fSPaolo Bonzini
write_audio(AC97LinkState * s,AC97BusMasterRegs * r,int max,int * stop)88649ab747fSPaolo Bonzini static int write_audio(AC97LinkState *s, AC97BusMasterRegs *r,
88749ab747fSPaolo Bonzini int max, int *stop)
88849ab747fSPaolo Bonzini {
88949ab747fSPaolo Bonzini uint8_t tmpbuf[4096];
89049ab747fSPaolo Bonzini uint32_t addr = r->bd.addr;
89149ab747fSPaolo Bonzini uint32_t temp = r->picb << 1;
89249ab747fSPaolo Bonzini uint32_t written = 0;
89349ab747fSPaolo Bonzini int to_copy = 0;
89458935915SKővágó, Zoltán temp = MIN(temp, max);
89549ab747fSPaolo Bonzini
89649ab747fSPaolo Bonzini if (!temp) {
89749ab747fSPaolo Bonzini *stop = 1;
89849ab747fSPaolo Bonzini return 0;
89949ab747fSPaolo Bonzini }
90049ab747fSPaolo Bonzini
90149ab747fSPaolo Bonzini while (temp) {
90249ab747fSPaolo Bonzini int copied;
90358935915SKővágó, Zoltán to_copy = MIN(temp, sizeof(tmpbuf));
90449ab747fSPaolo Bonzini pci_dma_read(&s->dev, addr, tmpbuf, to_copy);
90549ab747fSPaolo Bonzini copied = AUD_write(s->voice_po, tmpbuf, to_copy);
90649ab747fSPaolo Bonzini dolog("write_audio max=%x to_copy=%x copied=%x\n",
90749ab747fSPaolo Bonzini max, to_copy, copied);
90849ab747fSPaolo Bonzini if (!copied) {
90949ab747fSPaolo Bonzini *stop = 1;
91049ab747fSPaolo Bonzini break;
91149ab747fSPaolo Bonzini }
91249ab747fSPaolo Bonzini temp -= copied;
91349ab747fSPaolo Bonzini addr += copied;
91449ab747fSPaolo Bonzini written += copied;
91549ab747fSPaolo Bonzini }
91649ab747fSPaolo Bonzini
91749ab747fSPaolo Bonzini if (!temp) {
91849ab747fSPaolo Bonzini if (to_copy < 4) {
91949ab747fSPaolo Bonzini dolog("whoops\n");
92049ab747fSPaolo Bonzini s->last_samp = 0;
921ab9f0f7dSBALATON Zoltan } else {
92249ab747fSPaolo Bonzini s->last_samp = *(uint32_t *)&tmpbuf[to_copy - 4];
92349ab747fSPaolo Bonzini }
92449ab747fSPaolo Bonzini }
92549ab747fSPaolo Bonzini
92649ab747fSPaolo Bonzini r->bd.addr = addr;
92749ab747fSPaolo Bonzini return written;
92849ab747fSPaolo Bonzini }
92949ab747fSPaolo Bonzini
write_bup(AC97LinkState * s,int elapsed)93049ab747fSPaolo Bonzini static void write_bup(AC97LinkState *s, int elapsed)
93149ab747fSPaolo Bonzini {
93249ab747fSPaolo Bonzini dolog("write_bup\n");
93349ab747fSPaolo Bonzini if (!(s->bup_flag & BUP_SET)) {
93449ab747fSPaolo Bonzini if (s->bup_flag & BUP_LAST) {
93549ab747fSPaolo Bonzini int i;
93649ab747fSPaolo Bonzini uint8_t *p = s->silence;
93749ab747fSPaolo Bonzini for (i = 0; i < sizeof(s->silence) / 4; i++, p += 4) {
93849ab747fSPaolo Bonzini *(uint32_t *) p = s->last_samp;
93949ab747fSPaolo Bonzini }
940ab9f0f7dSBALATON Zoltan } else {
94149ab747fSPaolo Bonzini memset(s->silence, 0, sizeof(s->silence));
94249ab747fSPaolo Bonzini }
94349ab747fSPaolo Bonzini s->bup_flag |= BUP_SET;
94449ab747fSPaolo Bonzini }
94549ab747fSPaolo Bonzini
94649ab747fSPaolo Bonzini while (elapsed) {
94758935915SKővágó, Zoltán int temp = MIN(elapsed, sizeof(s->silence));
94849ab747fSPaolo Bonzini while (temp) {
94949ab747fSPaolo Bonzini int copied = AUD_write(s->voice_po, s->silence, temp);
950ab9f0f7dSBALATON Zoltan if (!copied) {
95149ab747fSPaolo Bonzini return;
952ab9f0f7dSBALATON Zoltan }
95349ab747fSPaolo Bonzini temp -= copied;
95449ab747fSPaolo Bonzini elapsed -= copied;
95549ab747fSPaolo Bonzini }
95649ab747fSPaolo Bonzini }
95749ab747fSPaolo Bonzini }
95849ab747fSPaolo Bonzini
read_audio(AC97LinkState * s,AC97BusMasterRegs * r,int max,int * stop)95949ab747fSPaolo Bonzini static int read_audio(AC97LinkState *s, AC97BusMasterRegs *r,
96049ab747fSPaolo Bonzini int max, int *stop)
96149ab747fSPaolo Bonzini {
96249ab747fSPaolo Bonzini uint8_t tmpbuf[4096];
96349ab747fSPaolo Bonzini uint32_t addr = r->bd.addr;
96449ab747fSPaolo Bonzini uint32_t temp = r->picb << 1;
96549ab747fSPaolo Bonzini uint32_t nread = 0;
96649ab747fSPaolo Bonzini int to_copy = 0;
96749ab747fSPaolo Bonzini SWVoiceIn *voice = (r - s->bm_regs) == MC_INDEX ? s->voice_mc : s->voice_pi;
96849ab747fSPaolo Bonzini
96958935915SKővágó, Zoltán temp = MIN(temp, max);
97049ab747fSPaolo Bonzini
97149ab747fSPaolo Bonzini if (!temp) {
97249ab747fSPaolo Bonzini *stop = 1;
97349ab747fSPaolo Bonzini return 0;
97449ab747fSPaolo Bonzini }
97549ab747fSPaolo Bonzini
97649ab747fSPaolo Bonzini while (temp) {
97749ab747fSPaolo Bonzini int acquired;
97858935915SKővágó, Zoltán to_copy = MIN(temp, sizeof(tmpbuf));
97949ab747fSPaolo Bonzini acquired = AUD_read(voice, tmpbuf, to_copy);
98049ab747fSPaolo Bonzini if (!acquired) {
98149ab747fSPaolo Bonzini *stop = 1;
98249ab747fSPaolo Bonzini break;
98349ab747fSPaolo Bonzini }
98449ab747fSPaolo Bonzini pci_dma_write(&s->dev, addr, tmpbuf, acquired);
98549ab747fSPaolo Bonzini temp -= acquired;
98649ab747fSPaolo Bonzini addr += acquired;
98749ab747fSPaolo Bonzini nread += acquired;
98849ab747fSPaolo Bonzini }
98949ab747fSPaolo Bonzini
99049ab747fSPaolo Bonzini r->bd.addr = addr;
99149ab747fSPaolo Bonzini return nread;
99249ab747fSPaolo Bonzini }
99349ab747fSPaolo Bonzini
transfer_audio(AC97LinkState * s,int index,int elapsed)99449ab747fSPaolo Bonzini static void transfer_audio(AC97LinkState *s, int index, int elapsed)
99549ab747fSPaolo Bonzini {
99649ab747fSPaolo Bonzini AC97BusMasterRegs *r = &s->bm_regs[index];
99749ab747fSPaolo Bonzini int stop = 0;
99849ab747fSPaolo Bonzini
99949ab747fSPaolo Bonzini if (s->invalid_freq[index]) {
100049ab747fSPaolo Bonzini AUD_log("ac97", "attempt to use voice %d with invalid frequency %d\n",
100149ab747fSPaolo Bonzini index, s->invalid_freq[index]);
100249ab747fSPaolo Bonzini return;
100349ab747fSPaolo Bonzini }
100449ab747fSPaolo Bonzini
100549ab747fSPaolo Bonzini if (r->sr & SR_DCH) {
100649ab747fSPaolo Bonzini if (r->cr & CR_RPBM) {
100749ab747fSPaolo Bonzini switch (index) {
100849ab747fSPaolo Bonzini case PO_INDEX:
100949ab747fSPaolo Bonzini write_bup(s, elapsed);
101049ab747fSPaolo Bonzini break;
101149ab747fSPaolo Bonzini }
101249ab747fSPaolo Bonzini }
101349ab747fSPaolo Bonzini return;
101449ab747fSPaolo Bonzini }
101549ab747fSPaolo Bonzini
101649ab747fSPaolo Bonzini while ((elapsed >> 1) && !stop) {
101749ab747fSPaolo Bonzini int temp;
101849ab747fSPaolo Bonzini
101949ab747fSPaolo Bonzini if (!r->bd_valid) {
102049ab747fSPaolo Bonzini dolog("invalid bd\n");
102149ab747fSPaolo Bonzini fetch_bd(s, r);
102249ab747fSPaolo Bonzini }
102349ab747fSPaolo Bonzini
102449ab747fSPaolo Bonzini if (!r->picb) {
1025ab9f0f7dSBALATON Zoltan dolog("fresh bd %d is empty 0x%x 0x%x\n",
102649ab747fSPaolo Bonzini r->civ, r->bd.addr, r->bd.ctl_len);
102749ab747fSPaolo Bonzini if (r->civ == r->lvi) {
102849ab747fSPaolo Bonzini r->sr |= SR_DCH; /* CELV? */
102949ab747fSPaolo Bonzini s->bup_flag = 0;
103049ab747fSPaolo Bonzini break;
103149ab747fSPaolo Bonzini }
103249ab747fSPaolo Bonzini r->sr &= ~SR_CELV;
103349ab747fSPaolo Bonzini r->civ = r->piv;
103449ab747fSPaolo Bonzini r->piv = (r->piv + 1) % 32;
103549ab747fSPaolo Bonzini fetch_bd(s, r);
103649ab747fSPaolo Bonzini return;
103749ab747fSPaolo Bonzini }
103849ab747fSPaolo Bonzini
103949ab747fSPaolo Bonzini switch (index) {
104049ab747fSPaolo Bonzini case PO_INDEX:
104149ab747fSPaolo Bonzini temp = write_audio(s, r, elapsed, &stop);
104249ab747fSPaolo Bonzini elapsed -= temp;
104349ab747fSPaolo Bonzini r->picb -= (temp >> 1);
104449ab747fSPaolo Bonzini break;
104549ab747fSPaolo Bonzini
104649ab747fSPaolo Bonzini case PI_INDEX:
104749ab747fSPaolo Bonzini case MC_INDEX:
104849ab747fSPaolo Bonzini temp = read_audio(s, r, elapsed, &stop);
104949ab747fSPaolo Bonzini elapsed -= temp;
105049ab747fSPaolo Bonzini r->picb -= (temp >> 1);
105149ab747fSPaolo Bonzini break;
105249ab747fSPaolo Bonzini }
105349ab747fSPaolo Bonzini
105449ab747fSPaolo Bonzini if (!r->picb) {
105549ab747fSPaolo Bonzini uint32_t new_sr = r->sr & ~SR_CELV;
105649ab747fSPaolo Bonzini
105749ab747fSPaolo Bonzini if (r->bd.ctl_len & BD_IOC) {
105849ab747fSPaolo Bonzini new_sr |= SR_BCIS;
105949ab747fSPaolo Bonzini }
106049ab747fSPaolo Bonzini
106149ab747fSPaolo Bonzini if (r->civ == r->lvi) {
106249ab747fSPaolo Bonzini dolog("Underrun civ (%d) == lvi (%d)\n", r->civ, r->lvi);
106349ab747fSPaolo Bonzini
106449ab747fSPaolo Bonzini new_sr |= SR_LVBCI | SR_DCH | SR_CELV;
106549ab747fSPaolo Bonzini stop = 1;
106649ab747fSPaolo Bonzini s->bup_flag = (r->bd.ctl_len & BD_BUP) ? BUP_LAST : 0;
1067ab9f0f7dSBALATON Zoltan } else {
106849ab747fSPaolo Bonzini r->civ = r->piv;
106949ab747fSPaolo Bonzini r->piv = (r->piv + 1) % 32;
107049ab747fSPaolo Bonzini fetch_bd(s, r);
107149ab747fSPaolo Bonzini }
107249ab747fSPaolo Bonzini
107349ab747fSPaolo Bonzini update_sr(s, r, new_sr);
107449ab747fSPaolo Bonzini }
107549ab747fSPaolo Bonzini }
107649ab747fSPaolo Bonzini }
107749ab747fSPaolo Bonzini
pi_callback(void * opaque,int avail)107849ab747fSPaolo Bonzini static void pi_callback(void *opaque, int avail)
107949ab747fSPaolo Bonzini {
108049ab747fSPaolo Bonzini transfer_audio(opaque, PI_INDEX, avail);
108149ab747fSPaolo Bonzini }
108249ab747fSPaolo Bonzini
mc_callback(void * opaque,int avail)108349ab747fSPaolo Bonzini static void mc_callback(void *opaque, int avail)
108449ab747fSPaolo Bonzini {
108549ab747fSPaolo Bonzini transfer_audio(opaque, MC_INDEX, avail);
108649ab747fSPaolo Bonzini }
108749ab747fSPaolo Bonzini
po_callback(void * opaque,int free)108849ab747fSPaolo Bonzini static void po_callback(void *opaque, int free)
108949ab747fSPaolo Bonzini {
109049ab747fSPaolo Bonzini transfer_audio(opaque, PO_INDEX, free);
109149ab747fSPaolo Bonzini }
109249ab747fSPaolo Bonzini
109349ab747fSPaolo Bonzini static const VMStateDescription vmstate_ac97_bm_regs = {
109449ab747fSPaolo Bonzini .name = "ac97_bm_regs",
109549ab747fSPaolo Bonzini .version_id = 1,
109649ab747fSPaolo Bonzini .minimum_version_id = 1,
1097856a6fe4SRichard Henderson .fields = (const VMStateField[]) {
109849ab747fSPaolo Bonzini VMSTATE_UINT32(bdbar, AC97BusMasterRegs),
109949ab747fSPaolo Bonzini VMSTATE_UINT8(civ, AC97BusMasterRegs),
110049ab747fSPaolo Bonzini VMSTATE_UINT8(lvi, AC97BusMasterRegs),
110149ab747fSPaolo Bonzini VMSTATE_UINT16(sr, AC97BusMasterRegs),
110249ab747fSPaolo Bonzini VMSTATE_UINT16(picb, AC97BusMasterRegs),
110349ab747fSPaolo Bonzini VMSTATE_UINT8(piv, AC97BusMasterRegs),
110449ab747fSPaolo Bonzini VMSTATE_UINT8(cr, AC97BusMasterRegs),
110549ab747fSPaolo Bonzini VMSTATE_UINT32(bd_valid, AC97BusMasterRegs),
110649ab747fSPaolo Bonzini VMSTATE_UINT32(bd.addr, AC97BusMasterRegs),
110749ab747fSPaolo Bonzini VMSTATE_UINT32(bd.ctl_len, AC97BusMasterRegs),
110849ab747fSPaolo Bonzini VMSTATE_END_OF_LIST()
110949ab747fSPaolo Bonzini }
111049ab747fSPaolo Bonzini };
111149ab747fSPaolo Bonzini
ac97_post_load(void * opaque,int version_id)111249ab747fSPaolo Bonzini static int ac97_post_load(void *opaque, int version_id)
111349ab747fSPaolo Bonzini {
111449ab747fSPaolo Bonzini uint8_t active[LAST_INDEX];
111549ab747fSPaolo Bonzini AC97LinkState *s = opaque;
111649ab747fSPaolo Bonzini
111749ab747fSPaolo Bonzini record_select(s, mixer_load(s, AC97_Record_Select));
111849ab747fSPaolo Bonzini set_volume(s, AC97_Master_Volume_Mute,
111949ab747fSPaolo Bonzini mixer_load(s, AC97_Master_Volume_Mute));
112049ab747fSPaolo Bonzini set_volume(s, AC97_PCM_Out_Volume_Mute,
112149ab747fSPaolo Bonzini mixer_load(s, AC97_PCM_Out_Volume_Mute));
112249ab747fSPaolo Bonzini set_volume(s, AC97_Record_Gain_Mute,
112349ab747fSPaolo Bonzini mixer_load(s, AC97_Record_Gain_Mute));
112449ab747fSPaolo Bonzini
112549ab747fSPaolo Bonzini active[PI_INDEX] = !!(s->bm_regs[PI_INDEX].cr & CR_RPBM);
112649ab747fSPaolo Bonzini active[PO_INDEX] = !!(s->bm_regs[PO_INDEX].cr & CR_RPBM);
112749ab747fSPaolo Bonzini active[MC_INDEX] = !!(s->bm_regs[MC_INDEX].cr & CR_RPBM);
112849ab747fSPaolo Bonzini reset_voices(s, active);
112949ab747fSPaolo Bonzini
113049ab747fSPaolo Bonzini s->bup_flag = 0;
113149ab747fSPaolo Bonzini s->last_samp = 0;
113249ab747fSPaolo Bonzini return 0;
113349ab747fSPaolo Bonzini }
113449ab747fSPaolo Bonzini
is_version_2(void * opaque,int version_id)113549ab747fSPaolo Bonzini static bool is_version_2(void *opaque, int version_id)
113649ab747fSPaolo Bonzini {
113749ab747fSPaolo Bonzini return version_id == 2;
113849ab747fSPaolo Bonzini }
113949ab747fSPaolo Bonzini
114049ab747fSPaolo Bonzini static const VMStateDescription vmstate_ac97 = {
114149ab747fSPaolo Bonzini .name = "ac97",
114249ab747fSPaolo Bonzini .version_id = 3,
114349ab747fSPaolo Bonzini .minimum_version_id = 2,
114449ab747fSPaolo Bonzini .post_load = ac97_post_load,
1145856a6fe4SRichard Henderson .fields = (const VMStateField[]) {
114649ab747fSPaolo Bonzini VMSTATE_PCI_DEVICE(dev, AC97LinkState),
114749ab747fSPaolo Bonzini VMSTATE_UINT32(glob_cnt, AC97LinkState),
114849ab747fSPaolo Bonzini VMSTATE_UINT32(glob_sta, AC97LinkState),
114949ab747fSPaolo Bonzini VMSTATE_UINT32(cas, AC97LinkState),
115049ab747fSPaolo Bonzini VMSTATE_STRUCT_ARRAY(bm_regs, AC97LinkState, 3, 1,
115149ab747fSPaolo Bonzini vmstate_ac97_bm_regs, AC97BusMasterRegs),
115249ab747fSPaolo Bonzini VMSTATE_BUFFER(mixer_data, AC97LinkState),
115349ab747fSPaolo Bonzini VMSTATE_UNUSED_TEST(is_version_2, 3),
115449ab747fSPaolo Bonzini VMSTATE_END_OF_LIST()
115549ab747fSPaolo Bonzini }
115649ab747fSPaolo Bonzini };
115749ab747fSPaolo Bonzini
nam_read(void * opaque,hwaddr addr,unsigned size)115849ab747fSPaolo Bonzini static uint64_t nam_read(void *opaque, hwaddr addr, unsigned size)
115949ab747fSPaolo Bonzini {
116049ab747fSPaolo Bonzini if ((addr / size) > 256) {
116149ab747fSPaolo Bonzini return -1;
116249ab747fSPaolo Bonzini }
116349ab747fSPaolo Bonzini
116449ab747fSPaolo Bonzini switch (size) {
116549ab747fSPaolo Bonzini case 1:
116649ab747fSPaolo Bonzini return nam_readb(opaque, addr);
116749ab747fSPaolo Bonzini case 2:
116849ab747fSPaolo Bonzini return nam_readw(opaque, addr);
116949ab747fSPaolo Bonzini case 4:
117049ab747fSPaolo Bonzini return nam_readl(opaque, addr);
117149ab747fSPaolo Bonzini default:
117249ab747fSPaolo Bonzini return -1;
117349ab747fSPaolo Bonzini }
117449ab747fSPaolo Bonzini }
117549ab747fSPaolo Bonzini
nam_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)117649ab747fSPaolo Bonzini static void nam_write(void *opaque, hwaddr addr, uint64_t val,
117749ab747fSPaolo Bonzini unsigned size)
117849ab747fSPaolo Bonzini {
117949ab747fSPaolo Bonzini if ((addr / size) > 256) {
118049ab747fSPaolo Bonzini return;
118149ab747fSPaolo Bonzini }
118249ab747fSPaolo Bonzini
118349ab747fSPaolo Bonzini switch (size) {
118449ab747fSPaolo Bonzini case 1:
118549ab747fSPaolo Bonzini nam_writeb(opaque, addr, val);
118649ab747fSPaolo Bonzini break;
118749ab747fSPaolo Bonzini case 2:
118849ab747fSPaolo Bonzini nam_writew(opaque, addr, val);
118949ab747fSPaolo Bonzini break;
119049ab747fSPaolo Bonzini case 4:
119149ab747fSPaolo Bonzini nam_writel(opaque, addr, val);
119249ab747fSPaolo Bonzini break;
119349ab747fSPaolo Bonzini }
119449ab747fSPaolo Bonzini }
119549ab747fSPaolo Bonzini
119649ab747fSPaolo Bonzini static const MemoryRegionOps ac97_io_nam_ops = {
119749ab747fSPaolo Bonzini .read = nam_read,
119849ab747fSPaolo Bonzini .write = nam_write,
119949ab747fSPaolo Bonzini .impl = {
120049ab747fSPaolo Bonzini .min_access_size = 1,
120149ab747fSPaolo Bonzini .max_access_size = 4,
120249ab747fSPaolo Bonzini },
120349ab747fSPaolo Bonzini .endianness = DEVICE_LITTLE_ENDIAN,
120449ab747fSPaolo Bonzini };
120549ab747fSPaolo Bonzini
nabm_read(void * opaque,hwaddr addr,unsigned size)120649ab747fSPaolo Bonzini static uint64_t nabm_read(void *opaque, hwaddr addr, unsigned size)
120749ab747fSPaolo Bonzini {
120849ab747fSPaolo Bonzini if ((addr / size) > 64) {
120949ab747fSPaolo Bonzini return -1;
121049ab747fSPaolo Bonzini }
121149ab747fSPaolo Bonzini
121249ab747fSPaolo Bonzini switch (size) {
121349ab747fSPaolo Bonzini case 1:
121449ab747fSPaolo Bonzini return nabm_readb(opaque, addr);
121549ab747fSPaolo Bonzini case 2:
121649ab747fSPaolo Bonzini return nabm_readw(opaque, addr);
121749ab747fSPaolo Bonzini case 4:
121849ab747fSPaolo Bonzini return nabm_readl(opaque, addr);
121949ab747fSPaolo Bonzini default:
122049ab747fSPaolo Bonzini return -1;
122149ab747fSPaolo Bonzini }
122249ab747fSPaolo Bonzini }
122349ab747fSPaolo Bonzini
nabm_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)122449ab747fSPaolo Bonzini static void nabm_write(void *opaque, hwaddr addr, uint64_t val,
122549ab747fSPaolo Bonzini unsigned size)
122649ab747fSPaolo Bonzini {
122749ab747fSPaolo Bonzini if ((addr / size) > 64) {
122849ab747fSPaolo Bonzini return;
122949ab747fSPaolo Bonzini }
123049ab747fSPaolo Bonzini
123149ab747fSPaolo Bonzini switch (size) {
123249ab747fSPaolo Bonzini case 1:
123349ab747fSPaolo Bonzini nabm_writeb(opaque, addr, val);
123449ab747fSPaolo Bonzini break;
123549ab747fSPaolo Bonzini case 2:
123649ab747fSPaolo Bonzini nabm_writew(opaque, addr, val);
123749ab747fSPaolo Bonzini break;
123849ab747fSPaolo Bonzini case 4:
123949ab747fSPaolo Bonzini nabm_writel(opaque, addr, val);
124049ab747fSPaolo Bonzini break;
124149ab747fSPaolo Bonzini }
124249ab747fSPaolo Bonzini }
124349ab747fSPaolo Bonzini
124449ab747fSPaolo Bonzini
124549ab747fSPaolo Bonzini static const MemoryRegionOps ac97_io_nabm_ops = {
124649ab747fSPaolo Bonzini .read = nabm_read,
124749ab747fSPaolo Bonzini .write = nabm_write,
124849ab747fSPaolo Bonzini .impl = {
124949ab747fSPaolo Bonzini .min_access_size = 1,
125049ab747fSPaolo Bonzini .max_access_size = 4,
125149ab747fSPaolo Bonzini },
125249ab747fSPaolo Bonzini .endianness = DEVICE_LITTLE_ENDIAN,
125349ab747fSPaolo Bonzini };
125449ab747fSPaolo Bonzini
ac97_on_reset(DeviceState * dev)125513377147SGerd Hoffmann static void ac97_on_reset(DeviceState *dev)
125649ab747fSPaolo Bonzini {
1257911a6afbSPhilippe Mathieu-Daudé AC97LinkState *s = AC97(dev);
125849ab747fSPaolo Bonzini
125949ab747fSPaolo Bonzini reset_bm_regs(s, &s->bm_regs[0]);
126049ab747fSPaolo Bonzini reset_bm_regs(s, &s->bm_regs[1]);
126149ab747fSPaolo Bonzini reset_bm_regs(s, &s->bm_regs[2]);
126249ab747fSPaolo Bonzini
126349ab747fSPaolo Bonzini /*
126449ab747fSPaolo Bonzini * Reset the mixer too. The Windows XP driver seems to rely on
126549ab747fSPaolo Bonzini * this. At least it wants to read the vendor id before it resets
126649ab747fSPaolo Bonzini * the codec manually.
126749ab747fSPaolo Bonzini */
126849ab747fSPaolo Bonzini mixer_reset(s);
126949ab747fSPaolo Bonzini }
127049ab747fSPaolo Bonzini
ac97_realize(PCIDevice * dev,Error ** errp)12719af21dbeSMarkus Armbruster static void ac97_realize(PCIDevice *dev, Error **errp)
127249ab747fSPaolo Bonzini {
1273417d430eSLi Qiang AC97LinkState *s = AC97(dev);
127449ab747fSPaolo Bonzini uint8_t *c = s->dev.config;
127549ab747fSPaolo Bonzini
1276cb94ff5fSMartin Kletzander if (!AUD_register_card ("ac97", &s->card, errp)) {
1277cb94ff5fSMartin Kletzander return;
1278cb94ff5fSMartin Kletzander }
1279cb94ff5fSMartin Kletzander
128049ab747fSPaolo Bonzini /* TODO: no need to override */
128149ab747fSPaolo Bonzini c[PCI_COMMAND] = 0x00; /* pcicmd pci command rw, ro */
128249ab747fSPaolo Bonzini c[PCI_COMMAND + 1] = 0x00;
128349ab747fSPaolo Bonzini
128449ab747fSPaolo Bonzini /* TODO: */
128549ab747fSPaolo Bonzini c[PCI_STATUS] = PCI_STATUS_FAST_BACK; /* pcists pci status rwc, ro */
128649ab747fSPaolo Bonzini c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_MEDIUM >> 8;
128749ab747fSPaolo Bonzini
128849ab747fSPaolo Bonzini c[PCI_CLASS_PROG] = 0x00; /* pi programming interface ro */
128949ab747fSPaolo Bonzini
129049ab747fSPaolo Bonzini /* TODO set when bar is registered. no need to override. */
129149ab747fSPaolo Bonzini /* nabmar native audio mixer base address rw */
129249ab747fSPaolo Bonzini c[PCI_BASE_ADDRESS_0] = PCI_BASE_ADDRESS_SPACE_IO;
129349ab747fSPaolo Bonzini c[PCI_BASE_ADDRESS_0 + 1] = 0x00;
129449ab747fSPaolo Bonzini c[PCI_BASE_ADDRESS_0 + 2] = 0x00;
129549ab747fSPaolo Bonzini c[PCI_BASE_ADDRESS_0 + 3] = 0x00;
129649ab747fSPaolo Bonzini
129749ab747fSPaolo Bonzini /* TODO set when bar is registered. no need to override. */
129849ab747fSPaolo Bonzini /* nabmbar native audio bus mastering base address rw */
129949ab747fSPaolo Bonzini c[PCI_BASE_ADDRESS_0 + 4] = PCI_BASE_ADDRESS_SPACE_IO;
130049ab747fSPaolo Bonzini c[PCI_BASE_ADDRESS_0 + 5] = 0x00;
130149ab747fSPaolo Bonzini c[PCI_BASE_ADDRESS_0 + 6] = 0x00;
130249ab747fSPaolo Bonzini c[PCI_BASE_ADDRESS_0 + 7] = 0x00;
130349ab747fSPaolo Bonzini
130449ab747fSPaolo Bonzini c[PCI_INTERRUPT_LINE] = 0x00; /* intr_ln interrupt line rw */
130549ab747fSPaolo Bonzini c[PCI_INTERRUPT_PIN] = 0x01; /* intr_pn interrupt pin ro */
130649ab747fSPaolo Bonzini
130764bde0f3SPaolo Bonzini memory_region_init_io(&s->io_nam, OBJECT(s), &ac97_io_nam_ops, s,
130864bde0f3SPaolo Bonzini "ac97-nam", 1024);
130964bde0f3SPaolo Bonzini memory_region_init_io(&s->io_nabm, OBJECT(s), &ac97_io_nabm_ops, s,
131064bde0f3SPaolo Bonzini "ac97-nabm", 256);
131149ab747fSPaolo Bonzini pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_nam);
131249ab747fSPaolo Bonzini pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_nabm);
1313cb94ff5fSMartin Kletzander
13140ce5e020SPhilippe Mathieu-Daudé ac97_on_reset(DEVICE(s));
131549ab747fSPaolo Bonzini }
131649ab747fSPaolo Bonzini
ac97_exit(PCIDevice * dev)131712351a91SLi Qiang static void ac97_exit(PCIDevice *dev)
131812351a91SLi Qiang {
1319417d430eSLi Qiang AC97LinkState *s = AC97(dev);
132012351a91SLi Qiang
132112351a91SLi Qiang AUD_close_in(&s->card, s->voice_pi);
132212351a91SLi Qiang AUD_close_out(&s->card, s->voice_po);
132312351a91SLi Qiang AUD_close_in(&s->card, s->voice_mc);
132412351a91SLi Qiang AUD_remove_card(&s->card);
132512351a91SLi Qiang }
132612351a91SLi Qiang
132749ab747fSPaolo Bonzini static Property ac97_properties[] = {
132888e47b9aSKővágó, Zoltán DEFINE_AUDIO_PROPERTIES(AC97LinkState, card),
132949ab747fSPaolo Bonzini DEFINE_PROP_END_OF_LIST(),
133049ab747fSPaolo Bonzini };
133149ab747fSPaolo Bonzini
ac97_class_init(ObjectClass * klass,void * data)133249ab747fSPaolo Bonzini static void ac97_class_init(ObjectClass *klass, void *data)
133349ab747fSPaolo Bonzini {
133449ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass);
133549ab747fSPaolo Bonzini PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
133649ab747fSPaolo Bonzini
13379af21dbeSMarkus Armbruster k->realize = ac97_realize;
133812351a91SLi Qiang k->exit = ac97_exit;
133949ab747fSPaolo Bonzini k->vendor_id = PCI_VENDOR_ID_INTEL;
134049ab747fSPaolo Bonzini k->device_id = PCI_DEVICE_ID_INTEL_82801AA_5;
134149ab747fSPaolo Bonzini k->revision = 0x01;
134249ab747fSPaolo Bonzini k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
1343125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
134449ab747fSPaolo Bonzini dc->desc = "Intel 82801AA AC97 Audio";
134549ab747fSPaolo Bonzini dc->vmsd = &vmstate_ac97;
13464f67d30bSMarc-André Lureau device_class_set_props(dc, ac97_properties);
1347*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, ac97_on_reset);
134849ab747fSPaolo Bonzini }
134949ab747fSPaolo Bonzini
135049ab747fSPaolo Bonzini static const TypeInfo ac97_info = {
1351417d430eSLi Qiang .name = TYPE_AC97,
135249ab747fSPaolo Bonzini .parent = TYPE_PCI_DEVICE,
135349ab747fSPaolo Bonzini .instance_size = sizeof(AC97LinkState),
135449ab747fSPaolo Bonzini .class_init = ac97_class_init,
1355fd3b02c8SEduardo Habkost .interfaces = (InterfaceInfo[]) {
1356fd3b02c8SEduardo Habkost { INTERFACE_CONVENTIONAL_PCI_DEVICE },
1357fd3b02c8SEduardo Habkost { },
1358fd3b02c8SEduardo Habkost },
135949ab747fSPaolo Bonzini };
136049ab747fSPaolo Bonzini
ac97_register_types(void)136149ab747fSPaolo Bonzini static void ac97_register_types(void)
136249ab747fSPaolo Bonzini {
136349ab747fSPaolo Bonzini type_register_static(&ac97_info);
13642957f5adSGerd Hoffmann deprecated_register_soundhw("ac97", "Intel 82801AA AC97 Audio",
13652957f5adSGerd Hoffmann 0, TYPE_AC97);
136649ab747fSPaolo Bonzini }
136749ab747fSPaolo Bonzini
136849ab747fSPaolo Bonzini type_init(ac97_register_types)
1369