196015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 296015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid * Use of this source code is governed by a BSD-style license that can be 396015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid * found in the LICENSE file. 496015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid */ 596015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 696015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include <alsa/asoundlib.h> 796015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include <errno.h> 896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include <limits.h> 996015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include <stdio.h> 102c1e4a94b93ea1ca23ae1ab177a80ad769a7c18aDylan Reid#include <sys/param.h> 1196015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include <sys/select.h> 1296015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include <sys/socket.h> 1396015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include <sys/time.h> 1496015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include <syslog.h> 1596015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include <time.h> 1696015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 1722fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid#include "audio_thread.h" 1896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include "cras_alsa_helpers.h" 195efd578fe4637bab0f766e736d5a6a8baa0accb7Dylan Reid#include "cras_alsa_io.h" 20c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid#include "cras_alsa_jack.h" 217e97d133d0acf2e9dceba372c60db2bd2e0a3660Dylan Reid#include "cras_alsa_mixer.h" 2220ca812f9b00ec3446c2fd849524e26488e21c3cDylan Reid#include "cras_alsa_ucm.h" 23cc32f2cfb655c1c2a39a1db448415583f8ecc2d0Hsin-Yu Chao#include "cras_audio_area.h" 2496015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include "cras_config.h" 2554d7d7c2c921c0e264b1b22d4f5ffe642e3d4f9dJohn Muir#include "cras_utf8.h" 2696015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include "cras_iodev.h" 2796015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include "cras_iodev_list.h" 2896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include "cras_messages.h" 2964350200f69f730bbb1eb33a648c8dd6c840da0bCheng-Yi Chiang#include "cras_ramp.h" 3096015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include "cras_rclient.h" 3196015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include "cras_shm.h" 3258fe4e9053c78a9a5c2ea2027f4cc994fab4b3e3Dylan Reid#include "cras_system_state.h" 3396015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include "cras_types.h" 3496015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include "cras_util.h" 357e97d133d0acf2e9dceba372c60db2bd2e0a3660Dylan Reid#include "cras_volume_curve.h" 3685137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen#include "sfh.h" 372f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen#include "softvol_curve.h" 3896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid#include "utlist.h" 3996015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 4061d1159da08dd6c4258316cc41865f5d5ad6a5c2Dylan Reid#define MAX_ALSA_DEV_NAME_LENGTH 9 /* Alsa names "hw:XX,YY" + 1 for null. */ 418c321ad746a3a71d07711e236496d813e816de23Hsin-Yu Chao#define HOTWORD_DEV "Wake on Voice" 42363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang#define DEFAULT "(default)" 439fbbb0f0e4a59c331aeca2fccfd6a85938cd7d00Hsin-Yu Chao#define HDMI "HDMI" 4432a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang#define INTERNAL_MICROPHONE "Internal Mic" 45363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang#define INTERNAL_SPEAKER "Speaker" 46c90fa076712768ee9a7e721e8f99d2858207e026Hsin-Yu Chao#define KEYBOARD_MIC "Keyboard Mic" 47363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang#define USB "USB" 4896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 4904bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 5004bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * For USB, pad the output buffer. This avoids a situation where there isn't a 514a7caa2e2c43ac6426facff6803dae097a1322e4Dylan Reid * complete URB's worth of audio ready to be transmitted when it is requested. 524a7caa2e2c43ac6426facff6803dae097a1322e4Dylan Reid * The URB interval does track directly to the audio clock, making it hard to 5304bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * predict the exact interval. 5404bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 554a7caa2e2c43ac6426facff6803dae097a1322e4Dylan Reid#define USB_EXTRA_BUFFER_FRAMES 768 564a7caa2e2c43ac6426facff6803dae097a1322e4Dylan Reid 5704bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 5804bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * When snd_pcm_avail returns a value that is greater than buffer size, 590caf60b0734cc215ad147320a42614635dd9fdacCheng-Yi Chiang * we know there is an underrun. If the number of underrun samples 600caf60b0734cc215ad147320a42614635dd9fdacCheng-Yi Chiang * (avail - buffer_size) is greater than SEVERE_UNDERRUN_MS * rate, 610caf60b0734cc215ad147320a42614635dd9fdacCheng-Yi Chiang * it is a severe underrun. Main thread should disable and then enable 6204bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * device to recover it from underrun. 6304bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 64e4cfb42dc25e5196bc0115c1402db24359adfe8bCheng-Yi Chiang#define SEVERE_UNDERRUN_MS 5000 650caf60b0734cc215ad147320a42614635dd9fdacCheng-Yi Chiang 6604bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 6704bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * This extends cras_ionode to include alsa-specific information. 685efd578fe4637bab0f766e736d5a6a8baa0accb7Dylan Reid * Members: 695efd578fe4637bab0f766e736d5a6a8baa0accb7Dylan Reid * mixer_output - From cras_alsa_mixer. 70428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao * volume_curve - Volume curve for this node. 71428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao * jack - The jack associated with the node. 725efd578fe4637bab0f766e736d5a6a8baa0accb7Dylan Reid */ 7317683d28d93572fc1a51336c363e94110e453857Dylan Reidstruct alsa_output_node { 740373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang struct cras_ionode base; 75d21516ae3065cd2bdc992b43ebcdad7b2b1fd778Yao-Wen Mao struct mixer_control *mixer_output; 76428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao struct cras_volume_curve *volume_curve; 77ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid const struct cras_alsa_jack *jack; 7817683d28d93572fc1a51336c363e94110e453857Dylan Reid}; 7917683d28d93572fc1a51336c363e94110e453857Dylan Reid 802c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chaostruct alsa_input_node { 810373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang struct cras_ionode base; 82d21516ae3065cd2bdc992b43ebcdad7b2b1fd778Yao-Wen Mao struct mixer_control* mixer_input; 832c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao const struct cras_alsa_jack *jack; 84d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao int8_t *channel_layout; 852c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao}; 862c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao 8704bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 8804bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Child of cras_iodev, alsa_io handles ALSA interaction for sound devices. 8996015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid * base - The cras_iodev structure "base class". 9096015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid * dev - String that names this device (e.g. "hw:0,0"). 91f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir * dev_name - value from snd_pcm_info_get_name 92f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir * dev_id - value from snd_pcm_info_get_id 9361d1159da08dd6c4258316cc41865f5d5ad6a5c2Dylan Reid * device_index - ALSA index of device, Y in "hw:X:Y". 946c9585dab2ebdcfb6dc557f6c7e9d6b5845a2cd7Chih-Chung Chang * next_ionode_index - The index we will give to the next ionode. Each ionode 956c9585dab2ebdcfb6dc557f6c7e9d6b5845a2cd7Chih-Chung Chang * have a unique index within the iodev. 963543aa74b7b45ee6a73f3c6817a9b14e9bcc47ebChih-Chung Chang * card_type - the type of the card this iodev belongs. 973543aa74b7b45ee6a73f3c6817a9b14e9bcc47ebChih-Chung Chang * is_first - true if this is the first iodev on the card. 98f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir * fully_specified - true if this device and it's nodes were fully specified. 99f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir * That is, don't automatically create nodes for it. 100fa4cd9766aec6eae544b1078ed835687c4e74617John Muir * enable_htimestamp - True when the device's htimestamp is used. 10196015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid * handle - Handle to the opened ALSA device. 10296015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid * num_underruns - Number of times we have run out of data (playback only). 103d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang * num_severe_underruns - Number of times we have run out of data badly. 104d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang Unlike num_underruns which records for the duration 105d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang where device is opened, num_severe_underruns records 106d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang since device is created. When severe underrun occurs 107d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang a possible action is to close/open device. 10896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid * alsa_stream - Playback or capture type. 1097e97d133d0acf2e9dceba372c60db2bd2e0a3660Dylan Reid * mixer - Alsa mixer used to control volume and mute of the device. 110428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao * config - Card config for this alsa device. 111c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid * jack_list - List of alsa jack controls for this device. 1129b631e24e0943ab1690cb93ffb916ffe23afe410Chinyue Chen * ucm - CRAS use case manager, if configuration is found. 113689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid * mmap_offset - offset returned from mmap_begin. 1147057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang * dsp_name_default - the default dsp name for the device. It can be overridden 1157057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang * by the jack specific dsp name. 116e7886389b0c2480c17582f752538c05d1b403a1dDylan Reid * poll_fd - Descriptor used to block until data is ready. 1178041030fdb87eef2aef22d3d4b347c266b418c5fJohn Muir * dma_period_set_microsecs - If non-zero, the value to apply to the dma_period. 118016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang * is_free_running - true if device is playing zeros in the buffer without 119016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang * user filling meaningful data. The device buffer is filled 120016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang * with zeros. In this state, appl_ptr remains the same 121016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang * while hw_ptr keeps running ahead. 122016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang * filled_zeros_for_draining - The number of zeros filled for draining. 1230caf60b0734cc215ad147320a42614635dd9fdacCheng-Yi Chiang * severe_underrun_frames - The threshold for severe underrun. 124428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao * default_volume_curve - Default volume curve that converts from an index 125428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao * to dBFS. 12696015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid */ 12796015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reidstruct alsa_io { 12896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid struct cras_iodev base; 12926296d882f4a5387bfecc634b51a891158ce5543Dylan Reid char *dev; 130f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir char *dev_name; 131f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir char *dev_id; 13254831a4bfe525d5e19bacdc4ccffcc3f229efde8Chih-Chung Chang uint32_t device_index; 13354831a4bfe525d5e19bacdc4ccffcc3f229efde8Chih-Chung Chang uint32_t next_ionode_index; 1343543aa74b7b45ee6a73f3c6817a9b14e9bcc47ebChih-Chung Chang enum CRAS_ALSA_CARD_TYPE card_type; 1353543aa74b7b45ee6a73f3c6817a9b14e9bcc47ebChih-Chung Chang int is_first; 136f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir int fully_specified; 137fa4cd9766aec6eae544b1078ed835687c4e74617John Muir int enable_htimestamp; 13896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid snd_pcm_t *handle; 139689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid unsigned int num_underruns; 140d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang unsigned int num_severe_underruns; 14196015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid snd_pcm_stream_t alsa_stream; 1427e97d133d0acf2e9dceba372c60db2bd2e0a3660Dylan Reid struct cras_alsa_mixer *mixer; 143428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao const struct cras_card_config *config; 144c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid struct cras_alsa_jack_list *jack_list; 1459b631e24e0943ab1690cb93ffb916ffe23afe410Chinyue Chen struct cras_use_case_mgr *ucm; 146689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid snd_pcm_uframes_t mmap_offset; 1477057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang const char *dsp_name_default; 148e7886389b0c2480c17582f752538c05d1b403a1dDylan Reid int poll_fd; 1498041030fdb87eef2aef22d3d4b347c266b418c5fJohn Muir unsigned int dma_period_set_microsecs; 150016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang int is_free_running; 151016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang unsigned int filled_zeros_for_draining; 1520caf60b0734cc215ad147320a42614635dd9fdacCheng-Yi Chiang snd_pcm_uframes_t severe_underrun_frames; 153428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao struct cras_volume_curve *default_volume_curve; 15496015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid}; 15596015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 156d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reidstatic void init_device_settings(struct alsa_io *aio); 157d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid 158cc5246e10e7f3da17d72592db6d8cf599ab05a38Hsin-Yu Chaostatic int alsa_iodev_set_active_node(struct cras_iodev *iodev, 1590def72b968591065f56e88d67e5c83234184811bHsin-Yu Chao struct cras_ionode *ionode, 1600def72b968591065f56e88d67e5c83234184811bHsin-Yu Chao unsigned dev_enabled); 161cc5246e10e7f3da17d72592db6d8cf599ab05a38Hsin-Yu Chao 16204bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 16304bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Defines the default values of nodes. 16404bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 165bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chaostatic const struct { 166bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao const char *name; 167bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao enum CRAS_NODE_TYPE type; 168bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao enum CRAS_NODE_POSITION position; 169bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao} node_defaults[] = { 170bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 171bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = DEFAULT, 172bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_UNKNOWN, 173bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_INTERNAL, 174bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 175bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 176bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = INTERNAL_SPEAKER, 177bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_INTERNAL_SPEAKER, 178bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_INTERNAL, 179bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 180bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 181bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = INTERNAL_MICROPHONE, 182bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_MIC, 183bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_INTERNAL, 184bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 185bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 186bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = KEYBOARD_MIC, 187bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_MIC, 188bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_KEYBOARD, 189bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 190bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 191bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = HDMI, 192bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_HDMI, 193bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_EXTERNAL, 194bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 195bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 196bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = "IEC958", 197bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_HDMI, 198bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_EXTERNAL, 199bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 200bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 201bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = "Headphone", 202bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_HEADPHONE, 203bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_EXTERNAL, 204bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 205bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 206bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = "Front Headphone", 207bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_HEADPHONE, 208bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_EXTERNAL, 209bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 210bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 211bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = "Front Mic", 212bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_MIC, 213bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_FRONT, 214bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 215bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 216bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = "Rear Mic", 217bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_MIC, 218bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_REAR, 219bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 220bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 221bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = "Mic", 222bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_MIC, 223bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_EXTERNAL, 224bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 225bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 226bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = HOTWORD_DEV, 227bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_HOTWORD, 228bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_INTERNAL, 229bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 230bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 231bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = "Haptic", 232bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_HAPTIC, 233bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_INTERNAL, 234bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 235bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 236bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = "Rumbler", 237bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_HAPTIC, 238bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_INTERNAL, 239bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 240bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao { 241bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .name = "Line Out", 242bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .type = CRAS_NODE_TYPE_LINEOUT, 243bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao .position = NODE_POSITION_EXTERNAL, 244bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao }, 245bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao}; 246bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao 247d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid/* 248d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid * iodev callbacks. 249d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid */ 250d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid 251fa4cd9766aec6eae544b1078ed835687c4e74617John Muirstatic int frames_queued(const struct cras_iodev *iodev, 252fa4cd9766aec6eae544b1078ed835687c4e74617John Muir struct timespec *tstamp) 253ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid{ 254ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid struct alsa_io *aio = (struct alsa_io *)iodev; 255ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid int rc; 256ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid snd_pcm_uframes_t frames; 257ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid 258ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid rc = cras_alsa_get_avail_frames(aio->handle, 259ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid aio->base.buffer_size, 2600caf60b0734cc215ad147320a42614635dd9fdacCheng-Yi Chiang aio->severe_underrun_frames, 2610caf60b0734cc215ad147320a42614635dd9fdacCheng-Yi Chiang iodev->info.name, 262fa4cd9766aec6eae544b1078ed835687c4e74617John Muir &frames, tstamp, 263595b415568beb0048c31e3111c5c935530be1032Cheng-Yi Chiang &aio->num_underruns); 264d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang if (rc < 0) { 265d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang if (rc == -EPIPE) 266d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang aio->num_severe_underruns++; 267ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid return rc; 268d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang } 269fa4cd9766aec6eae544b1078ed835687c4e74617John Muir if (!aio->enable_htimestamp) 270fa4cd9766aec6eae544b1078ed835687c4e74617John Muir clock_gettime(CLOCK_MONOTONIC_RAW, tstamp); 271ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid if (iodev->direction == CRAS_STREAM_INPUT) 272ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid return (int)frames; 273ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid 274ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid /* For output, return number of frames that are used. */ 275f9b98b36189361428b2d58e61d438cee0d0140deHsin-Yu Chao return iodev->buffer_size - frames; 276ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid} 277ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid 278d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reidstatic int delay_frames(const struct cras_iodev *iodev) 279ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid{ 280ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid struct alsa_io *aio = (struct alsa_io *)iodev; 281ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid snd_pcm_sframes_t delay; 282ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid int rc; 283ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid 284ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid rc = cras_alsa_get_delay_frames(aio->handle, 285ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid iodev->buffer_size, 286ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid &delay); 287ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid if (rc < 0) 288ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid return rc; 289ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid 290ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid return (int)delay; 291ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid} 292ad6c53b76a555d5b54dda5ef747c8bd20c8fa393Dylan Reid 293866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reidstatic int close_dev(struct cras_iodev *iodev) 294866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid{ 295866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid struct alsa_io *aio = (struct alsa_io *)iodev; 29614ceeb638c4017ab70781970960cba849238ad43Dylan Reid 2975528e7442cd3b86037fa84e45ca42cf10b126bdfHsin-Yu Chao /* Removes audio thread callback from main thread. */ 298e7886389b0c2480c17582f752538c05d1b403a1dDylan Reid if (aio->poll_fd >= 0) 2995528e7442cd3b86037fa84e45ca42cf10b126bdfHsin-Yu Chao audio_thread_rm_callback_sync( 3005528e7442cd3b86037fa84e45ca42cf10b126bdfHsin-Yu Chao cras_iodev_list_get_audio_thread(), 3015528e7442cd3b86037fa84e45ca42cf10b126bdfHsin-Yu Chao aio->poll_fd); 302866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid if (!aio->handle) 303866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid return 0; 304866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid cras_alsa_pcm_close(aio->handle); 305866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid aio->handle = NULL; 306016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang aio->is_free_running = 0; 307016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang aio->filled_zeros_for_draining = 0; 308866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid cras_iodev_free_format(&aio->base); 3099690a3281b7167e5a38368fd8db39375bea8a1f8Hsin-Yu Chao cras_iodev_free_audio_area(&aio->base); 310866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid return 0; 311866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid} 312866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid 3138c321ad746a3a71d07711e236496d813e816de23Hsin-Yu Chaostatic int dummy_hotword_cb(void *arg) 31422fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid{ 315e7886389b0c2480c17582f752538c05d1b403a1dDylan Reid /* Only need this once. */ 316e7886389b0c2480c17582f752538c05d1b403a1dDylan Reid struct alsa_io *aio = (struct alsa_io *)arg; 317e7886389b0c2480c17582f752538c05d1b403a1dDylan Reid audio_thread_rm_callback(aio->poll_fd); 318e7886389b0c2480c17582f752538c05d1b403a1dDylan Reid aio->poll_fd = -1; 31922fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid return 0; 32022fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid} 32122fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid 322866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reidstatic int open_dev(struct cras_iodev *iodev) 32396015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid{ 324866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid struct alsa_io *aio = (struct alsa_io *)iodev; 32596015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid snd_pcm_t *handle; 3260e2584d09b4b171fbf6a9637d13a57a7ad1e4cc8Harsha Priya int period_wakeup; 32796015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid int rc; 32896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 32942d564fca8e23506668ed701d8beb6fc8dcfd497Dylan Reid /* This is called after the first stream added so configure for it. 33042d564fca8e23506668ed701d8beb6fc8dcfd497Dylan Reid * format must be set before opening the device. 33142d564fca8e23506668ed701d8beb6fc8dcfd497Dylan Reid */ 332866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid if (iodev->format == NULL) 33396015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid return -EINVAL; 33496015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid aio->num_underruns = 0; 335016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang aio->is_free_running = 0; 336016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang aio->filled_zeros_for_draining = 0; 3370caf60b0734cc215ad147320a42614635dd9fdacCheng-Yi Chiang aio->severe_underrun_frames = 3380caf60b0734cc215ad147320a42614635dd9fdacCheng-Yi Chiang SEVERE_UNDERRUN_MS * iodev->format->frame_rate / 1000; 3390caf60b0734cc215ad147320a42614635dd9fdacCheng-Yi Chiang 3409690a3281b7167e5a38368fd8db39375bea8a1f8Hsin-Yu Chao cras_iodev_init_audio_area(iodev, iodev->format->num_channels); 34196015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 342cc4cabcf609be736876bef5de658aa8a2a7600a7Dylan Reid syslog(LOG_DEBUG, "Configure alsa device %s rate %zuHz, %zu channels", 343866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid aio->dev, iodev->format->frame_rate, 344866b57c504b9a33a1ec12dfb70ed20436ce3b89fDylan Reid iodev->format->num_channels); 3452e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen handle = 0; /* Avoid unused warning. */ 3462e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen rc = cras_alsa_pcm_open(&handle, aio->dev, aio->alsa_stream); 3472e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen if (rc < 0) 3482e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen return rc; 34996015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 3500e2584d09b4b171fbf6a9637d13a57a7ad1e4cc8Harsha Priya /* If it's a wake on voice device, period_wakeups are required. */ 3518c321ad746a3a71d07711e236496d813e816de23Hsin-Yu Chao period_wakeup = (iodev->active_node->type == CRAS_NODE_TYPE_HOTWORD); 352828868cb145790177903b68e229a397376e60c0cJohn Muir 3532e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen rc = cras_alsa_set_hwparams(handle, iodev->format, 354828868cb145790177903b68e229a397376e60c0cJohn Muir &iodev->buffer_size, period_wakeup, 3558041030fdb87eef2aef22d3d4b347c266b418c5fJohn Muir aio->dma_period_set_microsecs); 3562e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen if (rc < 0) { 3572e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen cras_alsa_pcm_close(handle); 35896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid return rc; 3592e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen } 36096015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 361663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao /* Set channel map to device */ 3622e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen rc = cras_alsa_set_channel_map(handle, 363663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao iodev->format); 3642e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen if (rc < 0) { 3652e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen cras_alsa_pcm_close(handle); 366663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao return rc; 3672e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen } 368663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao 369df0a98dc3d6d636a8a168a4086a4acc9746f990aDylan Reid /* Configure software params. */ 3702e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen rc = cras_alsa_set_swparams(handle, &aio->enable_htimestamp); 3712e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen if (rc < 0) { 3722e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen cras_alsa_pcm_close(handle); 37396015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid return rc; 3742e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen } 37596015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 3762e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen /* Assign pcm handle then initialize device settings. */ 3772e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen aio->handle = handle; 378948be308fcd7c04bfdb7c154dd6ef90e5fc61070Dylan Reid init_device_settings(aio); 37996015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 380e7886389b0c2480c17582f752538c05d1b403a1dDylan Reid aio->poll_fd = -1; 3818c321ad746a3a71d07711e236496d813e816de23Hsin-Yu Chao if (iodev->active_node->type == CRAS_NODE_TYPE_HOTWORD) { 38222fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid struct pollfd *ufds; 38322fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid int count, i; 38422fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid 3852e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen count = snd_pcm_poll_descriptors_count(handle); 38622fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid if (count <= 0) { 38722fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid syslog(LOG_ERR, "Invalid poll descriptors count\n"); 38822fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid return count; 38922fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid } 39022fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid 39122fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * count); 39222fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid if (ufds == NULL) 39322fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid return -ENOMEM; 39422fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid 3952e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen rc = snd_pcm_poll_descriptors(handle, ufds, count); 39622fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid if (rc < 0) { 39722fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid syslog(LOG_ERR, 39822fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid "Getting hotword poll descriptors: %s\n", 39922fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid snd_strerror(rc)); 40022fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid free(ufds); 40122fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid return rc; 40222fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid } 40322fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid 40422fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid for (i = 0; i < count; i++) { 40522fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid if (ufds[i].events & POLLIN) { 406e7886389b0c2480c17582f752538c05d1b403a1dDylan Reid aio->poll_fd = ufds[i].fd; 40722fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid break; 40822fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid } 40922fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid } 41022fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid free(ufds); 41122fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid 412e7886389b0c2480c17582f752538c05d1b403a1dDylan Reid if (aio->poll_fd >= 0) 4138c321ad746a3a71d07711e236496d813e816de23Hsin-Yu Chao audio_thread_add_callback(aio->poll_fd, 4148c321ad746a3a71d07711e236496d813e816de23Hsin-Yu Chao dummy_hotword_cb, 415e7886389b0c2480c17582f752538c05d1b403a1dDylan Reid aio); 41622fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid } 41722fb91edcc50170481670f922e3ff69d5ebf8e05Dylan Reid 418df0a98dc3d6d636a8a168a4086a4acc9746f990aDylan Reid /* Capture starts right away, playback will wait for samples. */ 419c1945f99727f89227abb666d99a532223623320eDylan Reid if (aio->alsa_stream == SND_PCM_STREAM_CAPTURE) 42096015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid cras_alsa_pcm_start(aio->handle); 42196015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 42296015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid return 0; 42396015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid} 42496015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 42504bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 42604bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Check if ALSA device is opened by checking if handle is valid. 42750cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang * Note that to fully open a cras_iodev, ALSA device is opened first, then there 42850cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang * are some device init settings to be done in init_device_settings. 42950cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang * Therefore, when setting volume/mute/gain in init_device_settings, 43050cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang * cras_iodev is not in CRAS_IODEV_STATE_OPEN yet. We need to check if handle 43150cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang * is valid when setting those properties, instead of checking 43250cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang * cras_iodev_is_open. 43350cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang */ 43450cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiangstatic int has_handle(const struct alsa_io *aio) 435d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid{ 436d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid return !!aio->handle; 437d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid} 438d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid 439e91895c69bef2515b60d6e4514a5f8e8c53cb36fCheng-Yi Chiangstatic int start(const struct cras_iodev *iodev) 440e91895c69bef2515b60d6e4514a5f8e8c53cb36fCheng-Yi Chiang{ 441e91895c69bef2515b60d6e4514a5f8e8c53cb36fCheng-Yi Chiang struct alsa_io *aio = (struct alsa_io *)iodev; 442e91895c69bef2515b60d6e4514a5f8e8c53cb36fCheng-Yi Chiang snd_pcm_t *handle = aio->handle; 443d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid int rc; 444d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid 445d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING) 446e91895c69bef2515b60d6e4514a5f8e8c53cb36fCheng-Yi Chiang return 0; 447d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid 448d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid if (snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) { 449d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid rc = cras_alsa_attempt_resume(handle); 450cb662443cb58291cb7c951e1bf12bea86a23d876Hsin-Yu Chao if (rc < 0) { 451cb662443cb58291cb7c951e1bf12bea86a23d876Hsin-Yu Chao syslog(LOG_ERR, "Resume error: %s", snd_strerror(rc)); 452e91895c69bef2515b60d6e4514a5f8e8c53cb36fCheng-Yi Chiang return rc; 453cb662443cb58291cb7c951e1bf12bea86a23d876Hsin-Yu Chao } 45427176a407129b2ac1e7c8d0b7dd0e3590b72615fHsin-Yu Chao cras_iodev_reset_rate_estimator(iodev); 455d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid } else { 456d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid rc = cras_alsa_pcm_start(handle); 457d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid if (rc < 0) { 458d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid syslog(LOG_ERR, "Start error: %s", snd_strerror(rc)); 459e91895c69bef2515b60d6e4514a5f8e8c53cb36fCheng-Yi Chiang return rc; 460d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid } 461d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid } 462d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid 463e91895c69bef2515b60d6e4514a5f8e8c53cb36fCheng-Yi Chiang return 0; 464d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid} 465d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid 466cc32f2cfb655c1c2a39a1db448415583f8ecc2d0Hsin-Yu Chaostatic int get_buffer(struct cras_iodev *iodev, 467cc32f2cfb655c1c2a39a1db448415583f8ecc2d0Hsin-Yu Chao struct cras_audio_area **area, 468cc32f2cfb655c1c2a39a1db448415583f8ecc2d0Hsin-Yu Chao unsigned *frames) 469689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid{ 470689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid struct alsa_io *aio = (struct alsa_io *)iodev; 471689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid snd_pcm_uframes_t nframes = *frames; 472cc32f2cfb655c1c2a39a1db448415583f8ecc2d0Hsin-Yu Chao uint8_t *dst = NULL; 473cc32f2cfb655c1c2a39a1db448415583f8ecc2d0Hsin-Yu Chao size_t format_bytes; 474689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid int rc; 475689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid 476689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid aio->mmap_offset = 0; 477cc32f2cfb655c1c2a39a1db448415583f8ecc2d0Hsin-Yu Chao format_bytes = cras_get_format_bytes(iodev->format); 478689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid 479689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid rc = cras_alsa_mmap_begin(aio->handle, 480cc32f2cfb655c1c2a39a1db448415583f8ecc2d0Hsin-Yu Chao format_bytes, 481cc32f2cfb655c1c2a39a1db448415583f8ecc2d0Hsin-Yu Chao &dst, 482689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid &aio->mmap_offset, 483689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid &nframes, 484689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid &aio->num_underruns); 485689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid 486cc32f2cfb655c1c2a39a1db448415583f8ecc2d0Hsin-Yu Chao iodev->area->frames = nframes; 487cc32f2cfb655c1c2a39a1db448415583f8ecc2d0Hsin-Yu Chao cras_audio_area_config_buf_pointers(iodev->area, iodev->format, dst); 488cc32f2cfb655c1c2a39a1db448415583f8ecc2d0Hsin-Yu Chao 489cc32f2cfb655c1c2a39a1db448415583f8ecc2d0Hsin-Yu Chao *area = iodev->area; 490689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid *frames = nframes; 491689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid 492689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid return rc; 493689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid} 494689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid 495689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reidstatic int put_buffer(struct cras_iodev *iodev, unsigned nwritten) 496689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid{ 497689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid struct alsa_io *aio = (struct alsa_io *)iodev; 498689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid 499689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid return cras_alsa_mmap_commit(aio->handle, 500689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid aio->mmap_offset, 501689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid nwritten, 502689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid &aio->num_underruns); 503689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid} 504689e18d21acca8dc40835ed9e96452e33e5f0878Dylan Reid 5053f787ac37a53be6b3a065ee5d1a47427fae78100Hsin-Yu Chaostatic int flush_buffer(struct cras_iodev *iodev) 5063f787ac37a53be6b3a065ee5d1a47427fae78100Hsin-Yu Chao{ 5073f787ac37a53be6b3a065ee5d1a47427fae78100Hsin-Yu Chao struct alsa_io *aio = (struct alsa_io *)iodev; 5083f787ac37a53be6b3a065ee5d1a47427fae78100Hsin-Yu Chao snd_pcm_uframes_t nframes; 5093f787ac37a53be6b3a065ee5d1a47427fae78100Hsin-Yu Chao 5103f787ac37a53be6b3a065ee5d1a47427fae78100Hsin-Yu Chao if (iodev->direction == CRAS_STREAM_INPUT) { 5113f787ac37a53be6b3a065ee5d1a47427fae78100Hsin-Yu Chao nframes = snd_pcm_forwardable(aio->handle); 5123f787ac37a53be6b3a065ee5d1a47427fae78100Hsin-Yu Chao return snd_pcm_forward(aio->handle, nframes); 5133f787ac37a53be6b3a065ee5d1a47427fae78100Hsin-Yu Chao } 5143f787ac37a53be6b3a065ee5d1a47427fae78100Hsin-Yu Chao return 0; 5153f787ac37a53be6b3a065ee5d1a47427fae78100Hsin-Yu Chao} 5163f787ac37a53be6b3a065ee5d1a47427fae78100Hsin-Yu Chao 51704bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 51804bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Gets the first plugged node in list. This is used as the 51904bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * default node to set as active. 52004bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 521bb28140f38fdf6627ec26696c943dfa4f9188450Hsin-Yu Chaostatic struct cras_ionode *first_plugged_node(struct cras_iodev *iodev) 52222443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao{ 52322443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao struct cras_ionode *n; 52422443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao 52522443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao /* When this is called at iodev creation, none of the nodes 52622443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao * are selected. Just pick the first plugged one and let Chrome 52722443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao * choose it later. */ 52822443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao DL_FOREACH(iodev->nodes, n) { 52922443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao if (n->plugged) 53022443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao return n; 53122443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao } 53222443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao return iodev->nodes; 53322443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao} 53422443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao 5350def72b968591065f56e88d67e5c83234184811bHsin-Yu Chaostatic void update_active_node(struct cras_iodev *iodev, unsigned node_idx, 5360def72b968591065f56e88d67e5c83234184811bHsin-Yu Chao unsigned dev_enabled) 537741ad2848629417c45ce2064b77cde0e26538f04Chih-Chung Chang{ 538bb28140f38fdf6627ec26696c943dfa4f9188450Hsin-Yu Chao struct cras_ionode *n; 539bb28140f38fdf6627ec26696c943dfa4f9188450Hsin-Yu Chao 540bb28140f38fdf6627ec26696c943dfa4f9188450Hsin-Yu Chao /* If a node exists for node_idx, set it as active. */ 541bb28140f38fdf6627ec26696c943dfa4f9188450Hsin-Yu Chao DL_FOREACH(iodev->nodes, n) { 542bb28140f38fdf6627ec26696c943dfa4f9188450Hsin-Yu Chao if (n->idx == node_idx) { 5430def72b968591065f56e88d67e5c83234184811bHsin-Yu Chao alsa_iodev_set_active_node(iodev, n, dev_enabled); 544bb28140f38fdf6627ec26696c943dfa4f9188450Hsin-Yu Chao return; 545bb28140f38fdf6627ec26696c943dfa4f9188450Hsin-Yu Chao } 546bb28140f38fdf6627ec26696c943dfa4f9188450Hsin-Yu Chao } 547a8aad4db2c01670f344d10cf9a17ac7a45c5cec2Chih-Chung Chang 5480def72b968591065f56e88d67e5c83234184811bHsin-Yu Chao alsa_iodev_set_active_node(iodev, first_plugged_node(iodev), 5490def72b968591065f56e88d67e5c83234184811bHsin-Yu Chao dev_enabled); 550741ad2848629417c45ce2064b77cde0e26538f04Chih-Chung Chang} 551741ad2848629417c45ce2064b77cde0e26538f04Chih-Chung Chang 552663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chaostatic int update_channel_layout(struct cras_iodev *iodev) 553663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao{ 554663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao struct alsa_io *aio = (struct alsa_io *)iodev; 5552e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen snd_pcm_t *handle = NULL; 556663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao snd_pcm_uframes_t buf_size = 0; 557663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao int err = 0; 558663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao 559d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao /* If the capture channel map is specified in UCM, prefer it over 560d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao * what ALSA provides. */ 561d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao if (aio->ucm && (iodev->direction == CRAS_STREAM_INPUT)) { 562d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao struct alsa_input_node *input = 563d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao (struct alsa_input_node *)iodev->active_node; 564d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao 565d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao if (input->channel_layout) { 566d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao memcpy(iodev->format->channel_layout, 567d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao input->channel_layout, 568d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao CRAS_CH_MAX * sizeof(*input->channel_layout)); 569d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao return 0; 570d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao } 571d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao } 572d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao 5732e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen err = cras_alsa_pcm_open(&handle, aio->dev, aio->alsa_stream); 5742e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen if (err < 0) { 5752e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen syslog(LOG_ERR, "snd_pcm_open_failed: %s", snd_strerror(err)); 5762e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen return err; 5772e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen } 5782e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen 579663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao /* Sets frame rate and channel count to alsa device before 580663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao * we test channel mapping. */ 5812e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen err = cras_alsa_set_hwparams(handle, iodev->format, &buf_size, 0, 5828041030fdb87eef2aef22d3d4b347c266b418c5fJohn Muir aio->dma_period_set_microsecs); 5832e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen if (err < 0) { 5842e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen cras_alsa_pcm_close(handle); 585663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao return err; 5862e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen } 5872e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen 5882e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen err = cras_alsa_get_channel_map(handle, iodev->format); 589663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao 5902e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen cras_alsa_pcm_close(handle); 5912e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen return err; 592663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao} 593663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao 5943e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chaostatic int set_hotword_model(struct cras_iodev *iodev, const char *model_name) 5953e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao{ 5963e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao struct alsa_io *aio = (struct alsa_io *)iodev; 5973e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao if (!aio->ucm) 5983e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao return -EINVAL; 5993e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao 6003e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao return ucm_set_hotword_model(aio->ucm, model_name); 6013e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao} 6023e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao 6033e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chaostatic char *get_hotword_models(struct cras_iodev *iodev) 6043e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao{ 6053e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao struct alsa_io *aio = (struct alsa_io *)iodev; 6063e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao if (!aio->ucm) 6073e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao return NULL; 6083e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao 6093e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao return ucm_get_hotword_models(aio->ucm); 6103e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao} 6113e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao 612d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid/* 613d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid * Alsa helper functions. 614d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid */ 615d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid 6160373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Changstatic struct alsa_output_node *get_active_output(const struct alsa_io *aio) 6170373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang{ 6183a29da7a17bba9820408fb0beadb2c432531dc2bChih-Chung Chang return (struct alsa_output_node *)aio->base.active_node; 6190373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang} 6200373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang 6210373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Changstatic struct alsa_input_node *get_active_input(const struct alsa_io *aio) 6220373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang{ 6233a29da7a17bba9820408fb0beadb2c432531dc2bChih-Chung Chang return (struct alsa_input_node *)aio->base.active_node; 6240373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang} 6250373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang 62604bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 62704bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Gets the curve for the active output node. If the node doesn't have volume 628428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao * curve specified, return the default volume curve of the parent iodev. 629e99b3f402531a36baaba0a315421c8b09af3e5b6Hsin-Yu Chao */ 630d21516ae3065cd2bdc992b43ebcdad7b2b1fd778Yao-Wen Maostatic const struct cras_volume_curve *get_curve_for_output_node( 631d21516ae3065cd2bdc992b43ebcdad7b2b1fd778Yao-Wen Mao const struct alsa_io *aio, 632e99b3f402531a36baaba0a315421c8b09af3e5b6Hsin-Yu Chao const struct alsa_output_node *node) 633d21516ae3065cd2bdc992b43ebcdad7b2b1fd778Yao-Wen Mao{ 634428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao if (node && node->volume_curve) 635428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao return node->volume_curve; 636428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao return aio->default_volume_curve; 637d21516ae3065cd2bdc992b43ebcdad7b2b1fd778Yao-Wen Mao} 638d21516ae3065cd2bdc992b43ebcdad7b2b1fd778Yao-Wen Mao 63904bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 64004bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Gets the curve for the active output. 64104bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 642d1032bd00399995773f0ab2802b4c68e24520bb6Dylan Reidstatic const struct cras_volume_curve *get_curve_for_active_output( 643d1032bd00399995773f0ab2802b4c68e24520bb6Dylan Reid const struct alsa_io *aio) 644d1032bd00399995773f0ab2802b4c68e24520bb6Dylan Reid{ 645e99b3f402531a36baaba0a315421c8b09af3e5b6Hsin-Yu Chao struct alsa_output_node *node = get_active_output(aio); 646e99b3f402531a36baaba0a315421c8b09af3e5b6Hsin-Yu Chao return get_curve_for_output_node(aio, node); 647d1032bd00399995773f0ab2802b4c68e24520bb6Dylan Reid} 648d1032bd00399995773f0ab2802b4c68e24520bb6Dylan Reid 64904bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 65004bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Informs the system of the volume limits for this device. 65104bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 652c11029cea8aa5ffcfa0591016f22ff8e0bf77e66Dylan Reidstatic void set_alsa_volume_limits(struct alsa_io *aio) 653c11029cea8aa5ffcfa0591016f22ff8e0bf77e66Dylan Reid{ 654c11029cea8aa5ffcfa0591016f22ff8e0bf77e66Dylan Reid const struct cras_volume_curve *curve; 655ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid 656e2ce9dad713bf0510aac6953f4d80090576fe2c8Dylan Reid /* Only set the limits if the dev is active. */ 65750cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang if (!has_handle(aio)) 658e2ce9dad713bf0510aac6953f4d80090576fe2c8Dylan Reid return; 659e2ce9dad713bf0510aac6953f4d80090576fe2c8Dylan Reid 660ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid curve = get_curve_for_active_output(aio); 661c11029cea8aa5ffcfa0591016f22ff8e0bf77e66Dylan Reid cras_system_set_volume_limits( 662c11029cea8aa5ffcfa0591016f22ff8e0bf77e66Dylan Reid curve->get_dBFS(curve, 1), /* min */ 663c11029cea8aa5ffcfa0591016f22ff8e0bf77e66Dylan Reid curve->get_dBFS(curve, CRAS_MAX_SYSTEM_VOLUME)); 664c11029cea8aa5ffcfa0591016f22ff8e0bf77e66Dylan Reid} 665c11029cea8aa5ffcfa0591016f22ff8e0bf77e66Dylan Reid 66604bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 66704bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Sets the alsa mute control for this iodev. 66804bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 669bba1e29846e375d554bdc2f07e56e8d395000eafCheng-Yi Chiangstatic void set_alsa_mute_control(const struct alsa_io *aio, int muted) 670ab9f6c65f216a5b7fc9ba37da79ddff1b94295a5Dylan Reid{ 6710373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang struct alsa_output_node *aout; 6720373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang 67350cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang if (!has_handle(aio)) 674e2ce9dad713bf0510aac6953f4d80090576fe2c8Dylan Reid return; 675e2ce9dad713bf0510aac6953f4d80090576fe2c8Dylan Reid 6760373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang aout = get_active_output(aio); 677ab9f6c65f216a5b7fc9ba37da79ddff1b94295a5Dylan Reid cras_alsa_mixer_set_mute( 678ab9f6c65f216a5b7fc9ba37da79ddff1b94295a5Dylan Reid aio->mixer, 679ab9f6c65f216a5b7fc9ba37da79ddff1b94295a5Dylan Reid muted, 6800373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang aout ? aout->mixer_output : NULL); 681ab9f6c65f216a5b7fc9ba37da79ddff1b94295a5Dylan Reid} 682ab9f6c65f216a5b7fc9ba37da79ddff1b94295a5Dylan Reid 68304bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 68404bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Sets the volume of the playback device to the specified level. Receives a 685de6d3a6e80401833fabd18dfc81c6be68e227699Dylan Reid * volume index from the system settings, ranging from 0 to 100, converts it to 68604bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * dB using the volume curve, and sends the dB value to alsa. 68704bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 688e87681b09b135f66f70618d00411370d00d5c821Dylan Reidstatic void set_alsa_volume(struct cras_iodev *iodev) 68975467041e85014a2b0c5398f7acec7b1a6f66846Dylan Reid{ 690e87681b09b135f66f70618d00411370d00d5c821Dylan Reid const struct alsa_io *aio = (const struct alsa_io *)iodev; 69178957c7652b9c0061b74b8eedadf83b6e8f8bb88Dylan Reid const struct cras_volume_curve *curve; 692f56514c752e6f1c58421383c4335fe2cfc44e7a2Dylan Reid size_t volume; 6930373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang struct alsa_output_node *aout; 69478957c7652b9c0061b74b8eedadf83b6e8f8bb88Dylan Reid 69575467041e85014a2b0c5398f7acec7b1a6f66846Dylan Reid assert(aio); 69675467041e85014a2b0c5398f7acec7b1a6f66846Dylan Reid if (aio->mixer == NULL) 69775467041e85014a2b0c5398f7acec7b1a6f66846Dylan Reid return; 698f56514c752e6f1c58421383c4335fe2cfc44e7a2Dylan Reid 699948be308fcd7c04bfdb7c154dd6ef90e5fc61070Dylan Reid /* Only set the volume if the dev is active. */ 70050cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang if (!has_handle(aio)) 701948be308fcd7c04bfdb7c154dd6ef90e5fc61070Dylan Reid return; 702948be308fcd7c04bfdb7c154dd6ef90e5fc61070Dylan Reid 703f56514c752e6f1c58421383c4335fe2cfc44e7a2Dylan Reid volume = cras_system_get_volume(); 704ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid curve = get_curve_for_active_output(aio); 705ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid if (curve == NULL) 706ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid return; 7070373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang aout = get_active_output(aio); 708b7fb7aad1bb54683361c3beda234e01bcf455becDylan Reid if (aout) 709b7fb7aad1bb54683361c3beda234e01bcf455becDylan Reid volume = cras_iodev_adjust_node_volume(&aout->base, volume); 7103a4b0c6b781391445efa20d59862d752ebdc8c75Chinyue Chen 7113a4b0c6b781391445efa20d59862d752ebdc8c75Chinyue Chen /* Samples get scaled for devices using software volume, set alsa 7123a4b0c6b781391445efa20d59862d752ebdc8c75Chinyue Chen * volume to 100. */ 7133a4b0c6b781391445efa20d59862d752ebdc8c75Chinyue Chen if (cras_iodev_software_volume_needed(iodev)) 7143a4b0c6b781391445efa20d59862d752ebdc8c75Chinyue Chen volume = 100; 7153a4b0c6b781391445efa20d59862d752ebdc8c75Chinyue Chen 71668f9ca562ea566455f4effdfdd806fbca5012aa5Dylan Reid cras_alsa_mixer_set_dBFS( 71768f9ca562ea566455f4effdfdd806fbca5012aa5Dylan Reid aio->mixer, 71868f9ca562ea566455f4effdfdd806fbca5012aa5Dylan Reid curve->get_dBFS(curve, volume), 7190373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang aout ? aout->mixer_output : NULL); 720bba1e29846e375d554bdc2f07e56e8d395000eafCheng-Yi Chiang} 721bba1e29846e375d554bdc2f07e56e8d395000eafCheng-Yi Chiang 722bba1e29846e375d554bdc2f07e56e8d395000eafCheng-Yi Chiangstatic void set_alsa_mute(struct cras_iodev *iodev) 723bba1e29846e375d554bdc2f07e56e8d395000eafCheng-Yi Chiang{ 724de6d3a6e80401833fabd18dfc81c6be68e227699Dylan Reid /* Mute for zero. */ 725bba1e29846e375d554bdc2f07e56e8d395000eafCheng-Yi Chiang const struct alsa_io *aio = (const struct alsa_io *)iodev; 726bba1e29846e375d554bdc2f07e56e8d395000eafCheng-Yi Chiang set_alsa_mute_control(aio, cras_system_get_mute()); 72775467041e85014a2b0c5398f7acec7b1a6f66846Dylan Reid} 72875467041e85014a2b0c5398f7acec7b1a6f66846Dylan Reid 729e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang/* 730e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang * Sets the capture gain to the current system input gain level, given in dBFS. 731272507e30f5316680e9191c495e3d721abcf4c70Dylan Reid * Set mute based on the system mute state. This gain can be positive or 732e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang * negative and might be adjusted often if an app is running an AGC. 733e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang */ 734e87681b09b135f66f70618d00411370d00d5c821Dylan Reidstatic void set_alsa_capture_gain(struct cras_iodev *iodev) 735272507e30f5316680e9191c495e3d721abcf4c70Dylan Reid{ 736e87681b09b135f66f70618d00411370d00d5c821Dylan Reid const struct alsa_io *aio = (const struct alsa_io *)iodev; 7370373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang struct alsa_input_node *ain; 738b7fb7aad1bb54683361c3beda234e01bcf455becDylan Reid long gain; 739272507e30f5316680e9191c495e3d721abcf4c70Dylan Reid 740272507e30f5316680e9191c495e3d721abcf4c70Dylan Reid assert(aio); 741272507e30f5316680e9191c495e3d721abcf4c70Dylan Reid if (aio->mixer == NULL) 742272507e30f5316680e9191c495e3d721abcf4c70Dylan Reid return; 743272507e30f5316680e9191c495e3d721abcf4c70Dylan Reid 744948be308fcd7c04bfdb7c154dd6ef90e5fc61070Dylan Reid /* Only set the volume if the dev is active. */ 74550cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang if (!has_handle(aio)) 746948be308fcd7c04bfdb7c154dd6ef90e5fc61070Dylan Reid return; 747e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang gain = cras_iodev_adjust_active_node_gain( 748e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang iodev, cras_system_get_capture_gain()); 749948be308fcd7c04bfdb7c154dd6ef90e5fc61070Dylan Reid 750194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang /* Set hardware gain to 0dB if software gain is needed. */ 751194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang if (cras_iodev_software_volume_needed(iodev)) 752194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang gain = 0; 753e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang 754e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang ain = get_active_input(aio); 755e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang 756dbeb4c2db2f454fa3599c29b8625a88b15441285Hsin-Yu Chao cras_alsa_mixer_set_capture_dBFS( 757dbeb4c2db2f454fa3599c29b8625a88b15441285Hsin-Yu Chao aio->mixer, 758b7fb7aad1bb54683361c3beda234e01bcf455becDylan Reid gain, 7590373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang ain ? ain->mixer_input : NULL); 760eb8b306c6696d72789f9a070ffb0a978601bb5e7Dylan Reid cras_alsa_mixer_set_capture_mute(aio->mixer, 7614eeb5b01f93e69e2d2deeca21a1b6ef42d32bfafYao-Wen Mao cras_system_get_capture_mute(), 7624eeb5b01f93e69e2d2deeca21a1b6ef42d32bfafYao-Wen Mao ain ? ain->mixer_input : NULL); 763272507e30f5316680e9191c495e3d721abcf4c70Dylan Reid} 764272507e30f5316680e9191c495e3d721abcf4c70Dylan Reid 76504bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 76604bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Swaps the left and right channels of the given node. 76704bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 768891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiangstatic int set_alsa_node_swapped(struct cras_iodev *iodev, 769891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiang struct cras_ionode *node, int enable) 770891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiang{ 771891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiang const struct alsa_io *aio = (const struct alsa_io *)iodev; 772891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiang assert(aio); 773891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiang return ucm_enable_swap_mode(aio->ucm, node->name, enable); 774891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiang} 775891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiang 776e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang/* 777e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang * Initializes the device settings according to system volume, mute, gain 778e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang * settings. 779e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang * Updates system capture gain limits based on current active device/node. 78075467041e85014a2b0c5398f7acec7b1a6f66846Dylan Reid */ 78175467041e85014a2b0c5398f7acec7b1a6f66846Dylan Reidstatic void init_device_settings(struct alsa_io *aio) 78275467041e85014a2b0c5398f7acec7b1a6f66846Dylan Reid{ 78375467041e85014a2b0c5398f7acec7b1a6f66846Dylan Reid /* Register for volume/mute callback and set initial volume/mute for 78475467041e85014a2b0c5398f7acec7b1a6f66846Dylan Reid * the device. */ 785272507e30f5316680e9191c495e3d721abcf4c70Dylan Reid if (aio->base.direction == CRAS_STREAM_OUTPUT) { 786c11029cea8aa5ffcfa0591016f22ff8e0bf77e66Dylan Reid set_alsa_volume_limits(aio); 787e87681b09b135f66f70618d00411370d00d5c821Dylan Reid set_alsa_volume(&aio->base); 788bba1e29846e375d554bdc2f07e56e8d395000eafCheng-Yi Chiang set_alsa_mute(&aio->base); 789272507e30f5316680e9191c495e3d721abcf4c70Dylan Reid } else { 790d21516ae3065cd2bdc992b43ebcdad7b2b1fd778Yao-Wen Mao struct mixer_control *mixer_input = NULL; 7910373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang struct alsa_input_node *ain = get_active_input(aio); 79204669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang long min_capture_gain, max_capture_gain; 79304669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang 7940373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang if (ain) 7950373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang mixer_input = ain->mixer_input; 79604669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang 79704669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang if (cras_iodev_software_volume_needed(&aio->base)) { 79804669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang min_capture_gain = DEFAULT_MIN_CAPTURE_GAIN; 79904669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang max_capture_gain = cras_iodev_maximum_software_gain( 80004669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang &aio->base); 80104669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang } else { 80204669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang min_capture_gain = 80304669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang cras_alsa_mixer_get_minimum_capture_gain( 80404669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang aio->mixer, mixer_input); 80504669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang max_capture_gain = 80604669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang cras_alsa_mixer_get_maximum_capture_gain( 80704669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang aio->mixer, mixer_input); 80804669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang } 80904669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang cras_system_set_capture_gain_limits(min_capture_gain, 81004669c271496b46a8f397f5cd378f4ebf8ec6d06Cheng-Yi Chiang max_capture_gain); 811e87681b09b135f66f70618d00411370d00d5c821Dylan Reid set_alsa_capture_gain(&aio->base); 812272507e30f5316680e9191c495e3d721abcf4c70Dylan Reid } 81375467041e85014a2b0c5398f7acec7b1a6f66846Dylan Reid} 81475467041e85014a2b0c5398f7acec7b1a6f66846Dylan Reid 81596015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid/* 81696015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid * Functions run in the main server context. 81796015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid */ 81896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 81904bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 82004bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Frees resources used by the alsa iodev. 8214340df47a2aad9d65f597e4f92c56dd5f57cf9d6Dylan Reid * Args: 8224340df47a2aad9d65f597e4f92c56dd5f57cf9d6Dylan Reid * iodev - the iodev to free the resources from. 8234340df47a2aad9d65f597e4f92c56dd5f57cf9d6Dylan Reid */ 8244340df47a2aad9d65f597e4f92c56dd5f57cf9d6Dylan Reidstatic void free_alsa_iodev_resources(struct alsa_io *aio) 8254340df47a2aad9d65f597e4f92c56dd5f57cf9d6Dylan Reid{ 826ea5981fe207266990ec8a119ed13c139b36c1f1dChih-Chung Chang struct cras_ionode *node; 8270373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang struct alsa_output_node *aout; 82817683d28d93572fc1a51336c363e94110e453857Dylan Reid 8294340df47a2aad9d65f597e4f92c56dd5f57cf9d6Dylan Reid free(aio->base.supported_rates); 8304340df47a2aad9d65f597e4f92c56dd5f57cf9d6Dylan Reid free(aio->base.supported_channel_counts); 831a485ac04bdfc71e374a3a5d2fbcf4699999a2939Dylan Reid free(aio->base.supported_formats); 8323a29da7a17bba9820408fb0beadb2c432531dc2bChih-Chung Chang 833ea5981fe207266990ec8a119ed13c139b36c1f1dChih-Chung Chang DL_FOREACH(aio->base.nodes, node) { 8343a29da7a17bba9820408fb0beadb2c432531dc2bChih-Chung Chang if (aio->base.direction == CRAS_STREAM_OUTPUT) { 8353a29da7a17bba9820408fb0beadb2c432531dc2bChih-Chung Chang aout = (struct alsa_output_node *)node; 836428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao cras_volume_curve_destroy(aout->volume_curve); 8373a29da7a17bba9820408fb0beadb2c432531dc2bChih-Chung Chang } 838fff2b998f9ae8463ccf279f85f0aff0e8f0d0d21Chih-Chung Chang cras_iodev_rm_node(&aio->base, node); 8392f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen free(node->softvol_scalers); 8403a29da7a17bba9820408fb0beadb2c432531dc2bChih-Chung Chang free(node); 8412c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao } 8423a29da7a17bba9820408fb0beadb2c432531dc2bChih-Chung Chang 8437057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang free((void *)aio->dsp_name_default); 844a854651b825010352a05f55e720e29bdca6fb1baDylan Reid cras_iodev_free_resources(&aio->base); 84526296d882f4a5387bfecc634b51a891158ce5543Dylan Reid free(aio->dev); 846f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (aio->dev_id) 847f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir free(aio->dev_id); 848f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (aio->dev_name) 849f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir free(aio->dev_name); 8504340df47a2aad9d65f597e4f92c56dd5f57cf9d6Dylan Reid} 8514340df47a2aad9d65f597e4f92c56dd5f57cf9d6Dylan Reid 85204bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 85304bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Returns true if this is the first internal device. 85404bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 855304683287610894db29af32c3d5d841949a32a2cChih-Chung Changstatic int first_internal_device(struct alsa_io *aio) 856304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang{ 857304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang return aio->is_first && aio->card_type == ALSA_CARD_TYPE_INTERNAL; 858304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang} 859304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang 86004bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 86104bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Returns true if there is already a node created with the given name. 86204bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 863304683287610894db29af32c3d5d841949a32a2cChih-Chung Changstatic int has_node(struct alsa_io *aio, const char *name) 864304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang{ 865304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang struct cras_ionode *node; 866304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang 867304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang DL_FOREACH(aio->base.nodes, node) 868304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang if (!strcmp(node->name, name)) 869304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang return 1; 870304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang 871304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang return 0; 872304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang} 873304683287610894db29af32c3d5d841949a32a2cChih-Chung Chang 87404bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 87504bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Returns true if string s ends with the given suffix. 87604bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 877d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Changint endswith(const char *s, const char *suffix) 878d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang{ 879d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang size_t n = strlen(s); 880d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang size_t m = strlen(suffix); 881d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang return n >= m && !strcmp(s + (n - m), suffix); 882d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang} 883d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang 88404bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 88504bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Drop the node name and replace it with node type. 88604bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 887363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiangstatic void drop_node_name(struct cras_ionode *node) 888363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang{ 889363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang if (node->type == CRAS_NODE_TYPE_USB) 890363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang strcpy(node->name, USB); 891363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang else if (node->type == CRAS_NODE_TYPE_HDMI) 892363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang strcpy(node->name, HDMI); 893363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang else { 894363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang /* Only HDMI or USB node might have invalid name to drop */ 895363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang syslog(LOG_ERR, "Unexpectedly drop node name for " 896363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang "node: %s, type: %d", node->name, node->type); 897363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang strcpy(node->name, DEFAULT); 898363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang } 899363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang} 900363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang 90104bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 90204bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Sets the initial plugged state and type of a node based on its 90322443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao * name. Chrome will assign priority to nodes base on node type. 904d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang */ 905d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Changstatic void set_node_initial_state(struct cras_ionode *node, 906d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang enum CRAS_ALSA_CARD_TYPE card_type) 90771ae68539370e7d9004b70cc9e39522fa4eb12c9Dylan Reid{ 908bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao 90971ae68539370e7d9004b70cc9e39522fa4eb12c9Dylan Reid unsigned i; 91071ae68539370e7d9004b70cc9e39522fa4eb12c9Dylan Reid 911b7fb7aad1bb54683361c3beda234e01bcf455becDylan Reid node->volume = 100; 91255a711534f451e769f32e8f4f0c8af1f2e8169b3Chih-Chung Chang node->type = CRAS_NODE_TYPE_UNKNOWN; 913d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang /* Go through the known names */ 91422443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao for (i = 0; i < ARRAY_SIZE(node_defaults); i++) 91522443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao if (!strncmp(node->name, node_defaults[i].name, 91622443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao strlen(node_defaults[i].name))) { 917bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao node->position = node_defaults[i].position; 918bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao node->plugged = (node->position 919bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao != NODE_POSITION_EXTERNAL); 92022443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao node->type = node_defaults[i].type; 921d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang if (node->plugged) 922d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang gettimeofday(&node->plugged_time, NULL); 92371ae68539370e7d9004b70cc9e39522fa4eb12c9Dylan Reid break; 92471ae68539370e7d9004b70cc9e39522fa4eb12c9Dylan Reid } 925d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang 926d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang /* If we didn't find a matching name above, but the node is a jack node, 92722443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao * set its type to headphone/mic. This matches node names like "DAISY-I2S Mic 928a058aa6a7e0dd2e93ffd94c0ae34c51e2592139fCheng-Yi Chiang * Jack". 929a058aa6a7e0dd2e93ffd94c0ae34c51e2592139fCheng-Yi Chiang * If HDMI is in the node name, set its type to HDMI. This matches node names 930a058aa6a7e0dd2e93ffd94c0ae34c51e2592139fCheng-Yi Chiang * like "Rockchip HDMI Jack". 931a058aa6a7e0dd2e93ffd94c0ae34c51e2592139fCheng-Yi Chiang */ 93222443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao if (i == ARRAY_SIZE(node_defaults)) { 93355a711534f451e769f32e8f4f0c8af1f2e8169b3Chih-Chung Chang if (endswith(node->name, "Jack")) { 93455a711534f451e769f32e8f4f0c8af1f2e8169b3Chih-Chung Chang if (node->dev->direction == CRAS_STREAM_OUTPUT) 93555a711534f451e769f32e8f4f0c8af1f2e8169b3Chih-Chung Chang node->type = CRAS_NODE_TYPE_HEADPHONE; 93655a711534f451e769f32e8f4f0c8af1f2e8169b3Chih-Chung Chang else 93755a711534f451e769f32e8f4f0c8af1f2e8169b3Chih-Chung Chang node->type = CRAS_NODE_TYPE_MIC; 93855a711534f451e769f32e8f4f0c8af1f2e8169b3Chih-Chung Chang } 9399fbbb0f0e4a59c331aeca2fccfd6a85938cd7d00Hsin-Yu Chao if (strstr(node->name, HDMI) && 940a058aa6a7e0dd2e93ffd94c0ae34c51e2592139fCheng-Yi Chiang node->dev->direction == CRAS_STREAM_OUTPUT) 941a058aa6a7e0dd2e93ffd94c0ae34c51e2592139fCheng-Yi Chiang node->type = CRAS_NODE_TYPE_HDMI; 942d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang } 943d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang 944d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang /* Regardless of the node name of a USB headset (it can be "Speaker"), 94522443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao * set it's type to usb. 94622443156a7c1fd2eb3e9053c5465cfafe0c36605Hsin-Yu Chao */ 947bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao if (card_type == ALSA_CARD_TYPE_USB) { 94855a711534f451e769f32e8f4f0c8af1f2e8169b3Chih-Chung Chang node->type = CRAS_NODE_TYPE_USB; 949bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao node->position = NODE_POSITION_EXTERNAL; 950bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao } 951363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang 952363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang if (!is_utf8_string(node->name)) 953363253560bbd4d261e03bc63b4281965d51dc0d2Cheng-Yi Chiang drop_node_name(node); 95471ae68539370e7d9004b70cc9e39522fa4eb12c9Dylan Reid} 95571ae68539370e7d9004b70cc9e39522fa4eb12c9Dylan Reid 95673d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chaostatic int get_ucm_flag_integer(struct alsa_io *aio, 95773d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao const char *flag_name, 95873d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao int *result) 959167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao{ 960167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao char *value; 961167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao int i; 962167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao 963167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao if (!aio->ucm) 96473d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao return -1; 965167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao 966167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao value = ucm_get_flag(aio->ucm, flag_name); 967167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao if (!value) 96873d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao return -1; 969167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao 970167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao i = atoi(value); 971167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao free(value); 97273d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao *result = i; 97373d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao return 0; 974167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao} 975167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao 976167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chaostatic int auto_unplug_input_node(struct alsa_io *aio) 977167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao{ 97873d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao int result; 97973d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao if (get_ucm_flag_integer(aio, "AutoUnplugInputNode", &result)) 98073d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao return 0; 98173d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao return result; 982167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao} 983167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao 984167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chaostatic int auto_unplug_output_node(struct alsa_io *aio) 985167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao{ 98673d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao int result; 98773d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao if (get_ucm_flag_integer(aio, "AutoUnplugOutputNode", &result)) 98873d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao return 0; 98973d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao return result; 990167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao} 991167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao 9922890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chaostatic int no_create_default_input_node(struct alsa_io *aio) 9932890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chao{ 99473d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao int result; 99573d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao if (get_ucm_flag_integer(aio, "NoCreateDefaultInputNode", &result)) 99673d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao return 0; 99773d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao return result; 9982890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chao} 9992890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chao 10002890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chaostatic int no_create_default_output_node(struct alsa_io *aio) 10012890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chao{ 100273d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao int result; 100373d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao if (get_ucm_flag_integer(aio, "NoCreateDefaultOutputNode", &result)) 100473d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao return 0; 100573d8ebc892d8d7faa2f2019b7007e2b6487ef30eHsin-Yu Chao return result; 10062890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chao} 10072890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chao 1008135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiangstatic void set_output_node_software_volume_needed( 10096cd908d9a963703726f1b31e1fef5e8c9231c6e6Cheng-Yi Chiang struct alsa_output_node *output, struct alsa_io *aio) 1010135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang{ 1011135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang 10126cd908d9a963703726f1b31e1fef5e8c9231c6e6Cheng-Yi Chiang struct cras_alsa_mixer *mixer = aio->mixer; 1013135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang long range = 0; 1014135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang 10156cd908d9a963703726f1b31e1fef5e8c9231c6e6Cheng-Yi Chiang if (aio->ucm && ucm_get_disable_software_volume(aio->ucm)) { 10166cd908d9a963703726f1b31e1fef5e8c9231c6e6Cheng-Yi Chiang output->base.software_volume_needed = 0; 10176cd908d9a963703726f1b31e1fef5e8c9231c6e6Cheng-Yi Chiang syslog(LOG_DEBUG, "Disable software volume for %s from ucm.", 10186cd908d9a963703726f1b31e1fef5e8c9231c6e6Cheng-Yi Chiang output->base.name); 10196cd908d9a963703726f1b31e1fef5e8c9231c6e6Cheng-Yi Chiang return; 10206cd908d9a963703726f1b31e1fef5e8c9231c6e6Cheng-Yi Chiang } 10216cd908d9a963703726f1b31e1fef5e8c9231c6e6Cheng-Yi Chiang 1022ffc4231f0ba8f41a259fc3ca3faab15450aee8feHsin-Yu Chao /* Use software volume for HDMI output and nodes without volume mixer 1023ffc4231f0ba8f41a259fc3ca3faab15450aee8feHsin-Yu Chao * control. */ 1024838a7a516f8efafd2a05c9d3730a79ab646b6e1bChinyue Chen if ((output->base.type == CRAS_NODE_TYPE_HDMI) || 1025838a7a516f8efafd2a05c9d3730a79ab646b6e1bChinyue Chen (!cras_alsa_mixer_has_main_volume(mixer) && 10268b25199aaa7dd1caaff9a863df64347ec6b73bf4John Muir !cras_alsa_mixer_has_volume(output->mixer_output))) 1027135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang output->base.software_volume_needed = 1; 1028135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang 1029135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang /* Use software volume if the usb device's volume range is smaller 1030135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang * than 40dB */ 1031135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang if (output->base.type == CRAS_NODE_TYPE_USB) { 1032135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang range += cras_alsa_mixer_get_dB_range(mixer); 1033135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang range += cras_alsa_mixer_get_output_dB_range( 1034135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang output->mixer_output); 1035135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang if (range < 4000) 1036135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang output->base.software_volume_needed = 1; 1037135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang } 1038135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang if (output->base.software_volume_needed) 1039135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang syslog(LOG_DEBUG, "Use software volume for node: %s", 1040135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang output->base.name); 1041135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang} 1042135414b4158b01c24145e5f83042f7d8a0205463Cheng-Yi Chiang 1043194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiangstatic void set_input_node_software_volume_needed( 1044194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang struct alsa_input_node *input, struct alsa_io *aio) 1045194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang{ 1046194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang long max_software_gain; 1047194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang int rc; 1048194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang 1049194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang input->base.software_volume_needed = 0; 1050194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang input->base.max_software_gain = 0; 1051194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang 1052194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang /* Enable software gain only if max software gain is specified in UCM.*/ 1053194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang if (!aio->ucm) 1054194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang return; 1055194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang 1056194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang rc = ucm_get_max_software_gain(aio->ucm, input->base.name, 1057194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang &max_software_gain); 1058194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang if (rc) 1059194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang return; 1060194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang 1061194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang input->base.software_volume_needed = 1; 1062194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang input->base.max_software_gain = max_software_gain; 1063194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang syslog(LOG_INFO, 1064194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang "Use software gain for %s with max %ld because it is specified" 1065194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang " in UCM", input->base.name, max_software_gain); 1066194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang} 1067194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang 1068e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiangstatic void set_input_default_node_gain(struct alsa_input_node *input, 1069e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang struct alsa_io *aio) 1070e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang{ 1071e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang long default_node_gain; 1072e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang int rc; 1073e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang 1074e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang if (!aio->ucm) 1075e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang return; 1076e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang 1077e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang rc = ucm_get_default_node_gain(aio->ucm, input->base.name, 1078e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang &default_node_gain); 1079e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang if (rc) 1080e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang return; 1081e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang 1082e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang input->base.capture_gain = default_node_gain; 1083e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang} 1084e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang 10856396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chaostatic void check_auto_unplug_output_node(struct alsa_io *aio, 10866396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao struct cras_ionode *node, 10876396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao int plugged) 10886396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao{ 10896396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao struct cras_ionode *tmp; 10906396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao 10916396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao if (!auto_unplug_output_node(aio)) 10926396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao return; 10936396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao 10946396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao /* Auto unplug internal speaker if any output node has been created */ 10956396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao if (!strcmp(node->name, INTERNAL_SPEAKER) && plugged) { 10966396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao DL_FOREACH(aio->base.nodes, tmp) 10976396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao if (tmp->plugged && (tmp != node)) 10986396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao cras_iodev_set_node_attr(node, 10996396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao IONODE_ATTR_PLUGGED, 11006396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao 0); 11016396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao } else { 11026396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao DL_FOREACH(aio->base.nodes, tmp) { 11036396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao if (!strcmp(tmp->name, INTERNAL_SPEAKER)) 11046396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao cras_iodev_set_node_attr(tmp, 11056396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao IONODE_ATTR_PLUGGED, 11066396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao !plugged); 11076396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao } 11086396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao } 11096396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao} 11106396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao 111104bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 111204bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Callback for listing mixer outputs. The mixer will call this once for each 111304bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * output associated with this device. Most commonly this is used to tell the 111404bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * device it has Headphones and Speakers. 111504bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 11162fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chaostatic struct alsa_output_node *new_output(struct alsa_io *aio, 11172fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao struct mixer_control *cras_output, 11182fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao const char *name) 111917683d28d93572fc1a51336c363e94110e453857Dylan Reid{ 112017683d28d93572fc1a51336c363e94110e453857Dylan Reid struct alsa_output_node *output; 1121443104660e0f4ea7cec475f7e6a7b198cbf96d8aJohn Muir syslog(LOG_DEBUG, "New output node for '%s'", name); 112217683d28d93572fc1a51336c363e94110e453857Dylan Reid if (aio == NULL) { 112317683d28d93572fc1a51336c363e94110e453857Dylan Reid syslog(LOG_ERR, "Invalid aio when listing outputs."); 11242fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao return NULL; 112517683d28d93572fc1a51336c363e94110e453857Dylan Reid } 112617683d28d93572fc1a51336c363e94110e453857Dylan Reid output = (struct alsa_output_node *)calloc(1, sizeof(*output)); 112717683d28d93572fc1a51336c363e94110e453857Dylan Reid if (output == NULL) { 112817683d28d93572fc1a51336c363e94110e453857Dylan Reid syslog(LOG_ERR, "Out of memory when listing outputs."); 11292fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao return NULL; 113017683d28d93572fc1a51336c363e94110e453857Dylan Reid } 1131a4de63604f25c665ee78b87f23dbff456658e8d1Chih-Chung Chang output->base.dev = &aio->base; 11326c9585dab2ebdcfb6dc557f6c7e9d6b5845a2cd7Chih-Chung Chang output->base.idx = aio->next_ionode_index++; 113310fe628c1fb07d521e21ed5f45ab75fe505b062aHsin-Yu Chao output->base.stable_id = SuperFastHash(name, 113481e7418b513f391d60019dd3a21ceebe9ca1a81cHsin-Yu Chao strlen(name), 113510fe628c1fb07d521e21ed5f45ab75fe505b062aHsin-Yu Chao aio->base.info.stable_id); 1136c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu output->base.stable_id_new = SuperFastHash(name, 1137c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu strlen(name), 1138c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu aio->base.info.stable_id_new 1139c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu ); 114017683d28d93572fc1a51336c363e94110e453857Dylan Reid output->mixer_output = cras_output; 1141428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao 1142428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao /* Volume curve. */ 1143428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao output->volume_curve = cras_card_config_get_volume_curve_for_control( 1144428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao aio->config, 1145428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao name ? name 1146428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao : cras_alsa_mixer_get_control_name(cras_output)); 1147428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao 1148a8f7447573cc84811484aaf38e576dbab8c35c6fChih-Chung Chang strncpy(output->base.name, name, sizeof(output->base.name) - 1); 1149d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang set_node_initial_state(&output->base, aio->card_type); 11506cd908d9a963703726f1b31e1fef5e8c9231c6e6Cheng-Yi Chiang set_output_node_software_volume_needed(output, aio); 1151eb9ebc4927f77b187106831e49181e7476b2f674Hsin-Yu Chao 11526396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao cras_iodev_add_node(&aio->base, &output->base); 11536396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao 11546396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao check_auto_unplug_output_node(aio, &output->base, output->base.plugged); 11552fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao return output; 11562fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao} 11572fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao 11582fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chaostatic void new_output_by_mixer_control(struct mixer_control *cras_output, 11592fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao void *callback_arg) 11602fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao{ 11612fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao struct alsa_io *aio = (struct alsa_io *)callback_arg; 1162c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao char node_name[CRAS_IODEV_NAME_BUFFER_SIZE]; 1163bf43e4e3ff263f0b435511c42a864b04bf8f8054Cheng-Yi Chiang const char *ctl_name; 1164bf43e4e3ff263f0b435511c42a864b04bf8f8054Cheng-Yi Chiang 1165bf43e4e3ff263f0b435511c42a864b04bf8f8054Cheng-Yi Chiang ctl_name = cras_alsa_mixer_get_control_name(cras_output); 11668b25199aaa7dd1caaff9a863df64347ec6b73bf4John Muir if (!ctl_name) 11678b25199aaa7dd1caaff9a863df64347ec6b73bf4John Muir return; 11682fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao 1169c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao if (aio->card_type == ALSA_CARD_TYPE_USB) { 1170c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao snprintf(node_name, sizeof(node_name), "%s: %s", 1171c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao aio->base.info.name, ctl_name); 1172c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao new_output(aio, cras_output, node_name); 1173c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao } else { 1174c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao new_output(aio, cras_output, ctl_name); 1175c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao } 11766396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao} 11776396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao 11786396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chaostatic void check_auto_unplug_input_node(struct alsa_io *aio, 11796396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao struct cras_ionode *node, 11806396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao int plugged) 11816396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao{ 11826396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao struct cras_ionode *tmp; 11836396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao if (!auto_unplug_input_node(aio)) 11846396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao return; 11856396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao 11866396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao /* Auto unplug internal mic if any input node has already 11876396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao * been created */ 11886396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao if (!strcmp(node->name, INTERNAL_MICROPHONE) && plugged) { 1189167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao DL_FOREACH(aio->base.nodes, tmp) 11906396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao if (tmp->plugged && (tmp != node)) 11916396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao cras_iodev_set_node_attr(node, 1192a63ce3d1e707f4b08ce4c800bbad1024103fe8bfHsin-Yu Chao IONODE_ATTR_PLUGGED, 1193a63ce3d1e707f4b08ce4c800bbad1024103fe8bfHsin-Yu Chao 0); 11946396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao } else { 11956396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao DL_FOREACH(aio->base.nodes, tmp) 11966396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao if (!strcmp(tmp->name, INTERNAL_MICROPHONE)) 11976396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao cras_iodev_set_node_attr(tmp, 11986396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao IONODE_ATTR_PLUGGED, 11996396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao !plugged); 1200167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao } 1201eb9ebc4927f77b187106831e49181e7476b2f674Hsin-Yu Chao} 1202eb9ebc4927f77b187106831e49181e7476b2f674Hsin-Yu Chao 12032fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chaostatic struct alsa_input_node *new_input(struct alsa_io *aio, 12042fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao struct mixer_control *cras_input, const char *name) 120532a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang{ 120632a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang struct alsa_input_node *input; 12077ae7661f076f7da5b4c9aa8c3a8d54e8508414c7Hsin-Yu Chao char *mic_positions; 1208d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao int err; 120932a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang 121032a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang input = (struct alsa_input_node *)calloc(1, sizeof(*input)); 121132a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang if (input == NULL) { 121232a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang syslog(LOG_ERR, "Out of memory when listing inputs."); 12132fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao return NULL; 121432a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang } 1215a4de63604f25c665ee78b87f23dbff456658e8d1Chih-Chung Chang input->base.dev = &aio->base; 121632a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang input->base.idx = aio->next_ionode_index++; 121710fe628c1fb07d521e21ed5f45ab75fe505b062aHsin-Yu Chao input->base.stable_id = SuperFastHash(name, 121881e7418b513f391d60019dd3a21ceebe9ca1a81cHsin-Yu Chao strlen(name), 121910fe628c1fb07d521e21ed5f45ab75fe505b062aHsin-Yu Chao aio->base.info.stable_id); 1220c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu input->base.stable_id_new = SuperFastHash(name, 1221c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu strlen(name), 1222c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu aio->base.info.stable_id_new); 12238011c4875739dfb86a9a26c12b82e29657ceb47cYao-Wen Mao input->mixer_input = cras_input; 122432a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang strncpy(input->base.name, name, sizeof(input->base.name) - 1); 1225d0e1151d5abb0a4749436b24c494cfcf9391e838Chih-Chung Chang set_node_initial_state(&input->base, aio->card_type); 1226194f243f86bc9147967f65a2fa550d506df7930bCheng-Yi Chiang set_input_node_software_volume_needed(input, aio); 1227e26a4a19c1e4d8e6b3f285eb6ea9233635a01093Cheng-Yi Chiang set_input_default_node_gain(input, aio); 1228eb9ebc4927f77b187106831e49181e7476b2f674Hsin-Yu Chao 1229d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao if (aio->ucm) { 1230d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao /* Check mic positions only for internal mic. */ 1231bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao if ((input->base.type == CRAS_NODE_TYPE_MIC) && 1232bf1f4c514711b3b33f82006f8937c61039a07c71Hsin-Yu Chao (input->base.position == NODE_POSITION_INTERNAL)) { 1233d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao mic_positions = ucm_get_mic_positions(aio->ucm); 1234d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao if (mic_positions) { 1235d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao strncpy(input->base.mic_positions, 1236d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao mic_positions, 1237d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao sizeof(input->base.mic_positions) - 1); 1238d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao free(mic_positions); 1239d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao } 1240d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao } 1241d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao 1242d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao /* Check if channel map is specified in UCM. */ 1243d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao input->channel_layout = (int8_t *)malloc( 1244d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao CRAS_CH_MAX * sizeof(*input->channel_layout)); 1245d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao err = ucm_get_capture_chmap_for_dev(aio->ucm, name, 1246d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao input->channel_layout); 1247d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao if (err) { 1248d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao free(input->channel_layout); 1249d31366a22f5fa5a623b40f328f3fa0f91cd2ab4aHsin-Yu Chao input->channel_layout = 0; 12507ae7661f076f7da5b4c9aa8c3a8d54e8508414c7Hsin-Yu Chao } 12517ae7661f076f7da5b4c9aa8c3a8d54e8508414c7Hsin-Yu Chao } 12527ae7661f076f7da5b4c9aa8c3a8d54e8508414c7Hsin-Yu Chao 1253fff2b998f9ae8463ccf279f85f0aff0e8f0d0d21Chih-Chung Chang cras_iodev_add_node(&aio->base, &input->base); 12546396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao check_auto_unplug_input_node(aio, &input->base, 12556396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao input->base.plugged); 12562fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao return input; 125732a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang} 125832a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang 12592fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chaostatic void new_input_by_mixer_control(struct mixer_control *cras_input, 12602fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao void *callback_arg) 12618011c4875739dfb86a9a26c12b82e29657ceb47cYao-Wen Mao{ 12622fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao struct alsa_io *aio = (struct alsa_io *)callback_arg; 1263c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao char node_name[CRAS_IODEV_NAME_BUFFER_SIZE]; 1264c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao const char *ctl_name = cras_alsa_mixer_get_control_name(cras_input); 12658011c4875739dfb86a9a26c12b82e29657ceb47cYao-Wen Mao 1266c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao if (aio->card_type == ALSA_CARD_TYPE_USB) { 1267c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao snprintf(node_name , sizeof(node_name), "%s: %s", 1268c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao aio->base.info.name, ctl_name); 1269c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao new_input(aio, cras_input, node_name); 1270c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao } else { 1271c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao new_input(aio, cras_input, ctl_name); 1272c14268822a6ddf28247bddffda383cbb15f052bbHsin-Yu Chao } 12738011c4875739dfb86a9a26c12b82e29657ceb47cYao-Wen Mao} 12748011c4875739dfb86a9a26c12b82e29657ceb47cYao-Wen Mao 127504bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 127604bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Finds the output node associated with the jack. Returns NULL if not found. 127704bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 12785efd578fe4637bab0f766e736d5a6a8baa0accb7Dylan Reidstatic struct alsa_output_node *get_output_node_from_jack( 12795efd578fe4637bab0f766e736d5a6a8baa0accb7Dylan Reid struct alsa_io *aio, const struct cras_alsa_jack *jack) 12805efd578fe4637bab0f766e736d5a6a8baa0accb7Dylan Reid{ 1281d21516ae3065cd2bdc992b43ebcdad7b2b1fd778Yao-Wen Mao struct mixer_control *mixer_output; 12820373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang struct cras_ionode *node = NULL; 12830373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang struct alsa_output_node *aout = NULL; 12845efd578fe4637bab0f766e736d5a6a8baa0accb7Dylan Reid 1285e68f4243cd7be12e418b4e456398a969f9dc254cHsin-Yu Chao /* Search by jack first. */ 1286e68f4243cd7be12e418b4e456398a969f9dc254cHsin-Yu Chao DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, aout, 1287e68f4243cd7be12e418b4e456398a969f9dc254cHsin-Yu Chao jack, jack); 1288e68f4243cd7be12e418b4e456398a969f9dc254cHsin-Yu Chao if (aout) 12890373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang return aout; 1290e68f4243cd7be12e418b4e456398a969f9dc254cHsin-Yu Chao 1291e68f4243cd7be12e418b4e456398a969f9dc254cHsin-Yu Chao /* Search by mixer control next. */ 1292e68f4243cd7be12e418b4e456398a969f9dc254cHsin-Yu Chao mixer_output = cras_alsa_jack_get_mixer_output(jack); 1293e68f4243cd7be12e418b4e456398a969f9dc254cHsin-Yu Chao if (mixer_output == NULL) 1294e68f4243cd7be12e418b4e456398a969f9dc254cHsin-Yu Chao return NULL; 12955efd578fe4637bab0f766e736d5a6a8baa0accb7Dylan Reid 12963a29da7a17bba9820408fb0beadb2c432531dc2bChih-Chung Chang DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, aout, 12970373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang mixer_output, mixer_output); 12980373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang return aout; 12995efd578fe4637bab0f766e736d5a6a8baa0accb7Dylan Reid} 13005efd578fe4637bab0f766e736d5a6a8baa0accb7Dylan Reid 13012c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chaostatic struct alsa_input_node *get_input_node_from_jack( 13022c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao struct alsa_io *aio, const struct cras_alsa_jack *jack) 13032c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao{ 1304d21516ae3065cd2bdc992b43ebcdad7b2b1fd778Yao-Wen Mao struct mixer_control *mixer_input; 13050373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang struct cras_ionode *node = NULL; 13060373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang struct alsa_input_node *ain = NULL; 13072c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao 13080373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang mixer_input = cras_alsa_jack_get_mixer_input(jack); 13092c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao if (mixer_input == NULL) { 13103a29da7a17bba9820408fb0beadb2c432531dc2bChih-Chung Chang DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, ain, 13110373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang jack, jack); 13120373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang return ain; 13132c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao } 13142c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao 13153a29da7a17bba9820408fb0beadb2c432531dc2bChih-Chung Chang DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, ain, 13160373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang mixer_input, mixer_input); 13170373a6ac2da4fec9562b26bcbb8ff04a96ee8f83Chih-Chung Chang return ain; 13182c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao} 13192c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao 132004bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 132104bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Returns the dsp name specified in the ucm config. If there is a dsp 13227057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang * name specified for the jack of the active node, use that. Otherwise 132304bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * use the default dsp name for the alsa_io device. 132404bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 13257057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Changstatic const char *get_active_dsp_name(struct alsa_io *aio) 13267057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang{ 13277057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang struct cras_ionode *node = aio->base.active_node; 13287057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang const struct cras_alsa_jack *jack; 13297057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang 13307057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang if (node == NULL) 13317057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang return NULL; 13327057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang 13337057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang if (aio->base.direction == CRAS_STREAM_OUTPUT) 13347057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang jack = ((struct alsa_output_node *) node)->jack; 13357057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang else 13367057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang jack = ((struct alsa_input_node *) node)->jack; 13377057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang 13387057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang return cras_alsa_jack_get_dsp_name(jack) ? : aio->dsp_name_default; 13397057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang} 13407057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang 134104bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 134204bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Creates volume curve for the node associated with given jack. 134304bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 13449fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chaostatic struct cras_volume_curve *create_volume_curve_for_jack( 1345428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao const struct cras_card_config *config, 13469fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao const struct cras_alsa_jack *jack) 13479fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao{ 13489fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao struct cras_volume_curve *curve; 13499fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao const char *name; 13509fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao 13519fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao /* Use jack's UCM device name as key to get volume curve. */ 13529fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao name = cras_alsa_jack_get_ucm_device(jack); 1353428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao curve = cras_card_config_get_volume_curve_for_control(config, name); 13549fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao if (curve) 13559fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao return curve; 13569fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao 13579fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao /* Use alsa jack's name as key to get volume curve. */ 13589fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao name = cras_alsa_jack_get_name(jack); 1359428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao curve = cras_card_config_get_volume_curve_for_control(config, name); 13609fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao if (curve) 13619fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao return curve; 13629fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao 13639fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao return NULL; 13649fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao} 13659fb7a508313ec89b837ccc66ddb7b24648b6666aHsin-Yu Chao 136604bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 136704bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Callback that is called when an output jack is plugged or unplugged. 136804bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 1369ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reidstatic void jack_output_plug_event(const struct cras_alsa_jack *jack, 1370c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid int plugged, 1371c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid void *arg) 1372c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid{ 1373c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid struct alsa_io *aio; 13744e974662b609dd93bac3b1b307d4fa48b4e02eb2Chih-Chung Chang struct alsa_output_node *node; 1375a8f7447573cc84811484aaf38e576dbab8c35c6fChih-Chung Chang const char *jack_name; 1376c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid 1377c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid if (arg == NULL) 1378c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid return; 1379c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid 1380c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid aio = (struct alsa_io *)arg; 13815efd578fe4637bab0f766e736d5a6a8baa0accb7Dylan Reid node = get_output_node_from_jack(aio, jack); 13822fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao jack_name = cras_alsa_jack_get_name(jack); 13832fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao if (!strcmp(jack_name, "Speaker Phantom Jack")) 13842fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao jack_name = INTERNAL_SPEAKER; 13855efd578fe4637bab0f766e736d5a6a8baa0accb7Dylan Reid 1386ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid /* If there isn't a node for this jack, create one. */ 1387ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid if (node == NULL) { 1388f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (aio->fully_specified) { 1389f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir /* When fully specified, can't have new nodes. */ 1390f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir syslog(LOG_ERR, "No matching output node for jack %s!", 1391f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir jack_name); 1392f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return; 1393f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir } 13942fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao node = new_output(aio, NULL, jack_name); 13952fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao if (node == NULL) 1396ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid return; 13972fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao 1398ecad378608becdec95ba4a4def33597cbe550600Cheng-Yi Chiang cras_alsa_jack_update_node_type(jack, &(node->base.type)); 13992fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao } 14002fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao 14012fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao if (!node->jack) { 1402f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (aio->fully_specified) 1403f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir syslog(LOG_ERR, 1404f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir "Jack '%s' was found to match output node '%s'." 1405f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir " Please fix your UCM configuration to match.", 1406f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir jack_name, node->base.name); 1407f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 1408c0dd0f2263b3d2156cbdf6906547381bc5240126Chih-Chung Chang /* If we already have the node, associate with the jack. */ 1409c0dd0f2263b3d2156cbdf6906547381bc5240126Chih-Chung Chang node->jack = jack; 1410428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao if (node->volume_curve == NULL) 1411428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao node->volume_curve = create_volume_curve_for_jack( 1412428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao aio->config, jack); 1413ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid } 1414ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid 1415f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir syslog(LOG_DEBUG, "%s plugged: %d, %s", jack_name, plugged, 1416f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir cras_alsa_mixer_get_control_name(node->mixer_output)); 1417f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 1418018080f82c65715eecb67b5dd068a95590656beaHsin-Yu Chao cras_alsa_jack_update_monitor_name(jack, node->base.name, 1419018080f82c65715eecb67b5dd068a95590656beaHsin-Yu Chao sizeof(node->base.name)); 14204fb7f8be4bfb2ba27cc0c84238ef13d69e81ea65Cheng-Yi Chiang /* The name got from jack might be an invalid UTF8 string. */ 14214fb7f8be4bfb2ba27cc0c84238ef13d69e81ea65Cheng-Yi Chiang if (!is_utf8_string(node->base.name)) 14224fb7f8be4bfb2ba27cc0c84238ef13d69e81ea65Cheng-Yi Chiang drop_node_name(&node->base); 1423018080f82c65715eecb67b5dd068a95590656beaHsin-Yu Chao 1424786f9d1938966af8e1eb9f60559a3535b5c71ff8Chih-Chung Chang cras_iodev_set_node_attr(&node->base, IONODE_ATTR_PLUGGED, plugged); 1425167b08ad93fa54677e0e7c779f8494417d09de4dHsin-Yu Chao 14266396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao check_auto_unplug_output_node(aio, &node->base, plugged); 1427ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid} 1428ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid 142904bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 143004bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Callback that is called when an input jack is plugged or unplugged. 143104bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 1432ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reidstatic void jack_input_plug_event(const struct cras_alsa_jack *jack, 1433ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid int plugged, 1434ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid void *arg) 1435ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid{ 1436ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid struct alsa_io *aio; 14372c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao struct alsa_input_node *node; 14382fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao struct mixer_control *cras_input; 1439a8f7447573cc84811484aaf38e576dbab8c35c6fChih-Chung Chang const char *jack_name; 1440ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid 1441ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid if (arg == NULL) 1442ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid return; 1443ad85cbe24fa120a49cb6fb11cb7a1b6f61a7192fDylan Reid aio = (struct alsa_io *)arg; 14442c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao node = get_input_node_from_jack(aio, jack); 1445f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir jack_name = cras_alsa_jack_get_name(jack); 1446c0dd0f2263b3d2156cbdf6906547381bc5240126Chih-Chung Chang 1447c0dd0f2263b3d2156cbdf6906547381bc5240126Chih-Chung Chang /* If there isn't a node for this jack, create one. */ 14482c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao if (node == NULL) { 1449f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (aio->fully_specified) { 1450f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir /* When fully specified, can't have new nodes. */ 1451f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir syslog(LOG_ERR, "No matching input node for jack %s!", 1452f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir jack_name); 1453f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return; 1454f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir } 14552fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao cras_input = cras_alsa_jack_get_mixer_input(jack); 14562fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao node = new_input(aio, cras_input, jack_name); 14572fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao if (node == NULL) 14582fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao return; 14592c587cb5f503c0fc0e23dc561385a1d976260bd2Hsin-Yu Chao } 1460ce9ddefe8494e3bb68c0742e53aa70e560c0ca0dDylan Reid 1461f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir syslog(LOG_DEBUG, "%s plugged: %d, %s", jack_name, plugged, 1462f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir cras_alsa_mixer_get_control_name(node->mixer_input)); 1463f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 14642fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao /* If we already have the node, associate with the jack. */ 1465f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (!node->jack) { 1466f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (aio->fully_specified) 1467f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir syslog(LOG_ERR, 1468f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir "Jack '%s' was found to match input node '%s'." 1469f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir " Please fix your UCM configuration to match.", 1470f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir jack_name, node->base.name); 14712fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao node->jack = jack; 1472f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir } 14732fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao 1474786f9d1938966af8e1eb9f60559a3535b5c71ff8Chih-Chung Chang cras_iodev_set_node_attr(&node->base, IONODE_ATTR_PLUGGED, plugged); 1475eb9ebc4927f77b187106831e49181e7476b2f674Hsin-Yu Chao 14766396855d78e601b55bee2262a34c4be5d934fdfcHsin-Yu Chao check_auto_unplug_input_node(aio, &node->base, plugged); 1477c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid} 1478c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid 147904bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 148004bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Sets the name of the given iodev, using the name and index of the card 148104bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * combined with the device index and direction. 148204bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 1483f22c4290b77f144b25cf5725994dfac227da3de2Dylan Reidstatic void set_iodev_name(struct cras_iodev *dev, 1484f22c4290b77f144b25cf5725994dfac227da3de2Dylan Reid const char *card_name, 1485990f4d49d6b64c61aa5a19b167b7132a946ca04cDylan Reid const char *dev_name, 1486f22c4290b77f144b25cf5725994dfac227da3de2Dylan Reid size_t card_index, 148785137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen size_t device_index, 148885137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen enum CRAS_ALSA_CARD_TYPE card_type, 148985137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen size_t usb_vid, 1490c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu size_t usb_pid, 1491c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu char *usb_serial_number) 1492f22c4290b77f144b25cf5725994dfac227da3de2Dylan Reid{ 1493f22c4290b77f144b25cf5725994dfac227da3de2Dylan Reid snprintf(dev->info.name, 1494f22c4290b77f144b25cf5725994dfac227da3de2Dylan Reid sizeof(dev->info.name), 149574b0d7e86d24c5d741635cd5122d600cdc4971b0Hsin-Yu Chao "%s: %s:%zu,%zu", 1496f22c4290b77f144b25cf5725994dfac227da3de2Dylan Reid card_name, 149774b0d7e86d24c5d741635cd5122d600cdc4971b0Hsin-Yu Chao dev_name, 149874b0d7e86d24c5d741635cd5122d600cdc4971b0Hsin-Yu Chao card_index, 149974b0d7e86d24c5d741635cd5122d600cdc4971b0Hsin-Yu Chao device_index); 1500f22c4290b77f144b25cf5725994dfac227da3de2Dylan Reid dev->info.name[ARRAY_SIZE(dev->info.name) - 1] = '\0'; 1501f22c4290b77f144b25cf5725994dfac227da3de2Dylan Reid syslog(LOG_DEBUG, "Add device name=%s", dev->info.name); 15023b7061ffdd69ed0bbcd084aab7b56c62ae35a167Richard Barnette 15033b7061ffdd69ed0bbcd084aab7b56c62ae35a167Richard Barnette dev->info.stable_id = SuperFastHash(card_name, 15043b7061ffdd69ed0bbcd084aab7b56c62ae35a167Richard Barnette strlen(card_name), 15053b7061ffdd69ed0bbcd084aab7b56c62ae35a167Richard Barnette strlen(card_name)); 15063b7061ffdd69ed0bbcd084aab7b56c62ae35a167Richard Barnette dev->info.stable_id = SuperFastHash(dev_name, 15073b7061ffdd69ed0bbcd084aab7b56c62ae35a167Richard Barnette strlen(dev_name), 15083b7061ffdd69ed0bbcd084aab7b56c62ae35a167Richard Barnette dev->info.stable_id); 150985137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen 151085137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen switch (card_type) { 151185137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen case ALSA_CARD_TYPE_INTERNAL: 151285137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen dev->info.stable_id = SuperFastHash((const char *)&device_index, 151385137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen sizeof(device_index), 151485137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen dev->info.stable_id); 1515c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu dev->info.stable_id_new = dev->info.stable_id; 151685137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen break; 151785137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen case ALSA_CARD_TYPE_USB: 151885137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen dev->info.stable_id = SuperFastHash((const char *)&usb_vid, 151985137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen sizeof(usb_vid), 152085137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen dev->info.stable_id); 152185137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen dev->info.stable_id = SuperFastHash((const char *)&usb_pid, 152285137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen sizeof(usb_pid), 152385137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen dev->info.stable_id); 1524c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu dev->info.stable_id_new = 1525c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu SuperFastHash(usb_serial_number, 1526c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu strlen(usb_serial_number), 1527c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu dev->info.stable_id); 1528c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu break; 1529c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu default: 1530c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu dev->info.stable_id_new = dev->info.stable_id; 153185137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen break; 153285137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen } 1533c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu syslog(LOG_DEBUG, "Stable ID=%08x, New Stable ID=%08x", 1534c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu dev->info.stable_id, dev->info.stable_id_new); 1535f22c4290b77f144b25cf5725994dfac227da3de2Dylan Reid} 1536f22c4290b77f144b25cf5725994dfac227da3de2Dylan Reid 15371f2609650665fa825622fe30f8404324a2cb6e60Dylan Reidstatic int get_fixed_rate(struct alsa_io *aio) 15381f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid{ 15391f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid const char *name; 15401f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid 15411f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid if (aio->base.direction == CRAS_STREAM_OUTPUT) { 15421f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid struct alsa_output_node *active = get_active_output(aio); 15431f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid if (!active) 15441f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid return -ENOENT; 15451f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid name = active->base.name; 15461f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid } else { 15471f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid struct alsa_input_node *active = get_active_input(aio); 15481f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid if (!active) 15491f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid return -ENOENT; 15501f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid name = active->base.name; 15511f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid } 15521f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid 15531f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid return ucm_get_sample_rate_for_dev(aio->ucm, name, aio->base.direction); 15541f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid} 15551f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid 155604bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 155704bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Updates the supported sample rates and channel counts. 155804bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 1559c2e8e4c4e28648869db777e7d48bab5c37b419a2Dylan Reidstatic int update_supported_formats(struct cras_iodev *iodev) 1560c2e8e4c4e28648869db777e7d48bab5c37b419a2Dylan Reid{ 1561c2e8e4c4e28648869db777e7d48bab5c37b419a2Dylan Reid struct alsa_io *aio = (struct alsa_io *)iodev; 1562c2e8e4c4e28648869db777e7d48bab5c37b419a2Dylan Reid int err; 15631f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid int fixed_rate; 1564c2e8e4c4e28648869db777e7d48bab5c37b419a2Dylan Reid 1565c2e8e4c4e28648869db777e7d48bab5c37b419a2Dylan Reid free(iodev->supported_rates); 15667fe37e18e2fc4534bb63aa63e00f12a293cdfb51Dylan Reid iodev->supported_rates = NULL; 1567c2e8e4c4e28648869db777e7d48bab5c37b419a2Dylan Reid free(iodev->supported_channel_counts); 15687fe37e18e2fc4534bb63aa63e00f12a293cdfb51Dylan Reid iodev->supported_channel_counts = NULL; 1569a485ac04bdfc71e374a3a5d2fbcf4699999a2939Dylan Reid free(iodev->supported_formats); 1570a485ac04bdfc71e374a3a5d2fbcf4699999a2939Dylan Reid iodev->supported_formats = NULL; 1571c2e8e4c4e28648869db777e7d48bab5c37b419a2Dylan Reid 15722e14c232763ac89fd9d2fa5e9b3fdcf6581b2db8Chinyue Chen err = cras_alsa_fill_properties(aio->dev, aio->alsa_stream, 1573c2e8e4c4e28648869db777e7d48bab5c37b419a2Dylan Reid &iodev->supported_rates, 1574a485ac04bdfc71e374a3a5d2fbcf4699999a2939Dylan Reid &iodev->supported_channel_counts, 1575a485ac04bdfc71e374a3a5d2fbcf4699999a2939Dylan Reid &iodev->supported_formats); 15761f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid if (err) 15771f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid return err; 15781f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid 15791f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid if (aio->ucm) { 15801f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid /* Allow UCM to override supplied rates. */ 15811f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid fixed_rate = get_fixed_rate(aio); 15821f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid if (fixed_rate > 0) { 15831f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid free(iodev->supported_rates); 15841f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid iodev->supported_rates = (size_t*)malloc( 15851f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid 2 * sizeof(iodev->supported_rates[0])); 15861f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid iodev->supported_rates[0] = fixed_rate; 15871f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid iodev->supported_rates[1] = 0; 15881f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid } 15891f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid } 15901f2609650665fa825622fe30f8404324a2cb6e60Dylan Reid return 0; 1591c2e8e4c4e28648869db777e7d48bab5c37b419a2Dylan Reid} 1592c2e8e4c4e28648869db777e7d48bab5c37b419a2Dylan Reid 159304bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 159404bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Builds software volume scalers for output nodes in the device. 159504bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 15962f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chenstatic void build_softvol_scalers(struct alsa_io *aio) 15972f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen{ 15982f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen struct cras_ionode *ionode; 15992f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen 16002f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen DL_FOREACH(aio->base.nodes, ionode) { 16012f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen struct alsa_output_node *aout; 16022f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen const struct cras_volume_curve *curve; 16032f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen 16042f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen aout = (struct alsa_output_node *)ionode; 1605d21516ae3065cd2bdc992b43ebcdad7b2b1fd778Yao-Wen Mao curve = get_curve_for_output_node(aio, aout); 16062f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen 16072f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen ionode->softvol_scalers = softvol_build_from_curve(curve); 16082f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen } 16092f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen} 16102f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen 1611f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muirstatic void enable_active_ucm(struct alsa_io *aio, int plugged) 1612f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir{ 1613f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir const struct cras_alsa_jack *jack; 1614f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir const char *name; 1615f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 1616f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (aio->base.direction == CRAS_STREAM_OUTPUT) { 1617f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir struct alsa_output_node *active = get_active_output(aio); 1618f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (!active) 1619f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return; 1620f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir name = active->base.name; 1621f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir jack = active->jack; 1622f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir } else { 1623f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir struct alsa_input_node *active = get_active_input(aio); 1624f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (!active) 1625f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return; 1626f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir name = active->base.name; 1627f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir jack = active->jack; 1628f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir } 1629f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 1630f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (jack) 1631f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir cras_alsa_jack_enable_ucm(jack, plugged); 1632f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir else if (aio->ucm) 1633f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir ucm_set_enabled(aio->ucm, name, plugged); 1634f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir} 1635f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 1636016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiangstatic int fill_whole_buffer_with_zeros(struct cras_iodev *iodev) 1637016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang{ 1638016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang struct alsa_io *aio = (struct alsa_io *)iodev; 1639016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang int rc; 1640016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang uint8_t *dst = NULL; 1641016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang size_t format_bytes; 1642016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1643016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang /* Fill whole buffer with zeros. */ 1644016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang rc = cras_alsa_mmap_get_whole_buffer( 1645016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang aio->handle, &dst, &aio->num_underruns); 1646016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1647016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang if (rc < 0) { 1648016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang syslog(LOG_ERR, "Failed to get whole buffer: %s", 1649016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang snd_strerror(rc)); 1650016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return rc; 1651016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang } 1652016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1653016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang format_bytes = cras_get_format_bytes(iodev->format); 1654016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang memset(dst, 0, iodev->buffer_size * format_bytes); 1655016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1656016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return 0; 1657016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang} 1658016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 16595c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiangstatic int adjust_appl_ptr(struct cras_iodev *odev) 16605c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang{ 16615c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang struct alsa_io *aio = (struct alsa_io *)odev; 16625c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang 16635c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang /* Move appl_ptr to min_buffer_level + min_cb_level frames ahead of 16645c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang * hw_ptr when resuming from free run or adjusting appl_ptr from 16655c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang * underrun. */ 16665c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang return cras_alsa_resume_appl_ptr( 16675c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang aio->handle, 16685c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang odev->min_buffer_level + odev->min_cb_level); 16695c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang} 16705c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang 16715c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiangstatic int alsa_output_underrun(struct cras_iodev *odev) 16725c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang{ 16735c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang int rc; 16745c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang /* Fill whole buffer with zeros. This avoids samples left in buffer causing 16755c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang * noise when device plays them. */ 16765c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang rc = fill_whole_buffer_with_zeros(odev); 16775c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang if (rc) 16785c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang return rc; 16795c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang /* Adjust appl_ptr to leave underrun. */ 16805c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang return adjust_appl_ptr(odev); 16815c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang} 16825c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang 1683016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiangstatic int possibly_enter_free_run(struct cras_iodev *odev) 1684016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang{ 1685016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang struct alsa_io *aio = (struct alsa_io *)odev; 1686016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang int rc; 1687016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang unsigned int hw_level, fr_to_write; 1688016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang unsigned int target_hw_level = odev->min_cb_level * 2; 1689fa4cd9766aec6eae544b1078ed835687c4e74617John Muir struct timespec hw_tstamp; 1690016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1691016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang if (aio->is_free_running) 1692016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return 0; 1693016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1694016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang /* Check if all valid samples are played. 1695016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang * If all valid samples are played, fill whole buffer with zeros. */ 1696fa4cd9766aec6eae544b1078ed835687c4e74617John Muir rc = cras_iodev_frames_queued(odev, &hw_tstamp); 1697016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang if (rc < 0) 1698016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return rc; 1699016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang hw_level = rc; 1700016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1701016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang if (hw_level < aio->filled_zeros_for_draining || hw_level == 0) { 1702016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang rc = fill_whole_buffer_with_zeros(odev); 1703016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang if (rc < 0) 1704016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return rc; 1705016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang aio->is_free_running = 1; 1706016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return 0; 1707016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang } 1708016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1709016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang /* Fill some zeros to drain valid samples. */ 1710016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang fr_to_write = cras_iodev_buffer_avail(odev, hw_level); 1711016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1712016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang if (hw_level <= target_hw_level) { 1713016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang fr_to_write = MIN(target_hw_level - hw_level, fr_to_write); 1714016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang rc = cras_iodev_fill_odev_zeros(odev, fr_to_write); 1715016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang if (rc) 1716016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return rc; 1717016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang aio->filled_zeros_for_draining += fr_to_write; 1718016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang } 1719016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1720016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return 0; 1721016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang} 1722016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1723016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiangstatic int leave_free_run(struct cras_iodev *odev) 1724016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang{ 1725016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang struct alsa_io *aio = (struct alsa_io *)odev; 1726016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang int rc; 1727016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1728016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang if (!aio->is_free_running) 1729016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return 0; 1730016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 17315c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang rc = adjust_appl_ptr(odev); 1732016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang if (rc) { 1733016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang syslog(LOG_ERR, "device %s failed to leave free run, rc = %d", 1734016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang odev->info.name, rc); 1735016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return rc; 1736016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang } 1737016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang aio->is_free_running = 0; 1738016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang aio->filled_zeros_for_draining = 0; 1739016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1740016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return 0; 1741016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang} 1742016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 174304bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang/* 174404bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * Free run state is the optimization of no_stream playback on alsa_io. 1745016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang * The whole buffer will be filled with zeros. Device can play these zeros 1746016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang * indefinitely. When there is new meaningful sample, appl_ptr should be 174704bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang * resumed to some distance ahead of hw_ptr. 174804bfd980f62f5eb126c82a6bd17beed75b0d76ebCheng-Yi Chiang */ 1749016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiangstatic int no_stream(struct cras_iodev *odev, int enable) 1750016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang{ 1751016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang if (enable) 1752016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return possibly_enter_free_run(odev); 1753016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang else 1754016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return leave_free_run(odev); 1755016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang} 1756016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1757016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiangstatic int output_should_wake(const struct cras_iodev *odev) 1758016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang{ 1759016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang struct alsa_io *aio = (struct alsa_io *)odev; 1760016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang if (aio->is_free_running) 1761016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang return 0; 1762016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang else 176350cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang return ((cras_iodev_state(odev) == 176450cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang CRAS_IODEV_STATE_NO_STREAM_RUN) || 176550cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang (cras_iodev_state(odev) == 176650cf4aec95391fb7768d9a98e35e618edc7443f3Cheng-Yi Chiang CRAS_IODEV_STATE_NORMAL_RUN)); 1767016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang} 1768016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 1769595b415568beb0048c31e3111c5c935530be1032Cheng-Yi Chiangstatic unsigned int get_num_underruns(const struct cras_iodev *iodev) 1770595b415568beb0048c31e3111c5c935530be1032Cheng-Yi Chiang{ 1771595b415568beb0048c31e3111c5c935530be1032Cheng-Yi Chiang const struct alsa_io *aio = (const struct alsa_io *)iodev; 1772595b415568beb0048c31e3111c5c935530be1032Cheng-Yi Chiang return aio->num_underruns; 1773595b415568beb0048c31e3111c5c935530be1032Cheng-Yi Chiang} 1774595b415568beb0048c31e3111c5c935530be1032Cheng-Yi Chiang 1775d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiangstatic unsigned int get_num_severe_underruns(const struct cras_iodev *iodev) 1776d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang{ 1777d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang const struct alsa_io *aio = (const struct alsa_io *)iodev; 1778d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang return aio->num_severe_underruns; 1779d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang} 17804d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang 17814d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhangstatic void set_default_hotword_model(struct cras_iodev *iodev) 17824d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang{ 17834d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang const char *default_model = "en_us"; 17844d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang cras_node_id_t node_id; 17854d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang 17864d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang if (!iodev->active_node || 17874d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang iodev->active_node->type != CRAS_NODE_TYPE_HOTWORD) 17884d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang return; 17894d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang 17904d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang node_id = cras_make_node_id(iodev->info.idx, iodev->active_node->idx); 17914d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang /* This is a no-op if the default_model is not supported */ 17924d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang cras_iodev_list_set_hotword_model(node_id, default_model); 17934d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang} 17944d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang 1795633768f8b54c6155cb148549c53f51d40cbfef78Dylan Reid/* 1796633768f8b54c6155cb148549c53f51d40cbfef78Dylan Reid * Exported Interface. 179796015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid */ 1798633768f8b54c6155cb148549c53f51d40cbfef78Dylan Reid 179961d1159da08dd6c4258316cc41865f5d5ad6a5c2Dylan Reidstruct cras_iodev *alsa_iodev_create(size_t card_index, 1800f22c4290b77f144b25cf5725994dfac227da3de2Dylan Reid const char *card_name, 180161d1159da08dd6c4258316cc41865f5d5ad6a5c2Dylan Reid size_t device_index, 1802990f4d49d6b64c61aa5a19b167b7132a946ca04cDylan Reid const char *dev_name, 1803777609a81ba9e537df0cc0cfc48dd65dcd95e0e6Dylan Reid const char *dev_id, 18043543aa74b7b45ee6a73f3c6817a9b14e9bcc47ebChih-Chung Chang enum CRAS_ALSA_CARD_TYPE card_type, 18053543aa74b7b45ee6a73f3c6817a9b14e9bcc47ebChih-Chung Chang int is_first, 1806633768f8b54c6155cb148549c53f51d40cbfef78Dylan Reid struct cras_alsa_mixer *mixer, 1807428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao const struct cras_card_config *config, 18089b631e24e0943ab1690cb93ffb916ffe23afe410Chinyue Chen struct cras_use_case_mgr *ucm, 18097c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir snd_hctl_t *hctl, 181085137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen enum CRAS_STREAM_DIRECTION direction, 181185137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen size_t usb_vid, 1812c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu size_t usb_pid, 1813c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu char *usb_serial_number) 181496015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid{ 181596015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid struct alsa_io *aio; 181696015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid struct cras_iodev *iodev; 181796015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 1818b9b911de8c01bdd5dfcee9ef62a416b251a3a5f8Dylan Reid if (direction != CRAS_STREAM_INPUT && direction != CRAS_STREAM_OUTPUT) 1819b9b911de8c01bdd5dfcee9ef62a416b251a3a5f8Dylan Reid return NULL; 1820b9b911de8c01bdd5dfcee9ef62a416b251a3a5f8Dylan Reid 1821f89b59db65423253e507f54890ee10983c9f4419Dylan Reid aio = (struct alsa_io *)calloc(1, sizeof(*aio)); 182296015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid if (!aio) 182396015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid return NULL; 182496015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid iodev = &aio->base; 18259549445d12072aaee36de4a8223dac6afdf14284Dylan Reid iodev->direction = direction; 18269549445d12072aaee36de4a8223dac6afdf14284Dylan Reid 182761d1159da08dd6c4258316cc41865f5d5ad6a5c2Dylan Reid aio->device_index = device_index; 18283543aa74b7b45ee6a73f3c6817a9b14e9bcc47ebChih-Chung Chang aio->card_type = card_type; 18293543aa74b7b45ee6a73f3c6817a9b14e9bcc47ebChih-Chung Chang aio->is_first = is_first; 1830965e605948aafa7900bc21fdff3ce7e6047566baDylan Reid aio->handle = NULL; 1831d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang aio->num_severe_underruns = 0; 1832f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (dev_name) { 1833f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir aio->dev_name = strdup(dev_name); 1834f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (!aio->dev_name) 1835f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir goto cleanup_iodev; 1836f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir } 1837f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (dev_id) { 1838f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir aio->dev_id = strdup(dev_id); 1839f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (!aio->dev_id) 1840f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir goto cleanup_iodev; 1841f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir } 1842016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang aio->is_free_running = 0; 1843016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang aio->filled_zeros_for_draining = 0; 184461d1159da08dd6c4258316cc41865f5d5ad6a5c2Dylan Reid aio->dev = (char *)malloc(MAX_ALSA_DEV_NAME_LENGTH); 184526296d882f4a5387bfecc634b51a891158ce5543Dylan Reid if (aio->dev == NULL) 184626296d882f4a5387bfecc634b51a891158ce5543Dylan Reid goto cleanup_iodev; 184761d1159da08dd6c4258316cc41865f5d5ad6a5c2Dylan Reid snprintf(aio->dev, 184861d1159da08dd6c4258316cc41865f5d5ad6a5c2Dylan Reid MAX_ALSA_DEV_NAME_LENGTH, 184961d1159da08dd6c4258316cc41865f5d5ad6a5c2Dylan Reid "hw:%zu,%zu", 185061d1159da08dd6c4258316cc41865f5d5ad6a5c2Dylan Reid card_index, 185161d1159da08dd6c4258316cc41865f5d5ad6a5c2Dylan Reid device_index); 1852a8cf110b5e4f50f73ba5a7f25556386a0e926350Dylan Reid 185396015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid if (direction == CRAS_STREAM_INPUT) { 185496015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid aio->alsa_stream = SND_PCM_STREAM_CAPTURE; 1855e87681b09b135f66f70618d00411370d00d5c821Dylan Reid aio->base.set_capture_gain = set_alsa_capture_gain; 1856e87681b09b135f66f70618d00411370d00d5c821Dylan Reid aio->base.set_capture_mute = set_alsa_capture_gain; 185796015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid } else { 185896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid aio->alsa_stream = SND_PCM_STREAM_PLAYBACK; 1859e87681b09b135f66f70618d00411370d00d5c821Dylan Reid aio->base.set_volume = set_alsa_volume; 1860bba1e29846e375d554bdc2f07e56e8d395000eafCheng-Yi Chiang aio->base.set_mute = set_alsa_mute; 18615c414105d8ec3c6db465ffa819da817747d9a379Cheng-Yi Chiang aio->base.output_underrun = alsa_output_underrun; 186296015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid } 186314ceeb638c4017ab70781970960cba849238ad43Dylan Reid iodev->open_dev = open_dev; 186414ceeb638c4017ab70781970960cba849238ad43Dylan Reid iodev->close_dev = close_dev; 1865b5ff162a957fb388e3bf9b7316a2f03e23914e03Dylan Reid iodev->update_supported_formats = update_supported_formats; 1866d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid iodev->frames_queued = frames_queued; 1867d6957a4d295e22b427af40ff4dd29c4d14514d7fDylan Reid iodev->delay_frames = delay_frames; 1868b5ff162a957fb388e3bf9b7316a2f03e23914e03Dylan Reid iodev->get_buffer = get_buffer; 1869b5ff162a957fb388e3bf9b7316a2f03e23914e03Dylan Reid iodev->put_buffer = put_buffer; 18703f787ac37a53be6b3a065ee5d1a47427fae78100Hsin-Yu Chao iodev->flush_buffer = flush_buffer; 1871e91895c69bef2515b60d6e4514a5f8e8c53cb36fCheng-Yi Chiang iodev->start = start; 18721161ad02f4762b8e6c7917d33b13de75ea3050b0Chih-Chung Chang iodev->update_active_node = update_active_node; 1873663a962502c7587d464fe555e836c5d70e1c7194Hsin-Yu Chao iodev->update_channel_layout = update_channel_layout; 18743e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao iodev->set_hotword_model = set_hotword_model; 18753e461df43514a41cbd096ee28a45d2f632961600Hsin-Yu Chao iodev->get_hotword_models = get_hotword_models; 187683b9d6a5579a226988e19ef22384a4f41ef0ccdaCheng-Yi Chiang iodev->no_stream = no_stream; 187783b9d6a5579a226988e19ef22384a4f41ef0ccdaCheng-Yi Chiang iodev->output_should_wake = output_should_wake; 1878595b415568beb0048c31e3111c5c935530be1032Cheng-Yi Chiang iodev->get_num_underruns = get_num_underruns; 1879d34bdb1fada946db0fb8e3360110b2cf9e16c09fCheng-Yi Chiang iodev->get_num_severe_underruns = get_num_severe_underruns; 1880c9f56d936f63f56186b6daaa3f0c9abed828531aCheng-Yi Chiang iodev->set_swap_mode_for_node = cras_iodev_dsp_set_swap_mode_for_node; 1881016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 18824a7caa2e2c43ac6426facff6803dae097a1322e4Dylan Reid if (card_type == ALSA_CARD_TYPE_USB) 18834a7caa2e2c43ac6426facff6803dae097a1322e4Dylan Reid iodev->min_buffer_level = USB_EXTRA_BUFFER_FRAMES; 188496015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 188564350200f69f730bbb1eb33a648c8dd6c840da0bCheng-Yi Chiang iodev->ramp = cras_ramp_create(); 188664350200f69f730bbb1eb33a648c8dd6c840da0bCheng-Yi Chiang if (iodev->ramp == NULL) 188764350200f69f730bbb1eb33a648c8dd6c840da0bCheng-Yi Chiang goto cleanup_iodev; 188864350200f69f730bbb1eb33a648c8dd6c840da0bCheng-Yi Chiang 1889633768f8b54c6155cb148549c53f51d40cbfef78Dylan Reid aio->mixer = mixer; 1890428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao aio->config = config; 1891428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao if (direction == CRAS_STREAM_OUTPUT) { 1892428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao aio->default_volume_curve = 1893428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao cras_card_config_get_volume_curve_for_control( 1894428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao config, "Default"); 1895428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao if (aio->default_volume_curve == NULL) 1896428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao aio->default_volume_curve = 1897428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao cras_volume_curve_create_default(); 1898428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao } 1899bebc106698b79d777afd4ba341b942ff0e81ce09Dylan Reid aio->ucm = ucm; 1900891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiang if (ucm) { 19016c567ab7b1b878e0e1e75a2afdc3600fbff8dde9Dylan Reid unsigned int level; 19026c567ab7b1b878e0e1e75a2afdc3600fbff8dde9Dylan Reid 19037057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang aio->dsp_name_default = ucm_get_dsp_name_default(ucm, 19047057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang direction); 1905891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiang /* Set callback for swap mode if it is supported 1906891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiang * in ucm modifier. */ 1907891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiang if (ucm_swap_mode_exists(ucm)) 1908891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiang aio->base.set_swap_mode_for_node = 1909891afa4c41ed215d3c6a6906611f7c30a1670d1eCheng-Yi Chiang set_alsa_node_swapped; 19106c567ab7b1b878e0e1e75a2afdc3600fbff8dde9Dylan Reid 19116c567ab7b1b878e0e1e75a2afdc3600fbff8dde9Dylan Reid level = ucm_get_min_buffer_level(ucm); 19126c567ab7b1b878e0e1e75a2afdc3600fbff8dde9Dylan Reid if (level && direction == CRAS_STREAM_OUTPUT) 19136c567ab7b1b878e0e1e75a2afdc3600fbff8dde9Dylan Reid iodev->min_buffer_level = level; 1914016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 19156babffc0a9eae9f4d3c901de6e6a870bfb4473b0John Muir aio->enable_htimestamp = 19166babffc0a9eae9f4d3c901de6e6a870bfb4473b0John Muir ucm_get_enable_htimestamp_flag(ucm); 1917c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu } 1918016277914b2ab40298e86145a25f8cb3cd22b790Cheng-Yi Chiang 191985137d0c83307ebbfa890e8a64446de5957fbbbcChinyue Chen set_iodev_name(iodev, card_name, dev_name, card_index, device_index, 1920c84eb15a0fbd4a6b336d11438836b644fefc2679Moja Hsu card_type, usb_vid, usb_pid, usb_serial_number); 19217e97d133d0acf2e9dceba372c60db2bd2e0a3660Dylan Reid 19227c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir aio->jack_list = 19237c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir cras_alsa_jack_list_create( 19245c6b778487612c08381279dd4f67846d0680dc3cDylan Reid card_index, 192571e47f74597f17ecbb95f7fa929c6b2b6353a06dDylan Reid card_name, 19265c6b778487612c08381279dd4f67846d0680dc3cDylan Reid device_index, 19277b43a3c84d46474cadd1c7aa30cb05ff2b375a74Ben Zhang is_first, 19285c6b778487612c08381279dd4f67846d0680dc3cDylan Reid mixer, 1929f1e0dec52c09168d9a5c880a9c5dd1c0a0f38867Dylan Reid ucm, 19307c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir hctl, 19315c6b778487612c08381279dd4f67846d0680dc3cDylan Reid direction, 19325c6b778487612c08381279dd4f67846d0680dc3cDylan Reid direction == CRAS_STREAM_OUTPUT ? 19335c6b778487612c08381279dd4f67846d0680dc3cDylan Reid jack_output_plug_event : 19345c6b778487612c08381279dd4f67846d0680dc3cDylan Reid jack_input_plug_event, 19355c6b778487612c08381279dd4f67846d0680dc3cDylan Reid aio); 19367c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir if (!aio->jack_list) 19377c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir goto cleanup_iodev; 19387c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir 1939f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir /* HDMI outputs don't have volume adjustment, do it in software. */ 1940f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (direction == CRAS_STREAM_OUTPUT && strstr(dev_name, HDMI)) 1941f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir iodev->software_volume_needed = 1; 1942f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 1943f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir /* Add this now so that cleanup of the iodev (in case of error or card 1944f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir * card removal will function as expected. */ 1945f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (direction == CRAS_STREAM_OUTPUT) 1946f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir cras_iodev_list_add_output(&aio->base); 1947f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir else 1948f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir cras_iodev_list_add_input(&aio->base); 1949f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return &aio->base; 1950f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 1951f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muircleanup_iodev: 1952f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir free_alsa_iodev_resources(aio); 1953f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir free(aio); 1954f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return NULL; 1955f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir} 1956f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 1957f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muirint alsa_iodev_legacy_complete_init(struct cras_iodev *iodev) 1958f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir{ 1959f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir struct alsa_io *aio = (struct alsa_io *)iodev; 1960f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir const char *dev_name; 1961f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir const char *dev_id; 1962f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir enum CRAS_STREAM_DIRECTION direction; 1963f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir int err; 1964f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir int is_first; 1965f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir struct cras_alsa_mixer *mixer; 1966f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 1967f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (!aio) 1968f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return -EINVAL; 1969f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir direction = iodev->direction; 1970f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir dev_name = aio->dev_name; 1971f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir dev_id = aio->dev_id; 1972f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir is_first = aio->is_first; 1973f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir mixer = aio->mixer; 1974f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 1975f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir /* Create output nodes for mixer controls, such as Headphone 1976f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir * and Speaker, only for the first device. */ 1977f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (direction == CRAS_STREAM_OUTPUT && is_first) 1978f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir cras_alsa_mixer_list_outputs(mixer, 1979f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir new_output_by_mixer_control, aio); 1980f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir else if (direction == CRAS_STREAM_INPUT && is_first) 1981f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir cras_alsa_mixer_list_inputs(mixer, 1982f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir new_input_by_mixer_control, aio); 1983f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 19847c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir err = cras_alsa_jack_list_find_jacks_by_name_matching(aio->jack_list); 19857c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir if (err) 1986f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return err; 19875c6b778487612c08381279dd4f67846d0680dc3cDylan Reid 198832a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang /* Create nodes for jacks that aren't associated with an 198932a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang * already existing node. Get an initial read of the jacks for 199032a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang * this device. */ 1991bdb1c8553513448043adc7d97a88b068e1b50960Dylan Reid cras_alsa_jack_list_report(aio->jack_list); 1992c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid 199332a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang /* Make a default node if there is still no node for this 199432a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang * device, or we still don't have the "Speaker"/"Internal Mic" 19952890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chao * node for the first internal device. Note that the default 19962890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chao * node creation can be supressed by UCM flags for platforms 19972890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chao * which really don't have an internal device. */ 19982890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chao if ((direction == CRAS_STREAM_OUTPUT) && 19992890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chao !no_create_default_output_node(aio)) { 2000cee0644040c7ece11520bf0f87641337b201edf3Hsin-Yu Chao if (first_internal_device(aio) && 2001cee0644040c7ece11520bf0f87641337b201edf3Hsin-Yu Chao !has_node(aio, INTERNAL_SPEAKER) && 2002cee0644040c7ece11520bf0f87641337b201edf3Hsin-Yu Chao !has_node(aio, HDMI)) { 2003cee0644040c7ece11520bf0f87641337b201edf3Hsin-Yu Chao if (strstr(aio->base.info.name, HDMI)) 2004cee0644040c7ece11520bf0f87641337b201edf3Hsin-Yu Chao new_output(aio, NULL, HDMI); 2005cee0644040c7ece11520bf0f87641337b201edf3Hsin-Yu Chao else 2006cee0644040c7ece11520bf0f87641337b201edf3Hsin-Yu Chao new_output(aio, NULL, INTERNAL_SPEAKER); 2007cee0644040c7ece11520bf0f87641337b201edf3Hsin-Yu Chao } else if (!aio->base.nodes) { 2008cee0644040c7ece11520bf0f87641337b201edf3Hsin-Yu Chao new_output(aio, NULL, DEFAULT); 20092fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao } 20102890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chao } else if ((direction == CRAS_STREAM_INPUT) && 20112890dafd4888d46b2bdd638abd8d794a91602355Hsin-Yu Chao !no_create_default_input_node(aio)) { 2012ffb4280e7bb833ffb25de45c01069de62d75c730Chih-Chung Chang if (first_internal_device(aio) && 2013ca158a76e097fc030bfd6e306600428a8b73673cCheng-Yi Chiang !has_node(aio, INTERNAL_MICROPHONE)) 20142fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao new_input(aio, NULL, INTERNAL_MICROPHONE); 2015c90fa076712768ee9a7e721e8f99d2858207e026Hsin-Yu Chao else if (strstr(dev_name, KEYBOARD_MIC)) 20162fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao new_input(aio, NULL, KEYBOARD_MIC); 20178c321ad746a3a71d07711e236496d813e816de23Hsin-Yu Chao else if (dev_id && strstr(dev_id, HOTWORD_DEV)) 20188c321ad746a3a71d07711e236496d813e816de23Hsin-Yu Chao new_input(aio, NULL, HOTWORD_DEV); 2019ffb4280e7bb833ffb25de45c01069de62d75c730Chih-Chung Chang else if (!aio->base.nodes) 20202fdefddae2db6d0f9891c058e02cc8c85083772bHsin-Yu Chao new_input(aio, NULL, DEFAULT); 202132a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang } 202232a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang 20232f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen /* Build software volume scalers. */ 20242f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen if (direction == CRAS_STREAM_OUTPUT) 20252f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen build_softvol_scalers(aio); 20262f0f0a06f40f830201b763c174c3b4a125e6e276Chinyue Chen 202732a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang /* Set the active node as the best node we have now. */ 20281161ad02f4762b8e6c7917d33b13de75ea3050b0Chih-Chung Chang alsa_iodev_set_active_node(&aio->base, 20290def72b968591065f56e88d67e5c83234184811bHsin-Yu Chao first_plugged_node(&aio->base), 20300def72b968591065f56e88d67e5c83234184811bHsin-Yu Chao 0); 203132a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang 203232a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang /* Set plugged for the first USB device per card when it appears. */ 2033f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (aio->card_type == ALSA_CARD_TYPE_USB && is_first) 2034786f9d1938966af8e1eb9f60559a3535b5c71ff8Chih-Chung Chang cras_iodev_set_node_attr(iodev->active_node, 20351161ad02f4762b8e6c7917d33b13de75ea3050b0Chih-Chung Chang IONODE_ATTR_PLUGGED, 1); 20364d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang 20374d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang set_default_hotword_model(iodev); 20384d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang 2039f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return 0; 2040f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir} 204132a79c991ade23beeb5725ef30ee064d9eccdc07Chih-Chung Chang 2042f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muirint alsa_iodev_ucm_add_nodes_and_jacks(struct cras_iodev *iodev, 2043f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir struct ucm_section *section) 2044f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir{ 2045f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir struct alsa_io *aio = (struct alsa_io *)iodev; 2046f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir struct mixer_control *control; 2047f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir struct alsa_input_node *input_node = NULL; 2048f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir struct cras_alsa_jack *jack; 2049f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir struct alsa_output_node *output_node = NULL; 2050f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir int rc; 205196015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 2052f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (!aio || !section) 2053f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return -EINVAL; 2054f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if ((uint32_t)section->dev_idx != aio->device_index) 2055f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return -EINVAL; 2056f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 2057f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir /* This iodev is fully specified. Avoid automatic node creation. */ 2058f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir aio->fully_specified = 1; 2059f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 20608041030fdb87eef2aef22d3d4b347c266b418c5fJohn Muir /* Check here in case the DmaPeriodMicrosecs flag has only been 20618041030fdb87eef2aef22d3d4b347c266b418c5fJohn Muir * specified on one of many device entries with the same PCM. */ 20628041030fdb87eef2aef22d3d4b347c266b418c5fJohn Muir if (!aio->dma_period_set_microsecs) 20638041030fdb87eef2aef22d3d4b347c266b418c5fJohn Muir aio->dma_period_set_microsecs = 20648041030fdb87eef2aef22d3d4b347c266b418c5fJohn Muir ucm_get_dma_period_for_dev(aio->ucm, section->name); 2065828868cb145790177903b68e229a397376e60c0cJohn Muir 2066f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir /* Create a node matching this section. If there is a matching 2067f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir * control use that, otherwise make a node without a control. */ 2068f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir control = cras_alsa_mixer_get_control_for_section(aio->mixer, section); 2069f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (iodev->direction == CRAS_STREAM_OUTPUT) { 2070f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir output_node = new_output(aio, control, section->name); 2071f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (!output_node) 2072f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return -ENOMEM; 2073f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir } else if (iodev->direction == CRAS_STREAM_INPUT) { 2074f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir input_node = new_input(aio, control, section->name); 2075f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (!input_node) 2076f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return -ENOMEM; 2077f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir } 2078f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 2079f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir /* Find any jack controls for this device. */ 2080f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir rc = cras_alsa_jack_list_add_jack_for_section( 2081f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir aio->jack_list, section, &jack); 2082f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (rc) 2083f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return rc; 2084f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 2085f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir /* Associated the jack with the node. */ 2086f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (jack) { 2087f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (output_node) { 2088f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir output_node->jack = jack; 2089428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao if (!output_node->volume_curve) 2090428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao output_node->volume_curve = 2091428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao create_volume_curve_for_jack( 2092428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao aio->config, jack); 2093f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir } else if (input_node) { 2094f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir input_node->jack = jack; 2095f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir } 2096f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir } 2097f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return 0; 2098f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir} 2099f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 2100f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muirvoid alsa_iodev_ucm_complete_init(struct cras_iodev *iodev) 2101f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir{ 2102f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir struct alsa_io *aio = (struct alsa_io *)iodev; 2103f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 2104f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (!iodev) 2105f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir return; 2106f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 2107f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir /* Get an initial read of the jacks for this device. */ 2108f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir cras_alsa_jack_list_report(aio->jack_list); 2109f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 2110f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir /* Build software volume scaler. */ 2111f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (iodev->direction == CRAS_STREAM_OUTPUT) 2112f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir build_softvol_scalers(aio); 2113f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 2114f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir /* Set the active node as the best node we have now. */ 2115f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir alsa_iodev_set_active_node(&aio->base, 2116f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir first_plugged_node(&aio->base), 2117f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 0); 2118f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir 2119f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir /* Set plugged for the first USB device per card when it appears. */ 2120f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir if (aio->card_type == ALSA_CARD_TYPE_USB && aio->is_first) 2121f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir cras_iodev_set_node_attr(iodev->active_node, 2122f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir IONODE_ATTR_PLUGGED, 1); 21234d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang 21244d988ad93d97aca4a20aa5e5d7a2e13f959791aaBen Zhang set_default_hotword_model(iodev); 212596015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid} 212696015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 2127633768f8b54c6155cb148549c53f51d40cbfef78Dylan Reidvoid alsa_iodev_destroy(struct cras_iodev *iodev) 212896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid{ 212996015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid struct alsa_io *aio = (struct alsa_io *)iodev; 21304340df47a2aad9d65f597e4f92c56dd5f57cf9d6Dylan Reid int rc; 213196015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 2132c37f0c7567bef0f309454d59056b1440a24dd5beDylan Reid cras_alsa_jack_list_destroy(aio->jack_list); 21334340df47a2aad9d65f597e4f92c56dd5f57cf9d6Dylan Reid if (iodev->direction == CRAS_STREAM_INPUT) 21344340df47a2aad9d65f597e4f92c56dd5f57cf9d6Dylan Reid rc = cras_iodev_list_rm_input(iodev); 21350dc69e07cfddacdc3ddbc70a0666d6fbd016520cDylan Reid else 21364340df47a2aad9d65f597e4f92c56dd5f57cf9d6Dylan Reid rc = cras_iodev_list_rm_output(iodev); 21370dc69e07cfddacdc3ddbc70a0666d6fbd016520cDylan Reid 21380fc4865c82dd7a07d978c598ec7f2d108a8a3279Hsin-Yu Chao if (rc == -EBUSY) { 21390fc4865c82dd7a07d978c598ec7f2d108a8a3279Hsin-Yu Chao syslog(LOG_ERR, "Failed to remove iodev %s", iodev->info.name); 21400fc4865c82dd7a07d978c598ec7f2d108a8a3279Hsin-Yu Chao return; 21410fc4865c82dd7a07d978c598ec7f2d108a8a3279Hsin-Yu Chao } 21420fc4865c82dd7a07d978c598ec7f2d108a8a3279Hsin-Yu Chao 21430fc4865c82dd7a07d978c598ec7f2d108a8a3279Hsin-Yu Chao /* Free resources when device successfully removed. */ 2144739716e134961568487b41f16e4c31727a258262Dylan Reid free_alsa_iodev_resources(aio); 2145428b555480c23b3f24ca1f64896506d357056bb7Hsin-Yu Chao cras_volume_curve_destroy(aio->default_volume_curve); 21460fc4865c82dd7a07d978c598ec7f2d108a8a3279Hsin-Yu Chao free(iodev); 214796015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid} 214896015bb70822188beb52804f6eec678d7a2ff9a2Dylan Reid 21490301023f29b05474fa9cde0958aa85a95580ce3eJohn Muirunsigned alsa_iodev_index(struct cras_iodev *iodev) 21500301023f29b05474fa9cde0958aa85a95580ce3eJohn Muir{ 21510301023f29b05474fa9cde0958aa85a95580ce3eJohn Muir struct alsa_io *aio = (struct alsa_io *)iodev; 21520301023f29b05474fa9cde0958aa85a95580ce3eJohn Muir return aio->device_index; 21530301023f29b05474fa9cde0958aa85a95580ce3eJohn Muir} 21540301023f29b05474fa9cde0958aa85a95580ce3eJohn Muir 21557c2ff701a0298963a01e8d540f08f3c3bd072b26John Muirint alsa_iodev_has_hctl_jacks(struct cras_iodev *iodev) 21567c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir{ 21577c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir struct alsa_io *aio = (struct alsa_io *)iodev; 21587c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir return cras_alsa_jack_list_has_hctl_jacks(aio->jack_list); 21597c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir} 21607c2ff701a0298963a01e8d540f08f3c3bd072b26John Muir 2161aa9890ee8f4c14408416d3be8d94669111046dcaChih-Chung Changstatic void alsa_iodev_unmute_node(struct alsa_io *aio, 2162aa9890ee8f4c14408416d3be8d94669111046dcaChih-Chung Chang struct cras_ionode *ionode) 216317683d28d93572fc1a51336c363e94110e453857Dylan Reid{ 2164cb5f45f2fc0e95aafa27bac45914d693e3534503Chih-Chung Chang struct alsa_output_node *active = (struct alsa_output_node *)ionode; 2165d21516ae3065cd2bdc992b43ebcdad7b2b1fd778Yao-Wen Mao struct mixer_control *mixer = active->mixer_output; 2166aa9890ee8f4c14408416d3be8d94669111046dcaChih-Chung Chang struct alsa_output_node *output; 2167cb5f45f2fc0e95aafa27bac45914d693e3534503Chih-Chung Chang struct cras_ionode *node; 2168cb5f45f2fc0e95aafa27bac45914d693e3534503Chih-Chung Chang 2169cb5f45f2fc0e95aafa27bac45914d693e3534503Chih-Chung Chang /* If this node is associated with mixer output, unmute the 2170cb5f45f2fc0e95aafa27bac45914d693e3534503Chih-Chung Chang * active mixer output and mute all others, otherwise just set 2171cb5f45f2fc0e95aafa27bac45914d693e3534503Chih-Chung Chang * the node as active and set the volume curve. */ 2172cb5f45f2fc0e95aafa27bac45914d693e3534503Chih-Chung Chang if (mixer) { 2173bba1e29846e375d554bdc2f07e56e8d395000eafCheng-Yi Chiang set_alsa_mute_control(aio, 1); 2174cb5f45f2fc0e95aafa27bac45914d693e3534503Chih-Chung Chang /* Unmute the active mixer output, mute all others. */ 2175cb5f45f2fc0e95aafa27bac45914d693e3534503Chih-Chung Chang DL_FOREACH(aio->base.nodes, node) { 2176cb5f45f2fc0e95aafa27bac45914d693e3534503Chih-Chung Chang output = (struct alsa_output_node *)node; 2177cb5f45f2fc0e95aafa27bac45914d693e3534503Chih-Chung Chang if (output->mixer_output) 2178cb5f45f2fc0e95aafa27bac45914d693e3534503Chih-Chung Chang cras_alsa_mixer_set_output_active_state( 2179cb5f45f2fc0e95aafa27bac45914d693e3534503Chih-Chung Chang output->mixer_output, node == ionode); 218017683d28d93572fc1a51336c363e94110e453857Dylan Reid } 218117683d28d93572fc1a51336c363e94110e453857Dylan Reid } 218217683d28d93572fc1a51336c363e94110e453857Dylan Reid} 21835beee8a3ee2c6e583657fce4cd5220d5c762f4b0Chih-Chung Chang 2184cc5246e10e7f3da17d72592db6d8cf599ab05a38Hsin-Yu Chaostatic int alsa_iodev_set_active_node(struct cras_iodev *iodev, 21850def72b968591065f56e88d67e5c83234184811bHsin-Yu Chao struct cras_ionode *ionode, 21860def72b968591065f56e88d67e5c83234184811bHsin-Yu Chao unsigned dev_enabled) 21875beee8a3ee2c6e583657fce4cd5220d5c762f4b0Chih-Chung Chang{ 21885beee8a3ee2c6e583657fce4cd5220d5c762f4b0Chih-Chung Chang struct alsa_io *aio = (struct alsa_io *)iodev; 21895beee8a3ee2c6e583657fce4cd5220d5c762f4b0Chih-Chung Chang 21900def72b968591065f56e88d67e5c83234184811bHsin-Yu Chao if (iodev->active_node == ionode) { 2191f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir enable_active_ucm(aio, dev_enabled); 21922d7d73485680f773eef56676995729216a6ae47fCheng-Yi Chiang init_device_settings(aio); 2193a36f68df3f451bc2803f8ea00a1add9e6926bd3aChih-Chung Chang return 0; 21940def72b968591065f56e88d67e5c83234184811bHsin-Yu Chao } 2195a36f68df3f451bc2803f8ea00a1add9e6926bd3aChih-Chung Chang 21960def72b968591065f56e88d67e5c83234184811bHsin-Yu Chao /* Disable jack ucm before switching node. */ 2197f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir enable_active_ucm(aio, 0); 2198635cce42dcc5b6c0ee641398a2f54954fe4d69b7Hsin-Yu Chao if (dev_enabled && (iodev->direction == CRAS_STREAM_OUTPUT)) 2199aa9890ee8f4c14408416d3be8d94669111046dcaChih-Chung Chang alsa_iodev_unmute_node(aio, ionode); 2200aa9890ee8f4c14408416d3be8d94669111046dcaChih-Chung Chang 2201625985784f638b3f65a50696982f71b3f3191ad7Chih-Chung Chang cras_iodev_set_active_node(iodev, ionode); 22027057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang aio->base.dsp_name = get_active_dsp_name(aio); 22037057df0d12f641d9bd9aad64d3aa09127a4e84a3Chih-Chung Chang cras_iodev_update_dsp(iodev); 2204f57c6c0d242507b9b80efce1491a6e5c53ef647bJohn Muir enable_active_ucm(aio, dev_enabled); 2205aa9890ee8f4c14408416d3be8d94669111046dcaChih-Chung Chang /* Setting the volume will also unmute if the system isn't muted. */ 22065beee8a3ee2c6e583657fce4cd5220d5c762f4b0Chih-Chung Chang init_device_settings(aio); 22075beee8a3ee2c6e583657fce4cd5220d5c762f4b0Chih-Chung Chang return 0; 22085beee8a3ee2c6e583657fce4cd5220d5c762f4b0Chih-Chung Chang} 2209