486 lines · 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 or4// 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 11/*12 * Common file to be used by amd platforms13 */14 15#include "amd.h"16#include <linux/pci.h>17#include <linux/export.h>18 19#define ACP_RENOIR_PDM_ADDR 0x0220#define ACP_REMBRANDT_PDM_ADDR 0x0321#define ACP63_PDM_ADDR 0x0222#define ACP70_PDM_ADDR 0x0223 24void acp_enable_interrupts(struct acp_dev_data *adata)25{26 struct acp_resource *rsrc = adata->rsrc;27 u32 ext_intr_ctrl;28 29 writel(0x01, ACP_EXTERNAL_INTR_ENB(adata));30 ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));31 ext_intr_ctrl |= ACP_ERROR_MASK;32 writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));33}34EXPORT_SYMBOL_NS_GPL(acp_enable_interrupts, SND_SOC_ACP_COMMON);35 36void acp_disable_interrupts(struct acp_dev_data *adata)37{38 struct acp_resource *rsrc = adata->rsrc;39 40 writel(ACP_EXT_INTR_STAT_CLEAR_MASK, ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));41 writel(0x00, ACP_EXTERNAL_INTR_ENB(adata));42}43EXPORT_SYMBOL_NS_GPL(acp_disable_interrupts, SND_SOC_ACP_COMMON);44 45static void set_acp_pdm_ring_buffer(struct snd_pcm_substream *substream,46 struct snd_soc_dai *dai)47{48 struct snd_pcm_runtime *runtime = substream->runtime;49 struct acp_stream *stream = runtime->private_data;50 struct device *dev = dai->component->dev;51 struct acp_dev_data *adata = dev_get_drvdata(dev);52 53 u32 physical_addr, pdm_size, period_bytes;54 55 period_bytes = frames_to_bytes(runtime, runtime->period_size);56 pdm_size = frames_to_bytes(runtime, runtime->buffer_size);57 physical_addr = stream->reg_offset + MEM_WINDOW_START;58 59 /* Init ACP PDM Ring buffer */60 writel(physical_addr, adata->acp_base + ACP_WOV_RX_RINGBUFADDR);61 writel(pdm_size, adata->acp_base + ACP_WOV_RX_RINGBUFSIZE);62 writel(period_bytes, adata->acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);63 writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL);64}65 66static void set_acp_pdm_clk(struct snd_pcm_substream *substream,67 struct snd_soc_dai *dai)68{69 struct device *dev = dai->component->dev;70 struct acp_dev_data *adata = dev_get_drvdata(dev);71 unsigned int pdm_ctrl;72 73 /* Enable default ACP PDM clk */74 writel(PDM_CLK_FREQ_MASK, adata->acp_base + ACP_WOV_CLK_CTRL);75 pdm_ctrl = readl(adata->acp_base + ACP_WOV_MISC_CTRL);76 pdm_ctrl |= PDM_MISC_CTRL_MASK;77 writel(pdm_ctrl, adata->acp_base + ACP_WOV_MISC_CTRL);78 set_acp_pdm_ring_buffer(substream, dai);79}80 81void restore_acp_pdm_params(struct snd_pcm_substream *substream,82 struct acp_dev_data *adata)83{84 struct snd_soc_dai *dai;85 struct snd_soc_pcm_runtime *soc_runtime;86 u32 ext_int_ctrl;87 88 soc_runtime = snd_soc_substream_to_rtd(substream);89 dai = snd_soc_rtd_to_cpu(soc_runtime, 0);90 /* Programming channel mask and sampling rate */91 writel(adata->ch_mask, adata->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS);92 writel(PDM_DEC_64, adata->acp_base + ACP_WOV_PDM_DECIMATION_FACTOR);93 94 /* Enabling ACP Pdm interuppts */95 ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0));96 ext_int_ctrl |= PDM_DMA_INTR_MASK;97 writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0));98 set_acp_pdm_clk(substream, dai);99}100EXPORT_SYMBOL_NS_GPL(restore_acp_pdm_params, SND_SOC_ACP_COMMON);101 102static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream,103 struct snd_soc_dai *dai)104{105 struct device *dev = dai->component->dev;106 struct acp_dev_data *adata = dev_get_drvdata(dev);107 struct acp_resource *rsrc = adata->rsrc;108 struct acp_stream *stream = substream->runtime->private_data;109 u32 reg_dma_size, reg_fifo_size, reg_fifo_addr;110 u32 phy_addr, acp_fifo_addr, ext_int_ctrl;111 unsigned int dir = substream->stream;112 113 switch (dai->driver->id) {114 case I2S_SP_INSTANCE:115 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {116 reg_dma_size = ACP_I2S_TX_DMA_SIZE(adata);117 acp_fifo_addr = rsrc->sram_pte_offset +118 SP_PB_FIFO_ADDR_OFFSET;119 reg_fifo_addr = ACP_I2S_TX_FIFOADDR(adata);120 reg_fifo_size = ACP_I2S_TX_FIFOSIZE(adata);121 phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;122 writel(phy_addr, adata->acp_base + ACP_I2S_TX_RINGBUFADDR(adata));123 } else {124 reg_dma_size = ACP_I2S_RX_DMA_SIZE(adata);125 acp_fifo_addr = rsrc->sram_pte_offset +126 SP_CAPT_FIFO_ADDR_OFFSET;127 reg_fifo_addr = ACP_I2S_RX_FIFOADDR(adata);128 reg_fifo_size = ACP_I2S_RX_FIFOSIZE(adata);129 phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;130 writel(phy_addr, adata->acp_base + ACP_I2S_RX_RINGBUFADDR(adata));131 }132 break;133 case I2S_BT_INSTANCE:134 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {135 reg_dma_size = ACP_BT_TX_DMA_SIZE(adata);136 acp_fifo_addr = rsrc->sram_pte_offset +137 BT_PB_FIFO_ADDR_OFFSET;138 reg_fifo_addr = ACP_BT_TX_FIFOADDR(adata);139 reg_fifo_size = ACP_BT_TX_FIFOSIZE(adata);140 phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;141 writel(phy_addr, adata->acp_base + ACP_BT_TX_RINGBUFADDR(adata));142 } else {143 reg_dma_size = ACP_BT_RX_DMA_SIZE(adata);144 acp_fifo_addr = rsrc->sram_pte_offset +145 BT_CAPT_FIFO_ADDR_OFFSET;146 reg_fifo_addr = ACP_BT_RX_FIFOADDR(adata);147 reg_fifo_size = ACP_BT_RX_FIFOSIZE(adata);148 phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;149 writel(phy_addr, adata->acp_base + ACP_BT_RX_RINGBUFADDR(adata));150 }151 break;152 case I2S_HS_INSTANCE:153 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {154 reg_dma_size = ACP_HS_TX_DMA_SIZE;155 acp_fifo_addr = rsrc->sram_pte_offset +156 HS_PB_FIFO_ADDR_OFFSET;157 reg_fifo_addr = ACP_HS_TX_FIFOADDR;158 reg_fifo_size = ACP_HS_TX_FIFOSIZE;159 phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset;160 writel(phy_addr, adata->acp_base + ACP_HS_TX_RINGBUFADDR);161 } else {162 reg_dma_size = ACP_HS_RX_DMA_SIZE;163 acp_fifo_addr = rsrc->sram_pte_offset +164 HS_CAPT_FIFO_ADDR_OFFSET;165 reg_fifo_addr = ACP_HS_RX_FIFOADDR;166 reg_fifo_size = ACP_HS_RX_FIFOSIZE;167 phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset;168 writel(phy_addr, adata->acp_base + ACP_HS_RX_RINGBUFADDR);169 }170 break;171 default:172 dev_err(dev, "Invalid dai id %x\n", dai->driver->id);173 return -EINVAL;174 }175 176 writel(DMA_SIZE, adata->acp_base + reg_dma_size);177 writel(acp_fifo_addr, adata->acp_base + reg_fifo_addr);178 writel(FIFO_SIZE, adata->acp_base + reg_fifo_size);179 180 ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));181 ext_int_ctrl |= BIT(I2S_RX_THRESHOLD(rsrc->offset)) |182 BIT(BT_RX_THRESHOLD(rsrc->offset)) |183 BIT(I2S_TX_THRESHOLD(rsrc->offset)) |184 BIT(BT_TX_THRESHOLD(rsrc->offset)) |185 BIT(HS_RX_THRESHOLD(rsrc->offset)) |186 BIT(HS_TX_THRESHOLD(rsrc->offset));187 188 writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));189 return 0;190}191 192int restore_acp_i2s_params(struct snd_pcm_substream *substream,193 struct acp_dev_data *adata,194 struct acp_stream *stream)195{196 struct snd_soc_dai *dai;197 struct snd_soc_pcm_runtime *soc_runtime;198 u32 tdm_fmt, reg_val, fmt_reg, val;199 200 soc_runtime = snd_soc_substream_to_rtd(substream);201 dai = snd_soc_rtd_to_cpu(soc_runtime, 0);202 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {203 tdm_fmt = adata->tdm_tx_fmt[stream->dai_id - 1];204 switch (stream->dai_id) {205 case I2S_BT_INSTANCE:206 reg_val = ACP_BTTDM_ITER;207 fmt_reg = ACP_BTTDM_TXFRMT;208 break;209 case I2S_SP_INSTANCE:210 reg_val = ACP_I2STDM_ITER;211 fmt_reg = ACP_I2STDM_TXFRMT;212 break;213 case I2S_HS_INSTANCE:214 reg_val = ACP_HSTDM_ITER;215 fmt_reg = ACP_HSTDM_TXFRMT;216 break;217 default:218 pr_err("Invalid dai id %x\n", stream->dai_id);219 return -EINVAL;220 }221 val = adata->xfer_tx_resolution[stream->dai_id - 1] << 3;222 } else {223 tdm_fmt = adata->tdm_rx_fmt[stream->dai_id - 1];224 switch (stream->dai_id) {225 case I2S_BT_INSTANCE:226 reg_val = ACP_BTTDM_IRER;227 fmt_reg = ACP_BTTDM_RXFRMT;228 break;229 case I2S_SP_INSTANCE:230 reg_val = ACP_I2STDM_IRER;231 fmt_reg = ACP_I2STDM_RXFRMT;232 break;233 case I2S_HS_INSTANCE:234 reg_val = ACP_HSTDM_IRER;235 fmt_reg = ACP_HSTDM_RXFRMT;236 break;237 default:238 pr_err("Invalid dai id %x\n", stream->dai_id);239 return -EINVAL;240 }241 val = adata->xfer_rx_resolution[stream->dai_id - 1] << 3;242 }243 writel(val, adata->acp_base + reg_val);244 if (adata->tdm_mode == TDM_ENABLE) {245 writel(tdm_fmt, adata->acp_base + fmt_reg);246 val = readl(adata->acp_base + reg_val);247 writel(val | 0x2, adata->acp_base + reg_val);248 }249 return set_acp_i2s_dma_fifo(substream, dai);250}251EXPORT_SYMBOL_NS_GPL(restore_acp_i2s_params, SND_SOC_ACP_COMMON);252 253static int acp_power_on(struct acp_chip_info *chip)254{255 u32 val, acp_pgfsm_stat_reg, acp_pgfsm_ctrl_reg;256 void __iomem *base;257 258 base = chip->base;259 switch (chip->acp_rev) {260 case ACP3X_DEV:261 acp_pgfsm_stat_reg = ACP_PGFSM_STATUS;262 acp_pgfsm_ctrl_reg = ACP_PGFSM_CONTROL;263 break;264 case ACP6X_DEV:265 acp_pgfsm_stat_reg = ACP6X_PGFSM_STATUS;266 acp_pgfsm_ctrl_reg = ACP6X_PGFSM_CONTROL;267 break;268 case ACP63_DEV:269 acp_pgfsm_stat_reg = ACP63_PGFSM_STATUS;270 acp_pgfsm_ctrl_reg = ACP63_PGFSM_CONTROL;271 break;272 case ACP70_DEV:273 case ACP71_DEV:274 acp_pgfsm_stat_reg = ACP70_PGFSM_STATUS;275 acp_pgfsm_ctrl_reg = ACP70_PGFSM_CONTROL;276 break;277 default:278 return -EINVAL;279 }280 281 val = readl(base + acp_pgfsm_stat_reg);282 if (val == ACP_POWERED_ON)283 return 0;284 285 if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)286 writel(ACP_PGFSM_CNTL_POWER_ON_MASK, base + acp_pgfsm_ctrl_reg);287 288 return readl_poll_timeout(base + acp_pgfsm_stat_reg, val,289 !val, DELAY_US, ACP_TIMEOUT);290}291 292static int acp_reset(void __iomem *base)293{294 u32 val;295 int ret;296 297 writel(1, base + ACP_SOFT_RESET);298 ret = readl_poll_timeout(base + ACP_SOFT_RESET, val, val & ACP_SOFT_RST_DONE_MASK,299 DELAY_US, ACP_TIMEOUT);300 if (ret)301 return ret;302 303 writel(0, base + ACP_SOFT_RESET);304 return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT);305}306 307int acp_init(struct acp_chip_info *chip)308{309 int ret;310 311 /* power on */312 ret = acp_power_on(chip);313 if (ret) {314 pr_err("ACP power on failed\n");315 return ret;316 }317 writel(0x01, chip->base + ACP_CONTROL);318 319 /* Reset */320 ret = acp_reset(chip->base);321 if (ret) {322 pr_err("ACP reset failed\n");323 return ret;324 }325 if (chip->acp_rev >= ACP70_DEV)326 writel(0, chip->base + ACP_ZSC_DSP_CTRL);327 return 0;328}329EXPORT_SYMBOL_NS_GPL(acp_init, SND_SOC_ACP_COMMON);330 331int acp_deinit(struct acp_chip_info *chip)332{333 int ret;334 335 /* Reset */336 ret = acp_reset(chip->base);337 if (ret)338 return ret;339 340 if (chip->acp_rev < ACP70_DEV)341 writel(0, chip->base + ACP_CONTROL);342 else343 writel(0x01, chip->base + ACP_ZSC_DSP_CTRL);344 return 0;345}346EXPORT_SYMBOL_NS_GPL(acp_deinit, SND_SOC_ACP_COMMON);347 348int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data)349{350 pci_write_config_dword(dev, 0x60, smn_addr);351 pci_write_config_dword(dev, 0x64, data);352 return 0;353}354EXPORT_SYMBOL_NS_GPL(smn_write, SND_SOC_ACP_COMMON);355 356int smn_read(struct pci_dev *dev, u32 smn_addr)357{358 u32 data;359 360 pci_write_config_dword(dev, 0x60, smn_addr);361 pci_read_config_dword(dev, 0x64, &data);362 return data;363}364EXPORT_SYMBOL_NS_GPL(smn_read, SND_SOC_ACP_COMMON);365 366static void check_acp3x_config(struct acp_chip_info *chip)367{368 u32 val;369 370 val = readl(chip->base + ACP3X_PIN_CONFIG);371 switch (val) {372 case ACP_CONFIG_4:373 chip->is_i2s_config = true;374 chip->is_pdm_config = true;375 break;376 default:377 chip->is_pdm_config = true;378 break;379 }380}381 382static void check_acp6x_config(struct acp_chip_info *chip)383{384 u32 val;385 386 val = readl(chip->base + ACP_PIN_CONFIG);387 switch (val) {388 case ACP_CONFIG_4:389 case ACP_CONFIG_5:390 case ACP_CONFIG_6:391 case ACP_CONFIG_7:392 case ACP_CONFIG_8:393 case ACP_CONFIG_11:394 case ACP_CONFIG_14:395 chip->is_pdm_config = true;396 break;397 case ACP_CONFIG_9:398 chip->is_i2s_config = true;399 break;400 case ACP_CONFIG_10:401 case ACP_CONFIG_12:402 case ACP_CONFIG_13:403 chip->is_i2s_config = true;404 chip->is_pdm_config = true;405 break;406 default:407 break;408 }409}410 411static void check_acp70_config(struct acp_chip_info *chip)412{413 u32 val;414 415 val = readl(chip->base + ACP_PIN_CONFIG);416 switch (val) {417 case ACP_CONFIG_4:418 case ACP_CONFIG_5:419 case ACP_CONFIG_6:420 case ACP_CONFIG_7:421 case ACP_CONFIG_8:422 case ACP_CONFIG_11:423 case ACP_CONFIG_14:424 case ACP_CONFIG_17:425 case ACP_CONFIG_18:426 chip->is_pdm_config = true;427 break;428 case ACP_CONFIG_9:429 chip->is_i2s_config = true;430 break;431 case ACP_CONFIG_10:432 case ACP_CONFIG_12:433 case ACP_CONFIG_13:434 case ACP_CONFIG_19:435 case ACP_CONFIG_20:436 chip->is_i2s_config = true;437 chip->is_pdm_config = true;438 break;439 default:440 break;441 }442}443 444void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip)445{446 struct acpi_device *pdm_dev;447 const union acpi_object *obj;448 u32 pdm_addr;449 450 switch (chip->acp_rev) {451 case ACP3X_DEV:452 pdm_addr = ACP_RENOIR_PDM_ADDR;453 check_acp3x_config(chip);454 break;455 case ACP6X_DEV:456 pdm_addr = ACP_REMBRANDT_PDM_ADDR;457 check_acp6x_config(chip);458 break;459 case ACP63_DEV:460 pdm_addr = ACP63_PDM_ADDR;461 check_acp6x_config(chip);462 break;463 case ACP70_DEV:464 case ACP71_DEV:465 pdm_addr = ACP70_PDM_ADDR;466 check_acp70_config(chip);467 break;468 default:469 break;470 }471 472 if (chip->is_pdm_config) {473 pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), pdm_addr, 0);474 if (pdm_dev) {475 if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type",476 ACPI_TYPE_INTEGER, &obj) &&477 obj->integer.value == pdm_addr)478 chip->is_pdm_dev = true;479 }480 }481}482EXPORT_SYMBOL_NS_GPL(check_acp_config, SND_SOC_ACP_COMMON);483 484MODULE_DESCRIPTION("AMD ACP legacy common features");485MODULE_LICENSE("Dual BSD/GPL");486