diff options
author | Judy Hsiao <judyhsiao@google.com> | 2020-12-21 11:47:43 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-02-02 16:30:46 +0000 |
commit | 287ea8bbe90e991fd7f1e147d59113bf2d23c203 (patch) | |
tree | bc7b241bb017551cfd877f3cb4cef6bdd6082371 | |
parent | cb1f53abc38a16258268c2571bf58b79c76033ed (diff) | |
download | adhd-287ea8bbe90e991fd7f1e147d59113bf2d23c203.tar.gz |
sound_card_init: add Max98373 boot time calibration logic
1. Implement functions to access the DSM calibration data from DSP.
2. Implement max98373 boot time calibration flow as the following
steps:
a. Limit the speaker volume to protect the speakers.
b. Check whether the speakers are overheated.
c. Trigger the DSM calibration from DSP.
d. Decide and apply a good calibration value and remove the speaker
output limitation.
BUG=b:157210111
TEST=/sbin/initctl start sound_card_init SOUND_CARD_ID=sofrt5682
Cq-Depend: chromium:2597532
Cq-Depend: chromium:2606416
Change-Id: I7a9ab8064727588fb00de818020a01218ed4d03d
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/adhd/+/2595662
Reviewed-by: Chih-Yang Hsia <paulhsia@chromium.org>
Tested-by: Judy Hsiao <judyhsiao@chromium.org>
Commit-Queue: Judy Hsiao <judyhsiao@chromium.org>
-rw-r--r-- | sound_card_init/Cargo.lock | 62 | ||||
-rw-r--r-- | sound_card_init/Cargo.toml | 8 | ||||
-rw-r--r-- | sound_card_init/amp/Cargo.lock | 254 | ||||
-rw-r--r-- | sound_card_init/amp/Cargo.toml | 1 | ||||
-rw-r--r-- | sound_card_init/amp/src/lib.rs | 3 | ||||
-rw-r--r-- | sound_card_init/amp/src/max98373d/dsm_param.rs | 211 | ||||
-rw-r--r-- | sound_card_init/amp/src/max98373d/mod.rs | 267 | ||||
-rw-r--r-- | sound_card_init/amp/src/max98373d/settings.rs | 40 | ||||
-rw-r--r-- | sound_card_init/dsm/src/error.rs | 24 | ||||
-rw-r--r-- | sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy | 123 |
10 files changed, 902 insertions, 91 deletions
diff --git a/sound_card_init/Cargo.lock b/sound_card_init/Cargo.lock index d2092cdf..959959ed 100644 --- a/sound_card_init/Cargo.lock +++ b/sound_card_init/Cargo.lock @@ -19,6 +19,7 @@ dependencies = [ "remain", "serde", "serde_yaml", + "sof_sys", "sys_util", ] @@ -90,9 +91,9 @@ dependencies = [ [[package]] name = "dtoa" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" +checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" [[package]] name = "getopts" @@ -105,9 +106,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.71" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" +checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" [[package]] name = "libcras" @@ -122,15 +123,15 @@ dependencies = [ [[package]] name = "linked-hash-map" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "pkg-config" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "poll_token_derive" @@ -143,27 +144,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.18" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" dependencies = [ "proc-macro2", ] [[package]] name = "remain" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c861227fc40c8da6fdaa3d58144ac84c0537080a43eb1d7d45c28f88dcb888" +checksum = "70ba1e78fa68412cb93ef642fd4d20b9a941be49ee9333875ebaf13112673ea7" dependencies = [ "proc-macro2", "quote", @@ -172,18 +173,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.111" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" +checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.111" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" +checksum = "552954ce79a059ddd5fd68c271592374bd15cab2274970380c000118aeffe1cd" dependencies = [ "proc-macro2", "quote", @@ -192,9 +193,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.12" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c7a592a1ec97c9c1c68d75b6e537dcbf60c7618e038e7841e00af1d9ccf0c4" +checksum = "971be8f6e4d4a47163b405a3df70d14359186f9ab0f3a3ec37df144ca1ce089f" dependencies = [ "dtoa", "linked-hash-map", @@ -203,6 +204,10 @@ dependencies = [ ] [[package]] +name = "sof_sys" +version = "0.1.0" + +[[package]] name = "sound_card_init" version = "0.1.0" dependencies = [ @@ -215,14 +220,15 @@ dependencies = [ "remain", "serde", "serde_yaml", + "sof_sys", "sys_util", ] [[package]] name = "syn" -version = "1.0.30" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" +checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" dependencies = [ "proc-macro2", "quote", @@ -259,21 +265,21 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "yaml-rust" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] diff --git a/sound_card_init/Cargo.toml b/sound_card_init/Cargo.toml index 41694557..9d5a107e 100644 --- a/sound_card_init/Cargo.toml +++ b/sound_card_init/Cargo.toml @@ -5,6 +5,12 @@ authors = ["The Chromium OS Authors"] edition = "2018" description = "Sound Card Initializer" +[workspace] +members = [ + "amp", + "dsm" +] + [dependencies] amp = { path = "amp" } audio_streams = "*" @@ -15,6 +21,7 @@ libcras = "*" remain = "0.2.1" serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.8.11" +sof_sys = "*" sys_util = "*" [patch.crates-io] @@ -22,4 +29,5 @@ audio_streams = { path = "../audio_streams" } # ignored by ebuild cros_alsa = { path = "../cros_alsa" } # ignored by ebuild cros_alsa_derive = { path = "../cros_alsa/cros_alsa_derive" } # ignored by ebuild libcras = { path = "../cras/client/libcras" } # ignored by ebuild +sof_sys = { path = "../sof_sys" } # ignored by ebuild sys_util = { path = "../../../platform/crosvm/sys_util" } # ignored by ebuild diff --git a/sound_card_init/amp/Cargo.lock b/sound_card_init/amp/Cargo.lock new file mode 100644 index 00000000..679e60cd --- /dev/null +++ b/sound_card_init/amp/Cargo.lock @@ -0,0 +1,254 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "alsa-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644d308f5822c2b39fba5a6d850f74c208bf73c61d1d2dfad62505d6960e4977" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "amp" +version = "0.1.0" +dependencies = [ + "cros_alsa", + "dsm", + "libcras", + "remain", + "serde", + "serde_yaml", + "sof_sys", + "sys_util", +] + +[[package]] +name = "android_log-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" + +[[package]] +name = "assertions" +version = "0.1.0" + +[[package]] +name = "audio_streams" +version = "0.1.0" +dependencies = [ + "sync", + "sys_util", +] + +[[package]] +name = "cras-sys" +version = "0.1.0" +dependencies = [ + "audio_streams", + "data_model", +] + +[[package]] +name = "cros_alsa" +version = "0.1.0" +dependencies = [ + "alsa-sys", + "cros_alsa_derive", + "libc", + "remain", +] + +[[package]] +name = "cros_alsa_derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "data_model" +version = "0.1.0" +dependencies = [ + "assertions", + "libc", +] + +[[package]] +name = "dsm" +version = "0.1.0" +dependencies = [ + "audio_streams", + "cros_alsa", + "libcras", + "remain", + "serde", + "serde_yaml", + "sys_util", +] + +[[package]] +name = "dtoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" + +[[package]] +name = "libc" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cca32fa0182e8c0989459524dc356b8f2b5c10f1b9eb521b7d182c03cf8c5ff" + +[[package]] +name = "libcras" +version = "0.1.0" +dependencies = [ + "audio_streams", + "cras-sys", + "data_model", + "libc", + "sys_util", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "poll_token_derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "remain" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ba1e78fa68412cb93ef642fd4d20b9a941be49ee9333875ebaf13112673ea7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_yaml" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "971be8f6e4d4a47163b405a3df70d14359186f9ab0f3a3ec37df144ca1ce089f" +dependencies = [ + "dtoa", + "linked-hash-map", + "serde", + "yaml-rust", +] + +[[package]] +name = "sof_sys" +version = "0.1.0" + +[[package]] +name = "syn" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "sync" +version = "0.1.0" + +[[package]] +name = "sys_util" +version = "0.1.0" +dependencies = [ + "android_log-sys", + "data_model", + "libc", + "poll_token_derive", + "sync", + "syscall_defines", + "tempfile", +] + +[[package]] +name = "syscall_defines" +version = "0.1.0" + +[[package]] +name = "tempfile" +version = "3.0.7" +dependencies = [ + "libc", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/sound_card_init/amp/Cargo.toml b/sound_card_init/amp/Cargo.toml index b7cfe85f..f912bcfe 100644 --- a/sound_card_init/amp/Cargo.toml +++ b/sound_card_init/amp/Cargo.toml @@ -11,4 +11,5 @@ dsm = { path = "../dsm" } remain = "0.2.1" serde = { version = "1.0", features = ["derive"]} serde_yaml = "0.8.11" +sof_sys = "*" sys_util = "*" diff --git a/sound_card_init/amp/src/lib.rs b/sound_card_init/amp/src/lib.rs index 4c489a36..7486ff0a 100644 --- a/sound_card_init/amp/src/lib.rs +++ b/sound_card_init/amp/src/lib.rs @@ -5,10 +5,12 @@ //! to create `Amp` objects. #![deny(missing_docs)] +mod max98373d; mod max98390d; use dsm::Error; +use max98373d::Max98373; use max98390d::Max98390; type Result<T> = std::result::Result<T, Error>; @@ -29,6 +31,7 @@ impl<'a> AmpBuilder<'a> { pub fn build(&self) -> Result<Box<dyn Amp>> { match self.sound_card_id { "sofcmlmax98390d" => Ok(Box::new(Max98390::new(self.sound_card_id)?) as Box<dyn Amp>), + "sofrt5682" => Ok(Box::new(Max98373::new(self.sound_card_id)?) as Box<dyn Amp>), _ => Err(Error::UnsupportedSoundCard(self.sound_card_id.to_owned())), } } diff --git a/sound_card_init/amp/src/max98373d/dsm_param.rs b/sound_card_init/amp/src/max98373d/dsm_param.rs new file mode 100644 index 00000000..d9257522 --- /dev/null +++ b/sound_card_init/amp/src/max98373d/dsm_param.rs @@ -0,0 +1,211 @@ +// Copyright 2020 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 std::mem; + +use cros_alsa::{Card, TLV}; +use sof_sys::sof_abi_hdr; + +use dsm::{self, Error, Result}; + +/// Amp volume mode enumeration used by set_volume(). +#[derive(Copy, Clone, PartialEq)] +pub enum VolumeMode { + /// Low mode protects the speaker by limiting its output volume if the + /// calibration has not been completed successfully. + Low = 0x1009B9CF, + /// High mode removes the speaker output volume limitation after + /// having successfully completed the calibration. + High = 0x20000000, +} + +#[derive(Copy, Clone)] +/// Calibration mode enumeration. +pub enum CalibMode { + ON = 0x4, + OFF = 0x1, +} + +#[derive(Copy, Clone)] +/// Smart pilot signal mode mode enumeration. +pub enum SPTMode { + ON = 0x1, + OFF = 0x0, +} + +#[derive(Copy, Clone)] +/// DSM Parem field enumeration. +enum DsmAPI { + ParamCount = 0x0, + CalibMode = 0x1, + MakeupGain = 0x5, + DsmRdc = 0x6, + DsmAmbientTemp = 0x8, + AdaptiveRdc = 0x12, + SPTMode = 0x68, +} + +#[derive(Debug)] +/// It implements functions to access the `DSMParam` fields. +pub struct DSMParam { + param_count: usize, + num_channels: usize, + tlv: TLV, +} + +impl DSMParam { + const DWORD_PER_PARAM: usize = 2; + const VALUE_OFFSET: usize = 1; + const SOF_HEADER_SIZE: usize = mem::size_of::<sof_abi_hdr>() / mem::size_of::<i32>(); + + /// Creates an `DSMParam`. + /// # Arguments + /// + /// * `card` - `&Card`. + /// * `num_channels` - number of channels. + /// * `ctl_name` - the mixer control name to access the DSM param. + /// + /// # Results + /// + /// * `DSMParam` - It is initialized by the content of the given byte control . + /// + /// # Errors + /// + /// * If `Card` creation from sound card name fails. + pub fn new(card: &mut Card, num_channels: usize, ctl_name: &str) -> Result<Self> { + let tlv = card.control_tlv_by_name(ctl_name)?.load()?; + Self::try_from_tlv(tlv, num_channels) + } + + /// Sets DSMParam to the given calibration mode. + pub fn set_calibration_mode(&mut self, mode: CalibMode) { + for channel in 0..self.num_channels { + self.set(channel, DsmAPI::CalibMode, mode as i32); + } + } + + /// Sets DSMParam to the given smart pilot signal mode. + pub fn set_spt_mode(&mut self, mode: SPTMode) { + for channel in 0..self.num_channels { + self.set(channel, DsmAPI::SPTMode, mode as i32); + } + } + + /// Sets DSMParam to the given VolumeMode. + pub fn set_volume_mode(&mut self, mode: VolumeMode) { + for channel in 0..self.num_channels { + self.set(channel, DsmAPI::MakeupGain, mode as i32); + } + } + + /// Reads the calibrated rdc from DSMParam. + pub fn get_adaptive_rdc(&self) -> Vec<i32> { + self.get(DsmAPI::AdaptiveRdc) + } + + /// Sets DSMParam to the given the calibrated rdc. + pub fn set_rdc(&mut self, ch: usize, rdc: i32) { + self.set(ch, DsmAPI::DsmRdc, rdc); + } + + /// Sets DSMParam to the given calibrated temp. + pub fn set_ambient_temp(&mut self, ch: usize, temp: i32) { + self.set(ch, DsmAPI::DsmAmbientTemp, temp); + } + + /// Sets the `id` field to the given `val`. + fn set(&mut self, channel: usize, id: DsmAPI, val: i32) { + let pos = Self::value_pos(self.param_count, channel, id); + self.tlv[pos] = val as u32; + } + + /// Gets the val from the `id` field from all the channels. + fn get(&self, id: DsmAPI) -> Vec<i32> { + (0..self.num_channels) + .map(|channel| { + let pos = Self::value_pos(self.param_count, channel, id); + self.tlv[pos] as i32 + }) + .collect() + } + + fn try_from_tlv(tlv: TLV, num_channels: usize) -> Result<Self> { + let param_count_pos = Self::value_pos(0, 0, DsmAPI::ParamCount); + + if tlv.len() < param_count_pos { + return Err(Error::InvalidDSMParam); + } + + let param_count = tlv[param_count_pos] as usize; + + if tlv.len() != Self::SOF_HEADER_SIZE + param_count * num_channels * Self::DWORD_PER_PARAM { + return Err(Error::InvalidDSMParam); + } + + Ok(Self { + param_count, + num_channels, + tlv, + }) + } + + #[inline] + fn value_pos(param_count: usize, channel: usize, id: DsmAPI) -> usize { + Self::SOF_HEADER_SIZE + + (channel * param_count + id as usize) * Self::DWORD_PER_PARAM + + Self::VALUE_OFFSET + } +} + +impl Into<TLV> for DSMParam { + fn into(self) -> TLV { + self.tlv + } +} + +#[cfg(test)] +mod tests { + use super::*; + const PARAM_COUNT: usize = 138; + const CHANNEL_COUNT: usize = 2; + + #[test] + fn test_dsmparam_try_from_tlv_ok() { + let mut data = vec![ + 0u32; + DSMParam::SOF_HEADER_SIZE + + CHANNEL_COUNT * PARAM_COUNT * DSMParam::DWORD_PER_PARAM + ]; + data[DSMParam::value_pos(PARAM_COUNT, 0, DsmAPI::ParamCount)] = PARAM_COUNT as u32; + data[DSMParam::value_pos(PARAM_COUNT, 1, DsmAPI::ParamCount)] = PARAM_COUNT as u32; + + let tlv = TLV::new(0, data); + assert!(DSMParam::try_from_tlv(tlv, CHANNEL_COUNT).is_ok()); + } + + #[test] + fn test_dsmparam_try_from_invalid_len() { + let data = vec![0u32; DSMParam::SOF_HEADER_SIZE]; + + let tlv = TLV::new(0, data); + assert_eq!( + DSMParam::try_from_tlv(tlv, CHANNEL_COUNT).unwrap_err(), + Error::InvalidDSMParam + ); + } + + #[test] + fn test_dsmparam_try_from_param_count() { + let data = vec![ + 0u32; + DSMParam::SOF_HEADER_SIZE + + CHANNEL_COUNT * PARAM_COUNT * DSMParam::DWORD_PER_PARAM + ]; + + let tlv = TLV::new(0, data); + assert_eq!( + DSMParam::try_from_tlv(tlv, CHANNEL_COUNT).unwrap_err(), + Error::InvalidDSMParam + ); + } +} diff --git a/sound_card_init/amp/src/max98373d/mod.rs b/sound_card_init/amp/src/max98373d/mod.rs new file mode 100644 index 00000000..166f64eb --- /dev/null +++ b/sound_card_init/amp/src/max98373d/mod.rs @@ -0,0 +1,267 @@ +// Copyright 2020 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. +//! `max98373d` module implements the required initialization workflows for sound +//! cards that use max98373d smart amp. +//! It currently supports boot time calibration for max98373d. +#![deny(missing_docs)] +mod dsm_param; +mod settings; + +use std::path::{Path, PathBuf}; +use std::time::Duration; +use std::{fs, thread}; + +use cros_alsa::{Card, IntControl}; +use dsm::{CalibData, Error, Result, SpeakerStatus, ZeroPlayer, DSM}; + +use crate::{Amp, CONF_DIR}; +use dsm_param::*; +use settings::{AmpCalibSettings, DeviceSettings}; + +/// It implements the amplifier boot time calibration flow. +pub struct Max98373 { + card: Card, + setting: AmpCalibSettings, +} + +impl Amp for Max98373 { + /// Performs max98373d boot time calibration. + /// + /// # Errors + /// + /// If any amplifiers fail to complete the calibration. + fn boot_time_calibration(&mut self) -> Result<()> { + if !Path::new(&self.setting.dsm_param).exists() { + return Err(Error::MissingDSMParam); + } + + let num_channels = self.setting.num_channels(); + let dsm = DSM::new( + &self.card.name(), + num_channels, + Self::rdc_to_ohm, + Self::TEMP_UPPER_LIMIT_CELSIUS, + Self::TEMP_LOWER_LIMIT_CELSIUS, + ); + self.set_volume(VolumeMode::Low)?; + + let calib = match dsm.check_speaker_over_heated_workflow()? { + SpeakerStatus::Hot(previous_calib) => previous_calib, + SpeakerStatus::Cold => { + let all_temp = self.get_ambient_temp()?; + let all_rdc = self.do_rdc_calibration()?; + all_rdc + .iter() + .zip(all_temp) + .enumerate() + .map(|(ch, (&rdc, temp))| { + dsm.decide_calibration_value_workflow(ch, CalibData { rdc, temp }) + }) + .collect::<Result<Vec<_>>>()? + } + }; + self.apply_calibration_value(&calib)?; + self.set_volume(VolumeMode::High)?; + Ok(()) + } +} + +impl Max98373 { + const TEMP_CALIB_WARM_UP_TIME: Duration = Duration::from_millis(10); + const RDC_CALIB_WARM_UP_TIME: Duration = Duration::from_millis(500); + const RDC_CALIB_INTERVAL: Duration = Duration::from_millis(200); + const CALIB_REPEAT_TIMES: usize = 5; + + const TEMP_UPPER_LIMIT_CELSIUS: f32 = 40.0; + const TEMP_LOWER_LIMIT_CELSIUS: f32 = 0.0; + + /// Creates an `Max98373`. + /// # Arguments + /// + /// * `card_name` - card_name. + /// + /// # Results + /// + /// * `Max98373` - It implements the Max98373 functions of boot time calibration. + /// + /// # Errors + /// + /// * If `Card` creation from sound card name fails. + pub fn new(card_name: &str) -> Result<Self> { + let config_path = PathBuf::from(CONF_DIR) + .join(card_name) + .with_extension("yaml"); + let conf = + fs::read_to_string(&config_path).map_err(|e| Error::FileIOFailed(config_path, e))?; + let settings = DeviceSettings::from_yaml_str(&conf)?; + Ok(Self { + card: Card::new(card_name)?, + setting: settings.amp_calibrations, + }) + } + + /// Triggers the amplifier calibration and reads the calibrated rdc. + /// To get accurate calibration results, the main thread calibrates the amplifier while + /// the `zero_player` starts another thread to play zeros to the speakers. + fn do_rdc_calibration(&mut self) -> Result<Vec<i32>> { + let mut zero_player: ZeroPlayer = Default::default(); + zero_player.start(Self::RDC_CALIB_WARM_UP_TIME)?; + // Playback of zeros is started for Self::RDC_CALIB_WARM_UP_TIME, and the main thread + // can start the calibration. + self.set_spt_mode(SPTMode::OFF)?; + self.set_calibration_mode(CalibMode::ON)?; + // Playback of zeros is started, and the main thread can start the calibration. + let mut avg_rdc = vec![0; self.setting.num_channels()]; + for _ in 0..Self::CALIB_REPEAT_TIMES { + let rdc = self.get_adaptive_rdc()?; + for i in 0..self.setting.num_channels() { + avg_rdc[i] += rdc[i]; + } + thread::sleep(Self::RDC_CALIB_INTERVAL); + } + self.set_spt_mode(SPTMode::ON)?; + self.set_calibration_mode(CalibMode::OFF)?; + zero_player.stop()?; + + avg_rdc = avg_rdc + .iter() + .map(|val| val / Self::CALIB_REPEAT_TIMES as i32) + .collect(); + Ok(avg_rdc) + } + + /// Sets the card volume control to the given VolumeMode. + fn set_volume(&mut self, mode: VolumeMode) -> Result<()> { + let mut dsm_param = DSMParam::new( + &mut self.card, + self.setting.num_channels(), + &self.setting.dsm_param_read_ctrl, + )?; + + dsm_param.set_volume_mode(mode); + + self.card + .control_tlv_by_name(&self.setting.dsm_param_write_ctrl)? + .save(dsm_param.into()) + .map_err(Error::DSMParamUpdateFailed)?; + Ok(()) + } + + /// Applies the calibration value to the amp. + fn apply_calibration_value(&mut self, calib: &[CalibData]) -> Result<()> { + let mut dsm_param = DSMParam::new( + &mut self.card, + self.setting.num_channels(), + &self.setting.dsm_param_read_ctrl, + )?; + for ch in 0..self.setting.num_channels() { + dsm_param.set_rdc(ch, calib[ch].rdc); + dsm_param.set_ambient_temp(ch, Self::celsius_to_dsm_unit(calib[ch].temp)); + } + self.card + .control_tlv_by_name(&self.setting.dsm_param_write_ctrl)? + .save(dsm_param.into()) + .map_err(Error::DSMParamUpdateFailed)?; + Ok(()) + } + + /// Rdc (ohm) = [ID:0x12] * 3.66 / 2^27 + #[inline] + fn rdc_to_ohm(x: i32) -> f32 { + (3.66 * x as f32) / (1 << 27) as f32 + } + + /// Returns the ambient temperature in celsius degree. + fn get_ambient_temp(&mut self) -> Result<Vec<f32>> { + let mut zero_player: ZeroPlayer = Default::default(); + zero_player.start(Self::TEMP_CALIB_WARM_UP_TIME)?; + let mut temps = Vec::new(); + for x in 0..self.setting.num_channels() as usize { + let temp = self + .card + .control_by_name::<IntControl>(&self.setting.temp_ctrl[x])? + .get()?; + let celsius = Self::measured_temp_to_celsius(temp); + temps.push(celsius); + } + zero_player.stop()?; + + Ok(temps) + } + + /// Converts the measured ambient temperature to celsius unit. + #[inline] + fn measured_temp_to_celsius(temp: i32) -> f32 { + // Measured Temperature (°C) = ([Mixer Val] * 1.28) - 29 + (temp as f32 * 1.28) - 29.0 + } + + /// Converts the ambient temperature from celsius to the DsmSetAPI::DsmAmbientTemp unit. + #[inline] + fn celsius_to_dsm_unit(celsius: f32) -> i32 { + // Temperature (℃) = [ID:0x12] / 2^19 + (celsius * (1 << 19) as f32) as i32 + } + + /// Sets the amp to the given smart pilot signal mode. + fn set_spt_mode(&mut self, mode: SPTMode) -> Result<()> { + let mut dsm_param = DSMParam::new( + &mut self.card, + self.setting.num_channels(), + &self.setting.dsm_param_read_ctrl, + )?; + dsm_param.set_spt_mode(mode); + self.card + .control_tlv_by_name(&self.setting.dsm_param_write_ctrl)? + .save(dsm_param.into()) + .map_err(Error::DSMParamUpdateFailed)?; + Ok(()) + } + + /// Sets the amp to the given the calibration mode. + fn set_calibration_mode(&mut self, mode: CalibMode) -> Result<()> { + let mut dsm_param = DSMParam::new( + &mut self.card, + self.setting.num_channels(), + &self.setting.dsm_param_read_ctrl, + )?; + dsm_param.set_calibration_mode(mode); + self.card + .control_tlv_by_name(&self.setting.dsm_param_write_ctrl)? + .save(dsm_param.into()) + .map_err(Error::DSMParamUpdateFailed)?; + Ok(()) + } + + /// Reads the calibrated rdc. + /// Must be called when the calibration mode in on. + fn get_adaptive_rdc(&mut self) -> Result<Vec<i32>> { + let dsm_param = DSMParam::new( + &mut self.card, + self.setting.num_channels(), + &self.setting.dsm_param_read_ctrl, + )?; + Ok(dsm_param.get_adaptive_rdc()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn celsius_to_dsm_unit() { + assert_eq!(Max98373::celsius_to_dsm_unit(37.0), 0x01280000); + assert_eq!(Max98373::celsius_to_dsm_unit(50.0), 0x01900000); + } + + #[test] + fn rdc_to_ohm() { + assert_eq!(Max98373::rdc_to_ohm(0x05cea0c7), 2.656767); + } + + #[test] + fn measured_temp_to_celsius() { + assert_eq!(Max98373::measured_temp_to_celsius(56), 42.68); + } +} diff --git a/sound_card_init/amp/src/max98373d/settings.rs b/sound_card_init/amp/src/max98373d/settings.rs new file mode 100644 index 00000000..b9449617 --- /dev/null +++ b/sound_card_init/amp/src/max98373d/settings.rs @@ -0,0 +1,40 @@ +// Copyright 2020 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 std::string::String; + +use dsm::{self, Error, Result}; +use serde::Deserialize; +/// `DeviceSettings` includes the settings of max98373. It currently includes: +/// * the settings of amplifier calibration. +/// * the path of dsm_param. +#[derive(Debug, Default, PartialEq, Deserialize, Clone)] +pub struct DeviceSettings { + pub amp_calibrations: AmpCalibSettings, +} + +/// `AmpCalibSettings` includes the settings needed for amplifier calibration. +#[derive(Debug, Default, PartialEq, Deserialize, Clone)] +pub struct AmpCalibSettings { + pub dsm_param_read_ctrl: String, + pub dsm_param_write_ctrl: String, + pub temp_ctrl: Vec<String>, + // Path of the dsm_param.bin file. + pub dsm_param: String, +} + +impl AmpCalibSettings { + /// Returns the number of channels. + pub fn num_channels(&self) -> usize { + self.temp_ctrl.len() + } +} + +impl DeviceSettings { + /// Creates a `DeviceSettings` from a yaml str. + pub fn from_yaml_str(conf: &str) -> Result<DeviceSettings> { + let settings: DeviceSettings = serde_yaml::from_str(conf) + .map_err(|e| Error::DeserializationFailed("DeviceSettings".to_owned(), e))?; + Ok(settings) + } +} diff --git a/sound_card_init/dsm/src/error.rs b/sound_card_init/dsm/src/error.rs index b93cba12..4b6e8dc2 100644 --- a/sound_card_init/dsm/src/error.rs +++ b/sound_card_init/dsm/src/error.rs @@ -21,9 +21,11 @@ pub type Result<T> = std::result::Result<T, Error>; pub enum Error { AlsaCardError(cros_alsa::CardError), AlsaControlError(cros_alsa::ControlError), + AlsaControlTLVError(cros_alsa::ControlTLVError), CalibrationTimeout, CrasClientFailed(libcras::Error), DeserializationFailed(String, serde_yaml::Error), + DSMParamUpdateFailed(cros_alsa::ControlTLVError), FileIOFailed(PathBuf, io::Error), InternalSpeakerNotFound, InvalidDatastore, @@ -46,6 +48,16 @@ pub enum Error { ZeroPlayerIsRunning, } +impl PartialEq for Error { + // Implement eq for more Error when needed. + fn eq(&self, other: &Error) -> bool { + match (self, other) { + (Error::InvalidDSMParam, Error::InvalidDSMParam) => true, + _ => false, + } + } +} + impl From<cros_alsa::CardError> for Error { fn from(err: cros_alsa::CardError) -> Error { Error::AlsaCardError(err) @@ -58,6 +70,12 @@ impl From<cros_alsa::ControlError> for Error { } } +impl From<cros_alsa::ControlTLVError> for Error { + fn from(err: cros_alsa::ControlTLVError) -> Error { + Error::AlsaControlTLVError(err) + } +} + impl<T> From<PoisonError<T>> for Error { fn from(_: PoisonError<T>) -> Error { Error::MutexPoisonError @@ -70,9 +88,11 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Error::*; match self { - AlsaCardError(e) => write!(f, "{}", e), - AlsaControlError(e) => write!(f, "{}", e), + AlsaCardError(e) => write!(f, "AlsaCardError: {}", e), + AlsaControlError(e) => write!(f, "AlsaControlError: {}", e), + AlsaControlTLVError(e) => write!(f, "AlsaControlTLVError: {}", e), CalibrationTimeout => write!(f, "calibration is not finished in time"), + DSMParamUpdateFailed(e) => write!(f, "failed to update DsmParam, err: {}", e), CrasClientFailed(e) => write!(f, "failed to create cras client: {}", e), DeserializationFailed(file_path, e) => { write!(f, "failed to parse {}: {}", file_path, e) diff --git a/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy b/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy index b1bcd6b8..0404924e 100644 --- a/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy +++ b/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy @@ -2,78 +2,79 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -lseek: 1 -read: 1 +access: 1 +arch_prctl: 1 +bind: 1 +brk: 1 +clone: 1 close: 1 -openat: 1 -mmap: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE -fstat: 1 -rt_sigaction: 1 -prlimit64: 1 -mprotect: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE -ioctl: arg1 == 0x5401 || arg1 == 0xc4c85512 || arg1 == 0x540f || arg1 == 0x80045500 || arg1 == 0xc4c85513 || arg1 == 0x81785501 || arg1 == 0x5413 || arg1 == 0xc1105511 -stat: 1 -munmap: 1 -write: 1 -setresuid: 1 -recvmsg: 1 -fcntl: 1 -futex: 1 +connect: 1 +dup2: 1 +dup: 1 epoll_create1: 1 epoll_ctl: 1 epoll_wait: 1 -setresgid: 1 -sigaltstack: 1 -rt_sigprocmask: 1 -access: 1 -getuid: 1 -brk: 1 -getpid: 1 -socket: arg0 == 0x10 || arg0 == 0x1 +execve: 1 +exit: 1 +exit_group: 1 +fcntl: 1 +fstat: 1 +futex: 1 +getcwd: 1 getdents: 1 -recvfrom: 1 -set_robust_list: 1 -umask: 1 -unlink: 1 -sendto: 1 -setgroups: 1 -connect: 1 +getegid: 1 geteuid: 1 -prctl: arg0 == 0x3 || arg0 == 0x4 -clone: 1 -dup: 1 -sched_getaffinity: 1 -execve: 1 -arch_prctl: 1 -set_tid_address: 1 +getgid: 1 getgroups: 1 -getresuid: 1 +getpgid: 1 +getpgrp: 1 +getpid: 1 +getppid: 1 +getpriority: 1 +getrandom: 1 getresgid: 1 +getresuid: 1 +getsid: 1 +getsockname: 1 +getuid: 1 +ioctl: arg1 == 0x5401 || arg1 == 0xc4c85512 || arg1 == 0x540f || arg1 == 0x80045500 || arg1 == 0xc4c85513 || arg1 == 0x81785501 || arg1 == 0x5413 || arg1 == 0xc1105511 || arg1 == 0x81785501 || arg1 == 0x80045500 || arg1 == 0xc008551a || arg1 == 0xc4c85512 || arg1 == 0xc008551b || arg1 == 0xc1105511 +lseek: 1 +madvise: 1 +mmap: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE +mprotect: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE +munmap: 1 +nanosleep: 1 +openat: 1 pipe2: 1 ppoll: 1 -statx: 1 -socketpair: 1 +prctl: arg0 == 0x3 || arg0 == 0x4 +prlimit64: 1 +read: 1 +recvfrom: 1 +recvmsg: 1 +restart_syscall: 1 +rt_sigaction: 1 +rt_sigprocmask: 1 +rt_sigreturn: 1 +sched_getaffinity: 1 +sched_yield: 1 sendmsg: 1 -madvise: 1 -exit: 1 -exit_group: 1 -getppid: 1 -getpgid: 1 -getsid: 1 -getgid: 1 -getegid: 1 -getcwd: 1 -uname: 1 -bind: 1 -getsockname: 1 -setuid: 1 +sendto: 1 +set_robust_list: 1 +set_tid_address: 1 setgid: 1 -getpriority: 1 +setgroups: 1 setpriority: 1 -getpgrp: 1 -dup2: 1 -getrandom: 1 -rt_sigreturn: 1 +setresgid: 1 +setresuid: 1 +setuid: 1 +sigaltstack: 1 +socket: arg0 == 0x10 || arg0 == 0x1 +socketpair: 1 +stat: 1 +statx: 1 +umask: 1 +uname: 1 +unlink: 1 wait4: 1 -restart_syscall: 1 -sched_yield: 1
\ No newline at end of file +write: 1 |