0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/pm_runtime.h>
0014 #include <sound/pcm_params.h>
0015 #include <sound/sof.h>
0016 #include "sof-priv.h"
0017 #include "sof-audio.h"
0018 #include "sof-utils.h"
0019 #include "ops.h"
0020
0021
0022 static int create_page_table(struct snd_soc_component *component,
0023 struct snd_pcm_substream *substream,
0024 unsigned char *dma_area, size_t size)
0025 {
0026 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0027 struct snd_sof_pcm *spcm;
0028 struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
0029 int stream = substream->stream;
0030
0031 spcm = snd_sof_find_spcm_dai(component, rtd);
0032 if (!spcm)
0033 return -EINVAL;
0034
0035 return snd_sof_create_page_table(component->dev, dmab,
0036 spcm->stream[stream].page_table.area, size);
0037 }
0038
0039
0040
0041
0042 static void snd_sof_pcm_period_elapsed_work(struct work_struct *work)
0043 {
0044 struct snd_sof_pcm_stream *sps =
0045 container_of(work, struct snd_sof_pcm_stream,
0046 period_elapsed_work);
0047
0048 snd_pcm_period_elapsed(sps->substream);
0049 }
0050
0051 void snd_sof_pcm_init_elapsed_work(struct work_struct *work)
0052 {
0053 INIT_WORK(work, snd_sof_pcm_period_elapsed_work);
0054 }
0055
0056
0057
0058
0059 void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream)
0060 {
0061 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0062 struct snd_soc_component *component =
0063 snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
0064 struct snd_sof_pcm *spcm;
0065
0066 spcm = snd_sof_find_spcm_dai(component, rtd);
0067 if (!spcm) {
0068 dev_err(component->dev,
0069 "error: period elapsed for unknown stream!\n");
0070 return;
0071 }
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081 schedule_work(&spcm->stream[substream->stream].period_elapsed_work);
0082 }
0083 EXPORT_SYMBOL(snd_sof_pcm_period_elapsed);
0084
0085 static int
0086 sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
0087 struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params,
0088 struct snd_sof_platform_stream_params *platform_params, int dir)
0089 {
0090 struct snd_soc_dai *dai;
0091 int ret, j;
0092
0093
0094 for_each_rtd_cpu_dais(rtd, j, dai) {
0095 struct snd_soc_dapm_widget_list *list;
0096
0097 ret = snd_soc_dapm_dai_get_connected_widgets(dai, dir, &list,
0098 dpcm_end_walk_at_be);
0099 if (ret < 0) {
0100 dev_err(sdev->dev, "error: dai %s has no valid %s path\n", dai->name,
0101 dir == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture");
0102 return ret;
0103 }
0104
0105 spcm->stream[dir].list = list;
0106
0107 ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir);
0108 if (ret < 0) {
0109 dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n",
0110 spcm->pcm.pcm_id, dir);
0111 spcm->stream[dir].list = NULL;
0112 snd_soc_dapm_dai_free_widgets(&list);
0113 return ret;
0114 }
0115 }
0116
0117 return 0;
0118 }
0119
0120 static int sof_pcm_hw_params(struct snd_soc_component *component,
0121 struct snd_pcm_substream *substream,
0122 struct snd_pcm_hw_params *params)
0123 {
0124 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
0125 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0126 struct snd_sof_platform_stream_params platform_params = { 0 };
0127 const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
0128 struct snd_pcm_runtime *runtime = substream->runtime;
0129 struct snd_sof_pcm *spcm;
0130 int ret;
0131
0132
0133 if (rtd->dai_link->no_pcm)
0134 return 0;
0135
0136 spcm = snd_sof_find_spcm_dai(component, rtd);
0137 if (!spcm)
0138 return -EINVAL;
0139
0140
0141
0142
0143
0144 if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
0145 ret = pcm_ops->hw_free(component, substream);
0146 if (ret < 0)
0147 return ret;
0148
0149 spcm->prepared[substream->stream] = false;
0150 }
0151
0152 dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n",
0153 spcm->pcm.pcm_id, substream->stream);
0154
0155 ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params);
0156 if (ret < 0) {
0157 dev_err(component->dev, "platform hw params failed\n");
0158 return ret;
0159 }
0160
0161
0162 if (!spcm->stream[substream->stream].list) {
0163 ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params,
0164 substream->stream);
0165 if (ret < 0)
0166 return ret;
0167 }
0168
0169
0170 if (runtime->buffer_changed) {
0171 ret = create_page_table(component, substream, runtime->dma_area,
0172 runtime->dma_bytes);
0173
0174 if (ret < 0)
0175 return ret;
0176 }
0177
0178 if (pcm_ops->hw_params) {
0179 ret = pcm_ops->hw_params(component, substream, params, &platform_params);
0180 if (ret < 0)
0181 return ret;
0182 }
0183
0184 spcm->prepared[substream->stream] = true;
0185
0186
0187 memcpy(&spcm->params[substream->stream], params, sizeof(*params));
0188
0189 return 0;
0190 }
0191
0192 static int sof_pcm_hw_free(struct snd_soc_component *component,
0193 struct snd_pcm_substream *substream)
0194 {
0195 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0196 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
0197 const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
0198 struct snd_sof_pcm *spcm;
0199 int ret, err = 0;
0200
0201
0202 if (rtd->dai_link->no_pcm)
0203 return 0;
0204
0205 spcm = snd_sof_find_spcm_dai(component, rtd);
0206 if (!spcm)
0207 return -EINVAL;
0208
0209 dev_dbg(component->dev, "pcm: free stream %d dir %d\n",
0210 spcm->pcm.pcm_id, substream->stream);
0211
0212
0213 if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
0214 ret = pcm_ops->hw_free(component, substream);
0215 if (ret < 0)
0216 err = ret;
0217
0218 spcm->prepared[substream->stream] = false;
0219 }
0220
0221
0222 ret = snd_sof_pcm_platform_hw_free(sdev, substream);
0223 if (ret < 0) {
0224 dev_err(component->dev, "error: platform hw free failed\n");
0225 err = ret;
0226 }
0227
0228
0229 ret = sof_widget_list_free(sdev, spcm, substream->stream);
0230 if (ret < 0)
0231 err = ret;
0232
0233 cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work);
0234
0235 return err;
0236 }
0237
0238 static int sof_pcm_prepare(struct snd_soc_component *component,
0239 struct snd_pcm_substream *substream)
0240 {
0241 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0242 struct snd_sof_pcm *spcm;
0243 int ret;
0244
0245
0246 if (rtd->dai_link->no_pcm)
0247 return 0;
0248
0249 spcm = snd_sof_find_spcm_dai(component, rtd);
0250 if (!spcm)
0251 return -EINVAL;
0252
0253 if (spcm->prepared[substream->stream])
0254 return 0;
0255
0256 dev_dbg(component->dev, "pcm: prepare stream %d dir %d\n",
0257 spcm->pcm.pcm_id, substream->stream);
0258
0259
0260 ret = sof_pcm_hw_params(component,
0261 substream, &spcm->params[substream->stream]);
0262 if (ret < 0) {
0263 dev_err(component->dev,
0264 "error: set pcm hw_params after resume\n");
0265 return ret;
0266 }
0267
0268 return 0;
0269 }
0270
0271
0272
0273
0274
0275 static int sof_pcm_trigger(struct snd_soc_component *component,
0276 struct snd_pcm_substream *substream, int cmd)
0277 {
0278 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0279 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
0280 const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
0281 struct snd_sof_pcm *spcm;
0282 bool reset_hw_params = false;
0283 bool free_widget_list = false;
0284 bool ipc_first = false;
0285 int ret = 0;
0286
0287
0288 if (rtd->dai_link->no_pcm)
0289 return 0;
0290
0291 spcm = snd_sof_find_spcm_dai(component, rtd);
0292 if (!spcm)
0293 return -EINVAL;
0294
0295 dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n",
0296 spcm->pcm.pcm_id, substream->stream, cmd);
0297
0298 switch (cmd) {
0299 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0300 ipc_first = true;
0301 break;
0302 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0303 break;
0304 case SNDRV_PCM_TRIGGER_START:
0305 if (spcm->stream[substream->stream].suspend_ignored) {
0306
0307
0308
0309
0310
0311 spcm->stream[substream->stream].suspend_ignored = false;
0312 return 0;
0313 }
0314 break;
0315 case SNDRV_PCM_TRIGGER_SUSPEND:
0316 if (sdev->system_suspend_target == SOF_SUSPEND_S0IX &&
0317 spcm->stream[substream->stream].d0i3_compatible) {
0318
0319
0320
0321
0322
0323
0324 spcm->stream[substream->stream].suspend_ignored = true;
0325 return 0;
0326 }
0327 free_widget_list = true;
0328 fallthrough;
0329 case SNDRV_PCM_TRIGGER_STOP:
0330 ipc_first = true;
0331 reset_hw_params = true;
0332 break;
0333 default:
0334 dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd);
0335 return -EINVAL;
0336 }
0337
0338
0339
0340
0341
0342 if (!ipc_first)
0343 snd_sof_pcm_platform_trigger(sdev, substream, cmd);
0344
0345 if (pcm_ops->trigger)
0346 ret = pcm_ops->trigger(component, substream, cmd);
0347
0348
0349 if (ipc_first)
0350 snd_sof_pcm_platform_trigger(sdev, substream, cmd);
0351
0352
0353 if (!ret && reset_hw_params)
0354 ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream,
0355 free_widget_list);
0356
0357 return ret;
0358 }
0359
0360 static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component,
0361 struct snd_pcm_substream *substream)
0362 {
0363 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0364 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
0365 struct snd_sof_pcm *spcm;
0366 snd_pcm_uframes_t host, dai;
0367
0368
0369 if (rtd->dai_link->no_pcm)
0370 return 0;
0371
0372
0373 if (sof_ops(sdev)->pcm_pointer)
0374 return sof_ops(sdev)->pcm_pointer(sdev, substream);
0375
0376 spcm = snd_sof_find_spcm_dai(component, rtd);
0377 if (!spcm)
0378 return -EINVAL;
0379
0380
0381 host = bytes_to_frames(substream->runtime,
0382 spcm->stream[substream->stream].posn.host_posn);
0383 dai = bytes_to_frames(substream->runtime,
0384 spcm->stream[substream->stream].posn.dai_posn);
0385
0386 dev_vdbg(component->dev,
0387 "PCM: stream %d dir %d DMA position %lu DAI position %lu\n",
0388 spcm->pcm.pcm_id, substream->stream, host, dai);
0389
0390 return host;
0391 }
0392
0393 static int sof_pcm_open(struct snd_soc_component *component,
0394 struct snd_pcm_substream *substream)
0395 {
0396 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0397 struct snd_pcm_runtime *runtime = substream->runtime;
0398 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
0399 struct snd_sof_dsp_ops *ops = sof_ops(sdev);
0400 struct snd_sof_pcm *spcm;
0401 struct snd_soc_tplg_stream_caps *caps;
0402 int ret;
0403
0404
0405 if (rtd->dai_link->no_pcm)
0406 return 0;
0407
0408 spcm = snd_sof_find_spcm_dai(component, rtd);
0409 if (!spcm)
0410 return -EINVAL;
0411
0412 dev_dbg(component->dev, "pcm: open stream %d dir %d\n",
0413 spcm->pcm.pcm_id, substream->stream);
0414
0415
0416 caps = &spcm->pcm.caps[substream->stream];
0417
0418
0419 runtime->hw.info = ops->hw_info;
0420
0421
0422 runtime->hw.formats = le64_to_cpu(caps->formats);
0423 runtime->hw.period_bytes_min = le32_to_cpu(caps->period_size_min);
0424 runtime->hw.period_bytes_max = le32_to_cpu(caps->period_size_max);
0425 runtime->hw.periods_min = le32_to_cpu(caps->periods_min);
0426 runtime->hw.periods_max = le32_to_cpu(caps->periods_max);
0427
0428
0429
0430
0431
0432 runtime->hw.buffer_bytes_max = le32_to_cpu(caps->buffer_size_max);
0433
0434 dev_dbg(component->dev, "period min %zd max %zd bytes\n",
0435 runtime->hw.period_bytes_min,
0436 runtime->hw.period_bytes_max);
0437 dev_dbg(component->dev, "period count %d max %d\n",
0438 runtime->hw.periods_min,
0439 runtime->hw.periods_max);
0440 dev_dbg(component->dev, "buffer max %zd bytes\n",
0441 runtime->hw.buffer_bytes_max);
0442
0443
0444 substream->wait_time = 500;
0445
0446 spcm->stream[substream->stream].posn.host_posn = 0;
0447 spcm->stream[substream->stream].posn.dai_posn = 0;
0448 spcm->stream[substream->stream].substream = substream;
0449 spcm->prepared[substream->stream] = false;
0450
0451 ret = snd_sof_pcm_platform_open(sdev, substream);
0452 if (ret < 0)
0453 dev_err(component->dev, "error: pcm open failed %d\n", ret);
0454
0455 return ret;
0456 }
0457
0458 static int sof_pcm_close(struct snd_soc_component *component,
0459 struct snd_pcm_substream *substream)
0460 {
0461 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0462 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
0463 struct snd_sof_pcm *spcm;
0464 int err;
0465
0466
0467 if (rtd->dai_link->no_pcm)
0468 return 0;
0469
0470 spcm = snd_sof_find_spcm_dai(component, rtd);
0471 if (!spcm)
0472 return -EINVAL;
0473
0474 dev_dbg(component->dev, "pcm: close stream %d dir %d\n",
0475 spcm->pcm.pcm_id, substream->stream);
0476
0477 err = snd_sof_pcm_platform_close(sdev, substream);
0478 if (err < 0) {
0479 dev_err(component->dev, "error: pcm close failed %d\n",
0480 err);
0481
0482
0483
0484
0485 }
0486
0487 return 0;
0488 }
0489
0490
0491
0492
0493
0494
0495 static int sof_pcm_new(struct snd_soc_component *component,
0496 struct snd_soc_pcm_runtime *rtd)
0497 {
0498 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
0499 struct snd_sof_pcm *spcm;
0500 struct snd_pcm *pcm = rtd->pcm;
0501 struct snd_soc_tplg_stream_caps *caps;
0502 int stream = SNDRV_PCM_STREAM_PLAYBACK;
0503
0504
0505 spcm = snd_sof_find_spcm_dai(component, rtd);
0506 if (!spcm) {
0507 dev_warn(component->dev, "warn: can't find PCM with DAI ID %d\n",
0508 rtd->dai_link->id);
0509 return 0;
0510 }
0511
0512 dev_dbg(component->dev, "creating new PCM %s\n", spcm->pcm.pcm_name);
0513
0514
0515 if (!spcm->pcm.playback)
0516 goto capture;
0517
0518 caps = &spcm->pcm.caps[stream];
0519
0520
0521 dev_dbg(component->dev,
0522 "spcm: allocate %s playback DMA buffer size 0x%x max 0x%x\n",
0523 caps->name, caps->buffer_size_min, caps->buffer_size_max);
0524
0525 if (!pcm->streams[stream].substream) {
0526 dev_err(component->dev, "error: NULL playback substream!\n");
0527 return -EINVAL;
0528 }
0529
0530 snd_pcm_set_managed_buffer(pcm->streams[stream].substream,
0531 SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
0532 0, le32_to_cpu(caps->buffer_size_max));
0533 capture:
0534 stream = SNDRV_PCM_STREAM_CAPTURE;
0535
0536
0537 if (!spcm->pcm.capture)
0538 return 0;
0539
0540 caps = &spcm->pcm.caps[stream];
0541
0542
0543 dev_dbg(component->dev,
0544 "spcm: allocate %s capture DMA buffer size 0x%x max 0x%x\n",
0545 caps->name, caps->buffer_size_min, caps->buffer_size_max);
0546
0547 if (!pcm->streams[stream].substream) {
0548 dev_err(component->dev, "error: NULL capture substream!\n");
0549 return -EINVAL;
0550 }
0551
0552 snd_pcm_set_managed_buffer(pcm->streams[stream].substream,
0553 SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
0554 0, le32_to_cpu(caps->buffer_size_max));
0555
0556 return 0;
0557 }
0558
0559
0560 int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params)
0561 {
0562 struct snd_interval *rate = hw_param_interval(params,
0563 SNDRV_PCM_HW_PARAM_RATE);
0564 struct snd_interval *channels = hw_param_interval(params,
0565 SNDRV_PCM_HW_PARAM_CHANNELS);
0566 struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
0567 struct snd_soc_component *component =
0568 snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
0569 struct snd_sof_dai *dai =
0570 snd_sof_find_dai(component, (char *)rtd->dai_link->name);
0571 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
0572 const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
0573
0574
0575 if (!dai) {
0576 dev_warn(component->dev,
0577 "warning: no topology found for BE DAI %s config\n",
0578 rtd->dai_link->name);
0579
0580
0581 rate->min = 48000;
0582 rate->max = 48000;
0583
0584 channels->min = 2;
0585 channels->max = 2;
0586
0587 snd_mask_none(fmt);
0588 snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
0589
0590 return 0;
0591 }
0592
0593 if (pcm_ops->dai_link_fixup)
0594 return pcm_ops->dai_link_fixup(rtd, params);
0595
0596 return 0;
0597 }
0598 EXPORT_SYMBOL(sof_pcm_dai_link_fixup);
0599
0600 static int sof_pcm_probe(struct snd_soc_component *component)
0601 {
0602 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
0603 struct snd_sof_pdata *plat_data = sdev->pdata;
0604 const char *tplg_filename;
0605 int ret;
0606
0607
0608
0609
0610
0611 ret = pm_runtime_resume_and_get(component->dev);
0612 if (ret < 0 && ret != -EACCES)
0613 return ret;
0614
0615
0616 sdev->component = component;
0617
0618 tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
0619 "%s/%s",
0620 plat_data->tplg_filename_prefix,
0621 plat_data->tplg_filename);
0622 if (!tplg_filename)
0623 return -ENOMEM;
0624
0625 ret = snd_sof_load_topology(component, tplg_filename);
0626 if (ret < 0) {
0627 dev_err(component->dev, "error: failed to load DSP topology %d\n",
0628 ret);
0629 return ret;
0630 }
0631
0632 pm_runtime_mark_last_busy(component->dev);
0633 pm_runtime_put_autosuspend(component->dev);
0634
0635 return ret;
0636 }
0637
0638 static void sof_pcm_remove(struct snd_soc_component *component)
0639 {
0640
0641 snd_soc_tplg_component_remove(component);
0642 }
0643
0644 static int sof_pcm_ack(struct snd_soc_component *component,
0645 struct snd_pcm_substream *substream)
0646 {
0647 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
0648
0649 return snd_sof_pcm_platform_ack(sdev, substream);
0650 }
0651
0652 void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
0653 {
0654 struct snd_soc_component_driver *pd = &sdev->plat_drv;
0655 struct snd_sof_pdata *plat_data = sdev->pdata;
0656 const char *drv_name;
0657
0658 drv_name = plat_data->machine->drv_name;
0659
0660 pd->name = "sof-audio-component";
0661 pd->probe = sof_pcm_probe;
0662 pd->remove = sof_pcm_remove;
0663 pd->open = sof_pcm_open;
0664 pd->close = sof_pcm_close;
0665 pd->hw_params = sof_pcm_hw_params;
0666 pd->prepare = sof_pcm_prepare;
0667 pd->hw_free = sof_pcm_hw_free;
0668 pd->trigger = sof_pcm_trigger;
0669 pd->pointer = sof_pcm_pointer;
0670 pd->ack = sof_pcm_ack;
0671
0672 #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
0673 pd->compress_ops = &sof_compressed_ops;
0674 #endif
0675
0676 pd->pcm_construct = sof_pcm_new;
0677 pd->ignore_machine = drv_name;
0678 pd->be_hw_params_fixup = sof_pcm_dai_link_fixup;
0679 pd->be_pcm_base = SOF_BE_PCM_BASE;
0680 pd->use_dai_pcm_id = true;
0681 pd->topology_name_prefix = "sof";
0682
0683
0684 pd->module_get_upon_open = 1;
0685
0686 pd->legacy_dai_naming = 1;
0687 }