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 ACP7.0 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 26 #define DRV_NAME "acp_asoc_acp70" 27 28 static struct acp_resource rsrc = { 29 .offset = 0, 30 .no_of_ctrls = 2, 31 .irqp_used = 1, 32 .soc_mclk = true, 33 .irq_reg_offset = 0x1a00, 34 .scratch_reg_offset = 0x12800, 35 .sram_pte_offset = 0x03802800, 36 }; 37 38 static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines[] = { 39 { 40 .id = "AMDI0029", 41 .drv_name = "acp70-acp", 42 }, 43 {}, 44 }; 45 46 static struct snd_soc_dai_driver acp70_dai[] = { 47 { 48 .name = "acp-i2s-sp", 49 .id = I2S_SP_INSTANCE, 50 .playback = { 51 .stream_name = "I2S SP Playback", 52 .rates = SNDRV_PCM_RATE_8000_96000, 53 .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 54 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, 55 .channels_min = 2, 56 .channels_max = 8, 57 .rate_min = 8000, 58 .rate_max = 96000, 59 }, 60 .capture = { 61 .stream_name = "I2S SP Capture", 62 .rates = SNDRV_PCM_RATE_8000_48000, 63 .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 64 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, 65 .channels_min = 2, 66 .channels_max = 2, 67 .rate_min = 8000, 68 .rate_max = 48000, 69 }, 70 .ops = &asoc_acp_cpu_dai_ops, 71 }, 72 { 73 .name = "acp-i2s-bt", 74 .id = I2S_BT_INSTANCE, 75 .playback = { 76 .stream_name = "I2S BT Playback", 77 .rates = SNDRV_PCM_RATE_8000_96000, 78 .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 79 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, 80 .channels_min = 2, 81 .channels_max = 8, 82 .rate_min = 8000, 83 .rate_max = 96000, 84 }, 85 .capture = { 86 .stream_name = "I2S BT Capture", 87 .rates = SNDRV_PCM_RATE_8000_48000, 88 .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 89 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, 90 .channels_min = 2, 91 .channels_max = 2, 92 .rate_min = 8000, 93 .rate_max = 48000, 94 }, 95 .ops = &asoc_acp_cpu_dai_ops, 96 }, 97 { 98 .name = "acp-i2s-hs", 99 .id = I2S_HS_INSTANCE, 100 .playback = { 101 .stream_name = "I2S HS Playback", 102 .rates = SNDRV_PCM_RATE_8000_96000, 103 .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 104 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, 105 .channels_min = 2, 106 .channels_max = 8, 107 .rate_min = 8000, 108 .rate_max = 96000, 109 }, 110 .capture = { 111 .stream_name = "I2S HS Capture", 112 .rates = SNDRV_PCM_RATE_8000_48000, 113 .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 114 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, 115 .channels_min = 2, 116 .channels_max = 8, 117 .rate_min = 8000, 118 .rate_max = 48000, 119 }, 120 .ops = &asoc_acp_cpu_dai_ops, 121 }, 122 { 123 .name = "acp-pdm-dmic", 124 .id = DMIC_INSTANCE, 125 .capture = { 126 .rates = SNDRV_PCM_RATE_8000_48000, 127 .formats = SNDRV_PCM_FMTBIT_S32_LE, 128 .channels_min = 2, 129 .channels_max = 2, 130 .rate_min = 8000, 131 .rate_max = 48000, 132 }, 133 .ops = &acp_dmic_dai_ops, 134 }, 135 }; 136 137 static int acp_acp70_audio_probe(struct platform_device *pdev) 138 { 139 struct device *dev = &pdev->dev; 140 struct acp_chip_info *chip; 141 struct acp_dev_data *adata; 142 struct resource *res; 143 144 chip = dev_get_platdata(&pdev->dev); 145 if (!chip || !chip->base) { 146 dev_err(&pdev->dev, "ACP chip data is NULL\n"); 147 return -ENODEV; 148 } 149 150 if (chip->acp_rev != ACP70_DEV) { 151 dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev); 152 return -ENODEV; 153 } 154 155 adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); 156 if (!adata) 157 return -ENOMEM; 158 159 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem"); 160 if (!res) { 161 dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); 162 return -ENODEV; 163 } 164 165 adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); 166 if (!adata->acp_base) 167 return -ENOMEM; 168 169 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq"); 170 if (!res) { 171 dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); 172 return -ENODEV; 173 } 174 175 adata->i2s_irq = res->start; 176 adata->dev = dev; 177 adata->dai_driver = acp70_dai; 178 adata->num_dai = ARRAY_SIZE(acp70_dai); 179 adata->rsrc = &rsrc; 180 adata->machines = snd_soc_acpi_amd_acp70_acp_machines; 181 adata->platform = ACP70; 182 adata->flag = chip->flag; 183 acp_machine_select(adata); 184 185 dev_set_drvdata(dev, adata); 186 acp_enable_interrupts(adata); 187 acp_platform_register(dev); 188 pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS); 189 pm_runtime_use_autosuspend(&pdev->dev); 190 pm_runtime_mark_last_busy(&pdev->dev); 191 pm_runtime_set_active(&pdev->dev); 192 pm_runtime_enable(&pdev->dev); 193 return 0; 194 } 195 196 static void acp_acp70_audio_remove(struct platform_device *pdev) 197 { 198 struct device *dev = &pdev->dev; 199 struct acp_dev_data *adata = dev_get_drvdata(dev); 200 201 acp_disable_interrupts(adata); 202 acp_platform_unregister(dev); 203 pm_runtime_disable(&pdev->dev); 204 } 205 206 static int __maybe_unused acp70_pcm_resume(struct device *dev) 207 { 208 struct acp_dev_data *adata = dev_get_drvdata(dev); 209 struct acp_stream *stream; 210 struct snd_pcm_substream *substream; 211 snd_pcm_uframes_t buf_in_frames; 212 u64 buf_size; 213 214 spin_lock(&adata->acp_lock); 215 list_for_each_entry(stream, &adata->stream_list, list) { 216 if (stream) { 217 substream = stream->substream; 218 if (substream && substream->runtime) { 219 buf_in_frames = (substream->runtime->buffer_size); 220 buf_size = frames_to_bytes(substream->runtime, buf_in_frames); 221 config_pte_for_stream(adata, stream); 222 config_acp_dma(adata, stream, buf_size); 223 if (stream->dai_id) 224 restore_acp_i2s_params(substream, adata, stream); 225 else 226 restore_acp_pdm_params(substream, adata); 227 } 228 } 229 } 230 spin_unlock(&adata->acp_lock); 231 return 0; 232 } 233 234 static const struct dev_pm_ops acp70_dma_pm_ops = { 235 SET_SYSTEM_SLEEP_PM_OPS(NULL, acp70_pcm_resume) 236 }; 237 238 static struct platform_driver acp70_driver = { 239 .probe = acp_acp70_audio_probe, 240 .remove_new = acp_acp70_audio_remove, 241 .driver = { 242 .name = "acp_asoc_acp70", 243 .pm = &acp70_dma_pm_ops, 244 }, 245 }; 246 247 module_platform_driver(acp70_driver); 248 249 MODULE_DESCRIPTION("AMD ACP ACP70 Driver"); 250 MODULE_IMPORT_NS(SND_SOC_ACP_COMMON); 251 MODULE_LICENSE("Dual BSD/GPL"); 252 MODULE_ALIAS("platform:" DRV_NAME); 253