• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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 use std::mem;
5 
6 use cros_alsa::{Card, TLV};
7 use sof_sys::sof_abi_hdr;
8 
9 use dsm::{self, Error, Result};
10 
11 /// Amp volume mode enumeration used by set_volume().
12 #[derive(Copy, Clone, PartialEq)]
13 pub enum VolumeMode {
14     /// Low mode protects the speaker by limiting its output volume if the
15     /// calibration has not been completed successfully.
16     Low = 0x1009B9CF,
17     /// High mode removes the speaker output volume limitation after
18     /// having successfully completed the calibration.
19     High = 0x20000000,
20 }
21 
22 #[derive(Copy, Clone)]
23 /// Calibration mode enumeration.
24 pub enum CalibMode {
25     ON = 0x4,
26     OFF = 0x1,
27 }
28 
29 #[derive(Copy, Clone)]
30 /// Smart pilot signal mode mode enumeration.
31 pub enum SPTMode {
32     ON = 0x1,
33     OFF = 0x0,
34 }
35 
36 #[derive(Copy, Clone)]
37 /// DSM Parem field enumeration.
38 enum DsmAPI {
39     ParamCount = 0x0,
40     CalibMode = 0x1,
41     MakeupGain = 0x5,
42     DsmRdc = 0x6,
43     DsmAmbientTemp = 0x8,
44     AdaptiveRdc = 0x12,
45     SPTMode = 0x68,
46 }
47 
48 #[derive(Debug)]
49 /// It implements functions to access the `DSMParam` fields.
50 pub struct DSMParam {
51     param_count: usize,
52     num_channels: usize,
53     tlv: TLV,
54 }
55 
56 impl DSMParam {
57     const DWORD_PER_PARAM: usize = 2;
58     const VALUE_OFFSET: usize = 1;
59     const SOF_HEADER_SIZE: usize = mem::size_of::<sof_abi_hdr>() / mem::size_of::<i32>();
60 
61     /// Creates an `DSMParam`.
62     /// # Arguments
63     ///
64     /// * `card` - `&Card`.
65     /// * `num_channels` - number of channels.
66     /// * `ctl_name` - the mixer control name to access the DSM param.
67     ///
68     /// # Results
69     ///
70     /// * `DSMParam` - It is initialized by the content of the given byte control .
71     ///
72     /// # Errors
73     ///
74     /// * If `Card` creation from sound card name fails.
new(card: &mut Card, num_channels: usize, ctl_name: &str) -> Result<Self>75     pub fn new(card: &mut Card, num_channels: usize, ctl_name: &str) -> Result<Self> {
76         let tlv = card.control_tlv_by_name(ctl_name)?.load()?;
77         Self::try_from_tlv(tlv, num_channels)
78     }
79 
80     /// Sets DSMParam to the given calibration mode.
set_calibration_mode(&mut self, mode: CalibMode)81     pub fn set_calibration_mode(&mut self, mode: CalibMode) {
82         for channel in 0..self.num_channels {
83             self.set(channel, DsmAPI::CalibMode, mode as i32);
84         }
85     }
86 
87     /// Sets DSMParam to the given smart pilot signal mode.
set_spt_mode(&mut self, mode: SPTMode)88     pub fn set_spt_mode(&mut self, mode: SPTMode) {
89         for channel in 0..self.num_channels {
90             self.set(channel, DsmAPI::SPTMode, mode as i32);
91         }
92     }
93 
94     /// Sets DSMParam to the given VolumeMode.
set_volume_mode(&mut self, mode: VolumeMode)95     pub fn set_volume_mode(&mut self, mode: VolumeMode) {
96         for channel in 0..self.num_channels {
97             self.set(channel, DsmAPI::MakeupGain, mode as i32);
98         }
99     }
100 
101     /// Reads the calibrated rdc from DSMParam.
get_adaptive_rdc(&self) -> Vec<i32>102     pub fn get_adaptive_rdc(&self) -> Vec<i32> {
103         self.get(DsmAPI::AdaptiveRdc)
104     }
105 
106     /// Sets DSMParam to the given the calibrated rdc.
set_rdc(&mut self, ch: usize, rdc: i32)107     pub fn set_rdc(&mut self, ch: usize, rdc: i32) {
108         self.set(ch, DsmAPI::DsmRdc, rdc);
109     }
110 
111     /// Sets DSMParam to the given calibrated temp.
set_ambient_temp(&mut self, ch: usize, temp: i32)112     pub fn set_ambient_temp(&mut self, ch: usize, temp: i32) {
113         self.set(ch, DsmAPI::DsmAmbientTemp, temp);
114     }
115 
116     /// Sets the `id` field to the given `val`.
set(&mut self, channel: usize, id: DsmAPI, val: i32)117     fn set(&mut self, channel: usize, id: DsmAPI, val: i32) {
118         let pos = Self::value_pos(self.param_count, channel, id);
119         self.tlv[pos] = val as u32;
120     }
121 
122     /// Gets the val from the `id` field from all the channels.
get(&self, id: DsmAPI) -> Vec<i32>123     fn get(&self, id: DsmAPI) -> Vec<i32> {
124         (0..self.num_channels)
125             .map(|channel| {
126                 let pos = Self::value_pos(self.param_count, channel, id);
127                 self.tlv[pos] as i32
128             })
129             .collect()
130     }
131 
try_from_tlv(tlv: TLV, num_channels: usize) -> Result<Self>132     fn try_from_tlv(tlv: TLV, num_channels: usize) -> Result<Self> {
133         let param_count_pos = Self::value_pos(0, 0, DsmAPI::ParamCount);
134 
135         if tlv.len() < param_count_pos {
136             return Err(Error::InvalidDSMParam);
137         }
138 
139         let param_count = tlv[param_count_pos] as usize;
140 
141         if tlv.len() != Self::SOF_HEADER_SIZE + param_count * num_channels * Self::DWORD_PER_PARAM {
142             return Err(Error::InvalidDSMParam);
143         }
144 
145         Ok(Self {
146             param_count,
147             num_channels,
148             tlv,
149         })
150     }
151 
152     #[inline]
value_pos(param_count: usize, channel: usize, id: DsmAPI) -> usize153     fn value_pos(param_count: usize, channel: usize, id: DsmAPI) -> usize {
154         Self::SOF_HEADER_SIZE
155             + (channel * param_count + id as usize) * Self::DWORD_PER_PARAM
156             + Self::VALUE_OFFSET
157     }
158 }
159 
160 impl Into<TLV> for DSMParam {
into(self) -> TLV161     fn into(self) -> TLV {
162         self.tlv
163     }
164 }
165 
166 #[cfg(test)]
167 mod tests {
168     use super::*;
169     const PARAM_COUNT: usize = 138;
170     const CHANNEL_COUNT: usize = 2;
171 
172     #[test]
test_dsmparam_try_from_tlv_ok()173     fn test_dsmparam_try_from_tlv_ok() {
174         let mut data = vec![
175             0u32;
176             DSMParam::SOF_HEADER_SIZE
177                 + CHANNEL_COUNT * PARAM_COUNT * DSMParam::DWORD_PER_PARAM
178         ];
179         data[DSMParam::value_pos(PARAM_COUNT, 0, DsmAPI::ParamCount)] = PARAM_COUNT as u32;
180         data[DSMParam::value_pos(PARAM_COUNT, 1, DsmAPI::ParamCount)] = PARAM_COUNT as u32;
181 
182         let tlv = TLV::new(0, data);
183         assert!(DSMParam::try_from_tlv(tlv, CHANNEL_COUNT).is_ok());
184     }
185 
186     #[test]
test_dsmparam_try_from_invalid_len()187     fn test_dsmparam_try_from_invalid_len() {
188         let data = vec![0u32; DSMParam::SOF_HEADER_SIZE];
189 
190         let tlv = TLV::new(0, data);
191         assert_eq!(
192             DSMParam::try_from_tlv(tlv, CHANNEL_COUNT).unwrap_err(),
193             Error::InvalidDSMParam
194         );
195     }
196 
197     #[test]
test_dsmparam_try_from_param_count()198     fn test_dsmparam_try_from_param_count() {
199         let data = vec![
200             0u32;
201             DSMParam::SOF_HEADER_SIZE
202                 + CHANNEL_COUNT * PARAM_COUNT * DSMParam::DWORD_PER_PARAM
203         ];
204 
205         let tlv = TLV::new(0, data);
206         assert_eq!(
207             DSMParam::try_from_tlv(tlv, CHANNEL_COUNT).unwrap_err(),
208             Error::InvalidDSMParam
209         );
210     }
211 }
212