sound_card_init: `DSM` uses celsius as the unit of temperature
To make `DSM` more readable and maintainable, we:
1. Uses celsius as the unit of temperature in `DSM` as it's more easy
to understand.
2. Allow each amps to define the conversion between celsius and
the unit in VPD::dsm_calib_temp in `DSM`. Max98390 uses its dsm unit
for VPD::dsm_calib_temp and needs to convert it to celsius
to communicate with `DSM` struct.
3. Replace rdc_diff with rdc_to_ohm as compute the rdc difference in
real DC resistence is more general.
BUG=b:157210111
TEST=/sbin/initctl start sound_card_init SOUND_CARD_ID=sofcmlmax98390d
Change-Id: I8dbcc44b365192657f382ae8a43e091354f2e4cf
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/adhd/+/2650004
Tested-by: Judy Hsiao <judyhsiao@chromium.org>
Reviewed-by: Chih-Yang Hsia <paulhsia@chromium.org>
Commit-Queue: Judy Hsiao <judyhsiao@chromium.org>
diff --git a/sound_card_init/amp/src/max98390d/mod.rs b/sound_card_init/amp/src/max98390d/mod.rs
index 57c8ea8..f989255 100644
--- a/sound_card_init/amp/src/max98390d/mod.rs
+++ b/sound_card_init/amp/src/max98390d/mod.rs
@@ -14,7 +14,7 @@
};
use cros_alsa::{Card, IntControl, SwitchControl};
-use dsm::{CalibData, Error, Result, SpeakerStatus, ZeroPlayer, DSM};
+use dsm::{CalibData, Error, Result, SpeakerStatus, TempConverter, ZeroPlayer, DSM};
use crate::{Amp, CONF_DIR};
use settings::{AmpCalibSettings, DeviceSettings};
@@ -48,13 +48,17 @@
return Err(Error::MissingDSMParam);
}
- let dsm = DSM::new(
+ let mut dsm = DSM::new(
&self.card.name(),
self.setting.num_channels(),
- Self::rdc_diff,
- Self::celsius_to_dsm_unit(Self::TEMP_UPPER_LIMIT_CELSIUS),
- Self::celsius_to_dsm_unit(Self::TEMP_LOWER_LIMIT_CELSIUS),
+ Self::rdc_to_ohm,
+ Self::TEMP_UPPER_LIMIT_CELSIUS,
+ Self::TEMP_LOWER_LIMIT_CELSIUS,
);
+ dsm.set_temp_converter(TempConverter::new(
+ Self::dsm_unit_to_celsius,
+ Self::celsius_to_dsm_unit,
+ ));
self.set_volume(VolumeMode::Low)?;
let calib = match dsm.check_speaker_over_heated_workflow()? {
@@ -102,11 +106,6 @@
})
}
- /// Converts the ambient temperature from celsius to the DSM unit.
- fn celsius_to_dsm_unit(celsius: f32) -> f32 {
- celsius * ((1 << 12) as f32) / 100.0
- }
-
/// Sets the card volume control to given VolumeMode.
fn set_volume(&mut self, mode: VolumeMode) -> Result<()> {
for control in &self.setting.controls {
@@ -125,7 +124,7 @@
.set(rdc)?;
self.card
.control_by_name::<IntControl>(&self.setting.controls[ch].temp_ctrl)?
- .set(temp)?;
+ .set(Self::celsius_to_dsm_unit(temp))?;
}
Ok(())
}
@@ -155,19 +154,32 @@
.get()?;
card.control_by_name::<SwitchControl>(&control.calib_ctrl)?
.off()?;
- Ok(CalibData { rdc, temp })
+ Ok(CalibData {
+ rdc,
+ temp: Self::dsm_unit_to_celsius(temp),
+ })
})
.collect::<Result<Vec<CalibData>>>()?;
zero_player.stop()?;
Ok(calib)
}
- // rdc_cali is (11 / 3) / actual_rdc * 2^20.
- // Given that rdc_cali is the inverse of hardware real_rdc, the result of `rdc_diff`
- // equals to transforming `rdc`s to `real_rdc`s and calculating the relative difference
- // from the `real_rdc`s.
- fn rdc_diff(x: i32, x_ref: i32) -> f32 {
- (x - x_ref).abs() as f32 / x as f32
+ /// Converts the ambient temperature from celsius to the DSM unit.
+ #[inline]
+ fn celsius_to_dsm_unit(celsius: f32) -> i32 {
+ (celsius * ((1 << 12) as f32) / 100.0) as i32
+ }
+
+ /// Converts the ambient temperature from DSM unit to celsius.
+ #[inline]
+ fn dsm_unit_to_celsius(temp: i32) -> f32 {
+ temp as f32 * 100.0 / (1 << 12) as f32
+ }
+
+ /// Converts the calibrated value to real DC resistance in ohm unit.
+ #[inline]
+ fn rdc_to_ohm(x: i32) -> f32 {
+ 3.66 * (1 << 20) as f32 / x as f32
}
}
@@ -178,11 +190,29 @@
fn celsius_to_dsm_unit() {
assert_eq!(
Max98390::celsius_to_dsm_unit(Max98390::TEMP_UPPER_LIMIT_CELSIUS),
- 1638.4
+ 1638
);
assert_eq!(
Max98390::celsius_to_dsm_unit(Max98390::TEMP_LOWER_LIMIT_CELSIUS),
- 0.0
+ 0
);
}
+
+ #[test]
+ fn dsm_unit_to_celsius() {
+ assert_eq!(
+ Max98390::dsm_unit_to_celsius(1638).round(),
+ Max98390::TEMP_UPPER_LIMIT_CELSIUS
+ );
+ assert_eq!(
+ Max98390::dsm_unit_to_celsius(0),
+ Max98390::TEMP_LOWER_LIMIT_CELSIUS
+ );
+ }
+
+ #[test]
+ fn rdc_to_ohm() {
+ assert_eq!(Max98390::rdc_to_ohm(1123160), 3.416956);
+ assert_eq!(Max98390::rdc_to_ohm(1157049), 3.3168762);
+ }
}
diff --git a/sound_card_init/dsm/src/error.rs b/sound_card_init/dsm/src/error.rs
index 1240cb1..b93cba1 100644
--- a/sound_card_init/dsm/src/error.rs
+++ b/sound_card_init/dsm/src/error.rs
@@ -12,6 +12,8 @@
use remain::sorted;
+use crate::CalibData;
+
pub type Result<T> = std::result::Result<T, Error>;
#[sorted]
@@ -27,8 +29,8 @@
InvalidDatastore,
InvalidDSMParam,
InvalidShutDownTime,
- InvalidTemperature(i32),
- LargeCalibrationDiff(i32, i32),
+ InvalidTemperature(f32),
+ LargeCalibrationDiff(CalibData),
MissingDSMParam,
MutexPoisonError,
NewPlayStreamFailed(libcras::BoxError),
@@ -85,11 +87,9 @@
),
InvalidDatastore => write!(f, "invalid datastore format"),
InvalidDSMParam => write!(f, "invalid dsm param from kcontrol"),
- LargeCalibrationDiff(rdc, temp) => write!(
- f,
- "calibration difference is too large, rdc: {}, temp: {}",
- rdc, temp
- ),
+ LargeCalibrationDiff(calib) => {
+ write!(f, "calibration difference is too large, calib: {:?}", calib)
+ }
MissingDSMParam => write!(f, "missing dsm_param.bin"),
MutexPoisonError => write!(f, "mutex is poisoned"),
NewPlayStreamFailed(e) => write!(f, "{}", e),
diff --git a/sound_card_init/dsm/src/lib.rs b/sound_card_init/dsm/src/lib.rs
index b6e40ed..29c571c 100644
--- a/sound_card_init/dsm/src/lib.rs
+++ b/sound_card_init/dsm/src/lib.rs
@@ -22,10 +22,45 @@
#[derive(Debug, Clone, Copy)]
/// `CalibData` represents the calibration data.
pub struct CalibData {
- /// The DC resistance of the speaker.
+ /// The DC resistance of the speaker is DSM unit.
pub rdc: i32,
- /// The ambient temperature at which the rdc is measured.
- pub temp: i32,
+ /// The ambient temperature in celsius unit at which the rdc is measured.
+ pub temp: f32,
+}
+
+/// `TempConverter` converts the temperature value between celsius and unit in VPD::dsm_calib_temp.
+pub struct TempConverter {
+ vpd_to_celsius: fn(i32) -> f32,
+ celsius_to_vpd: fn(f32) -> i32,
+}
+
+impl Default for TempConverter {
+ fn default() -> Self {
+ let vpd_to_celsius = |x: i32| x as f32;
+ let celsius_to_vpd = |x: f32| x.round() as i32;
+ Self {
+ vpd_to_celsius,
+ celsius_to_vpd,
+ }
+ }
+}
+
+impl TempConverter {
+ /// Creates a `TempConverter`
+ ///
+ /// # Arguments
+ ///
+ /// * `vpd_to_celsius` - function to convert VPD::dsm_calib_temp to celsius unit`
+ /// * `celsius_to_vpd` - function to convert celsius unit to VPD::dsm_calib_temp`
+ /// # Results
+ ///
+ /// * `TempConverter` - it converts the temperature value between celsius and unit in VPD::dsm_calib_temp.
+ pub fn new(vpd_to_celsius: fn(i32) -> f32, celsius_to_vpd: fn(f32) -> i32) -> Self {
+ Self {
+ vpd_to_celsius,
+ celsius_to_vpd,
+ }
+ }
}
/// `SpeakerStatus` are the possible return results of
@@ -44,7 +79,8 @@
pub struct DSM {
snd_card: String,
num_channels: usize,
- rdc_diff: fn(i32, i32) -> f32,
+ temp_converter: TempConverter,
+ rdc_to_ohm: fn(i32) -> f32,
temp_upper_limit: f32,
temp_lower_limit: f32,
}
@@ -60,8 +96,7 @@
///
/// * `snd_card` - `sound card name`.
/// * `num_channels` - `number of channels`.
- /// * `rdc_diff` - `fn(rdc: i32, rdc_ref: i32) -> f32 to calculate the rdc difference`.
- /// * rdc is the newly calibrated rdc and rdc_ref is the reference rdc.
+ /// * `rdc_to_ohm` - `fn(rdc: i32) -> f32 to convert the CalibData::rdc to ohm unit`.
/// * `temp_upper_limit` - the high limit of the valid ambient temperature in dsm unit.
/// * `temp_lower_limit` - the low limit of the valid ambient temperature in dsm unit.
///
@@ -71,19 +106,29 @@
pub fn new(
snd_card: &str,
num_channels: usize,
- rdc_diff: fn(i32, i32) -> f32,
+ rdc_to_ohm: fn(i32) -> f32,
temp_upper_limit: f32,
temp_lower_limit: f32,
) -> Self {
Self {
snd_card: snd_card.to_owned(),
num_channels,
- rdc_diff,
+ rdc_to_ohm,
+ temp_converter: TempConverter::default(),
temp_upper_limit,
temp_lower_limit,
}
}
+ /// Sets self.temp_converter to the given temp_converter.
+ ///
+ /// # Arguments
+ ///
+ /// * `temp_converter` - the convert function to use.
+ pub fn set_temp_converter(&mut self, temp_converter: TempConverter) {
+ self.temp_converter = temp_converter;
+ }
+
/// Checks whether the speakers are overheated or not according to the previous shutdown time.
/// The boot time calibration should be skipped when the speakers may be too hot
/// and the Amp should use the previous calibration value returned by the
@@ -160,35 +205,36 @@
channel: usize,
calib_data: CalibData,
) -> Result<CalibData> {
- if !((calib_data.temp as f32) < self.temp_upper_limit
- && (calib_data.temp as f32) > self.temp_lower_limit)
- {
+ if calib_data.temp < self.temp_lower_limit || calib_data.temp > self.temp_upper_limit {
info!("invalid temperature: {}.", calib_data.temp);
return self
.get_previous_calibration_value(channel)
.map_err(|_| Error::InvalidTemperature(calib_data.temp));
}
- let (datastore_exist, rdc, temp) = match self.get_previous_calibration_value(channel) {
- Ok(CalibData { rdc, temp }) => (true, rdc, temp),
+ let (datastore_exist, previous_calib) = match self.get_previous_calibration_value(channel) {
+ Ok(previous_calib) => (true, previous_calib),
Err(e) => {
info!("{}, use vpd as previous calibration value", e);
- let vpd = VPD::new(channel)?;
- (false, vpd.dsm_calib_r0, vpd.dsm_calib_temp)
+ (false, self.get_vpd_calibration_value(channel)?)
}
};
- let diff: f32 = (self.rdc_diff)(calib_data.rdc, rdc);
+ let diff = {
+ let calib_rdc_ohm = (self.rdc_to_ohm)(calib_data.rdc);
+ let previous_rdc_ohm = (self.rdc_to_ohm)(previous_calib.rdc);
+ (calib_rdc_ohm - previous_rdc_ohm) / previous_rdc_ohm
+ };
if diff > Self::CALI_ERROR_UPPER_LIMIT {
- Err(Error::LargeCalibrationDiff(calib_data.rdc, calib_data.temp))
+ Err(Error::LargeCalibrationDiff(calib_data))
} else if diff < Self::CALI_ERROR_LOWER_LIMIT {
if !datastore_exist {
Datastore::UseVPD.save(&self.snd_card, channel)?;
}
- Ok(CalibData { rdc, temp })
+ Ok(previous_calib)
} else {
Datastore::DSM {
rdc: calib_data.rdc,
- temp: calib_data.temp,
+ temp: (self.temp_converter.celsius_to_vpd)(calib_data.temp),
}
.save(&self.snd_card, channel)?;
Ok(calib_data)
@@ -225,14 +271,19 @@
fn get_previous_calibration_value(&self, ch: usize) -> Result<CalibData> {
let sci_calib = Datastore::from_file(&self.snd_card, ch)?;
match sci_calib {
- Datastore::UseVPD => {
- let vpd = VPD::new(ch)?;
- Ok(CalibData {
- rdc: vpd.dsm_calib_r0,
- temp: vpd.dsm_calib_temp,
- })
- }
- Datastore::DSM { rdc, temp } => Ok(CalibData { rdc, temp }),
+ Datastore::UseVPD => self.get_vpd_calibration_value(ch),
+ Datastore::DSM { rdc, temp } => Ok(CalibData {
+ rdc,
+ temp: (self.temp_converter.vpd_to_celsius)(temp),
+ }),
}
}
+
+ fn get_vpd_calibration_value(&self, channel: usize) -> Result<CalibData> {
+ let vpd = VPD::new(channel)?;
+ Ok(CalibData {
+ rdc: vpd.dsm_calib_r0,
+ temp: (self.temp_converter.vpd_to_celsius)(vpd.dsm_calib_temp),
+ })
+ }
}