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:
Judy Hsiao 2020-06-20 15:36:27 +08:00 committed by Commit Bot
parent 0130b08077
commit fedb4eddbf
3 changed files with 66 additions and 10 deletions

View file

@ -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);

View file

@ -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,

View file

@ -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;