~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/amd/acp/acp63.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 #include "amd.h"
 24 #include "acp-mach.h"
 25 #include "../mach-config.h"
 26 
 27 #define DRV_NAME "acp_asoc_acp63"
 28 
 29 #define CLK_PLL_PWR_REQ_N0              0X0006C2C0
 30 #define CLK_SPLL_FIELD_2_N0             0X0006C114
 31 #define CLK_PLL_REQ_N0                  0X0006C0DC
 32 #define CLK_DFSBYPASS_CONTR             0X0006C2C8
 33 #define CLK_DFS_CNTL_N0                 0X0006C1A4
 34 
 35 #define PLL_AUTO_STOP_REQ               BIT(4)
 36 #define PLL_AUTO_START_REQ              BIT(0)
 37 #define PLL_FRANCE_EN                   BIT(4)
 38 #define EXIT_DPF_BYPASS_0               BIT(16)
 39 #define EXIT_DPF_BYPASS_1               BIT(17)
 40 #define CLK0_DIVIDER                    0X30
 41 
 42 union clk_pll_req_no {
 43         struct {
 44                 u32 fb_mult_int : 9;
 45                 u32 reserved : 3;
 46                 u32 pll_spine_div : 4;
 47                 u32 gb_mult_frac : 16;
 48         } bitfields, bits;
 49         u32 clk_pll_req_no_reg;
 50 };
 51 
 52 static struct acp_resource rsrc = {
 53         .offset = 0,
 54         .no_of_ctrls = 2,
 55         .irqp_used = 1,
 56         .soc_mclk = true,
 57         .irq_reg_offset = 0x1a00,
 58         .scratch_reg_offset = 0x12800,
 59         .sram_pte_offset = 0x03802800,
 60 };
 61 
 62 static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines[] = {
 63         {
 64                 .id = "AMDI0052",
 65                 .drv_name = "acp63-acp",
 66         },
 67         {},
 68 };
 69 
 70 static struct snd_soc_dai_driver acp63_dai[] = {
 71 {
 72         .name = "acp-i2s-sp",
 73         .id = I2S_SP_INSTANCE,
 74         .playback = {
 75                 .stream_name = "I2S SP Playback",
 76                 .rates = SNDRV_PCM_RATE_8000_96000,
 77                 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
 78                            SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
 79                 .channels_min = 2,
 80                 .channels_max = 8,
 81                 .rate_min = 8000,
 82                 .rate_max = 96000,
 83         },
 84         .capture = {
 85                 .stream_name = "I2S SP Capture",
 86                 .rates = SNDRV_PCM_RATE_8000_48000,
 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 = 2,
 91                 .rate_min = 8000,
 92                 .rate_max = 48000,
 93         },
 94         .ops = &asoc_acp_cpu_dai_ops,
 95 },
 96 {
 97         .name = "acp-i2s-bt",
 98         .id = I2S_BT_INSTANCE,
 99         .playback = {
100                 .stream_name = "I2S BT Playback",
101                 .rates = SNDRV_PCM_RATE_8000_96000,
102                 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
103                            SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
104                 .channels_min = 2,
105                 .channels_max = 8,
106                 .rate_min = 8000,
107                 .rate_max = 96000,
108         },
109         .capture = {
110                 .stream_name = "I2S BT Capture",
111                 .rates = SNDRV_PCM_RATE_8000_48000,
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 = 2,
116                 .rate_min = 8000,
117                 .rate_max = 48000,
118         },
119         .ops = &asoc_acp_cpu_dai_ops,
120 },
121 {
122         .name = "acp-i2s-hs",
123         .id = I2S_HS_INSTANCE,
124         .playback = {
125                 .stream_name = "I2S HS Playback",
126                 .rates = SNDRV_PCM_RATE_8000_96000,
127                 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
128                            SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
129                 .channels_min = 2,
130                 .channels_max = 8,
131                 .rate_min = 8000,
132                 .rate_max = 96000,
133         },
134         .capture = {
135                 .stream_name = "I2S HS Capture",
136                 .rates = SNDRV_PCM_RATE_8000_48000,
137                 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
138                            SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
139                 .channels_min = 2,
140                 .channels_max = 8,
141                 .rate_min = 8000,
142                 .rate_max = 48000,
143         },
144         .ops = &asoc_acp_cpu_dai_ops,
145 },
146 {
147         .name = "acp-pdm-dmic",
148         .id = DMIC_INSTANCE,
149         .capture = {
150                 .rates = SNDRV_PCM_RATE_8000_48000,
151                 .formats = SNDRV_PCM_FMTBIT_S32_LE,
152                 .channels_min = 2,
153                 .channels_max = 2,
154                 .rate_min = 8000,
155                 .rate_max = 48000,
156         },
157         .ops = &acp_dmic_dai_ops,
158 },
159 };
160 
161 static int acp63_i2s_master_clock_generate(struct acp_dev_data *adata)
162 {
163         u32 data;
164         union clk_pll_req_no clk_pll;
165         struct pci_dev *smn_dev;
166 
167         smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x14E8, NULL);
168         if (!smn_dev)
169                 return -ENODEV;
170 
171         /* Clk5 pll register values to get mclk as 196.6MHz*/
172         clk_pll.bits.fb_mult_int = 0x31;
173         clk_pll.bits.pll_spine_div = 0;
174         clk_pll.bits.gb_mult_frac = 0x26E9;
175 
176         data = smn_read(smn_dev, CLK_PLL_PWR_REQ_N0);
177         smn_write(smn_dev, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_STOP_REQ);
178 
179         data = smn_read(smn_dev, CLK_SPLL_FIELD_2_N0);
180         if (data & PLL_FRANCE_EN)
181                 smn_write(smn_dev, CLK_SPLL_FIELD_2_N0, data | PLL_FRANCE_EN);
182 
183         smn_write(smn_dev, CLK_PLL_REQ_N0, clk_pll.clk_pll_req_no_reg);
184 
185         data = smn_read(smn_dev, CLK_PLL_PWR_REQ_N0);
186         smn_write(smn_dev, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_START_REQ);
187 
188         data = smn_read(smn_dev, CLK_DFSBYPASS_CONTR);
189         smn_write(smn_dev, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_0);
190         smn_write(smn_dev, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_1);
191 
192         smn_write(smn_dev, CLK_DFS_CNTL_N0, CLK0_DIVIDER);
193         return 0;
194 }
195 
196 static int acp63_audio_probe(struct platform_device *pdev)
197 {
198         struct device *dev = &pdev->dev;
199         struct acp_chip_info *chip;
200         struct acp_dev_data *adata;
201         struct resource *res;
202         int ret;
203 
204         chip = dev_get_platdata(&pdev->dev);
205         if (!chip || !chip->base) {
206                 dev_err(&pdev->dev, "ACP chip data is NULL\n");
207                 return -ENODEV;
208         }
209 
210         if (chip->acp_rev != ACP63_DEV) {
211                 dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
212                 return -ENODEV;
213         }
214 
215         adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
216         if (!adata)
217                 return -ENOMEM;
218 
219         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem");
220         if (!res) {
221                 dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
222                 return -ENODEV;
223         }
224 
225         adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
226         if (!adata->acp_base)
227                 return -ENOMEM;
228 
229         res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq");
230         if (!res) {
231                 dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
232                 return -ENODEV;
233         }
234 
235         adata->i2s_irq = res->start;
236         adata->dev = dev;
237         adata->dai_driver = acp63_dai;
238         adata->num_dai = ARRAY_SIZE(acp63_dai);
239         adata->rsrc = &rsrc;
240         adata->platform = ACP63;
241         adata->flag = chip->flag;
242         adata->is_i2s_config = chip->is_i2s_config;
243         adata->machines = snd_soc_acpi_amd_acp63_acp_machines;
244         acp_machine_select(adata);
245         dev_set_drvdata(dev, adata);
246 
247         if (chip->is_i2s_config && rsrc.soc_mclk) {
248                 ret = acp63_i2s_master_clock_generate(adata);
249                 if (ret)
250                         return ret;
251         }
252         acp_enable_interrupts(adata);
253         acp_platform_register(dev);
254         pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
255         pm_runtime_use_autosuspend(&pdev->dev);
256         pm_runtime_mark_last_busy(&pdev->dev);
257         pm_runtime_set_active(&pdev->dev);
258         pm_runtime_enable(&pdev->dev);
259         return 0;
260 }
261 
262 static void acp63_audio_remove(struct platform_device *pdev)
263 {
264         struct device *dev = &pdev->dev;
265         struct acp_dev_data *adata = dev_get_drvdata(dev);
266 
267         acp_disable_interrupts(adata);
268         acp_platform_unregister(dev);
269         pm_runtime_disable(&pdev->dev);
270 }
271 
272 static int __maybe_unused acp63_pcm_resume(struct device *dev)
273 {
274         struct acp_dev_data *adata = dev_get_drvdata(dev);
275         struct acp_stream *stream;
276         struct snd_pcm_substream *substream;
277         snd_pcm_uframes_t buf_in_frames;
278         u64 buf_size;
279 
280         if (adata->is_i2s_config && adata->rsrc->soc_mclk)
281                 acp63_i2s_master_clock_generate(adata);
282 
283         spin_lock(&adata->acp_lock);
284         list_for_each_entry(stream, &adata->stream_list, list) {
285                 substream = stream->substream;
286                 if (substream && substream->runtime) {
287                         buf_in_frames = (substream->runtime->buffer_size);
288                         buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
289                         config_pte_for_stream(adata, stream);
290                         config_acp_dma(adata, stream, buf_size);
291                         if (stream->dai_id)
292                                 restore_acp_i2s_params(substream, adata, stream);
293                         else
294                                 restore_acp_pdm_params(substream, adata);
295                 }
296         }
297         spin_unlock(&adata->acp_lock);
298         return 0;
299 }
300 
301 static const struct dev_pm_ops acp63_dma_pm_ops = {
302         SET_SYSTEM_SLEEP_PM_OPS(NULL, acp63_pcm_resume)
303 };
304 
305 static struct platform_driver acp63_driver = {
306         .probe = acp63_audio_probe,
307         .remove_new = acp63_audio_remove,
308         .driver = {
309                 .name = "acp_asoc_acp63",
310                 .pm = &acp63_dma_pm_ops,
311         },
312 };
313 
314 module_platform_driver(acp63_driver);
315 
316 MODULE_DESCRIPTION("AMD ACP acp63 Driver");
317 MODULE_IMPORT_NS(SND_SOC_ACP_COMMON);
318 MODULE_LICENSE("Dual BSD/GPL");
319 MODULE_ALIAS("platform:" DRV_NAME);
320 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php