diff options
Diffstat (limited to 'meta-v1000/recipes-kernel/linux/linux-yocto-4.14.71/1104-ASoC-AMD-add-ACP3x-PCM-driver-DMA-ops.patch')
-rw-r--r-- | meta-v1000/recipes-kernel/linux/linux-yocto-4.14.71/1104-ASoC-AMD-add-ACP3x-PCM-driver-DMA-ops.patch | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/meta-v1000/recipes-kernel/linux/linux-yocto-4.14.71/1104-ASoC-AMD-add-ACP3x-PCM-driver-DMA-ops.patch b/meta-v1000/recipes-kernel/linux/linux-yocto-4.14.71/1104-ASoC-AMD-add-ACP3x-PCM-driver-DMA-ops.patch new file mode 100644 index 00000000..cd5bbdd4 --- /dev/null +++ b/meta-v1000/recipes-kernel/linux/linux-yocto-4.14.71/1104-ASoC-AMD-add-ACP3x-PCM-driver-DMA-ops.patch @@ -0,0 +1,337 @@ +From 2f325ed3e8ad6a5500438221823698361da72b5f Mon Sep 17 00:00:00 2001 +From: Maruthi Srinivas Bayyavarapu <Maruthi.Bayyavarapu@amd.com> +Date: Wed, 29 Mar 2017 18:59:54 +0530 +Subject: [PATCH 1104/4131] ASoC: AMD: add ACP3x PCM driver DMA ops + +ACP3x has a DMA controller to access system memory. This controller +transfers data from/to system memory to/from ACP internal fifo. +The patch adds PCM driver DMA operations. + +Signed-off-by: Maruthi Bayyavarapu <maruthi.bayyavarapu@amd.com> +Signed-off-by: Alex Deucher <alexander.deucher@amd.com> +--- + sound/soc/amd/raven/acp3x-pcm-dma.c | 269 ++++++++++++++++++++++++++++++++++-- + sound/soc/amd/raven/acp3x.h | 14 ++ + 2 files changed, 275 insertions(+), 8 deletions(-) + +diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c +index 0ce04f0..346ebcb 100644 +--- a/sound/soc/amd/raven/acp3x-pcm-dma.c ++++ b/sound/soc/amd/raven/acp3x-pcm-dma.c +@@ -31,6 +31,56 @@ struct i2s_dev_data { + struct snd_pcm_substream *capture_stream; + }; + ++struct i2s_stream_instance { ++ u16 num_pages; ++ u16 channels; ++ u32 xfer_resolution; ++ u32 fmt; ++ u32 val; ++ struct page *pg; ++ void __iomem *acp3x_base; ++}; ++ ++static const struct snd_pcm_hardware acp3x_pcm_hardware_playback = { ++ .info = SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_BATCH | ++ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 2, ++ .channels_max = 8, ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .rate_min = 8000, ++ .rate_max = 96000, ++ .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE, ++ .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, ++ .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, ++ .periods_min = PLAYBACK_MIN_NUM_PERIODS, ++ .periods_max = PLAYBACK_MAX_NUM_PERIODS, ++}; ++ ++static const struct snd_pcm_hardware acp3x_pcm_hardware_capture = { ++ .info = SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_BATCH | ++ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE, ++ .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, ++ .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, ++ .periods_min = CAPTURE_MIN_NUM_PERIODS, ++ .periods_max = CAPTURE_MAX_NUM_PERIODS, ++}; ++ + static int acp3x_power_on(void __iomem *acp3x_base, bool on) + { + u16 val, mask; +@@ -168,19 +218,222 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id) + return IRQ_NONE; + } + ++static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction) ++{ ++ u16 page_idx; ++ u64 addr; ++ u32 low, high, val, acp_fifo_addr; ++ struct page *pg = rtd->pg; ++ ++ /* 8 scratch registers used to map one 64 bit address. ++ * For 2 pages (4096 * 2 bytes), it will be 16 registers. ++ */ ++ if (direction == SNDRV_PCM_STREAM_PLAYBACK) ++ val = 0; ++ else ++ val = 16; ++ ++ /* Group Enable */ ++ rv_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp3x_base + ++ mmACPAXI2AXI_ATU_BASE_ADDR_GRP_1); ++ rv_writel(PAGE_SIZE_4K_ENABLE, rtd->acp3x_base + ++ mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_1); ++ ++ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) { ++ /* Load the low address of page int ACP SRAM through SRBM */ ++ addr = page_to_phys(pg); ++ low = lower_32_bits(addr); ++ high = upper_32_bits(addr); ++ ++ rv_writel(low, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val); ++ high |= BIT(31); ++ rv_writel(high, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val ++ + 4); ++ /* Move to next physically contiguos page */ ++ val += 8; ++ pg++; ++ } ++ ++ if (direction == SNDRV_PCM_STREAM_PLAYBACK) { ++ /* Config ringbuffer */ ++ rv_writel(MEM_WINDOW_START, rtd->acp3x_base + ++ mmACP_BT_TX_RINGBUFADDR); ++ rv_writel(MAX_BUFFER, rtd->acp3x_base + ++ mmACP_BT_TX_RINGBUFSIZE); ++ rv_writel(0x40, rtd->acp3x_base + mmACP_BT_TX_DMA_SIZE); ++ ++ /* Config audio fifo */ ++ acp_fifo_addr = ACP_SRAM_PTE_OFFSET + (rtd->num_pages * 8) ++ + 1024; ++ rv_writel(acp_fifo_addr, rtd->acp3x_base + ++ mmACP_BT_TX_FIFOADDR); ++ rv_writel(256, rtd->acp3x_base + mmACP_BT_TX_FIFOSIZE); ++ rv_writel(PLAYBACK_MIN_PERIOD_SIZE, rtd->acp3x_base + ++ mmACP_BT_TX_INTR_WATERMARK_SIZE); ++ } else { ++ /* Config ringbuffer */ ++ rv_writel(MEM_WINDOW_START + MAX_BUFFER, rtd->acp3x_base + ++ mmACP_BT_RX_RINGBUFADDR); ++ rv_writel(MAX_BUFFER, rtd->acp3x_base + ++ mmACP_BT_RX_RINGBUFSIZE); ++ rv_writel(0x40, rtd->acp3x_base + mmACP_BT_RX_DMA_SIZE); ++ ++ /* Config audio fifo */ ++ acp_fifo_addr = ACP_SRAM_PTE_OFFSET + ++ (rtd->num_pages * 8) + 1024 + 256; ++ rv_writel(acp_fifo_addr, rtd->acp3x_base + ++ mmACP_BT_RX_FIFOADDR); ++ rv_writel(256, rtd->acp3x_base + mmACP_BT_RX_FIFOSIZE); ++ rv_writel(CAPTURE_MIN_PERIOD_SIZE, rtd->acp3x_base + ++ mmACP_BT_RX_INTR_WATERMARK_SIZE); ++ } ++ ++ /* Enable watermark/period interrupt to host */ ++ rv_writel(BIT(BT_TX_THRESHOLD) | BIT(BT_RX_THRESHOLD), ++ rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL); ++} ++ ++static int acp3x_dma_open(struct snd_pcm_substream *substream) ++{ ++ int ret = 0; ++ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct snd_soc_pcm_runtime *prtd = substream->private_data; ++ struct i2s_dev_data *adata = dev_get_drvdata(prtd->platform->dev); ++ ++ struct i2s_stream_instance *i2s_data = kzalloc(sizeof( ++ struct i2s_stream_instance), GFP_KERNEL); ++ if (i2s_data == NULL) ++ return -EINVAL; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ runtime->hw = acp3x_pcm_hardware_playback; ++ else ++ runtime->hw = acp3x_pcm_hardware_capture; ++ ++ ret = snd_pcm_hw_constraint_integer(runtime, ++ SNDRV_PCM_HW_PARAM_PERIODS); ++ if (ret < 0) { ++ dev_err(prtd->platform->dev, "set integer constraint failed\n"); ++ return ret; ++ } ++ ++ if (!adata->play_stream && !adata->capture_stream) ++ rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ adata->play_stream = substream; ++ else ++ adata->capture_stream = substream; ++ ++ i2s_data->acp3x_base = adata->acp3x_base; ++ runtime->private_data = i2s_data; ++ return 0; ++} ++ ++static int acp3x_dma_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ int status; ++ uint64_t size; ++ struct snd_dma_buffer *dma_buffer; ++ struct page *pg; ++ struct i2s_stream_instance *rtd = substream->runtime->private_data; ++ ++ if (rtd == NULL) ++ return -EINVAL; ++ ++ dma_buffer = &substream->dma_buffer; ++ size = params_buffer_bytes(params); ++ status = snd_pcm_lib_malloc_pages(substream, size); ++ if (status < 0) ++ return status; ++ ++ memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); ++ pg = virt_to_page(substream->dma_buffer.area); ++ if (pg != NULL) { ++ rtd->pg = pg; ++ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); ++ config_acp3x_dma(rtd, substream->stream); ++ status = 0; ++ } else { ++ status = -ENOMEM; ++ } ++ return status; ++} ++ ++static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_pcm_substream *substream) ++{ ++ u32 pos = 0; ++ struct i2s_stream_instance *rtd = substream->runtime->private_data; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ pos = rv_readl(rtd->acp3x_base + ++ mmACP_BT_TX_LINKPOSITIONCNTR); ++ else ++ pos = rv_readl(rtd->acp3x_base + ++ mmACP_BT_RX_LINKPOSITIONCNTR); ++ ++ if (pos >= MAX_BUFFER) ++ pos = 0; ++ ++ return bytes_to_frames(substream->runtime, pos); ++} ++ ++static int acp3x_dma_new(struct snd_soc_pcm_runtime *rtd) ++{ ++ return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, ++ SNDRV_DMA_TYPE_DEV, ++ NULL, MIN_BUFFER, ++ MAX_BUFFER); ++} ++ ++static int acp3x_dma_hw_free(struct snd_pcm_substream *substream) ++{ ++ return snd_pcm_lib_free_pages(substream); ++} ++ ++static int acp3x_dma_mmap(struct snd_pcm_substream *substream, ++ struct vm_area_struct *vma) ++{ ++ return snd_pcm_lib_default_mmap(substream, vma); ++} ++ ++static int acp3x_dma_close(struct snd_pcm_substream *substream) ++{ ++ struct snd_soc_pcm_runtime *prtd = substream->private_data; ++ struct i2s_dev_data *adata = dev_get_drvdata(prtd->platform->dev); ++ struct i2s_stream_instance *rtd = substream->runtime->private_data; ++ ++ kfree(rtd); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ adata->play_stream = NULL; ++ else ++ adata->capture_stream = NULL; ++ ++ /* Disable ACP irq, when the current stream is being closed and ++ * another stream is also not active. ++ */ ++ if (!adata->play_stream && !adata->capture_stream) ++ rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); ++ ++ return 0; ++} ++ + static struct snd_pcm_ops acp3x_dma_ops = { +- .open = NULL, +- .close = NULL, +- .ioctl = NULL, +- .hw_params = NULL, +- .hw_free = NULL, +- .pointer = NULL, +- .mmap = NULL, ++ .open = acp3x_dma_open, ++ .close = acp3x_dma_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = acp3x_dma_hw_params, ++ .hw_free = acp3x_dma_hw_free, ++ .pointer = acp3x_dma_pointer, ++ .mmap = acp3x_dma_mmap, + }; + + static struct snd_soc_platform_driver acp3x_asoc_platform = { + .ops = &acp3x_dma_ops, +- .pcm_new = NULL, ++ .pcm_new = acp3x_dma_new, + }; + + struct snd_soc_dai_ops acp3x_dai_i2s_ops = { +diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h +index b6d4659..d35d252 100644 +--- a/sound/soc/amd/raven/acp3x.h ++++ b/sound/soc/amd/raven/acp3x.h +@@ -6,7 +6,21 @@ + #define ACP3x_REG_END 0x1250200 + #define BT_TX_THRESHOLD 26 + #define BT_RX_THRESHOLD 25 ++#define ACP_SRAM_PTE_OFFSET 0x02050000 ++#define PAGE_SIZE_4K_ENABLE 0x2 ++#define MEM_WINDOW_START 0x4000000 + ++#define PLAYBACK_MIN_NUM_PERIODS 2 ++#define PLAYBACK_MAX_NUM_PERIODS 2 ++#define PLAYBACK_MAX_PERIOD_SIZE 4096 ++#define PLAYBACK_MIN_PERIOD_SIZE 4096 ++#define CAPTURE_MIN_NUM_PERIODS 2 ++#define CAPTURE_MAX_NUM_PERIODS 2 ++#define CAPTURE_MAX_PERIOD_SIZE 4096 ++#define CAPTURE_MIN_PERIOD_SIZE 4096 ++ ++#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS) ++#define MIN_BUFFER MAX_BUFFER + + static inline u32 rv_readl(void __iomem *base_addr) + { +-- +2.7.4 + |