sof-audio.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  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) 2019 Intel Corporation. All rights reserved.
  7. //
  8. // Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
  9. //
  10. #include "sof-audio.h"
  11. #include "ops.h"
  12. /*
  13. * helper to determine if there are only D0i3 compatible
  14. * streams active
  15. */
  16. bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev)
  17. {
  18. struct snd_pcm_substream *substream;
  19. struct snd_sof_pcm *spcm;
  20. bool d0i3_compatible_active = false;
  21. int dir;
  22. list_for_each_entry(spcm, &sdev->pcm_list, list) {
  23. for_each_pcm_streams(dir) {
  24. substream = spcm->stream[dir].substream;
  25. if (!substream || !substream->runtime)
  26. continue;
  27. /*
  28. * substream->runtime being not NULL indicates
  29. * that the stream is open. No need to check the
  30. * stream state.
  31. */
  32. if (!spcm->stream[dir].d0i3_compatible)
  33. return false;
  34. d0i3_compatible_active = true;
  35. }
  36. }
  37. return d0i3_compatible_active;
  38. }
  39. EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active);
  40. bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
  41. {
  42. struct snd_sof_pcm *spcm;
  43. list_for_each_entry(spcm, &sdev->pcm_list, list) {
  44. if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored ||
  45. spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored)
  46. return true;
  47. }
  48. return false;
  49. }
  50. int sof_set_hw_params_upon_resume(struct device *dev)
  51. {
  52. struct snd_sof_dev *sdev = dev_get_drvdata(dev);
  53. struct snd_pcm_substream *substream;
  54. struct snd_sof_pcm *spcm;
  55. snd_pcm_state_t state;
  56. int dir;
  57. /*
  58. * SOF requires hw_params to be set-up internally upon resume.
  59. * So, set the flag to indicate this for those streams that
  60. * have been suspended.
  61. */
  62. list_for_each_entry(spcm, &sdev->pcm_list, list) {
  63. for_each_pcm_streams(dir) {
  64. /*
  65. * do not reset hw_params upon resume for streams that
  66. * were kept running during suspend
  67. */
  68. if (spcm->stream[dir].suspend_ignored)
  69. continue;
  70. substream = spcm->stream[dir].substream;
  71. if (!substream || !substream->runtime)
  72. continue;
  73. state = substream->runtime->status->state;
  74. if (state == SNDRV_PCM_STATE_SUSPENDED)
  75. spcm->prepared[dir] = false;
  76. }
  77. }
  78. /* set internal flag for BE */
  79. return snd_sof_dsp_hw_params_upon_resume(sdev);
  80. }
  81. static int sof_restore_kcontrols(struct device *dev)
  82. {
  83. struct snd_sof_dev *sdev = dev_get_drvdata(dev);
  84. struct snd_sof_control *scontrol;
  85. int ipc_cmd, ctrl_type;
  86. int ret = 0;
  87. /* restore kcontrol values */
  88. list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
  89. /* reset readback offset for scontrol after resuming */
  90. scontrol->readback_offset = 0;
  91. /* notify DSP of kcontrol values */
  92. switch (scontrol->cmd) {
  93. case SOF_CTRL_CMD_VOLUME:
  94. case SOF_CTRL_CMD_ENUM:
  95. case SOF_CTRL_CMD_SWITCH:
  96. ipc_cmd = SOF_IPC_COMP_SET_VALUE;
  97. ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET;
  98. ret = snd_sof_ipc_set_get_comp_data(scontrol,
  99. ipc_cmd, ctrl_type,
  100. scontrol->cmd,
  101. true);
  102. break;
  103. case SOF_CTRL_CMD_BINARY:
  104. ipc_cmd = SOF_IPC_COMP_SET_DATA;
  105. ctrl_type = SOF_CTRL_TYPE_DATA_SET;
  106. ret = snd_sof_ipc_set_get_comp_data(scontrol,
  107. ipc_cmd, ctrl_type,
  108. scontrol->cmd,
  109. true);
  110. break;
  111. default:
  112. break;
  113. }
  114. if (ret < 0) {
  115. dev_err(dev,
  116. "error: failed kcontrol value set for widget: %d\n",
  117. scontrol->comp_id);
  118. return ret;
  119. }
  120. }
  121. return 0;
  122. }
  123. const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev,
  124. int pipeline_id)
  125. {
  126. const struct snd_sof_widget *swidget;
  127. list_for_each_entry(swidget, &sdev->widget_list, list)
  128. if (swidget->id == snd_soc_dapm_scheduler) {
  129. const struct sof_ipc_pipe_new *pipeline =
  130. swidget->private;
  131. if (pipeline->pipeline_id == pipeline_id)
  132. return pipeline;
  133. }
  134. return NULL;
  135. }
  136. int sof_restore_pipelines(struct device *dev)
  137. {
  138. struct snd_sof_dev *sdev = dev_get_drvdata(dev);
  139. struct snd_sof_widget *swidget;
  140. struct snd_sof_route *sroute;
  141. struct sof_ipc_pipe_new *pipeline;
  142. struct snd_sof_dai *dai;
  143. struct sof_ipc_cmd_hdr *hdr;
  144. struct sof_ipc_comp *comp;
  145. size_t ipc_size;
  146. int ret;
  147. /* restore pipeline components */
  148. list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
  149. struct sof_ipc_comp_reply r;
  150. /* skip if there is no private data */
  151. if (!swidget->private)
  152. continue;
  153. ret = sof_pipeline_core_enable(sdev, swidget);
  154. if (ret < 0) {
  155. dev_err(dev,
  156. "error: failed to enable target core: %d\n",
  157. ret);
  158. return ret;
  159. }
  160. switch (swidget->id) {
  161. case snd_soc_dapm_dai_in:
  162. case snd_soc_dapm_dai_out:
  163. ipc_size = sizeof(struct sof_ipc_comp_dai) +
  164. sizeof(struct sof_ipc_comp_ext);
  165. comp = kzalloc(ipc_size, GFP_KERNEL);
  166. if (!comp)
  167. return -ENOMEM;
  168. dai = swidget->private;
  169. memcpy(comp, &dai->comp_dai,
  170. sizeof(struct sof_ipc_comp_dai));
  171. /* append extended data to the end of the component */
  172. memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai),
  173. &swidget->comp_ext, sizeof(swidget->comp_ext));
  174. ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd,
  175. comp, ipc_size,
  176. &r, sizeof(r));
  177. kfree(comp);
  178. break;
  179. case snd_soc_dapm_scheduler:
  180. /*
  181. * During suspend, all DSP cores are powered off.
  182. * Therefore upon resume, create the pipeline comp
  183. * and power up the core that the pipeline is
  184. * scheduled on.
  185. */
  186. pipeline = swidget->private;
  187. ret = sof_load_pipeline_ipc(dev, pipeline, &r);
  188. break;
  189. default:
  190. hdr = swidget->private;
  191. ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd,
  192. swidget->private, hdr->size,
  193. &r, sizeof(r));
  194. break;
  195. }
  196. if (ret < 0) {
  197. dev_err(dev,
  198. "error: failed to load widget type %d with ID: %d\n",
  199. swidget->widget->id, swidget->comp_id);
  200. return ret;
  201. }
  202. }
  203. /* restore pipeline connections */
  204. list_for_each_entry_reverse(sroute, &sdev->route_list, list) {
  205. struct sof_ipc_pipe_comp_connect *connect;
  206. struct sof_ipc_reply reply;
  207. /* skip if there's no private data */
  208. if (!sroute->private)
  209. continue;
  210. connect = sroute->private;
  211. /* send ipc */
  212. ret = sof_ipc_tx_message(sdev->ipc,
  213. connect->hdr.cmd,
  214. connect, sizeof(*connect),
  215. &reply, sizeof(reply));
  216. if (ret < 0) {
  217. dev_err(dev,
  218. "error: failed to load route sink %s control %s source %s\n",
  219. sroute->route->sink,
  220. sroute->route->control ? sroute->route->control
  221. : "none",
  222. sroute->route->source);
  223. return ret;
  224. }
  225. }
  226. /* restore dai links */
  227. list_for_each_entry_reverse(dai, &sdev->dai_list, list) {
  228. struct sof_ipc_reply reply;
  229. struct sof_ipc_dai_config *config = dai->dai_config;
  230. if (!config) {
  231. dev_err(dev, "error: no config for DAI %s\n",
  232. dai->name);
  233. continue;
  234. }
  235. /*
  236. * The link DMA channel would be invalidated for running
  237. * streams but not for streams that were in the PAUSED
  238. * state during suspend. So invalidate it here before setting
  239. * the dai config in the DSP.
  240. */
  241. if (config->type == SOF_DAI_INTEL_HDA)
  242. config->hda.link_dma_ch = DMA_CHAN_INVALID;
  243. ret = sof_ipc_tx_message(sdev->ipc,
  244. config->hdr.cmd, config,
  245. config->hdr.size,
  246. &reply, sizeof(reply));
  247. if (ret < 0) {
  248. dev_err(dev,
  249. "error: failed to set dai config for %s\n",
  250. dai->name);
  251. return ret;
  252. }
  253. }
  254. /* complete pipeline */
  255. list_for_each_entry(swidget, &sdev->widget_list, list) {
  256. switch (swidget->id) {
  257. case snd_soc_dapm_scheduler:
  258. swidget->complete =
  259. snd_sof_complete_pipeline(dev, swidget);
  260. break;
  261. default:
  262. break;
  263. }
  264. }
  265. /* restore pipeline kcontrols */
  266. ret = sof_restore_kcontrols(dev);
  267. if (ret < 0)
  268. dev_err(dev,
  269. "error: restoring kcontrols after resume\n");
  270. return ret;
  271. }
  272. /*
  273. * Generic object lookup APIs.
  274. */
  275. struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp,
  276. const char *name)
  277. {
  278. struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
  279. struct snd_sof_pcm *spcm;
  280. list_for_each_entry(spcm, &sdev->pcm_list, list) {
  281. /* match with PCM dai name */
  282. if (strcmp(spcm->pcm.dai_name, name) == 0)
  283. return spcm;
  284. /* match with playback caps name if set */
  285. if (*spcm->pcm.caps[0].name &&
  286. !strcmp(spcm->pcm.caps[0].name, name))
  287. return spcm;
  288. /* match with capture caps name if set */
  289. if (*spcm->pcm.caps[1].name &&
  290. !strcmp(spcm->pcm.caps[1].name, name))
  291. return spcm;
  292. }
  293. return NULL;
  294. }
  295. struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
  296. unsigned int comp_id,
  297. int *direction)
  298. {
  299. struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
  300. struct snd_sof_pcm *spcm;
  301. int dir;
  302. list_for_each_entry(spcm, &sdev->pcm_list, list) {
  303. for_each_pcm_streams(dir) {
  304. if (spcm->stream[dir].comp_id == comp_id) {
  305. *direction = dir;
  306. return spcm;
  307. }
  308. }
  309. }
  310. return NULL;
  311. }
  312. struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp,
  313. unsigned int pcm_id)
  314. {
  315. struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
  316. struct snd_sof_pcm *spcm;
  317. list_for_each_entry(spcm, &sdev->pcm_list, list) {
  318. if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id)
  319. return spcm;
  320. }
  321. return NULL;
  322. }
  323. struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp,
  324. const char *name)
  325. {
  326. struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
  327. struct snd_sof_widget *swidget;
  328. list_for_each_entry(swidget, &sdev->widget_list, list) {
  329. if (strcmp(name, swidget->widget->name) == 0)
  330. return swidget;
  331. }
  332. return NULL;
  333. }
  334. /* find widget by stream name and direction */
  335. struct snd_sof_widget *
  336. snd_sof_find_swidget_sname(struct snd_soc_component *scomp,
  337. const char *pcm_name, int dir)
  338. {
  339. struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
  340. struct snd_sof_widget *swidget;
  341. enum snd_soc_dapm_type type;
  342. if (dir == SNDRV_PCM_STREAM_PLAYBACK)
  343. type = snd_soc_dapm_aif_in;
  344. else
  345. type = snd_soc_dapm_aif_out;
  346. list_for_each_entry(swidget, &sdev->widget_list, list) {
  347. if (!strcmp(pcm_name, swidget->widget->sname) &&
  348. swidget->id == type)
  349. return swidget;
  350. }
  351. return NULL;
  352. }
  353. struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp,
  354. const char *name)
  355. {
  356. struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
  357. struct snd_sof_dai *dai;
  358. list_for_each_entry(dai, &sdev->dai_list, list) {
  359. if (dai->name && (strcmp(name, dai->name) == 0))
  360. return dai;
  361. }
  362. return NULL;
  363. }
  364. /*
  365. * SOF Driver enumeration.
  366. */
  367. int sof_machine_check(struct snd_sof_dev *sdev)
  368. {
  369. struct snd_sof_pdata *sof_pdata = sdev->pdata;
  370. const struct sof_dev_desc *desc = sof_pdata->desc;
  371. struct snd_soc_acpi_mach *mach;
  372. int ret;
  373. /* force nocodec mode */
  374. #if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)
  375. dev_warn(sdev->dev, "Force to use nocodec mode\n");
  376. goto nocodec;
  377. #endif
  378. /* find machine */
  379. snd_sof_machine_select(sdev);
  380. if (sof_pdata->machine) {
  381. snd_sof_set_mach_params(sof_pdata->machine, sdev->dev);
  382. return 0;
  383. }
  384. #if !IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)
  385. dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
  386. return -ENODEV;
  387. #endif
  388. #if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)
  389. nocodec:
  390. #endif
  391. /* select nocodec mode */
  392. dev_warn(sdev->dev, "Using nocodec machine driver\n");
  393. mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
  394. if (!mach)
  395. return -ENOMEM;
  396. mach->drv_name = "sof-nocodec";
  397. sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
  398. ret = sof_nocodec_setup(sdev->dev, desc->ops);
  399. if (ret < 0)
  400. return ret;
  401. sof_pdata->machine = mach;
  402. snd_sof_set_mach_params(sof_pdata->machine, sdev->dev);
  403. return 0;
  404. }
  405. EXPORT_SYMBOL(sof_machine_check);
  406. int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
  407. {
  408. struct snd_sof_pdata *plat_data = pdata;
  409. const char *drv_name;
  410. const void *mach;
  411. int size;
  412. drv_name = plat_data->machine->drv_name;
  413. mach = plat_data->machine;
  414. size = sizeof(*plat_data->machine);
  415. /* register machine driver, pass machine info as pdata */
  416. plat_data->pdev_mach =
  417. platform_device_register_data(sdev->dev, drv_name,
  418. PLATFORM_DEVID_NONE, mach, size);
  419. if (IS_ERR(plat_data->pdev_mach))
  420. return PTR_ERR(plat_data->pdev_mach);
  421. dev_dbg(sdev->dev, "created machine %s\n",
  422. dev_name(&plat_data->pdev_mach->dev));
  423. return 0;
  424. }
  425. EXPORT_SYMBOL(sof_machine_register);
  426. void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
  427. {
  428. struct snd_sof_pdata *plat_data = pdata;
  429. if (!IS_ERR_OR_NULL(plat_data->pdev_mach))
  430. platform_device_unregister(plat_data->pdev_mach);
  431. }
  432. EXPORT_SYMBOL(sof_machine_unregister);