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