acp5x-pcm-dma.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. // SPDX-License-Identifier: GPL-2.0+
  2. //
  3. // AMD ALSA SoC PCM Driver
  4. //
  5. // Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
  6. #include <linux/platform_device.h>
  7. #include <linux/module.h>
  8. #include <linux/err.h>
  9. #include <linux/io.h>
  10. #include <linux/pm_runtime.h>
  11. #include <sound/pcm.h>
  12. #include <sound/pcm_params.h>
  13. #include <sound/soc.h>
  14. #include <sound/soc-dai.h>
  15. #include "acp5x.h"
  16. #define DRV_NAME "acp5x_i2s_dma"
  17. static const struct snd_pcm_hardware acp5x_pcm_hardware_playback = {
  18. .info = SNDRV_PCM_INFO_INTERLEAVED |
  19. SNDRV_PCM_INFO_BLOCK_TRANSFER |
  20. SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
  21. SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
  22. .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
  23. SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
  24. .channels_min = 2,
  25. .channels_max = 2,
  26. .rates = SNDRV_PCM_RATE_8000_96000,
  27. .rate_min = 8000,
  28. .rate_max = 96000,
  29. .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
  30. .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
  31. .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
  32. .periods_min = PLAYBACK_MIN_NUM_PERIODS,
  33. .periods_max = PLAYBACK_MAX_NUM_PERIODS,
  34. };
  35. static const struct snd_pcm_hardware acp5x_pcm_hardware_capture = {
  36. .info = SNDRV_PCM_INFO_INTERLEAVED |
  37. SNDRV_PCM_INFO_BLOCK_TRANSFER |
  38. SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
  39. SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
  40. .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
  41. SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
  42. .channels_min = 2,
  43. .channels_max = 2,
  44. .rates = SNDRV_PCM_RATE_8000_96000,
  45. .rate_min = 8000,
  46. .rate_max = 96000,
  47. .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
  48. .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
  49. .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
  50. .periods_min = CAPTURE_MIN_NUM_PERIODS,
  51. .periods_max = CAPTURE_MAX_NUM_PERIODS,
  52. };
  53. static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
  54. {
  55. struct i2s_dev_data *vg_i2s_data;
  56. u16 irq_flag;
  57. u32 val;
  58. vg_i2s_data = dev_id;
  59. if (!vg_i2s_data)
  60. return IRQ_NONE;
  61. irq_flag = 0;
  62. val = acp_readl(vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
  63. if ((val & BIT(HS_TX_THRESHOLD)) && vg_i2s_data->play_stream) {
  64. acp_writel(BIT(HS_TX_THRESHOLD), vg_i2s_data->acp5x_base +
  65. ACP_EXTERNAL_INTR_STAT);
  66. snd_pcm_period_elapsed(vg_i2s_data->play_stream);
  67. irq_flag = 1;
  68. }
  69. if ((val & BIT(I2S_TX_THRESHOLD)) && vg_i2s_data->i2ssp_play_stream) {
  70. acp_writel(BIT(I2S_TX_THRESHOLD),
  71. vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
  72. snd_pcm_period_elapsed(vg_i2s_data->i2ssp_play_stream);
  73. irq_flag = 1;
  74. }
  75. if ((val & BIT(HS_RX_THRESHOLD)) && vg_i2s_data->capture_stream) {
  76. acp_writel(BIT(HS_RX_THRESHOLD), vg_i2s_data->acp5x_base +
  77. ACP_EXTERNAL_INTR_STAT);
  78. snd_pcm_period_elapsed(vg_i2s_data->capture_stream);
  79. irq_flag = 1;
  80. }
  81. if ((val & BIT(I2S_RX_THRESHOLD)) && vg_i2s_data->i2ssp_capture_stream) {
  82. acp_writel(BIT(I2S_RX_THRESHOLD),
  83. vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
  84. snd_pcm_period_elapsed(vg_i2s_data->i2ssp_capture_stream);
  85. irq_flag = 1;
  86. }
  87. if (irq_flag)
  88. return IRQ_HANDLED;
  89. else
  90. return IRQ_NONE;
  91. }
  92. static void config_acp5x_dma(struct i2s_stream_instance *rtd, int direction)
  93. {
  94. u16 page_idx;
  95. u32 low, high, val, acp_fifo_addr, reg_fifo_addr;
  96. u32 reg_dma_size, reg_fifo_size;
  97. dma_addr_t addr;
  98. addr = rtd->dma_addr;
  99. if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
  100. switch (rtd->i2s_instance) {
  101. case I2S_HS_INSTANCE:
  102. val = ACP_SRAM_HS_PB_PTE_OFFSET;
  103. break;
  104. case I2S_SP_INSTANCE:
  105. default:
  106. val = ACP_SRAM_SP_PB_PTE_OFFSET;
  107. }
  108. } else {
  109. switch (rtd->i2s_instance) {
  110. case I2S_HS_INSTANCE:
  111. val = ACP_SRAM_HS_CP_PTE_OFFSET;
  112. break;
  113. case I2S_SP_INSTANCE:
  114. default:
  115. val = ACP_SRAM_SP_CP_PTE_OFFSET;
  116. }
  117. }
  118. /* Group Enable */
  119. acp_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp5x_base +
  120. ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
  121. acp_writel(PAGE_SIZE_4K_ENABLE, rtd->acp5x_base +
  122. ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
  123. for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
  124. /* Load the low address of page int ACP SRAM through SRBM */
  125. low = lower_32_bits(addr);
  126. high = upper_32_bits(addr);
  127. acp_writel(low, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val);
  128. high |= BIT(31);
  129. acp_writel(high, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val + 4);
  130. /* Move to next physically contiguous page */
  131. val += 8;
  132. addr += PAGE_SIZE;
  133. }
  134. if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
  135. switch (rtd->i2s_instance) {
  136. case I2S_HS_INSTANCE:
  137. reg_dma_size = ACP_HS_TX_DMA_SIZE;
  138. acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
  139. HS_PB_FIFO_ADDR_OFFSET;
  140. reg_fifo_addr = ACP_HS_TX_FIFOADDR;
  141. reg_fifo_size = ACP_HS_TX_FIFOSIZE;
  142. acp_writel(I2S_HS_TX_MEM_WINDOW_START,
  143. rtd->acp5x_base + ACP_HS_TX_RINGBUFADDR);
  144. break;
  145. case I2S_SP_INSTANCE:
  146. default:
  147. reg_dma_size = ACP_I2S_TX_DMA_SIZE;
  148. acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
  149. SP_PB_FIFO_ADDR_OFFSET;
  150. reg_fifo_addr = ACP_I2S_TX_FIFOADDR;
  151. reg_fifo_size = ACP_I2S_TX_FIFOSIZE;
  152. acp_writel(I2S_SP_TX_MEM_WINDOW_START,
  153. rtd->acp5x_base + ACP_I2S_TX_RINGBUFADDR);
  154. }
  155. } else {
  156. switch (rtd->i2s_instance) {
  157. case I2S_HS_INSTANCE:
  158. reg_dma_size = ACP_HS_RX_DMA_SIZE;
  159. acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
  160. HS_CAPT_FIFO_ADDR_OFFSET;
  161. reg_fifo_addr = ACP_HS_RX_FIFOADDR;
  162. reg_fifo_size = ACP_HS_RX_FIFOSIZE;
  163. acp_writel(I2S_HS_RX_MEM_WINDOW_START,
  164. rtd->acp5x_base + ACP_HS_RX_RINGBUFADDR);
  165. break;
  166. case I2S_SP_INSTANCE:
  167. default:
  168. reg_dma_size = ACP_I2S_RX_DMA_SIZE;
  169. acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
  170. SP_CAPT_FIFO_ADDR_OFFSET;
  171. reg_fifo_addr = ACP_I2S_RX_FIFOADDR;
  172. reg_fifo_size = ACP_I2S_RX_FIFOSIZE;
  173. acp_writel(I2S_SP_RX_MEM_WINDOW_START,
  174. rtd->acp5x_base + ACP_I2S_RX_RINGBUFADDR);
  175. }
  176. }
  177. acp_writel(DMA_SIZE, rtd->acp5x_base + reg_dma_size);
  178. acp_writel(acp_fifo_addr, rtd->acp5x_base + reg_fifo_addr);
  179. acp_writel(FIFO_SIZE, rtd->acp5x_base + reg_fifo_size);
  180. acp_writel(BIT(I2S_RX_THRESHOLD) | BIT(HS_RX_THRESHOLD)
  181. | BIT(I2S_TX_THRESHOLD) | BIT(HS_TX_THRESHOLD),
  182. rtd->acp5x_base + ACP_EXTERNAL_INTR_CNTL);
  183. }
  184. static int acp5x_dma_open(struct snd_soc_component *component,
  185. struct snd_pcm_substream *substream)
  186. {
  187. struct snd_pcm_runtime *runtime;
  188. struct snd_soc_pcm_runtime *prtd;
  189. struct i2s_dev_data *adata;
  190. struct i2s_stream_instance *i2s_data;
  191. int ret;
  192. runtime = substream->runtime;
  193. prtd = asoc_substream_to_rtd(substream);
  194. component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
  195. adata = dev_get_drvdata(component->dev);
  196. i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL);
  197. if (!i2s_data)
  198. return -ENOMEM;
  199. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  200. runtime->hw = acp5x_pcm_hardware_playback;
  201. else
  202. runtime->hw = acp5x_pcm_hardware_capture;
  203. ret = snd_pcm_hw_constraint_integer(runtime,
  204. SNDRV_PCM_HW_PARAM_PERIODS);
  205. if (ret < 0) {
  206. dev_err(component->dev, "set integer constraint failed\n");
  207. kfree(i2s_data);
  208. return ret;
  209. }
  210. i2s_data->acp5x_base = adata->acp5x_base;
  211. runtime->private_data = i2s_data;
  212. return ret;
  213. }
  214. static int acp5x_dma_hw_params(struct snd_soc_component *component,
  215. struct snd_pcm_substream *substream,
  216. struct snd_pcm_hw_params *params)
  217. {
  218. struct i2s_stream_instance *rtd;
  219. struct snd_soc_pcm_runtime *prtd;
  220. struct snd_soc_card *card;
  221. struct acp5x_platform_info *pinfo;
  222. struct i2s_dev_data *adata;
  223. u64 size;
  224. prtd = asoc_substream_to_rtd(substream);
  225. card = prtd->card;
  226. pinfo = snd_soc_card_get_drvdata(card);
  227. adata = dev_get_drvdata(component->dev);
  228. rtd = substream->runtime->private_data;
  229. if (!rtd)
  230. return -EINVAL;
  231. if (pinfo) {
  232. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  233. rtd->i2s_instance = pinfo->play_i2s_instance;
  234. switch (rtd->i2s_instance) {
  235. case I2S_HS_INSTANCE:
  236. adata->play_stream = substream;
  237. break;
  238. case I2S_SP_INSTANCE:
  239. default:
  240. adata->i2ssp_play_stream = substream;
  241. }
  242. } else {
  243. rtd->i2s_instance = pinfo->cap_i2s_instance;
  244. switch (rtd->i2s_instance) {
  245. case I2S_HS_INSTANCE:
  246. adata->capture_stream = substream;
  247. break;
  248. case I2S_SP_INSTANCE:
  249. default:
  250. adata->i2ssp_capture_stream = substream;
  251. }
  252. }
  253. } else {
  254. dev_err(component->dev, "pinfo failed\n");
  255. return -EINVAL;
  256. }
  257. size = params_buffer_bytes(params);
  258. rtd->dma_addr = substream->runtime->dma_addr;
  259. rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
  260. config_acp5x_dma(rtd, substream->stream);
  261. return 0;
  262. }
  263. static snd_pcm_uframes_t acp5x_dma_pointer(struct snd_soc_component *component,
  264. struct snd_pcm_substream *substream)
  265. {
  266. struct i2s_stream_instance *rtd;
  267. u32 pos;
  268. u32 buffersize;
  269. u64 bytescount;
  270. rtd = substream->runtime->private_data;
  271. buffersize = frames_to_bytes(substream->runtime,
  272. substream->runtime->buffer_size);
  273. bytescount = acp_get_byte_count(rtd, substream->stream);
  274. if (bytescount > rtd->bytescount)
  275. bytescount -= rtd->bytescount;
  276. pos = do_div(bytescount, buffersize);
  277. return bytes_to_frames(substream->runtime, pos);
  278. }
  279. static int acp5x_dma_new(struct snd_soc_component *component,
  280. struct snd_soc_pcm_runtime *rtd)
  281. {
  282. struct device *parent = component->dev->parent;
  283. snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
  284. parent, MIN_BUFFER, MAX_BUFFER);
  285. return 0;
  286. }
  287. static int acp5x_dma_close(struct snd_soc_component *component,
  288. struct snd_pcm_substream *substream)
  289. {
  290. struct snd_soc_pcm_runtime *prtd;
  291. struct i2s_dev_data *adata;
  292. struct i2s_stream_instance *ins;
  293. prtd = asoc_substream_to_rtd(substream);
  294. component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
  295. adata = dev_get_drvdata(component->dev);
  296. ins = substream->runtime->private_data;
  297. if (!ins)
  298. return -EINVAL;
  299. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  300. switch (ins->i2s_instance) {
  301. case I2S_HS_INSTANCE:
  302. adata->play_stream = NULL;
  303. break;
  304. case I2S_SP_INSTANCE:
  305. default:
  306. adata->i2ssp_play_stream = NULL;
  307. }
  308. } else {
  309. switch (ins->i2s_instance) {
  310. case I2S_HS_INSTANCE:
  311. adata->capture_stream = NULL;
  312. break;
  313. case I2S_SP_INSTANCE:
  314. default:
  315. adata->i2ssp_capture_stream = NULL;
  316. }
  317. }
  318. kfree(ins);
  319. return 0;
  320. }
  321. static const struct snd_soc_component_driver acp5x_i2s_component = {
  322. .name = DRV_NAME,
  323. .open = acp5x_dma_open,
  324. .close = acp5x_dma_close,
  325. .hw_params = acp5x_dma_hw_params,
  326. .pointer = acp5x_dma_pointer,
  327. .pcm_construct = acp5x_dma_new,
  328. };
  329. static int acp5x_audio_probe(struct platform_device *pdev)
  330. {
  331. struct resource *res;
  332. struct i2s_dev_data *adata;
  333. unsigned int irqflags;
  334. int status;
  335. if (!pdev->dev.platform_data) {
  336. dev_err(&pdev->dev, "platform_data not retrieved\n");
  337. return -ENODEV;
  338. }
  339. irqflags = *((unsigned int *)(pdev->dev.platform_data));
  340. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  341. if (!res) {
  342. dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
  343. return -ENODEV;
  344. }
  345. adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
  346. if (!adata)
  347. return -ENOMEM;
  348. adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,
  349. resource_size(res));
  350. if (!adata->acp5x_base)
  351. return -ENOMEM;
  352. status = platform_get_irq(pdev, 0);
  353. if (status < 0)
  354. return status;
  355. adata->i2s_irq = status;
  356. dev_set_drvdata(&pdev->dev, adata);
  357. status = devm_snd_soc_register_component(&pdev->dev,
  358. &acp5x_i2s_component,
  359. NULL, 0);
  360. if (status) {
  361. dev_err(&pdev->dev, "Fail to register acp i2s component\n");
  362. return status;
  363. }
  364. status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler,
  365. irqflags, "ACP5x_I2S_IRQ", adata);
  366. if (status) {
  367. dev_err(&pdev->dev, "ACP5x I2S IRQ request failed\n");
  368. return status;
  369. }
  370. pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
  371. pm_runtime_use_autosuspend(&pdev->dev);
  372. pm_runtime_enable(&pdev->dev);
  373. pm_runtime_allow(&pdev->dev);
  374. return 0;
  375. }
  376. static int acp5x_audio_remove(struct platform_device *pdev)
  377. {
  378. pm_runtime_disable(&pdev->dev);
  379. return 0;
  380. }
  381. static int __maybe_unused acp5x_pcm_resume(struct device *dev)
  382. {
  383. struct i2s_dev_data *adata;
  384. struct i2s_stream_instance *rtd;
  385. u32 val;
  386. adata = dev_get_drvdata(dev);
  387. if (adata->play_stream && adata->play_stream->runtime) {
  388. rtd = adata->play_stream->runtime->private_data;
  389. config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);
  390. acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_ITER);
  391. if (adata->tdm_mode == TDM_ENABLE) {
  392. acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_TXFRMT);
  393. val = acp_readl(adata->acp5x_base + ACP_HSTDM_ITER);
  394. acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_ITER);
  395. }
  396. }
  397. if (adata->i2ssp_play_stream && adata->i2ssp_play_stream->runtime) {
  398. rtd = adata->i2ssp_play_stream->runtime->private_data;
  399. config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);
  400. acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_ITER);
  401. if (adata->tdm_mode == TDM_ENABLE) {
  402. acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_TXFRMT);
  403. val = acp_readl(adata->acp5x_base + ACP_I2STDM_ITER);
  404. acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_ITER);
  405. }
  406. }
  407. if (adata->capture_stream && adata->capture_stream->runtime) {
  408. rtd = adata->capture_stream->runtime->private_data;
  409. config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
  410. acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_IRER);
  411. if (adata->tdm_mode == TDM_ENABLE) {
  412. acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_RXFRMT);
  413. val = acp_readl(adata->acp5x_base + ACP_HSTDM_IRER);
  414. acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_IRER);
  415. }
  416. }
  417. if (adata->i2ssp_capture_stream && adata->i2ssp_capture_stream->runtime) {
  418. rtd = adata->i2ssp_capture_stream->runtime->private_data;
  419. config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
  420. acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_IRER);
  421. if (adata->tdm_mode == TDM_ENABLE) {
  422. acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_RXFRMT);
  423. val = acp_readl(adata->acp5x_base + ACP_I2STDM_IRER);
  424. acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_IRER);
  425. }
  426. }
  427. acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
  428. return 0;
  429. }
  430. static int __maybe_unused acp5x_pcm_suspend(struct device *dev)
  431. {
  432. struct i2s_dev_data *adata;
  433. adata = dev_get_drvdata(dev);
  434. acp_writel(0, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
  435. return 0;
  436. }
  437. static int __maybe_unused acp5x_pcm_runtime_resume(struct device *dev)
  438. {
  439. struct i2s_dev_data *adata;
  440. adata = dev_get_drvdata(dev);
  441. acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
  442. return 0;
  443. }
  444. static const struct dev_pm_ops acp5x_pm_ops = {
  445. SET_RUNTIME_PM_OPS(acp5x_pcm_suspend,
  446. acp5x_pcm_runtime_resume, NULL)
  447. SET_SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_resume)
  448. };
  449. static struct platform_driver acp5x_dma_driver = {
  450. .probe = acp5x_audio_probe,
  451. .remove = acp5x_audio_remove,
  452. .driver = {
  453. .name = "acp5x_i2s_dma",
  454. .pm = &acp5x_pm_ops,
  455. },
  456. };
  457. module_platform_driver(acp5x_dma_driver);
  458. MODULE_AUTHOR("[email protected]");
  459. MODULE_DESCRIPTION("AMD ACP 5.x PCM Driver");
  460. MODULE_LICENSE("GPL v2");
  461. MODULE_ALIAS("platform:" DRV_NAME);