1292706Spkelsey// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2292706Spkelsey//
3292706Spkelsey// This file is provided under a dual BSD/GPLv2 license. When using or
4292706Spkelsey// redistributing this file, you may do so under either license.
5292706Spkelsey//
6292706Spkelsey// Copyright(c) 2022 Advanced Micro Devices, Inc.
7292706Spkelsey//
8292706Spkelsey// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
9292706Spkelsey//          V sujith kumar Reddy <Vsujithkumar.Reddy@amd.com>
10292706Spkelsey/*
11292706Spkelsey * Hardware interface for Renoir ACP block
12292706Spkelsey */
13292706Spkelsey
14292706Spkelsey#include <linux/platform_device.h>
15292706Spkelsey#include <linux/module.h>
16292706Spkelsey#include <linux/err.h>
17292706Spkelsey#include <linux/io.h>
18292706Spkelsey#include <sound/pcm_params.h>
19292706Spkelsey#include <sound/soc.h>
20292706Spkelsey#include <sound/soc-dai.h>
21292706Spkelsey#include <linux/dma-mapping.h>
22292706Spkelsey#include <linux/pci.h>
23292706Spkelsey#include <linux/pm_runtime.h>
24292706Spkelsey
25292706Spkelsey#include "amd.h"
26292706Spkelsey#include "../mach-config.h"
27292706Spkelsey#include "acp-mach.h"
28292706Spkelsey
29292706Spkelsey#define DRV_NAME "acp_asoc_rembrandt"
30292706Spkelsey
31292706Spkelsey#define MP1_C2PMSG_69 0x3B10A14
32292706Spkelsey#define MP1_C2PMSG_85 0x3B10A54
33292706Spkelsey#define MP1_C2PMSG_93 0x3B10A74
34292706Spkelsey#define HOST_BRIDGE_ID 0x14B5
35292706Spkelsey
36292706Spkelseystatic struct acp_resource rsrc = {
37292706Spkelsey	.offset = 0,
38292706Spkelsey	.no_of_ctrls = 2,
39292706Spkelsey	.irqp_used = 1,
40292706Spkelsey	.soc_mclk = true,
41292706Spkelsey	.irq_reg_offset = 0x1a00,
42292706Spkelsey	.i2s_pin_cfg_offset = 0x1440,
43292706Spkelsey	.i2s_mode = 0x0a,
44292706Spkelsey	.scratch_reg_offset = 0x12800,
45292706Spkelsey	.sram_pte_offset = 0x03802800,
46292706Spkelsey};
47292706Spkelsey
48292706Spkelseystatic struct snd_soc_acpi_codecs amp_rt1019 = {
49292706Spkelsey	.num_codecs = 1,
50292706Spkelsey	.codecs = {"10EC1019"}
51292706Spkelsey};
52292706Spkelsey
53292706Spkelseystatic struct snd_soc_acpi_codecs amp_max = {
54292706Spkelsey	.num_codecs = 1,
55292706Spkelsey	.codecs = {"MX98360A"}
56292706Spkelsey};
57292706Spkelsey
58292706Spkelseystatic struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[] = {
59292706Spkelsey	{
60292706Spkelsey		.id = "10508825",
61292706Spkelsey		.drv_name = "rmb-nau8825-max",
62292706Spkelsey		.machine_quirk = snd_soc_acpi_codec_list,
63292706Spkelsey		.quirk_data = &amp_max,
64292706Spkelsey	},
65292706Spkelsey	{
66292706Spkelsey		.id = "AMDI0007",
67292706Spkelsey		.drv_name = "rembrandt-acp",
68292706Spkelsey	},
69292706Spkelsey	{
70292706Spkelsey		.id = "RTL5682",
71292706Spkelsey		.drv_name = "rmb-rt5682s-rt1019",
72292706Spkelsey		.machine_quirk = snd_soc_acpi_codec_list,
73292706Spkelsey		.quirk_data = &amp_rt1019,
74292706Spkelsey	},
75292706Spkelsey	{},
76292706Spkelsey};
77292706Spkelsey
78292706Spkelseystatic struct snd_soc_dai_driver acp_rmb_dai[] = {
79292706Spkelsey{
80292706Spkelsey	.name = "acp-i2s-sp",
81292706Spkelsey	.id = I2S_SP_INSTANCE,
82292706Spkelsey	.playback = {
83292706Spkelsey		.stream_name = "I2S SP Playback",
84292706Spkelsey		.rates = SNDRV_PCM_RATE_8000_96000,
85292706Spkelsey		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
86292706Spkelsey			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
87292706Spkelsey		.channels_min = 2,
88292706Spkelsey		.channels_max = 8,
89292706Spkelsey		.rate_min = 8000,
90292706Spkelsey		.rate_max = 96000,
91292706Spkelsey	},
92292706Spkelsey	.capture = {
93292706Spkelsey		.stream_name = "I2S SP Capture",
94292706Spkelsey		.rates = SNDRV_PCM_RATE_8000_48000,
95292706Spkelsey		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
96292706Spkelsey			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
97292706Spkelsey		.channels_min = 2,
98292706Spkelsey		.channels_max = 2,
99292706Spkelsey		.rate_min = 8000,
100292706Spkelsey		.rate_max = 48000,
101292706Spkelsey	},
102292706Spkelsey	.ops = &asoc_acp_cpu_dai_ops,
103292706Spkelsey},
104292706Spkelsey{
105292706Spkelsey	.name = "acp-i2s-bt",
106292706Spkelsey	.id = I2S_BT_INSTANCE,
107292706Spkelsey	.playback = {
108292706Spkelsey		.stream_name = "I2S BT Playback",
109292706Spkelsey		.rates = SNDRV_PCM_RATE_8000_96000,
110292706Spkelsey		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
111304086Skarels			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
112292706Spkelsey		.channels_min = 2,
113292706Spkelsey		.channels_max = 8,
114292706Spkelsey		.rate_min = 8000,
115292706Spkelsey		.rate_max = 96000,
116292706Spkelsey	},
117292706Spkelsey	.capture = {
118292706Spkelsey		.stream_name = "I2S BT Capture",
119292706Spkelsey		.rates = SNDRV_PCM_RATE_8000_48000,
120292706Spkelsey		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
121292706Spkelsey			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
122292706Spkelsey		.channels_min = 2,
123292706Spkelsey		.channels_max = 2,
124292706Spkelsey		.rate_min = 8000,
125292706Spkelsey		.rate_max = 48000,
126292706Spkelsey	},
127292706Spkelsey	.ops = &asoc_acp_cpu_dai_ops,
128292706Spkelsey},
129292706Spkelsey{
130292706Spkelsey	.name = "acp-i2s-hs",
131292706Spkelsey	.id = I2S_HS_INSTANCE,
132292706Spkelsey	.playback = {
133292706Spkelsey		.stream_name = "I2S HS Playback",
134292706Spkelsey		.rates = SNDRV_PCM_RATE_8000_96000,
135292706Spkelsey		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
136292706Spkelsey			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
137292706Spkelsey		.channels_min = 2,
138292706Spkelsey		.channels_max = 8,
139292706Spkelsey		.rate_min = 8000,
140292706Spkelsey		.rate_max = 96000,
141292706Spkelsey	},
142292706Spkelsey	.capture = {
143292706Spkelsey		.stream_name = "I2S HS Capture",
144292706Spkelsey		.rates = SNDRV_PCM_RATE_8000_48000,
145292706Spkelsey		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
146292706Spkelsey			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
147292706Spkelsey		.channels_min = 2,
148292706Spkelsey		.channels_max = 8,
149292706Spkelsey		.rate_min = 8000,
150292706Spkelsey		.rate_max = 48000,
151292706Spkelsey	},
152292706Spkelsey	.ops = &asoc_acp_cpu_dai_ops,
153292706Spkelsey},
154292706Spkelsey{
155292706Spkelsey	.name = "acp-pdm-dmic",
156292706Spkelsey	.id = DMIC_INSTANCE,
157292706Spkelsey	.capture = {
158292706Spkelsey		.rates = SNDRV_PCM_RATE_8000_48000,
159292706Spkelsey		.formats = SNDRV_PCM_FMTBIT_S32_LE,
160292706Spkelsey		.channels_min = 2,
161292706Spkelsey		.channels_max = 2,
162292706Spkelsey		.rate_min = 8000,
163292706Spkelsey		.rate_max = 48000,
164292706Spkelsey	},
165292706Spkelsey	.ops = &acp_dmic_dai_ops,
166292706Spkelsey},
167292706Spkelsey};
168292706Spkelsey
169292706Spkelseystatic int acp6x_master_clock_generate(struct device *dev)
170292706Spkelsey{
171292706Spkelsey	int data = 0;
172292706Spkelsey	struct pci_dev *smn_dev;
173292706Spkelsey
174292706Spkelsey	smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, HOST_BRIDGE_ID, NULL);
175292706Spkelsey	if (!smn_dev) {
176292706Spkelsey		dev_err(dev, "Failed to get host bridge device\n");
177292706Spkelsey		return -ENODEV;
178292706Spkelsey	}
179292706Spkelsey
180292706Spkelsey	smn_write(smn_dev, MP1_C2PMSG_93, 0);
181292706Spkelsey	smn_write(smn_dev, MP1_C2PMSG_85, 0xC4);
182292706Spkelsey	smn_write(smn_dev, MP1_C2PMSG_69, 0x4);
183292706Spkelsey	read_poll_timeout(smn_read, data, data, DELAY_US,
184292706Spkelsey			  ACP_TIMEOUT, false, smn_dev, MP1_C2PMSG_93);
185292706Spkelsey	return 0;
186292706Spkelsey}
187292706Spkelsey
188292706Spkelseystatic int rembrandt_audio_probe(struct platform_device *pdev)
189292706Spkelsey{
190292706Spkelsey	struct device *dev = &pdev->dev;
191292706Spkelsey	struct acp_chip_info *chip;
192292706Spkelsey	struct acp_dev_data *adata;
193292706Spkelsey	struct resource *res;
194292706Spkelsey	u32 ret;
195292706Spkelsey
196292706Spkelsey	chip = dev_get_platdata(&pdev->dev);
197292706Spkelsey	if (!chip || !chip->base) {
198292706Spkelsey		dev_err(&pdev->dev, "ACP chip data is NULL\n");
199292706Spkelsey		return -ENODEV;
200292706Spkelsey	}
201292706Spkelsey
202292706Spkelsey	if (chip->acp_rev != ACP6X_DEV) {
203292706Spkelsey		dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
204292706Spkelsey		return -ENODEV;
205292706Spkelsey	}
206292706Spkelsey
207292706Spkelsey	adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
208297738Sbz	if (!adata)
209292706Spkelsey		return -ENOMEM;
210292706Spkelsey
211292706Spkelsey	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem");
212339037Sae	if (!res) {
213292706Spkelsey		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
214292706Spkelsey		return -ENODEV;
215292706Spkelsey	}
216292706Spkelsey
217292706Spkelsey	adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
218292706Spkelsey	if (!adata->acp_base)
219292706Spkelsey		return -ENOMEM;
220292706Spkelsey
221292706Spkelsey	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq");
222292706Spkelsey	if (!res) {
223292706Spkelsey		dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
224292706Spkelsey		return -ENODEV;
225292706Spkelsey	}
226292706Spkelsey
227292706Spkelsey	adata->i2s_irq = res->start;
228292706Spkelsey	adata->dev = dev;
229292706Spkelsey	adata->dai_driver = acp_rmb_dai;
230292706Spkelsey	adata->num_dai = ARRAY_SIZE(acp_rmb_dai);
231292706Spkelsey	adata->rsrc = &rsrc;
232292706Spkelsey	adata->platform = REMBRANDT;
233292706Spkelsey	adata->flag = chip->flag;
234292706Spkelsey	adata->machines = snd_soc_acpi_amd_rmb_acp_machines;
235292706Spkelsey	acp_machine_select(adata);
236292706Spkelsey
237292706Spkelsey	dev_set_drvdata(dev, adata);
238292706Spkelsey
239292706Spkelsey	if (chip->flag != FLAG_AMD_LEGACY_ONLY_DMIC) {
240292706Spkelsey		ret = acp6x_master_clock_generate(dev);
241292706Spkelsey		if (ret)
242292706Spkelsey			return ret;
243292706Spkelsey	}
244292706Spkelsey	acp_enable_interrupts(adata);
245292706Spkelsey	acp_platform_register(dev);
246292706Spkelsey	pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
247292706Spkelsey	pm_runtime_use_autosuspend(&pdev->dev);
248292706Spkelsey	pm_runtime_mark_last_busy(&pdev->dev);
249292706Spkelsey	pm_runtime_set_active(&pdev->dev);
250292706Spkelsey	pm_runtime_enable(&pdev->dev);
251292706Spkelsey	return 0;
252292706Spkelsey}
253292706Spkelsey
254292706Spkelseystatic void rembrandt_audio_remove(struct platform_device *pdev)
255292706Spkelsey{
256292706Spkelsey	struct device *dev = &pdev->dev;
257292706Spkelsey	struct acp_dev_data *adata = dev_get_drvdata(dev);
258292706Spkelsey
259292706Spkelsey	acp_disable_interrupts(adata);
260292706Spkelsey	acp_platform_unregister(dev);
261292706Spkelsey	pm_runtime_disable(&pdev->dev);
262292706Spkelsey}
263292706Spkelsey
264292706Spkelseystatic int __maybe_unused rmb_pcm_resume(struct device *dev)
265292706Spkelsey{
266292706Spkelsey	struct acp_dev_data *adata = dev_get_drvdata(dev);
267292706Spkelsey	struct acp_stream *stream;
268292706Spkelsey	struct snd_pcm_substream *substream;
269292706Spkelsey	snd_pcm_uframes_t buf_in_frames;
270292706Spkelsey	u64 buf_size;
271292706Spkelsey
272292706Spkelsey	if (adata->flag != FLAG_AMD_LEGACY_ONLY_DMIC)
273292706Spkelsey		acp6x_master_clock_generate(dev);
274292706Spkelsey
275292706Spkelsey	spin_lock(&adata->acp_lock);
276292706Spkelsey	list_for_each_entry(stream, &adata->stream_list, list) {
277292706Spkelsey		substream = stream->substream;
278292706Spkelsey		if (substream && substream->runtime) {
279292706Spkelsey			buf_in_frames = (substream->runtime->buffer_size);
280292706Spkelsey			buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
281292706Spkelsey			config_pte_for_stream(adata, stream);
282292706Spkelsey			config_acp_dma(adata, stream, buf_size);
283292706Spkelsey			if (stream->dai_id)
284292706Spkelsey				restore_acp_i2s_params(substream, adata, stream);
285292706Spkelsey			else
286292706Spkelsey				restore_acp_pdm_params(substream, adata);
287292706Spkelsey		}
288292706Spkelsey	}
289292706Spkelsey	spin_unlock(&adata->acp_lock);
290292706Spkelsey	return 0;
291292706Spkelsey}
292292706Spkelsey
293292706Spkelseystatic const struct dev_pm_ops rmb_dma_pm_ops = {
294292706Spkelsey	SET_SYSTEM_SLEEP_PM_OPS(NULL, rmb_pcm_resume)
295292706Spkelsey};
296292706Spkelsey
297292706Spkelseystatic struct platform_driver rembrandt_driver = {
298292706Spkelsey	.probe = rembrandt_audio_probe,
299292706Spkelsey	.remove_new = rembrandt_audio_remove,
300292706Spkelsey	.driver = {
301292706Spkelsey		.name = "acp_asoc_rembrandt",
302292706Spkelsey		.pm = &rmb_dma_pm_ops,
303292706Spkelsey	},
304292706Spkelsey};
305292706Spkelsey
306292706Spkelseymodule_platform_driver(rembrandt_driver);
307292706Spkelsey
308292706SpkelseyMODULE_DESCRIPTION("AMD ACP Rembrandt Driver");
309292706SpkelseyMODULE_IMPORT_NS(SND_SOC_ACP_COMMON);
310292706SpkelseyMODULE_LICENSE("Dual BSD/GPL");
311292706SpkelseyMODULE_ALIAS("platform:" DRV_NAME);
312292706Spkelsey