// Copyright 2018 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use crate::pci::ac97_regs::*; // AC97 Vendor ID const AC97_VENDOR_ID1: u16 = 0x8086; const AC97_VENDOR_ID2: u16 = 0x8086; // Master volume register is specified in 1.5dB steps. const MASTER_VOLUME_STEP_DB: f64 = 1.5; /// `Ac97Mixer` holds the mixer state for the AC97 bus. /// The mixer is used by calling the `readb`/`readw`/`readl` functions to read register values and /// the `writeb`/`writew`/`writel` functions to set register values. pub struct Ac97Mixer { // Mixer Registers master_volume_l: u8, master_volume_r: u8, master_mute: bool, mic_muted: bool, mic_20db: bool, mic_volume: u8, record_gain_l: u8, record_gain_r: u8, record_gain_mute: bool, pcm_out_vol_l: u16, pcm_out_vol_r: u16, pcm_out_mute: bool, power_down_control: u16, } impl Ac97Mixer { /// Creates an 'Ac97Mixer' with the standard default register values. pub fn new() -> Self { Ac97Mixer { master_volume_l: 0, master_volume_r: 0, master_mute: true, mic_muted: true, mic_20db: false, mic_volume: 0x8, record_gain_l: 0, record_gain_r: 0, record_gain_mute: true, pcm_out_vol_l: 0x8, pcm_out_vol_r: 0x8, pcm_out_mute: true, power_down_control: PD_REG_STATUS_MASK, // Report everything is ready. } } /// Reads a word from the register at `offset`. pub fn readw(&self, offset: u64) -> u16 { match offset { MIXER_MASTER_VOL_MUTE_02 => self.get_master_reg(), MIXER_MIC_VOL_MUTE_0E => self.get_mic_volume(), 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_VENDOR_ID1_7C => AC97_VENDOR_ID1, MIXER_VENDOR_ID2_7E => AC97_VENDOR_ID2, _ => 0, } } /// Writes a word `val` to the register `offset`. pub fn writew(&mut self, offset: u64, val: u16) { match offset { MIXER_MASTER_VOL_MUTE_02 => self.set_master_reg(val), MIXER_MIC_VOL_MUTE_0E => self.set_mic_volume(val), MIXER_PCM_OUT_VOL_MUTE_18 => self.set_pcm_out_volume(val), MIXER_REC_VOL_MUTE_1C => self.set_record_gain_reg(val), MIXER_POWER_DOWN_CONTROL_26 => self.set_power_down_reg(val), _ => (), } } /// Returns the mute status and left and right attenuation from the master volume register. pub fn get_master_volume(&self) -> (bool, f64, f64) { ( self.master_mute, f64::from(self.master_volume_l) * MASTER_VOLUME_STEP_DB, f64::from(self.master_volume_r) * MASTER_VOLUME_STEP_DB, ) } // Returns the master mute and l/r volumes (reg 0x02). fn get_master_reg(&self) -> u16 { let reg = (u16::from(self.master_volume_l)) << 8 | u16::from(self.master_volume_r); if self.master_mute { reg | MUTE_REG_BIT } else { reg } } // Handles writes to the master register (0x02). fn set_master_reg(&mut self, val: u16) { self.master_mute = val & MUTE_REG_BIT != 0; self.master_volume_r = (val & VOL_REG_MASK) as u8; self.master_volume_l = (val >> 8 & VOL_REG_MASK) as u8; } // Returns the value read in the Mic volume register (0x0e). fn get_mic_volume(&self) -> u16 { let mut reg = u16::from(self.mic_volume); if self.mic_muted { reg |= MUTE_REG_BIT; } if self.mic_20db { reg |= MIXER_MIC_20DB; } reg } // Sets the mic input mute, boost, and volume settings (0x0e). fn set_mic_volume(&mut self, val: u16) { self.mic_volume = (val & MIXER_VOL_MASK) as u8; self.mic_muted = val & MUTE_REG_BIT != 0; self.mic_20db = val & MIXER_MIC_20DB != 0; } // Returns the value read in the Mic volume register (0x18). fn get_pcm_out_volume(&self) -> u16 { let reg = (self.pcm_out_vol_l as u16) << 8 | self.pcm_out_vol_r as u16; if self.pcm_out_mute { reg | MUTE_REG_BIT } else { reg } } // Sets the pcm output mute and volume states (0x18). fn set_pcm_out_volume(&mut self, val: u16) { self.pcm_out_vol_r = val & MIXER_VOL_MASK; self.pcm_out_vol_l = (val >> MIXER_VOL_LEFT_SHIFT) & MIXER_VOL_MASK; self.pcm_out_mute = val & MUTE_REG_BIT != 0; } // Returns the record gain register (0x01c). fn get_record_gain_reg(&self) -> u16 { let reg = u16::from(self.record_gain_l) << 8 | u16::from(self.record_gain_r); if self.record_gain_mute { reg | MUTE_REG_BIT } else { reg } } // Handles writes to the record_gain register (0x1c). fn set_record_gain_reg(&mut self, val: u16) { self.record_gain_mute = val & MUTE_REG_BIT != 0; self.record_gain_r = (val & VOL_REG_MASK) as u8; self.record_gain_l = (val >> 8 & VOL_REG_MASK) as u8; } // Handles writes to the powerdown ctrl/status register (0x26). fn set_power_down_reg(&mut self, val: u16) { self.power_down_control = (val & !PD_REG_STATUS_MASK) | (self.power_down_control & PD_REG_STATUS_MASK); } }