acp-renoir.c (7473B)
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) 2021 Advanced Micro Devices, Inc. 7// 8// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> 9// 10 11/* 12 * Hardware interface for Renoir ACP block 13 */ 14 15#include <linux/platform_device.h> 16#include <linux/module.h> 17#include <linux/err.h> 18#include <linux/io.h> 19#include <sound/pcm_params.h> 20#include <sound/soc.h> 21#include <sound/soc-dai.h> 22#include <linux/dma-mapping.h> 23 24#include "amd.h" 25 26#define DRV_NAME "acp_asoc_renoir" 27 28#define ACP_SOFT_RST_DONE_MASK 0x00010001 29 30#define ACP_PWR_ON_MASK 0x01 31#define ACP_PWR_OFF_MASK 0x00 32#define ACP_PGFSM_STAT_MASK 0x03 33#define ACP_POWERED_ON 0x00 34#define ACP_PWR_ON_IN_PROGRESS 0x01 35#define ACP_POWERED_OFF 0x02 36#define DELAY_US 5 37#define ACP_TIMEOUT 500 38 39#define ACP_ERROR_MASK 0x20000000 40#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF 41 42static struct snd_soc_acpi_codecs amp_rt1019 = { 43 .num_codecs = 1, 44 .codecs = {"10EC1019"} 45}; 46 47static struct snd_soc_acpi_codecs amp_max = { 48 .num_codecs = 1, 49 .codecs = {"MX98360A"} 50}; 51 52static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = { 53 { 54 .id = "10EC5682", 55 .drv_name = "acp3xalc56821019", 56 .machine_quirk = snd_soc_acpi_codec_list, 57 .quirk_data = &_rt1019, 58 }, 59 { 60 .id = "RTL5682", 61 .drv_name = "acp3xalc5682sm98360", 62 .machine_quirk = snd_soc_acpi_codec_list, 63 .quirk_data = &_max, 64 }, 65 { 66 .id = "RTL5682", 67 .drv_name = "acp3xalc5682s1019", 68 .machine_quirk = snd_soc_acpi_codec_list, 69 .quirk_data = &_rt1019, 70 }, 71 { 72 .id = "AMDI1019", 73 .drv_name = "renoir-acp", 74 }, 75 {}, 76}; 77 78static struct snd_soc_dai_driver acp_renoir_dai[] = { 79{ 80 .name = "acp-i2s-sp", 81 .id = I2S_SP_INSTANCE, 82 .playback = { 83 .stream_name = "I2S SP Playback", 84 .rates = SNDRV_PCM_RATE_8000_96000, 85 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 86 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, 87 .channels_min = 2, 88 .channels_max = 8, 89 .rate_min = 8000, 90 .rate_max = 96000, 91 }, 92 .capture = { 93 .stream_name = "I2S SP Capture", 94 .rates = SNDRV_PCM_RATE_8000_48000, 95 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 96 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, 97 .channels_min = 2, 98 .channels_max = 2, 99 .rate_min = 8000, 100 .rate_max = 48000, 101 }, 102 .ops = &asoc_acp_cpu_dai_ops, 103 .probe = &asoc_acp_i2s_probe, 104}, 105{ 106 .name = "acp-i2s-bt", 107 .id = I2S_BT_INSTANCE, 108 .playback = { 109 .stream_name = "I2S BT Playback", 110 .rates = SNDRV_PCM_RATE_8000_96000, 111 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 112 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, 113 .channels_min = 2, 114 .channels_max = 8, 115 .rate_min = 8000, 116 .rate_max = 96000, 117 }, 118 .capture = { 119 .stream_name = "I2S BT Capture", 120 .rates = SNDRV_PCM_RATE_8000_48000, 121 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 122 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, 123 .channels_min = 2, 124 .channels_max = 2, 125 .rate_min = 8000, 126 .rate_max = 48000, 127 }, 128 .ops = &asoc_acp_cpu_dai_ops, 129 .probe = &asoc_acp_i2s_probe, 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 acp3x_power_on(void __iomem *base) 147{ 148 u32 val; 149 150 val = readl(base + ACP_PGFSM_STATUS); 151 152 if (val == ACP_POWERED_ON) 153 return 0; 154 155 if ((val & ACP_PGFSM_STAT_MASK) != ACP_PWR_ON_IN_PROGRESS) 156 writel(ACP_PWR_ON_MASK, base + ACP_PGFSM_CONTROL); 157 158 return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT); 159} 160 161static int acp3x_power_off(void __iomem *base) 162{ 163 u32 val; 164 165 writel(ACP_PWR_OFF_MASK, base + ACP_PGFSM_CONTROL); 166 167 return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, 168 (val & ACP_PGFSM_STAT_MASK) == ACP_POWERED_OFF, 169 DELAY_US, ACP_TIMEOUT); 170} 171 172static int acp3x_reset(void __iomem *base) 173{ 174 u32 val; 175 int ret; 176 177 writel(1, base + ACP_SOFT_RESET); 178 179 ret = readl_poll_timeout(base + ACP_SOFT_RESET, val, val & ACP_SOFT_RST_DONE_MASK, 180 DELAY_US, ACP_TIMEOUT); 181 if (ret) 182 return ret; 183 184 writel(0, base + ACP_SOFT_RESET); 185 186 return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT); 187} 188 189static void acp3x_enable_interrupts(void __iomem *base) 190{ 191 u32 ext_intr_ctrl; 192 193 writel(0x01, base + ACP_EXTERNAL_INTR_ENB); 194 ext_intr_ctrl = readl(base + ACP_EXTERNAL_INTR_CNTL); 195 ext_intr_ctrl |= ACP_ERROR_MASK; 196 writel(ext_intr_ctrl, base + ACP_EXTERNAL_INTR_CNTL); 197} 198 199static void acp3x_disable_interrupts(void __iomem *base) 200{ 201 writel(ACP_EXT_INTR_STAT_CLEAR_MASK, base + ACP_EXTERNAL_INTR_STAT); 202 writel(0x00, base + ACP_EXTERNAL_INTR_ENB); 203} 204 205static int rn_acp_init(void __iomem *base) 206{ 207 int ret; 208 209 /* power on */ 210 ret = acp3x_power_on(base); 211 if (ret) 212 return ret; 213 214 writel(0x01, base + ACP_CONTROL); 215 216 /* Reset */ 217 ret = acp3x_reset(base); 218 if (ret) 219 return ret; 220 221 acp3x_enable_interrupts(base); 222 223 return 0; 224} 225 226static int rn_acp_deinit(void __iomem *base) 227{ 228 int ret = 0; 229 230 acp3x_disable_interrupts(base); 231 232 /* Reset */ 233 ret = acp3x_reset(base); 234 if (ret) 235 return ret; 236 237 writel(0x00, base + ACP_CONTROL); 238 239 /* power off */ 240 ret = acp3x_power_off(base); 241 if (ret) 242 return ret; 243 244 return 0; 245} 246static int renoir_audio_probe(struct platform_device *pdev) 247{ 248 struct device *dev = &pdev->dev; 249 struct acp_chip_info *chip; 250 struct acp_dev_data *adata; 251 struct resource *res; 252 int ret; 253 254 chip = dev_get_platdata(&pdev->dev); 255 if (!chip || !chip->base) { 256 dev_err(&pdev->dev, "ACP chip data is NULL\n"); 257 return -ENODEV; 258 } 259 260 if (chip->acp_rev != ACP3X_DEV) { 261 dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev); 262 return -ENODEV; 263 } 264 265 ret = rn_acp_init(chip->base); 266 if (ret) { 267 dev_err(&pdev->dev, "ACP Init failed\n"); 268 return -EINVAL; 269 } 270 271 adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); 272 if (!adata) 273 return -ENOMEM; 274 275 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem"); 276 if (!res) { 277 dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); 278 return -ENODEV; 279 } 280 281 adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); 282 if (!adata->acp_base) 283 return -ENOMEM; 284 285 ret = platform_get_irq_byname(pdev, "acp_dai_irq"); 286 if (ret < 0) 287 return ret; 288 adata->i2s_irq = ret; 289 290 adata->dev = dev; 291 adata->dai_driver = acp_renoir_dai; 292 adata->num_dai = ARRAY_SIZE(acp_renoir_dai); 293 294 adata->machines = snd_soc_acpi_amd_acp_machines; 295 acp_machine_select(adata); 296 297 dev_set_drvdata(dev, adata); 298 acp_platform_register(dev); 299 300 return 0; 301} 302 303static int renoir_audio_remove(struct platform_device *pdev) 304{ 305 struct device *dev = &pdev->dev; 306 struct acp_chip_info *chip; 307 int ret; 308 309 chip = dev_get_platdata(&pdev->dev); 310 if (!chip || !chip->base) { 311 dev_err(&pdev->dev, "ACP chip data is NULL\n"); 312 return -ENODEV; 313 } 314 315 ret = rn_acp_deinit(chip->base); 316 if (ret) { 317 dev_err(&pdev->dev, "ACP de-init Failed\n"); 318 return -EINVAL; 319 } 320 321 acp_platform_unregister(dev); 322 return 0; 323} 324 325static struct platform_driver renoir_driver = { 326 .probe = renoir_audio_probe, 327 .remove = renoir_audio_remove, 328 .driver = { 329 .name = "acp_asoc_renoir", 330 }, 331}; 332 333module_platform_driver(renoir_driver); 334 335MODULE_DESCRIPTION("AMD ACP Renoir Driver"); 336MODULE_IMPORT_NS(SND_SOC_ACP_COMMON); 337MODULE_LICENSE("Dual BSD/GPL"); 338MODULE_ALIAS("platform:" DRV_NAME);