162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2021 Advanced Micro Devices, Inc. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * Generic interface for ACP audio blck PCM component 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/err.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <sound/pcm_params.h> 1962306a36Sopenharmony_ci#include <sound/soc.h> 2062306a36Sopenharmony_ci#include <sound/soc-dai.h> 2162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "amd.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define DRV_NAME "acp_i2s_dma" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic const struct snd_pcm_hardware acp_pcm_hardware_playback = { 2862306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_INTERLEAVED | 2962306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 3062306a36Sopenharmony_ci SNDRV_PCM_INFO_BATCH | 3162306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 3262306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 3362306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 3462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | 3562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 3662306a36Sopenharmony_ci .channels_min = 2, 3762306a36Sopenharmony_ci .channels_max = 8, 3862306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_96000, 3962306a36Sopenharmony_ci .rate_min = 8000, 4062306a36Sopenharmony_ci .rate_max = 96000, 4162306a36Sopenharmony_ci .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE, 4262306a36Sopenharmony_ci .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, 4362306a36Sopenharmony_ci .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, 4462306a36Sopenharmony_ci .periods_min = PLAYBACK_MIN_NUM_PERIODS, 4562306a36Sopenharmony_ci .periods_max = PLAYBACK_MAX_NUM_PERIODS, 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic const struct snd_pcm_hardware acp_pcm_hardware_capture = { 4962306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_INTERLEAVED | 5062306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 5162306a36Sopenharmony_ci SNDRV_PCM_INFO_BATCH | 5262306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 5362306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, 5462306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 5562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | 5662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE, 5762306a36Sopenharmony_ci .channels_min = 2, 5862306a36Sopenharmony_ci .channels_max = 2, 5962306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 6062306a36Sopenharmony_ci .rate_min = 8000, 6162306a36Sopenharmony_ci .rate_max = 48000, 6262306a36Sopenharmony_ci .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE, 6362306a36Sopenharmony_ci .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, 6462306a36Sopenharmony_ci .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, 6562306a36Sopenharmony_ci .periods_min = CAPTURE_MIN_NUM_PERIODS, 6662306a36Sopenharmony_ci .periods_max = CAPTURE_MAX_NUM_PERIODS, 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ciint acp_machine_select(struct acp_dev_data *adata) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct snd_soc_acpi_mach *mach; 7262306a36Sopenharmony_ci int size; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci size = sizeof(*adata->machines); 7562306a36Sopenharmony_ci mach = snd_soc_acpi_find_machine(adata->machines); 7662306a36Sopenharmony_ci if (!mach) { 7762306a36Sopenharmony_ci dev_err(adata->dev, "warning: No matching ASoC machine driver found\n"); 7862306a36Sopenharmony_ci return -EINVAL; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci adata->mach_dev = platform_device_register_data(adata->dev, mach->drv_name, 8262306a36Sopenharmony_ci PLATFORM_DEVID_NONE, mach, size); 8362306a36Sopenharmony_ci if (IS_ERR(adata->mach_dev)) 8462306a36Sopenharmony_ci dev_warn(adata->dev, "Unable to register Machine device\n"); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(acp_machine_select, SND_SOC_ACP_COMMON); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic irqreturn_t i2s_irq_handler(int irq, void *data) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct acp_dev_data *adata = data; 9362306a36Sopenharmony_ci struct acp_resource *rsrc = adata->rsrc; 9462306a36Sopenharmony_ci struct acp_stream *stream; 9562306a36Sopenharmony_ci u16 i2s_flag = 0; 9662306a36Sopenharmony_ci u32 ext_intr_stat, ext_intr_stat1; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (!adata) 9962306a36Sopenharmony_ci return IRQ_NONE; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (adata->rsrc->no_of_ctrls == 2) 10262306a36Sopenharmony_ci ext_intr_stat1 = readl(ACP_EXTERNAL_INTR_STAT(adata, (rsrc->irqp_used - 1))); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci ext_intr_stat = readl(ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used)); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci spin_lock(&adata->acp_lock); 10762306a36Sopenharmony_ci list_for_each_entry(stream, &adata->stream_list, list) { 10862306a36Sopenharmony_ci if (ext_intr_stat & stream->irq_bit) { 10962306a36Sopenharmony_ci writel(stream->irq_bit, 11062306a36Sopenharmony_ci ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used)); 11162306a36Sopenharmony_ci snd_pcm_period_elapsed(stream->substream); 11262306a36Sopenharmony_ci i2s_flag = 1; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci if (adata->rsrc->no_of_ctrls == 2) { 11562306a36Sopenharmony_ci if (ext_intr_stat1 & stream->irq_bit) { 11662306a36Sopenharmony_ci writel(stream->irq_bit, ACP_EXTERNAL_INTR_STAT(adata, 11762306a36Sopenharmony_ci (rsrc->irqp_used - 1))); 11862306a36Sopenharmony_ci snd_pcm_period_elapsed(stream->substream); 11962306a36Sopenharmony_ci i2s_flag = 1; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci spin_unlock(&adata->acp_lock); 12462306a36Sopenharmony_ci if (i2s_flag) 12562306a36Sopenharmony_ci return IRQ_HANDLED; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return IRQ_NONE; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_civoid config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct acp_resource *rsrc = adata->rsrc; 13362306a36Sopenharmony_ci u32 pte_reg, pte_size, reg_val; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Use ATU base Group5 */ 13662306a36Sopenharmony_ci pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5; 13762306a36Sopenharmony_ci pte_size = ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5; 13862306a36Sopenharmony_ci stream->reg_offset = 0x02000000; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* Group Enable */ 14162306a36Sopenharmony_ci reg_val = rsrc->sram_pte_offset; 14262306a36Sopenharmony_ci writel(reg_val | BIT(31), adata->acp_base + pte_reg); 14362306a36Sopenharmony_ci writel(PAGE_SIZE_4K_ENABLE, adata->acp_base + pte_size); 14462306a36Sopenharmony_ci writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(config_pte_for_stream, SND_SOC_ACP_COMMON); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_civoid config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct snd_pcm_substream *substream = stream->substream; 15162306a36Sopenharmony_ci struct acp_resource *rsrc = adata->rsrc; 15262306a36Sopenharmony_ci dma_addr_t addr = substream->dma_buffer.addr; 15362306a36Sopenharmony_ci int num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); 15462306a36Sopenharmony_ci u32 low, high, val; 15562306a36Sopenharmony_ci u16 page_idx; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci val = stream->pte_offset; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci for (page_idx = 0; page_idx < num_pages; page_idx++) { 16062306a36Sopenharmony_ci /* Load the low address of page int ACP SRAM through SRBM */ 16162306a36Sopenharmony_ci low = lower_32_bits(addr); 16262306a36Sopenharmony_ci high = upper_32_bits(addr); 16362306a36Sopenharmony_ci writel(low, adata->acp_base + rsrc->scratch_reg_offset + val); 16462306a36Sopenharmony_ci high |= BIT(31); 16562306a36Sopenharmony_ci writel(high, adata->acp_base + rsrc->scratch_reg_offset + val + 4); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* Move to next physically contiguous page */ 16862306a36Sopenharmony_ci val += 8; 16962306a36Sopenharmony_ci addr += PAGE_SIZE; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(config_acp_dma, SND_SOC_ACP_COMMON); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 17762306a36Sopenharmony_ci struct device *dev = component->dev; 17862306a36Sopenharmony_ci struct acp_dev_data *adata = dev_get_drvdata(dev); 17962306a36Sopenharmony_ci struct acp_stream *stream; 18062306a36Sopenharmony_ci int ret; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci stream = kzalloc(sizeof(*stream), GFP_KERNEL); 18362306a36Sopenharmony_ci if (!stream) 18462306a36Sopenharmony_ci return -ENOMEM; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci stream->substream = substream; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 18962306a36Sopenharmony_ci runtime->hw = acp_pcm_hardware_playback; 19062306a36Sopenharmony_ci else 19162306a36Sopenharmony_ci runtime->hw = acp_pcm_hardware_capture; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 19462306a36Sopenharmony_ci if (ret < 0) { 19562306a36Sopenharmony_ci dev_err(component->dev, "set integer constraint failed\n"); 19662306a36Sopenharmony_ci kfree(stream); 19762306a36Sopenharmony_ci return ret; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci runtime->private_data = stream; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci writel(1, ACP_EXTERNAL_INTR_ENB(adata)); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci spin_lock_irq(&adata->acp_lock); 20462306a36Sopenharmony_ci list_add_tail(&stream->list, &adata->stream_list); 20562306a36Sopenharmony_ci spin_unlock_irq(&adata->acp_lock); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return ret; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int acp_dma_hw_params(struct snd_soc_component *component, 21162306a36Sopenharmony_ci struct snd_pcm_substream *substream, 21262306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct acp_dev_data *adata = snd_soc_component_get_drvdata(component); 21562306a36Sopenharmony_ci struct acp_stream *stream = substream->runtime->private_data; 21662306a36Sopenharmony_ci u64 size = params_buffer_bytes(params); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* Configure ACP DMA block with params */ 21962306a36Sopenharmony_ci config_pte_for_stream(adata, stream); 22062306a36Sopenharmony_ci config_acp_dma(adata, stream, size); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component, 22662306a36Sopenharmony_ci struct snd_pcm_substream *substream) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct device *dev = component->dev; 22962306a36Sopenharmony_ci struct acp_dev_data *adata = dev_get_drvdata(dev); 23062306a36Sopenharmony_ci struct acp_stream *stream = substream->runtime->private_data; 23162306a36Sopenharmony_ci u32 pos, buffersize; 23262306a36Sopenharmony_ci u64 bytescount; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci buffersize = frames_to_bytes(substream->runtime, 23562306a36Sopenharmony_ci substream->runtime->buffer_size); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci bytescount = acp_get_byte_count(adata, stream->dai_id, substream->stream); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (bytescount > stream->bytescount) 24062306a36Sopenharmony_ci bytescount -= stream->bytescount; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci pos = do_div(bytescount, buffersize); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, pos); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int acp_dma_new(struct snd_soc_component *component, 24862306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct device *parent = component->dev->parent; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, 25362306a36Sopenharmony_ci parent, MIN_BUFFER, MAX_BUFFER); 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int acp_dma_close(struct snd_soc_component *component, 25862306a36Sopenharmony_ci struct snd_pcm_substream *substream) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct device *dev = component->dev; 26162306a36Sopenharmony_ci struct acp_dev_data *adata = dev_get_drvdata(dev); 26262306a36Sopenharmony_ci struct acp_stream *stream = substream->runtime->private_data; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Remove entry from list */ 26562306a36Sopenharmony_ci spin_lock_irq(&adata->acp_lock); 26662306a36Sopenharmony_ci list_del(&stream->list); 26762306a36Sopenharmony_ci spin_unlock_irq(&adata->acp_lock); 26862306a36Sopenharmony_ci kfree(stream); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic const struct snd_soc_component_driver acp_pcm_component = { 27462306a36Sopenharmony_ci .name = DRV_NAME, 27562306a36Sopenharmony_ci .open = acp_dma_open, 27662306a36Sopenharmony_ci .close = acp_dma_close, 27762306a36Sopenharmony_ci .hw_params = acp_dma_hw_params, 27862306a36Sopenharmony_ci .pointer = acp_dma_pointer, 27962306a36Sopenharmony_ci .pcm_construct = acp_dma_new, 28062306a36Sopenharmony_ci .legacy_dai_naming = 1, 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ciint acp_platform_register(struct device *dev) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct acp_dev_data *adata = dev_get_drvdata(dev); 28662306a36Sopenharmony_ci struct snd_soc_dai_driver; 28762306a36Sopenharmony_ci unsigned int status; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci status = devm_request_irq(dev, adata->i2s_irq, i2s_irq_handler, 29062306a36Sopenharmony_ci IRQF_SHARED, "ACP_I2S_IRQ", adata); 29162306a36Sopenharmony_ci if (status) { 29262306a36Sopenharmony_ci dev_err(dev, "ACP I2S IRQ request failed\n"); 29362306a36Sopenharmony_ci return status; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci status = devm_snd_soc_register_component(dev, &acp_pcm_component, 29762306a36Sopenharmony_ci adata->dai_driver, 29862306a36Sopenharmony_ci adata->num_dai); 29962306a36Sopenharmony_ci if (status) { 30062306a36Sopenharmony_ci dev_err(dev, "Fail to register acp i2s component\n"); 30162306a36Sopenharmony_ci return status; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci INIT_LIST_HEAD(&adata->stream_list); 30562306a36Sopenharmony_ci spin_lock_init(&adata->acp_lock); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(acp_platform_register, SND_SOC_ACP_COMMON); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciint acp_platform_unregister(struct device *dev) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct acp_dev_data *adata = dev_get_drvdata(dev); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (adata->mach_dev) 31662306a36Sopenharmony_ci platform_device_unregister(adata->mach_dev); 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(acp_platform_unregister, SND_SOC_ACP_COMMON); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ciMODULE_DESCRIPTION("AMD ACP PCM Driver"); 32262306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 32362306a36Sopenharmony_ciMODULE_ALIAS(DRV_NAME); 324