acp5x-pcm-dma.c (15085B)
1// SPDX-License-Identifier: GPL-2.0+ 2// 3// AMD ALSA SoC PCM Driver 4// 5// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved. 6 7#include <linux/platform_device.h> 8#include <linux/module.h> 9#include <linux/err.h> 10#include <linux/io.h> 11#include <linux/pm_runtime.h> 12#include <sound/pcm.h> 13#include <sound/pcm_params.h> 14#include <sound/soc.h> 15#include <sound/soc-dai.h> 16 17#include "acp5x.h" 18 19#define DRV_NAME "acp5x_i2s_dma" 20 21static const struct snd_pcm_hardware acp5x_pcm_hardware_playback = { 22 .info = SNDRV_PCM_INFO_INTERLEAVED | 23 SNDRV_PCM_INFO_BLOCK_TRANSFER | 24 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 25 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 26 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 27 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, 28 .channels_min = 2, 29 .channels_max = 2, 30 .rates = SNDRV_PCM_RATE_8000_96000, 31 .rate_min = 8000, 32 .rate_max = 96000, 33 .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE, 34 .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, 35 .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, 36 .periods_min = PLAYBACK_MIN_NUM_PERIODS, 37 .periods_max = PLAYBACK_MAX_NUM_PERIODS, 38}; 39 40static const struct snd_pcm_hardware acp5x_pcm_hardware_capture = { 41 .info = SNDRV_PCM_INFO_INTERLEAVED | 42 SNDRV_PCM_INFO_BLOCK_TRANSFER | 43 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 44 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 45 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 46 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, 47 .channels_min = 2, 48 .channels_max = 2, 49 .rates = SNDRV_PCM_RATE_8000_96000, 50 .rate_min = 8000, 51 .rate_max = 96000, 52 .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE, 53 .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, 54 .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, 55 .periods_min = CAPTURE_MIN_NUM_PERIODS, 56 .periods_max = CAPTURE_MAX_NUM_PERIODS, 57}; 58 59static irqreturn_t i2s_irq_handler(int irq, void *dev_id) 60{ 61 struct i2s_dev_data *vg_i2s_data; 62 u16 irq_flag; 63 u32 val; 64 65 vg_i2s_data = dev_id; 66 if (!vg_i2s_data) 67 return IRQ_NONE; 68 69 irq_flag = 0; 70 val = acp_readl(vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT); 71 if ((val & BIT(HS_TX_THRESHOLD)) && vg_i2s_data->play_stream) { 72 acp_writel(BIT(HS_TX_THRESHOLD), vg_i2s_data->acp5x_base + 73 ACP_EXTERNAL_INTR_STAT); 74 snd_pcm_period_elapsed(vg_i2s_data->play_stream); 75 irq_flag = 1; 76 } 77 if ((val & BIT(I2S_TX_THRESHOLD)) && vg_i2s_data->i2ssp_play_stream) { 78 acp_writel(BIT(I2S_TX_THRESHOLD), 79 vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT); 80 snd_pcm_period_elapsed(vg_i2s_data->i2ssp_play_stream); 81 irq_flag = 1; 82 } 83 84 if ((val & BIT(HS_RX_THRESHOLD)) && vg_i2s_data->capture_stream) { 85 acp_writel(BIT(HS_RX_THRESHOLD), vg_i2s_data->acp5x_base + 86 ACP_EXTERNAL_INTR_STAT); 87 snd_pcm_period_elapsed(vg_i2s_data->capture_stream); 88 irq_flag = 1; 89 } 90 if ((val & BIT(I2S_RX_THRESHOLD)) && vg_i2s_data->i2ssp_capture_stream) { 91 acp_writel(BIT(I2S_RX_THRESHOLD), 92 vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT); 93 snd_pcm_period_elapsed(vg_i2s_data->i2ssp_capture_stream); 94 irq_flag = 1; 95 } 96 97 if (irq_flag) 98 return IRQ_HANDLED; 99 else 100 return IRQ_NONE; 101} 102 103static void config_acp5x_dma(struct i2s_stream_instance *rtd, int direction) 104{ 105 u16 page_idx; 106 u32 low, high, val, acp_fifo_addr, reg_fifo_addr; 107 u32 reg_dma_size, reg_fifo_size; 108 dma_addr_t addr; 109 110 addr = rtd->dma_addr; 111 if (direction == SNDRV_PCM_STREAM_PLAYBACK) { 112 switch (rtd->i2s_instance) { 113 case I2S_HS_INSTANCE: 114 val = ACP_SRAM_HS_PB_PTE_OFFSET; 115 break; 116 case I2S_SP_INSTANCE: 117 default: 118 val = ACP_SRAM_SP_PB_PTE_OFFSET; 119 } 120 } else { 121 switch (rtd->i2s_instance) { 122 case I2S_HS_INSTANCE: 123 val = ACP_SRAM_HS_CP_PTE_OFFSET; 124 break; 125 case I2S_SP_INSTANCE: 126 default: 127 val = ACP_SRAM_SP_CP_PTE_OFFSET; 128 } 129 } 130 /* Group Enable */ 131 acp_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp5x_base + 132 ACPAXI2AXI_ATU_BASE_ADDR_GRP_1); 133 acp_writel(PAGE_SIZE_4K_ENABLE, rtd->acp5x_base + 134 ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1); 135 136 for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) { 137 /* Load the low address of page int ACP SRAM through SRBM */ 138 low = lower_32_bits(addr); 139 high = upper_32_bits(addr); 140 141 acp_writel(low, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val); 142 high |= BIT(31); 143 acp_writel(high, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val + 4); 144 /* Move to next physically contiguous page */ 145 val += 8; 146 addr += PAGE_SIZE; 147 } 148 149 if (direction == SNDRV_PCM_STREAM_PLAYBACK) { 150 switch (rtd->i2s_instance) { 151 case I2S_HS_INSTANCE: 152 reg_dma_size = ACP_HS_TX_DMA_SIZE; 153 acp_fifo_addr = ACP_SRAM_PTE_OFFSET + 154 HS_PB_FIFO_ADDR_OFFSET; 155 reg_fifo_addr = ACP_HS_TX_FIFOADDR; 156 reg_fifo_size = ACP_HS_TX_FIFOSIZE; 157 acp_writel(I2S_HS_TX_MEM_WINDOW_START, 158 rtd->acp5x_base + ACP_HS_TX_RINGBUFADDR); 159 break; 160 161 case I2S_SP_INSTANCE: 162 default: 163 reg_dma_size = ACP_I2S_TX_DMA_SIZE; 164 acp_fifo_addr = ACP_SRAM_PTE_OFFSET + 165 SP_PB_FIFO_ADDR_OFFSET; 166 reg_fifo_addr = ACP_I2S_TX_FIFOADDR; 167 reg_fifo_size = ACP_I2S_TX_FIFOSIZE; 168 acp_writel(I2S_SP_TX_MEM_WINDOW_START, 169 rtd->acp5x_base + ACP_I2S_TX_RINGBUFADDR); 170 } 171 } else { 172 switch (rtd->i2s_instance) { 173 case I2S_HS_INSTANCE: 174 reg_dma_size = ACP_HS_RX_DMA_SIZE; 175 acp_fifo_addr = ACP_SRAM_PTE_OFFSET + 176 HS_CAPT_FIFO_ADDR_OFFSET; 177 reg_fifo_addr = ACP_HS_RX_FIFOADDR; 178 reg_fifo_size = ACP_HS_RX_FIFOSIZE; 179 acp_writel(I2S_HS_RX_MEM_WINDOW_START, 180 rtd->acp5x_base + ACP_HS_RX_RINGBUFADDR); 181 break; 182 183 case I2S_SP_INSTANCE: 184 default: 185 reg_dma_size = ACP_I2S_RX_DMA_SIZE; 186 acp_fifo_addr = ACP_SRAM_PTE_OFFSET + 187 SP_CAPT_FIFO_ADDR_OFFSET; 188 reg_fifo_addr = ACP_I2S_RX_FIFOADDR; 189 reg_fifo_size = ACP_I2S_RX_FIFOSIZE; 190 acp_writel(I2S_SP_RX_MEM_WINDOW_START, 191 rtd->acp5x_base + ACP_I2S_RX_RINGBUFADDR); 192 } 193 } 194 acp_writel(DMA_SIZE, rtd->acp5x_base + reg_dma_size); 195 acp_writel(acp_fifo_addr, rtd->acp5x_base + reg_fifo_addr); 196 acp_writel(FIFO_SIZE, rtd->acp5x_base + reg_fifo_size); 197 acp_writel(BIT(I2S_RX_THRESHOLD) | BIT(HS_RX_THRESHOLD) 198 | BIT(I2S_TX_THRESHOLD) | BIT(HS_TX_THRESHOLD), 199 rtd->acp5x_base + ACP_EXTERNAL_INTR_CNTL); 200} 201 202static int acp5x_dma_open(struct snd_soc_component *component, 203 struct snd_pcm_substream *substream) 204{ 205 struct snd_pcm_runtime *runtime; 206 struct snd_soc_pcm_runtime *prtd; 207 struct i2s_dev_data *adata; 208 struct i2s_stream_instance *i2s_data; 209 int ret; 210 211 runtime = substream->runtime; 212 prtd = asoc_substream_to_rtd(substream); 213 component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); 214 adata = dev_get_drvdata(component->dev); 215 216 i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL); 217 if (!i2s_data) 218 return -ENOMEM; 219 220 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 221 runtime->hw = acp5x_pcm_hardware_playback; 222 else 223 runtime->hw = acp5x_pcm_hardware_capture; 224 225 ret = snd_pcm_hw_constraint_integer(runtime, 226 SNDRV_PCM_HW_PARAM_PERIODS); 227 if (ret < 0) { 228 dev_err(component->dev, "set integer constraint failed\n"); 229 kfree(i2s_data); 230 return ret; 231 } 232 i2s_data->acp5x_base = adata->acp5x_base; 233 runtime->private_data = i2s_data; 234 return ret; 235} 236 237static int acp5x_dma_hw_params(struct snd_soc_component *component, 238 struct snd_pcm_substream *substream, 239 struct snd_pcm_hw_params *params) 240{ 241 struct i2s_stream_instance *rtd; 242 struct snd_soc_pcm_runtime *prtd; 243 struct snd_soc_card *card; 244 struct acp5x_platform_info *pinfo; 245 struct i2s_dev_data *adata; 246 u64 size; 247 248 prtd = asoc_substream_to_rtd(substream); 249 card = prtd->card; 250 pinfo = snd_soc_card_get_drvdata(card); 251 adata = dev_get_drvdata(component->dev); 252 rtd = substream->runtime->private_data; 253 254 if (!rtd) 255 return -EINVAL; 256 257 if (pinfo) { 258 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 259 rtd->i2s_instance = pinfo->play_i2s_instance; 260 switch (rtd->i2s_instance) { 261 case I2S_HS_INSTANCE: 262 adata->play_stream = substream; 263 break; 264 case I2S_SP_INSTANCE: 265 default: 266 adata->i2ssp_play_stream = substream; 267 } 268 } else { 269 rtd->i2s_instance = pinfo->cap_i2s_instance; 270 switch (rtd->i2s_instance) { 271 case I2S_HS_INSTANCE: 272 adata->capture_stream = substream; 273 break; 274 case I2S_SP_INSTANCE: 275 default: 276 adata->i2ssp_capture_stream = substream; 277 } 278 } 279 } else { 280 dev_err(component->dev, "pinfo failed\n"); 281 return -EINVAL; 282 } 283 size = params_buffer_bytes(params); 284 rtd->dma_addr = substream->runtime->dma_addr; 285 rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); 286 config_acp5x_dma(rtd, substream->stream); 287 return 0; 288} 289 290static snd_pcm_uframes_t acp5x_dma_pointer(struct snd_soc_component *component, 291 struct snd_pcm_substream *substream) 292{ 293 struct i2s_stream_instance *rtd; 294 u32 pos; 295 u32 buffersize; 296 u64 bytescount; 297 298 rtd = substream->runtime->private_data; 299 buffersize = frames_to_bytes(substream->runtime, 300 substream->runtime->buffer_size); 301 bytescount = acp_get_byte_count(rtd, substream->stream); 302 if (bytescount > rtd->bytescount) 303 bytescount -= rtd->bytescount; 304 pos = do_div(bytescount, buffersize); 305 return bytes_to_frames(substream->runtime, pos); 306} 307 308static int acp5x_dma_new(struct snd_soc_component *component, 309 struct snd_soc_pcm_runtime *rtd) 310{ 311 struct device *parent = component->dev->parent; 312 313 snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, 314 parent, MIN_BUFFER, MAX_BUFFER); 315 return 0; 316} 317 318static int acp5x_dma_close(struct snd_soc_component *component, 319 struct snd_pcm_substream *substream) 320{ 321 struct snd_soc_pcm_runtime *prtd; 322 struct i2s_dev_data *adata; 323 struct i2s_stream_instance *ins; 324 325 prtd = asoc_substream_to_rtd(substream); 326 component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); 327 adata = dev_get_drvdata(component->dev); 328 ins = substream->runtime->private_data; 329 if (!ins) 330 return -EINVAL; 331 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 332 switch (ins->i2s_instance) { 333 case I2S_HS_INSTANCE: 334 adata->play_stream = NULL; 335 break; 336 case I2S_SP_INSTANCE: 337 default: 338 adata->i2ssp_play_stream = NULL; 339 } 340 } else { 341 switch (ins->i2s_instance) { 342 case I2S_HS_INSTANCE: 343 adata->capture_stream = NULL; 344 break; 345 case I2S_SP_INSTANCE: 346 default: 347 adata->i2ssp_capture_stream = NULL; 348 } 349 } 350 kfree(ins); 351 return 0; 352} 353 354static const struct snd_soc_component_driver acp5x_i2s_component = { 355 .name = DRV_NAME, 356 .open = acp5x_dma_open, 357 .close = acp5x_dma_close, 358 .hw_params = acp5x_dma_hw_params, 359 .pointer = acp5x_dma_pointer, 360 .pcm_construct = acp5x_dma_new, 361}; 362 363static int acp5x_audio_probe(struct platform_device *pdev) 364{ 365 struct resource *res; 366 struct i2s_dev_data *adata; 367 unsigned int irqflags; 368 int status; 369 370 if (!pdev->dev.platform_data) { 371 dev_err(&pdev->dev, "platform_data not retrieved\n"); 372 return -ENODEV; 373 } 374 irqflags = *((unsigned int *)(pdev->dev.platform_data)); 375 376 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 377 if (!res) { 378 dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); 379 return -ENODEV; 380 } 381 382 adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL); 383 if (!adata) 384 return -ENOMEM; 385 386 adata->acp5x_base = devm_ioremap(&pdev->dev, res->start, 387 resource_size(res)); 388 if (!adata->acp5x_base) 389 return -ENOMEM; 390 391 status = platform_get_irq(pdev, 0); 392 if (status < 0) 393 return status; 394 adata->i2s_irq = status; 395 396 dev_set_drvdata(&pdev->dev, adata); 397 status = devm_snd_soc_register_component(&pdev->dev, 398 &acp5x_i2s_component, 399 NULL, 0); 400 if (status) { 401 dev_err(&pdev->dev, "Fail to register acp i2s component\n"); 402 return status; 403 } 404 status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler, 405 irqflags, "ACP5x_I2S_IRQ", adata); 406 if (status) { 407 dev_err(&pdev->dev, "ACP5x I2S IRQ request failed\n"); 408 return status; 409 } 410 pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); 411 pm_runtime_use_autosuspend(&pdev->dev); 412 pm_runtime_enable(&pdev->dev); 413 pm_runtime_allow(&pdev->dev); 414 415 return 0; 416} 417 418static int acp5x_audio_remove(struct platform_device *pdev) 419{ 420 pm_runtime_disable(&pdev->dev); 421 return 0; 422} 423 424static int __maybe_unused acp5x_pcm_resume(struct device *dev) 425{ 426 struct i2s_dev_data *adata; 427 struct i2s_stream_instance *rtd; 428 u32 val; 429 430 adata = dev_get_drvdata(dev); 431 432 if (adata->play_stream && adata->play_stream->runtime) { 433 rtd = adata->play_stream->runtime->private_data; 434 config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK); 435 acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_ITER); 436 if (adata->tdm_mode == TDM_ENABLE) { 437 acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_TXFRMT); 438 val = acp_readl(adata->acp5x_base + ACP_HSTDM_ITER); 439 acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_ITER); 440 } 441 } 442 if (adata->i2ssp_play_stream && adata->i2ssp_play_stream->runtime) { 443 rtd = adata->i2ssp_play_stream->runtime->private_data; 444 config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK); 445 acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_ITER); 446 if (adata->tdm_mode == TDM_ENABLE) { 447 acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_TXFRMT); 448 val = acp_readl(adata->acp5x_base + ACP_I2STDM_ITER); 449 acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_ITER); 450 } 451 } 452 453 if (adata->capture_stream && adata->capture_stream->runtime) { 454 rtd = adata->capture_stream->runtime->private_data; 455 config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE); 456 acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_IRER); 457 if (adata->tdm_mode == TDM_ENABLE) { 458 acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_RXFRMT); 459 val = acp_readl(adata->acp5x_base + ACP_HSTDM_IRER); 460 acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_IRER); 461 } 462 } 463 if (adata->i2ssp_capture_stream && adata->i2ssp_capture_stream->runtime) { 464 rtd = adata->i2ssp_capture_stream->runtime->private_data; 465 config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE); 466 acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_IRER); 467 if (adata->tdm_mode == TDM_ENABLE) { 468 acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_RXFRMT); 469 val = acp_readl(adata->acp5x_base + ACP_I2STDM_IRER); 470 acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_IRER); 471 } 472 } 473 acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB); 474 return 0; 475} 476 477static int __maybe_unused acp5x_pcm_suspend(struct device *dev) 478{ 479 struct i2s_dev_data *adata; 480 481 adata = dev_get_drvdata(dev); 482 acp_writel(0, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB); 483 return 0; 484} 485 486static int __maybe_unused acp5x_pcm_runtime_resume(struct device *dev) 487{ 488 struct i2s_dev_data *adata; 489 490 adata = dev_get_drvdata(dev); 491 acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB); 492 return 0; 493} 494 495static const struct dev_pm_ops acp5x_pm_ops = { 496 SET_RUNTIME_PM_OPS(acp5x_pcm_suspend, 497 acp5x_pcm_runtime_resume, NULL) 498 SET_SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_resume) 499}; 500 501static struct platform_driver acp5x_dma_driver = { 502 .probe = acp5x_audio_probe, 503 .remove = acp5x_audio_remove, 504 .driver = { 505 .name = "acp5x_i2s_dma", 506 .pm = &acp5x_pm_ops, 507 }, 508}; 509 510module_platform_driver(acp5x_dma_driver); 511 512MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); 513MODULE_DESCRIPTION("AMD ACP 5.x PCM Driver"); 514MODULE_LICENSE("GPL v2"); 515MODULE_ALIAS("platform:" DRV_NAME);