build/patch/kernel/meson64-dev/0336-ASoC-meson-aiu-add-i2s-fifo-support.patch
Igor Pečovnik cc7ab6a6b1
Move meson64 to mainline + patches (#1748)
* Attach Meson64 to mainline with a bunch of patches. Tested, but need further work.
* Enable DVFS on N2 which sometimes works, sometime doesn't, cleanup
* Enable beta targets for Meson64 kernel family
* Bump with version
2020-01-18 19:05:55 +01:00

580 lines
16 KiB
Diff

From 9a78881a4a3b0b079433c222655f9a51052854d1 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Tue, 22 Oct 2019 17:51:56 +0200
Subject: [PATCH 50/94] WIP: ASoC: meson: aiu: add i2s fifo support
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
sound/soc/meson/Kconfig | 11 ++
sound/soc/meson/Makefile | 4 +
sound/soc/meson/aiu-fifo.c | 268 +++++++++++++++++++++++++++++++++++++++++
sound/soc/meson/aiu-fifo.h | 60 +++++++++
sound/soc/meson/aiu-i2s-fifo.c | 170 ++++++++++++++++++++++++++
5 files changed, 513 insertions(+)
create mode 100644 sound/soc/meson/aiu-fifo.c
create mode 100644 sound/soc/meson/aiu-fifo.h
create mode 100644 sound/soc/meson/aiu-i2s-fifo.c
diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
index 5b15077..7633057 100644
--- a/sound/soc/meson/Kconfig
+++ b/sound/soc/meson/Kconfig
@@ -9,6 +9,17 @@ config SND_MESON_AIU_BUS
Select Y or M to add support for audio output interfaces
embedded in the Amlogic GX SoC families
+config SND_MESON_AIU_FIFO
+ tristate
+ imply SND_MESON_AIU_BUS
+ select MFD_SYSCON
+
+config SND_MESON_AIU_I2S_FIFO
+ tristate "Amlogic AIU I2S FIFO"
+ select SND_MESON_AIU_FIFO
+ help
+ Select Y or M to add support for i2s FIFO of the GXL family
+
config SND_MESON_AXG_FIFO
tristate
select REGMAP_MMIO
diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
index 6c89ab3..b3c23ec 100644
--- a/sound/soc/meson/Makefile
+++ b/sound/soc/meson/Makefile
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: (GPL-2.0 OR MIT)
snd-soc-meson-aiu-bus-objs := aiu-bus.o
+snd-soc-meson-aiu-fifo-objs := aiu-fifo.o
+snd-soc-meson-aiu-i2s-fifo-objs := aiu-i2s-fifo.o
snd-soc-meson-axg-fifo-objs := axg-fifo.o
snd-soc-meson-axg-frddr-objs := axg-frddr.o
snd-soc-meson-axg-toddr-objs := axg-toddr.o
@@ -18,6 +20,8 @@ snd-soc-meson-g12a-tohdmitx-objs := g12a-tohdmitx.o
snd-soc-meson-t9015-objs := t9015.o
obj-$(CONFIG_SND_MESON_AIU_BUS) += snd-soc-meson-aiu-bus.o
+obj-$(CONFIG_SND_MESON_AIU_FIFO) += snd-soc-meson-aiu-fifo.o
+obj-$(CONFIG_SND_MESON_AIU_I2S_FIFO) += snd-soc-meson-aiu-i2s-fifo.o
obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o
obj-$(CONFIG_SND_MESON_AXG_FRDDR) += snd-soc-meson-axg-frddr.o
obj-$(CONFIG_SND_MESON_AXG_TODDR) += snd-soc-meson-axg-toddr.o
diff --git a/sound/soc/meson/aiu-fifo.c b/sound/soc/meson/aiu-fifo.c
new file mode 100644
index 0000000..14eccd5
--- /dev/null
+++ b/sound/soc/meson/aiu-fifo.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2019 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "aiu-fifo.h"
+
+#define AIU_MEM_START 0x00
+#define AIU_MEM_RD 0x04
+#define AIU_MEM_END 0x08
+#define AIU_MEM_MASKS 0x0c
+#define AIU_MEM_MASK_CH_RD GENMASK(7, 0)
+#define AIU_MEM_MASK_CH_MEM GENMASK(15, 8)
+#define AIU_MEM_CONTROL 0x10
+#define AIU_MEM_CONTROL_INIT BIT(0)
+#define AIU_MEM_CONTROL_FILL_EN BIT(1)
+#define AIU_MEM_CONTROL_EMPTY_EN BIT(2)
+
+snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int addr;
+
+ snd_soc_component_read(component, fifo->hw->mem_offset + AIU_MEM_RD,
+ &addr);
+
+ return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr);
+}
+EXPORT_SYMBOL_GPL(aiu_fifo_pointer);
+
+static void aiu_fifo_enable(struct snd_soc_component *component,
+ bool enable)
+{
+ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component);
+ unsigned int en_mask = (AIU_MEM_CONTROL_FILL_EN |
+ AIU_MEM_CONTROL_EMPTY_EN);
+
+ snd_soc_component_update_bits(component,
+ fifo->hw->mem_offset + AIU_MEM_CONTROL,
+ en_mask, enable ? en_mask : 0);
+}
+
+int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ aiu_fifo_enable(component, true);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ aiu_fifo_enable(component, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aiu_fifo_trigger);
+
+
+int aiu_fifo_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_update_bits(component,
+ fifo->hw->mem_offset + AIU_MEM_CONTROL,
+ AIU_MEM_CONTROL_INIT,
+ AIU_MEM_CONTROL_INIT);
+ snd_soc_component_update_bits(component,
+ fifo->hw->mem_offset + AIU_MEM_CONTROL,
+ AIU_MEM_CONTROL_INIT, 0);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aiu_fifo_prepare);
+
+int aiu_fifo_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_component *component = dai->component;
+ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component);
+ dma_addr_t end;
+ int ret;
+
+ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ if (ret < 0)
+ return ret;
+
+ /* Setup the fifo boundaries */
+ end = runtime->dma_addr + runtime->dma_bytes - fifo->hw->fifo_block;
+ snd_soc_component_write(component, fifo->hw->mem_offset + AIU_MEM_START,
+ runtime->dma_addr);
+ snd_soc_component_write(component, fifo->hw->mem_offset + AIU_MEM_RD,
+ runtime->dma_addr);
+ snd_soc_component_write(component, fifo->hw->mem_offset + AIU_MEM_END,
+ end);
+
+ /* Setup the fifo to read all the memory - no skip */
+ snd_soc_component_update_bits(component,
+ fifo->hw->mem_offset + AIU_MEM_MASKS,
+ AIU_MEM_MASK_CH_RD | AIU_MEM_MASK_CH_MEM,
+ FIELD_PREP(AIU_MEM_MASK_CH_RD, 0xff) |
+ FIELD_PREP(AIU_MEM_MASK_CH_MEM, 0xff));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aiu_fifo_hw_params);
+
+int aiu_fifo_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+EXPORT_SYMBOL_GPL(aiu_fifo_hw_free);
+
+static irqreturn_t aiu_fifo_isr(int irq, void *dev_id)
+{
+ struct snd_pcm_substream *playback = dev_id;
+
+ snd_pcm_period_elapsed(playback);
+
+ return IRQ_HANDLED;
+}
+
+int aiu_fifo_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, fifo->hw->pcm);
+
+ /*
+ * Make sure the buffer and period size are multiple of the fifo burst
+ * size
+ */
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ fifo->hw->fifo_block);
+ if (ret)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ fifo->hw->fifo_block);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(fifo->pclk);
+ if (ret)
+ return ret;
+
+ ret = request_irq(fifo->irq, aiu_fifo_isr, 0, dev_name(dai->dev),
+ substream);
+ if (ret)
+ clk_disable_unprepare(fifo->pclk);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aiu_fifo_startup);
+
+void aiu_fifo_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component);
+
+ free_irq(fifo->irq, substream);
+ clk_disable_unprepare(fifo->pclk);
+}
+EXPORT_SYMBOL_GPL(aiu_fifo_shutdown);
+
+int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_soc_component *component = dai->component;
+ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component);
+ size_t size = fifo->hw->pcm->buffer_bytes_max;
+
+
+ snd_pcm_lib_preallocate_pages(
+ rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+ SNDRV_DMA_TYPE_DEV, card->dev, size, size);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aiu_fifo_pcm_new);
+
+int aiu_fifo_component_probe(struct snd_soc_component *component)
+{
+ struct device *dev = component->dev;
+ struct regmap *map;
+
+ map = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(map)) {
+ dev_err(dev, "Could not get regmap\n");
+ return PTR_ERR(map);
+ }
+
+ snd_soc_component_init_regmap(component, map);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aiu_fifo_component_probe);
+
+int aiu_fifo_probe(struct platform_device *pdev)
+{
+ const struct aiu_fifo_match_data *data;
+ struct device *dev = &pdev->dev;
+ struct aiu_fifo *fifo;
+
+ data = of_device_get_match_data(dev);
+ if (!data) {
+ dev_err(dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ fifo = devm_kzalloc(dev, sizeof(*fifo), GFP_KERNEL);
+ if (!fifo)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, fifo);
+ fifo->hw = data->hw;
+
+ fifo->pclk = devm_clk_get(dev, NULL);
+ if (IS_ERR(fifo->pclk)) {
+ if (PTR_ERR(fifo->pclk) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get pclk: %ld\n",
+ PTR_ERR(fifo->pclk));
+ return PTR_ERR(fifo->pclk);
+ }
+
+ fifo->irq = platform_get_irq(pdev, 0);
+ if (fifo->irq <= 0) {
+ dev_err(dev, "Can't get irq\n");
+ return fifo->irq;
+ }
+
+ return devm_snd_soc_register_component(dev, data->component_drv,
+ data->dai_drv, 1);
+}
+EXPORT_SYMBOL_GPL(aiu_fifo_probe);
+
+MODULE_DESCRIPTION("Amlogic AIU FIFO driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/aiu-fifo.h b/sound/soc/meson/aiu-fifo.h
new file mode 100644
index 0000000..3de5f2d
--- /dev/null
+++ b/sound/soc/meson/aiu-fifo.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2019 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef _MESON_AIU_FIFO_H
+#define _MESON_AIU_FIFO_H
+
+struct snd_pcm_hardware;
+struct snd_soc_component_driver;
+struct snd_soc_dai_driver;
+struct clk;
+struct snd_pcm_ops;
+struct snd_pcm_substream;
+struct snd_soc_dai;
+struct snd_pcm_hw_params;
+struct platform_device;
+
+struct aiu_fifo_hw {
+ struct snd_pcm_hardware *pcm;
+ unsigned int mem_offset;
+ unsigned int fifo_block;
+};
+
+struct aiu_fifo_match_data {
+ const struct snd_soc_component_driver *component_drv;
+ const struct aiu_fifo_hw *hw;
+ struct snd_soc_dai_driver *dai_drv;
+};
+
+struct aiu_fifo {
+ const struct aiu_fifo_hw *hw;
+ struct clk *pclk;
+ int irq;
+};
+
+snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+
+int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai);
+int aiu_fifo_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int aiu_fifo_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+int aiu_fifo_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int aiu_fifo_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+void aiu_fifo_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai);
+
+int aiu_fifo_component_probe(struct snd_soc_component *component);
+int aiu_fifo_probe(struct platform_device *pdev);
+
+#endif /* _MESON_AIU_FIFO_H */
diff --git a/sound/soc/meson/aiu-i2s-fifo.c b/sound/soc/meson/aiu-i2s-fifo.c
new file mode 100644
index 0000000..efb6bbd
--- /dev/null
+++ b/sound/soc/meson/aiu-i2s-fifo.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2019 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "aiu-fifo.h"
+
+#define AIU_MEM_I2S_START 0x180
+#define AIU_MEM_I2S_MASKS 0x18c
+#define AIU_MEM_I2S_MASKS_IRQ_BLOCK GENMASK(31, 16)
+#define AIU_MEM_I2S_CONTROL 0x190
+#define AIU_MEM_I2S_CONTROL_MODE_16BIT BIT(6)
+#define AIU_MEM_I2S_BUF_CNTL 0x1d8
+#define AIU_MEM_I2S_BUF_CNTL_INIT BIT(0)
+
+#define AIU_I2S_FIFO_BLOCK 256
+#define AIU_I2S_FIFO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static int i2s_fifo_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ int ret;
+
+ ret = aiu_fifo_prepare(substream, dai);
+ if (ret)
+ return ret;
+
+ snd_soc_component_update_bits(component,
+ AIU_MEM_I2S_BUF_CNTL,
+ AIU_MEM_I2S_BUF_CNTL_INIT,
+ AIU_MEM_I2S_BUF_CNTL_INIT);
+ snd_soc_component_update_bits(component,
+ AIU_MEM_I2S_BUF_CNTL,
+ AIU_MEM_I2S_BUF_CNTL_INIT, 0);
+
+ return 0;
+}
+
+static int i2s_fifo_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct aiu_fifo *fifo = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int ret;
+
+ ret = aiu_fifo_hw_params(substream, params, dai);
+ if (ret)
+ return ret;
+
+ switch (params_physical_width(params)) {
+ case 16:
+ val = AIU_MEM_I2S_CONTROL_MODE_16BIT;
+ break;
+ case 32:
+ val = 0;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported physical width %u\n",
+ params_physical_width(params));
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, AIU_MEM_I2S_CONTROL,
+ AIU_MEM_I2S_CONTROL_MODE_16BIT,
+ val);
+
+ /* Setup the irq periodicity */
+ val = params_period_bytes(params) / fifo->hw->fifo_block;
+ val = FIELD_PREP(AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);
+ snd_soc_component_update_bits(component, AIU_MEM_I2S_MASKS,
+ AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops i2s_fifo_dai_ops = {
+ .trigger = aiu_fifo_trigger,
+ .prepare = i2s_fifo_prepare,
+ .hw_params = i2s_fifo_hw_params,
+ .hw_free = aiu_fifo_hw_free,
+ .startup = aiu_fifo_startup,
+ .shutdown = aiu_fifo_shutdown,
+};
+
+static struct snd_soc_dai_driver i2s_fifo_dai_drv = {
+ .name = "AIU I2S FIFO",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .formats = AIU_I2S_FIFO_FORMATS,
+ },
+ .ops = &i2s_fifo_dai_ops,
+ .pcm_new = aiu_fifo_pcm_new,
+};
+
+static const struct snd_soc_component_driver i2s_fifo_component_drv = {
+ .probe = aiu_fifo_component_probe,
+ .pointer = aiu_fifo_pointer,
+ .ioctl = snd_soc_pcm_lib_ioctl,
+};
+
+static struct snd_pcm_hardware i2s_fifo_pcm = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = AIU_I2S_FIFO_FORMATS,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .channels_min = 2,
+ .channels_max = 8,
+ .period_bytes_min = AIU_I2S_FIFO_BLOCK,
+ .period_bytes_max = AIU_I2S_FIFO_BLOCK * USHRT_MAX,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+
+ /* No real justification for this */
+ .buffer_bytes_max = 1 * 1024 * 1024,
+};
+
+static const struct aiu_fifo_hw i2s_fifo_hw = {
+ .fifo_block = AIU_I2S_FIFO_BLOCK,
+ .mem_offset = AIU_MEM_I2S_START,
+ .pcm = &i2s_fifo_pcm,
+};
+
+static const struct aiu_fifo_match_data i2s_fifo_data = {
+ .component_drv = &i2s_fifo_component_drv,
+ .dai_drv = &i2s_fifo_dai_drv,
+ .hw = &i2s_fifo_hw,
+};
+
+static const struct of_device_id aiu_i2s_fifo_of_match[] = {
+ {
+ .compatible = "amlogic,aiu-i2s-fifo",
+ .data = &i2s_fifo_data,
+ }, {}
+};
+MODULE_DEVICE_TABLE(of, aiu_i2s_fifo_of_match);
+
+static struct platform_driver aiu_i2s_fifo_pdrv = {
+ .probe = aiu_fifo_probe,
+ .driver = {
+ .name = "meson-aiu-i2s-fifo",
+ .of_match_table = aiu_i2s_fifo_of_match,
+ },
+};
+module_platform_driver(aiu_i2s_fifo_pdrv);
+
+MODULE_DESCRIPTION("Amlogic AIU I2S FIFO driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
--
2.7.1