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