DevIchAc97.cpp revision 24d466e56e3082b3497e64b59d6ecdd42359258d
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * VBox ICH AC97 Audio Controller
e64031e20c39650a7bc902a3e1aba613b9415deevboxsync * Copyright (C) 2006 InnoTek Systemberatung GmbH
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * available from http://www.virtualbox.org. This file is free software;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * you can redistribute it and/or modify it under the terms of the GNU
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * General Public License as published by the Free Software Foundation,
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * distribution. VirtualBox OSE is distributed in the hope that it will
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * be useful, but WITHOUT ANY WARRANTY of any kind.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * If you received this file as part of a commercial VirtualBox
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * distribution, then only the terms of your commercial VirtualBox
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync * license agreement apply instead of the previous paragraph.
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync/*******************************************************************************
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync* Header Files *
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync*******************************************************************************/
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsyncextern "C" {
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync//#define USE_MIXER
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync#define SR_BCIS BIT(3) /* rwc, buffer completion interrupt status */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync#define SR_LVBCI BIT(2) /* rwc, last valid buffer completion interrupt */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync#define SR_CELV BIT(1) /* ro, current equals last valid */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync#define SR_WCLEAR_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync#define CR_DONT_CLEAR_MASK (CR_IOCE | CR_FEIE | CR_LVBIE)
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync#define GS_WCLEAR_MASK (GS_RCS|GS_S1R1|GS_S0R1|GS_GSCI)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync/** Buffer Descriptor */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync#define BD_IOC BIT(31) /* Interrupt on Completion */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsynctypedef struct BD
93f91841f87620d1cb6d0238b3d0d5e52cd3b9a4vboxsync uint32_t bdbar; /* rw 0, buffer descriptor list base address register */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync uint16_t picb; /* ro 0, position in current buffer */
44a2ecaf2d0fc196ab76cab13b3f909299e386d1vboxsync /** Global Control (Bus Master Control Register) */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /** Global Status (Bus Master Control Register) */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /** Codec Access Semaphore Register (Bus Master Control Register) */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /** Bus Master Control Registers for PCM in, PCM out, and Mic in */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync /** PCM in */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync /** PCM out */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync /** Mic in */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync /** Pointer to the device instance. */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync /** Pointer to the connector of the attached audio driver. */
0dd6dfbebcda0af90da4413aaea5f3b9d1817556vboxsync /** Pointer to the attached audio driver. */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync /** The base interface. */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync /** Base port of the I/O space region. */
44a2ecaf2d0fc196ab76cab13b3f909299e386d1vboxsync#define ICHAC97STATE_2_DEVINS(pAC97) ((pAC97)->pDevIns)
f687f34bd232be13744edbc0cc5155fa5d4540edvboxsync#define PCIDEV_2_ICHAC97STATE(pPciDev) ((PCIAC97LinkState *)(pPciDev))
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync/** Fetch Buffer Descriptor at _CIV */
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsyncstatic void fetch_bd (AC97LinkState *s, AC97BusMasterRegs *r)
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync PDMDevHlpPhysRead (pDevIns, r->bdbar + r->civ * 8, b, sizeof(b));
4569bf0ad094b40d2e177299a00d37e94d28616cvboxsync#error Please adapt the code (audio buffers are little endian)!
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync Log (("ac97: bd %2d addr=%#x ctl=%#06x len=%#x(%d bytes)\n",
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync r->bd.ctl_len & 0xffff, (r->bd.ctl_len & 0xffff) << 1));
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync * Update the BM status register
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsyncstatic void update_sr (AC97LinkState *s, AC97BusMasterRegs *r, uint32_t new_sr)
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync /** @todo is IRQ deasserted when only one of status bits is cleared? */
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync else if ((new_mask & SR_LVBCI) && (r->cr & CR_LVBIE))
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync else if ((new_mask & SR_BCIS) && (r->cr & CR_IOCE))
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync Log (("ac97: IOC%d LVB%d sr=%#x event=%d level=%d\n",
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync r->sr & SR_BCIS, r->sr & SR_LVBCI, r->sr, event, level));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncstatic void voice_set_active (AC97LinkState *s, int bm_index, int on)
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync case PI_INDEX: AUD_set_active_in (s->voice_pi, on); break;
71f6a34b72f9cc873da208630959de49df1a28a5vboxsync case PO_INDEX: AUD_set_active_out(s->voice_po, on); break;
71f6a34b72f9cc873da208630959de49df1a28a5vboxsync case MC_INDEX: AUD_set_active_in (s->voice_mc, on); break;
d4e9ccea0ea1ed303b5708ff94f6c202755f0dc6vboxsyncstatic void reset_bm_regs (AC97LinkState *s, AC97BusMasterRegs *r)
d7125f3a1b435761c393f9ec406e85a73ae2a3e7vboxsync /** @todo do we need to do that? */
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncstatic void mixer_store (AC97LinkState *s, uint32_t i, uint16_t v)
0c4004948fca34f2db87e7b38013137e9472c306vboxsync Log (("ac97: mixer_store: index %d out of bounds %d\n",
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync i, sizeof (s->mixer_data)));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncstatic uint16_t mixer_load (AC97LinkState *s, uint32_t i)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync Log (("ac97: mixer_store: index %d out of bounds %d\n",
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync i, sizeof (s->mixer_data)));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync val = s->mixer_data[i + 0] | (s->mixer_data[i + 1] << 8);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncstatic void open_voice (AC97LinkState *s, int index, int freq)
d4e9ccea0ea1ed303b5708ff94f6c202755f0dc6vboxsync s->voice_pi = AUD_open_in (&s->card, s->voice_pi, "ac97.pi",
d4e9ccea0ea1ed303b5708ff94f6c202755f0dc6vboxsync LogRel(("AC97: open PI freq=%d (%s)\n", freq, s->voice_pi ? "ok" : "FAIL"));
d4e9ccea0ea1ed303b5708ff94f6c202755f0dc6vboxsync s->voice_po = AUD_open_out (&s->card, s->voice_po, "ac97.po",
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync LogRel(("AC97: open PO freq=%d (%s)\n", freq, s->voice_po ? "ok" : "FAIL"));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync s->voice_mc = AUD_open_in (&s->card, s->voice_mc, "ac97.mc",
d4e9ccea0ea1ed303b5708ff94f6c202755f0dc6vboxsync LogRel(("AC97: open MC freq=%d (%s)\n", freq, s->voice_mc ? "ok" : "FAIL"));
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsyncstatic void reset_voices (AC97LinkState *s, uint8_t active[LAST_INDEX])
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync AUD_set_active_out (s->voice_po, active[PO_INDEX]);
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsyncstatic void set_volume (AC97LinkState *s, int index,
0c4004948fca34f2db87e7b38013137e9472c306vboxsync AUD_set_volume_out (s->voice_po, mute, lvol, rvol);
71f6a34b72f9cc873da208630959de49df1a28a5vboxsyncstatic audrecsource_t ac97_to_aud_record_source (uint8_t i)
71f6a34b72f9cc873da208630959de49df1a28a5vboxsync default: Log (("ac97: Unknown record source %d, using MIC\n", i));
71f6a34b72f9cc873da208630959de49df1a28a5vboxsyncstatic uint8_t aud_to_ac97_record_source (audrecsource_t rs)
71f6a34b72f9cc873da208630959de49df1a28a5vboxsync default: Log (("ac97: Unknown audio recording source %d using MIC\n", rs));
0c4004948fca34f2db87e7b38013137e9472c306vboxsyncstatic void record_select (AC97LinkState *s, uint32_t val)
0c4004948fca34f2db87e7b38013137e9472c306vboxsync audrecsource_t ars = ac97_to_aud_record_source (rs);
d4e9ccea0ea1ed303b5708ff94f6c202755f0dc6vboxsync audrecsource_t als = ac97_to_aud_record_source (ls);
0c4004948fca34f2db87e7b38013137e9472c306vboxsync mixer_store (s, AC97_Record_Select, rs | (ls << 8));
71f6a34b72f9cc873da208630959de49df1a28a5vboxsync mixer_store (s, AC97_Master_Volume_Mono_Mute , 0x8000);
71f6a34b72f9cc873da208630959de49df1a28a5vboxsync mixer_store (s, AC97_PC_BEEP_Volume_Mute , 0x0000);
d4e9ccea0ea1ed303b5708ff94f6c202755f0dc6vboxsync mixer_store (s, AC97_Record_Gain_Mic_Mute , 0x8000);
8f0fc87a72dee210b62acc9dd859a4bebf8bfb33vboxsync mixer_store (s, AC97_Powerdown_Ctrl_Stat , 0x000f);
8f0fc87a72dee210b62acc9dd859a4bebf8bfb33vboxsync * Sigmatel 9700 (STAC9700)
8f0fc87a72dee210b62acc9dd859a4bebf8bfb33vboxsync mixer_store (s, AC97_Vendor_ID2 , 0x7600); /* 7608 */
8f0fc87a72dee210b62acc9dd859a4bebf8bfb33vboxsync mixer_store (s, AC97_Extended_Audio_Ctrl_Stat, 0x0009);
0c4004948fca34f2db87e7b38013137e9472c306vboxsync mixer_store (s, AC97_PCM_Surround_DAC_Rate , 0xbb80);
0c4004948fca34f2db87e7b38013137e9472c306vboxsync set_volume (s, AC97_Master_Volume_Mute, AUD_MIXER_VOLUME, 0x8000);
0c4004948fca34f2db87e7b38013137e9472c306vboxsync set_volume (s, AC97_PCM_Out_Volume_Mute, AUD_MIXER_PCM, 0x8808);
0c4004948fca34f2db87e7b38013137e9472c306vboxsync set_volume (s, AC97_Line_In_Volume_Mute, AUD_MIXER_LINE_IN, 0x8808);
71f6a34b72f9cc873da208630959de49df1a28a5vboxsyncstatic int write_audio (AC97LinkState *s, AC97BusMasterRegs *r,
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync PDMDevHlpPhysRead (pDevIns, addr, tmpbuf, to_copy);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync Log (("ac97: write_audio max=%x to_copy=%x copied=%x\n",
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncstatic void write_bup (AC97LinkState *s, int elapsed)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync unsigned int i;
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsync unsigned int temp = audio_MIN ((unsigned int)elapsed, sizeof (s->silence));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync int copied = AUD_write (s->voice_po, s->silence, temp);
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsyncstatic int read_audio (AC97LinkState *s, AC97BusMasterRegs *r,
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync SWVoiceIn *voice = (r - s->bm_regs) == MC_INDEX ? s->voice_mc : s->voice_pi;
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync PDMDevHlpPhysWrite (pDevIns, addr, tmpbuf, acquired);
ea779b55cc87f3e3fadddca4672c6697c82606edvboxsyncstatic void transfer_audio (AC97LinkState *s, int index, int elapsed)
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync Log (("ac97: Underrun civ (%d) == lvi (%d)\n", r->civ, r->lvi));
9dca051a5f8ff457ef1692990f6ecfa280daf265vboxsync s->bup_flag = (r->bd.ctl_len & BD_BUP) ? BUP_LAST : 0;
switch (cb)
*pu32 = ~0U;
switch (index)
case CAS:
case PI_CIV:
case PO_CIV:
case MC_CIV:
case PI_LVI:
case PO_LVI:
case MC_LVI:
case PI_PIV:
case PO_PIV:
case MC_PIV:
case PI_CR:
case PO_CR:
case MC_CR:
case PI_SR:
case PO_SR:
case MC_SR:
*pu32 = ~0U;
switch (index)
case PI_SR:
case PO_SR:
case MC_SR:
case PI_PICB:
case PO_PICB:
case MC_PICB:
*pu32 = ~0U;
switch (index)
case PI_BDBAR:
case PO_BDBAR:
case MC_BDBAR:
case PI_CIV:
case PO_CIV:
case MC_CIV:
case PI_PICB:
case PO_PICB:
case MC_PICB:
case GLOB_CNT:
case GLOB_STA:
return VERR_IOM_IOPORT_UNUSED;
return VINF_SUCCESS;
switch (cb)
switch (index)
case PI_LVI:
case PO_LVI:
case MC_LVI:
fetch_bd (s, r);
case PI_CR:
case PO_CR:
case MC_CR:
reset_bm_regs (s, r);
fetch_bd (s, r);
case PI_SR:
case PO_SR:
case MC_SR:
switch (index)
case PI_SR:
case PO_SR:
case MC_SR:
switch (index)
case PI_BDBAR:
case PO_BDBAR:
case MC_BDBAR:
case GLOB_CNT:
warm_reset (s);
cold_reset (s);
case GLOB_STA:
return VINF_SUCCESS;
switch (cb)
s->cas = 0;
*pu32 = ~0U;
*pu32 = ~0U;
s->cas = 0;
switch (index)
s->cas = 0;
*pu32 = ~0U;
return VERR_IOM_IOPORT_UNUSED;
return VINF_SUCCESS;
switch (cb)
s->cas = 0;
s->cas = 0;
switch (index)
case AC97_Reset:
mixer_reset (s);
case AC97_Powerdown_Ctrl_Stat:
#ifdef USE_MIXER
case AC97_Master_Volume_Mute:
case AC97_PCM_Out_Volume_Mute:
case AC97_Line_In_Volume_Mute:
case AC97_Record_Select:
case AC97_Master_Volume_Mute:
case AC97_PCM_Out_Volume_Mute:
case AC97_Line_In_Volume_Mute:
case AC97_Record_Select:
case AC97_Vendor_ID1:
case AC97_Vendor_ID2:
case AC97_Extended_Audio_ID:
case AC97_PCM_Front_DAC_Rate:
u32));
case AC97_MIC_ADC_Rate:
u32));
case AC97_PCM_LR_ADC_Rate:
u32));
s->cas = 0;
return VINF_SUCCESS;
int rc;
if (iRegion == 0)
return rc;
return VINF_SUCCESS;
size_t i;
return VINF_SUCCESS;
size_t i;
AssertFailed();
#ifdef USE_MIXER
s->bup_flag = 0;
s->last_samp = 0;
return VINF_SUCCESS;
switch (enmInterface)
case PDMINTERFACE_BASE:
return NULL;
int rc;
return rc;
return rc;
return rc;
return rc;
return rc;
return VINF_SUCCESS;
sizeof(PCIAC97LinkState),
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,