aboutsummaryrefslogtreecommitdiffstats
path: root/meta-v1000/recipes-kernel/linux/linux-yocto-4.14.71/1104-ASoC-AMD-add-ACP3x-PCM-driver-DMA-ops.patch
diff options
context:
space:
mode:
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.patch337
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
+