Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/amd/acp/acp-pdm.c
26481 views
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) 2022 Advanced Micro Devices, Inc.
7
//
8
// Authors: Ajit Kumar Pandey <[email protected]>
9
// Vijendar Mukunda <[email protected]>
10
//
11
12
/*
13
* Generic Hardware interface for ACP Audio PDM controller
14
*/
15
16
#include <linux/err.h>
17
#include <linux/io.h>
18
#include <linux/module.h>
19
#include <linux/platform_device.h>
20
#include <sound/pcm_params.h>
21
#include <sound/soc.h>
22
#include <sound/soc-dai.h>
23
24
#include "amd.h"
25
26
#define DRV_NAME "acp-pdm"
27
28
static int acp_dmic_prepare(struct snd_pcm_substream *substream,
29
struct snd_soc_dai *dai)
30
{
31
struct acp_stream *stream = substream->runtime->private_data;
32
struct device *dev = dai->component->dev;
33
struct acp_chip_info *chip;
34
u32 physical_addr, size_dmic, period_bytes;
35
unsigned int dmic_ctrl;
36
37
chip = dev_get_platdata(dev);
38
/* Enable default DMIC clk */
39
writel(PDM_CLK_FREQ_MASK, chip->base + ACP_WOV_CLK_CTRL);
40
dmic_ctrl = readl(chip->base + ACP_WOV_MISC_CTRL);
41
dmic_ctrl |= PDM_MISC_CTRL_MASK;
42
writel(dmic_ctrl, chip->base + ACP_WOV_MISC_CTRL);
43
44
period_bytes = frames_to_bytes(substream->runtime,
45
substream->runtime->period_size);
46
size_dmic = frames_to_bytes(substream->runtime,
47
substream->runtime->buffer_size);
48
49
if (chip->acp_rev >= ACP70_PCI_ID)
50
physical_addr = ACP7x_DMIC_MEM_WINDOW_START;
51
else
52
physical_addr = stream->reg_offset + MEM_WINDOW_START;
53
54
/* Init DMIC Ring buffer */
55
writel(physical_addr, chip->base + ACP_WOV_RX_RINGBUFADDR);
56
writel(size_dmic, chip->base + ACP_WOV_RX_RINGBUFSIZE);
57
writel(period_bytes, chip->base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
58
writel(0x01, chip->base + ACPAXI2AXI_ATU_CTRL);
59
60
return 0;
61
}
62
63
static int acp_dmic_dai_trigger(struct snd_pcm_substream *substream,
64
int cmd, struct snd_soc_dai *dai)
65
{
66
struct device *dev = dai->component->dev;
67
struct acp_chip_info *chip = dev_get_platdata(dev);
68
unsigned int dma_enable;
69
int ret = 0;
70
71
switch (cmd) {
72
case SNDRV_PCM_TRIGGER_START:
73
case SNDRV_PCM_TRIGGER_RESUME:
74
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
75
dma_enable = readl(chip->base + ACP_WOV_PDM_DMA_ENABLE);
76
if (!(dma_enable & DMA_EN_MASK)) {
77
writel(PDM_ENABLE, chip->base + ACP_WOV_PDM_ENABLE);
78
writel(PDM_ENABLE, chip->base + ACP_WOV_PDM_DMA_ENABLE);
79
}
80
81
ret = readl_poll_timeout_atomic(chip->base + ACP_WOV_PDM_DMA_ENABLE,
82
dma_enable, (dma_enable & DMA_EN_MASK),
83
DELAY_US, PDM_TIMEOUT);
84
break;
85
case SNDRV_PCM_TRIGGER_STOP:
86
case SNDRV_PCM_TRIGGER_SUSPEND:
87
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
88
dma_enable = readl(chip->base + ACP_WOV_PDM_DMA_ENABLE);
89
if ((dma_enable & DMA_EN_MASK)) {
90
writel(PDM_DISABLE, chip->base + ACP_WOV_PDM_ENABLE);
91
writel(PDM_DISABLE, chip->base + ACP_WOV_PDM_DMA_ENABLE);
92
93
}
94
95
ret = readl_poll_timeout_atomic(chip->base + ACP_WOV_PDM_DMA_ENABLE,
96
dma_enable, !(dma_enable & DMA_EN_MASK),
97
DELAY_US, PDM_TIMEOUT);
98
break;
99
default:
100
ret = -EINVAL;
101
break;
102
}
103
104
return ret;
105
}
106
107
static int acp_dmic_hwparams(struct snd_pcm_substream *substream,
108
struct snd_pcm_hw_params *hwparams, struct snd_soc_dai *dai)
109
{
110
struct device *dev = dai->component->dev;
111
struct acp_chip_info *chip = dev_get_platdata(dev);
112
unsigned int channels, ch_mask;
113
114
channels = params_channels(hwparams);
115
switch (channels) {
116
case 2:
117
ch_mask = 0;
118
break;
119
case 4:
120
ch_mask = 1;
121
break;
122
case 6:
123
ch_mask = 2;
124
break;
125
default:
126
dev_err(dev, "Invalid channels %d\n", channels);
127
return -EINVAL;
128
}
129
130
chip->ch_mask = ch_mask;
131
if (params_format(hwparams) != SNDRV_PCM_FORMAT_S32_LE) {
132
dev_err(dai->dev, "Invalid format:%d\n", params_format(hwparams));
133
return -EINVAL;
134
}
135
136
writel(ch_mask, chip->base + ACP_WOV_PDM_NO_OF_CHANNELS);
137
writel(PDM_DEC_64, chip->base + ACP_WOV_PDM_DECIMATION_FACTOR);
138
139
return 0;
140
}
141
142
static int acp_dmic_dai_startup(struct snd_pcm_substream *substream,
143
struct snd_soc_dai *dai)
144
{
145
struct acp_stream *stream = substream->runtime->private_data;
146
struct device *dev = dai->component->dev;
147
struct acp_chip_info *chip = dev_get_platdata(dev);
148
u32 ext_int_ctrl;
149
150
stream->dai_id = DMIC_INSTANCE;
151
stream->irq_bit = BIT(PDM_DMA_STAT);
152
stream->pte_offset = ACP_SRAM_PDM_PTE_OFFSET;
153
stream->reg_offset = ACP_REGION2_OFFSET;
154
155
/* Enable DMIC Interrupts */
156
ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 0));
157
ext_int_ctrl |= PDM_DMA_INTR_MASK;
158
writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 0));
159
160
return 0;
161
}
162
163
static void acp_dmic_dai_shutdown(struct snd_pcm_substream *substream,
164
struct snd_soc_dai *dai)
165
{
166
struct device *dev = dai->component->dev;
167
struct acp_chip_info *chip = dev_get_platdata(dev);
168
u32 ext_int_ctrl;
169
170
/* Disable DMIC interrupts */
171
ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 0));
172
ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
173
writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 0));
174
}
175
176
const struct snd_soc_dai_ops acp_dmic_dai_ops = {
177
.prepare = acp_dmic_prepare,
178
.hw_params = acp_dmic_hwparams,
179
.trigger = acp_dmic_dai_trigger,
180
.startup = acp_dmic_dai_startup,
181
.shutdown = acp_dmic_dai_shutdown,
182
};
183
EXPORT_SYMBOL_NS_GPL(acp_dmic_dai_ops, "SND_SOC_ACP_COMMON");
184
185
MODULE_DESCRIPTION("AMD ACP Audio PDM controller");
186
MODULE_LICENSE("Dual BSD/GPL");
187
MODULE_ALIAS(DRV_NAME);
188
189