Commit | Line | Data |
---|---|---|
7006d20e RS |
1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) |
2 | // | |
3 | // This file is provided under a dual BSD/GPLv2 license. When using or | |
4 | // redistributing this file, you may do so under either license. | |
5 | // | |
293ad281 | 6 | // Copyright(c) 2021 Intel Corporation |
7006d20e RS |
7 | // |
8 | // | |
9 | ||
10 | #include <uapi/sound/sof/tokens.h> | |
8ef1439c | 11 | #include <sound/pcm_params.h> |
7006d20e RS |
12 | #include "sof-priv.h" |
13 | #include "sof-audio.h" | |
f80beaf6 | 14 | #include "ipc3-priv.h" |
7006d20e RS |
15 | #include "ops.h" |
16 | ||
b5cee8fe RS |
17 | /* Full volume for default values */ |
18 | #define VOL_ZERO_DB BIT(VOLUME_FWL) | |
19 | ||
323aa1f0 RS |
20 | /* size of tplg ABI in bytes */ |
21 | #define SOF_IPC3_TPLG_ABI_SIZE 3 | |
22 | ||
f2cf24a1 RS |
23 | struct sof_widget_data { |
24 | int ctrl_type; | |
25 | int ipc_cmd; | |
a962890a PU |
26 | void *pdata; |
27 | size_t pdata_size; | |
f2cf24a1 RS |
28 | struct snd_sof_control *control; |
29 | }; | |
30 | ||
31 | struct sof_process_types { | |
32 | const char *name; | |
33 | enum sof_ipc_process_type type; | |
34 | enum sof_comp_type comp_type; | |
35 | }; | |
36 | ||
37 | static const struct sof_process_types sof_process[] = { | |
38 | {"EQFIR", SOF_PROCESS_EQFIR, SOF_COMP_EQ_FIR}, | |
39 | {"EQIIR", SOF_PROCESS_EQIIR, SOF_COMP_EQ_IIR}, | |
40 | {"KEYWORD_DETECT", SOF_PROCESS_KEYWORD_DETECT, SOF_COMP_KEYWORD_DETECT}, | |
41 | {"KPB", SOF_PROCESS_KPB, SOF_COMP_KPB}, | |
42 | {"CHAN_SELECTOR", SOF_PROCESS_CHAN_SELECTOR, SOF_COMP_SELECTOR}, | |
43 | {"MUX", SOF_PROCESS_MUX, SOF_COMP_MUX}, | |
44 | {"DEMUX", SOF_PROCESS_DEMUX, SOF_COMP_DEMUX}, | |
45 | {"DCBLOCK", SOF_PROCESS_DCBLOCK, SOF_COMP_DCBLOCK}, | |
46 | {"SMART_AMP", SOF_PROCESS_SMART_AMP, SOF_COMP_SMART_AMP}, | |
47 | }; | |
48 | ||
49 | static enum sof_ipc_process_type find_process(const char *name) | |
50 | { | |
51 | int i; | |
52 | ||
53 | for (i = 0; i < ARRAY_SIZE(sof_process); i++) { | |
54 | if (strcmp(name, sof_process[i].name) == 0) | |
55 | return sof_process[i].type; | |
56 | } | |
57 | ||
58 | return SOF_PROCESS_NONE; | |
59 | } | |
60 | ||
61 | static int get_token_process_type(void *elem, void *object, u32 offset) | |
62 | { | |
63 | u32 *val = (u32 *)((u8 *)object + offset); | |
64 | ||
65 | *val = find_process((const char *)elem); | |
66 | return 0; | |
67 | } | |
68 | ||
6bd0be1c RS |
69 | /* Buffers */ |
70 | static const struct sof_topology_token buffer_tokens[] = { | |
71 | {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
72 | offsetof(struct sof_ipc_buffer, size)}, | |
73 | {SOF_TKN_BUF_CAPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
74 | offsetof(struct sof_ipc_buffer, caps)}, | |
ebd12b2c CM |
75 | {SOF_TKN_BUF_FLAGS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, |
76 | offsetof(struct sof_ipc_buffer, flags)}, | |
6bd0be1c RS |
77 | }; |
78 | ||
909dadf2 RS |
79 | /* DAI */ |
80 | static const struct sof_topology_token dai_tokens[] = { | |
81 | {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, | |
82 | offsetof(struct sof_ipc_comp_dai, type)}, | |
83 | {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
84 | offsetof(struct sof_ipc_comp_dai, dai_index)}, | |
85 | {SOF_TKN_DAI_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
86 | offsetof(struct sof_ipc_comp_dai, direction)}, | |
87 | }; | |
88 | ||
89 | /* BE DAI link */ | |
90 | static const struct sof_topology_token dai_link_tokens[] = { | |
91 | {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, | |
92 | offsetof(struct sof_ipc_dai_config, type)}, | |
93 | {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
94 | offsetof(struct sof_ipc_dai_config, dai_index)}, | |
95 | }; | |
96 | ||
2141b55d RS |
97 | /* scheduling */ |
98 | static const struct sof_topology_token sched_tokens[] = { | |
99 | {SOF_TKN_SCHED_PERIOD, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
100 | offsetof(struct sof_ipc_pipe_new, period)}, | |
101 | {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
102 | offsetof(struct sof_ipc_pipe_new, priority)}, | |
103 | {SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
104 | offsetof(struct sof_ipc_pipe_new, period_mips)}, | |
105 | {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
106 | offsetof(struct sof_ipc_pipe_new, core)}, | |
107 | {SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
108 | offsetof(struct sof_ipc_pipe_new, frames_per_sched)}, | |
109 | {SOF_TKN_SCHED_TIME_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
110 | offsetof(struct sof_ipc_pipe_new, time_domain)}, | |
111 | }; | |
112 | ||
113 | static const struct sof_topology_token pipeline_tokens[] = { | |
114 | {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, | |
115 | offsetof(struct snd_sof_widget, dynamic_pipeline_widget)}, | |
116 | ||
117 | }; | |
118 | ||
8a2e4a73 RS |
119 | /* volume */ |
120 | static const struct sof_topology_token volume_tokens[] = { | |
121 | {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
122 | offsetof(struct sof_ipc_comp_volume, ramp)}, | |
123 | {SOF_TKN_VOLUME_RAMP_STEP_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
124 | offsetof(struct sof_ipc_comp_volume, initial_ramp)}, | |
125 | }; | |
126 | ||
8d8b1293 RS |
127 | /* SRC */ |
128 | static const struct sof_topology_token src_tokens[] = { | |
129 | {SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
130 | offsetof(struct sof_ipc_comp_src, source_rate)}, | |
131 | {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
132 | offsetof(struct sof_ipc_comp_src, sink_rate)}, | |
133 | }; | |
134 | ||
cb7ed49a RS |
135 | /* ASRC */ |
136 | static const struct sof_topology_token asrc_tokens[] = { | |
137 | {SOF_TKN_ASRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
138 | offsetof(struct sof_ipc_comp_asrc, source_rate)}, | |
139 | {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
140 | offsetof(struct sof_ipc_comp_asrc, sink_rate)}, | |
141 | {SOF_TKN_ASRC_ASYNCHRONOUS_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
142 | offsetof(struct sof_ipc_comp_asrc, asynchronous_mode)}, | |
143 | {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
144 | offsetof(struct sof_ipc_comp_asrc, operation_mode)}, | |
145 | }; | |
146 | ||
f2cf24a1 RS |
147 | /* EFFECT */ |
148 | static const struct sof_topology_token process_tokens[] = { | |
149 | {SOF_TKN_PROCESS_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_process_type, | |
150 | offsetof(struct sof_ipc_comp_process, type)}, | |
151 | }; | |
152 | ||
7006d20e RS |
153 | /* PCM */ |
154 | static const struct sof_topology_token pcm_tokens[] = { | |
155 | {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
156 | offsetof(struct sof_ipc_comp_host, dmac_config)}, | |
157 | }; | |
158 | ||
159 | /* Generic components */ | |
160 | static const struct sof_topology_token comp_tokens[] = { | |
161 | {SOF_TKN_COMP_PERIOD_SINK_COUNT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
162 | offsetof(struct sof_ipc_comp_config, periods_sink)}, | |
163 | {SOF_TKN_COMP_PERIOD_SOURCE_COUNT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
164 | offsetof(struct sof_ipc_comp_config, periods_source)}, | |
165 | {SOF_TKN_COMP_FORMAT, | |
166 | SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, | |
167 | offsetof(struct sof_ipc_comp_config, frame_fmt)}, | |
168 | }; | |
169 | ||
909dadf2 RS |
170 | /* SSP */ |
171 | static const struct sof_topology_token ssp_tokens[] = { | |
172 | {SOF_TKN_INTEL_SSP_CLKS_CONTROL, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
173 | offsetof(struct sof_ipc_dai_ssp_params, clks_control)}, | |
174 | {SOF_TKN_INTEL_SSP_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
175 | offsetof(struct sof_ipc_dai_ssp_params, mclk_id)}, | |
176 | {SOF_TKN_INTEL_SSP_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
177 | offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits)}, | |
178 | {SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
179 | offsetof(struct sof_ipc_dai_ssp_params, frame_pulse_width)}, | |
180 | {SOF_TKN_INTEL_SSP_QUIRKS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
181 | offsetof(struct sof_ipc_dai_ssp_params, quirks)}, | |
182 | {SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, | |
183 | offsetof(struct sof_ipc_dai_ssp_params, tdm_per_slot_padding_flag)}, | |
184 | {SOF_TKN_INTEL_SSP_BCLK_DELAY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
185 | offsetof(struct sof_ipc_dai_ssp_params, bclk_delay)}, | |
186 | }; | |
187 | ||
188 | /* ALH */ | |
189 | static const struct sof_topology_token alh_tokens[] = { | |
190 | {SOF_TKN_INTEL_ALH_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
191 | offsetof(struct sof_ipc_dai_alh_params, rate)}, | |
192 | {SOF_TKN_INTEL_ALH_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
193 | offsetof(struct sof_ipc_dai_alh_params, channels)}, | |
194 | }; | |
195 | ||
196 | /* DMIC */ | |
197 | static const struct sof_topology_token dmic_tokens[] = { | |
198 | {SOF_TKN_INTEL_DMIC_DRIVER_VERSION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
199 | offsetof(struct sof_ipc_dai_dmic_params, driver_ipc_version)}, | |
200 | {SOF_TKN_INTEL_DMIC_CLK_MIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
201 | offsetof(struct sof_ipc_dai_dmic_params, pdmclk_min)}, | |
202 | {SOF_TKN_INTEL_DMIC_CLK_MAX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
203 | offsetof(struct sof_ipc_dai_dmic_params, pdmclk_max)}, | |
204 | {SOF_TKN_INTEL_DMIC_SAMPLE_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
205 | offsetof(struct sof_ipc_dai_dmic_params, fifo_fs)}, | |
206 | {SOF_TKN_INTEL_DMIC_DUTY_MIN, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
207 | offsetof(struct sof_ipc_dai_dmic_params, duty_min)}, | |
208 | {SOF_TKN_INTEL_DMIC_DUTY_MAX, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
209 | offsetof(struct sof_ipc_dai_dmic_params, duty_max)}, | |
210 | {SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
211 | offsetof(struct sof_ipc_dai_dmic_params, num_pdm_active)}, | |
212 | {SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
213 | offsetof(struct sof_ipc_dai_dmic_params, fifo_bits)}, | |
214 | {SOF_TKN_INTEL_DMIC_UNMUTE_RAMP_TIME_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
215 | offsetof(struct sof_ipc_dai_dmic_params, unmute_ramp_time)}, | |
216 | }; | |
217 | ||
218 | /* ESAI */ | |
219 | static const struct sof_topology_token esai_tokens[] = { | |
220 | {SOF_TKN_IMX_ESAI_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
221 | offsetof(struct sof_ipc_dai_esai_params, mclk_id)}, | |
222 | }; | |
223 | ||
224 | /* SAI */ | |
225 | static const struct sof_topology_token sai_tokens[] = { | |
226 | {SOF_TKN_IMX_SAI_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
227 | offsetof(struct sof_ipc_dai_sai_params, mclk_id)}, | |
228 | }; | |
229 | ||
230 | /* | |
231 | * DMIC PDM Tokens | |
232 | * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token | |
233 | * as it increments the index while parsing the array of pdm tokens | |
234 | * and determines the correct offset | |
235 | */ | |
236 | static const struct sof_topology_token dmic_pdm_tokens[] = { | |
237 | {SOF_TKN_INTEL_DMIC_PDM_CTRL_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
238 | offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, id)}, | |
239 | {SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
240 | offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_a)}, | |
241 | {SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
242 | offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_b)}, | |
243 | {SOF_TKN_INTEL_DMIC_PDM_POLARITY_A, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
244 | offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_a)}, | |
245 | {SOF_TKN_INTEL_DMIC_PDM_POLARITY_B, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
246 | offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_b)}, | |
247 | {SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
248 | offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, clk_edge)}, | |
249 | {SOF_TKN_INTEL_DMIC_PDM_SKEW, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, | |
250 | offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, skew)}, | |
251 | }; | |
252 | ||
253 | /* HDA */ | |
254 | static const struct sof_topology_token hda_tokens[] = { | |
255 | {SOF_TKN_INTEL_HDA_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
256 | offsetof(struct sof_ipc_dai_hda_params, rate)}, | |
257 | {SOF_TKN_INTEL_HDA_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
258 | offsetof(struct sof_ipc_dai_hda_params, channels)}, | |
259 | }; | |
260 | ||
261 | /* AFE */ | |
262 | static const struct sof_topology_token afe_tokens[] = { | |
263 | {SOF_TKN_MEDIATEK_AFE_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
264 | offsetof(struct sof_ipc_dai_mtk_afe_params, rate)}, | |
265 | {SOF_TKN_MEDIATEK_AFE_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
266 | offsetof(struct sof_ipc_dai_mtk_afe_params, channels)}, | |
267 | {SOF_TKN_MEDIATEK_AFE_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, | |
268 | offsetof(struct sof_ipc_dai_mtk_afe_params, format)}, | |
269 | }; | |
270 | ||
689614ce AKP |
271 | /* ACPDMIC */ |
272 | static const struct sof_topology_token acpdmic_tokens[] = { | |
273 | {SOF_TKN_AMD_ACPDMIC_RATE, | |
274 | SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
275 | offsetof(struct sof_ipc_dai_acpdmic_params, pdm_rate)}, | |
276 | {SOF_TKN_AMD_ACPDMIC_CH, | |
277 | SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
278 | offsetof(struct sof_ipc_dai_acpdmic_params, pdm_ch)}, | |
279 | }; | |
280 | ||
75af4199 R |
281 | /* ACPI2S */ |
282 | static const struct sof_topology_token acpi2s_tokens[] = { | |
283 | {SOF_TKN_AMD_ACPI2S_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
284 | offsetof(struct sof_ipc_dai_acp_params, fsync_rate)}, | |
285 | {SOF_TKN_AMD_ACPI2S_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
286 | offsetof(struct sof_ipc_dai_acp_params, tdm_slots)}, | |
287 | {SOF_TKN_AMD_ACPI2S_TDM_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
288 | offsetof(struct sof_ipc_dai_acp_params, tdm_mode)}, | |
289 | }; | |
290 | ||
89ef4208 DB |
291 | /* MICFIL PDM */ |
292 | static const struct sof_topology_token micfil_pdm_tokens[] = { | |
293 | {SOF_TKN_IMX_MICFIL_RATE, | |
294 | SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
295 | offsetof(struct sof_ipc_dai_micfil_params, pdm_rate)}, | |
296 | {SOF_TKN_IMX_MICFIL_CH, | |
297 | SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
298 | offsetof(struct sof_ipc_dai_micfil_params, pdm_ch)}, | |
299 | }; | |
300 | ||
14d89e55 VM |
301 | /* ACP_SDW */ |
302 | static const struct sof_topology_token acp_sdw_tokens[] = { | |
303 | {SOF_TKN_AMD_ACP_SDW_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
304 | offsetof(struct sof_ipc_dai_acp_sdw_params, rate)}, | |
305 | {SOF_TKN_AMD_ACP_SDW_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
306 | offsetof(struct sof_ipc_dai_acp_sdw_params, channels)}, | |
307 | }; | |
308 | ||
7006d20e RS |
309 | /* Core tokens */ |
310 | static const struct sof_topology_token core_tokens[] = { | |
311 | {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, | |
312 | offsetof(struct sof_ipc_comp, core)}, | |
313 | }; | |
314 | ||
315 | /* Component extended tokens */ | |
316 | static const struct sof_topology_token comp_ext_tokens[] = { | |
317 | {SOF_TKN_COMP_UUID, SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid, | |
318 | offsetof(struct snd_sof_widget, uuid)}, | |
319 | }; | |
320 | ||
321 | static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = { | |
322 | [SOF_PCM_TOKENS] = {"PCM tokens", pcm_tokens, ARRAY_SIZE(pcm_tokens)}, | |
2141b55d RS |
323 | [SOF_PIPELINE_TOKENS] = {"Pipeline tokens", pipeline_tokens, ARRAY_SIZE(pipeline_tokens)}, |
324 | [SOF_SCHED_TOKENS] = {"Scheduler tokens", sched_tokens, ARRAY_SIZE(sched_tokens)}, | |
7006d20e RS |
325 | [SOF_COMP_TOKENS] = {"Comp tokens", comp_tokens, ARRAY_SIZE(comp_tokens)}, |
326 | [SOF_CORE_TOKENS] = {"Core tokens", core_tokens, ARRAY_SIZE(core_tokens)}, | |
327 | [SOF_COMP_EXT_TOKENS] = {"AFE tokens", comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens)}, | |
6bd0be1c | 328 | [SOF_BUFFER_TOKENS] = {"Buffer tokens", buffer_tokens, ARRAY_SIZE(buffer_tokens)}, |
8a2e4a73 | 329 | [SOF_VOLUME_TOKENS] = {"Volume tokens", volume_tokens, ARRAY_SIZE(volume_tokens)}, |
8d8b1293 | 330 | [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)}, |
cb7ed49a | 331 | [SOF_ASRC_TOKENS] = {"ASRC tokens", asrc_tokens, ARRAY_SIZE(asrc_tokens)}, |
f2cf24a1 | 332 | [SOF_PROCESS_TOKENS] = {"Process tokens", process_tokens, ARRAY_SIZE(process_tokens)}, |
909dadf2 RS |
333 | [SOF_DAI_TOKENS] = {"DAI tokens", dai_tokens, ARRAY_SIZE(dai_tokens)}, |
334 | [SOF_DAI_LINK_TOKENS] = {"DAI link tokens", dai_link_tokens, ARRAY_SIZE(dai_link_tokens)}, | |
335 | [SOF_HDA_TOKENS] = {"HDA tokens", hda_tokens, ARRAY_SIZE(hda_tokens)}, | |
336 | [SOF_SSP_TOKENS] = {"SSP tokens", ssp_tokens, ARRAY_SIZE(ssp_tokens)}, | |
337 | [SOF_ALH_TOKENS] = {"ALH tokens", alh_tokens, ARRAY_SIZE(alh_tokens)}, | |
338 | [SOF_DMIC_TOKENS] = {"DMIC tokens", dmic_tokens, ARRAY_SIZE(dmic_tokens)}, | |
339 | [SOF_DMIC_PDM_TOKENS] = {"DMIC PDM tokens", dmic_pdm_tokens, ARRAY_SIZE(dmic_pdm_tokens)}, | |
340 | [SOF_ESAI_TOKENS] = {"ESAI tokens", esai_tokens, ARRAY_SIZE(esai_tokens)}, | |
341 | [SOF_SAI_TOKENS] = {"SAI tokens", sai_tokens, ARRAY_SIZE(sai_tokens)}, | |
342 | [SOF_AFE_TOKENS] = {"AFE tokens", afe_tokens, ARRAY_SIZE(afe_tokens)}, | |
689614ce | 343 | [SOF_ACPDMIC_TOKENS] = {"ACPDMIC tokens", acpdmic_tokens, ARRAY_SIZE(acpdmic_tokens)}, |
75af4199 | 344 | [SOF_ACPI2S_TOKENS] = {"ACPI2S tokens", acpi2s_tokens, ARRAY_SIZE(acpi2s_tokens)}, |
89ef4208 DB |
345 | [SOF_MICFIL_TOKENS] = {"MICFIL PDM tokens", |
346 | micfil_pdm_tokens, ARRAY_SIZE(micfil_pdm_tokens)}, | |
14d89e55 | 347 | [SOF_ACP_SDW_TOKENS] = {"ACP_SDW tokens", acp_sdw_tokens, ARRAY_SIZE(acp_sdw_tokens)}, |
7006d20e RS |
348 | }; |
349 | ||
350 | /** | |
351 | * sof_comp_alloc - allocate and initialize buffer for a new component | |
352 | * @swidget: pointer to struct snd_sof_widget containing extended data | |
353 | * @ipc_size: IPC payload size that will be updated depending on valid | |
354 | * extended data. | |
355 | * @index: ID of the pipeline the component belongs to | |
356 | * | |
357 | * Return: The pointer to the new allocated component, NULL if failed. | |
358 | */ | |
359 | static void *sof_comp_alloc(struct snd_sof_widget *swidget, size_t *ipc_size, | |
360 | int index) | |
361 | { | |
362 | struct sof_ipc_comp *comp; | |
363 | size_t total_size = *ipc_size; | |
364 | size_t ext_size = sizeof(swidget->uuid); | |
365 | ||
366 | /* only non-zero UUID is valid */ | |
367 | if (!guid_is_null(&swidget->uuid)) | |
368 | total_size += ext_size; | |
369 | ||
370 | comp = kzalloc(total_size, GFP_KERNEL); | |
371 | if (!comp) | |
372 | return NULL; | |
373 | ||
374 | /* configure comp new IPC message */ | |
375 | comp->hdr.size = total_size; | |
376 | comp->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; | |
377 | comp->id = swidget->comp_id; | |
378 | comp->pipeline_id = index; | |
379 | comp->core = swidget->core; | |
380 | ||
381 | /* handle the extended data if needed */ | |
382 | if (total_size > *ipc_size) { | |
383 | /* append extended data to the end of the component */ | |
384 | memcpy((u8 *)comp + *ipc_size, &swidget->uuid, ext_size); | |
385 | comp->ext_data_length = ext_size; | |
386 | } | |
387 | ||
388 | /* update ipc_size and return */ | |
389 | *ipc_size = total_size; | |
390 | return comp; | |
391 | } | |
392 | ||
393 | static void sof_dbg_comp_config(struct snd_soc_component *scomp, struct sof_ipc_comp_config *config) | |
394 | { | |
395 | dev_dbg(scomp->dev, " config: periods snk %d src %d fmt %d\n", | |
396 | config->periods_sink, config->periods_source, | |
397 | config->frame_fmt); | |
398 | } | |
399 | ||
400 | static int sof_ipc3_widget_setup_comp_host(struct snd_sof_widget *swidget) | |
401 | { | |
402 | struct snd_soc_component *scomp = swidget->scomp; | |
403 | struct sof_ipc_comp_host *host; | |
404 | size_t ipc_size = sizeof(*host); | |
405 | int ret; | |
406 | ||
407 | host = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); | |
408 | if (!host) | |
409 | return -ENOMEM; | |
410 | swidget->private = host; | |
411 | ||
412 | /* configure host comp IPC message */ | |
413 | host->comp.type = SOF_COMP_HOST; | |
414 | host->config.hdr.size = sizeof(host->config); | |
415 | ||
416 | if (swidget->id == snd_soc_dapm_aif_out) | |
417 | host->direction = SOF_IPC_STREAM_CAPTURE; | |
418 | else | |
419 | host->direction = SOF_IPC_STREAM_PLAYBACK; | |
420 | ||
421 | /* parse one set of pcm_tokens */ | |
422 | ret = sof_update_ipc_object(scomp, host, SOF_PCM_TOKENS, swidget->tuples, | |
423 | swidget->num_tuples, sizeof(*host), 1); | |
424 | if (ret < 0) | |
425 | goto err; | |
426 | ||
427 | /* parse one set of comp_tokens */ | |
428 | ret = sof_update_ipc_object(scomp, &host->config, SOF_COMP_TOKENS, swidget->tuples, | |
429 | swidget->num_tuples, sizeof(host->config), 1); | |
430 | if (ret < 0) | |
431 | goto err; | |
432 | ||
433 | dev_dbg(scomp->dev, "loaded host %s\n", swidget->widget->name); | |
434 | sof_dbg_comp_config(scomp, &host->config); | |
435 | ||
436 | return 0; | |
437 | err: | |
438 | kfree(swidget->private); | |
439 | swidget->private = NULL; | |
440 | ||
441 | return ret; | |
442 | } | |
443 | ||
444 | static void sof_ipc3_widget_free_comp(struct snd_sof_widget *swidget) | |
445 | { | |
446 | kfree(swidget->private); | |
447 | } | |
448 | ||
111d66f6 RS |
449 | static int sof_ipc3_widget_setup_comp_tone(struct snd_sof_widget *swidget) |
450 | { | |
451 | struct snd_soc_component *scomp = swidget->scomp; | |
452 | struct sof_ipc_comp_tone *tone; | |
453 | size_t ipc_size = sizeof(*tone); | |
454 | int ret; | |
455 | ||
456 | tone = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); | |
457 | if (!tone) | |
458 | return -ENOMEM; | |
459 | ||
460 | swidget->private = tone; | |
461 | ||
462 | /* configure siggen IPC message */ | |
463 | tone->comp.type = SOF_COMP_TONE; | |
464 | tone->config.hdr.size = sizeof(tone->config); | |
465 | ||
466 | /* parse one set of comp tokens */ | |
467 | ret = sof_update_ipc_object(scomp, &tone->config, SOF_COMP_TOKENS, swidget->tuples, | |
468 | swidget->num_tuples, sizeof(tone->config), 1); | |
469 | if (ret < 0) { | |
470 | kfree(swidget->private); | |
471 | swidget->private = NULL; | |
472 | return ret; | |
473 | } | |
474 | ||
475 | dev_dbg(scomp->dev, "tone %s: frequency %d amplitude %d\n", | |
476 | swidget->widget->name, tone->frequency, tone->amplitude); | |
477 | sof_dbg_comp_config(scomp, &tone->config); | |
478 | ||
479 | return 0; | |
480 | } | |
481 | ||
30f41680 RS |
482 | static int sof_ipc3_widget_setup_comp_mixer(struct snd_sof_widget *swidget) |
483 | { | |
484 | struct snd_soc_component *scomp = swidget->scomp; | |
485 | struct sof_ipc_comp_mixer *mixer; | |
486 | size_t ipc_size = sizeof(*mixer); | |
487 | int ret; | |
488 | ||
489 | mixer = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); | |
490 | if (!mixer) | |
491 | return -ENOMEM; | |
492 | ||
493 | swidget->private = mixer; | |
494 | ||
495 | /* configure mixer IPC message */ | |
496 | mixer->comp.type = SOF_COMP_MIXER; | |
497 | mixer->config.hdr.size = sizeof(mixer->config); | |
498 | ||
499 | /* parse one set of comp tokens */ | |
500 | ret = sof_update_ipc_object(scomp, &mixer->config, SOF_COMP_TOKENS, | |
501 | swidget->tuples, swidget->num_tuples, | |
502 | sizeof(mixer->config), 1); | |
503 | if (ret < 0) { | |
504 | kfree(swidget->private); | |
505 | swidget->private = NULL; | |
506 | ||
507 | return ret; | |
508 | } | |
509 | ||
510 | dev_dbg(scomp->dev, "loaded mixer %s\n", swidget->widget->name); | |
511 | sof_dbg_comp_config(scomp, &mixer->config); | |
512 | ||
513 | return 0; | |
514 | } | |
515 | ||
2141b55d RS |
516 | static int sof_ipc3_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) |
517 | { | |
518 | struct snd_soc_component *scomp = swidget->scomp; | |
0376b995 | 519 | struct snd_sof_pipeline *spipe = swidget->spipe; |
2141b55d RS |
520 | struct sof_ipc_pipe_new *pipeline; |
521 | struct snd_sof_widget *comp_swidget; | |
522 | int ret; | |
523 | ||
524 | pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL); | |
525 | if (!pipeline) | |
526 | return -ENOMEM; | |
527 | ||
528 | /* configure pipeline IPC message */ | |
529 | pipeline->hdr.size = sizeof(*pipeline); | |
530 | pipeline->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW; | |
531 | pipeline->pipeline_id = swidget->pipeline_id; | |
532 | pipeline->comp_id = swidget->comp_id; | |
533 | ||
534 | swidget->private = pipeline; | |
535 | ||
536 | /* component at start of pipeline is our stream id */ | |
537 | comp_swidget = snd_sof_find_swidget(scomp, swidget->widget->sname); | |
538 | if (!comp_swidget) { | |
539 | dev_err(scomp->dev, "scheduler %s refers to non existent widget %s\n", | |
540 | swidget->widget->name, swidget->widget->sname); | |
541 | ret = -EINVAL; | |
542 | goto err; | |
543 | } | |
544 | ||
545 | pipeline->sched_id = comp_swidget->comp_id; | |
546 | ||
547 | /* parse one set of scheduler tokens */ | |
548 | ret = sof_update_ipc_object(scomp, pipeline, SOF_SCHED_TOKENS, swidget->tuples, | |
549 | swidget->num_tuples, sizeof(*pipeline), 1); | |
550 | if (ret < 0) | |
551 | goto err; | |
552 | ||
553 | /* parse one set of pipeline tokens */ | |
554 | ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples, | |
555 | swidget->num_tuples, sizeof(*swidget), 1); | |
556 | if (ret < 0) | |
557 | goto err; | |
558 | ||
559 | if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) | |
560 | pipeline->core = SOF_DSP_PRIMARY_CORE; | |
561 | ||
562 | if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE)) | |
563 | swidget->dynamic_pipeline_widget = | |
564 | sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_ENABLE); | |
565 | ||
566 | dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d dynamic %d\n", | |
567 | swidget->widget->name, pipeline->period, pipeline->priority, | |
568 | pipeline->period_mips, pipeline->core, pipeline->frames_per_sched, | |
569 | swidget->dynamic_pipeline_widget); | |
570 | ||
571 | swidget->core = pipeline->core; | |
0376b995 | 572 | spipe->core_mask |= BIT(pipeline->core); |
2141b55d RS |
573 | |
574 | return 0; | |
575 | ||
576 | err: | |
577 | kfree(swidget->private); | |
578 | swidget->private = NULL; | |
579 | ||
580 | return ret; | |
581 | } | |
582 | ||
6bd0be1c RS |
583 | static int sof_ipc3_widget_setup_comp_buffer(struct snd_sof_widget *swidget) |
584 | { | |
585 | struct snd_soc_component *scomp = swidget->scomp; | |
586 | struct sof_ipc_buffer *buffer; | |
587 | int ret; | |
588 | ||
589 | buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); | |
590 | if (!buffer) | |
591 | return -ENOMEM; | |
592 | ||
593 | swidget->private = buffer; | |
594 | ||
595 | /* configure dai IPC message */ | |
596 | buffer->comp.hdr.size = sizeof(*buffer); | |
597 | buffer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW; | |
598 | buffer->comp.id = swidget->comp_id; | |
599 | buffer->comp.type = SOF_COMP_BUFFER; | |
600 | buffer->comp.pipeline_id = swidget->pipeline_id; | |
601 | buffer->comp.core = swidget->core; | |
602 | ||
603 | /* parse one set of buffer tokens */ | |
604 | ret = sof_update_ipc_object(scomp, buffer, SOF_BUFFER_TOKENS, swidget->tuples, | |
605 | swidget->num_tuples, sizeof(*buffer), 1); | |
606 | if (ret < 0) { | |
607 | kfree(swidget->private); | |
608 | swidget->private = NULL; | |
609 | return ret; | |
610 | } | |
611 | ||
612 | dev_dbg(scomp->dev, "buffer %s: size %d caps 0x%x\n", | |
613 | swidget->widget->name, buffer->size, buffer->caps); | |
614 | ||
615 | return 0; | |
616 | } | |
617 | ||
8d8b1293 RS |
618 | static int sof_ipc3_widget_setup_comp_src(struct snd_sof_widget *swidget) |
619 | { | |
620 | struct snd_soc_component *scomp = swidget->scomp; | |
621 | struct sof_ipc_comp_src *src; | |
622 | size_t ipc_size = sizeof(*src); | |
623 | int ret; | |
624 | ||
625 | src = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); | |
626 | if (!src) | |
627 | return -ENOMEM; | |
628 | ||
629 | swidget->private = src; | |
630 | ||
631 | /* configure src IPC message */ | |
632 | src->comp.type = SOF_COMP_SRC; | |
633 | src->config.hdr.size = sizeof(src->config); | |
634 | ||
635 | /* parse one set of src tokens */ | |
636 | ret = sof_update_ipc_object(scomp, src, SOF_SRC_TOKENS, swidget->tuples, | |
637 | swidget->num_tuples, sizeof(*src), 1); | |
638 | if (ret < 0) | |
639 | goto err; | |
640 | ||
641 | /* parse one set of comp tokens */ | |
642 | ret = sof_update_ipc_object(scomp, &src->config, SOF_COMP_TOKENS, | |
643 | swidget->tuples, swidget->num_tuples, sizeof(src->config), 1); | |
644 | if (ret < 0) | |
645 | goto err; | |
646 | ||
647 | dev_dbg(scomp->dev, "src %s: source rate %d sink rate %d\n", | |
648 | swidget->widget->name, src->source_rate, src->sink_rate); | |
649 | sof_dbg_comp_config(scomp, &src->config); | |
650 | ||
651 | return 0; | |
652 | err: | |
653 | kfree(swidget->private); | |
654 | swidget->private = NULL; | |
655 | ||
656 | return ret; | |
657 | } | |
658 | ||
cb7ed49a RS |
659 | static int sof_ipc3_widget_setup_comp_asrc(struct snd_sof_widget *swidget) |
660 | { | |
661 | struct snd_soc_component *scomp = swidget->scomp; | |
662 | struct sof_ipc_comp_asrc *asrc; | |
663 | size_t ipc_size = sizeof(*asrc); | |
664 | int ret; | |
665 | ||
666 | asrc = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); | |
667 | if (!asrc) | |
668 | return -ENOMEM; | |
669 | ||
670 | swidget->private = asrc; | |
671 | ||
672 | /* configure ASRC IPC message */ | |
673 | asrc->comp.type = SOF_COMP_ASRC; | |
674 | asrc->config.hdr.size = sizeof(asrc->config); | |
675 | ||
676 | /* parse one set of asrc tokens */ | |
677 | ret = sof_update_ipc_object(scomp, asrc, SOF_ASRC_TOKENS, swidget->tuples, | |
678 | swidget->num_tuples, sizeof(*asrc), 1); | |
679 | if (ret < 0) | |
680 | goto err; | |
681 | ||
682 | /* parse one set of comp tokens */ | |
683 | ret = sof_update_ipc_object(scomp, &asrc->config, SOF_COMP_TOKENS, | |
684 | swidget->tuples, swidget->num_tuples, sizeof(asrc->config), 1); | |
685 | if (ret < 0) | |
686 | goto err; | |
687 | ||
688 | dev_dbg(scomp->dev, "asrc %s: source rate %d sink rate %d asynch %d operation %d\n", | |
689 | swidget->widget->name, asrc->source_rate, asrc->sink_rate, | |
690 | asrc->asynchronous_mode, asrc->operation_mode); | |
691 | ||
692 | sof_dbg_comp_config(scomp, &asrc->config); | |
693 | ||
694 | return 0; | |
695 | err: | |
696 | kfree(swidget->private); | |
697 | swidget->private = NULL; | |
698 | ||
699 | return ret; | |
700 | } | |
701 | ||
683b54ef RS |
702 | /* |
703 | * Mux topology | |
704 | */ | |
705 | static int sof_ipc3_widget_setup_comp_mux(struct snd_sof_widget *swidget) | |
706 | { | |
707 | struct snd_soc_component *scomp = swidget->scomp; | |
708 | struct sof_ipc_comp_mux *mux; | |
709 | size_t ipc_size = sizeof(*mux); | |
710 | int ret; | |
711 | ||
712 | mux = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); | |
713 | if (!mux) | |
714 | return -ENOMEM; | |
715 | ||
716 | swidget->private = mux; | |
717 | ||
718 | /* configure mux IPC message */ | |
719 | mux->comp.type = SOF_COMP_MUX; | |
720 | mux->config.hdr.size = sizeof(mux->config); | |
721 | ||
722 | /* parse one set of comp tokens */ | |
723 | ret = sof_update_ipc_object(scomp, &mux->config, SOF_COMP_TOKENS, | |
724 | swidget->tuples, swidget->num_tuples, sizeof(mux->config), 1); | |
725 | if (ret < 0) { | |
726 | kfree(swidget->private); | |
727 | swidget->private = NULL; | |
728 | return ret; | |
729 | } | |
730 | ||
731 | dev_dbg(scomp->dev, "loaded mux %s\n", swidget->widget->name); | |
732 | sof_dbg_comp_config(scomp, &mux->config); | |
733 | ||
734 | return 0; | |
735 | } | |
736 | ||
8a2e4a73 RS |
737 | /* |
738 | * PGA Topology | |
739 | */ | |
740 | ||
741 | static int sof_ipc3_widget_setup_comp_pga(struct snd_sof_widget *swidget) | |
742 | { | |
743 | struct snd_soc_component *scomp = swidget->scomp; | |
744 | struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); | |
745 | struct sof_ipc_comp_volume *volume; | |
746 | struct snd_sof_control *scontrol; | |
747 | size_t ipc_size = sizeof(*volume); | |
748 | int min_step, max_step; | |
749 | int ret; | |
750 | ||
751 | volume = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); | |
752 | if (!volume) | |
753 | return -ENOMEM; | |
754 | ||
755 | swidget->private = volume; | |
756 | ||
757 | /* configure volume IPC message */ | |
758 | volume->comp.type = SOF_COMP_VOLUME; | |
759 | volume->config.hdr.size = sizeof(volume->config); | |
760 | ||
761 | /* parse one set of volume tokens */ | |
762 | ret = sof_update_ipc_object(scomp, volume, SOF_VOLUME_TOKENS, swidget->tuples, | |
763 | swidget->num_tuples, sizeof(*volume), 1); | |
764 | if (ret < 0) | |
765 | goto err; | |
766 | ||
767 | /* parse one set of comp tokens */ | |
768 | ret = sof_update_ipc_object(scomp, &volume->config, SOF_COMP_TOKENS, | |
769 | swidget->tuples, swidget->num_tuples, | |
770 | sizeof(volume->config), 1); | |
771 | if (ret < 0) | |
772 | goto err; | |
773 | ||
774 | dev_dbg(scomp->dev, "loaded PGA %s\n", swidget->widget->name); | |
775 | sof_dbg_comp_config(scomp, &volume->config); | |
776 | ||
777 | list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { | |
778 | if (scontrol->comp_id == swidget->comp_id && | |
779 | scontrol->volume_table) { | |
780 | min_step = scontrol->min_volume_step; | |
781 | max_step = scontrol->max_volume_step; | |
782 | volume->min_value = scontrol->volume_table[min_step]; | |
783 | volume->max_value = scontrol->volume_table[max_step]; | |
784 | volume->channels = scontrol->num_channels; | |
785 | break; | |
786 | } | |
787 | } | |
788 | ||
789 | return 0; | |
790 | err: | |
791 | kfree(swidget->private); | |
792 | swidget->private = NULL; | |
793 | ||
794 | return ret; | |
795 | } | |
796 | ||
f2cf24a1 RS |
797 | static int sof_get_control_data(struct snd_soc_component *scomp, |
798 | struct snd_soc_dapm_widget *widget, | |
799 | struct sof_widget_data *wdata, size_t *size) | |
800 | { | |
801 | const struct snd_kcontrol_new *kc; | |
b5cee8fe | 802 | struct sof_ipc_ctrl_data *cdata; |
f2cf24a1 RS |
803 | struct soc_mixer_control *sm; |
804 | struct soc_bytes_ext *sbe; | |
805 | struct soc_enum *se; | |
806 | int i; | |
807 | ||
808 | *size = 0; | |
809 | ||
810 | for (i = 0; i < widget->num_kcontrols; i++) { | |
811 | kc = &widget->kcontrol_news[i]; | |
812 | ||
813 | switch (widget->dobj.widget.kcontrol_type[i]) { | |
814 | case SND_SOC_TPLG_TYPE_MIXER: | |
815 | sm = (struct soc_mixer_control *)kc->private_value; | |
816 | wdata[i].control = sm->dobj.private; | |
817 | break; | |
818 | case SND_SOC_TPLG_TYPE_BYTES: | |
819 | sbe = (struct soc_bytes_ext *)kc->private_value; | |
820 | wdata[i].control = sbe->dobj.private; | |
821 | break; | |
822 | case SND_SOC_TPLG_TYPE_ENUM: | |
823 | se = (struct soc_enum *)kc->private_value; | |
824 | wdata[i].control = se->dobj.private; | |
825 | break; | |
826 | default: | |
827 | dev_err(scomp->dev, "Unknown kcontrol type %u in widget %s\n", | |
828 | widget->dobj.widget.kcontrol_type[i], widget->name); | |
829 | return -EINVAL; | |
830 | } | |
831 | ||
832 | if (!wdata[i].control) { | |
833 | dev_err(scomp->dev, "No scontrol for widget %s\n", widget->name); | |
834 | return -EINVAL; | |
835 | } | |
836 | ||
b5cee8fe | 837 | cdata = wdata[i].control->ipc_control_data; |
f2cf24a1 | 838 | |
a962890a PU |
839 | if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES) { |
840 | /* make sure data is valid - data can be updated at runtime */ | |
841 | if (cdata->data->magic != SOF_ABI_MAGIC) | |
842 | return -EINVAL; | |
843 | ||
844 | wdata[i].pdata = cdata->data->data; | |
845 | wdata[i].pdata_size = cdata->data->size; | |
846 | } else { | |
847 | /* points to the control data union */ | |
848 | wdata[i].pdata = cdata->chanv; | |
849 | /* | |
850 | * wdata[i].control->size is calculated with struct_size | |
851 | * and includes the size of struct sof_ipc_ctrl_data | |
852 | */ | |
853 | wdata[i].pdata_size = wdata[i].control->size - | |
854 | sizeof(struct sof_ipc_ctrl_data); | |
855 | } | |
f2cf24a1 | 856 | |
a962890a | 857 | *size += wdata[i].pdata_size; |
f2cf24a1 RS |
858 | |
859 | /* get data type */ | |
b5cee8fe | 860 | switch (cdata->cmd) { |
f2cf24a1 RS |
861 | case SOF_CTRL_CMD_VOLUME: |
862 | case SOF_CTRL_CMD_ENUM: | |
863 | case SOF_CTRL_CMD_SWITCH: | |
864 | wdata[i].ipc_cmd = SOF_IPC_COMP_SET_VALUE; | |
865 | wdata[i].ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; | |
866 | break; | |
867 | case SOF_CTRL_CMD_BINARY: | |
868 | wdata[i].ipc_cmd = SOF_IPC_COMP_SET_DATA; | |
869 | wdata[i].ctrl_type = SOF_CTRL_TYPE_DATA_SET; | |
870 | break; | |
871 | default: | |
872 | break; | |
873 | } | |
874 | } | |
875 | ||
876 | return 0; | |
877 | } | |
878 | ||
879 | static int sof_process_load(struct snd_soc_component *scomp, | |
880 | struct snd_sof_widget *swidget, int type) | |
881 | { | |
882 | struct snd_soc_dapm_widget *widget = swidget->widget; | |
883 | struct sof_ipc_comp_process *process; | |
884 | struct sof_widget_data *wdata = NULL; | |
885 | size_t ipc_data_size = 0; | |
886 | size_t ipc_size; | |
887 | int offset = 0; | |
888 | int ret; | |
889 | int i; | |
890 | ||
891 | /* allocate struct for widget control data sizes and types */ | |
892 | if (widget->num_kcontrols) { | |
893 | wdata = kcalloc(widget->num_kcontrols, sizeof(*wdata), GFP_KERNEL); | |
894 | if (!wdata) | |
895 | return -ENOMEM; | |
896 | ||
897 | /* get possible component controls and get size of all pdata */ | |
898 | ret = sof_get_control_data(scomp, widget, wdata, &ipc_data_size); | |
899 | if (ret < 0) | |
900 | goto out; | |
901 | } | |
902 | ||
903 | ipc_size = sizeof(struct sof_ipc_comp_process) + ipc_data_size; | |
904 | ||
905 | /* we are exceeding max ipc size, config needs to be sent separately */ | |
906 | if (ipc_size > SOF_IPC_MSG_MAX_SIZE) { | |
907 | ipc_size -= ipc_data_size; | |
908 | ipc_data_size = 0; | |
909 | } | |
910 | ||
911 | process = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); | |
912 | if (!process) { | |
913 | ret = -ENOMEM; | |
914 | goto out; | |
915 | } | |
916 | ||
917 | swidget->private = process; | |
918 | ||
919 | /* configure iir IPC message */ | |
920 | process->comp.type = type; | |
921 | process->config.hdr.size = sizeof(process->config); | |
922 | ||
923 | /* parse one set of comp tokens */ | |
924 | ret = sof_update_ipc_object(scomp, &process->config, SOF_COMP_TOKENS, | |
925 | swidget->tuples, swidget->num_tuples, | |
926 | sizeof(process->config), 1); | |
927 | if (ret < 0) | |
928 | goto err; | |
929 | ||
930 | dev_dbg(scomp->dev, "loaded process %s\n", swidget->widget->name); | |
931 | sof_dbg_comp_config(scomp, &process->config); | |
932 | ||
933 | /* | |
934 | * found private data in control, so copy it. | |
935 | * get possible component controls - get size of all pdata, | |
936 | * then memcpy with headers | |
937 | */ | |
938 | if (ipc_data_size) { | |
939 | for (i = 0; i < widget->num_kcontrols; i++) { | |
a962890a PU |
940 | if (!wdata[i].pdata_size) |
941 | continue; | |
942 | ||
943 | memcpy(&process->data[offset], wdata[i].pdata, | |
944 | wdata[i].pdata_size); | |
945 | offset += wdata[i].pdata_size; | |
f2cf24a1 RS |
946 | } |
947 | } | |
948 | ||
949 | process->size = ipc_data_size; | |
950 | ||
951 | kfree(wdata); | |
952 | ||
953 | return 0; | |
954 | err: | |
955 | kfree(swidget->private); | |
956 | swidget->private = NULL; | |
957 | out: | |
958 | kfree(wdata); | |
959 | return ret; | |
960 | } | |
961 | ||
962 | static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type) | |
963 | { | |
964 | int i; | |
965 | ||
966 | for (i = 0; i < ARRAY_SIZE(sof_process); i++) { | |
967 | if (sof_process[i].type == type) | |
968 | return sof_process[i].comp_type; | |
969 | } | |
970 | ||
971 | return SOF_COMP_NONE; | |
972 | } | |
973 | ||
974 | /* | |
975 | * Processing Component Topology - can be "effect", "codec", or general | |
976 | * "processing". | |
977 | */ | |
978 | ||
979 | static int sof_widget_update_ipc_comp_process(struct snd_sof_widget *swidget) | |
980 | { | |
981 | struct snd_soc_component *scomp = swidget->scomp; | |
982 | struct sof_ipc_comp_process config; | |
983 | int ret; | |
984 | ||
985 | memset(&config, 0, sizeof(config)); | |
986 | config.comp.core = swidget->core; | |
987 | ||
988 | /* parse one set of process tokens */ | |
989 | ret = sof_update_ipc_object(scomp, &config, SOF_PROCESS_TOKENS, swidget->tuples, | |
990 | swidget->num_tuples, sizeof(config), 1); | |
991 | if (ret < 0) | |
992 | return ret; | |
993 | ||
994 | /* now load process specific data and send IPC */ | |
995 | return sof_process_load(scomp, swidget, find_process_comp_type(config.type)); | |
996 | } | |
997 | ||
909dadf2 RS |
998 | static int sof_link_hda_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, |
999 | struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) | |
1000 | { | |
1001 | struct sof_dai_private_data *private = dai->private; | |
1002 | u32 size = sizeof(*config); | |
1003 | int ret; | |
1004 | ||
1005 | /* init IPC */ | |
1006 | memset(&config->hda, 0, sizeof(config->hda)); | |
1007 | config->hdr.size = size; | |
1008 | ||
1009 | /* parse one set of HDA tokens */ | |
1010 | ret = sof_update_ipc_object(scomp, &config->hda, SOF_HDA_TOKENS, slink->tuples, | |
1011 | slink->num_tuples, size, 1); | |
1012 | if (ret < 0) | |
1013 | return ret; | |
1014 | ||
1015 | dev_dbg(scomp->dev, "HDA config rate %d channels %d\n", | |
1016 | config->hda.rate, config->hda.channels); | |
1017 | ||
1018 | config->hda.link_dma_ch = DMA_CHAN_INVALID; | |
1019 | ||
1020 | dai->number_configs = 1; | |
1021 | dai->current_config = 0; | |
1022 | private->dai_config = kmemdup(config, size, GFP_KERNEL); | |
1023 | if (!private->dai_config) | |
1024 | return -ENOMEM; | |
1025 | ||
1026 | return 0; | |
1027 | } | |
1028 | ||
1029 | static void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config, | |
1030 | struct sof_ipc_dai_config *config) | |
1031 | { | |
1032 | /* clock directions wrt codec */ | |
1033 | config->format &= ~SOF_DAI_FMT_CLOCK_PROVIDER_MASK; | |
1034 | if (hw_config->bclk_provider == SND_SOC_TPLG_BCLK_CP) { | |
1035 | /* codec is bclk provider */ | |
1036 | if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP) | |
1037 | config->format |= SOF_DAI_FMT_CBP_CFP; | |
1038 | else | |
1039 | config->format |= SOF_DAI_FMT_CBP_CFC; | |
1040 | } else { | |
1041 | /* codec is bclk consumer */ | |
1042 | if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP) | |
1043 | config->format |= SOF_DAI_FMT_CBC_CFP; | |
1044 | else | |
1045 | config->format |= SOF_DAI_FMT_CBC_CFC; | |
1046 | } | |
1047 | ||
1048 | /* inverted clocks ? */ | |
1049 | config->format &= ~SOF_DAI_FMT_INV_MASK; | |
1050 | if (hw_config->invert_bclk) { | |
1051 | if (hw_config->invert_fsync) | |
1052 | config->format |= SOF_DAI_FMT_IB_IF; | |
1053 | else | |
1054 | config->format |= SOF_DAI_FMT_IB_NF; | |
1055 | } else { | |
1056 | if (hw_config->invert_fsync) | |
1057 | config->format |= SOF_DAI_FMT_NB_IF; | |
1058 | else | |
1059 | config->format |= SOF_DAI_FMT_NB_NF; | |
1060 | } | |
1061 | } | |
1062 | ||
1063 | static int sof_link_sai_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, | |
1064 | struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) | |
1065 | { | |
1066 | struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; | |
1067 | struct sof_dai_private_data *private = dai->private; | |
1068 | u32 size = sizeof(*config); | |
1069 | int ret; | |
1070 | ||
1071 | /* handle master/slave and inverted clocks */ | |
1072 | sof_dai_set_format(hw_config, config); | |
1073 | ||
1074 | /* init IPC */ | |
1075 | memset(&config->sai, 0, sizeof(config->sai)); | |
1076 | config->hdr.size = size; | |
1077 | ||
1078 | /* parse one set of SAI tokens */ | |
1079 | ret = sof_update_ipc_object(scomp, &config->sai, SOF_SAI_TOKENS, slink->tuples, | |
1080 | slink->num_tuples, size, 1); | |
1081 | if (ret < 0) | |
1082 | return ret; | |
1083 | ||
1084 | config->sai.mclk_rate = le32_to_cpu(hw_config->mclk_rate); | |
1085 | config->sai.bclk_rate = le32_to_cpu(hw_config->bclk_rate); | |
1086 | config->sai.fsync_rate = le32_to_cpu(hw_config->fsync_rate); | |
1087 | config->sai.mclk_direction = hw_config->mclk_direction; | |
1088 | ||
1089 | config->sai.tdm_slots = le32_to_cpu(hw_config->tdm_slots); | |
1090 | config->sai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); | |
1091 | config->sai.rx_slots = le32_to_cpu(hw_config->rx_slots); | |
1092 | config->sai.tx_slots = le32_to_cpu(hw_config->tx_slots); | |
1093 | ||
1094 | dev_info(scomp->dev, | |
1095 | "tplg: config SAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n", | |
1096 | config->dai_index, config->format, | |
1097 | config->sai.mclk_rate, config->sai.tdm_slot_width, | |
1098 | config->sai.tdm_slots, config->sai.mclk_id); | |
1099 | ||
1100 | if (config->sai.tdm_slots < 1 || config->sai.tdm_slots > 8) { | |
1101 | dev_err(scomp->dev, "Invalid channel count for SAI%d\n", config->dai_index); | |
1102 | return -EINVAL; | |
1103 | } | |
1104 | ||
1105 | dai->number_configs = 1; | |
1106 | dai->current_config = 0; | |
1107 | private->dai_config = kmemdup(config, size, GFP_KERNEL); | |
1108 | if (!private->dai_config) | |
1109 | return -ENOMEM; | |
1110 | ||
1111 | return 0; | |
1112 | } | |
1113 | ||
1114 | static int sof_link_esai_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, | |
1115 | struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) | |
1116 | { | |
1117 | struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; | |
1118 | struct sof_dai_private_data *private = dai->private; | |
1119 | u32 size = sizeof(*config); | |
1120 | int ret; | |
1121 | ||
1122 | /* handle master/slave and inverted clocks */ | |
1123 | sof_dai_set_format(hw_config, config); | |
1124 | ||
1125 | /* init IPC */ | |
1126 | memset(&config->esai, 0, sizeof(config->esai)); | |
1127 | config->hdr.size = size; | |
1128 | ||
1129 | /* parse one set of ESAI tokens */ | |
1130 | ret = sof_update_ipc_object(scomp, &config->esai, SOF_ESAI_TOKENS, slink->tuples, | |
1131 | slink->num_tuples, size, 1); | |
1132 | if (ret < 0) | |
1133 | return ret; | |
1134 | ||
1135 | config->esai.mclk_rate = le32_to_cpu(hw_config->mclk_rate); | |
1136 | config->esai.bclk_rate = le32_to_cpu(hw_config->bclk_rate); | |
1137 | config->esai.fsync_rate = le32_to_cpu(hw_config->fsync_rate); | |
1138 | config->esai.mclk_direction = hw_config->mclk_direction; | |
1139 | config->esai.tdm_slots = le32_to_cpu(hw_config->tdm_slots); | |
1140 | config->esai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); | |
1141 | config->esai.rx_slots = le32_to_cpu(hw_config->rx_slots); | |
1142 | config->esai.tx_slots = le32_to_cpu(hw_config->tx_slots); | |
1143 | ||
1144 | dev_info(scomp->dev, | |
1145 | "tplg: config ESAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n", | |
1146 | config->dai_index, config->format, | |
1147 | config->esai.mclk_rate, config->esai.tdm_slot_width, | |
1148 | config->esai.tdm_slots, config->esai.mclk_id); | |
1149 | ||
1150 | if (config->esai.tdm_slots < 1 || config->esai.tdm_slots > 8) { | |
1151 | dev_err(scomp->dev, "Invalid channel count for ESAI%d\n", config->dai_index); | |
1152 | return -EINVAL; | |
1153 | } | |
1154 | ||
1155 | dai->number_configs = 1; | |
1156 | dai->current_config = 0; | |
1157 | private->dai_config = kmemdup(config, size, GFP_KERNEL); | |
1158 | if (!private->dai_config) | |
1159 | return -ENOMEM; | |
1160 | ||
1161 | return 0; | |
1162 | } | |
1163 | ||
89ef4208 DB |
1164 | static int sof_link_micfil_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, |
1165 | struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) | |
1166 | { | |
1167 | struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; | |
1168 | struct sof_dai_private_data *private = dai->private; | |
1169 | u32 size = sizeof(*config); | |
1170 | int ret; | |
1171 | ||
1172 | /* handle master/slave and inverted clocks */ | |
1173 | sof_dai_set_format(hw_config, config); | |
1174 | ||
1175 | config->hdr.size = size; | |
1176 | ||
1177 | /* parse the required set of MICFIL PDM tokens based on num_hw_cfgs */ | |
1178 | ret = sof_update_ipc_object(scomp, &config->micfil, SOF_MICFIL_TOKENS, slink->tuples, | |
1179 | slink->num_tuples, size, slink->num_hw_configs); | |
1180 | if (ret < 0) | |
1181 | return ret; | |
1182 | ||
1183 | dev_info(scomp->dev, "MICFIL PDM config dai_index %d channel %d rate %d\n", | |
1184 | config->dai_index, config->micfil.pdm_ch, config->micfil.pdm_rate); | |
1185 | ||
1186 | dai->number_configs = 1; | |
1187 | dai->current_config = 0; | |
1188 | private->dai_config = kmemdup(config, size, GFP_KERNEL); | |
1189 | if (!private->dai_config) | |
1190 | return -ENOMEM; | |
1191 | ||
1192 | return 0; | |
1193 | } | |
1194 | ||
909dadf2 RS |
1195 | static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, |
1196 | struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) | |
1197 | { | |
1198 | struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; | |
1199 | struct sof_dai_private_data *private = dai->private; | |
1200 | u32 size = sizeof(*config); | |
689614ce | 1201 | int ret; |
909dadf2 RS |
1202 | |
1203 | /* handle master/slave and inverted clocks */ | |
1204 | sof_dai_set_format(hw_config, config); | |
1205 | ||
909dadf2 RS |
1206 | config->hdr.size = size; |
1207 | ||
689614ce AKP |
1208 | /* parse the required set of ACPDMIC tokens based on num_hw_cfgs */ |
1209 | ret = sof_update_ipc_object(scomp, &config->acpdmic, SOF_ACPDMIC_TOKENS, slink->tuples, | |
1210 | slink->num_tuples, size, slink->num_hw_configs); | |
1211 | if (ret < 0) | |
1212 | return ret; | |
909dadf2 RS |
1213 | |
1214 | dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n", | |
689614ce AKP |
1215 | config->dai_index, config->acpdmic.pdm_ch, |
1216 | config->acpdmic.pdm_rate); | |
909dadf2 RS |
1217 | |
1218 | dai->number_configs = 1; | |
1219 | dai->current_config = 0; | |
1220 | private->dai_config = kmemdup(config, size, GFP_KERNEL); | |
1221 | if (!private->dai_config) | |
1222 | return -ENOMEM; | |
1223 | ||
1224 | return 0; | |
1225 | } | |
1226 | ||
1227 | static int sof_link_acp_bt_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, | |
1228 | struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) | |
1229 | { | |
1230 | struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; | |
1231 | struct sof_dai_private_data *private = dai->private; | |
1232 | u32 size = sizeof(*config); | |
3953de2d | 1233 | int ret; |
909dadf2 RS |
1234 | |
1235 | /* handle master/slave and inverted clocks */ | |
1236 | sof_dai_set_format(hw_config, config); | |
1237 | ||
1238 | /* init IPC */ | |
1239 | memset(&config->acpbt, 0, sizeof(config->acpbt)); | |
1240 | config->hdr.size = size; | |
1241 | ||
3953de2d VPP |
1242 | ret = sof_update_ipc_object(scomp, &config->acpbt, SOF_ACPI2S_TOKENS, slink->tuples, |
1243 | slink->num_tuples, size, slink->num_hw_configs); | |
1244 | if (ret < 0) | |
1245 | return ret; | |
909dadf2 | 1246 | |
3953de2d | 1247 | dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d tdm_mode %d\n", |
909dadf2 | 1248 | config->dai_index, config->acpbt.tdm_slots, |
3953de2d | 1249 | config->acpbt.fsync_rate, config->acpbt.tdm_mode); |
909dadf2 RS |
1250 | |
1251 | dai->number_configs = 1; | |
1252 | dai->current_config = 0; | |
1253 | private->dai_config = kmemdup(config, size, GFP_KERNEL); | |
1254 | if (!private->dai_config) | |
1255 | return -ENOMEM; | |
1256 | ||
1257 | return 0; | |
1258 | } | |
1259 | ||
1260 | static int sof_link_acp_sp_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, | |
1261 | struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) | |
1262 | { | |
1263 | struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; | |
1264 | struct sof_dai_private_data *private = dai->private; | |
1265 | u32 size = sizeof(*config); | |
75af4199 | 1266 | int ret; |
909dadf2 RS |
1267 | |
1268 | /* handle master/slave and inverted clocks */ | |
1269 | sof_dai_set_format(hw_config, config); | |
1270 | ||
1271 | /* init IPC */ | |
1272 | memset(&config->acpsp, 0, sizeof(config->acpsp)); | |
1273 | config->hdr.size = size; | |
1274 | ||
75af4199 R |
1275 | ret = sof_update_ipc_object(scomp, &config->acpsp, SOF_ACPI2S_TOKENS, slink->tuples, |
1276 | slink->num_tuples, size, slink->num_hw_configs); | |
1277 | if (ret < 0) | |
1278 | return ret; | |
909dadf2 | 1279 | |
75af4199 R |
1280 | |
1281 | dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d tdm_mode %d\n", | |
909dadf2 | 1282 | config->dai_index, config->acpsp.tdm_slots, |
75af4199 | 1283 | config->acpsp.fsync_rate, config->acpsp.tdm_mode); |
909dadf2 RS |
1284 | |
1285 | dai->number_configs = 1; | |
1286 | dai->current_config = 0; | |
1287 | private->dai_config = kmemdup(config, size, GFP_KERNEL); | |
1288 | if (!private->dai_config) | |
1289 | return -ENOMEM; | |
1290 | ||
1291 | return 0; | |
1292 | } | |
1293 | ||
ed2562c6 R |
1294 | static int sof_link_acp_hs_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, |
1295 | struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) | |
1296 | { | |
1297 | struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; | |
1298 | struct sof_dai_private_data *private = dai->private; | |
1299 | u32 size = sizeof(*config); | |
75af4199 | 1300 | int ret; |
ed2562c6 R |
1301 | |
1302 | /* Configures the DAI hardware format and inverted clocks */ | |
1303 | sof_dai_set_format(hw_config, config); | |
1304 | ||
1305 | /* init IPC */ | |
1306 | memset(&config->acphs, 0, sizeof(config->acphs)); | |
1307 | config->hdr.size = size; | |
1308 | ||
75af4199 R |
1309 | ret = sof_update_ipc_object(scomp, &config->acphs, SOF_ACPI2S_TOKENS, slink->tuples, |
1310 | slink->num_tuples, size, slink->num_hw_configs); | |
1311 | if (ret < 0) | |
1312 | return ret; | |
ed2562c6 | 1313 | |
75af4199 | 1314 | dev_info(scomp->dev, "ACP_HS config ACP%d channel %d rate %d tdm_mode %d\n", |
ed2562c6 | 1315 | config->dai_index, config->acphs.tdm_slots, |
75af4199 | 1316 | config->acphs.fsync_rate, config->acphs.tdm_mode); |
ed2562c6 R |
1317 | |
1318 | dai->number_configs = 1; | |
1319 | dai->current_config = 0; | |
1320 | private->dai_config = kmemdup(config, size, GFP_KERNEL); | |
1321 | if (!private->dai_config) | |
1322 | return -ENOMEM; | |
1323 | ||
1324 | return 0; | |
1325 | } | |
1326 | ||
14d89e55 VM |
1327 | static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, |
1328 | struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) | |
1329 | { | |
1330 | struct sof_dai_private_data *private = dai->private; | |
1331 | u32 size = sizeof(*config); | |
1332 | int ret; | |
1333 | ||
1334 | /* parse the required set of ACP_SDW tokens based on num_hw_cfgs */ | |
1335 | ret = sof_update_ipc_object(scomp, &config->acp_sdw, SOF_ACP_SDW_TOKENS, slink->tuples, | |
1336 | slink->num_tuples, size, slink->num_hw_configs); | |
1337 | if (ret < 0) | |
1338 | return ret; | |
1339 | ||
1340 | /* init IPC */ | |
1341 | config->hdr.size = size; | |
1342 | dev_dbg(scomp->dev, "ACP SDW config rate %d channels %d\n", | |
1343 | config->acp_sdw.rate, config->acp_sdw.channels); | |
1344 | ||
1345 | /* set config for all DAI's with name matching the link name */ | |
1346 | dai->number_configs = 1; | |
1347 | dai->current_config = 0; | |
1348 | private->dai_config = kmemdup(config, size, GFP_KERNEL); | |
1349 | if (!private->dai_config) | |
1350 | return -ENOMEM; | |
1351 | ||
1352 | return 0; | |
1353 | } | |
1354 | ||
909dadf2 RS |
1355 | static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, |
1356 | struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) | |
1357 | { | |
1358 | struct sof_dai_private_data *private = dai->private; | |
1359 | u32 size = sizeof(*config); | |
1360 | int ret; | |
1361 | ||
1362 | config->hdr.size = size; | |
1363 | ||
1364 | /* parse the required set of AFE tokens based on num_hw_cfgs */ | |
1365 | ret = sof_update_ipc_object(scomp, &config->afe, SOF_AFE_TOKENS, slink->tuples, | |
1366 | slink->num_tuples, size, slink->num_hw_configs); | |
1367 | if (ret < 0) | |
1368 | return ret; | |
1369 | ||
1370 | dev_dbg(scomp->dev, "AFE config rate %d channels %d format:%d\n", | |
1371 | config->afe.rate, config->afe.channels, config->afe.format); | |
1372 | ||
1373 | config->afe.stream_id = DMA_CHAN_INVALID; | |
1374 | ||
1375 | dai->number_configs = 1; | |
1376 | dai->current_config = 0; | |
1377 | private->dai_config = kmemdup(config, size, GFP_KERNEL); | |
1378 | if (!private->dai_config) | |
1379 | return -ENOMEM; | |
1380 | ||
1381 | return 0; | |
1382 | } | |
1383 | ||
1384 | static int sof_link_ssp_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, | |
1385 | struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) | |
1386 | { | |
d136949d | 1387 | struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); |
909dadf2 RS |
1388 | struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; |
1389 | struct sof_dai_private_data *private = dai->private; | |
1390 | u32 size = sizeof(*config); | |
1391 | int current_config = 0; | |
1392 | int i, ret; | |
1393 | ||
1394 | /* | |
1395 | * Parse common data, we should have 1 common data per hw_config. | |
1396 | */ | |
1397 | ret = sof_update_ipc_object(scomp, &config->ssp, SOF_SSP_TOKENS, slink->tuples, | |
1398 | slink->num_tuples, size, slink->num_hw_configs); | |
1399 | if (ret < 0) | |
1400 | return ret; | |
1401 | ||
1402 | /* process all possible hw configs */ | |
1403 | for (i = 0; i < slink->num_hw_configs; i++) { | |
1404 | if (le32_to_cpu(hw_config[i].id) == slink->default_hw_cfg_id) | |
1405 | current_config = i; | |
1406 | ||
1407 | /* handle master/slave and inverted clocks */ | |
1408 | sof_dai_set_format(&hw_config[i], &config[i]); | |
1409 | ||
1410 | config[i].hdr.size = size; | |
1411 | ||
d136949d PLB |
1412 | if (sdev->mclk_id_override) { |
1413 | dev_dbg(scomp->dev, "tplg: overriding topology mclk_id %d by quirk %d\n", | |
1414 | config[i].ssp.mclk_id, sdev->mclk_id_quirk); | |
1415 | config[i].ssp.mclk_id = sdev->mclk_id_quirk; | |
1416 | } | |
1417 | ||
909dadf2 RS |
1418 | /* copy differentiating hw configs to ipc structs */ |
1419 | config[i].ssp.mclk_rate = le32_to_cpu(hw_config[i].mclk_rate); | |
1420 | config[i].ssp.bclk_rate = le32_to_cpu(hw_config[i].bclk_rate); | |
1421 | config[i].ssp.fsync_rate = le32_to_cpu(hw_config[i].fsync_rate); | |
1422 | config[i].ssp.tdm_slots = le32_to_cpu(hw_config[i].tdm_slots); | |
1423 | config[i].ssp.tdm_slot_width = le32_to_cpu(hw_config[i].tdm_slot_width); | |
1424 | config[i].ssp.mclk_direction = hw_config[i].mclk_direction; | |
1425 | config[i].ssp.rx_slots = le32_to_cpu(hw_config[i].rx_slots); | |
1426 | config[i].ssp.tx_slots = le32_to_cpu(hw_config[i].tx_slots); | |
1427 | ||
1428 | dev_dbg(scomp->dev, "tplg: config SSP%d fmt %#x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d clks_control %#x\n", | |
1429 | config[i].dai_index, config[i].format, | |
1430 | config[i].ssp.mclk_rate, config[i].ssp.bclk_rate, | |
1431 | config[i].ssp.fsync_rate, config[i].ssp.sample_valid_bits, | |
1432 | config[i].ssp.tdm_slot_width, config[i].ssp.tdm_slots, | |
1433 | config[i].ssp.mclk_id, config[i].ssp.quirks, config[i].ssp.clks_control); | |
1434 | ||
1435 | /* validate SSP fsync rate and channel count */ | |
1436 | if (config[i].ssp.fsync_rate < 8000 || config[i].ssp.fsync_rate > 192000) { | |
1437 | dev_err(scomp->dev, "Invalid fsync rate for SSP%d\n", config[i].dai_index); | |
1438 | return -EINVAL; | |
1439 | } | |
1440 | ||
1441 | if (config[i].ssp.tdm_slots < 1 || config[i].ssp.tdm_slots > 8) { | |
1442 | dev_err(scomp->dev, "Invalid channel count for SSP%d\n", | |
1443 | config[i].dai_index); | |
1444 | return -EINVAL; | |
1445 | } | |
1446 | } | |
1447 | ||
1448 | dai->number_configs = slink->num_hw_configs; | |
1449 | dai->current_config = current_config; | |
1450 | private->dai_config = kmemdup(config, size * slink->num_hw_configs, GFP_KERNEL); | |
1451 | if (!private->dai_config) | |
1452 | return -ENOMEM; | |
1453 | ||
1454 | return 0; | |
1455 | } | |
1456 | ||
1457 | static int sof_link_dmic_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, | |
1458 | struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) | |
1459 | { | |
1460 | struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); | |
1461 | struct sof_dai_private_data *private = dai->private; | |
1462 | struct sof_ipc_fw_ready *ready = &sdev->fw_ready; | |
1463 | struct sof_ipc_fw_version *v = &ready->version; | |
1464 | size_t size = sizeof(*config); | |
1465 | int i, ret; | |
1466 | ||
1467 | /* Ensure the entire DMIC config struct is zeros */ | |
1468 | memset(&config->dmic, 0, sizeof(config->dmic)); | |
1469 | ||
1470 | /* parse the required set of DMIC tokens based on num_hw_cfgs */ | |
1471 | ret = sof_update_ipc_object(scomp, &config->dmic, SOF_DMIC_TOKENS, slink->tuples, | |
1472 | slink->num_tuples, size, slink->num_hw_configs); | |
1473 | if (ret < 0) | |
1474 | return ret; | |
1475 | ||
1476 | /* parse the required set of DMIC PDM tokens based on number of active PDM's */ | |
1477 | ret = sof_update_ipc_object(scomp, &config->dmic.pdm[0], SOF_DMIC_PDM_TOKENS, | |
1478 | slink->tuples, slink->num_tuples, | |
1479 | sizeof(struct sof_ipc_dai_dmic_pdm_ctrl), | |
1480 | config->dmic.num_pdm_active); | |
1481 | if (ret < 0) | |
1482 | return ret; | |
1483 | ||
1484 | /* set IPC header size */ | |
1485 | config->hdr.size = size; | |
1486 | ||
1487 | /* debug messages */ | |
1488 | dev_dbg(scomp->dev, "tplg: config DMIC%d driver version %d\n", | |
1489 | config->dai_index, config->dmic.driver_ipc_version); | |
1490 | dev_dbg(scomp->dev, "pdmclk_min %d pdm_clkmax %d duty_min %d\n", | |
1491 | config->dmic.pdmclk_min, config->dmic.pdmclk_max, | |
1492 | config->dmic.duty_min); | |
1493 | dev_dbg(scomp->dev, "duty_max %d fifo_fs %d num_pdms active %d\n", | |
1494 | config->dmic.duty_max, config->dmic.fifo_fs, | |
1495 | config->dmic.num_pdm_active); | |
1496 | dev_dbg(scomp->dev, "fifo word length %d\n", config->dmic.fifo_bits); | |
1497 | ||
1498 | for (i = 0; i < config->dmic.num_pdm_active; i++) { | |
1499 | dev_dbg(scomp->dev, "pdm %d mic a %d mic b %d\n", | |
1500 | config->dmic.pdm[i].id, | |
1501 | config->dmic.pdm[i].enable_mic_a, | |
1502 | config->dmic.pdm[i].enable_mic_b); | |
1503 | dev_dbg(scomp->dev, "pdm %d polarity a %d polarity b %d\n", | |
1504 | config->dmic.pdm[i].id, | |
1505 | config->dmic.pdm[i].polarity_mic_a, | |
1506 | config->dmic.pdm[i].polarity_mic_b); | |
1507 | dev_dbg(scomp->dev, "pdm %d clk_edge %d skew %d\n", | |
1508 | config->dmic.pdm[i].id, | |
1509 | config->dmic.pdm[i].clk_edge, | |
1510 | config->dmic.pdm[i].skew); | |
1511 | } | |
1512 | ||
1513 | /* | |
1514 | * this takes care of backwards compatible handling of fifo_bits_b. | |
1515 | * It is deprecated since firmware ABI version 3.0.1. | |
1516 | */ | |
1517 | if (SOF_ABI_VER(v->major, v->minor, v->micro) < SOF_ABI_VER(3, 0, 1)) | |
1518 | config->dmic.fifo_bits_b = config->dmic.fifo_bits; | |
1519 | ||
1520 | dai->number_configs = 1; | |
1521 | dai->current_config = 0; | |
1522 | private->dai_config = kmemdup(config, size, GFP_KERNEL); | |
1523 | if (!private->dai_config) | |
1524 | return -ENOMEM; | |
1525 | ||
1526 | return 0; | |
1527 | } | |
1528 | ||
1529 | static int sof_link_alh_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, | |
1530 | struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) | |
1531 | { | |
1532 | struct sof_dai_private_data *private = dai->private; | |
1533 | u32 size = sizeof(*config); | |
1534 | int ret; | |
1535 | ||
1536 | /* parse the required set of ALH tokens based on num_hw_cfgs */ | |
1537 | ret = sof_update_ipc_object(scomp, &config->alh, SOF_ALH_TOKENS, slink->tuples, | |
1538 | slink->num_tuples, size, slink->num_hw_configs); | |
1539 | if (ret < 0) | |
1540 | return ret; | |
1541 | ||
1542 | /* init IPC */ | |
1543 | config->hdr.size = size; | |
1544 | ||
1545 | /* set config for all DAI's with name matching the link name */ | |
1546 | dai->number_configs = 1; | |
1547 | dai->current_config = 0; | |
1548 | private->dai_config = kmemdup(config, size, GFP_KERNEL); | |
1549 | if (!private->dai_config) | |
1550 | return -ENOMEM; | |
1551 | ||
1552 | return 0; | |
1553 | } | |
1554 | ||
1555 | static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) | |
1556 | { | |
1557 | struct snd_soc_component *scomp = swidget->scomp; | |
1558 | struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); | |
1559 | struct snd_sof_dai *dai = swidget->private; | |
1560 | struct sof_dai_private_data *private; | |
1561 | struct sof_ipc_comp_dai *comp_dai; | |
1562 | size_t ipc_size = sizeof(*comp_dai); | |
1563 | struct sof_ipc_dai_config *config; | |
1564 | struct snd_sof_dai_link *slink; | |
1565 | int ret; | |
1566 | ||
1567 | private = kzalloc(sizeof(*private), GFP_KERNEL); | |
1568 | if (!private) | |
1569 | return -ENOMEM; | |
1570 | ||
1571 | dai->private = private; | |
1572 | ||
1573 | private->comp_dai = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); | |
1574 | if (!private->comp_dai) { | |
1575 | ret = -ENOMEM; | |
1576 | goto free; | |
1577 | } | |
1578 | ||
1579 | /* configure dai IPC message */ | |
1580 | comp_dai = private->comp_dai; | |
1581 | comp_dai->comp.type = SOF_COMP_DAI; | |
1582 | comp_dai->config.hdr.size = sizeof(comp_dai->config); | |
1583 | ||
1584 | /* parse one set of DAI tokens */ | |
1585 | ret = sof_update_ipc_object(scomp, comp_dai, SOF_DAI_TOKENS, swidget->tuples, | |
1586 | swidget->num_tuples, sizeof(*comp_dai), 1); | |
1587 | if (ret < 0) | |
1588 | goto free; | |
1589 | ||
1590 | /* update comp_tokens */ | |
1591 | ret = sof_update_ipc_object(scomp, &comp_dai->config, SOF_COMP_TOKENS, | |
1592 | swidget->tuples, swidget->num_tuples, | |
1593 | sizeof(comp_dai->config), 1); | |
1594 | if (ret < 0) | |
1595 | goto free; | |
1596 | ||
f132dc02 PLB |
1597 | dev_dbg(scomp->dev, "dai %s: type %d index %d\n", |
1598 | swidget->widget->name, comp_dai->type, comp_dai->dai_index); | |
909dadf2 RS |
1599 | sof_dbg_comp_config(scomp, &comp_dai->config); |
1600 | ||
1601 | /* now update DAI config */ | |
1602 | list_for_each_entry(slink, &sdev->dai_link_list, list) { | |
1603 | struct sof_ipc_dai_config common_config; | |
1604 | int i; | |
1605 | ||
1606 | if (strcmp(slink->link->name, dai->name)) | |
1607 | continue; | |
1608 | ||
1609 | /* Reserve memory for all hw configs, eventually freed by widget */ | |
1610 | config = kcalloc(slink->num_hw_configs, sizeof(*config), GFP_KERNEL); | |
1611 | if (!config) { | |
1612 | ret = -ENOMEM; | |
1613 | goto free_comp; | |
1614 | } | |
1615 | ||
1616 | /* parse one set of DAI link tokens */ | |
1617 | ret = sof_update_ipc_object(scomp, &common_config, SOF_DAI_LINK_TOKENS, | |
1618 | slink->tuples, slink->num_tuples, | |
1619 | sizeof(common_config), 1); | |
1620 | if (ret < 0) | |
1621 | goto free_config; | |
1622 | ||
1623 | for (i = 0; i < slink->num_hw_configs; i++) { | |
1624 | config[i].hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; | |
1625 | config[i].format = le32_to_cpu(slink->hw_configs[i].fmt); | |
1626 | config[i].type = common_config.type; | |
1627 | config[i].dai_index = comp_dai->dai_index; | |
1628 | } | |
1629 | ||
1630 | switch (common_config.type) { | |
1631 | case SOF_DAI_INTEL_SSP: | |
1632 | ret = sof_link_ssp_load(scomp, slink, config, dai); | |
1633 | break; | |
1634 | case SOF_DAI_INTEL_DMIC: | |
1635 | ret = sof_link_dmic_load(scomp, slink, config, dai); | |
1636 | break; | |
1637 | case SOF_DAI_INTEL_HDA: | |
1638 | ret = sof_link_hda_load(scomp, slink, config, dai); | |
1639 | break; | |
1640 | case SOF_DAI_INTEL_ALH: | |
1641 | ret = sof_link_alh_load(scomp, slink, config, dai); | |
1642 | break; | |
1643 | case SOF_DAI_IMX_SAI: | |
1644 | ret = sof_link_sai_load(scomp, slink, config, dai); | |
1645 | break; | |
1646 | case SOF_DAI_IMX_ESAI: | |
1647 | ret = sof_link_esai_load(scomp, slink, config, dai); | |
1648 | break; | |
89ef4208 DB |
1649 | case SOF_DAI_IMX_MICFIL: |
1650 | ret = sof_link_micfil_load(scomp, slink, config, dai); | |
1651 | break; | |
909dadf2 RS |
1652 | case SOF_DAI_AMD_BT: |
1653 | ret = sof_link_acp_bt_load(scomp, slink, config, dai); | |
1654 | break; | |
1655 | case SOF_DAI_AMD_SP: | |
75af4199 | 1656 | case SOF_DAI_AMD_SP_VIRTUAL: |
909dadf2 RS |
1657 | ret = sof_link_acp_sp_load(scomp, slink, config, dai); |
1658 | break; | |
ed2562c6 | 1659 | case SOF_DAI_AMD_HS: |
75af4199 | 1660 | case SOF_DAI_AMD_HS_VIRTUAL: |
ed2562c6 R |
1661 | ret = sof_link_acp_hs_load(scomp, slink, config, dai); |
1662 | break; | |
909dadf2 RS |
1663 | case SOF_DAI_AMD_DMIC: |
1664 | ret = sof_link_acp_dmic_load(scomp, slink, config, dai); | |
1665 | break; | |
1666 | case SOF_DAI_MEDIATEK_AFE: | |
1667 | ret = sof_link_afe_load(scomp, slink, config, dai); | |
1668 | break; | |
14d89e55 VM |
1669 | case SOF_DAI_AMD_SDW: |
1670 | ret = sof_link_acp_sdw_load(scomp, slink, config, dai); | |
1671 | break; | |
909dadf2 RS |
1672 | default: |
1673 | break; | |
1674 | } | |
1675 | if (ret < 0) { | |
1676 | dev_err(scomp->dev, "failed to load config for dai %s\n", dai->name); | |
1677 | goto free_config; | |
1678 | } | |
1679 | ||
1680 | kfree(config); | |
1681 | } | |
1682 | ||
1683 | return 0; | |
1684 | free_config: | |
1685 | kfree(config); | |
1686 | free_comp: | |
1687 | kfree(comp_dai); | |
1688 | free: | |
1689 | kfree(private); | |
1690 | dai->private = NULL; | |
1691 | return ret; | |
1692 | } | |
1693 | ||
1694 | static void sof_ipc3_widget_free_comp_dai(struct snd_sof_widget *swidget) | |
1695 | { | |
1696 | switch (swidget->id) { | |
1697 | case snd_soc_dapm_dai_in: | |
1698 | case snd_soc_dapm_dai_out: | |
1699 | { | |
1700 | struct snd_sof_dai *dai = swidget->private; | |
1701 | struct sof_dai_private_data *dai_data; | |
1702 | ||
1703 | if (!dai) | |
1704 | return; | |
1705 | ||
1706 | dai_data = dai->private; | |
1707 | if (dai_data) { | |
1708 | kfree(dai_data->comp_dai); | |
1709 | kfree(dai_data->dai_config); | |
1710 | kfree(dai_data); | |
1711 | } | |
1712 | kfree(dai); | |
1713 | break; | |
1714 | } | |
1715 | default: | |
1716 | break; | |
1717 | } | |
1718 | } | |
1719 | ||
85ec8560 RS |
1720 | static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) |
1721 | { | |
1722 | struct sof_ipc_pipe_comp_connect connect; | |
85ec8560 RS |
1723 | int ret; |
1724 | ||
1725 | connect.hdr.size = sizeof(connect); | |
1726 | connect.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; | |
1727 | connect.source_id = sroute->src_widget->comp_id; | |
1728 | connect.sink_id = sroute->sink_widget->comp_id; | |
1729 | ||
1730 | dev_dbg(sdev->dev, "setting up route %s -> %s\n", | |
1731 | sroute->src_widget->widget->name, | |
1732 | sroute->sink_widget->widget->name); | |
1733 | ||
1734 | /* send ipc */ | |
367fd6ff | 1735 | ret = sof_ipc_tx_message_no_reply(sdev->ipc, &connect, sizeof(connect)); |
85ec8560 RS |
1736 | if (ret < 0) |
1737 | dev_err(sdev->dev, "%s: route %s -> %s failed\n", __func__, | |
1738 | sroute->src_widget->widget->name, sroute->sink_widget->widget->name); | |
1739 | ||
1740 | return ret; | |
1741 | } | |
1742 | ||
b5cee8fe RS |
1743 | static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) |
1744 | { | |
1745 | struct sof_ipc_ctrl_data *cdata; | |
8a0eb06e | 1746 | size_t priv_size_check; |
b5cee8fe RS |
1747 | int ret; |
1748 | ||
5702b838 PU |
1749 | if (scontrol->max_size < (sizeof(*cdata) + sizeof(struct sof_abi_hdr))) { |
1750 | dev_err(sdev->dev, "%s: insufficient size for a bytes control: %zu.\n", | |
1751 | __func__, scontrol->max_size); | |
1752 | return -EINVAL; | |
b5cee8fe RS |
1753 | } |
1754 | ||
b5cee8fe | 1755 | if (scontrol->priv_size > scontrol->max_size - sizeof(*cdata)) { |
5702b838 PU |
1756 | dev_err(sdev->dev, |
1757 | "%s: bytes data size %zu exceeds max %zu.\n", __func__, | |
b5cee8fe | 1758 | scontrol->priv_size, scontrol->max_size - sizeof(*cdata)); |
5702b838 | 1759 | return -EINVAL; |
b5cee8fe RS |
1760 | } |
1761 | ||
5702b838 PU |
1762 | scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL); |
1763 | if (!scontrol->ipc_control_data) | |
1764 | return -ENOMEM; | |
1765 | ||
b5cee8fe RS |
1766 | scontrol->size = sizeof(struct sof_ipc_ctrl_data) + scontrol->priv_size; |
1767 | ||
1768 | cdata = scontrol->ipc_control_data; | |
1769 | cdata->cmd = SOF_CTRL_CMD_BINARY; | |
1770 | cdata->index = scontrol->index; | |
1771 | ||
1772 | if (scontrol->priv_size > 0) { | |
1773 | memcpy(cdata->data, scontrol->priv, scontrol->priv_size); | |
1774 | kfree(scontrol->priv); | |
a403993c | 1775 | scontrol->priv = NULL; |
b5cee8fe RS |
1776 | |
1777 | if (cdata->data->magic != SOF_ABI_MAGIC) { | |
1778 | dev_err(sdev->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic); | |
1779 | ret = -EINVAL; | |
1780 | goto err; | |
1781 | } | |
1782 | ||
1783 | if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { | |
1784 | dev_err(sdev->dev, "Incompatible ABI version 0x%08x.\n", | |
1785 | cdata->data->abi); | |
1786 | ret = -EINVAL; | |
1787 | goto err; | |
1788 | } | |
1789 | ||
8a0eb06e SI |
1790 | priv_size_check = cdata->data->size + sizeof(struct sof_abi_hdr); |
1791 | if (priv_size_check != scontrol->priv_size) { | |
1792 | dev_err(sdev->dev, "Conflict in bytes (%zu) vs. priv size (%zu).\n", | |
1793 | priv_size_check, scontrol->priv_size); | |
b5cee8fe RS |
1794 | ret = -EINVAL; |
1795 | goto err; | |
1796 | } | |
1797 | } | |
1798 | ||
1799 | return 0; | |
1800 | err: | |
1801 | kfree(scontrol->ipc_control_data); | |
d5bd47f3 | 1802 | scontrol->ipc_control_data = NULL; |
b5cee8fe RS |
1803 | return ret; |
1804 | } | |
1805 | ||
1806 | static int sof_ipc3_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) | |
1807 | { | |
1808 | struct sof_ipc_ctrl_data *cdata; | |
1809 | int i; | |
1810 | ||
1811 | /* init the volume get/put data */ | |
1812 | scontrol->size = struct_size(cdata, chanv, scontrol->num_channels); | |
1813 | ||
1814 | scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL); | |
1815 | if (!scontrol->ipc_control_data) | |
1816 | return -ENOMEM; | |
1817 | ||
1818 | cdata = scontrol->ipc_control_data; | |
1819 | cdata->index = scontrol->index; | |
1820 | ||
1821 | /* set cmd for mixer control */ | |
1822 | if (scontrol->max == 1) { | |
1823 | cdata->cmd = SOF_CTRL_CMD_SWITCH; | |
1824 | return 0; | |
1825 | } | |
1826 | ||
1827 | cdata->cmd = SOF_CTRL_CMD_VOLUME; | |
1828 | ||
1829 | /* set default volume values to 0dB in control */ | |
1830 | for (i = 0; i < scontrol->num_channels; i++) { | |
1831 | cdata->chanv[i].channel = i; | |
1832 | cdata->chanv[i].value = VOL_ZERO_DB; | |
1833 | } | |
1834 | ||
1835 | return 0; | |
1836 | } | |
1837 | ||
1838 | static int sof_ipc3_control_load_enum(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) | |
1839 | { | |
1840 | struct sof_ipc_ctrl_data *cdata; | |
1841 | ||
1842 | /* init the enum get/put data */ | |
1843 | scontrol->size = struct_size(cdata, chanv, scontrol->num_channels); | |
1844 | ||
1845 | scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL); | |
1846 | if (!scontrol->ipc_control_data) | |
1847 | return -ENOMEM; | |
1848 | ||
1849 | cdata = scontrol->ipc_control_data; | |
1850 | cdata->index = scontrol->index; | |
1851 | cdata->cmd = SOF_CTRL_CMD_ENUM; | |
1852 | ||
1853 | return 0; | |
1854 | } | |
1855 | ||
1856 | static int sof_ipc3_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) | |
1857 | { | |
1858 | switch (scontrol->info_type) { | |
1859 | case SND_SOC_TPLG_CTL_VOLSW: | |
1860 | case SND_SOC_TPLG_CTL_VOLSW_SX: | |
1861 | case SND_SOC_TPLG_CTL_VOLSW_XR_SX: | |
1862 | return sof_ipc3_control_load_volume(sdev, scontrol); | |
1863 | case SND_SOC_TPLG_CTL_BYTES: | |
1864 | return sof_ipc3_control_load_bytes(sdev, scontrol); | |
1865 | case SND_SOC_TPLG_CTL_ENUM: | |
1866 | case SND_SOC_TPLG_CTL_ENUM_VALUE: | |
1867 | return sof_ipc3_control_load_enum(sdev, scontrol); | |
1868 | default: | |
1869 | break; | |
1870 | } | |
1871 | ||
1872 | return 0; | |
1873 | } | |
1874 | ||
1875 | static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) | |
1876 | { | |
1877 | struct sof_ipc_free fcomp; | |
1878 | ||
1879 | fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE; | |
1880 | fcomp.hdr.size = sizeof(fcomp); | |
1881 | fcomp.id = scontrol->comp_id; | |
1882 | ||
1883 | /* send IPC to the DSP */ | |
367fd6ff | 1884 | return sof_ipc_tx_message_no_reply(sdev->ipc, &fcomp, sizeof(fcomp)); |
b5cee8fe RS |
1885 | } |
1886 | ||
8ef1439c RS |
1887 | /* send pcm params ipc */ |
1888 | static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, int dir) | |
1889 | { | |
1890 | struct snd_soc_component *scomp = swidget->scomp; | |
1891 | struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); | |
8ef1439c RS |
1892 | struct snd_pcm_hw_params *params; |
1893 | struct sof_ipc_pcm_params pcm; | |
1894 | struct snd_sof_pcm *spcm; | |
1895 | int ret; | |
1896 | ||
1897 | /* get runtime PCM params using widget's stream name */ | |
1898 | spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname); | |
1899 | if (!spcm) { | |
1900 | dev_err(scomp->dev, "Cannot find PCM for %s\n", swidget->widget->name); | |
1901 | return -EINVAL; | |
1902 | } | |
1903 | ||
1904 | params = &spcm->params[dir]; | |
1905 | ||
1906 | /* set IPC PCM params */ | |
1907 | memset(&pcm, 0, sizeof(pcm)); | |
1908 | pcm.hdr.size = sizeof(pcm); | |
1909 | pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; | |
1910 | pcm.comp_id = swidget->comp_id; | |
1911 | pcm.params.hdr.size = sizeof(pcm.params); | |
1912 | pcm.params.direction = dir; | |
1913 | pcm.params.sample_valid_bytes = params_width(params) >> 3; | |
1914 | pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; | |
1915 | pcm.params.rate = params_rate(params); | |
1916 | pcm.params.channels = params_channels(params); | |
1917 | pcm.params.host_period_bytes = params_period_bytes(params); | |
1918 | ||
1919 | /* set format */ | |
1920 | switch (params_format(params)) { | |
1921 | case SNDRV_PCM_FORMAT_S16: | |
1922 | pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; | |
1923 | break; | |
1924 | case SNDRV_PCM_FORMAT_S24: | |
1925 | pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; | |
1926 | break; | |
1927 | case SNDRV_PCM_FORMAT_S32: | |
1928 | pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; | |
1929 | break; | |
1930 | default: | |
1931 | return -EINVAL; | |
1932 | } | |
1933 | ||
1934 | /* send IPC to the DSP */ | |
367fd6ff | 1935 | ret = sof_ipc_tx_message_no_reply(sdev->ipc, &pcm, sizeof(pcm)); |
8ef1439c RS |
1936 | if (ret < 0) |
1937 | dev_err(scomp->dev, "%s: PCM params failed for %s\n", __func__, | |
1938 | swidget->widget->name); | |
1939 | ||
1940 | return ret; | |
1941 | } | |
1942 | ||
1943 | /* send stream trigger ipc */ | |
1944 | static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int cmd) | |
1945 | { | |
1946 | struct snd_soc_component *scomp = swidget->scomp; | |
1947 | struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); | |
1948 | struct sof_ipc_stream stream; | |
8ef1439c RS |
1949 | int ret; |
1950 | ||
1951 | /* set IPC stream params */ | |
1952 | stream.hdr.size = sizeof(stream); | |
1953 | stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | cmd; | |
1954 | stream.comp_id = swidget->comp_id; | |
1955 | ||
1956 | /* send IPC to the DSP */ | |
367fd6ff | 1957 | ret = sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream)); |
8ef1439c RS |
1958 | if (ret < 0) |
1959 | dev_err(scomp->dev, "%s: Failed to trigger %s\n", __func__, swidget->widget->name); | |
1960 | ||
1961 | return ret; | |
1962 | } | |
1963 | ||
1964 | static int sof_ipc3_keyword_dapm_event(struct snd_soc_dapm_widget *w, | |
1965 | struct snd_kcontrol *k, int event) | |
1966 | { | |
1967 | struct snd_sof_widget *swidget = w->dobj.private; | |
1968 | struct snd_soc_component *scomp; | |
1969 | int stream = SNDRV_PCM_STREAM_CAPTURE; | |
1970 | struct snd_sof_pcm *spcm; | |
1971 | int ret = 0; | |
1972 | ||
1973 | if (!swidget) | |
1974 | return 0; | |
1975 | ||
1976 | scomp = swidget->scomp; | |
1977 | ||
1978 | dev_dbg(scomp->dev, "received event %d for widget %s\n", | |
1979 | event, w->name); | |
1980 | ||
1981 | /* get runtime PCM params using widget's stream name */ | |
1982 | spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname); | |
1983 | if (!spcm) { | |
1984 | dev_err(scomp->dev, "%s: Cannot find PCM for %s\n", __func__, | |
1985 | swidget->widget->name); | |
1986 | return -EINVAL; | |
1987 | } | |
1988 | ||
1989 | /* process events */ | |
1990 | switch (event) { | |
1991 | case SND_SOC_DAPM_PRE_PMU: | |
1992 | if (spcm->stream[stream].suspend_ignored) { | |
1993 | dev_dbg(scomp->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n"); | |
1994 | return 0; | |
1995 | } | |
1996 | ||
1997 | /* set pcm params */ | |
1998 | ret = sof_ipc3_keyword_detect_pcm_params(swidget, stream); | |
1999 | if (ret < 0) { | |
2000 | dev_err(scomp->dev, "%s: Failed to set pcm params for widget %s\n", | |
2001 | __func__, swidget->widget->name); | |
2002 | break; | |
2003 | } | |
2004 | ||
2005 | /* start trigger */ | |
2006 | ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_START); | |
2007 | if (ret < 0) | |
2008 | dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__, | |
2009 | swidget->widget->name); | |
2010 | break; | |
2011 | case SND_SOC_DAPM_POST_PMD: | |
2012 | if (spcm->stream[stream].suspend_ignored) { | |
2013 | dev_dbg(scomp->dev, | |
2014 | "POST_PMD event ignored, KWD pipeline will remain RUNNING\n"); | |
2015 | return 0; | |
2016 | } | |
2017 | ||
2018 | /* stop trigger */ | |
2019 | ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP); | |
2020 | if (ret < 0) | |
2021 | dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__, | |
2022 | swidget->widget->name); | |
2023 | ||
2024 | /* pcm free */ | |
2025 | ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_PCM_FREE); | |
2026 | if (ret < 0) | |
2027 | dev_err(scomp->dev, "%s: Failed to free PCM for widget %s\n", __func__, | |
2028 | swidget->widget->name); | |
2029 | break; | |
2030 | default: | |
2031 | break; | |
2032 | } | |
2033 | ||
2034 | return ret; | |
2035 | } | |
2036 | ||
2037 | /* event handlers for keyword detect component */ | |
2038 | static const struct snd_soc_tplg_widget_events sof_kwd_events[] = { | |
2039 | {SOF_KEYWORD_DETECT_DAPM_EVENT, sof_ipc3_keyword_dapm_event}, | |
2040 | }; | |
2041 | ||
2042 | static int sof_ipc3_widget_bind_event(struct snd_soc_component *scomp, | |
2043 | struct snd_sof_widget *swidget, u16 event_type) | |
2044 | { | |
2045 | struct sof_ipc_comp *ipc_comp; | |
2046 | ||
2047 | /* validate widget event type */ | |
2048 | switch (event_type) { | |
2049 | case SOF_KEYWORD_DETECT_DAPM_EVENT: | |
2050 | /* only KEYWORD_DETECT comps should handle this */ | |
2051 | if (swidget->id != snd_soc_dapm_effect) | |
2052 | break; | |
2053 | ||
2054 | ipc_comp = swidget->private; | |
2055 | if (ipc_comp && ipc_comp->type != SOF_COMP_KEYWORD_DETECT) | |
2056 | break; | |
2057 | ||
2058 | /* bind event to keyword detect comp */ | |
2059 | return snd_soc_tplg_widget_bind_event(swidget->widget, sof_kwd_events, | |
2060 | ARRAY_SIZE(sof_kwd_events), event_type); | |
2061 | default: | |
2062 | break; | |
2063 | } | |
2064 | ||
2065 | dev_err(scomp->dev, "Invalid event type %d for widget %s\n", event_type, | |
2066 | swidget->widget->name); | |
2067 | ||
2068 | return -EINVAL; | |
2069 | } | |
2070 | ||
61ad28ff RS |
2071 | static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) |
2072 | { | |
2073 | struct sof_ipc_pipe_ready ready; | |
61ad28ff RS |
2074 | int ret; |
2075 | ||
2076 | dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n", | |
2077 | swidget->widget->name, swidget->comp_id); | |
2078 | ||
2079 | memset(&ready, 0, sizeof(ready)); | |
2080 | ready.hdr.size = sizeof(ready); | |
2081 | ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE; | |
2082 | ready.comp_id = swidget->comp_id; | |
2083 | ||
367fd6ff | 2084 | ret = sof_ipc_tx_message_no_reply(sdev->ipc, &ready, sizeof(ready)); |
61ad28ff RS |
2085 | if (ret < 0) |
2086 | return ret; | |
2087 | ||
2088 | return 1; | |
2089 | } | |
2090 | ||
051744b1 RS |
2091 | static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) |
2092 | { | |
2093 | struct sof_ipc_free ipc_free = { | |
2094 | .hdr = { | |
2095 | .size = sizeof(ipc_free), | |
2096 | .cmd = SOF_IPC_GLB_TPLG_MSG, | |
2097 | }, | |
2098 | .id = swidget->comp_id, | |
2099 | }; | |
051744b1 RS |
2100 | int ret; |
2101 | ||
2102 | if (!swidget->private) | |
2103 | return 0; | |
2104 | ||
2105 | switch (swidget->id) { | |
2106 | case snd_soc_dapm_scheduler: | |
2107 | { | |
2108 | ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE; | |
2109 | break; | |
2110 | } | |
2111 | case snd_soc_dapm_buffer: | |
2112 | ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE; | |
2113 | break; | |
2114 | default: | |
2115 | ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE; | |
2116 | break; | |
2117 | } | |
2118 | ||
367fd6ff | 2119 | ret = sof_ipc_tx_message_no_reply(sdev->ipc, &ipc_free, sizeof(ipc_free)); |
051744b1 RS |
2120 | if (ret < 0) |
2121 | dev_err(sdev->dev, "failed to free widget %s\n", swidget->widget->name); | |
2122 | ||
2123 | return ret; | |
2124 | } | |
2125 | ||
2126 | static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, | |
2127 | unsigned int flags, struct snd_sof_dai_config_data *data) | |
2128 | { | |
2129 | struct sof_ipc_fw_version *v = &sdev->fw_ready.version; | |
2130 | struct snd_sof_dai *dai = swidget->private; | |
2131 | struct sof_dai_private_data *private; | |
2132 | struct sof_ipc_dai_config *config; | |
051744b1 RS |
2133 | int ret = 0; |
2134 | ||
2135 | if (!dai || !dai->private) { | |
2136 | dev_err(sdev->dev, "No private data for DAI %s\n", swidget->widget->name); | |
2137 | return -EINVAL; | |
2138 | } | |
2139 | ||
2140 | private = dai->private; | |
2141 | if (!private->dai_config) { | |
2142 | dev_err(sdev->dev, "No config for DAI %s\n", dai->name); | |
2143 | return -EINVAL; | |
2144 | } | |
2145 | ||
2146 | config = &private->dai_config[dai->current_config]; | |
2147 | if (!config) { | |
2148 | dev_err(sdev->dev, "Invalid current config for DAI %s\n", dai->name); | |
2149 | return -EINVAL; | |
2150 | } | |
2151 | ||
2152 | switch (config->type) { | |
2153 | case SOF_DAI_INTEL_SSP: | |
2154 | /* | |
2155 | * DAI_CONFIG IPC during hw_params/hw_free for SSP DAI's is not supported in older | |
2156 | * firmware | |
2157 | */ | |
2158 | if (v->abi_version < SOF_ABI_VER(3, 18, 0) && | |
2159 | ((flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) || | |
2160 | (flags & SOF_DAI_CONFIG_FLAGS_HW_FREE))) | |
2161 | return 0; | |
2162 | break; | |
2163 | case SOF_DAI_INTEL_HDA: | |
2164 | if (data) | |
2165 | config->hda.link_dma_ch = data->dai_data; | |
2166 | break; | |
2167 | case SOF_DAI_INTEL_ALH: | |
2168 | if (data) { | |
b66bfc3a RS |
2169 | /* save the dai_index during hw_params and reuse it for hw_free */ |
2170 | if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) | |
2171 | config->dai_index = data->dai_index; | |
051744b1 RS |
2172 | config->alh.stream_id = data->dai_data; |
2173 | } | |
2174 | break; | |
2175 | default: | |
2176 | break; | |
2177 | } | |
2178 | ||
b66bfc3a RS |
2179 | /* |
2180 | * The dai_config op is invoked several times and the flags argument varies as below: | |
2181 | * BE DAI hw_params: When the op is invoked during the BE DAI hw_params, flags contains | |
2182 | * SOF_DAI_CONFIG_FLAGS_HW_PARAMS along with quirks | |
2183 | * FE DAI hw_params: When invoked during FE DAI hw_params after the DAI widget has | |
2184 | * just been set up in the DSP, flags is set to SOF_DAI_CONFIG_FLAGS_HW_PARAMS with no | |
2185 | * quirks | |
2186 | * BE DAI trigger: When invoked during the BE DAI trigger, flags is set to | |
2187 | * SOF_DAI_CONFIG_FLAGS_PAUSE and contains no quirks | |
2188 | * BE DAI hw_free: When invoked during the BE DAI hw_free, flags is set to | |
2189 | * SOF_DAI_CONFIG_FLAGS_HW_FREE and contains no quirks | |
2190 | * FE DAI hw_free: When invoked during the FE DAI hw_free, flags is set to | |
2191 | * SOF_DAI_CONFIG_FLAGS_HW_FREE and contains no quirks | |
2192 | * | |
2193 | * The DAI_CONFIG IPC is sent to the DSP, only after the widget is set up during the FE | |
2194 | * DAI hw_params. But since the BE DAI hw_params precedes the FE DAI hw_params, the quirks | |
2195 | * need to be preserved when assigning the flags before sending the IPC. | |
2196 | * For the case of PAUSE/HW_FREE, since there are no quirks, flags can be used as is. | |
2197 | */ | |
2198 | ||
4708449e PU |
2199 | if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) { |
2200 | /* Clear stale command */ | |
2201 | config->flags &= ~SOF_DAI_CONFIG_FLAGS_CMD_MASK; | |
b66bfc3a | 2202 | config->flags |= flags; |
4708449e | 2203 | } else { |
b66bfc3a | 2204 | config->flags = flags; |
4708449e | 2205 | } |
051744b1 RS |
2206 | |
2207 | /* only send the IPC if the widget is set up in the DSP */ | |
2208 | if (swidget->use_count > 0) { | |
367fd6ff | 2209 | ret = sof_ipc_tx_message_no_reply(sdev->ipc, config, config->hdr.size); |
051744b1 RS |
2210 | if (ret < 0) |
2211 | dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name); | |
b66bfc3a RS |
2212 | |
2213 | /* clear the flags once the IPC has been sent even if it fails */ | |
2214 | config->flags = SOF_DAI_CONFIG_FLAGS_NONE; | |
051744b1 RS |
2215 | } |
2216 | ||
2217 | return ret; | |
2218 | } | |
2219 | ||
2220 | static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) | |
2221 | { | |
051744b1 RS |
2222 | int ret; |
2223 | ||
2224 | if (!swidget->private) | |
2225 | return 0; | |
2226 | ||
2227 | switch (swidget->id) { | |
2228 | case snd_soc_dapm_dai_in: | |
2229 | case snd_soc_dapm_dai_out: | |
2230 | { | |
2231 | struct snd_sof_dai *dai = swidget->private; | |
2232 | struct sof_dai_private_data *dai_data = dai->private; | |
2233 | struct sof_ipc_comp *comp = &dai_data->comp_dai->comp; | |
2234 | ||
367fd6ff | 2235 | ret = sof_ipc_tx_message_no_reply(sdev->ipc, dai_data->comp_dai, comp->hdr.size); |
051744b1 RS |
2236 | break; |
2237 | } | |
2238 | case snd_soc_dapm_scheduler: | |
2239 | { | |
2240 | struct sof_ipc_pipe_new *pipeline; | |
2241 | ||
2242 | pipeline = swidget->private; | |
367fd6ff | 2243 | ret = sof_ipc_tx_message_no_reply(sdev->ipc, pipeline, sizeof(*pipeline)); |
051744b1 RS |
2244 | break; |
2245 | } | |
2246 | default: | |
2247 | { | |
2248 | struct sof_ipc_cmd_hdr *hdr; | |
2249 | ||
2250 | hdr = swidget->private; | |
367fd6ff | 2251 | ret = sof_ipc_tx_message_no_reply(sdev->ipc, swidget->private, hdr->size); |
051744b1 RS |
2252 | break; |
2253 | } | |
2254 | } | |
2255 | if (ret < 0) | |
2256 | dev_err(sdev->dev, "Failed to setup widget %s\n", swidget->widget->name); | |
2257 | ||
2258 | return ret; | |
2259 | } | |
2260 | ||
31cd6e46 RS |
2261 | static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) |
2262 | { | |
2263 | struct sof_ipc_fw_version *v = &sdev->fw_ready.version; | |
2264 | struct snd_sof_widget *swidget; | |
2265 | struct snd_sof_route *sroute; | |
2266 | int ret; | |
2267 | ||
2268 | /* restore pipeline components */ | |
2269 | list_for_each_entry(swidget, &sdev->widget_list, list) { | |
2270 | /* only set up the widgets belonging to static pipelines */ | |
2271 | if (!verify && swidget->dynamic_pipeline_widget) | |
2272 | continue; | |
2273 | ||
2274 | /* | |
2275 | * For older firmware, skip scheduler widgets in this loop, | |
2276 | * sof_widget_setup() will be called in the 'complete pipeline' loop | |
2277 | */ | |
2278 | if (v->abi_version < SOF_ABI_VER(3, 19, 0) && | |
2279 | swidget->id == snd_soc_dapm_scheduler) | |
2280 | continue; | |
2281 | ||
2282 | /* update DAI config. The IPC will be sent in sof_widget_setup() */ | |
2283 | if (WIDGET_IS_DAI(swidget->id)) { | |
2284 | struct snd_sof_dai *dai = swidget->private; | |
2285 | struct sof_dai_private_data *private; | |
2286 | struct sof_ipc_dai_config *config; | |
2287 | ||
2288 | if (!dai || !dai->private) | |
2289 | continue; | |
2290 | private = dai->private; | |
2291 | if (!private->dai_config) | |
2292 | continue; | |
2293 | ||
2294 | config = private->dai_config; | |
2295 | /* | |
2296 | * The link DMA channel would be invalidated for running | |
2297 | * streams but not for streams that were in the PAUSED | |
2298 | * state during suspend. So invalidate it here before setting | |
2299 | * the dai config in the DSP. | |
2300 | */ | |
2301 | if (config->type == SOF_DAI_INTEL_HDA) | |
2302 | config->hda.link_dma_ch = DMA_CHAN_INVALID; | |
2303 | } | |
2304 | ||
2305 | ret = sof_widget_setup(sdev, swidget); | |
2306 | if (ret < 0) | |
2307 | return ret; | |
2308 | } | |
2309 | ||
2310 | /* restore pipeline connections */ | |
2311 | list_for_each_entry(sroute, &sdev->route_list, list) { | |
2312 | /* only set up routes belonging to static pipelines */ | |
2313 | if (!verify && (sroute->src_widget->dynamic_pipeline_widget || | |
2314 | sroute->sink_widget->dynamic_pipeline_widget)) | |
2315 | continue; | |
2316 | ||
2317 | /* | |
2318 | * For virtual routes, both sink and source are not buffer. IPC3 only supports | |
2319 | * connections between a buffer and a component. Ignore the rest. | |
2320 | */ | |
2321 | if (sroute->src_widget->id != snd_soc_dapm_buffer && | |
2322 | sroute->sink_widget->id != snd_soc_dapm_buffer) | |
2323 | continue; | |
2324 | ||
2325 | ret = sof_route_setup(sdev, sroute->src_widget->widget, | |
2326 | sroute->sink_widget->widget); | |
2327 | if (ret < 0) { | |
2328 | dev_err(sdev->dev, "%s: route set up failed\n", __func__); | |
2329 | return ret; | |
2330 | } | |
2331 | } | |
2332 | ||
2333 | /* complete pipeline */ | |
2334 | list_for_each_entry(swidget, &sdev->widget_list, list) { | |
2335 | switch (swidget->id) { | |
2336 | case snd_soc_dapm_scheduler: | |
2337 | /* only complete static pipelines */ | |
2338 | if (!verify && swidget->dynamic_pipeline_widget) | |
2339 | continue; | |
2340 | ||
2341 | if (v->abi_version < SOF_ABI_VER(3, 19, 0)) { | |
2342 | ret = sof_widget_setup(sdev, swidget); | |
2343 | if (ret < 0) | |
2344 | return ret; | |
2345 | } | |
2346 | ||
9c04363d RS |
2347 | swidget->spipe->complete = sof_ipc3_complete_pipeline(sdev, swidget); |
2348 | if (swidget->spipe->complete < 0) | |
2349 | return swidget->spipe->complete; | |
31cd6e46 RS |
2350 | break; |
2351 | default: | |
2352 | break; | |
2353 | } | |
2354 | } | |
2355 | ||
2356 | return 0; | |
2357 | } | |
2358 | ||
2359 | /* | |
2360 | * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that | |
2361 | * did not get suspended(ex: paused streams) so the widgets can be set up again during resume. | |
2362 | */ | |
2363 | static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) | |
2364 | { | |
2365 | struct snd_sof_widget *swidget; | |
2366 | struct snd_sof_pcm *spcm; | |
2367 | int dir, ret; | |
2368 | ||
2369 | /* | |
2370 | * free all PCMs and their associated DAPM widgets if their connected DAPM widget | |
2371 | * list is not NULL. This should only be true for paused streams at this point. | |
2372 | * This is equivalent to the handling of FE DAI suspend trigger for running streams. | |
2373 | */ | |
2374 | list_for_each_entry(spcm, &sdev->pcm_list, list) { | |
2375 | for_each_pcm_streams(dir) { | |
2376 | struct snd_pcm_substream *substream = spcm->stream[dir].substream; | |
2377 | ||
82b18242 | 2378 | if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored) |
31cd6e46 RS |
2379 | continue; |
2380 | ||
2381 | if (spcm->stream[dir].list) { | |
2382 | ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true); | |
2383 | if (ret < 0) | |
2384 | return ret; | |
2385 | } | |
2386 | } | |
2387 | } | |
2388 | ||
2389 | /* | |
2390 | * free any left over DAI widgets. This is equivalent to the handling of suspend trigger | |
2391 | * for the BE DAI for running streams. | |
2392 | */ | |
2393 | list_for_each_entry(swidget, &sdev->widget_list, list) | |
2394 | if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) { | |
2395 | ret = sof_widget_free(sdev, swidget); | |
2396 | if (ret < 0) | |
2397 | return ret; | |
2398 | } | |
2399 | ||
2400 | return 0; | |
2401 | } | |
2402 | ||
d7332c4a RS |
2403 | static int sof_ipc3_free_widgets_in_list(struct snd_sof_dev *sdev, bool include_scheduler, |
2404 | bool *dyn_widgets, bool verify) | |
31cd6e46 RS |
2405 | { |
2406 | struct sof_ipc_fw_version *v = &sdev->fw_ready.version; | |
2407 | struct snd_sof_widget *swidget; | |
31cd6e46 RS |
2408 | int ret; |
2409 | ||
31cd6e46 | 2410 | list_for_each_entry(swidget, &sdev->widget_list, list) { |
003b786b | 2411 | if (swidget->dynamic_pipeline_widget) { |
d7332c4a | 2412 | *dyn_widgets = true; |
31cd6e46 | 2413 | continue; |
003b786b | 2414 | } |
31cd6e46 | 2415 | |
003b786b | 2416 | /* Do not free widgets for static pipelines with FW older than SOF2.2 */ |
31cd6e46 | 2417 | if (!verify && !swidget->dynamic_pipeline_widget && |
003b786b | 2418 | SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) { |
f94f3915 | 2419 | mutex_lock(&swidget->setup_mutex); |
31cd6e46 | 2420 | swidget->use_count = 0; |
f94f3915 | 2421 | mutex_unlock(&swidget->setup_mutex); |
9c04363d RS |
2422 | if (swidget->spipe) |
2423 | swidget->spipe->complete = 0; | |
31cd6e46 RS |
2424 | continue; |
2425 | } | |
2426 | ||
d7332c4a RS |
2427 | if (include_scheduler && swidget->id != snd_soc_dapm_scheduler) |
2428 | continue; | |
2429 | ||
2430 | if (!include_scheduler && swidget->id == snd_soc_dapm_scheduler) | |
2431 | continue; | |
2432 | ||
31cd6e46 RS |
2433 | ret = sof_widget_free(sdev, swidget); |
2434 | if (ret < 0) | |
2435 | return ret; | |
2436 | } | |
2437 | ||
d7332c4a RS |
2438 | return 0; |
2439 | } | |
2440 | ||
2441 | /* | |
2442 | * For older firmware, this function doesn't free widgets for static pipelines during suspend. | |
2443 | * It only resets use_count for all widgets. | |
2444 | */ | |
2445 | static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify) | |
2446 | { | |
2447 | struct sof_ipc_fw_version *v = &sdev->fw_ready.version; | |
2448 | struct snd_sof_widget *swidget; | |
2449 | struct snd_sof_route *sroute; | |
2450 | bool dyn_widgets = false; | |
2451 | int ret; | |
2452 | ||
2453 | /* | |
2454 | * This function is called during suspend and for one-time topology verification during | |
2455 | * first boot. In both cases, there is no need to protect swidget->use_count and | |
2456 | * sroute->setup because during suspend all running streams are suspended and during | |
2457 | * topology loading the sound card unavailable to open PCMs. Do not free the scheduler | |
2458 | * widgets yet so that the secondary cores do not get powered down before all the widgets | |
2459 | * associated with the scheduler are freed. | |
2460 | */ | |
2461 | ret = sof_ipc3_free_widgets_in_list(sdev, false, &dyn_widgets, verify); | |
2462 | if (ret < 0) | |
2463 | return ret; | |
2464 | ||
2465 | /* free all the scheduler widgets now */ | |
2466 | ret = sof_ipc3_free_widgets_in_list(sdev, true, &dyn_widgets, verify); | |
2467 | if (ret < 0) | |
2468 | return ret; | |
2469 | ||
31cd6e46 RS |
2470 | /* |
2471 | * Tear down all pipelines associated with PCMs that did not get suspended | |
2472 | * and unset the prepare flag so that they can be set up again during resume. | |
003b786b KV |
2473 | * Skip this step for older firmware unless topology has any |
2474 | * dynamic pipeline (in which case the step is mandatory). | |
31cd6e46 | 2475 | */ |
003b786b KV |
2476 | if (!verify && (dyn_widgets || SOF_FW_VER(v->major, v->minor, v->micro) >= |
2477 | SOF_FW_VER(2, 2, 0))) { | |
31cd6e46 RS |
2478 | ret = sof_tear_down_left_over_pipelines(sdev); |
2479 | if (ret < 0) { | |
2480 | dev_err(sdev->dev, "failed to tear down paused pipelines\n"); | |
2481 | return ret; | |
2482 | } | |
2483 | } | |
2484 | ||
2485 | list_for_each_entry(sroute, &sdev->route_list, list) | |
2486 | sroute->setup = false; | |
2487 | ||
d1c73a21 PLB |
2488 | /* |
2489 | * before suspending, make sure the refcounts are all zeroed out. There's no way | |
2490 | * to recover at this point but this will help root cause bad sequences leading to | |
2491 | * more issues on resume | |
2492 | */ | |
2493 | list_for_each_entry(swidget, &sdev->widget_list, list) { | |
2494 | if (swidget->use_count != 0) { | |
2495 | dev_err(sdev->dev, "%s: widget %s is still in use: count %d\n", | |
2496 | __func__, swidget->widget->name, swidget->use_count); | |
2497 | } | |
2498 | } | |
2499 | ||
31cd6e46 RS |
2500 | return 0; |
2501 | } | |
2502 | ||
85f7a8b6 RS |
2503 | static int sof_ipc3_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type) |
2504 | { | |
2505 | struct sof_dai_private_data *private = dai->private; | |
2506 | ||
2507 | if (!private || !private->dai_config) | |
2508 | return 0; | |
2509 | ||
2510 | switch (private->dai_config->type) { | |
2511 | case SOF_DAI_INTEL_SSP: | |
2512 | switch (clk_type) { | |
2513 | case SOF_DAI_CLK_INTEL_SSP_MCLK: | |
2514 | return private->dai_config->ssp.mclk_rate; | |
2515 | case SOF_DAI_CLK_INTEL_SSP_BCLK: | |
2516 | return private->dai_config->ssp.bclk_rate; | |
2517 | default: | |
2518 | break; | |
2519 | } | |
2520 | dev_err(sdev->dev, "fail to get SSP clk %d rate\n", clk_type); | |
2521 | break; | |
2522 | default: | |
2523 | /* not yet implemented for platforms other than the above */ | |
2524 | dev_err(sdev->dev, "DAI type %d not supported yet!\n", private->dai_config->type); | |
2525 | break; | |
2526 | } | |
2527 | ||
2528 | return -EINVAL; | |
2529 | } | |
2530 | ||
323aa1f0 RS |
2531 | static int sof_ipc3_parse_manifest(struct snd_soc_component *scomp, int index, |
2532 | struct snd_soc_tplg_manifest *man) | |
2533 | { | |
2534 | u32 size = le32_to_cpu(man->priv.size); | |
2535 | u32 abi_version; | |
2536 | ||
2537 | /* backward compatible with tplg without ABI info */ | |
2538 | if (!size) { | |
2539 | dev_dbg(scomp->dev, "No topology ABI info\n"); | |
2540 | return 0; | |
2541 | } | |
2542 | ||
2543 | if (size != SOF_IPC3_TPLG_ABI_SIZE) { | |
2544 | dev_err(scomp->dev, "%s: Invalid topology ABI size: %u\n", | |
2545 | __func__, size); | |
2546 | return -EINVAL; | |
2547 | } | |
2548 | ||
2549 | dev_info(scomp->dev, | |
b7bf23c0 | 2550 | "Topology: ABI %d:%d:%d Kernel ABI %d:%d:%d\n", |
323aa1f0 RS |
2551 | man->priv.data[0], man->priv.data[1], man->priv.data[2], |
2552 | SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH); | |
2553 | ||
2554 | abi_version = SOF_ABI_VER(man->priv.data[0], man->priv.data[1], man->priv.data[2]); | |
2555 | ||
2556 | if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, abi_version)) { | |
2557 | dev_err(scomp->dev, "%s: Incompatible topology ABI version\n", __func__); | |
2558 | return -EINVAL; | |
2559 | } | |
2560 | ||
fd1c769d PLB |
2561 | if (IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS) && |
2562 | SOF_ABI_VERSION_MINOR(abi_version) > SOF_ABI_MINOR) { | |
2563 | dev_err(scomp->dev, "%s: Topology ABI is more recent than kernel\n", __func__); | |
2564 | return -EINVAL; | |
323aa1f0 RS |
2565 | } |
2566 | ||
2567 | return 0; | |
2568 | } | |
2569 | ||
e380c907 RS |
2570 | static int sof_ipc3_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link) |
2571 | { | |
2572 | if (link->no_pcm) | |
2573 | return 0; | |
2574 | ||
2575 | /* | |
2576 | * set default trigger order for all links. Exceptions to | |
2577 | * the rule will be handled in sof_pcm_dai_link_fixup() | |
2578 | * For playback, the sequence is the following: start FE, | |
2579 | * start BE, stop BE, stop FE; for Capture the sequence is | |
2580 | * inverted start BE, start FE, stop FE, stop BE | |
2581 | */ | |
2582 | link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_PRE; | |
2583 | link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_POST; | |
2584 | ||
2585 | return 0; | |
2586 | } | |
2587 | ||
7006d20e RS |
2588 | /* token list for each topology object */ |
2589 | static enum sof_tokens host_token_list[] = { | |
2590 | SOF_CORE_TOKENS, | |
2591 | SOF_COMP_EXT_TOKENS, | |
2592 | SOF_PCM_TOKENS, | |
2593 | SOF_COMP_TOKENS, | |
2594 | }; | |
2595 | ||
30f41680 RS |
2596 | static enum sof_tokens comp_generic_token_list[] = { |
2597 | SOF_CORE_TOKENS, | |
2598 | SOF_COMP_EXT_TOKENS, | |
2599 | SOF_COMP_TOKENS, | |
2600 | }; | |
2601 | ||
6bd0be1c RS |
2602 | static enum sof_tokens buffer_token_list[] = { |
2603 | SOF_BUFFER_TOKENS, | |
2604 | }; | |
2605 | ||
2141b55d RS |
2606 | static enum sof_tokens pipeline_token_list[] = { |
2607 | SOF_CORE_TOKENS, | |
2608 | SOF_COMP_EXT_TOKENS, | |
2609 | SOF_PIPELINE_TOKENS, | |
2610 | SOF_SCHED_TOKENS, | |
2611 | }; | |
2612 | ||
cb7ed49a RS |
2613 | static enum sof_tokens asrc_token_list[] = { |
2614 | SOF_CORE_TOKENS, | |
2615 | SOF_COMP_EXT_TOKENS, | |
2616 | SOF_ASRC_TOKENS, | |
2617 | SOF_COMP_TOKENS, | |
2618 | }; | |
2619 | ||
8d8b1293 RS |
2620 | static enum sof_tokens src_token_list[] = { |
2621 | SOF_CORE_TOKENS, | |
2622 | SOF_COMP_EXT_TOKENS, | |
2623 | SOF_SRC_TOKENS, | |
2624 | SOF_COMP_TOKENS | |
2625 | }; | |
2626 | ||
8a2e4a73 RS |
2627 | static enum sof_tokens pga_token_list[] = { |
2628 | SOF_CORE_TOKENS, | |
2629 | SOF_COMP_EXT_TOKENS, | |
2630 | SOF_VOLUME_TOKENS, | |
2631 | SOF_COMP_TOKENS, | |
2632 | }; | |
2633 | ||
909dadf2 RS |
2634 | static enum sof_tokens dai_token_list[] = { |
2635 | SOF_CORE_TOKENS, | |
2636 | SOF_COMP_EXT_TOKENS, | |
2637 | SOF_DAI_TOKENS, | |
2638 | SOF_COMP_TOKENS, | |
2639 | }; | |
2640 | ||
f2cf24a1 RS |
2641 | static enum sof_tokens process_token_list[] = { |
2642 | SOF_CORE_TOKENS, | |
2643 | SOF_COMP_EXT_TOKENS, | |
2644 | SOF_PROCESS_TOKENS, | |
2645 | SOF_COMP_TOKENS, | |
2646 | }; | |
2647 | ||
7006d20e RS |
2648 | static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = { |
2649 | [snd_soc_dapm_aif_in] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp, | |
2650 | host_token_list, ARRAY_SIZE(host_token_list), NULL}, | |
2651 | [snd_soc_dapm_aif_out] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp, | |
2652 | host_token_list, ARRAY_SIZE(host_token_list), NULL}, | |
909dadf2 RS |
2653 | |
2654 | [snd_soc_dapm_dai_in] = {sof_ipc3_widget_setup_comp_dai, sof_ipc3_widget_free_comp_dai, | |
2655 | dai_token_list, ARRAY_SIZE(dai_token_list), NULL}, | |
2656 | [snd_soc_dapm_dai_out] = {sof_ipc3_widget_setup_comp_dai, sof_ipc3_widget_free_comp_dai, | |
2657 | dai_token_list, ARRAY_SIZE(dai_token_list), NULL}, | |
6bd0be1c RS |
2658 | [snd_soc_dapm_buffer] = {sof_ipc3_widget_setup_comp_buffer, sof_ipc3_widget_free_comp, |
2659 | buffer_token_list, ARRAY_SIZE(buffer_token_list), NULL}, | |
30f41680 RS |
2660 | [snd_soc_dapm_mixer] = {sof_ipc3_widget_setup_comp_mixer, sof_ipc3_widget_free_comp, |
2661 | comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), | |
2662 | NULL}, | |
8d8b1293 RS |
2663 | [snd_soc_dapm_src] = {sof_ipc3_widget_setup_comp_src, sof_ipc3_widget_free_comp, |
2664 | src_token_list, ARRAY_SIZE(src_token_list), NULL}, | |
cb7ed49a RS |
2665 | [snd_soc_dapm_asrc] = {sof_ipc3_widget_setup_comp_asrc, sof_ipc3_widget_free_comp, |
2666 | asrc_token_list, ARRAY_SIZE(asrc_token_list), NULL}, | |
111d66f6 RS |
2667 | [snd_soc_dapm_siggen] = {sof_ipc3_widget_setup_comp_tone, sof_ipc3_widget_free_comp, |
2668 | comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), | |
2669 | NULL}, | |
2141b55d RS |
2670 | [snd_soc_dapm_scheduler] = {sof_ipc3_widget_setup_comp_pipeline, sof_ipc3_widget_free_comp, |
2671 | pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL}, | |
8a2e4a73 RS |
2672 | [snd_soc_dapm_pga] = {sof_ipc3_widget_setup_comp_pga, sof_ipc3_widget_free_comp, |
2673 | pga_token_list, ARRAY_SIZE(pga_token_list), NULL}, | |
683b54ef RS |
2674 | [snd_soc_dapm_mux] = {sof_ipc3_widget_setup_comp_mux, sof_ipc3_widget_free_comp, |
2675 | comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), NULL}, | |
2676 | [snd_soc_dapm_demux] = {sof_ipc3_widget_setup_comp_mux, sof_ipc3_widget_free_comp, | |
2677 | comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), | |
2678 | NULL}, | |
f2cf24a1 | 2679 | [snd_soc_dapm_effect] = {sof_widget_update_ipc_comp_process, sof_ipc3_widget_free_comp, |
8ef1439c RS |
2680 | process_token_list, ARRAY_SIZE(process_token_list), |
2681 | sof_ipc3_widget_bind_event}, | |
7006d20e RS |
2682 | }; |
2683 | ||
657774ac | 2684 | const struct sof_ipc_tplg_ops ipc3_tplg_ops = { |
7006d20e | 2685 | .widget = tplg_ipc3_widget_ops, |
10f461d7 | 2686 | .control = &tplg_ipc3_control_ops, |
85ec8560 | 2687 | .route_setup = sof_ipc3_route_setup, |
b5cee8fe RS |
2688 | .control_setup = sof_ipc3_control_setup, |
2689 | .control_free = sof_ipc3_control_free, | |
61ad28ff | 2690 | .pipeline_complete = sof_ipc3_complete_pipeline, |
7006d20e | 2691 | .token_list = ipc3_token_list, |
051744b1 RS |
2692 | .widget_free = sof_ipc3_widget_free, |
2693 | .widget_setup = sof_ipc3_widget_setup, | |
2694 | .dai_config = sof_ipc3_dai_config, | |
85f7a8b6 | 2695 | .dai_get_clk = sof_ipc3_dai_get_clk, |
31cd6e46 RS |
2696 | .set_up_all_pipelines = sof_ipc3_set_up_all_pipelines, |
2697 | .tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines, | |
323aa1f0 | 2698 | .parse_manifest = sof_ipc3_parse_manifest, |
e380c907 | 2699 | .link_setup = sof_ipc3_link_setup, |
7006d20e | 2700 | }; |