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 = &_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 = &_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