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 | // |
6 | // Copyright(c) 2021 Intel Corporation. All rights reserved. |
7 | // |
8 | // |
9 | |
10 | #include <uapi/sound/sof/tokens.h> |
11 | #include <sound/pcm_params.h> |
12 | #include "sof-priv.h" |
13 | #include "sof-audio.h" |
14 | #include "ipc3-priv.h" |
15 | #include "ops.h" |
16 | |
17 | /* Full volume for default values */ |
18 | #define VOL_ZERO_DB BIT(VOLUME_FWL) |
19 | |
20 | /* size of tplg ABI in bytes */ |
21 | #define SOF_IPC3_TPLG_ABI_SIZE 3 |
22 | |
23 | struct sof_widget_data { |
24 | int ctrl_type; |
25 | int ipc_cmd; |
26 | void *pdata; |
27 | size_t pdata_size; |
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(name: (const char *)elem); |
66 | return 0; |
67 | } |
68 | |
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)}, |
75 | {SOF_TKN_BUF_FLAGS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, |
76 | offsetof(struct sof_ipc_buffer, flags)}, |
77 | }; |
78 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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)}, |
323 | [SOF_PIPELINE_TOKENS] = {.name: "Pipeline tokens" , .tokens: pipeline_tokens, ARRAY_SIZE(pipeline_tokens)}, |
324 | [SOF_SCHED_TOKENS] = {.name: "Scheduler tokens" , .tokens: sched_tokens, ARRAY_SIZE(sched_tokens)}, |
325 | [SOF_COMP_TOKENS] = {.name: "Comp tokens" , .tokens: comp_tokens, ARRAY_SIZE(comp_tokens)}, |
326 | [SOF_CORE_TOKENS] = {.name: "Core tokens" , .tokens: core_tokens, ARRAY_SIZE(core_tokens)}, |
327 | [SOF_COMP_EXT_TOKENS] = {.name: "AFE tokens" , .tokens: comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens)}, |
328 | [SOF_BUFFER_TOKENS] = {.name: "Buffer tokens" , .tokens: buffer_tokens, ARRAY_SIZE(buffer_tokens)}, |
329 | [SOF_VOLUME_TOKENS] = {.name: "Volume tokens" , .tokens: volume_tokens, ARRAY_SIZE(volume_tokens)}, |
330 | [SOF_SRC_TOKENS] = {.name: "SRC tokens" , .tokens: src_tokens, ARRAY_SIZE(src_tokens)}, |
331 | [SOF_ASRC_TOKENS] = {.name: "ASRC tokens" , .tokens: asrc_tokens, ARRAY_SIZE(asrc_tokens)}, |
332 | [SOF_PROCESS_TOKENS] = {.name: "Process tokens" , .tokens: process_tokens, ARRAY_SIZE(process_tokens)}, |
333 | [SOF_DAI_TOKENS] = {.name: "DAI tokens" , .tokens: dai_tokens, ARRAY_SIZE(dai_tokens)}, |
334 | [SOF_DAI_LINK_TOKENS] = {.name: "DAI link tokens" , .tokens: dai_link_tokens, ARRAY_SIZE(dai_link_tokens)}, |
335 | [SOF_HDA_TOKENS] = {.name: "HDA tokens" , .tokens: hda_tokens, ARRAY_SIZE(hda_tokens)}, |
336 | [SOF_SSP_TOKENS] = {.name: "SSP tokens" , .tokens: ssp_tokens, ARRAY_SIZE(ssp_tokens)}, |
337 | [SOF_ALH_TOKENS] = {.name: "ALH tokens" , .tokens: alh_tokens, ARRAY_SIZE(alh_tokens)}, |
338 | [SOF_DMIC_TOKENS] = {.name: "DMIC tokens" , .tokens: dmic_tokens, ARRAY_SIZE(dmic_tokens)}, |
339 | [SOF_DMIC_PDM_TOKENS] = {.name: "DMIC PDM tokens" , .tokens: dmic_pdm_tokens, ARRAY_SIZE(dmic_pdm_tokens)}, |
340 | [SOF_ESAI_TOKENS] = {.name: "ESAI tokens" , .tokens: esai_tokens, ARRAY_SIZE(esai_tokens)}, |
341 | [SOF_SAI_TOKENS] = {.name: "SAI tokens" , .tokens: sai_tokens, ARRAY_SIZE(sai_tokens)}, |
342 | [SOF_AFE_TOKENS] = {.name: "AFE tokens" , .tokens: afe_tokens, ARRAY_SIZE(afe_tokens)}, |
343 | [SOF_ACPDMIC_TOKENS] = {.name: "ACPDMIC tokens" , .tokens: acpdmic_tokens, ARRAY_SIZE(acpdmic_tokens)}, |
344 | [SOF_ACPI2S_TOKENS] = {.name: "ACPI2S tokens" , .tokens: acpi2s_tokens, ARRAY_SIZE(acpi2s_tokens)}, |
345 | [SOF_MICFIL_TOKENS] = {.name: "MICFIL PDM tokens" , |
346 | .tokens: micfil_pdm_tokens, ARRAY_SIZE(micfil_pdm_tokens)}, |
347 | [SOF_ACP_SDW_TOKENS] = {.name: "ACP_SDW tokens" , .tokens: acp_sdw_tokens, ARRAY_SIZE(acp_sdw_tokens)}, |
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(guid: &swidget->uuid)) |
368 | total_size += ext_size; |
369 | |
370 | comp = kzalloc(size: 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: &ipc_size, index: 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, object: host, token_id: SOF_PCM_TOKENS, tuples: swidget->tuples, |
423 | num_tuples: swidget->num_tuples, object_size: sizeof(*host), token_instance_num: 1); |
424 | if (ret < 0) |
425 | goto err; |
426 | |
427 | /* parse one set of comp_tokens */ |
428 | ret = sof_update_ipc_object(scomp, object: &host->config, token_id: SOF_COMP_TOKENS, tuples: swidget->tuples, |
429 | num_tuples: swidget->num_tuples, object_size: sizeof(host->config), token_instance_num: 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, config: &host->config); |
435 | |
436 | return 0; |
437 | err: |
438 | kfree(objp: 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(objp: swidget->private); |
447 | } |
448 | |
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: &ipc_size, index: 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, object: &tone->config, token_id: SOF_COMP_TOKENS, tuples: swidget->tuples, |
468 | num_tuples: swidget->num_tuples, object_size: sizeof(tone->config), token_instance_num: 1); |
469 | if (ret < 0) { |
470 | kfree(objp: 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, config: &tone->config); |
478 | |
479 | return 0; |
480 | } |
481 | |
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: &ipc_size, index: 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, object: &mixer->config, token_id: SOF_COMP_TOKENS, |
501 | tuples: swidget->tuples, num_tuples: swidget->num_tuples, |
502 | object_size: sizeof(mixer->config), token_instance_num: 1); |
503 | if (ret < 0) { |
504 | kfree(objp: 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, config: &mixer->config); |
512 | |
513 | return 0; |
514 | } |
515 | |
516 | static int sof_ipc3_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) |
517 | { |
518 | struct snd_soc_component *scomp = swidget->scomp; |
519 | struct snd_sof_pipeline *spipe = swidget->spipe; |
520 | struct sof_ipc_pipe_new *pipeline; |
521 | struct snd_sof_widget *comp_swidget; |
522 | int ret; |
523 | |
524 | pipeline = kzalloc(size: 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, name: 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, object: pipeline, token_id: SOF_SCHED_TOKENS, tuples: swidget->tuples, |
549 | num_tuples: swidget->num_tuples, object_size: sizeof(*pipeline), token_instance_num: 1); |
550 | if (ret < 0) |
551 | goto err; |
552 | |
553 | /* parse one set of pipeline tokens */ |
554 | ret = sof_update_ipc_object(scomp, object: swidget, token_id: SOF_PIPELINE_TOKENS, tuples: swidget->tuples, |
555 | num_tuples: swidget->num_tuples, object_size: sizeof(*swidget), token_instance_num: 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; |
572 | spipe->core_mask |= BIT(pipeline->core); |
573 | |
574 | return 0; |
575 | |
576 | err: |
577 | kfree(objp: swidget->private); |
578 | swidget->private = NULL; |
579 | |
580 | return ret; |
581 | } |
582 | |
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(size: 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, object: buffer, token_id: SOF_BUFFER_TOKENS, tuples: swidget->tuples, |
605 | num_tuples: swidget->num_tuples, object_size: sizeof(*buffer), token_instance_num: 1); |
606 | if (ret < 0) { |
607 | kfree(objp: 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 | |
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: &ipc_size, index: 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, object: src, token_id: SOF_SRC_TOKENS, tuples: swidget->tuples, |
637 | num_tuples: swidget->num_tuples, object_size: sizeof(*src), token_instance_num: 1); |
638 | if (ret < 0) |
639 | goto err; |
640 | |
641 | /* parse one set of comp tokens */ |
642 | ret = sof_update_ipc_object(scomp, object: &src->config, token_id: SOF_COMP_TOKENS, |
643 | tuples: swidget->tuples, num_tuples: swidget->num_tuples, object_size: sizeof(src->config), token_instance_num: 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, config: &src->config); |
650 | |
651 | return 0; |
652 | err: |
653 | kfree(objp: swidget->private); |
654 | swidget->private = NULL; |
655 | |
656 | return ret; |
657 | } |
658 | |
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: &ipc_size, index: 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, object: asrc, token_id: SOF_ASRC_TOKENS, tuples: swidget->tuples, |
678 | num_tuples: swidget->num_tuples, object_size: sizeof(*asrc), token_instance_num: 1); |
679 | if (ret < 0) |
680 | goto err; |
681 | |
682 | /* parse one set of comp tokens */ |
683 | ret = sof_update_ipc_object(scomp, object: &asrc->config, token_id: SOF_COMP_TOKENS, |
684 | tuples: swidget->tuples, num_tuples: swidget->num_tuples, object_size: sizeof(asrc->config), token_instance_num: 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, config: &asrc->config); |
693 | |
694 | return 0; |
695 | err: |
696 | kfree(objp: swidget->private); |
697 | swidget->private = NULL; |
698 | |
699 | return ret; |
700 | } |
701 | |
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: &ipc_size, index: 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, object: &mux->config, token_id: SOF_COMP_TOKENS, |
724 | tuples: swidget->tuples, num_tuples: swidget->num_tuples, object_size: sizeof(mux->config), token_instance_num: 1); |
725 | if (ret < 0) { |
726 | kfree(objp: 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, config: &mux->config); |
733 | |
734 | return 0; |
735 | } |
736 | |
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(c: 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: &ipc_size, index: 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, object: volume, token_id: SOF_VOLUME_TOKENS, tuples: swidget->tuples, |
763 | num_tuples: swidget->num_tuples, object_size: sizeof(*volume), token_instance_num: 1); |
764 | if (ret < 0) |
765 | goto err; |
766 | |
767 | /* parse one set of comp tokens */ |
768 | ret = sof_update_ipc_object(scomp, object: &volume->config, token_id: SOF_COMP_TOKENS, |
769 | tuples: swidget->tuples, num_tuples: swidget->num_tuples, |
770 | object_size: sizeof(volume->config), token_instance_num: 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, config: &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(objp: swidget->private); |
792 | swidget->private = NULL; |
793 | |
794 | return ret; |
795 | } |
796 | |
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; |
802 | struct sof_ipc_ctrl_data *cdata; |
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 | |
837 | cdata = wdata[i].control->ipc_control_data; |
838 | |
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 | } |
856 | |
857 | *size += wdata[i].pdata_size; |
858 | |
859 | /* get data type */ |
860 | switch (cdata->cmd) { |
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(n: widget->num_kcontrols, size: 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, size: &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: &ipc_size, index: 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, object: &process->config, token_id: SOF_COMP_TOKENS, |
925 | tuples: swidget->tuples, num_tuples: swidget->num_tuples, |
926 | object_size: sizeof(process->config), token_instance_num: 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, config: &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++) { |
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; |
946 | } |
947 | } |
948 | |
949 | process->size = ipc_data_size; |
950 | |
951 | kfree(objp: wdata); |
952 | |
953 | return 0; |
954 | err: |
955 | kfree(objp: swidget->private); |
956 | swidget->private = NULL; |
957 | out: |
958 | kfree(objp: 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, object: &config, token_id: SOF_PROCESS_TOKENS, tuples: swidget->tuples, |
990 | num_tuples: swidget->num_tuples, object_size: sizeof(config), token_instance_num: 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, type: find_process_comp_type(type: config.type)); |
996 | } |
997 | |
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, object: &config->hda, token_id: SOF_HDA_TOKENS, tuples: slink->tuples, |
1011 | num_tuples: slink->num_tuples, object_size: size, token_instance_num: 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(p: 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, object: &config->sai, token_id: SOF_SAI_TOKENS, tuples: slink->tuples, |
1080 | num_tuples: slink->num_tuples, object_size: size, token_instance_num: 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(p: 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, object: &config->esai, token_id: SOF_ESAI_TOKENS, tuples: slink->tuples, |
1131 | num_tuples: slink->num_tuples, object_size: size, token_instance_num: 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(p: config, size, GFP_KERNEL); |
1158 | if (!private->dai_config) |
1159 | return -ENOMEM; |
1160 | |
1161 | return 0; |
1162 | } |
1163 | |
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, object: &config->micfil, token_id: SOF_MICFIL_TOKENS, tuples: slink->tuples, |
1179 | num_tuples: slink->num_tuples, object_size: size, token_instance_num: 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(p: config, size, GFP_KERNEL); |
1189 | if (!private->dai_config) |
1190 | return -ENOMEM; |
1191 | |
1192 | return 0; |
1193 | } |
1194 | |
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); |
1201 | int ret; |
1202 | |
1203 | /* handle master/slave and inverted clocks */ |
1204 | sof_dai_set_format(hw_config, config); |
1205 | |
1206 | config->hdr.size = size; |
1207 | |
1208 | /* parse the required set of ACPDMIC tokens based on num_hw_cfgs */ |
1209 | ret = sof_update_ipc_object(scomp, object: &config->acpdmic, token_id: SOF_ACPDMIC_TOKENS, tuples: slink->tuples, |
1210 | num_tuples: slink->num_tuples, object_size: size, token_instance_num: slink->num_hw_configs); |
1211 | if (ret < 0) |
1212 | return ret; |
1213 | |
1214 | dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n" , |
1215 | config->dai_index, config->acpdmic.pdm_ch, |
1216 | config->acpdmic.pdm_rate); |
1217 | |
1218 | dai->number_configs = 1; |
1219 | dai->current_config = 0; |
1220 | private->dai_config = kmemdup(p: 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); |
1233 | int ret; |
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 | |
1242 | ret = sof_update_ipc_object(scomp, object: &config->acpbt, token_id: SOF_ACPI2S_TOKENS, tuples: slink->tuples, |
1243 | num_tuples: slink->num_tuples, object_size: size, token_instance_num: slink->num_hw_configs); |
1244 | if (ret < 0) |
1245 | return ret; |
1246 | |
1247 | dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d tdm_mode %d\n" , |
1248 | config->dai_index, config->acpbt.tdm_slots, |
1249 | config->acpbt.fsync_rate, config->acpbt.tdm_mode); |
1250 | |
1251 | dai->number_configs = 1; |
1252 | dai->current_config = 0; |
1253 | private->dai_config = kmemdup(p: 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); |
1266 | int ret; |
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 | |
1275 | ret = sof_update_ipc_object(scomp, object: &config->acpsp, token_id: SOF_ACPI2S_TOKENS, tuples: slink->tuples, |
1276 | num_tuples: slink->num_tuples, object_size: size, token_instance_num: slink->num_hw_configs); |
1277 | if (ret < 0) |
1278 | return ret; |
1279 | |
1280 | |
1281 | dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d tdm_mode %d\n" , |
1282 | config->dai_index, config->acpsp.tdm_slots, |
1283 | config->acpsp.fsync_rate, config->acpsp.tdm_mode); |
1284 | |
1285 | dai->number_configs = 1; |
1286 | dai->current_config = 0; |
1287 | private->dai_config = kmemdup(p: config, size, GFP_KERNEL); |
1288 | if (!private->dai_config) |
1289 | return -ENOMEM; |
1290 | |
1291 | return 0; |
1292 | } |
1293 | |
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); |
1300 | int ret; |
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 | |
1309 | ret = sof_update_ipc_object(scomp, object: &config->acphs, token_id: SOF_ACPI2S_TOKENS, tuples: slink->tuples, |
1310 | num_tuples: slink->num_tuples, object_size: size, token_instance_num: slink->num_hw_configs); |
1311 | if (ret < 0) |
1312 | return ret; |
1313 | |
1314 | dev_info(scomp->dev, "ACP_HS config ACP%d channel %d rate %d tdm_mode %d\n" , |
1315 | config->dai_index, config->acphs.tdm_slots, |
1316 | config->acphs.fsync_rate, config->acphs.tdm_mode); |
1317 | |
1318 | dai->number_configs = 1; |
1319 | dai->current_config = 0; |
1320 | private->dai_config = kmemdup(p: config, size, GFP_KERNEL); |
1321 | if (!private->dai_config) |
1322 | return -ENOMEM; |
1323 | |
1324 | return 0; |
1325 | } |
1326 | |
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, object: &config->acp_sdw, token_id: SOF_ACP_SDW_TOKENS, tuples: slink->tuples, |
1336 | num_tuples: slink->num_tuples, object_size: size, token_instance_num: 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(p: config, size, GFP_KERNEL); |
1349 | if (!private->dai_config) |
1350 | return -ENOMEM; |
1351 | |
1352 | return 0; |
1353 | } |
1354 | |
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, object: &config->afe, token_id: SOF_AFE_TOKENS, tuples: slink->tuples, |
1366 | num_tuples: slink->num_tuples, object_size: size, token_instance_num: 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(p: 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 | { |
1387 | struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(c: scomp); |
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, object: &config->ssp, token_id: SOF_SSP_TOKENS, tuples: slink->tuples, |
1398 | num_tuples: slink->num_tuples, object_size: size, token_instance_num: 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: &hw_config[i], config: &config[i]); |
1409 | |
1410 | config[i].hdr.size = size; |
1411 | |
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 | |
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(p: config, size: 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(c: 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, object: &config->dmic, token_id: SOF_DMIC_TOKENS, tuples: slink->tuples, |
1472 | num_tuples: slink->num_tuples, object_size: size, token_instance_num: 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, object: &config->dmic.pdm[0], token_id: SOF_DMIC_PDM_TOKENS, |
1478 | tuples: slink->tuples, num_tuples: slink->num_tuples, |
1479 | object_size: sizeof(struct sof_ipc_dai_dmic_pdm_ctrl), |
1480 | token_instance_num: 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(p: 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, object: &config->alh, token_id: SOF_ALH_TOKENS, tuples: slink->tuples, |
1538 | num_tuples: slink->num_tuples, object_size: size, token_instance_num: 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(p: 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(c: 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(size: 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: &ipc_size, index: 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, object: comp_dai, token_id: SOF_DAI_TOKENS, tuples: swidget->tuples, |
1586 | num_tuples: swidget->num_tuples, object_size: sizeof(*comp_dai), token_instance_num: 1); |
1587 | if (ret < 0) |
1588 | goto free; |
1589 | |
1590 | /* update comp_tokens */ |
1591 | ret = sof_update_ipc_object(scomp, object: &comp_dai->config, token_id: SOF_COMP_TOKENS, |
1592 | tuples: swidget->tuples, num_tuples: swidget->num_tuples, |
1593 | object_size: sizeof(comp_dai->config), token_instance_num: 1); |
1594 | if (ret < 0) |
1595 | goto free; |
1596 | |
1597 | dev_dbg(scomp->dev, "dai %s: type %d index %d\n" , |
1598 | swidget->widget->name, comp_dai->type, comp_dai->dai_index); |
1599 | sof_dbg_comp_config(scomp, config: &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(n: slink->num_hw_configs, size: 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, object: &common_config, token_id: SOF_DAI_LINK_TOKENS, |
1618 | tuples: slink->tuples, num_tuples: slink->num_tuples, |
1619 | object_size: sizeof(common_config), token_instance_num: 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; |
1649 | case SOF_DAI_IMX_MICFIL: |
1650 | ret = sof_link_micfil_load(scomp, slink, config, dai); |
1651 | break; |
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: |
1656 | case SOF_DAI_AMD_SP_VIRTUAL: |
1657 | ret = sof_link_acp_sp_load(scomp, slink, config, dai); |
1658 | break; |
1659 | case SOF_DAI_AMD_HS: |
1660 | case SOF_DAI_AMD_HS_VIRTUAL: |
1661 | ret = sof_link_acp_hs_load(scomp, slink, config, dai); |
1662 | break; |
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; |
1669 | case SOF_DAI_AMD_SDW: |
1670 | ret = sof_link_acp_sdw_load(scomp, slink, config, dai); |
1671 | break; |
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(objp: config); |
1681 | } |
1682 | |
1683 | return 0; |
1684 | free_config: |
1685 | kfree(objp: config); |
1686 | free_comp: |
1687 | kfree(objp: comp_dai); |
1688 | free: |
1689 | kfree(objp: 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(objp: dai_data->comp_dai); |
1709 | kfree(objp: dai_data->dai_config); |
1710 | kfree(objp: dai_data); |
1711 | } |
1712 | kfree(objp: dai); |
1713 | break; |
1714 | } |
1715 | default: |
1716 | break; |
1717 | } |
1718 | } |
1719 | |
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; |
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 */ |
1735 | ret = sof_ipc_tx_message_no_reply(ipc: sdev->ipc, msg_data: &connect, msg_bytes: sizeof(connect)); |
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 | |
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; |
1746 | size_t priv_size_check; |
1747 | int ret; |
1748 | |
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; |
1753 | } |
1754 | |
1755 | if (scontrol->priv_size > scontrol->max_size - sizeof(*cdata)) { |
1756 | dev_err(sdev->dev, |
1757 | "%s: bytes data size %zu exceeds max %zu.\n" , __func__, |
1758 | scontrol->priv_size, scontrol->max_size - sizeof(*cdata)); |
1759 | return -EINVAL; |
1760 | } |
1761 | |
1762 | scontrol->ipc_control_data = kzalloc(size: scontrol->max_size, GFP_KERNEL); |
1763 | if (!scontrol->ipc_control_data) |
1764 | return -ENOMEM; |
1765 | |
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(objp: scontrol->priv); |
1775 | scontrol->priv = NULL; |
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 | |
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); |
1794 | ret = -EINVAL; |
1795 | goto err; |
1796 | } |
1797 | } |
1798 | |
1799 | return 0; |
1800 | err: |
1801 | kfree(objp: scontrol->ipc_control_data); |
1802 | scontrol->ipc_control_data = NULL; |
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(size: 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(size: 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 */ |
1884 | return sof_ipc_tx_message_no_reply(ipc: sdev->ipc, msg_data: &fcomp, msg_bytes: sizeof(fcomp)); |
1885 | } |
1886 | |
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(c: scomp); |
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, name: 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(p: params) >> 3; |
1914 | pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; |
1915 | pcm.params.rate = params_rate(p: params); |
1916 | pcm.params.channels = params_channels(p: params); |
1917 | pcm.params.host_period_bytes = params_period_bytes(p: params); |
1918 | |
1919 | /* set format */ |
1920 | switch (params_format(p: 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 */ |
1935 | ret = sof_ipc_tx_message_no_reply(ipc: sdev->ipc, msg_data: &pcm, msg_bytes: sizeof(pcm)); |
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(c: scomp); |
1948 | struct sof_ipc_stream stream; |
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 */ |
1957 | ret = sof_ipc_tx_message_no_reply(ipc: sdev->ipc, msg_data: &stream, msg_bytes: sizeof(stream)); |
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, name: 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, dir: 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(w: swidget->widget, events: 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 | |
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; |
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 | |
2084 | ret = sof_ipc_tx_message_no_reply(ipc: sdev->ipc, msg_data: &ready, msg_bytes: sizeof(ready)); |
2085 | if (ret < 0) |
2086 | return ret; |
2087 | |
2088 | return 1; |
2089 | } |
2090 | |
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 | }; |
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 | |
2119 | ret = sof_ipc_tx_message_no_reply(ipc: sdev->ipc, msg_data: &ipc_free, msg_bytes: sizeof(ipc_free)); |
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; |
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) { |
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; |
2172 | config->alh.stream_id = data->dai_data; |
2173 | } |
2174 | break; |
2175 | default: |
2176 | break; |
2177 | } |
2178 | |
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 | |
2199 | if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) { |
2200 | /* Clear stale command */ |
2201 | config->flags &= ~SOF_DAI_CONFIG_FLAGS_CMD_MASK; |
2202 | config->flags |= flags; |
2203 | } else { |
2204 | config->flags = flags; |
2205 | } |
2206 | |
2207 | /* only send the IPC if the widget is set up in the DSP */ |
2208 | if (swidget->use_count > 0) { |
2209 | ret = sof_ipc_tx_message_no_reply(ipc: sdev->ipc, msg_data: config, msg_bytes: config->hdr.size); |
2210 | if (ret < 0) |
2211 | dev_err(sdev->dev, "Failed to set dai config for %s\n" , dai->name); |
2212 | |
2213 | /* clear the flags once the IPC has been sent even if it fails */ |
2214 | config->flags = SOF_DAI_CONFIG_FLAGS_NONE; |
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 | { |
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 | |
2235 | ret = sof_ipc_tx_message_no_reply(ipc: sdev->ipc, msg_data: dai_data->comp_dai, msg_bytes: comp->hdr.size); |
2236 | break; |
2237 | } |
2238 | case snd_soc_dapm_scheduler: |
2239 | { |
2240 | struct sof_ipc_pipe_new *pipeline; |
2241 | |
2242 | pipeline = swidget->private; |
2243 | ret = sof_ipc_tx_message_no_reply(ipc: sdev->ipc, msg_data: pipeline, msg_bytes: sizeof(*pipeline)); |
2244 | break; |
2245 | } |
2246 | default: |
2247 | { |
2248 | struct sof_ipc_cmd_hdr *hdr; |
2249 | |
2250 | hdr = swidget->private; |
2251 | ret = sof_ipc_tx_message_no_reply(ipc: sdev->ipc, msg_data: swidget->private, msg_bytes: hdr->size); |
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 | |
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, wsource: sroute->src_widget->widget, |
2326 | wsink: 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 | |
2347 | swidget->spipe->complete = sof_ipc3_complete_pipeline(sdev, swidget); |
2348 | if (swidget->spipe->complete < 0) |
2349 | return swidget->spipe->complete; |
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 | |
2378 | if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored) |
2379 | continue; |
2380 | |
2381 | if (spcm->stream[dir].list) { |
2382 | ret = sof_pcm_stream_free(sdev, substream, spcm, dir, free_widget_list: 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 | |
2403 | static int sof_ipc3_free_widgets_in_list(struct snd_sof_dev *sdev, bool include_scheduler, |
2404 | bool *dyn_widgets, bool verify) |
2405 | { |
2406 | struct sof_ipc_fw_version *v = &sdev->fw_ready.version; |
2407 | struct snd_sof_widget *swidget; |
2408 | int ret; |
2409 | |
2410 | list_for_each_entry(swidget, &sdev->widget_list, list) { |
2411 | if (swidget->dynamic_pipeline_widget) { |
2412 | *dyn_widgets = true; |
2413 | continue; |
2414 | } |
2415 | |
2416 | /* Do not free widgets for static pipelines with FW older than SOF2.2 */ |
2417 | if (!verify && !swidget->dynamic_pipeline_widget && |
2418 | SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) { |
2419 | mutex_lock(&swidget->setup_mutex); |
2420 | swidget->use_count = 0; |
2421 | mutex_unlock(lock: &swidget->setup_mutex); |
2422 | if (swidget->spipe) |
2423 | swidget->spipe->complete = 0; |
2424 | continue; |
2425 | } |
2426 | |
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 | |
2433 | ret = sof_widget_free(sdev, swidget); |
2434 | if (ret < 0) |
2435 | return ret; |
2436 | } |
2437 | |
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, include_scheduler: false, dyn_widgets: &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, include_scheduler: true, dyn_widgets: &dyn_widgets, verify); |
2467 | if (ret < 0) |
2468 | return ret; |
2469 | |
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. |
2473 | * Skip this step for older firmware unless topology has any |
2474 | * dynamic pipeline (in which case the step is mandatory). |
2475 | */ |
2476 | if (!verify && (dyn_widgets || SOF_FW_VER(v->major, v->minor, v->micro) >= |
2477 | SOF_FW_VER(2, 2, 0))) { |
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 | |
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 | |
2500 | return 0; |
2501 | } |
2502 | |
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 | |
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, |
2550 | "Topology: ABI %d:%d:%d Kernel ABI %d:%d:%d\n" , |
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 | |
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; |
2565 | } |
2566 | |
2567 | return 0; |
2568 | } |
2569 | |
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 | |
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 | |
2596 | static enum sof_tokens comp_generic_token_list[] = { |
2597 | SOF_CORE_TOKENS, |
2598 | SOF_COMP_EXT_TOKENS, |
2599 | SOF_COMP_TOKENS, |
2600 | }; |
2601 | |
2602 | static enum sof_tokens buffer_token_list[] = { |
2603 | SOF_BUFFER_TOKENS, |
2604 | }; |
2605 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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] = {.ipc_setup: sof_ipc3_widget_setup_comp_host, .ipc_free: sof_ipc3_widget_free_comp, |
2652 | .token_list: host_token_list, ARRAY_SIZE(host_token_list), NULL}, |
2653 | |
2654 | [snd_soc_dapm_dai_in] = {.ipc_setup: sof_ipc3_widget_setup_comp_dai, .ipc_free: sof_ipc3_widget_free_comp_dai, |
2655 | .token_list: dai_token_list, ARRAY_SIZE(dai_token_list), NULL}, |
2656 | [snd_soc_dapm_dai_out] = {.ipc_setup: sof_ipc3_widget_setup_comp_dai, .ipc_free: sof_ipc3_widget_free_comp_dai, |
2657 | .token_list: dai_token_list, ARRAY_SIZE(dai_token_list), NULL}, |
2658 | [snd_soc_dapm_buffer] = {.ipc_setup: sof_ipc3_widget_setup_comp_buffer, .ipc_free: sof_ipc3_widget_free_comp, |
2659 | .token_list: buffer_token_list, ARRAY_SIZE(buffer_token_list), NULL}, |
2660 | [snd_soc_dapm_mixer] = {.ipc_setup: sof_ipc3_widget_setup_comp_mixer, .ipc_free: sof_ipc3_widget_free_comp, |
2661 | .token_list: comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), |
2662 | NULL}, |
2663 | [snd_soc_dapm_src] = {.ipc_setup: sof_ipc3_widget_setup_comp_src, .ipc_free: sof_ipc3_widget_free_comp, |
2664 | .token_list: src_token_list, ARRAY_SIZE(src_token_list), NULL}, |
2665 | [snd_soc_dapm_asrc] = {.ipc_setup: sof_ipc3_widget_setup_comp_asrc, .ipc_free: sof_ipc3_widget_free_comp, |
2666 | .token_list: asrc_token_list, ARRAY_SIZE(asrc_token_list), NULL}, |
2667 | [snd_soc_dapm_siggen] = {.ipc_setup: sof_ipc3_widget_setup_comp_tone, .ipc_free: sof_ipc3_widget_free_comp, |
2668 | .token_list: comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), |
2669 | NULL}, |
2670 | [snd_soc_dapm_scheduler] = {.ipc_setup: sof_ipc3_widget_setup_comp_pipeline, .ipc_free: sof_ipc3_widget_free_comp, |
2671 | .token_list: pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL}, |
2672 | [snd_soc_dapm_pga] = {.ipc_setup: sof_ipc3_widget_setup_comp_pga, .ipc_free: sof_ipc3_widget_free_comp, |
2673 | .token_list: pga_token_list, ARRAY_SIZE(pga_token_list), NULL}, |
2674 | [snd_soc_dapm_mux] = {.ipc_setup: sof_ipc3_widget_setup_comp_mux, .ipc_free: sof_ipc3_widget_free_comp, |
2675 | .token_list: comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), NULL}, |
2676 | [snd_soc_dapm_demux] = {.ipc_setup: sof_ipc3_widget_setup_comp_mux, .ipc_free: sof_ipc3_widget_free_comp, |
2677 | .token_list: comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), |
2678 | NULL}, |
2679 | [snd_soc_dapm_effect] = {.ipc_setup: sof_widget_update_ipc_comp_process, .ipc_free: sof_ipc3_widget_free_comp, |
2680 | .token_list: process_token_list, ARRAY_SIZE(process_token_list), |
2681 | .bind_event: sof_ipc3_widget_bind_event}, |
2682 | }; |
2683 | |
2684 | const struct sof_ipc_tplg_ops ipc3_tplg_ops = { |
2685 | .widget = tplg_ipc3_widget_ops, |
2686 | .control = &tplg_ipc3_control_ops, |
2687 | .route_setup = sof_ipc3_route_setup, |
2688 | .control_setup = sof_ipc3_control_setup, |
2689 | .control_free = sof_ipc3_control_free, |
2690 | .pipeline_complete = sof_ipc3_complete_pipeline, |
2691 | .token_list = ipc3_token_list, |
2692 | .widget_free = sof_ipc3_widget_free, |
2693 | .widget_setup = sof_ipc3_widget_setup, |
2694 | .dai_config = sof_ipc3_dai_config, |
2695 | .dai_get_clk = sof_ipc3_dai_get_clk, |
2696 | .set_up_all_pipelines = sof_ipc3_set_up_all_pipelines, |
2697 | .tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines, |
2698 | .parse_manifest = sof_ipc3_parse_manifest, |
2699 | .link_setup = sof_ipc3_link_setup, |
2700 | }; |
2701 | |