File Index
Files and directories
Sign in
FILE

acp63.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/amd/acp/acp63.c C
  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) 2023 Advanced Micro Devices, Inc.
  7//
  8// Authors: Syed Saba kareem <syed.sabakareem@amd.com>
  9/*
 10 * Hardware interface for ACP6.3 block
 11 */
 12
 13#include <linux/platform_device.h>
 14#include <linux/module.h>
 15#include <linux/err.h>
 16#include <linux/io.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>
 23
 24#include <asm/amd/node.h>
 25
 26#include "amd.h"
 27#include "acp-mach.h"
 28#include "../mach-config.h"
 29
 30#define DRV_NAME "acp_asoc_acp63"
 31
 32#define CLK_PLL_PWR_REQ_N0		0X0006C2C0
 33#define CLK_SPLL_FIELD_2_N0		0X0006C114
 34#define CLK_PLL_REQ_N0			0X0006C0DC
 35#define CLK_DFSBYPASS_CONTR		0X0006C2C8
 36#define CLK_DFS_CNTL_N0			0X0006C1A4
 37
 38#define PLL_AUTO_STOP_REQ		BIT(4)
 39#define PLL_AUTO_START_REQ		BIT(0)
 40#define PLL_FRANCE_EN			BIT(4)
 41#define EXIT_DPF_BYPASS_0		BIT(16)
 42#define EXIT_DPF_BYPASS_1		BIT(17)
 43#define CLK0_DIVIDER			0X30
 44
 45union clk_pll_req_no {
 46	struct {
 47		u32 fb_mult_int : 9;
 48		u32 reserved : 3;
 49		u32 pll_spine_div : 4;
 50		u32 gb_mult_frac : 16;
 51	} bitfields, bits;
 52	u32 clk_pll_req_no_reg;
 53};
 54
 55static struct snd_soc_dai_driver acp63_dai[] = {
 56{
 57	.name = "acp-i2s-sp",
 58	.id = I2S_SP_INSTANCE,
 59	.playback = {
 60		.stream_name = "I2S SP Playback",
 61		.rates = SNDRV_PCM_RATE_8000_96000,
 62		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
 63			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
 64		.channels_min = 2,
 65		.channels_max = 8,
 66		.rate_min = 8000,
 67		.rate_max = 96000,
 68	},
 69	.capture = {
 70		.stream_name = "I2S SP Capture",
 71		.rates = SNDRV_PCM_RATE_8000_48000,
 72		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
 73			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
 74		.channels_min = 2,
 75		.channels_max = 2,
 76		.rate_min = 8000,
 77		.rate_max = 48000,
 78	},
 79	.ops = &asoc_acp_cpu_dai_ops,
 80},
 81{
 82	.name = "acp-i2s-bt",
 83	.id = I2S_BT_INSTANCE,
 84	.playback = {
 85		.stream_name = "I2S BT Playback",
 86		.rates = SNDRV_PCM_RATE_8000_96000,
 87		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
 88			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
 89		.channels_min = 2,
 90		.channels_max = 8,
 91		.rate_min = 8000,
 92		.rate_max = 96000,
 93	},
 94	.capture = {
 95		.stream_name = "I2S BT Capture",
 96		.rates = SNDRV_PCM_RATE_8000_48000,
 97		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
 98			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
 99		.channels_min = 2,
100		.channels_max = 2,
101		.rate_min = 8000,
102		.rate_max = 48000,
103	},
104	.ops = &asoc_acp_cpu_dai_ops,
105},
106{
107	.name = "acp-i2s-hs",
108	.id = I2S_HS_INSTANCE,
109	.playback = {
110		.stream_name = "I2S HS Playback",
111		.rates = SNDRV_PCM_RATE_8000_96000,
112		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
113			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
114		.channels_min = 2,
115		.channels_max = 8,
116		.rate_min = 8000,
117		.rate_max = 96000,
118	},
119	.capture = {
120		.stream_name = "I2S HS Capture",
121		.rates = SNDRV_PCM_RATE_8000_48000,
122		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
123			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
124		.channels_min = 2,
125		.channels_max = 8,
126		.rate_min = 8000,
127		.rate_max = 48000,
128	},
129	.ops = &asoc_acp_cpu_dai_ops,
130},
131{
132	.name = "acp-pdm-dmic",
133	.id = DMIC_INSTANCE,
134	.capture = {
135		.rates = SNDRV_PCM_RATE_8000_48000,
136		.formats = SNDRV_PCM_FMTBIT_S32_LE,
137		.channels_min = 2,
138		.channels_max = 2,
139		.rate_min = 8000,
140		.rate_max = 48000,
141	},
142	.ops = &acp_dmic_dai_ops,
143},
144};
145
146static int acp63_i2s_master_clock_generate(struct acp_chip_info *chip)
147{
148	int rc;
149	u32 data;
150	union clk_pll_req_no clk_pll;
151
152	/* Clk5 pll register values to get mclk as 196.6MHz*/
153	clk_pll.bits.fb_mult_int = 0x31;
154	clk_pll.bits.pll_spine_div = 0;
155	clk_pll.bits.gb_mult_frac = 0x26E9;
156
157	rc = amd_smn_read(0, CLK_PLL_PWR_REQ_N0, &data);
158	if (rc)
159		return rc;
160	rc = amd_smn_write(0, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_STOP_REQ);
161	if (rc)
162		return rc;
163
164	rc = amd_smn_read(0, CLK_SPLL_FIELD_2_N0, &data);
165	if (rc)
166		return rc;
167	if (data & PLL_FRANCE_EN) {
168		rc = amd_smn_write(0, CLK_SPLL_FIELD_2_N0, data | PLL_FRANCE_EN);
169		if (rc)
170			return rc;
171	}
172
173	rc = amd_smn_write(0, CLK_PLL_REQ_N0, clk_pll.clk_pll_req_no_reg);
174	if (rc)
175		return rc;
176
177	rc = amd_smn_read(0, CLK_PLL_PWR_REQ_N0, &data);
178	if (rc)
179		return rc;
180	rc = amd_smn_write(0, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_START_REQ);
181	if (rc)
182		return rc;
183
184	rc = amd_smn_read(0, CLK_DFSBYPASS_CONTR, &data);
185	if (rc)
186		return rc;
187	rc = amd_smn_write(0, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_0);
188	if (rc)
189		return rc;
190	rc = amd_smn_write(0, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_1);
191	if (rc)
192		return rc;
193
194	return amd_smn_write(0, CLK_DFS_CNTL_N0, CLK0_DIVIDER);
195}
196
197static int acp63_audio_probe(struct platform_device *pdev)
198{
199	struct device *dev = &pdev->dev;
200	struct acp_chip_info *chip;
201	int ret;
202
203	chip = dev_get_platdata(&pdev->dev);
204	if (!chip || !chip->base) {
205		dev_err(&pdev->dev, "ACP chip data is NULL\n");
206		return -ENODEV;
207	}
208
209	if (chip->acp_rev != ACP63_PCI_ID) {
210		dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
211		return -ENODEV;
212	}
213
214	chip->dev = dev;
215	chip->dai_driver = acp63_dai;
216	chip->num_dai = ARRAY_SIZE(acp63_dai);
217
218	if (chip->is_i2s_config && chip->rsrc->soc_mclk) {
219		ret = acp63_i2s_master_clock_generate(chip);
220		if (ret)
221			return ret;
222	}
223	ret = acp_hw_en_interrupts(chip);
224	if (ret) {
225		dev_err(dev, "ACP en-interrupts failed\n");
226		return ret;
227	}
228	acp_platform_register(dev);
229	pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
230	pm_runtime_use_autosuspend(&pdev->dev);
231	pm_runtime_mark_last_busy(&pdev->dev);
232	pm_runtime_set_active(&pdev->dev);
233	pm_runtime_enable(&pdev->dev);
234	return 0;
235}
236
237static void acp63_audio_remove(struct platform_device *pdev)
238{
239	struct device *dev = &pdev->dev;
240	struct acp_chip_info *chip = dev_get_platdata(dev);
241	int ret;
242
243	ret = acp_hw_dis_interrupts(chip);
244	if (ret)
245		dev_err(dev, "ACP dis-interrupts failed\n");
246
247	acp_platform_unregister(dev);
248	pm_runtime_disable(&pdev->dev);
249}
250
251static int acp63_pcm_resume(struct device *dev)
252{
253	struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
254	struct acp_stream *stream;
255	struct snd_pcm_substream *substream;
256	snd_pcm_uframes_t buf_in_frames;
257	u64 buf_size;
258
259	if (chip->is_i2s_config && chip->rsrc->soc_mclk)
260		acp63_i2s_master_clock_generate(chip);
261
262	spin_lock(&chip->acp_lock);
263	list_for_each_entry(stream, &chip->stream_list, list) {
264		substream = stream->substream;
265		if (substream && substream->runtime) {
266			buf_in_frames = (substream->runtime->buffer_size);
267			buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
268			config_pte_for_stream(chip, stream);
269			config_acp_dma(chip, stream, buf_size);
270			if (stream->dai_id)
271				restore_acp_i2s_params(substream, chip, stream);
272			else
273				restore_acp_pdm_params(substream, chip);
274		}
275	}
276	spin_unlock(&chip->acp_lock);
277	return 0;
278}
279
280static const struct dev_pm_ops acp63_dma_pm_ops = {
281	SYSTEM_SLEEP_PM_OPS(NULL, acp63_pcm_resume)
282};
283
284static struct platform_driver acp63_driver = {
285	.probe = acp63_audio_probe,
286	.remove = acp63_audio_remove,
287	.driver = {
288		.name = "acp_asoc_acp63",
289		.pm = pm_ptr(&acp63_dma_pm_ops),
290	},
291};
292
293module_platform_driver(acp63_driver);
294
295MODULE_DESCRIPTION("AMD ACP acp63 Driver");
296MODULE_IMPORT_NS("SND_SOC_ACP_COMMON");
297MODULE_LICENSE("Dual BSD/GPL");
298MODULE_ALIAS("platform:" DRV_NAME);
~/ Read-only file preview.