-
Notifications
You must be signed in to change notification settings - Fork 350
Expand file tree
/
Copy pathdai.c
More file actions
437 lines (387 loc) · 11.5 KB
/
dai.c
File metadata and controls
437 lines (387 loc) · 11.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
// SPDX-License-Identifier: BSD-3-Clause
//
// Copyright(c) 2016 Intel Corporation. All rights reserved.
//
// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
// Keyon Jie <yang.jie@linux.intel.com>
#include <sof/audio/component_ext.h>
#include <sof/audio/ipc-config.h>
#include <rtos/idc.h>
#include <sof/ipc/topology.h>
#include <sof/ipc/common.h>
#include <sof/ipc/msg.h>
#include <sof/ipc/driver.h>
#include <sof/lib/dai.h>
#include <sof/lib/notifier.h>
#include <sof/drivers/afe-dai.h>
#include <sof/drivers/edma.h>
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/* IPC3 headers */
#include <ipc/dai.h>
#include <ipc/header.h>
#include <ipc/stream.h>
#include <ipc/topology.h>
LOG_MODULE_DECLARE(ipc, CONFIG_SOF_LOG_LEVEL);
void dai_set_link_hda_config(uint16_t *link_config,
struct ipc_config_dai *common_config,
const void *spec_config)
{ }
int dai_config_dma_channel(struct dai_data *dd, struct comp_dev *dev, const void *spec_config)
{
const struct sof_ipc_dai_config *config = spec_config;
struct ipc_config_dai *dai = &dd->ipc_config;
int channel;
int handshake;
assert(config);
switch (config->type) {
case SOF_DAI_INTEL_SSP:
COMPILER_FALLTHROUGH;
case SOF_DAI_INTEL_DMIC:
channel = 0;
break;
case SOF_DAI_INTEL_HDA:
channel = config->hda.link_dma_ch;
break;
case SOF_DAI_INTEL_ALH:
/* As with HDA, the DMA channel is assigned in runtime,
* not during topology parsing.
*/
channel = config->alh.stream_id;
break;
case SOF_DAI_IMX_SAI:
COMPILER_FALLTHROUGH;
case SOF_DAI_IMX_ESAI:
handshake = dai_get_handshake(dd->dai, dai->direction,
dd->stream_id);
/* TODO: remove this when transition to native drivers is complete on all NXP platforms */
#ifndef CONFIG_ZEPHYR_NATIVE_DRIVERS
channel = EDMA_HS_GET_CHAN(handshake);
#else
channel = handshake & GENMASK(7, 0);
#endif /* CONFIG_ZEPHYR_NATIVE_DRIVERS */
break;
case SOF_DAI_IMX_MICFIL:
channel = dai_get_handshake(dd->dai, dai->direction,
dd->stream_id);
/* TODO: remove ifdef when transitioning to native drivers is complete on all NXP platforms */
#ifdef CONFIG_ZEPHYR_NATIVE_DRIVERS
channel = channel & GENMASK(7, 0);
#endif
break;
case SOF_DAI_AMD_BT:
channel = dai_get_handshake(dd->dai, dai->direction,
dd->stream_id);
break;
case SOF_DAI_AMD_SP:
case SOF_DAI_AMD_SP_VIRTUAL:
channel = dai_get_handshake(dd->dai, dai->direction,
dd->stream_id);
break;
case SOF_DAI_AMD_DMIC:
channel = dai_get_handshake(dd->dai, dai->direction,
dd->stream_id);
break;
case SOF_DAI_AMD_HS:
case SOF_DAI_AMD_HS_VIRTUAL:
case SOF_DAI_AMD_SDW:
channel = dai_get_handshake(dd->dai, dai->direction,
dd->stream_id);
#if defined(CONFIG_SOC_ACP_7_0)
if (channel >= 64 && channel < 128) {
channel = channel - 64;
}
#endif
break;
case SOF_DAI_MEDIATEK_AFE:
handshake = dai_get_handshake(dd->dai, dai->direction,
dd->stream_id);
channel = AFE_HS_GET_CHAN(handshake);
break;
default:
/* other types of DAIs not handled for now */
comp_err(dev, "Unknown dai type %d",
config->type);
channel = SOF_DMA_CHAN_INVALID;
break;
}
return channel;
}
int ipc_dai_data_config(struct dai_data *dd, struct comp_dev *dev)
{
struct ipc_config_dai *dai = &dd->ipc_config;
struct sof_ipc_dai_config *config = ipc_from_dai_config(dd->dai_spec_config);
if (!config) {
comp_err(dev, "no config set for dai %d type %d",
dai->dai_index, dai->type);
return -EINVAL;
}
comp_info(dev, "dai_data_config() dai type = %d index = %d dd %p",
dai->type, dai->dai_index, dd);
/* cannot configure DAI while active */
if (dev->state == COMP_STATE_ACTIVE) {
comp_info(dev, "Component is in active state.");
return 0;
}
/* validate direction */
if (dai->direction != SOF_IPC_STREAM_PLAYBACK &&
dai->direction != SOF_IPC_STREAM_CAPTURE) {
comp_err(dev, "no direction set for dai %d type %d",
dai->dai_index, dai->type);
return -EINVAL;
}
switch (config->type) {
case SOF_DAI_INTEL_SSP:
/* set dma burst elems to slot number */
dd->config.burst_elems = config->ssp.tdm_slots;
break;
case SOF_DAI_INTEL_DMIC:
/* Depth is passed by DMIC driver that retrieves it from blob */
dd->config.burst_elems = dai_get_fifo_depth(dd->dai, dai->direction);
comp_info(dev, "dai_data_config() burst_elems = %d", dd->config.burst_elems);
break;
case SOF_DAI_INTEL_HDA:
break;
case SOF_DAI_INTEL_ALH:
/* SDW HW FIFO always requires 32bit MSB aligned sample data for
* all formats, such as 8/16/24/32 bits.
*/
dev->ipc_config.frame_fmt = SOF_IPC_FRAME_S32_LE;
if (dd->dma_buffer)
audio_stream_set_frm_fmt(&dd->dma_buffer->stream,
dev->ipc_config.frame_fmt);
dd->config.burst_elems = dai_get_fifo_depth(dd->dai, dai->direction);
/* As with HDA, the DMA channel is assigned in runtime,
* not during topology parsing.
*/
dd->stream_id = config->alh.stream_id;
break;
case SOF_DAI_IMX_MICFIL:
case SOF_DAI_IMX_SAI:
case SOF_DAI_IMX_ESAI:
dd->config.burst_elems = dai_get_fifo_depth(dd->dai, dai->direction);
break;
case SOF_DAI_AMD_BT:
dev->ipc_config.frame_fmt = SOF_IPC_FRAME_S16_LE;
break;
case SOF_DAI_AMD_SP:
case SOF_DAI_AMD_SP_VIRTUAL:
dev->ipc_config.frame_fmt = SOF_IPC_FRAME_S16_LE;
break;
case SOF_DAI_AMD_DMIC:
dev->ipc_config.frame_fmt = SOF_IPC_FRAME_S32_LE;
if (dd->dma_buffer)
audio_stream_set_frm_fmt(&dd->dma_buffer->stream,
dev->ipc_config.frame_fmt);
break;
case SOF_DAI_AMD_HS:
case SOF_DAI_AMD_HS_VIRTUAL:
case SOF_DAI_AMD_SDW:
#define SDW_INSTANCES 2
#if defined(CONFIG_SOC_ACP_6_0)
break;
#else
struct acp_dma_dev_data *dev_data = dd->dma->z_dev->data;
struct sdw_pin_data *pin_data;
/* Allocate memory only if not already allocated */
if (!dev_data->dai_index_ptr) {
pin_data = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT,
sizeof(*pin_data));
dev_data->dai_index_ptr = pin_data;
} else {
pin_data = dev_data->dai_index_ptr;
}
pin_data->pin_num = dd->dai->index;
pin_data->pin_dir = dai->direction;
pin_data->dma_channel = dd->chan ? dd->chan->index : 0xFFFF;
pin_data->index = 0xFFFF;
pin_data->instance = 0xFFFF;
dev_data->dai_index_ptr = pin_data;
break;
#endif
case SOF_DAI_MEDIATEK_AFE:
break;
default:
/* other types of DAIs not handled for now */
comp_warn(dev, "Unknown dai type %d",
config->type);
break;
}
/* some DAIs may not need extra config */
return 0;
}
int ipc_comp_dai_config(struct ipc *ipc, struct ipc_config_dai *common_config,
void *spec_config)
{
struct sof_ipc_dai_config __maybe_unused *config = spec_config;
bool comp_on_core[CONFIG_CORE_COUNT] = { false };
struct sof_ipc_reply reply;
struct ipc_comp_dev *icd;
struct list_item *clist;
int ret = -ENODEV;
int i;
tr_info(&ipc_tr, "dai type = %d index = %d",
config->type, config->dai_index);
/* for each component */
list_for_item(clist, &ipc->comp_list) {
icd = container_of(clist, struct ipc_comp_dev, list);
/* make sure we only config DAI comps */
if (icd->type != COMP_TYPE_COMPONENT)
continue;
if (!cpu_is_me(icd->core)) {
comp_on_core[icd->core] = true;
ret = 0;
continue;
}
if (dev_comp_type(icd->cd) == SOF_COMP_DAI ||
dev_comp_type(icd->cd) == SOF_COMP_SG_DAI) {
ret = comp_dai_config(icd->cd, common_config, spec_config);
if (ret < 0)
break;
}
}
if (ret < 0) {
tr_err(&ipc_tr, "comp_dai_config() failed");
return ret;
}
/* message forwarded only by primary core */
if (cpu_is_primary(cpu_get_id())) {
for (i = 0; i < CONFIG_CORE_COUNT; ++i) {
if (!comp_on_core[i])
continue;
/*
* TODO: can secondary cores execute dai_config() in
* parallel? Then we could just wait for the last of
* them. For now execute them sequentially.
*/
ret = ipc_process_on_core(i, true);
if (ret < 0)
break;
/* check whether IPC failed on secondary core */
mailbox_hostbox_read(&reply, sizeof(reply), 0,
sizeof(reply));
if (reply.error < 0) {
/* error reply already written */
ret = 1;
break;
}
}
/* We have waited until all secondary cores configured their DAIs */
ipc->core = PLATFORM_PRIMARY_CORE_ID;
}
return ret;
}
void dai_dma_release(struct dai_data *dd, struct comp_dev *dev)
{
/* cannot configure DAI while active */
if (dev->state == COMP_STATE_ACTIVE) {
comp_info(dev, "Component is in active state. Ignore resetting");
return;
}
/* put the allocated DMA channel first */
if (dd->chan) {
/* remove callback */
notifier_unregister(dev, dd->chan, NOTIFIER_ID_DMA_COPY);
#if CONFIG_ZEPHYR_NATIVE_DRIVERS
dma_release_channel(dd->chan->dma->z_dev, dd->chan->index);
#else
dma_channel_put_legacy(dd->chan);
#endif
dd->chan->dev_data = NULL;
dd->chan = NULL;
}
}
int dai_config(struct dai_data *dd, struct comp_dev *dev, struct ipc_config_dai *common_config,
const void *spec_config)
{
const struct sof_ipc_dai_config *config = spec_config;
int ret;
/* ignore if message not for this DAI id/type */
if (dd->ipc_config.dai_index != config->dai_index ||
dd->ipc_config.type != config->type)
return 0;
comp_info(dev, "dai type = %d index = %d dd %p",
config->type, config->dai_index, dd);
/* cannot configure DAI while active */
if (dev->state == COMP_STATE_ACTIVE) {
comp_info(dev, "Component is in active state. Ignore config");
return 0;
}
dd->dai_dev = dev;
switch (config->flags & SOF_DAI_CONFIG_FLAGS_CMD_MASK) {
case SOF_DAI_CONFIG_FLAGS_HW_PARAMS:
/* set the delayed_dma_stop flag */
if (SOF_DAI_QUIRK_IS_SET(config->flags, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP))
dd->delayed_dma_stop = true;
if (dd->chan) {
comp_info(dev, "Configured. dma channel index %d, ignore...",
dd->chan->index);
return 0;
}
break;
case SOF_DAI_CONFIG_FLAGS_HW_FREE:
if (!dd->chan)
return 0;
/* stop DMA and reset config for two-step stop DMA */
if (dd->delayed_dma_stop) {
#if CONFIG_ZEPHYR_NATIVE_DRIVERS
ret = dma_stop(dd->chan->dma->z_dev, dd->chan->index);
#else
ret = dma_stop_delayed_legacy(dd->chan);
#endif
if (ret < 0)
return ret;
dai_dma_release(dd, dev);
}
return 0;
case SOF_DAI_CONFIG_FLAGS_PAUSE:
if (!dd->chan)
return 0;
#if CONFIG_ZEPHYR_NATIVE_DRIVERS
return dma_stop(dd->chan->dma->z_dev, dd->chan->index);
#else
return dma_stop_delayed_legacy(dd->chan);
#endif
default:
break;
}
#if CONFIG_COMP_DAI_GROUP
if (config->group_id) {
ret = dai_assign_group(dd, dev, config->group_id);
if (ret)
return ret;
}
#endif
/* do nothing for asking for channel free, for compatibility. */
if (dai_config_dma_channel(dd, dev, spec_config) == SOF_DMA_CHAN_INVALID)
return 0;
/* allocated dai_config if not yet */
if (!dd->dai_spec_config) {
dd->dai_spec_config = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT,
sizeof(struct sof_ipc_dai_config));
if (!dd->dai_spec_config) {
comp_err(dev, "No memory");
return -ENOMEM;
}
}
ret = memcpy_s(dd->dai_spec_config, sizeof(struct sof_ipc_dai_config), config,
sizeof(struct sof_ipc_dai_config));
if (ret < 0) {
rfree(dd->dai_spec_config);
dd->dai_spec_config = NULL;
}
return ret;
}
int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn)
{
struct dai_data *dd = comp_get_drvdata(dev);
/* TODO: improve accuracy by adding current DMA position */
posn->dai_posn = dd->total_data_processed;
/* set stream start wallclock */
posn->wallclock = dd->wallclock;
return 0;
}
void dai_dma_position_update(struct dai_data *dd, struct comp_dev *dev) { }
void dai_release_llp_slot(struct dai_data *dd) { }