1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
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.
6 // Copyright(c) 2023 Advanced Micro Devices, Inc.
8 // Authors: Syed Saba kareem <syed.sabakareem@amd.com>
10 * Hardware interface for ACP7.0 block
13 #include <linux/platform_device.h>
14 #include <linux/module.h>
15 #include <linux/err.h>
17 #include <sound/pcm_params.h>
18 #include <sound/soc.h>
19 #include <sound/soc-dai.h>
20 #include <linux/dma-mapping.h>
21 #include <linux/pm_runtime.h>
22 #include <linux/pci.h>
26 #define DRV_NAME "acp_asoc_acp70"
28 #define CLK7_CLK0_DFS_CNTL_N1 0X0006C1A4
29 #define CLK0_DIVIDER 0X19
31 static struct acp_resource rsrc
= {
36 .irq_reg_offset
= 0x1a00,
37 .scratch_reg_offset
= 0x10000,
38 .sram_pte_offset
= 0x03800000,
41 static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines
[] = {
44 .drv_name
= "acp70-acp",
49 static struct snd_soc_dai_driver acp70_dai
[] = {
52 .id
= I2S_SP_INSTANCE
,
54 .stream_name
= "I2S SP Playback",
55 .rates
= SNDRV_PCM_RATE_8000_192000
,
56 .formats
= SNDRV_PCM_FMTBIT_U8
| SNDRV_PCM_FMTBIT_S8
|
57 SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S32_LE
,
64 .stream_name
= "I2S SP Capture",
65 .rates
= SNDRV_PCM_RATE_8000_192000
,
66 .formats
= SNDRV_PCM_FMTBIT_U8
| SNDRV_PCM_FMTBIT_S8
|
67 SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S32_LE
,
73 .ops
= &asoc_acp_cpu_dai_ops
,
77 .id
= I2S_BT_INSTANCE
,
79 .stream_name
= "I2S BT Playback",
80 .rates
= SNDRV_PCM_RATE_8000_192000
,
81 .formats
= SNDRV_PCM_FMTBIT_U8
| SNDRV_PCM_FMTBIT_S8
|
82 SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S32_LE
,
89 .stream_name
= "I2S BT Capture",
90 .rates
= SNDRV_PCM_RATE_8000_192000
,
91 .formats
= SNDRV_PCM_FMTBIT_U8
| SNDRV_PCM_FMTBIT_S8
|
92 SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S32_LE
,
98 .ops
= &asoc_acp_cpu_dai_ops
,
101 .name
= "acp-i2s-hs",
102 .id
= I2S_HS_INSTANCE
,
104 .stream_name
= "I2S HS Playback",
105 .rates
= SNDRV_PCM_RATE_8000_192000
,
106 .formats
= SNDRV_PCM_FMTBIT_U8
| SNDRV_PCM_FMTBIT_S8
|
107 SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S32_LE
,
114 .stream_name
= "I2S HS Capture",
115 .rates
= SNDRV_PCM_RATE_8000_192000
,
116 .formats
= SNDRV_PCM_FMTBIT_U8
| SNDRV_PCM_FMTBIT_S8
|
117 SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S32_LE
,
123 .ops
= &asoc_acp_cpu_dai_ops
,
126 .name
= "acp-pdm-dmic",
129 .rates
= SNDRV_PCM_RATE_8000_48000
,
130 .formats
= SNDRV_PCM_FMTBIT_S32_LE
,
136 .ops
= &acp_dmic_dai_ops
,
140 static int acp70_i2s_master_clock_generate(struct acp_dev_data
*adata
)
142 struct pci_dev
*smn_dev
;
145 if (adata
->acp_rev
== ACP70_PCI_ID
)
147 else if (adata
->acp_rev
== ACP71_PCI_ID
)
152 smn_dev
= pci_get_device(PCI_VENDOR_ID_AMD
, device_id
, NULL
);
157 /* Set clk7 DFS clock divider register value to get mclk as 196.608MHz*/
158 smn_write(smn_dev
, CLK7_CLK0_DFS_CNTL_N1
, CLK0_DIVIDER
);
163 static int acp_acp70_audio_probe(struct platform_device
*pdev
)
165 struct device
*dev
= &pdev
->dev
;
166 struct acp_chip_info
*chip
;
167 struct acp_dev_data
*adata
;
168 struct resource
*res
;
171 chip
= dev_get_platdata(&pdev
->dev
);
172 if (!chip
|| !chip
->base
) {
173 dev_err(&pdev
->dev
, "ACP chip data is NULL\n");
177 switch (chip
->acp_rev
) {
182 dev_err(&pdev
->dev
, "Un-supported ACP Revision %d\n", chip
->acp_rev
);
186 adata
= devm_kzalloc(dev
, sizeof(struct acp_dev_data
), GFP_KERNEL
);
190 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "acp_mem");
192 dev_err(&pdev
->dev
, "IORESOURCE_MEM FAILED\n");
196 adata
->acp_base
= devm_ioremap(&pdev
->dev
, res
->start
, resource_size(res
));
197 if (!adata
->acp_base
)
200 res
= platform_get_resource_byname(pdev
, IORESOURCE_IRQ
, "acp_dai_irq");
202 dev_err(&pdev
->dev
, "IORESOURCE_IRQ FAILED\n");
206 adata
->i2s_irq
= res
->start
;
208 adata
->dai_driver
= acp70_dai
;
209 adata
->num_dai
= ARRAY_SIZE(acp70_dai
);
211 adata
->machines
= snd_soc_acpi_amd_acp70_acp_machines
;
212 adata
->acp_rev
= chip
->acp_rev
;
213 adata
->flag
= chip
->flag
;
214 acp_machine_select(adata
);
216 dev_set_drvdata(dev
, adata
);
218 ret
= acp70_i2s_master_clock_generate(adata
);
220 dev_err(&pdev
->dev
, "Failed to set I2S master clock as 196.608MHz\n");
223 acp_enable_interrupts(adata
);
224 acp_platform_register(dev
);
225 pm_runtime_set_autosuspend_delay(&pdev
->dev
, ACP_SUSPEND_DELAY_MS
);
226 pm_runtime_use_autosuspend(&pdev
->dev
);
227 pm_runtime_mark_last_busy(&pdev
->dev
);
228 pm_runtime_set_active(&pdev
->dev
);
229 pm_runtime_enable(&pdev
->dev
);
233 static void acp_acp70_audio_remove(struct platform_device
*pdev
)
235 struct device
*dev
= &pdev
->dev
;
236 struct acp_dev_data
*adata
= dev_get_drvdata(dev
);
238 acp_disable_interrupts(adata
);
239 acp_platform_unregister(dev
);
240 pm_runtime_disable(&pdev
->dev
);
243 static int __maybe_unused
acp70_pcm_resume(struct device
*dev
)
245 struct acp_dev_data
*adata
= dev_get_drvdata(dev
);
246 struct acp_stream
*stream
;
247 struct snd_pcm_substream
*substream
;
248 snd_pcm_uframes_t buf_in_frames
;
251 spin_lock(&adata
->acp_lock
);
252 list_for_each_entry(stream
, &adata
->stream_list
, list
) {
254 substream
= stream
->substream
;
255 if (substream
&& substream
->runtime
) {
256 buf_in_frames
= (substream
->runtime
->buffer_size
);
257 buf_size
= frames_to_bytes(substream
->runtime
, buf_in_frames
);
258 config_pte_for_stream(adata
, stream
);
259 config_acp_dma(adata
, stream
, buf_size
);
261 restore_acp_i2s_params(substream
, adata
, stream
);
263 restore_acp_pdm_params(substream
, adata
);
267 spin_unlock(&adata
->acp_lock
);
271 static const struct dev_pm_ops acp70_dma_pm_ops
= {
272 SET_SYSTEM_SLEEP_PM_OPS(NULL
, acp70_pcm_resume
)
275 static struct platform_driver acp70_driver
= {
276 .probe
= acp_acp70_audio_probe
,
277 .remove
= acp_acp70_audio_remove
,
279 .name
= "acp_asoc_acp70",
280 .pm
= &acp70_dma_pm_ops
,
284 module_platform_driver(acp70_driver
);
286 MODULE_DESCRIPTION("AMD ACP ACP70 Driver");
287 MODULE_IMPORT_NS("SND_SOC_ACP_COMMON");
288 MODULE_LICENSE("Dual BSD/GPL");
289 MODULE_ALIAS("platform:" DRV_NAME
);