1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use crate::pci::ac97_regs::*;
6 
7 // AC97 Vendor ID
8 const AC97_VENDOR_ID1: u16 = 0x8086;
9 const AC97_VENDOR_ID2: u16 = 0x8086;
10 // Extented Audio ID
11 const AC97_EXTENDED_ID: u16 = MIXER_EI_VRA | MIXER_EI_CDAC | MIXER_EI_SDAC | MIXER_EI_LDAC;
12 
13 // Master volume register is specified in 1.5dB steps.
14 const MASTER_VOLUME_STEP_DB: f64 = 1.5;
15 
16 /// `Ac97Mixer` holds the mixer state for the AC97 bus.
17 /// The mixer is used by calling the `readb`/`readw`/`readl` functions to read register values and
18 /// the `writeb`/`writew`/`writel` functions to set register values.
19 pub struct Ac97Mixer {
20     // Mixer Registers
21     master_volume_l: u8,
22     master_volume_r: u8,
23     master_mute: bool,
24     mic_muted: bool,
25     mic_20db: bool,
26     mic_volume: u8,
27     record_gain_l: u8,
28     record_gain_r: u8,
29     record_gain_mute: bool,
30     pcm_out_vol_l: u16,
31     pcm_out_vol_r: u16,
32     pcm_out_mute: bool,
33     power_down_control: u16,
34     ext_audio_status_ctl: u16,
35     pcm_front_dac_rate: u16,
36     pcm_surr_dac_rate: u16,
37     pcm_lfe_dac_rate: u16,
38 }
39 
40 impl Ac97Mixer {
41     /// Creates an 'Ac97Mixer' with the standard default register values.
new() -> Self42     pub fn new() -> Self {
43         Ac97Mixer {
44             master_volume_l: 0,
45             master_volume_r: 0,
46             master_mute: true,
47             mic_muted: true,
48             mic_20db: false,
49             mic_volume: 0x8,
50             record_gain_l: 0,
51             record_gain_r: 0,
52             record_gain_mute: true,
53             pcm_out_vol_l: 0x8,
54             pcm_out_vol_r: 0x8,
55             pcm_out_mute: true,
56             power_down_control: PD_REG_STATUS_MASK, // Report everything is ready.
57             ext_audio_status_ctl: 0,
58             // Default to 48 kHz.
59             pcm_front_dac_rate: 0xBB80,
60             pcm_surr_dac_rate: 0xBB80,
61             pcm_lfe_dac_rate: 0xBB80,
62         }
63     }
64 
reset(&mut self)65     pub fn reset(&mut self) {
66         // Upon reset, the audio sample rate registers default to 48 kHz, and VRA=0.
67         self.ext_audio_status_ctl &= !MIXER_EI_VRA;
68         self.pcm_front_dac_rate = 0xBB80;
69         self.pcm_surr_dac_rate = 0xBB80;
70         self.pcm_lfe_dac_rate = 0xBB80;
71     }
72 
73     /// Reads a word from the register at `offset`.
readw(&self, offset: u64) -> u1674     pub fn readw(&self, offset: u64) -> u16 {
75         match offset {
76             MIXER_RESET_00 => BC_DEDICATED_MIC,
77             MIXER_MASTER_VOL_MUTE_02 => self.get_master_reg(),
78             MIXER_MIC_VOL_MUTE_0E => self.get_mic_volume(),
79             MIXER_PCM_OUT_VOL_MUTE_18 => self.get_pcm_out_volume(),
80             MIXER_REC_VOL_MUTE_1C => self.get_record_gain_reg(),
81             MIXER_POWER_DOWN_CONTROL_26 => self.power_down_control,
82             MIXER_EXTENDED_AUDIO_ID_28 => AC97_EXTENDED_ID,
83             MIXER_VENDOR_ID1_7C => AC97_VENDOR_ID1,
84             MIXER_VENDOR_ID2_7E => AC97_VENDOR_ID2,
85             MIXER_EXTENDED_AUDIO_STATUS_CONTROL_28 => self.ext_audio_status_ctl,
86             MIXER_PCM_FRONT_DAC_RATE_2C => self.pcm_front_dac_rate,
87             MIXER_PCM_SURR_DAC_RATE_2E => self.pcm_surr_dac_rate,
88             MIXER_PCM_LFE_DAC_RATE_30 => self.pcm_lfe_dac_rate,
89             _ => 0,
90         }
91     }
92 
93     /// Writes a word `val` to the register `offset`.
writew(&mut self, offset: u64, val: u16)94     pub fn writew(&mut self, offset: u64, val: u16) {
95         match offset {
96             MIXER_RESET_00 => self.reset(),
97             MIXER_MASTER_VOL_MUTE_02 => self.set_master_reg(val),
98             MIXER_MIC_VOL_MUTE_0E => self.set_mic_volume(val),
99             MIXER_PCM_OUT_VOL_MUTE_18 => self.set_pcm_out_volume(val),
100             MIXER_REC_VOL_MUTE_1C => self.set_record_gain_reg(val),
101             MIXER_POWER_DOWN_CONTROL_26 => self.set_power_down_reg(val),
102             MIXER_EXTENDED_AUDIO_STATUS_CONTROL_28 => self.ext_audio_status_ctl = val,
103             MIXER_PCM_FRONT_DAC_RATE_2C => self.pcm_front_dac_rate = val,
104             MIXER_PCM_SURR_DAC_RATE_2E => self.pcm_surr_dac_rate = val,
105             MIXER_PCM_LFE_DAC_RATE_30 => self.pcm_lfe_dac_rate = val,
106             _ => (),
107         }
108     }
109 
110     /// Returns the mute status and left and right attenuation from the master volume register.
get_master_volume(&self) -> (bool, f64, f64)111     pub fn get_master_volume(&self) -> (bool, f64, f64) {
112         (
113             self.master_mute,
114             f64::from(self.master_volume_l) * MASTER_VOLUME_STEP_DB,
115             f64::from(self.master_volume_r) * MASTER_VOLUME_STEP_DB,
116         )
117     }
118 
119     /// Returns the front sample rate (reg 0x2c).
get_sample_rate(&self) -> u16120     pub fn get_sample_rate(&self) -> u16 {
121         // MIXER_PCM_FRONT_DAC_RATE_2C, MIXER_PCM_SURR_DAC_RATE_2E, and MIXER_PCM_LFE_DAC_RATE_30
122         // are updated to the same rate when playback with 2,4 and 6 tubes.
123         self.pcm_front_dac_rate
124     }
125 
126     // Returns the master mute and l/r volumes (reg 0x02).
get_master_reg(&self) -> u16127     fn get_master_reg(&self) -> u16 {
128         let reg = (u16::from(self.master_volume_l)) << 8 | u16::from(self.master_volume_r);
129         if self.master_mute {
130             reg | MUTE_REG_BIT
131         } else {
132             reg
133         }
134     }
135 
136     // Handles writes to the master register (0x02).
set_master_reg(&mut self, val: u16)137     fn set_master_reg(&mut self, val: u16) {
138         self.master_mute = val & MUTE_REG_BIT != 0;
139         self.master_volume_r = (val & VOL_REG_MASK) as u8;
140         self.master_volume_l = (val >> 8 & VOL_REG_MASK) as u8;
141     }
142 
143     // Returns the value read in the Mic volume register (0x0e).
get_mic_volume(&self) -> u16144     fn get_mic_volume(&self) -> u16 {
145         let mut reg = u16::from(self.mic_volume);
146         if self.mic_muted {
147             reg |= MUTE_REG_BIT;
148         }
149         if self.mic_20db {
150             reg |= MIXER_MIC_20DB;
151         }
152         reg
153     }
154 
155     // Sets the mic input mute, boost, and volume settings (0x0e).
set_mic_volume(&mut self, val: u16)156     fn set_mic_volume(&mut self, val: u16) {
157         self.mic_volume = (val & MIXER_VOL_MASK) as u8;
158         self.mic_muted = val & MUTE_REG_BIT != 0;
159         self.mic_20db = val & MIXER_MIC_20DB != 0;
160     }
161 
162     // Returns the value read in the Mic volume register (0x18).
get_pcm_out_volume(&self) -> u16163     fn get_pcm_out_volume(&self) -> u16 {
164         let reg = (self.pcm_out_vol_l as u16) << 8 | self.pcm_out_vol_r as u16;
165         if self.pcm_out_mute {
166             reg | MUTE_REG_BIT
167         } else {
168             reg
169         }
170     }
171 
172     // Sets the pcm output mute and volume states (0x18).
set_pcm_out_volume(&mut self, val: u16)173     fn set_pcm_out_volume(&mut self, val: u16) {
174         self.pcm_out_vol_r = val & MIXER_VOL_MASK;
175         self.pcm_out_vol_l = (val >> MIXER_VOL_LEFT_SHIFT) & MIXER_VOL_MASK;
176         self.pcm_out_mute = val & MUTE_REG_BIT != 0;
177     }
178 
179     // Returns the record gain register (0x01c).
get_record_gain_reg(&self) -> u16180     fn get_record_gain_reg(&self) -> u16 {
181         let reg = u16::from(self.record_gain_l) << 8 | u16::from(self.record_gain_r);
182         if self.record_gain_mute {
183             reg | MUTE_REG_BIT
184         } else {
185             reg
186         }
187     }
188 
189     // Handles writes to the record_gain register (0x1c).
set_record_gain_reg(&mut self, val: u16)190     fn set_record_gain_reg(&mut self, val: u16) {
191         self.record_gain_mute = val & MUTE_REG_BIT != 0;
192         self.record_gain_r = (val & VOL_REG_MASK) as u8;
193         self.record_gain_l = (val >> 8 & VOL_REG_MASK) as u8;
194     }
195 
196     // Handles writes to the powerdown ctrl/status register (0x26).
set_power_down_reg(&mut self, val: u16)197     fn set_power_down_reg(&mut self, val: u16) {
198         self.power_down_control =
199             (val & !PD_REG_STATUS_MASK) | (self.power_down_control & PD_REG_STATUS_MASK);
200     }
201 }
202