📄 ac97.c
字号:
/* * Copyright (C) 2006 InnoTek Systemberatung GmbH * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License as published by the Free Software Foundation, * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE * distribution. VirtualBox OSE is distributed in the hope that it will * be useful, but WITHOUT ANY WARRANTY of any kind. * * If you received this file as part of a commercial VirtualBox * distribution, then only the terms of your commercial VirtualBox * license agreement apply instead of the previous paragraph. */#include "hw.h"#include "audiodev.h"#include "audio/audio.h"#include "pci.h"enum { AC97_Reset = 0x00, AC97_Master_Volume_Mute = 0x02, AC97_Headphone_Volume_Mute = 0x04, AC97_Master_Volume_Mono_Mute = 0x06, AC97_Master_Tone_RL = 0x08, AC97_PC_BEEP_Volume_Mute = 0x0A, AC97_Phone_Volume_Mute = 0x0C, AC97_Mic_Volume_Mute = 0x0E, AC97_Line_In_Volume_Mute = 0x10, AC97_CD_Volume_Mute = 0x12, AC97_Video_Volume_Mute = 0x14, AC97_Aux_Volume_Mute = 0x16, AC97_PCM_Out_Volume_Mute = 0x18, AC97_Record_Select = 0x1A, AC97_Record_Gain_Mute = 0x1C, AC97_Record_Gain_Mic_Mute = 0x1E, AC97_General_Purpose = 0x20, AC97_3D_Control = 0x22, AC97_AC_97_RESERVED = 0x24, AC97_Powerdown_Ctrl_Stat = 0x26, AC97_Extended_Audio_ID = 0x28, AC97_Extended_Audio_Ctrl_Stat = 0x2A, AC97_PCM_Front_DAC_Rate = 0x2C, AC97_PCM_Surround_DAC_Rate = 0x2E, AC97_PCM_LFE_DAC_Rate = 0x30, AC97_PCM_LR_ADC_Rate = 0x32, AC97_MIC_ADC_Rate = 0x34, AC97_6Ch_Vol_C_LFE_Mute = 0x36, AC97_6Ch_Vol_L_R_Surround_Mute = 0x38, AC97_Vendor_Reserved = 0x58, AC97_Vendor_ID1 = 0x7c, AC97_Vendor_ID2 = 0x7e};#define SOFT_VOLUME#define SR_FIFOE 16 /* rwc */#define SR_BCIS 8 /* rwc */#define SR_LVBCI 4 /* rwc */#define SR_CELV 2 /* ro */#define SR_DCH 1 /* ro */#define SR_VALID_MASK ((1 << 5) - 1)#define SR_WCLEAR_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)#define SR_RO_MASK (SR_DCH | SR_CELV)#define SR_INT_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)#define CR_IOCE 16 /* rw */#define CR_FEIE 8 /* rw */#define CR_LVBIE 4 /* rw */#define CR_RR 2 /* rw */#define CR_RPBM 1 /* rw */#define CR_VALID_MASK ((1 << 5) - 1)#define CR_DONT_CLEAR_MASK (CR_IOCE | CR_FEIE | CR_LVBIE)#define GC_WR 4 /* rw */#define GC_CR 2 /* rw */#define GC_VALID_MASK ((1 << 6) - 1)#define GS_MD3 (1<<17) /* rw */#define GS_AD3 (1<<16) /* rw */#define GS_RCS (1<<15) /* rwc */#define GS_B3S12 (1<<14) /* ro */#define GS_B2S12 (1<<13) /* ro */#define GS_B1S12 (1<<12) /* ro */#define GS_S1R1 (1<<11) /* rwc */#define GS_S0R1 (1<<10) /* rwc */#define GS_S1CR (1<<9) /* ro */#define GS_S0CR (1<<8) /* ro */#define GS_MINT (1<<7) /* ro */#define GS_POINT (1<<6) /* ro */#define GS_PIINT (1<<5) /* ro */#define GS_RSRVD ((1<<4)|(1<<3))#define GS_MOINT (1<<2) /* ro */#define GS_MIINT (1<<1) /* ro */#define GS_GSCI 1 /* rwc */#define GS_RO_MASK (GS_B3S12| \ GS_B2S12| \ GS_B1S12| \ GS_S1CR| \ GS_S0CR| \ GS_MINT| \ GS_POINT| \ GS_PIINT| \ GS_RSRVD| \ GS_MOINT| \ GS_MIINT)#define GS_VALID_MASK ((1 << 18) - 1)#define GS_WCLEAR_MASK (GS_RCS|GS_S1R1|GS_S0R1|GS_GSCI)#define BD_IOC (1<<31)#define BD_BUP (1<<30)#define EACS_VRA 1#define EACS_VRM 8#define VOL_MASK 0x1f#define MUTE_SHIFT 15#define REC_MASK 7enum { REC_MIC = 0, REC_CD, REC_VIDEO, REC_AUX, REC_LINE_IN, REC_STEREO_MIX, REC_MONO_MIX, REC_PHONE};typedef struct BD { uint32_t addr; uint32_t ctl_len;} BD;typedef struct AC97BusMasterRegs { uint32_t bdbar; /* rw 0 */ uint8_t civ; /* ro 0 */ uint8_t lvi; /* rw 0 */ uint16_t sr; /* rw 1 */ uint16_t picb; /* ro 0 */ uint8_t piv; /* ro 0 */ uint8_t cr; /* rw 0 */ unsigned int bd_valid; BD bd;} AC97BusMasterRegs;typedef struct AC97LinkState { PCIDevice *pci_dev; QEMUSoundCard card; uint32_t glob_cnt; uint32_t glob_sta; uint32_t cas; uint32_t last_samp; AC97BusMasterRegs bm_regs[3]; uint8_t mixer_data[256]; SWVoiceIn *voice_pi; SWVoiceOut *voice_po; SWVoiceIn *voice_mc; uint8_t silence[128]; uint32_t base[2]; int bup_flag;} AC97LinkState;enum { BUP_SET = 1, BUP_LAST = 2};#ifdef DEBUG_AC97#define dolog(...) AUD_log ("ac97", __VA_ARGS__)#else#define dolog(...)#endiftypedef struct PCIAC97LinkState { PCIDevice dev; AC97LinkState ac97;} PCIAC97LinkState;#define MKREGS(prefix, start) \enum { \ prefix ## _BDBAR = start, \ prefix ## _CIV = start + 4, \ prefix ## _LVI = start + 5, \ prefix ## _SR = start + 6, \ prefix ## _PICB = start + 8, \ prefix ## _PIV = start + 10, \ prefix ## _CR = start + 11 \}enum { PI_INDEX = 0, PO_INDEX, MC_INDEX, LAST_INDEX};MKREGS (PI, PI_INDEX * 16);MKREGS (PO, PO_INDEX * 16);MKREGS (MC, MC_INDEX * 16);enum { GLOB_CNT = 0x2c, GLOB_STA = 0x30, CAS = 0x34};#define GET_BM(index) (((index) >> 4) & 3)static void po_callback (void *opaque, int free);static void pi_callback (void *opaque, int avail);static void mc_callback (void *opaque, int avail);static void warm_reset (AC97LinkState *s){ (void) s;}static void cold_reset (AC97LinkState * s){ (void) s;}static void fetch_bd (AC97LinkState *s, AC97BusMasterRegs *r){ uint8_t b[8]; cpu_physical_memory_read (r->bdbar + r->civ * 8, b, 8); r->bd_valid = 1; r->bd.addr = le32_to_cpu (*(uint32_t *) &b[0]) & ~3; r->bd.ctl_len = le32_to_cpu (*(uint32_t *) &b[4]); r->picb = r->bd.ctl_len & 0xffff; dolog ("bd %2d addr=%#x ctl=%#06x len=%#x(%d bytes)\n", r->civ, r->bd.addr, r->bd.ctl_len >> 16, r->bd.ctl_len & 0xffff, (r->bd.ctl_len & 0xffff) << 1);}static void update_sr (AC97LinkState *s, AC97BusMasterRegs *r, uint32_t new_sr){ int event = 0; int level = 0; uint32_t new_mask = new_sr & SR_INT_MASK; uint32_t old_mask = r->sr & SR_INT_MASK; uint32_t masks[] = {GS_PIINT, GS_POINT, GS_MINT}; if (new_mask ^ old_mask) { /** @todo is IRQ deasserted when only one of status bits is cleared? */ if (!new_mask) { event = 1; level = 0; } else { if ((new_mask & SR_LVBCI) && (r->cr & CR_LVBIE)) { event = 1; level = 1; } if ((new_mask & SR_BCIS) && (r->cr & CR_IOCE)) { event = 1; level = 1; } } } r->sr = new_sr; dolog ("IOC%d LVB%d sr=%#x event=%d level=%d\n", r->sr & SR_BCIS, r->sr & SR_LVBCI, r->sr, event, level); if (!event) return; if (level) { s->glob_sta |= masks[r - s->bm_regs]; dolog ("set irq level=1\n"); qemu_set_irq(s->pci_dev->irq[0], 1); } else { s->glob_sta &= ~masks[r - s->bm_regs]; dolog ("set irq level=0\n"); qemu_set_irq(s->pci_dev->irq[0], 0); }}static void voice_set_active (AC97LinkState *s, int bm_index, int on){ switch (bm_index) { case PI_INDEX: AUD_set_active_in (s->voice_pi, on); break; case PO_INDEX: AUD_set_active_out (s->voice_po, on); break; case MC_INDEX: AUD_set_active_in (s->voice_mc, on); break; default: AUD_log ("ac97", "invalid bm_index(%d) in voice_set_active", bm_index); break; }}static void reset_bm_regs (AC97LinkState *s, AC97BusMasterRegs *r){ dolog ("reset_bm_regs\n"); r->bdbar = 0; r->civ = 0; r->lvi = 0; /** todo do we need to do that? */ update_sr (s, r, SR_DCH); r->picb = 0; r->piv = 0; r->cr = r->cr & CR_DONT_CLEAR_MASK; r->bd_valid = 0; voice_set_active (s, r - s->bm_regs, 0); memset (s->silence, 0, sizeof (s->silence));}static void mixer_store (AC97LinkState *s, uint32_t i, uint16_t v){ if (i + 2 > sizeof (s->mixer_data)) { dolog ("mixer_store: index %d out of bounds %d\n", i, sizeof (s->mixer_data)); return; } s->mixer_data[i + 0] = v & 0xff; s->mixer_data[i + 1] = v >> 8;}static uint16_t mixer_load (AC97LinkState *s, uint32_t i){ uint16_t val = 0xffff; if (i + 2 > sizeof (s->mixer_data)) { dolog ("mixer_store: index %d out of bounds %d\n", i, sizeof (s->mixer_data)); } else { val = s->mixer_data[i + 0] | (s->mixer_data[i + 1] << 8); } return val;}static void open_voice (AC97LinkState *s, int index, int freq){ audsettings_t as; as.freq = freq; as.nchannels = 2; as.fmt = AUD_FMT_S16; as.endianness = 0; switch (index) { case PI_INDEX: s->voice_pi = AUD_open_in ( &s->card, s->voice_pi, "ac97.pi", s, pi_callback, &as ); break; case PO_INDEX: s->voice_po = AUD_open_out ( &s->card, s->voice_po, "ac97.po", s, po_callback, &as ); break; case MC_INDEX: s->voice_mc = AUD_open_in ( &s->card, s->voice_mc, "ac97.mc", s, mc_callback, &as ); break; }}static void reset_voices (AC97LinkState *s, uint8_t active[LAST_INDEX]){ uint16_t freq; freq = mixer_load (s, AC97_PCM_LR_ADC_Rate); open_voice (s, PI_INDEX, freq); AUD_set_active_in (s->voice_pi, active[PI_INDEX]); freq = mixer_load (s, AC97_PCM_Front_DAC_Rate); open_voice (s, PO_INDEX, freq); AUD_set_active_out (s->voice_po, active[PO_INDEX]); freq = mixer_load (s, AC97_MIC_ADC_Rate); open_voice (s, MC_INDEX, freq); AUD_set_active_in (s->voice_mc, active[MC_INDEX]);}#ifdef USE_MIXERstatic void set_volume (AC97LinkState *s, int index, audmixerctl_t mt, uint32_t val){ int mute = (val >> MUTE_SHIFT) & 1; uint8_t rvol = VOL_MASK - (val & VOL_MASK); uint8_t lvol = VOL_MASK - ((val >> 8) & VOL_MASK); rvol = 255 * rvol / VOL_MASK; lvol = 255 * lvol / VOL_MASK;#ifdef SOFT_VOLUME if (index == AC97_Master_Volume_Mute) { AUD_set_volume_out (s->voice_po, mute, lvol, rvol); } else { AUD_set_volume (mt, &mute, &lvol, &rvol); }#else AUD_set_volume (mt, &mute, &lvol, &rvol);#endif rvol = VOL_MASK - ((VOL_MASK * rvol) / 255); lvol = VOL_MASK - ((VOL_MASK * lvol) / 255); mixer_store (s, index, val);}static audrecsource_t ac97_to_aud_record_source (uint8_t i){ switch (i) { case REC_MIC: return AUD_REC_MIC; case REC_CD: return AUD_REC_CD;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -