diff options
author | He Yangxuan <yangxuan8282@gmail.com> | 2018-12-12 04:18:06 +0000 |
---|---|---|
committer | Natanael Copa <ncopa@alpinelinux.org> | 2018-12-20 14:35:09 +0000 |
commit | 468ce5d3db5bea1bfc4d0b21ce0460bbc508c0ff (patch) | |
tree | eed21d01ea548aec8278a37ccf3a67357c56ed13 /testing/linux-amlogic/0011-ASoC-meson-add-initial-spdif-dai-support.patch | |
parent | 27ddcaeab2051aaf617a81eeddfa34fb6557e64a (diff) | |
download | aports-468ce5d3db5bea1bfc4d0b21ce0460bbc508c0ff.tar.bz2 aports-468ce5d3db5bea1bfc4d0b21ce0460bbc508c0ff.tar.xz |
testing/linux-amlogic: upgrade to 4.19.8
Diffstat (limited to 'testing/linux-amlogic/0011-ASoC-meson-add-initial-spdif-dai-support.patch')
-rw-r--r-- | testing/linux-amlogic/0011-ASoC-meson-add-initial-spdif-dai-support.patch | 426 |
1 files changed, 0 insertions, 426 deletions
diff --git a/testing/linux-amlogic/0011-ASoC-meson-add-initial-spdif-dai-support.patch b/testing/linux-amlogic/0011-ASoC-meson-add-initial-spdif-dai-support.patch deleted file mode 100644 index 3d36f4b064..0000000000 --- a/testing/linux-amlogic/0011-ASoC-meson-add-initial-spdif-dai-support.patch +++ /dev/null @@ -1,426 +0,0 @@ -From e635299f76dc27b97a768f2a044d04c1917b9ad1 Mon Sep 17 00:00:00 2001 -From: Jerome Brunet <jbrunet@baylibre.com> -Date: Thu, 30 Mar 2017 13:46:03 +0200 -Subject: [PATCH] ASoC: meson: add initial spdif dai support - -Add support for the spdif dai found on Amlogic Meson SoC family. -With this initial implementation, only uncompressed pcm playback -from the spdif dma is supported. Future work will add compressed -support, pcm playback from i2s dma and capture. - -Signed-off-by: Jerome Brunet <jbrunet@baylibre.com> ---- - sound/soc/meson/Kconfig | 3 +- - sound/soc/meson/Makefile | 2 + - sound/soc/meson/spdif-dai.c | 374 ++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 378 insertions(+), 1 deletion(-) - create mode 100644 sound/soc/meson/spdif-dai.c - -diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig -index 3fb93b9..301d3a3 100644 ---- a/sound/soc/meson/Kconfig -+++ b/sound/soc/meson/Kconfig -@@ -18,6 +18,7 @@ config SND_SOC_MESON_I2S - config SND_SOC_MESON_SPDIF - tristate "Meson spdif interface" - depends on SND_SOC_MESON -+ select SND_PCM_IEC958 - help -- Say Y or M if you want to add support for spdif dma driver for Amlogic -+ Say Y or M if you want to add support for spdif driver for Amlogic - Meson SoCs. -diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile -index cef9a9d..bc4391c 100644 ---- a/sound/soc/meson/Makefile -+++ b/sound/soc/meson/Makefile -@@ -2,8 +2,10 @@ snd-soc-meson-audio-core-objs := audio-core.o - snd-soc-meson-aiu-i2s-dma-objs := aiu-i2s-dma.o - snd-soc-meson-aiu-spdif-dma-objs := aiu-spdif-dma.o - snd-soc-meson-i2s-dai-objs := i2s-dai.o -+snd-soc-meson-spdif-dai-objs := spdif-dai.o - - obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o - obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-aiu-i2s-dma.o - obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-i2s-dai.o - obj-$(CONFIG_SND_SOC_MESON_SPDIF) += snd-soc-meson-aiu-spdif-dma.o -+obj-$(CONFIG_SND_SOC_MESON_SPDIF) += snd-soc-meson-spdif-dai.o -diff --git a/sound/soc/meson/spdif-dai.c b/sound/soc/meson/spdif-dai.c -new file mode 100644 -index 0000000..e763000 ---- /dev/null -+++ b/sound/soc/meson/spdif-dai.c -@@ -0,0 +1,374 @@ -+/* -+ * Copyright (C) 2017 BayLibre, SAS -+ * Author: Jerome Brunet <jbrunet@baylibre.com> -+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation; either version 2 of the -+ * License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, see <http://www.gnu.org/licenses/>. -+ */ -+ -+#include <linux/clk.h> -+#include <linux/mfd/syscon.h> -+#include <linux/module.h> -+#include <linux/of.h> -+#include <linux/platform_device.h> -+#include <linux/regmap.h> -+ -+#include <sound/pcm_params.h> -+#include <sound/soc.h> -+#include <sound/soc-dai.h> -+#include <sound/pcm_iec958.h> -+ -+#include "aiu-regs.h" -+#include "audio-core.h" -+ -+#define DRV_NAME "meson-spdif-dai" -+ -+struct meson_spdif_dai { -+ struct meson_audio_core_data *core; -+ struct clk *iface; -+ struct clk *fast; -+ struct clk *mclk_i958; -+ struct clk *mclk; -+}; -+ -+#define AIU_CLK_CTRL_958_DIV_EN BIT(1) -+#define AIU_CLK_CTRL_958_DIV_MASK GENMASK(5, 4) -+#define AIU_CLK_CTRL_958_DIV_MORE BIT(12) -+#define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8) -+#define AIU_958_CTRL_HOLD_EN BIT(0) -+#define AIU_958_MISC_NON_PCM BIT(0) -+#define AIU_958_MISC_MODE_16BITS BIT(1) -+#define AIU_958_MISC_16BITS_ALIGN_MASK GENMASK(6, 5) -+#define AIU_958_MISC_16BITS_ALIGN(val) ((val) << 5) -+#define AIU_958_MISC_MODE_32BITS BIT(7) -+#define AIU_958_MISC_32BITS_SHIFT_MASK GENMASK(10, 8) -+#define AIU_958_MISC_32BITS_SHIFT(val) ((val) << 8) -+#define AIU_958_MISC_U_FROM_STREAM BIT(12) -+#define AIU_958_MISC_FORCE_LR BIT(13) -+ -+#define AIU_CS_WORD_LEN 4 -+ -+static void __hold(struct meson_spdif_dai *priv, bool enable) -+{ -+ regmap_update_bits(priv->core->aiu, AIU_958_CTRL, -+ AIU_958_CTRL_HOLD_EN, -+ enable ? AIU_958_CTRL_HOLD_EN : 0); -+} -+ -+static void __divider_enable(struct meson_spdif_dai *priv, bool enable) -+{ -+ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL, -+ AIU_CLK_CTRL_958_DIV_EN, -+ enable ? AIU_CLK_CTRL_958_DIV_EN : 0); -+} -+ -+static void __playback_start(struct meson_spdif_dai *priv) -+{ -+ __divider_enable(priv, true); -+ __hold(priv, false); -+} -+ -+static void __playback_stop(struct meson_spdif_dai *priv) -+{ -+ __hold(priv, true); -+ __divider_enable(priv, false); -+} -+ -+static int meson_spdif_dai_trigger(struct snd_pcm_substream *substream, int cmd, -+ struct snd_soc_dai *dai) -+{ -+ struct meson_spdif_dai *priv = snd_soc_dai_get_drvdata(dai); -+ -+ switch (cmd) { -+ case SNDRV_PCM_TRIGGER_START: -+ case SNDRV_PCM_TRIGGER_RESUME: -+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -+ __playback_start(priv); -+ return 0; -+ -+ case SNDRV_PCM_TRIGGER_STOP: -+ case SNDRV_PCM_TRIGGER_SUSPEND: -+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -+ __playback_stop(priv); -+ return 0; -+ -+ default: -+ return -EINVAL; -+ } -+} -+ -+static int __setup_spdif_clk(struct meson_spdif_dai *priv, unsigned int rate) -+{ -+ unsigned int mrate; -+ -+ /* Leave the internal divisor alone */ -+ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL, -+ AIU_CLK_CTRL_958_DIV_MASK | -+ AIU_CLK_CTRL_958_DIV_MORE, -+ 0); -+ -+ /* 2 * 32bits per subframe * 2 channels = 128 */ -+ mrate = rate * 128; -+ return clk_set_rate(priv->mclk, mrate); -+} -+ -+static int __setup_cs_word(struct meson_spdif_dai *priv, -+ struct snd_pcm_hw_params *params) -+{ -+ u8 cs[AIU_CS_WORD_LEN]; -+ u32 val; -+ int ret; -+ -+ ret = snd_pcm_create_iec958_consumer_hw_params(params, cs, -+ AIU_CS_WORD_LEN); -+ if (ret < 0) -+ return -EINVAL; -+ -+ /* Write the 1st half word */ -+ val = cs[1] | cs[0] << 8; -+ regmap_write(priv->core->aiu, AIU_958_CHSTAT_L0, val); -+ regmap_write(priv->core->aiu, AIU_958_CHSTAT_R0, val); -+ -+ /* Write the 2nd half word */ -+ val = cs[3] | cs[2] << 8; -+ regmap_write(priv->core->aiu, AIU_958_CHSTAT_L1, val); -+ regmap_write(priv->core->aiu, AIU_958_CHSTAT_R1, val); -+ -+ return 0; -+} -+ -+static int __setup_pcm_fmt(struct meson_spdif_dai *priv, -+ unsigned int width) -+{ -+ u32 val = 0; -+ -+ switch (width) { -+ case 16: -+ val |= AIU_958_MISC_MODE_16BITS; -+ val |= AIU_958_MISC_16BITS_ALIGN(2); -+ break; -+ case 32: -+ case 24: -+ /* -+ * Looks like this should only be set for 32bits mode, but the -+ * vendor kernel sets it like this for 24bits as well, let's -+ * try and see -+ */ -+ val |= AIU_958_MISC_MODE_32BITS; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ /* No idea what this actually does, copying the vendor kernel for now */ -+ val |= AIU_958_MISC_FORCE_LR; -+ val |= AIU_958_MISC_U_FROM_STREAM; -+ -+ regmap_update_bits(priv->core->aiu, AIU_958_MISC, -+ AIU_958_MISC_NON_PCM | -+ AIU_958_MISC_MODE_16BITS | -+ AIU_958_MISC_16BITS_ALIGN_MASK | -+ AIU_958_MISC_MODE_32BITS | -+ AIU_958_MISC_FORCE_LR, -+ val); -+ -+ return 0; -+} -+ -+static int meson_spdif_dai_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) -+{ -+ struct meson_spdif_dai *priv = snd_soc_dai_get_drvdata(dai); -+ int ret; -+ -+ ret = __setup_spdif_clk(priv, params_rate(params)); -+ if (ret) { -+ dev_err(dai->dev, "Unable to set the spdif clock\n"); -+ return ret; -+ } -+ -+ ret = __setup_cs_word(priv, params); -+ if (ret) { -+ dev_err(dai->dev, "Unable to set the channel status word\n"); -+ return ret; -+ } -+ -+ ret = __setup_pcm_fmt(priv, params_width(params)); -+ if (ret) { -+ dev_err(dai->dev, "Unable to set the pcm format\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int meson_spdif_dai_startup(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct meson_spdif_dai *priv = snd_soc_dai_get_drvdata(dai); -+ int ret; -+ -+ /* Power up the spdif fast domain - can't write the registers w/o it */ -+ ret = clk_prepare_enable(priv->fast); -+ if (ret) -+ goto out_clk_fast; -+ -+ /* Make sure nothing gets out of the DAI yet*/ -+ __hold(priv, true); -+ -+ ret = clk_set_parent(priv->mclk, priv->mclk_i958); -+ if (ret) -+ return ret; -+ -+ /* Enable the clock gate */ -+ ret = clk_prepare_enable(priv->iface); -+ if (ret) -+ goto out_clk_iface; -+ -+ /* Enable the spdif clock */ -+ ret = clk_prepare_enable(priv->mclk); -+ if (ret) -+ goto out_mclk; -+ -+ /* -+ * Make sure the interface expect a memory layout we can work with -+ * MEM prefixed register usually belong to the DMA, but when the spdif -+ * DAI takes data from the i2s buffer, we need to make sure it works in -+ * split mode and not the "normal mode" (channel samples packed in -+ * 32 bytes groups) -+ */ -+ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_CONTROL, -+ AIU_MEM_IEC958_CONTROL_MODE_LINEAR, -+ AIU_MEM_IEC958_CONTROL_MODE_LINEAR); -+ -+ return 0; -+ -+out_mclk: -+ clk_disable_unprepare(priv->iface); -+out_clk_iface: -+ clk_disable_unprepare(priv->fast); -+out_clk_fast: -+ return ret; -+} -+ -+static void meson_spdif_dai_shutdown(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct meson_spdif_dai *priv = snd_soc_dai_get_drvdata(dai); -+ -+ clk_disable_unprepare(priv->iface); -+ clk_disable_unprepare(priv->mclk); -+ clk_disable_unprepare(priv->fast); -+} -+ -+static const struct snd_soc_dai_ops meson_spdif_dai_ops = { -+ .startup = meson_spdif_dai_startup, -+ .shutdown = meson_spdif_dai_shutdown, -+ .trigger = meson_spdif_dai_trigger, -+ .hw_params = meson_spdif_dai_hw_params, -+}; -+ -+static struct snd_soc_dai_driver meson_spdif_dai = { -+ .playback = { -+ .stream_name = "Playback", -+ .channels_min = 2, -+ .channels_max = 2, -+ .rates = (SNDRV_PCM_RATE_32000 | -+ SNDRV_PCM_RATE_44100 | -+ SNDRV_PCM_RATE_48000 | -+ SNDRV_PCM_RATE_96000 | -+ SNDRV_PCM_RATE_192000), -+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | -+ SNDRV_PCM_FMTBIT_S24_LE) -+ }, -+ .ops = &meson_spdif_dai_ops, -+}; -+ -+static const struct snd_soc_component_driver meson_spdif_dai_component = { -+ .name = DRV_NAME, -+}; -+ -+static int meson_spdif_dai_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct meson_spdif_dai *priv; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ platform_set_drvdata(pdev, priv); -+ priv->core = dev_get_drvdata(dev->parent); -+ -+ priv->fast = devm_clk_get(dev, "fast"); -+ if (IS_ERR(priv->fast)) { -+ if (PTR_ERR(priv->fast) != -EPROBE_DEFER) -+ dev_err(dev, "Can't get spdif fast domain clockt\n"); -+ return PTR_ERR(priv->fast); -+ } -+ -+ priv->iface = devm_clk_get(dev, "iface"); -+ if (IS_ERR(priv->iface)) { -+ if (PTR_ERR(priv->iface) != -EPROBE_DEFER) -+ dev_err(dev, -+ "Can't get the dai clock gate\n"); -+ return PTR_ERR(priv->iface); -+ } -+ -+ priv->mclk_i958 = devm_clk_get(dev, "mclk_i958"); -+ if (IS_ERR(priv->mclk_i958)) { -+ if (PTR_ERR(priv->mclk_i958) != -EPROBE_DEFER) -+ dev_err(dev, "Can't get the spdif master clock\n"); -+ return PTR_ERR(priv->mclk_i958); -+ } -+ -+ /* -+ * TODO: the spdif dai can also get its data from the i2s fifo. -+ * For this use-case, the DAI driver will need to get the i2s master -+ * clock in order to reparent the spdif clock from cts_mclk_i958 to -+ * cts_amclk -+ */ -+ -+ priv->mclk = devm_clk_get(dev, "mclk"); -+ if (IS_ERR(priv->mclk)) { -+ if (PTR_ERR(priv->mclk) != -EPROBE_DEFER) -+ dev_err(dev, "Can't get the spdif input mux clock\n"); -+ return PTR_ERR(priv->mclk); -+ } -+ -+ return devm_snd_soc_register_component(dev, &meson_spdif_dai_component, -+ &meson_spdif_dai, 1); -+} -+ -+static const struct of_device_id meson_spdif_dai_of_match[] = { -+ { .compatible = "amlogic,meson-spdif-dai", }, -+ { .compatible = "amlogic,meson-gxbb-spdif-dai", }, -+ { .compatible = "amlogic,meson-gxl-spdif-dai", }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, meson_spdif_dai_of_match); -+ -+static struct platform_driver meson_spdif_dai_pdrv = { -+ .probe = meson_spdif_dai_probe, -+ .driver = { -+ .name = DRV_NAME, -+ .of_match_table = meson_spdif_dai_of_match, -+ }, -+}; -+module_platform_driver(meson_spdif_dai_pdrv); -+ -+MODULE_DESCRIPTION("Meson spdif DAI ASoC Driver"); -+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); -+MODULE_LICENSE("GPL v2"); |