|
12 | 12 | #include <sound/pcm_params.h> |
13 | 13 | #include <sound/soc.h> |
14 | 14 | #include <sound/soc-dai.h> |
| 15 | +#include <linux/pm_runtime.h> |
15 | 16 | #include <linux/soundwire/sdw_amd.h> |
16 | 17 | #include "acp63.h" |
17 | 18 |
|
@@ -102,6 +103,29 @@ static const struct snd_pcm_hardware acp63_sdw_hardware_capture = { |
102 | 103 | .periods_max = SDW_CAPTURE_MAX_NUM_PERIODS, |
103 | 104 | }; |
104 | 105 |
|
| 106 | +static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, bool enable) |
| 107 | +{ |
| 108 | + u32 ext_intr_cntl, ext_intr_cntl1; |
| 109 | + u32 irq_mask = ACP_SDW_DMA_IRQ_MASK; |
| 110 | + u32 irq_mask1 = ACP_P1_SDW_DMA_IRQ_MASK; |
| 111 | + |
| 112 | + if (enable) { |
| 113 | + ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL); |
| 114 | + ext_intr_cntl |= irq_mask; |
| 115 | + writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL); |
| 116 | + ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1); |
| 117 | + ext_intr_cntl1 |= irq_mask1; |
| 118 | + writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1); |
| 119 | + } else { |
| 120 | + ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL); |
| 121 | + ext_intr_cntl &= ~irq_mask; |
| 122 | + writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL); |
| 123 | + ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1); |
| 124 | + ext_intr_cntl1 &= ~irq_mask1; |
| 125 | + writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1); |
| 126 | + } |
| 127 | +} |
| 128 | + |
105 | 129 | static void acp63_config_dma(struct acp_sdw_dma_stream *stream, void __iomem *acp_base, |
106 | 130 | u32 stream_id) |
107 | 131 | { |
@@ -440,16 +464,86 @@ static int acp63_sdw_platform_probe(struct platform_device *pdev) |
440 | 464 | status = devm_snd_soc_register_component(&pdev->dev, |
441 | 465 | &acp63_sdw_component, |
442 | 466 | NULL, 0); |
443 | | - if (status) |
| 467 | + if (status) { |
444 | 468 | dev_err(&pdev->dev, "Fail to register sdw dma component\n"); |
| 469 | + return status; |
| 470 | + } |
| 471 | + pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS); |
| 472 | + pm_runtime_use_autosuspend(&pdev->dev); |
| 473 | + pm_runtime_mark_last_busy(&pdev->dev); |
| 474 | + pm_runtime_set_active(&pdev->dev); |
| 475 | + pm_runtime_enable(&pdev->dev); |
| 476 | + return 0; |
| 477 | +} |
445 | 478 |
|
446 | | - return status; |
| 479 | +static int acp63_sdw_platform_remove(struct platform_device *pdev) |
| 480 | +{ |
| 481 | + pm_runtime_disable(&pdev->dev); |
| 482 | + return 0; |
447 | 483 | } |
448 | 484 |
|
| 485 | +static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data) |
| 486 | +{ |
| 487 | + struct acp_sdw_dma_stream *stream; |
| 488 | + struct snd_pcm_substream *substream; |
| 489 | + struct snd_pcm_runtime *runtime; |
| 490 | + u32 period_bytes, buf_size, water_mark_size_reg; |
| 491 | + u32 stream_count; |
| 492 | + int index, instance, ret; |
| 493 | + |
| 494 | + for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) { |
| 495 | + if (instance == ACP_SDW0) |
| 496 | + stream_count = ACP63_SDW0_DMA_MAX_STREAMS; |
| 497 | + else |
| 498 | + stream_count = ACP63_SDW1_DMA_MAX_STREAMS; |
| 499 | + |
| 500 | + for (index = 0; index < stream_count; index++) { |
| 501 | + if (instance == ACP_SDW0) { |
| 502 | + substream = sdw_data->sdw0_dma_stream[index]; |
| 503 | + water_mark_size_reg = |
| 504 | + sdw0_dma_ring_buf_reg[index].water_mark_size_reg; |
| 505 | + } else { |
| 506 | + substream = sdw_data->sdw1_dma_stream[index]; |
| 507 | + water_mark_size_reg = |
| 508 | + sdw1_dma_ring_buf_reg[index].water_mark_size_reg; |
| 509 | + } |
| 510 | + |
| 511 | + if (substream && substream->runtime) { |
| 512 | + runtime = substream->runtime; |
| 513 | + stream = runtime->private_data; |
| 514 | + period_bytes = frames_to_bytes(runtime, runtime->period_size); |
| 515 | + buf_size = frames_to_bytes(runtime, runtime->buffer_size); |
| 516 | + acp63_config_dma(stream, sdw_data->acp_base, index); |
| 517 | + ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index, |
| 518 | + buf_size, instance); |
| 519 | + if (ret) |
| 520 | + return ret; |
| 521 | + writel(period_bytes, sdw_data->acp_base + water_mark_size_reg); |
| 522 | + } |
| 523 | + } |
| 524 | + } |
| 525 | + acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, true); |
| 526 | + return 0; |
| 527 | +} |
| 528 | + |
| 529 | +static int __maybe_unused acp63_sdw_pcm_resume(struct device *dev) |
| 530 | +{ |
| 531 | + struct sdw_dma_dev_data *sdw_data; |
| 532 | + |
| 533 | + sdw_data = dev_get_drvdata(dev); |
| 534 | + return acp_restore_sdw_dma_config(sdw_data); |
| 535 | +} |
| 536 | + |
| 537 | +static const struct dev_pm_ops acp63_pm_ops = { |
| 538 | + SET_SYSTEM_SLEEP_PM_OPS(NULL, acp63_sdw_pcm_resume) |
| 539 | +}; |
| 540 | + |
449 | 541 | static struct platform_driver acp63_sdw_dma_driver = { |
450 | 542 | .probe = acp63_sdw_platform_probe, |
| 543 | + .remove = acp63_sdw_platform_remove, |
451 | 544 | .driver = { |
452 | 545 | .name = "amd_ps_sdw_dma", |
| 546 | + .pm = &acp63_pm_ops, |
453 | 547 | }, |
454 | 548 | }; |
455 | 549 |
|
|
0 commit comments