mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-06 02:25:23 +00:00
ac97: Support 2, 4 and 6 output channels
1. Turn on the Extended Audio ID Register D6-8 to indicate the capability to support PCM Center DAC(CDAC), Surround L&R DACs (SDAC), PCM LFE DAC (LDAC). 2. Turn on the Global Status Register D21:20 to support 4 and 6 channels on PCM out. 3. Read the Global Control Register to configure the output channel count. 4. Enable start_playback unit test, and add assert for output stream channel count. BUG=b:157433024 TEST=`sox -n -t s16 -r48000 -c2 - synth sine 440 vol 0.1 | aplay -D hw:0,0 -f S16_LE -r 48000 -c 2` TEST=`sox -n -t s16 -r48000 -c4 - synth sine 440 vol 0.1 | aplay -D hw:0,0 -f S16_LE -r 48000 -c 4` TEST=`sox -n -t s16 -r48000 -c6 - synth sine 440 vol 0.1 | aplay -D hw:0,0 -f S16_LE -r 48000 -c 6` TEST=cras_test_client --dump_a to check the num_channels of the opened stream matches the aplay -c param. TEST=cargo test Cq-Depend: chromium:2259312 Change-Id: I2e1e48e78387b1b4bba72c63e6c93bf7b8012ea8 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2256078 Tested-by: Judy Hsiao <judyhsiao@chromium.org> Auto-Submit: Judy Hsiao <judyhsiao@chromium.org> Commit-Queue: Chih-Yang Hsia <paulhsia@chromium.org> Reviewed-by: Chih-Yang Hsia <paulhsia@chromium.org>
This commit is contained in:
parent
0130b08077
commit
fedb4eddbf
3 changed files with 66 additions and 10 deletions
|
@ -25,7 +25,7 @@ use crate::pci::ac97_mixer::Ac97Mixer;
|
|||
use crate::pci::ac97_regs::*;
|
||||
|
||||
const DEVICE_SAMPLE_RATE: usize = 48000;
|
||||
const DEVICE_CHANNEL_COUNT: usize = 2;
|
||||
const DEVICE_INPUT_CHANNEL_COUNT: usize = 2;
|
||||
|
||||
// Bus Master registers. Keeps the state of the bus master register values. Used to share the state
|
||||
// between the main and audio threads.
|
||||
|
@ -69,6 +69,26 @@ impl Ac97BusMasterRegs {
|
|||
Ac97Function::Microphone => &mut self.mc_regs,
|
||||
}
|
||||
}
|
||||
|
||||
fn channel_count(&self, func: Ac97Function) -> usize {
|
||||
fn output_channel_count(glob_cnt: u32) -> usize {
|
||||
let val = (glob_cnt & GLOB_CNT_PCM_246_MASK) >> 20;
|
||||
match val {
|
||||
0 => 2,
|
||||
1 => 4,
|
||||
2 => 6,
|
||||
_ => {
|
||||
warn!("unknown channel_count: 0x{:x}", val);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match func {
|
||||
Ac97Function::Output => output_channel_count(self.glob_cnt),
|
||||
_ => DEVICE_INPUT_CHANNEL_COUNT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal error type used for reporting errors from guest memory reading.
|
||||
|
@ -295,7 +315,7 @@ impl Ac97BusMaster {
|
|||
regs.po_regs.picb
|
||||
} else {
|
||||
// Estimate how many samples have been played since the last audio callback.
|
||||
let num_channels = 2;
|
||||
let num_channels = regs.channel_count(Ac97Function::Output) as u64;
|
||||
let micros = regs.po_pointer_update_time.elapsed().subsec_micros();
|
||||
// Round down to the next 10 millisecond boundary. The linux driver often
|
||||
// assumes that two rapid reads from picb will return the same value.
|
||||
|
@ -499,7 +519,8 @@ impl Ac97BusMaster {
|
|||
};
|
||||
|
||||
let buffer_samples = current_buffer_size(self.regs.lock().func_regs(func), &self.mem)?;
|
||||
let buffer_frames = buffer_samples / DEVICE_CHANNEL_COUNT;
|
||||
let num_channels = self.regs.lock().channel_count(func);
|
||||
let buffer_frames = buffer_samples / num_channels;
|
||||
thread_info.thread_run.store(true, Ordering::Relaxed);
|
||||
let thread_run = thread_info.thread_run.clone();
|
||||
let thread_semaphore = thread_info.thread_semaphore.clone();
|
||||
|
@ -525,7 +546,7 @@ impl Ac97BusMaster {
|
|||
.audio_server
|
||||
.new_stream(
|
||||
direction,
|
||||
DEVICE_CHANNEL_COUNT,
|
||||
num_channels,
|
||||
SampleFormat::S16LE,
|
||||
DEVICE_SAMPLE_RATE,
|
||||
buffer_frames,
|
||||
|
@ -678,7 +699,7 @@ fn next_guest_buffer<'a>(
|
|||
let offset = get_buffer_offset(func_regs, mem, index)?
|
||||
.try_into()
|
||||
.map_err(|_| AudioError::InvalidBufferOffset)?;
|
||||
let frames = get_buffer_samples(func_regs, mem, index)? / DEVICE_CHANNEL_COUNT;
|
||||
let frames = get_buffer_samples(func_regs, mem, index)? / regs.channel_count(func);
|
||||
|
||||
Ok(Some(GuestBuffer {
|
||||
index,
|
||||
|
@ -959,8 +980,13 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // flaky - see crbug.com/1058881
|
||||
fn start_playback() {
|
||||
fn run_playback() {
|
||||
start_playback(2);
|
||||
start_playback(4);
|
||||
start_playback(6);
|
||||
}
|
||||
|
||||
fn start_playback(num_channels: usize) {
|
||||
const TIMEOUT: Duration = Duration::from_millis(500);
|
||||
const LVI_MASK: u8 = 0x1f; // Five bits for 32 total entries.
|
||||
const IOC_MASK: u32 = 0x8000_0000; // Interrupt on completion.
|
||||
|
@ -997,15 +1023,28 @@ mod test {
|
|||
bm.writeb(PO_LVI_15, LVI_MASK, &mixer);
|
||||
assert_eq!(bm.readb(PO_CIV_14), 0);
|
||||
|
||||
// Set channel count.
|
||||
let mut cnt = bm.readl(GLOB_CNT_2C);
|
||||
cnt &= !GLOB_CNT_PCM_246_MASK;
|
||||
if num_channels == 4 {
|
||||
cnt |= GLOB_CNT_PCM_4;
|
||||
} else if num_channels == 6 {
|
||||
cnt |= GLOB_CNT_PCM_6;
|
||||
}
|
||||
bm.writel(GLOB_CNT_2C, cnt);
|
||||
|
||||
// Start.
|
||||
bm.writeb(PO_CR_1B, CR_IOCE | CR_RPBM, &mixer);
|
||||
assert_eq!(bm.readw(PO_PICB_18), 0);
|
||||
// TODO(crbug.com/1058881): The test is flaky in builder.
|
||||
// assert_eq!(bm.readw(PO_PICB_18), 0);
|
||||
|
||||
let mut stream = stream_source.get_last_stream();
|
||||
// Trigger callback and see that CIV has not changed, since only 1
|
||||
// buffer has been sent.
|
||||
assert!(stream.trigger_callback_with_timeout(TIMEOUT));
|
||||
|
||||
assert_eq!(stream.num_channels(), num_channels);
|
||||
|
||||
let mut civ = bm.readb(PO_CIV_14);
|
||||
assert_eq!(civ, 0);
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ use crate::pci::ac97_regs::*;
|
|||
// AC97 Vendor ID
|
||||
const AC97_VENDOR_ID1: u16 = 0x8086;
|
||||
const AC97_VENDOR_ID2: u16 = 0x8086;
|
||||
// Extented Audio ID
|
||||
const AC97_EXTENDED_ID: u16 = MIXER_EI_CDAC | MIXER_EI_SDAC | MIXER_EI_LDAC;
|
||||
|
||||
// Master volume register is specified in 1.5dB steps.
|
||||
const MASTER_VOLUME_STEP_DB: f64 = 1.5;
|
||||
|
@ -59,6 +61,7 @@ impl Ac97Mixer {
|
|||
MIXER_PCM_OUT_VOL_MUTE_18 => self.get_pcm_out_volume(),
|
||||
MIXER_REC_VOL_MUTE_1C => self.get_record_gain_reg(),
|
||||
MIXER_POWER_DOWN_CONTROL_26 => self.power_down_control,
|
||||
MIXER_EXTENDED_AUDIO_ID_28 => AC97_EXTENDED_ID,
|
||||
MIXER_VENDOR_ID1_7C => AC97_VENDOR_ID1,
|
||||
MIXER_VENDOR_ID2_7E => AC97_VENDOR_ID2,
|
||||
_ => 0,
|
||||
|
|
|
@ -37,9 +37,15 @@ pub const MIXER_MIC_VOL_MUTE_0E: u64 = 0x0e;
|
|||
pub const MIXER_PCM_OUT_VOL_MUTE_18: u64 = 0x18;
|
||||
pub const MIXER_REC_VOL_MUTE_1C: u64 = 0x1c;
|
||||
pub const MIXER_POWER_DOWN_CONTROL_26: u64 = 0x26;
|
||||
pub const MIXER_EXTENDED_AUDIO_ID_28: u64 = 0x28;
|
||||
pub const MIXER_VENDOR_ID1_7C: u64 = 0x7c;
|
||||
pub const MIXER_VENDOR_ID2_7E: u64 = 0x7e;
|
||||
|
||||
// Extended Audio ID Bits.
|
||||
pub const MIXER_EI_CDAC: u16 = 0x0040; // PCM Center DAC is available.
|
||||
pub const MIXER_EI_SDAC: u16 = 0x0080; // PCM Surround DAC is available.
|
||||
pub const MIXER_EI_LDAC: u16 = 0x0100; // PCM LFE DAC is available.
|
||||
|
||||
// Bus Master regs from ICH spec:
|
||||
// 00h PI_BDBAR PCM In Buffer Descriptor list Base Address Register
|
||||
// 04h PI_CIV PCM In Current Index Value
|
||||
|
@ -72,10 +78,18 @@ pub const GLOB_CNT_COLD_RESET: u32 = 0x0000_0002;
|
|||
pub const GLOB_CNT_WARM_RESET: u32 = 0x0000_0004;
|
||||
pub const GLOB_CNT_STABLE_BITS: u32 = 0x0000_007f; // Bits not affected by reset.
|
||||
|
||||
// PCM 4/6 Enable bits
|
||||
pub const GLOB_CNT_PCM_2: u32 = 0x0000_0000; // 2 channels
|
||||
pub const GLOB_CNT_PCM_4: u32 = 0x0010_0000; // 4 channels
|
||||
pub const GLOB_CNT_PCM_6: u32 = 0x0020_0000; // 6 channels
|
||||
pub const GLOB_CNT_PCM_246_MASK: u32 = GLOB_CNT_PCM_4 | GLOB_CNT_PCM_6; // channel mask
|
||||
|
||||
// Global status
|
||||
pub const GLOB_STA_30: u64 = 0x30;
|
||||
pub const GLOB_STA_RESET_VAL: u32 = 0x0000_0100; // primary codec ready set.
|
||||
// glob_sta bits
|
||||
// Primary codec ready set and turn on D20:21 to support 4 and 6 channels on PCM out.
|
||||
pub const GLOB_STA_RESET_VAL: u32 = 0x0030_0100;
|
||||
|
||||
// glob_sta bits
|
||||
pub const GS_MD3: u32 = 1 << 17;
|
||||
pub const GS_AD3: u32 = 1 << 16;
|
||||
pub const GS_RCS: u32 = 1 << 15;
|
||||
|
|
Loading…
Reference in a new issue