Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
0002 //
0003 // This file is provided under a dual BSD/GPLv2 license. When using or
0004 // redistributing this file, you may do so under either license.
0005 //
0006 // Copyright(c) 2021 Advanced Micro Devices, Inc.
0007 //
0008 // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
0009 
0010 /*
0011  * Generic interface for ACP audio blck PCM component
0012  */
0013 
0014 #include <linux/platform_device.h>
0015 #include <linux/module.h>
0016 #include <linux/err.h>
0017 #include <linux/io.h>
0018 #include <sound/pcm_params.h>
0019 #include <sound/soc.h>
0020 #include <sound/soc-dai.h>
0021 #include <linux/pm_runtime.h>
0022 #include <linux/dma-mapping.h>
0023 
0024 #include "amd.h"
0025 
0026 #define DRV_NAME "acp_i2s_dma"
0027 
0028 static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
0029     .info = SNDRV_PCM_INFO_INTERLEAVED |
0030         SNDRV_PCM_INFO_BLOCK_TRANSFER |
0031         SNDRV_PCM_INFO_BATCH |
0032         SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
0033         SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
0034     .formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
0035            SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
0036            SNDRV_PCM_FMTBIT_S32_LE,
0037     .channels_min = 2,
0038     .channels_max = 8,
0039     .rates = SNDRV_PCM_RATE_8000_96000,
0040     .rate_min = 8000,
0041     .rate_max = 96000,
0042     .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
0043     .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
0044     .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
0045     .periods_min = PLAYBACK_MIN_NUM_PERIODS,
0046     .periods_max = PLAYBACK_MAX_NUM_PERIODS,
0047 };
0048 
0049 static const struct snd_pcm_hardware acp_pcm_hardware_capture = {
0050     .info = SNDRV_PCM_INFO_INTERLEAVED |
0051         SNDRV_PCM_INFO_BLOCK_TRANSFER |
0052         SNDRV_PCM_INFO_BATCH |
0053         SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
0054         SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
0055     .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
0056            SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
0057            SNDRV_PCM_FMTBIT_S32_LE,
0058     .channels_min = 2,
0059     .channels_max = 2,
0060     .rates = SNDRV_PCM_RATE_8000_48000,
0061     .rate_min = 8000,
0062     .rate_max = 48000,
0063     .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
0064     .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
0065     .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
0066     .periods_min = CAPTURE_MIN_NUM_PERIODS,
0067     .periods_max = CAPTURE_MAX_NUM_PERIODS,
0068 };
0069 
0070 int acp_machine_select(struct acp_dev_data *adata)
0071 {
0072     struct snd_soc_acpi_mach *mach;
0073     int size;
0074 
0075     size = sizeof(*adata->machines);
0076     mach = snd_soc_acpi_find_machine(adata->machines);
0077     if (!mach) {
0078         dev_err(adata->dev, "warning: No matching ASoC machine driver found\n");
0079         return -EINVAL;
0080     }
0081 
0082     adata->mach_dev = platform_device_register_data(adata->dev, mach->drv_name,
0083                             PLATFORM_DEVID_NONE, mach, size);
0084     if (IS_ERR(adata->mach_dev))
0085         dev_warn(adata->dev, "Unable to register Machine device\n");
0086 
0087     return 0;
0088 }
0089 EXPORT_SYMBOL_NS_GPL(acp_machine_select, SND_SOC_ACP_COMMON);
0090 
0091 static irqreturn_t i2s_irq_handler(int irq, void *data)
0092 {
0093     struct acp_dev_data *adata = data;
0094     struct acp_resource *rsrc = adata->rsrc;
0095     struct acp_stream *stream;
0096     u16 i2s_flag = 0;
0097     u32 ext_intr_stat, ext_intr_stat1, i;
0098 
0099     if (!adata)
0100         return IRQ_NONE;
0101 
0102     if (adata->rsrc->no_of_ctrls == 2)
0103         ext_intr_stat1 = readl(ACP_EXTERNAL_INTR_STAT(adata, (rsrc->irqp_used - 1)));
0104 
0105     ext_intr_stat = readl(ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
0106 
0107     for (i = 0; i < ACP_MAX_STREAM; i++) {
0108         stream = adata->stream[i];
0109         if (stream && (ext_intr_stat & stream->irq_bit)) {
0110             writel(stream->irq_bit,
0111                    ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
0112             snd_pcm_period_elapsed(stream->substream);
0113             i2s_flag = 1;
0114             break;
0115         }
0116         if (adata->rsrc->no_of_ctrls == 2) {
0117             if (stream && (ext_intr_stat1 & stream->irq_bit)) {
0118                 writel(stream->irq_bit, ACP_EXTERNAL_INTR_STAT(adata,
0119                        (rsrc->irqp_used - 1)));
0120                 snd_pcm_period_elapsed(stream->substream);
0121                 i2s_flag = 1;
0122                 break;
0123             }
0124         }
0125     }
0126     if (i2s_flag)
0127         return IRQ_HANDLED;
0128 
0129     return IRQ_NONE;
0130 }
0131 
0132 static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream)
0133 {
0134     struct acp_resource *rsrc = adata->rsrc;
0135     u32 pte_reg, pte_size, reg_val;
0136 
0137     /* Use ATU base Group5 */
0138     pte_reg = ACPAXI2AXI_ATU_BASE_ADDR_GRP_5;
0139     pte_size =  ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5;
0140     stream->reg_offset = 0x02000000;
0141 
0142     /* Group Enable */
0143     reg_val = rsrc->sram_pte_offset;
0144     writel(reg_val | BIT(31), adata->acp_base + pte_reg);
0145     writel(PAGE_SIZE_4K_ENABLE,  adata->acp_base + pte_size);
0146     writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL);
0147 }
0148 
0149 static void config_acp_dma(struct acp_dev_data *adata, int cpu_id, int size)
0150 {
0151     struct acp_stream *stream = adata->stream[cpu_id];
0152     struct snd_pcm_substream *substream = stream->substream;
0153     struct acp_resource *rsrc = adata->rsrc;
0154     dma_addr_t addr = substream->dma_buffer.addr;
0155     int num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
0156     u32 low, high, val;
0157     u16 page_idx;
0158 
0159     val = stream->pte_offset;
0160 
0161     for (page_idx = 0; page_idx < num_pages; page_idx++) {
0162         /* Load the low address of page int ACP SRAM through SRBM */
0163         low = lower_32_bits(addr);
0164         high = upper_32_bits(addr);
0165         writel(low, adata->acp_base + rsrc->scratch_reg_offset + val);
0166         high |= BIT(31);
0167         writel(high, adata->acp_base + rsrc->scratch_reg_offset + val + 4);
0168 
0169         /* Move to next physically contiguous page */
0170         val += 8;
0171         addr += PAGE_SIZE;
0172     }
0173 }
0174 
0175 static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream)
0176 {
0177     struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
0178     struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
0179     struct snd_pcm_runtime *runtime = substream->runtime;
0180     struct device *dev = component->dev;
0181     struct acp_dev_data *adata = dev_get_drvdata(dev);
0182     struct acp_stream *stream;
0183     int stream_id = cpu_dai->driver->id * 2 + substream->stream;
0184     int ret;
0185 
0186     stream = kzalloc(sizeof(*stream), GFP_KERNEL);
0187     if (!stream)
0188         return -ENOMEM;
0189 
0190     stream->substream = substream;
0191     adata->stream[stream_id] = stream;
0192 
0193     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
0194         runtime->hw = acp_pcm_hardware_playback;
0195     else
0196         runtime->hw = acp_pcm_hardware_capture;
0197 
0198     ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
0199     if (ret < 0) {
0200         dev_err(component->dev, "set integer constraint failed\n");
0201         kfree(stream);
0202         return ret;
0203     }
0204     runtime->private_data = stream;
0205 
0206     writel(1, ACP_EXTERNAL_INTR_ENB(adata));
0207 
0208     return ret;
0209 }
0210 
0211 static int acp_dma_hw_params(struct snd_soc_component *component,
0212                  struct snd_pcm_substream *substream,
0213                  struct snd_pcm_hw_params *params)
0214 {
0215     struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
0216     struct acp_dev_data *adata = snd_soc_component_get_drvdata(component);
0217     struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
0218     struct acp_stream *stream = substream->runtime->private_data;
0219     int stream_id = cpu_dai->driver->id * 2 + substream->stream;
0220     u64 size = params_buffer_bytes(params);
0221 
0222     /* Configure ACP DMA block with params */
0223     config_pte_for_stream(adata, stream);
0224     config_acp_dma(adata, stream_id, size);
0225 
0226     return 0;
0227 }
0228 
0229 static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
0230                      struct snd_pcm_substream *substream)
0231 {
0232     struct device *dev = component->dev;
0233     struct acp_dev_data *adata = dev_get_drvdata(dev);
0234     struct acp_stream *stream = substream->runtime->private_data;
0235     u32 pos, buffersize;
0236     u64 bytescount;
0237 
0238     buffersize = frames_to_bytes(substream->runtime,
0239                      substream->runtime->buffer_size);
0240 
0241     bytescount = acp_get_byte_count(adata, stream->dai_id, substream->stream);
0242 
0243     if (bytescount > stream->bytescount)
0244         bytescount -= stream->bytescount;
0245 
0246     pos = do_div(bytescount, buffersize);
0247 
0248     return bytes_to_frames(substream->runtime, pos);
0249 }
0250 
0251 static int acp_dma_new(struct snd_soc_component *component,
0252                struct snd_soc_pcm_runtime *rtd)
0253 {
0254     struct device *parent = component->dev->parent;
0255 
0256     snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
0257                        parent, MIN_BUFFER, MAX_BUFFER);
0258     return 0;
0259 }
0260 
0261 static int acp_dma_close(struct snd_soc_component *component,
0262              struct snd_pcm_substream *substream)
0263 {
0264     struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
0265     struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
0266     struct device *dev = component->dev;
0267     struct acp_dev_data *adata = dev_get_drvdata(dev);
0268     struct acp_stream *stream;
0269     int stream_id = cpu_dai->driver->id * 2 + substream->stream;
0270 
0271     stream = adata->stream[stream_id];
0272     kfree(stream);
0273     adata->stream[stream_id] = NULL;
0274 
0275     return 0;
0276 }
0277 
0278 static const struct snd_soc_component_driver acp_pcm_component = {
0279     .name           = DRV_NAME,
0280     .open           = acp_dma_open,
0281     .close          = acp_dma_close,
0282     .hw_params      = acp_dma_hw_params,
0283     .pointer        = acp_dma_pointer,
0284     .pcm_construct      = acp_dma_new,
0285     .legacy_dai_naming  = 1,
0286 };
0287 
0288 int acp_platform_register(struct device *dev)
0289 {
0290     struct acp_dev_data *adata = dev_get_drvdata(dev);
0291     struct snd_soc_dai_driver;
0292     unsigned int status;
0293 
0294     status = devm_request_irq(dev, adata->i2s_irq, i2s_irq_handler,
0295                   IRQF_SHARED, "ACP_I2S_IRQ", adata);
0296     if (status) {
0297         dev_err(dev, "ACP I2S IRQ request failed\n");
0298         return status;
0299     }
0300 
0301     status = devm_snd_soc_register_component(dev, &acp_pcm_component,
0302                          adata->dai_driver,
0303                          adata->num_dai);
0304     if (status) {
0305         dev_err(dev, "Fail to register acp i2s component\n");
0306         return status;
0307     }
0308     return 0;
0309 }
0310 EXPORT_SYMBOL_NS_GPL(acp_platform_register, SND_SOC_ACP_COMMON);
0311 
0312 int acp_platform_unregister(struct device *dev)
0313 {
0314     struct acp_dev_data *adata = dev_get_drvdata(dev);
0315 
0316     if (adata->mach_dev)
0317         platform_device_unregister(adata->mach_dev);
0318     return 0;
0319 }
0320 EXPORT_SYMBOL_NS_GPL(acp_platform_unregister, SND_SOC_ACP_COMMON);
0321 
0322 MODULE_DESCRIPTION("AMD ACP PCM Driver");
0323 MODULE_LICENSE("Dual BSD/GPL");
0324 MODULE_ALIAS(DRV_NAME);