summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJudy Hsiao <judyhsiao@google.com>2020-12-21 11:47:43 +0800
committerCommit Bot <commit-bot@chromium.org>2021-02-02 16:30:46 +0000
commit287ea8bbe90e991fd7f1e147d59113bf2d23c203 (patch)
treebc7b241bb017551cfd877f3cb4cef6bdd6082371
parentcb1f53abc38a16258268c2571bf58b79c76033ed (diff)
downloadadhd-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.lock62
-rw-r--r--sound_card_init/Cargo.toml8
-rw-r--r--sound_card_init/amp/Cargo.lock254
-rw-r--r--sound_card_init/amp/Cargo.toml1
-rw-r--r--sound_card_init/amp/src/lib.rs3
-rw-r--r--sound_card_init/amp/src/max98373d/dsm_param.rs211
-rw-r--r--sound_card_init/amp/src/max98373d/mod.rs267
-rw-r--r--sound_card_init/amp/src/max98373d/settings.rs40
-rw-r--r--sound_card_init/dsm/src/error.rs24
-rw-r--r--sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy123
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