527 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// AMD ALSA SoC PCM Driver4//5//Copyright 2016 Advanced Micro Devices, Inc.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_params.h>13#include <sound/soc.h>14#include <sound/soc-dai.h>15 16#include "acp3x.h"17 18#define DRV_NAME "acp3x_rv_i2s_dma"19 20static const struct snd_pcm_hardware acp3x_pcm_hardware_playback = {21 .info = SNDRV_PCM_INFO_INTERLEAVED |22 SNDRV_PCM_INFO_BLOCK_TRANSFER |23 SNDRV_PCM_INFO_BATCH |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 = 8,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 acp3x_pcm_hardware_capture = {41 .info = SNDRV_PCM_INFO_INTERLEAVED |42 SNDRV_PCM_INFO_BLOCK_TRANSFER |43 SNDRV_PCM_INFO_BATCH |44 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |45 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,46 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |47 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,48 .channels_min = 2,49 .channels_max = 2,50 .rates = SNDRV_PCM_RATE_8000_48000,51 .rate_min = 8000,52 .rate_max = 48000,53 .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,54 .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,55 .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,56 .periods_min = CAPTURE_MIN_NUM_PERIODS,57 .periods_max = CAPTURE_MAX_NUM_PERIODS,58};59 60static irqreturn_t i2s_irq_handler(int irq, void *dev_id)61{62 struct i2s_dev_data *rv_i2s_data;63 u16 play_flag, cap_flag;64 u32 val;65 66 rv_i2s_data = dev_id;67 if (!rv_i2s_data)68 return IRQ_NONE;69 70 play_flag = 0;71 cap_flag = 0;72 val = rv_readl(rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT);73 if ((val & BIT(BT_TX_THRESHOLD)) && rv_i2s_data->play_stream) {74 rv_writel(BIT(BT_TX_THRESHOLD), rv_i2s_data->acp3x_base +75 mmACP_EXTERNAL_INTR_STAT);76 snd_pcm_period_elapsed(rv_i2s_data->play_stream);77 play_flag = 1;78 }79 if ((val & BIT(I2S_TX_THRESHOLD)) &&80 rv_i2s_data->i2ssp_play_stream) {81 rv_writel(BIT(I2S_TX_THRESHOLD),82 rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT);83 snd_pcm_period_elapsed(rv_i2s_data->i2ssp_play_stream);84 play_flag = 1;85 }86 87 if ((val & BIT(BT_RX_THRESHOLD)) && rv_i2s_data->capture_stream) {88 rv_writel(BIT(BT_RX_THRESHOLD), rv_i2s_data->acp3x_base +89 mmACP_EXTERNAL_INTR_STAT);90 snd_pcm_period_elapsed(rv_i2s_data->capture_stream);91 cap_flag = 1;92 }93 if ((val & BIT(I2S_RX_THRESHOLD)) &&94 rv_i2s_data->i2ssp_capture_stream) {95 rv_writel(BIT(I2S_RX_THRESHOLD),96 rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT);97 snd_pcm_period_elapsed(rv_i2s_data->i2ssp_capture_stream);98 cap_flag = 1;99 }100 101 if (play_flag | cap_flag)102 return IRQ_HANDLED;103 else104 return IRQ_NONE;105}106 107static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction)108{109 u16 page_idx;110 u32 low, high, val, acp_fifo_addr, reg_fifo_addr;111 u32 reg_dma_size, reg_fifo_size;112 dma_addr_t addr;113 114 addr = rtd->dma_addr;115 116 if (direction == SNDRV_PCM_STREAM_PLAYBACK) {117 switch (rtd->i2s_instance) {118 case I2S_BT_INSTANCE:119 val = ACP_SRAM_BT_PB_PTE_OFFSET;120 break;121 case I2S_SP_INSTANCE:122 default:123 val = ACP_SRAM_SP_PB_PTE_OFFSET;124 }125 } else {126 switch (rtd->i2s_instance) {127 case I2S_BT_INSTANCE:128 val = ACP_SRAM_BT_CP_PTE_OFFSET;129 break;130 case I2S_SP_INSTANCE:131 default:132 val = ACP_SRAM_SP_CP_PTE_OFFSET;133 }134 }135 /* Group Enable */136 rv_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp3x_base +137 mmACPAXI2AXI_ATU_BASE_ADDR_GRP_1);138 rv_writel(PAGE_SIZE_4K_ENABLE, rtd->acp3x_base +139 mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);140 141 for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {142 /* Load the low address of page int ACP SRAM through SRBM */143 low = lower_32_bits(addr);144 high = upper_32_bits(addr);145 146 rv_writel(low, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val);147 high |= BIT(31);148 rv_writel(high, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val149 + 4);150 /* Move to next physically contiguous page */151 val += 8;152 addr += PAGE_SIZE;153 }154 155 if (direction == SNDRV_PCM_STREAM_PLAYBACK) {156 switch (rtd->i2s_instance) {157 case I2S_BT_INSTANCE:158 reg_dma_size = mmACP_BT_TX_DMA_SIZE;159 acp_fifo_addr = ACP_SRAM_PTE_OFFSET +160 BT_PB_FIFO_ADDR_OFFSET;161 reg_fifo_addr = mmACP_BT_TX_FIFOADDR;162 reg_fifo_size = mmACP_BT_TX_FIFOSIZE;163 rv_writel(I2S_BT_TX_MEM_WINDOW_START,164 rtd->acp3x_base + mmACP_BT_TX_RINGBUFADDR);165 break;166 167 case I2S_SP_INSTANCE:168 default:169 reg_dma_size = mmACP_I2S_TX_DMA_SIZE;170 acp_fifo_addr = ACP_SRAM_PTE_OFFSET +171 SP_PB_FIFO_ADDR_OFFSET;172 reg_fifo_addr = mmACP_I2S_TX_FIFOADDR;173 reg_fifo_size = mmACP_I2S_TX_FIFOSIZE;174 rv_writel(I2S_SP_TX_MEM_WINDOW_START,175 rtd->acp3x_base + mmACP_I2S_TX_RINGBUFADDR);176 }177 } else {178 switch (rtd->i2s_instance) {179 case I2S_BT_INSTANCE:180 reg_dma_size = mmACP_BT_RX_DMA_SIZE;181 acp_fifo_addr = ACP_SRAM_PTE_OFFSET +182 BT_CAPT_FIFO_ADDR_OFFSET;183 reg_fifo_addr = mmACP_BT_RX_FIFOADDR;184 reg_fifo_size = mmACP_BT_RX_FIFOSIZE;185 rv_writel(I2S_BT_RX_MEM_WINDOW_START,186 rtd->acp3x_base + mmACP_BT_RX_RINGBUFADDR);187 break;188 189 case I2S_SP_INSTANCE:190 default:191 reg_dma_size = mmACP_I2S_RX_DMA_SIZE;192 acp_fifo_addr = ACP_SRAM_PTE_OFFSET +193 SP_CAPT_FIFO_ADDR_OFFSET;194 reg_fifo_addr = mmACP_I2S_RX_FIFOADDR;195 reg_fifo_size = mmACP_I2S_RX_FIFOSIZE;196 rv_writel(I2S_SP_RX_MEM_WINDOW_START,197 rtd->acp3x_base + mmACP_I2S_RX_RINGBUFADDR);198 }199 }200 rv_writel(DMA_SIZE, rtd->acp3x_base + reg_dma_size);201 rv_writel(acp_fifo_addr, rtd->acp3x_base + reg_fifo_addr);202 rv_writel(FIFO_SIZE, rtd->acp3x_base + reg_fifo_size);203 rv_writel(BIT(I2S_RX_THRESHOLD) | BIT(BT_RX_THRESHOLD)204 | BIT(I2S_TX_THRESHOLD) | BIT(BT_TX_THRESHOLD),205 rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL);206}207 208static int acp3x_dma_open(struct snd_soc_component *component,209 struct snd_pcm_substream *substream)210{211 struct snd_pcm_runtime *runtime;212 struct snd_soc_pcm_runtime *prtd;213 struct i2s_dev_data *adata;214 struct i2s_stream_instance *i2s_data;215 int ret;216 217 runtime = substream->runtime;218 prtd = snd_soc_substream_to_rtd(substream);219 component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);220 adata = dev_get_drvdata(component->dev);221 i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL);222 if (!i2s_data)223 return -EINVAL;224 225 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)226 runtime->hw = acp3x_pcm_hardware_playback;227 else228 runtime->hw = acp3x_pcm_hardware_capture;229 230 ret = snd_pcm_hw_constraint_integer(runtime,231 SNDRV_PCM_HW_PARAM_PERIODS);232 if (ret < 0) {233 dev_err(component->dev, "set integer constraint failed\n");234 kfree(i2s_data);235 return ret;236 }237 238 i2s_data->acp3x_base = adata->acp3x_base;239 runtime->private_data = i2s_data;240 return ret;241}242 243 244static int acp3x_dma_hw_params(struct snd_soc_component *component,245 struct snd_pcm_substream *substream,246 struct snd_pcm_hw_params *params)247{248 struct i2s_stream_instance *rtd;249 struct snd_soc_pcm_runtime *prtd;250 struct snd_soc_card *card;251 struct acp3x_platform_info *pinfo;252 struct i2s_dev_data *adata;253 u64 size;254 255 prtd = snd_soc_substream_to_rtd(substream);256 card = prtd->card;257 pinfo = snd_soc_card_get_drvdata(card);258 adata = dev_get_drvdata(component->dev);259 rtd = substream->runtime->private_data;260 if (!rtd)261 return -EINVAL;262 263 if (pinfo) {264 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {265 rtd->i2s_instance = pinfo->play_i2s_instance;266 switch (rtd->i2s_instance) {267 case I2S_BT_INSTANCE:268 adata->play_stream = substream;269 break;270 case I2S_SP_INSTANCE:271 default:272 adata->i2ssp_play_stream = substream;273 }274 } else {275 rtd->i2s_instance = pinfo->cap_i2s_instance;276 switch (rtd->i2s_instance) {277 case I2S_BT_INSTANCE:278 adata->capture_stream = substream;279 break;280 case I2S_SP_INSTANCE:281 default:282 adata->i2ssp_capture_stream = substream;283 }284 }285 } else {286 pr_err("pinfo failed\n");287 }288 size = params_buffer_bytes(params);289 rtd->dma_addr = substream->runtime->dma_addr;290 rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);291 config_acp3x_dma(rtd, substream->stream);292 return 0;293}294 295static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_soc_component *component,296 struct snd_pcm_substream *substream)297{298 struct i2s_stream_instance *rtd;299 u32 pos;300 u32 buffersize;301 u64 bytescount;302 303 rtd = substream->runtime->private_data;304 305 buffersize = frames_to_bytes(substream->runtime,306 substream->runtime->buffer_size);307 bytescount = acp_get_byte_count(rtd, substream->stream);308 if (bytescount > rtd->bytescount)309 bytescount -= rtd->bytescount;310 pos = do_div(bytescount, buffersize);311 return bytes_to_frames(substream->runtime, pos);312}313 314static int acp3x_dma_new(struct snd_soc_component *component,315 struct snd_soc_pcm_runtime *rtd)316{317 struct device *parent = component->dev->parent;318 snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,319 parent, MIN_BUFFER, MAX_BUFFER);320 return 0;321}322 323static int acp3x_dma_close(struct snd_soc_component *component,324 struct snd_pcm_substream *substream)325{326 struct snd_soc_pcm_runtime *prtd;327 struct i2s_dev_data *adata;328 struct i2s_stream_instance *ins;329 330 prtd = snd_soc_substream_to_rtd(substream);331 component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);332 adata = dev_get_drvdata(component->dev);333 ins = substream->runtime->private_data;334 if (!ins)335 return -EINVAL;336 337 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {338 switch (ins->i2s_instance) {339 case I2S_BT_INSTANCE:340 adata->play_stream = NULL;341 break;342 case I2S_SP_INSTANCE:343 default:344 adata->i2ssp_play_stream = NULL;345 }346 } else {347 switch (ins->i2s_instance) {348 case I2S_BT_INSTANCE:349 adata->capture_stream = NULL;350 break;351 case I2S_SP_INSTANCE:352 default:353 adata->i2ssp_capture_stream = NULL;354 }355 }356 357 return 0;358}359 360static const struct snd_soc_component_driver acp3x_i2s_component = {361 .name = DRV_NAME,362 .open = acp3x_dma_open,363 .close = acp3x_dma_close,364 .hw_params = acp3x_dma_hw_params,365 .pointer = acp3x_dma_pointer,366 .pcm_construct = acp3x_dma_new,367};368 369static int acp3x_audio_probe(struct platform_device *pdev)370{371 struct resource *res;372 struct i2s_dev_data *adata;373 unsigned int irqflags;374 int status;375 376 if (!pdev->dev.platform_data) {377 dev_err(&pdev->dev, "platform_data not retrieved\n");378 return -ENODEV;379 }380 irqflags = *((unsigned int *)(pdev->dev.platform_data));381 382 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);383 if (!res) {384 dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");385 return -ENODEV;386 }387 388 adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);389 if (!adata)390 return -ENOMEM;391 392 adata->acp3x_base = devm_ioremap(&pdev->dev, res->start,393 resource_size(res));394 if (!adata->acp3x_base)395 return -ENOMEM;396 397 status = platform_get_irq(pdev, 0);398 if (status < 0)399 return status;400 adata->i2s_irq = status;401 402 dev_set_drvdata(&pdev->dev, adata);403 status = devm_snd_soc_register_component(&pdev->dev,404 &acp3x_i2s_component,405 NULL, 0);406 if (status) {407 dev_err(&pdev->dev, "Fail to register acp i2s component\n");408 return -ENODEV;409 }410 status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler,411 irqflags, "ACP3x_I2S_IRQ", adata);412 if (status) {413 dev_err(&pdev->dev, "ACP3x I2S IRQ request failed\n");414 return -ENODEV;415 }416 417 pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);418 pm_runtime_use_autosuspend(&pdev->dev);419 pm_runtime_mark_last_busy(&pdev->dev);420 pm_runtime_set_active(&pdev->dev);421 pm_runtime_enable(&pdev->dev);422 return 0;423}424 425static void acp3x_audio_remove(struct platform_device *pdev)426{427 pm_runtime_disable(&pdev->dev);428}429 430static int acp3x_resume(struct device *dev)431{432 struct i2s_dev_data *adata;433 u32 val, reg_val, frmt_val;434 435 reg_val = 0;436 frmt_val = 0;437 adata = dev_get_drvdata(dev);438 439 if (adata->play_stream && adata->play_stream->runtime) {440 struct i2s_stream_instance *rtd =441 adata->play_stream->runtime->private_data;442 config_acp3x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);443 switch (rtd->i2s_instance) {444 case I2S_BT_INSTANCE:445 reg_val = mmACP_BTTDM_ITER;446 frmt_val = mmACP_BTTDM_TXFRMT;447 break;448 case I2S_SP_INSTANCE:449 default:450 reg_val = mmACP_I2STDM_ITER;451 frmt_val = mmACP_I2STDM_TXFRMT;452 }453 rv_writel((rtd->xfer_resolution << 3),454 rtd->acp3x_base + reg_val);455 }456 if (adata->capture_stream && adata->capture_stream->runtime) {457 struct i2s_stream_instance *rtd =458 adata->capture_stream->runtime->private_data;459 config_acp3x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);460 switch (rtd->i2s_instance) {461 case I2S_BT_INSTANCE:462 reg_val = mmACP_BTTDM_IRER;463 frmt_val = mmACP_BTTDM_RXFRMT;464 break;465 case I2S_SP_INSTANCE:466 default:467 reg_val = mmACP_I2STDM_IRER;468 frmt_val = mmACP_I2STDM_RXFRMT;469 }470 rv_writel((rtd->xfer_resolution << 3),471 rtd->acp3x_base + reg_val);472 }473 if (adata->tdm_mode == TDM_ENABLE) {474 rv_writel(adata->tdm_fmt, adata->acp3x_base + frmt_val);475 val = rv_readl(adata->acp3x_base + reg_val);476 rv_writel(val | 0x2, adata->acp3x_base + reg_val);477 }478 rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);479 return 0;480}481 482 483static int acp3x_pcm_runtime_suspend(struct device *dev)484{485 struct i2s_dev_data *adata;486 487 adata = dev_get_drvdata(dev);488 489 rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);490 491 return 0;492}493 494static int acp3x_pcm_runtime_resume(struct device *dev)495{496 struct i2s_dev_data *adata;497 498 adata = dev_get_drvdata(dev);499 500 rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);501 return 0;502}503 504static const struct dev_pm_ops acp3x_pm_ops = {505 .runtime_suspend = acp3x_pcm_runtime_suspend,506 .runtime_resume = acp3x_pcm_runtime_resume,507 .resume = acp3x_resume,508};509 510static struct platform_driver acp3x_dma_driver = {511 .probe = acp3x_audio_probe,512 .remove = acp3x_audio_remove,513 .driver = {514 .name = "acp3x_rv_i2s_dma",515 .pm = &acp3x_pm_ops,516 },517};518 519module_platform_driver(acp3x_dma_driver);520 521MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");522MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");523MODULE_AUTHOR("Vijendar.Mukunda@amd.com");524MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver");525MODULE_LICENSE("GPL v2");526MODULE_ALIAS("platform:"DRV_NAME);527