summaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/Kconfig4
-rw-r--r--sound/soc/Makefile4
-rw-r--r--sound/soc/adi/Kconfig21
-rw-r--r--sound/soc/adi/Makefile5
-rw-r--r--sound/soc/adi/axi-i2s.c277
-rw-r--r--sound/soc/adi/axi-spdif.c272
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c30
-rw-r--r--sound/soc/atmel/sam9x5_wm8731.c6
-rw-r--r--sound/soc/au1x/dbdma2.c9
-rw-r--r--sound/soc/au1x/dma.c14
-rw-r--r--sound/soc/bcm/Kconfig10
-rw-r--r--sound/soc/bcm/Makefile5
-rw-r--r--sound/soc/bcm/bcm2835-i2s.c879
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c1
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c3
-rw-r--r--sound/soc/cirrus/edb93xx.c2
-rw-r--r--sound/soc/cirrus/ep93xx-ac97.c20
-rw-r--r--sound/soc/cirrus/ep93xx-i2s.c22
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.c48
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.h22
-rw-r--r--sound/soc/cirrus/simone.c2
-rw-r--r--sound/soc/cirrus/snappercl15.c2
-rw-r--r--sound/soc/codecs/ad1836.c2
-rw-r--r--sound/soc/codecs/ad193x.c6
-rw-r--r--sound/soc/codecs/adav80x.c6
-rw-r--r--sound/soc/codecs/ak4641.c38
-rw-r--r--sound/soc/codecs/ak4642.c138
-rw-r--r--sound/soc/codecs/cs4271.c8
-rw-r--r--sound/soc/codecs/cs42l52.c105
-rw-r--r--sound/soc/codecs/da7210.c6
-rw-r--r--sound/soc/codecs/hdmi.c12
-rw-r--r--sound/soc/codecs/sgtl5000.c76
-rw-r--r--sound/soc/codecs/ssm2518.c8
-rw-r--r--sound/soc/codecs/ssm2602.c56
-rw-r--r--sound/soc/codecs/tlv320aic3x.c126
-rw-r--r--sound/soc/codecs/twl6040.c227
-rw-r--r--sound/soc/codecs/uda1380.c6
-rw-r--r--sound/soc/codecs/wm5110.c27
-rw-r--r--sound/soc/codecs/wm8510.c6
-rw-r--r--sound/soc/codecs/wm8523.c6
-rw-r--r--sound/soc/codecs/wm8580.c6
-rw-r--r--sound/soc/codecs/wm8711.c6
-rw-r--r--sound/soc/codecs/wm8728.c6
-rw-r--r--sound/soc/codecs/wm8731.c10
-rw-r--r--sound/soc/codecs/wm8741.c6
-rw-r--r--sound/soc/codecs/wm8750.c6
-rw-r--r--sound/soc/codecs/wm8753.c6
-rw-r--r--sound/soc/codecs/wm8776.c6
-rw-r--r--sound/soc/codecs/wm8804.c6
-rw-r--r--sound/soc/codecs/wm8900.c6
-rw-r--r--sound/soc/codecs/wm8904.c2
-rw-r--r--sound/soc/codecs/wm8940.c221
-rw-r--r--sound/soc/codecs/wm8962.c17
-rw-r--r--sound/soc/codecs/wm8974.c54
-rw-r--r--sound/soc/codecs/wm8985.c6
-rw-r--r--sound/soc/codecs/wm8988.c6
-rw-r--r--sound/soc/codecs/wm8990.c258
-rw-r--r--sound/soc/codecs/wm8990.h9
-rw-r--r--sound/soc/codecs/wm8991.c293
-rw-r--r--sound/soc/codecs/wm8991.h9
-rw-r--r--sound/soc/codecs/wm8994.c11
-rw-r--r--sound/soc/codecs/wm8995.c6
-rw-r--r--sound/soc/codecs/wm9081.c2
-rw-r--r--sound/soc/codecs/wm_adsp.c29
-rw-r--r--sound/soc/davinci/Kconfig29
-rw-r--r--sound/soc/davinci/Makefile8
-rw-r--r--sound/soc/davinci/davinci-evm.c48
-rw-r--r--sound/soc/davinci/davinci-mcasp.c1016
-rw-r--r--sound/soc/davinci/davinci-mcasp.h304
-rw-r--r--sound/soc/davinci/davinci-pcm.c28
-rw-r--r--sound/soc/fsl/Kconfig5
-rw-r--r--sound/soc/fsl/Makefile4
-rw-r--r--sound/soc/fsl/fsl_dma.c2
-rw-r--r--sound/soc/fsl/fsl_sai.c460
-rw-r--r--sound/soc/fsl/fsl_sai.h114
-rw-r--r--sound/soc/fsl/fsl_spdif.c8
-rw-r--r--sound/soc/fsl/fsl_ssi.c435
-rw-r--r--sound/soc/fsl/fsl_ssi.h2
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c9
-rw-r--r--sound/soc/fsl/imx-pcm.h5
-rw-r--r--sound/soc/fsl/imx-spdif.c85
-rw-r--r--sound/soc/fsl/imx-ssi.c12
-rw-r--r--sound/soc/fsl/imx-ssi.h1
-rw-r--r--sound/soc/fsl/imx-wm8962.c2
-rw-r--r--sound/soc/fsl/pcm030-audio-fabric.c3
-rw-r--r--sound/soc/generic/simple-card.c193
-rw-r--r--sound/soc/intel/Kconfig (renamed from sound/soc/mid-x86/Kconfig)0
-rw-r--r--sound/soc/intel/Makefile (renamed from sound/soc/mid-x86/Makefile)0
-rw-r--r--sound/soc/intel/mfld_machine.c (renamed from sound/soc/mid-x86/mfld_machine.c)0
-rw-r--r--sound/soc/intel/sst_dsp.h (renamed from sound/soc/mid-x86/sst_dsp.h)0
-rw-r--r--sound/soc/intel/sst_platform.c (renamed from sound/soc/mid-x86/sst_platform.c)0
-rw-r--r--sound/soc/intel/sst_platform.h (renamed from sound/soc/mid-x86/sst_platform.h)0
-rw-r--r--sound/soc/jz4740/Kconfig1
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c135
-rw-r--r--sound/soc/jz4740/jz4740-pcm.c358
-rw-r--r--sound/soc/jz4740/jz4740-pcm.h20
-rw-r--r--sound/soc/jz4740/qi_lb60.c2
-rw-r--r--sound/soc/kirkwood/kirkwood-i2s.c46
-rw-r--r--sound/soc/mxs/mxs-pcm.c8
-rw-r--r--sound/soc/mxs/mxs-pcm.h1
-rw-r--r--sound/soc/mxs/mxs-saif.c18
-rw-r--r--sound/soc/omap/mcbsp.c12
-rw-r--r--sound/soc/omap/n810.c4
-rw-r--r--sound/soc/omap/omap-dmic.c4
-rw-r--r--sound/soc/omap/omap-mcpdm.c4
-rw-r--r--sound/soc/omap/omap-pcm.c2
-rw-r--r--sound/soc/pxa/Kconfig2
-rw-r--r--sound/soc/pxa/mmp-pcm.c24
-rw-r--r--sound/soc/s6000/s6000-pcm.c7
-rw-r--r--sound/soc/samsung/Kconfig13
-rw-r--r--sound/soc/samsung/Makefile6
-rw-r--r--sound/soc/samsung/ac97.c51
-rw-r--r--sound/soc/samsung/dma.c14
-rw-r--r--sound/soc/samsung/dma.h6
-rw-r--r--sound/soc/samsung/dmaengine.c84
-rw-r--r--sound/soc/samsung/i2s.c9
-rw-r--r--sound/soc/samsung/idma.c8
-rw-r--r--sound/soc/samsung/pcm.c18
-rw-r--r--sound/soc/samsung/regs-ac97.h9
-rw-r--r--sound/soc/samsung/regs-iis.h9
-rw-r--r--sound/soc/sh/Kconfig1
-rw-r--r--sound/soc/sh/fsi.c36
-rw-r--r--sound/soc/sh/rcar/adg.c151
-rw-r--r--sound/soc/sh/rcar/gen.c256
-rw-r--r--sound/soc/sh/rcar/rsnd.h46
-rw-r--r--sound/soc/sh/rcar/scu.c181
-rw-r--r--sound/soc/sh/rcar/ssi.c19
-rw-r--r--sound/soc/soc-core.c14
-rw-r--r--sound/soc/soc-dapm.c28
-rw-r--r--sound/soc/soc-devres.c45
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c94
-rw-r--r--sound/soc/soc-io.c4
-rw-r--r--sound/soc/soc-pcm.c179
-rw-r--r--sound/soc/soc-utils.c4
-rw-r--r--sound/soc/spear/spdif_in.c17
-rw-r--r--sound/soc/spear/spdif_out.c18
-rw-r--r--sound/soc/spear/spear_pcm.c42
-rw-r--r--sound/soc/spear/spear_pcm.h24
-rw-r--r--sound/soc/tegra/Kconfig10
-rw-r--r--sound/soc/tegra/Makefile2
-rw-r--r--sound/soc/tegra/tegra20_ac97.c6
-rw-r--r--sound/soc/tegra/tegra20_i2s.c6
-rw-r--r--sound/soc/tegra/tegra20_spdif.c10
-rw-r--r--sound/soc/tegra/tegra30_i2s.c6
-rw-r--r--sound/soc/tegra/tegra_max98090.c275
-rw-r--r--sound/soc/tegra/tegra_pcm.c3
-rw-r--r--sound/soc/tegra/tegra_wm9712.c1
-rw-r--r--sound/soc/txx9/txx9aclc.c5
148 files changed, 5699 insertions, 2936 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 5138b8493051..d62ce483a443 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -31,8 +31,10 @@ config SND_SOC_GENERIC_DMAENGINE_PCM
select SND_DMAENGINE_PCM
# All the supported SoCs
+source "sound/soc/adi/Kconfig"
source "sound/soc/atmel/Kconfig"
source "sound/soc/au1x/Kconfig"
+source "sound/soc/bcm/Kconfig"
source "sound/soc/blackfin/Kconfig"
source "sound/soc/cirrus/Kconfig"
source "sound/soc/davinci/Kconfig"
@@ -42,7 +44,7 @@ source "sound/soc/jz4740/Kconfig"
source "sound/soc/nuc900/Kconfig"
source "sound/soc/omap/Kconfig"
source "sound/soc/kirkwood/Kconfig"
-source "sound/soc/mid-x86/Kconfig"
+source "sound/soc/intel/Kconfig"
source "sound/soc/mxs/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/samsung/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 8b9e70105dd2..62a1822e77bf 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -8,15 +8,17 @@ endif
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
obj-$(CONFIG_SND_SOC) += generic/
+obj-$(CONFIG_SND_SOC) += adi/
obj-$(CONFIG_SND_SOC) += atmel/
obj-$(CONFIG_SND_SOC) += au1x/
+obj-$(CONFIG_SND_SOC) += bcm/
obj-$(CONFIG_SND_SOC) += blackfin/
obj-$(CONFIG_SND_SOC) += cirrus/
obj-$(CONFIG_SND_SOC) += davinci/
obj-$(CONFIG_SND_SOC) += dwc/
obj-$(CONFIG_SND_SOC) += fsl/
obj-$(CONFIG_SND_SOC) += jz4740/
-obj-$(CONFIG_SND_SOC) += mid-x86/
+obj-$(CONFIG_SND_SOC) += intel/
obj-$(CONFIG_SND_SOC) += mxs/
obj-$(CONFIG_SND_SOC) += nuc900/
obj-$(CONFIG_SND_SOC) += omap/
diff --git a/sound/soc/adi/Kconfig b/sound/soc/adi/Kconfig
new file mode 100644
index 000000000000..dd763f55edac
--- /dev/null
+++ b/sound/soc/adi/Kconfig
@@ -0,0 +1,21 @@
+config SND_SOC_ADI
+ tristate "Audio support for Analog Devices reference designs"
+ depends on MICROBLAZE || ARCH_ZYNQ || COMPILE_TEST
+ help
+ Audio support for various reference designs by Analog Devices.
+
+config SND_SOC_ADI_AXI_I2S
+ tristate "AXI-I2S support"
+ depends on SND_SOC_ADI
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ ASoC driver for the Analog Devices AXI-I2S softcore peripheral.
+
+config SND_SOC_ADI_AXI_SPDIF
+ tristate "AXI-SPDIF support"
+ depends on SND_SOC_ADI
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ ASoC driver for the Analog Devices AXI-SPDIF softcore peripheral.
diff --git a/sound/soc/adi/Makefile b/sound/soc/adi/Makefile
new file mode 100644
index 000000000000..64456c1e5347
--- /dev/null
+++ b/sound/soc/adi/Makefile
@@ -0,0 +1,5 @@
+snd-soc-adi-axi-i2s-objs := axi-i2s.o
+snd-soc-adi-axi-spdif-objs := axi-spdif.o
+
+obj-$(CONFIG_SND_SOC_ADI_AXI_I2S) += snd-soc-adi-axi-i2s.o
+obj-$(CONFIG_SND_SOC_ADI_AXI_SPDIF) += snd-soc-adi-axi-spdif.o
diff --git a/sound/soc/adi/axi-i2s.c b/sound/soc/adi/axi-i2s.c
new file mode 100644
index 000000000000..7f91a86dd734
--- /dev/null
+++ b/sound/soc/adi/axi-i2s.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2012-2013, Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#define AXI_I2S_REG_RESET 0x00
+#define AXI_I2S_REG_CTRL 0x04
+#define AXI_I2S_REG_CLK_CTRL 0x08
+#define AXI_I2S_REG_STATUS 0x10
+
+#define AXI_I2S_REG_RX_FIFO 0x28
+#define AXI_I2S_REG_TX_FIFO 0x2C
+
+#define AXI_I2S_RESET_GLOBAL BIT(0)
+#define AXI_I2S_RESET_TX_FIFO BIT(1)
+#define AXI_I2S_RESET_RX_FIFO BIT(2)
+
+#define AXI_I2S_CTRL_TX_EN BIT(0)
+#define AXI_I2S_CTRL_RX_EN BIT(1)
+
+/* The frame size is configurable, but for now we always set it 64 bit */
+#define AXI_I2S_BITS_PER_FRAME 64
+
+struct axi_i2s {
+ struct regmap *regmap;
+ struct clk *clk;
+ struct clk *clk_ref;
+
+ struct snd_soc_dai_driver dai_driver;
+
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+
+ struct snd_ratnum ratnum;
+ struct snd_pcm_hw_constraint_ratnums rate_constraints;
+};
+
+static int axi_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned int mask, val;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mask = AXI_I2S_CTRL_RX_EN;
+ else
+ mask = AXI_I2S_CTRL_TX_EN;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = mask;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(i2s->regmap, AXI_I2S_REG_CTRL, mask, val);
+
+ return 0;
+}
+
+static int axi_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned int bclk_div, word_size;
+ unsigned int bclk_rate;
+
+ bclk_rate = params_rate(params) * AXI_I2S_BITS_PER_FRAME;
+
+ word_size = AXI_I2S_BITS_PER_FRAME / 2 - 1;
+ bclk_div = DIV_ROUND_UP(clk_get_rate(i2s->clk_ref), bclk_rate) / 2 - 1;
+
+ regmap_write(i2s->regmap, AXI_I2S_REG_CLK_CTRL, (word_size << 16) |
+ bclk_div);
+
+ return 0;
+}
+
+static int axi_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ uint32_t mask;
+ int ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mask = AXI_I2S_RESET_RX_FIFO;
+ else
+ mask = AXI_I2S_RESET_TX_FIFO;
+
+ regmap_write(i2s->regmap, AXI_I2S_REG_RESET, mask);
+
+ ret = snd_pcm_hw_constraint_ratnums(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &i2s->rate_constraints);
+ if (ret)
+ return ret;
+
+ return clk_prepare_enable(i2s->clk_ref);
+}
+
+static void axi_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ clk_disable_unprepare(i2s->clk_ref);
+}
+
+static int axi_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
+ &i2s->capture_dma_data);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops axi_i2s_dai_ops = {
+ .startup = axi_i2s_startup,
+ .shutdown = axi_i2s_shutdown,
+ .trigger = axi_i2s_trigger,
+ .hw_params = axi_i2s_hw_params,
+};
+
+static struct snd_soc_dai_driver axi_i2s_dai = {
+ .probe = axi_i2s_dai_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
+ },
+ .ops = &axi_i2s_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static const struct snd_soc_component_driver axi_i2s_component = {
+ .name = "axi-i2s",
+};
+
+static const struct regmap_config axi_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = AXI_I2S_REG_STATUS,
+};
+
+static int axi_i2s_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct axi_i2s *i2s;
+ void __iomem *base;
+ int ret;
+
+ i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
+ if (!i2s)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, i2s);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ i2s->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &axi_i2s_regmap_config);
+ if (IS_ERR(i2s->regmap))
+ return PTR_ERR(i2s->regmap);
+
+ i2s->clk = devm_clk_get(&pdev->dev, "axi");
+ if (IS_ERR(i2s->clk))
+ return PTR_ERR(i2s->clk);
+
+ i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
+ if (IS_ERR(i2s->clk_ref))
+ return PTR_ERR(i2s->clk_ref);
+
+ ret = clk_prepare_enable(i2s->clk);
+ if (ret)
+ return ret;
+
+ i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
+ i2s->playback_dma_data.addr_width = 4;
+ i2s->playback_dma_data.maxburst = 1;
+
+ i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
+ i2s->capture_dma_data.addr_width = 4;
+ i2s->capture_dma_data.maxburst = 1;
+
+ i2s->ratnum.num = clk_get_rate(i2s->clk_ref) / 2 / AXI_I2S_BITS_PER_FRAME;
+ i2s->ratnum.den_step = 1;
+ i2s->ratnum.den_min = 1;
+ i2s->ratnum.den_max = 64;
+
+ i2s->rate_constraints.rats = &i2s->ratnum;
+ i2s->rate_constraints.nrats = 1;
+
+ regmap_write(i2s->regmap, AXI_I2S_REG_RESET, AXI_I2S_RESET_GLOBAL);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &axi_i2s_component,
+ &axi_i2s_dai, 1);
+ if (ret)
+ goto err_clk_disable;
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+ SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
+ if (ret)
+ goto err_clk_disable;
+
+err_clk_disable:
+ clk_disable_unprepare(i2s->clk);
+ return ret;
+}
+
+static int axi_i2s_dev_remove(struct platform_device *pdev)
+{
+ struct axi_i2s *i2s = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(i2s->clk);
+
+ return 0;
+}
+
+static const struct of_device_id axi_i2s_of_match[] = {
+ { .compatible = "adi,axi-i2s-1.00.a", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, axi_i2s_of_match);
+
+static struct platform_driver axi_i2s_driver = {
+ .driver = {
+ .name = "axi-i2s",
+ .owner = THIS_MODULE,
+ .of_match_table = axi_i2s_of_match,
+ },
+ .probe = axi_i2s_probe,
+ .remove = axi_i2s_dev_remove,
+};
+module_platform_driver(axi_i2s_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("AXI I2S driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/adi/axi-spdif.c b/sound/soc/adi/axi-spdif.c
new file mode 100644
index 000000000000..8db7a9920695
--- /dev/null
+++ b/sound/soc/adi/axi-spdif.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2012-2013, Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/dmaengine_pcm.h>
+
+#define AXI_SPDIF_REG_CTRL 0x0
+#define AXI_SPDIF_REG_STAT 0x4
+#define AXI_SPDIF_REG_TX_FIFO 0xc
+
+#define AXI_SPDIF_CTRL_TXDATA BIT(1)
+#define AXI_SPDIF_CTRL_TXEN BIT(0)
+#define AXI_SPDIF_CTRL_CLKDIV_OFFSET 8
+#define AXI_SPDIF_CTRL_CLKDIV_MASK (0xff << 8)
+
+#define AXI_SPDIF_FREQ_44100 (0x0 << 6)
+#define AXI_SPDIF_FREQ_48000 (0x1 << 6)
+#define AXI_SPDIF_FREQ_32000 (0x2 << 6)
+#define AXI_SPDIF_FREQ_NA (0x3 << 6)
+
+struct axi_spdif {
+ struct regmap *regmap;
+ struct clk *clk;
+ struct clk *clk_ref;
+
+ struct snd_dmaengine_dai_dma_data dma_data;
+
+ struct snd_ratnum ratnum;
+ struct snd_pcm_hw_constraint_ratnums rate_constraints;
+};
+
+static int axi_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+ unsigned int val;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = AXI_SPDIF_CTRL_TXDATA;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
+ AXI_SPDIF_CTRL_TXDATA, val);
+
+ return 0;
+}
+
+static int axi_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+ unsigned int rate = params_rate(params);
+ unsigned int clkdiv, stat;
+
+ switch (params_rate(params)) {
+ case 32000:
+ stat = AXI_SPDIF_FREQ_32000;
+ break;
+ case 44100:
+ stat = AXI_SPDIF_FREQ_44100;
+ break;
+ case 48000:
+ stat = AXI_SPDIF_FREQ_48000;
+ break;
+ default:
+ stat = AXI_SPDIF_FREQ_NA;
+ break;
+ }
+
+ clkdiv = DIV_ROUND_CLOSEST(clk_get_rate(spdif->clk_ref),
+ rate * 64 * 2) - 1;
+ clkdiv <<= AXI_SPDIF_CTRL_CLKDIV_OFFSET;
+
+ regmap_write(spdif->regmap, AXI_SPDIF_REG_STAT, stat);
+ regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
+ AXI_SPDIF_CTRL_CLKDIV_MASK, clkdiv);
+
+ return 0;
+}
+
+static int axi_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &spdif->dma_data, NULL);
+
+ return 0;
+}
+
+static int axi_spdif_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = snd_pcm_hw_constraint_ratnums(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &spdif->rate_constraints);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(spdif->clk_ref);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
+ AXI_SPDIF_CTRL_TXEN, AXI_SPDIF_CTRL_TXEN);
+
+ return 0;
+}
+
+static void axi_spdif_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+
+ regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
+ AXI_SPDIF_CTRL_TXEN, 0);
+
+ clk_disable_unprepare(spdif->clk_ref);
+}
+
+static const struct snd_soc_dai_ops axi_spdif_dai_ops = {
+ .startup = axi_spdif_startup,
+ .shutdown = axi_spdif_shutdown,
+ .trigger = axi_spdif_trigger,
+ .hw_params = axi_spdif_hw_params,
+};
+
+static struct snd_soc_dai_driver axi_spdif_dai = {
+ .probe = axi_spdif_dai_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &axi_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver axi_spdif_component = {
+ .name = "axi-spdif",
+};
+
+static const struct regmap_config axi_spdif_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = AXI_SPDIF_REG_STAT,
+};
+
+static int axi_spdif_probe(struct platform_device *pdev)
+{
+ struct axi_spdif *spdif;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+
+ spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
+ if (!spdif)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, spdif);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ spdif->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &axi_spdif_regmap_config);
+ if (IS_ERR(spdif->regmap))
+ return PTR_ERR(spdif->regmap);
+
+ spdif->clk = devm_clk_get(&pdev->dev, "axi");
+ if (IS_ERR(spdif->clk))
+ return PTR_ERR(spdif->clk);
+
+ spdif->clk_ref = devm_clk_get(&pdev->dev, "ref");
+ if (IS_ERR(spdif->clk_ref))
+ return PTR_ERR(spdif->clk_ref);
+
+ ret = clk_prepare_enable(spdif->clk);
+ if (ret)
+ return ret;
+
+ spdif->dma_data.addr = res->start + AXI_SPDIF_REG_TX_FIFO;
+ spdif->dma_data.addr_width = 4;
+ spdif->dma_data.maxburst = 1;
+
+ spdif->ratnum.num = clk_get_rate(spdif->clk_ref) / 128;
+ spdif->ratnum.den_step = 1;
+ spdif->ratnum.den_min = 1;
+ spdif->ratnum.den_max = 64;
+
+ spdif->rate_constraints.rats = &spdif->ratnum;
+ spdif->rate_constraints.nrats = 1;
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &axi_spdif_component,
+ &axi_spdif_dai, 1);
+ if (ret)
+ goto err_clk_disable;
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+ SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
+ if (ret)
+ goto err_clk_disable;
+
+ return 0;
+
+err_clk_disable:
+ clk_disable_unprepare(spdif->clk);
+ return ret;
+}
+
+static int axi_spdif_dev_remove(struct platform_device *pdev)
+{
+ struct axi_spdif *spdif = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(spdif->clk);
+
+ return 0;
+}
+
+static const struct of_device_id axi_spdif_of_match[] = {
+ { .compatible = "adi,axi-spdif-tx-1.00.a", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, axi_spdif_of_match);
+
+static struct platform_driver axi_spdif_driver = {
+ .driver = {
+ .name = "axi-spdif",
+ .owner = THIS_MODULE,
+ .of_match_table = axi_spdif_of_match,
+ },
+ .probe = axi_spdif_probe,
+ .remove = axi_spdif_dev_remove,
+};
+module_platform_driver(axi_spdif_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("AXI SPDIF driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 8697cedccd21..1ead3c977a51 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -648,7 +648,7 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
dma_params = ssc_p->dma_params[dir];
- ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable);
+ ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable);
ssc_writel(ssc_p->ssc->regs, IDR, dma_params->mask->ssc_error);
pr_debug("%s enabled SSC_SR=0x%08x\n",
@@ -657,6 +657,33 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
return 0;
}
+static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+ struct atmel_pcm_dma_params *dma_params;
+ int dir;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = 0;
+ else
+ dir = 1;
+
+ dma_params = ssc_p->dma_params[dir];
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable);
+ break;
+ default:
+ ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable);
+ break;
+ }
+
+ return 0;
+}
#ifdef CONFIG_PM
static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
@@ -731,6 +758,7 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
.startup = atmel_ssc_startup,
.shutdown = atmel_ssc_shutdown,
.prepare = atmel_ssc_prepare,
+ .trigger = atmel_ssc_trigger,
.hw_params = atmel_ssc_hw_params,
.set_fmt = atmel_ssc_set_dai_fmt,
.set_clkdiv = atmel_ssc_set_dai_clkdiv,
diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c
index 992ae38d5a15..3188036a18f0 100644
--- a/sound/soc/atmel/sam9x5_wm8731.c
+++ b/sound/soc/atmel/sam9x5_wm8731.c
@@ -97,6 +97,8 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
goto out;
}
+ snd_soc_card_set_drvdata(card, priv);
+
card->dev = &pdev->dev;
card->owner = THIS_MODULE;
card->dai_link = dai;
@@ -107,7 +109,7 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
dai->stream_name = "WM8731 PCM";
dai->codec_dai_name = "wm8731-hifi";
dai->init = sam9x5_wm8731_init;
- dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ dai->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBM_CFM;
ret = snd_soc_of_parse_card_name(card, "atmel,model");
@@ -153,8 +155,6 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
of_node_put(codec_np);
of_node_put(cpu_np);
- platform_set_drvdata(pdev, card);
-
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev,
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
index 3b4eafaf30d3..17a24d804734 100644
--- a/sound/soc/au1x/dbdma2.c
+++ b/sound/soc/au1x/dbdma2.c
@@ -65,19 +65,10 @@ struct au1xpsc_audio_dmadata {
#define AU1XPSC_PERIOD_MIN_BYTES 1024
#define AU1XPSC_BUFFER_MIN_BYTES 65536
-#define AU1XPSC_PCM_FMTS \
- (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE | \
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | \
- SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE | \
- 0)
-
/* PCM hardware DMA capabilities - platform specific */
static const struct snd_pcm_hardware au1xpsc_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH,
- .formats = AU1XPSC_PCM_FMTS,
.period_bytes_min = AU1XPSC_PERIOD_MIN_BYTES,
.period_bytes_max = 4096 * 1024 - 1,
.periods_min = 2,
diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c
index befd1074f9bd..e920b60bf6c2 100644
--- a/sound/soc/au1x/dma.c
+++ b/sound/soc/au1x/dma.c
@@ -21,14 +21,6 @@
#include "psc.h"
-#define ALCHEMY_PCM_FMTS \
- (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE | \
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | \
- SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE | \
- 0)
-
struct pcm_period {
u32 start;
u32 relative_end; /* relative to start of buffer */
@@ -171,12 +163,6 @@ static irqreturn_t au1000_dma_interrupt(int irq, void *ptr)
static const struct snd_pcm_hardware alchemy_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH,
- .formats = ALCHEMY_PCM_FMTS,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .rate_min = SNDRV_PCM_RATE_8000,
- .rate_max = SNDRV_PCM_RATE_192000,
- .channels_min = 2,
- .channels_max = 2,
.period_bytes_min = 1024,
.period_bytes_max = 16 * 1024 - 1,
.periods_min = 4,
diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig
new file mode 100644
index 000000000000..3d82a29ce3a8
--- /dev/null
+++ b/sound/soc/bcm/Kconfig
@@ -0,0 +1,10 @@
+config SND_BCM2835_SOC_I2S
+ tristate "SoC Audio support for the Broadcom BCM2835 I2S module"
+ depends on ARCH_BCM2835 || COMPILE_TEST
+ select SND_SOC_DMAENGINE_PCM
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the BCM2835 I2S interface. You will also need
+ to select the audio interfaces to support below.
diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile
new file mode 100644
index 000000000000..bc816b71e5a4
--- /dev/null
+++ b/sound/soc/bcm/Makefile
@@ -0,0 +1,5 @@
+# BCM2835 Platform Support
+snd-soc-bcm2835-i2s-objs := bcm2835-i2s.o
+
+obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o
+
diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c
new file mode 100644
index 000000000000..2685fe4f8427
--- /dev/null
+++ b/sound/soc/bcm/bcm2835-i2s.c
@@ -0,0 +1,879 @@
+/*
+ * ALSA SoC I2S Audio Layer for Broadcom BCM2835 SoC
+ *
+ * Author: Florian Meier <florian.meier@koalo.de>
+ * Copyright 2013
+ *
+ * Based on
+ * Raspberry Pi PCM I2S ALSA Driver
+ * Copyright (c) by Phil Poole 2013
+ *
+ * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
+ * Vladimir Barinov, <vbarinov@embeddedalley.com>
+ * Copyright (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * OMAP ALSA SoC DAI driver using McBSP port
+ * Copyright (C) 2008 Nokia Corporation
+ * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver
+ * Author: Timur Tabi <timur@freescale.com>
+ * Copyright 2007-2010 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+/* Clock registers */
+#define BCM2835_CLK_PCMCTL_REG 0x00
+#define BCM2835_CLK_PCMDIV_REG 0x04
+
+/* Clock register settings */
+#define BCM2835_CLK_PASSWD (0x5a000000)
+#define BCM2835_CLK_PASSWD_MASK (0xff000000)
+#define BCM2835_CLK_MASH(v) ((v) << 9)
+#define BCM2835_CLK_FLIP BIT(8)
+#define BCM2835_CLK_BUSY BIT(7)
+#define BCM2835_CLK_KILL BIT(5)
+#define BCM2835_CLK_ENAB BIT(4)
+#define BCM2835_CLK_SRC(v) (v)
+
+#define BCM2835_CLK_SHIFT (12)
+#define BCM2835_CLK_DIVI(v) ((v) << BCM2835_CLK_SHIFT)
+#define BCM2835_CLK_DIVF(v) (v)
+#define BCM2835_CLK_DIVF_MASK (0xFFF)
+
+enum {
+ BCM2835_CLK_MASH_0 = 0,
+ BCM2835_CLK_MASH_1,
+ BCM2835_CLK_MASH_2,
+ BCM2835_CLK_MASH_3,
+};
+
+enum {
+ BCM2835_CLK_SRC_GND = 0,
+ BCM2835_CLK_SRC_OSC,
+ BCM2835_CLK_SRC_DBG0,
+ BCM2835_CLK_SRC_DBG1,
+ BCM2835_CLK_SRC_PLLA,
+ BCM2835_CLK_SRC_PLLC,
+ BCM2835_CLK_SRC_PLLD,
+ BCM2835_CLK_SRC_HDMI,
+};
+
+/* Most clocks are not useable (freq = 0) */
+static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = {
+ [BCM2835_CLK_SRC_GND] = 0,
+ [BCM2835_CLK_SRC_OSC] = 19200000,
+ [BCM2835_CLK_SRC_DBG0] = 0,
+ [BCM2835_CLK_SRC_DBG1] = 0,
+ [BCM2835_CLK_SRC_PLLA] = 0,
+ [BCM2835_CLK_SRC_PLLC] = 0,
+ [BCM2835_CLK_SRC_PLLD] = 500000000,
+ [BCM2835_CLK_SRC_HDMI] = 0,
+};
+
+/* I2S registers */
+#define BCM2835_I2S_CS_A_REG 0x00
+#define BCM2835_I2S_FIFO_A_REG 0x04
+#define BCM2835_I2S_MODE_A_REG 0x08
+#define BCM2835_I2S_RXC_A_REG 0x0c
+#define BCM2835_I2S_TXC_A_REG 0x10
+#define BCM2835_I2S_DREQ_A_REG 0x14
+#define BCM2835_I2S_INTEN_A_REG 0x18
+#define BCM2835_I2S_INTSTC_A_REG 0x1c
+#define BCM2835_I2S_GRAY_REG 0x20
+
+/* I2S register settings */
+#define BCM2835_I2S_STBY BIT(25)
+#define BCM2835_I2S_SYNC BIT(24)
+#define BCM2835_I2S_RXSEX BIT(23)
+#define BCM2835_I2S_RXF BIT(22)
+#define BCM2835_I2S_TXE BIT(21)
+#define BCM2835_I2S_RXD BIT(20)
+#define BCM2835_I2S_TXD BIT(19)
+#define BCM2835_I2S_RXR BIT(18)
+#define BCM2835_I2S_TXW BIT(17)
+#define BCM2835_I2S_CS_RXERR BIT(16)
+#define BCM2835_I2S_CS_TXERR BIT(15)
+#define BCM2835_I2S_RXSYNC BIT(14)
+#define BCM2835_I2S_TXSYNC BIT(13)
+#define BCM2835_I2S_DMAEN BIT(9)
+#define BCM2835_I2S_RXTHR(v) ((v) << 7)
+#define BCM2835_I2S_TXTHR(v) ((v) << 5)
+#define BCM2835_I2S_RXCLR BIT(4)
+#define BCM2835_I2S_TXCLR BIT(3)
+#define BCM2835_I2S_TXON BIT(2)
+#define BCM2835_I2S_RXON BIT(1)
+#define BCM2835_I2S_EN (1)
+
+#define BCM2835_I2S_CLKDIS BIT(28)
+#define BCM2835_I2S_PDMN BIT(27)
+#define BCM2835_I2S_PDME BIT(26)
+#define BCM2835_I2S_FRXP BIT(25)
+#define BCM2835_I2S_FTXP BIT(24)
+#define BCM2835_I2S_CLKM BIT(23)
+#define BCM2835_I2S_CLKI BIT(22)
+#define BCM2835_I2S_FSM BIT(21)
+#define BCM2835_I2S_FSI BIT(20)
+#define BCM2835_I2S_FLEN(v) ((v) << 10)
+#define BCM2835_I2S_FSLEN(v) (v)
+
+#define BCM2835_I2S_CHWEX BIT(15)
+#define BCM2835_I2S_CHEN BIT(14)
+#define BCM2835_I2S_CHPOS(v) ((v) << 4)
+#define BCM2835_I2S_CHWID(v) (v)
+#define BCM2835_I2S_CH1(v) ((v) << 16)
+#define BCM2835_I2S_CH2(v) (v)
+
+#define BCM2835_I2S_TX_PANIC(v) ((v) << 24)
+#define BCM2835_I2S_RX_PANIC(v) ((v) << 16)
+#define BCM2835_I2S_TX(v) ((v) << 8)
+#define BCM2835_I2S_RX(v) (v)
+
+#define BCM2835_I2S_INT_RXERR BIT(3)
+#define BCM2835_I2S_INT_TXERR BIT(2)
+#define BCM2835_I2S_INT_RXR BIT(1)
+#define BCM2835_I2S_INT_TXW BIT(0)
+
+/* I2S DMA interface */
+/* FIXME: Needs IOMMU support */
+#define BCM2835_VCMMU_SHIFT (0x7E000000 - 0x20000000)
+
+/* General device struct */
+struct bcm2835_i2s_dev {
+ struct device *dev;
+ struct snd_dmaengine_dai_dma_data dma_data[2];
+ unsigned int fmt;
+ unsigned int bclk_ratio;
+
+ struct regmap *i2s_regmap;
+ struct regmap *clk_regmap;
+};
+
+static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev)
+{
+ /* Start the clock if in master mode */
+ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
+
+ switch (master) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+ regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
+ BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
+ BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB);
+ break;
+ default:
+ break;
+ }
+}
+
+static void bcm2835_i2s_stop_clock(struct bcm2835_i2s_dev *dev)
+{
+ uint32_t clkreg;
+ int timeout = 1000;
+
+ /* Stop clock */
+ regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
+ BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
+ BCM2835_CLK_PASSWD);
+
+ /* Wait for the BUSY flag going down */
+ while (--timeout) {
+ regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg);
+ if (!(clkreg & BCM2835_CLK_BUSY))
+ break;
+ }
+
+ if (!timeout) {
+ /* KILL the clock */
+ dev_err(dev->dev, "I2S clock didn't stop. Kill the clock!\n");
+ regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
+ BCM2835_CLK_KILL | BCM2835_CLK_PASSWD_MASK,
+ BCM2835_CLK_KILL | BCM2835_CLK_PASSWD);
+ }
+}
+
+static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
+ bool tx, bool rx)
+{
+ int timeout = 1000;
+ uint32_t syncval;
+ uint32_t csreg;
+ uint32_t i2s_active_state;
+ uint32_t clkreg;
+ uint32_t clk_active_state;
+ uint32_t off;
+ uint32_t clr;
+
+ off = tx ? BCM2835_I2S_TXON : 0;
+ off |= rx ? BCM2835_I2S_RXON : 0;
+
+ clr = tx ? BCM2835_I2S_TXCLR : 0;
+ clr |= rx ? BCM2835_I2S_RXCLR : 0;
+
+ /* Backup the current state */
+ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg);
+ i2s_active_state = csreg & (BCM2835_I2S_RXON | BCM2835_I2S_TXON);
+
+ regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg);
+ clk_active_state = clkreg & BCM2835_CLK_ENAB;
+
+ /* Start clock if not running */
+ if (!clk_active_state) {
+ regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
+ BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
+ BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB);
+ }
+
+ /* Stop I2S module */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, off, 0);
+
+ /*
+ * Clear the FIFOs
+ * Requires at least 2 PCM clock cycles to take effect
+ */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, clr, clr);
+
+ /* Wait for 2 PCM clock cycles */
+
+ /*
+ * Toggle the SYNC flag. After 2 PCM clock cycles it can be read back
+ * FIXME: This does not seem to work for slave mode!
+ */
+ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &syncval);
+ syncval &= BCM2835_I2S_SYNC;
+
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+ BCM2835_I2S_SYNC, ~syncval);
+
+ /* Wait for the SYNC flag changing it's state */
+ while (--timeout) {
+ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg);
+ if ((csreg & BCM2835_I2S_SYNC) != syncval)
+ break;
+ }
+
+ if (!timeout)
+ dev_err(dev->dev, "I2S SYNC error!\n");
+
+ /* Stop clock if it was not running before */
+ if (!clk_active_state)
+ bcm2835_i2s_stop_clock(dev);
+
+ /* Restore I2S state */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+ BCM2835_I2S_RXON | BCM2835_I2S_TXON, i2s_active_state);
+}
+
+static int bcm2835_i2s_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ dev->fmt = fmt;
+ return 0;
+}
+
+static int bcm2835_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ dev->bclk_ratio = ratio;
+ return 0;
+}
+
+static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ unsigned int sampling_rate = params_rate(params);
+ unsigned int data_length, data_delay, bclk_ratio;
+ unsigned int ch1pos, ch2pos, mode, format;
+ unsigned int mash = BCM2835_CLK_MASH_1;
+ unsigned int divi, divf, target_frequency;
+ int clk_src = -1;
+ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
+ bool bit_master = (master == SND_SOC_DAIFMT_CBS_CFS
+ || master == SND_SOC_DAIFMT_CBS_CFM);
+
+ bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS
+ || master == SND_SOC_DAIFMT_CBM_CFS);
+ uint32_t csreg;
+
+ /*
+ * If a stream is already enabled,
+ * the registers are already set properly.
+ */
+ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg);
+
+ if (csreg & (BCM2835_I2S_TXON | BCM2835_I2S_RXON))
+ return 0;
+
+ /*
+ * Adjust the data length according to the format.
+ * We prefill the half frame length with an integer
+ * divider of 2400 as explained at the clock settings.
+ * Maybe it is overwritten there, if the Integer mode
+ * does not apply.
+ */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ data_length = 16;
+ bclk_ratio = 40;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ data_length = 32;
+ bclk_ratio = 80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* If bclk_ratio already set, use that one. */
+ if (dev->bclk_ratio)
+ bclk_ratio = dev->bclk_ratio;
+
+ /*
+ * Clock Settings
+ *
+ * The target frequency of the bit clock is
+ * sampling rate * frame length
+ *
+ * Integer mode:
+ * Sampling rates that are multiples of 8000 kHz
+ * can be driven by the oscillator of 19.2 MHz
+ * with an integer divider as long as the frame length
+ * is an integer divider of 19200000/8000=2400 as set up above.
+ * This is no longer possible if the sampling rate
+ * is too high (e.g. 192 kHz), because the oscillator is too slow.
+ *
+ * MASH mode:
+ * For all other sampling rates, it is not possible to
+ * have an integer divider. Approximate the clock
+ * with the MASH module that induces a slight frequency
+ * variance. To minimize that it is best to have the fastest
+ * clock here. That is PLLD with 500 MHz.
+ */
+ target_frequency = sampling_rate * bclk_ratio;
+ clk_src = BCM2835_CLK_SRC_OSC;
+ mash = BCM2835_CLK_MASH_0;
+
+ if (bcm2835_clk_freq[clk_src] % target_frequency == 0
+ && bit_master && frame_master) {
+ divi = bcm2835_clk_freq[clk_src] / target_frequency;
+ divf = 0;
+ } else {
+ uint64_t dividend;
+
+ if (!dev->bclk_ratio) {
+ /*
+ * Overwrite bclk_ratio, because the
+ * above trick is not needed or can
+ * not be used.
+ */
+ bclk_ratio = 2 * data_length;
+ }
+
+ target_frequency = sampling_rate * bclk_ratio;
+
+ clk_src = BCM2835_CLK_SRC_PLLD;
+ mash = BCM2835_CLK_MASH_1;
+
+ dividend = bcm2835_clk_freq[clk_src];
+ dividend <<= BCM2835_CLK_SHIFT;
+ do_div(dividend, target_frequency);
+ divi = dividend >> BCM2835_CLK_SHIFT;
+ divf = dividend & BCM2835_CLK_DIVF_MASK;
+ }
+
+ /* Set clock divider */
+ regmap_write(dev->clk_regmap, BCM2835_CLK_PCMDIV_REG, BCM2835_CLK_PASSWD
+ | BCM2835_CLK_DIVI(divi)
+ | BCM2835_CLK_DIVF(divf));
+
+ /* Setup clock, but don't start it yet */
+ regmap_write(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, BCM2835_CLK_PASSWD
+ | BCM2835_CLK_MASH(mash)
+ | BCM2835_CLK_SRC(clk_src));
+
+ /* Setup the frame format */
+ format = BCM2835_I2S_CHEN;
+
+ if (data_length > 24)
+ format |= BCM2835_I2S_CHWEX;
+
+ format |= BCM2835_I2S_CHWID((data_length-8)&0xf);
+
+ switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ data_delay = 1;
+ break;
+ default:
+ /*
+ * TODO
+ * Others are possible but are not implemented at the moment.
+ */
+ dev_err(dev->dev, "%s:bad format\n", __func__);
+ return -EINVAL;
+ }
+
+ ch1pos = data_delay;
+ ch2pos = bclk_ratio / 2 + data_delay;
+
+ switch (params_channels(params)) {
+ case 2:
+ format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format);
+ format |= BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(ch1pos));
+ format |= BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(ch2pos));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * Set format for both streams.
+ * We cannot set another frame length
+ * (and therefore word length) anyway,
+ * so the format will be the same.
+ */
+ regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, format);
+ regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, format);
+
+ /* Setup the I2S mode */
+ mode = 0;
+
+ if (data_length <= 16) {
+ /*
+ * Use frame packed mode (2 channels per 32 bit word)
+ * We cannot set another frame length in the second stream
+ * (and therefore word length) anyway,
+ * so the format will be the same.
+ */
+ mode |= BCM2835_I2S_FTXP | BCM2835_I2S_FRXP;
+ }
+
+ mode |= BCM2835_I2S_FLEN(bclk_ratio - 1);
+ mode |= BCM2835_I2S_FSLEN(bclk_ratio / 2);
+
+ /* Master or slave? */
+ switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* CPU is master */
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ /*
+ * CODEC is bit clock master
+ * CPU is frame master
+ */
+ mode |= BCM2835_I2S_CLKM;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ /*
+ * CODEC is frame master
+ * CPU is bit clock master
+ */
+ mode |= BCM2835_I2S_FSM;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* CODEC is master */
+ mode |= BCM2835_I2S_CLKM;
+ mode |= BCM2835_I2S_FSM;
+ break;
+ default:
+ dev_err(dev->dev, "%s:bad master\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Invert clocks?
+ *
+ * The BCM approach seems to be inverted to the classical I2S approach.
+ */
+ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ /* None. Therefore, both for BCM */
+ mode |= BCM2835_I2S_CLKI;
+ mode |= BCM2835_I2S_FSI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ /* Both. Therefore, none for BCM */
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ /*
+ * Invert only frame sync. Therefore,
+ * invert only bit clock for BCM
+ */
+ mode |= BCM2835_I2S_CLKI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ /*
+ * Invert only bit clock. Therefore,
+ * invert only frame sync for BCM
+ */
+ mode |= BCM2835_I2S_FSI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(dev->i2s_regmap, BCM2835_I2S_MODE_A_REG, mode);
+
+ /* Setup the DMA parameters */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+ BCM2835_I2S_RXTHR(1)
+ | BCM2835_I2S_TXTHR(1)
+ | BCM2835_I2S_DMAEN, 0xffffffff);
+
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_DREQ_A_REG,
+ BCM2835_I2S_TX_PANIC(0x10)
+ | BCM2835_I2S_RX_PANIC(0x30)
+ | BCM2835_I2S_TX(0x30)
+ | BCM2835_I2S_RX(0x20), 0xffffffff);
+
+ /* Clear FIFOs */
+ bcm2835_i2s_clear_fifos(dev, true, true);
+
+ return 0;
+}
+
+static int bcm2835_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ uint32_t cs_reg;
+
+ bcm2835_i2s_start_clock(dev);
+
+ /*
+ * Clear both FIFOs if the one that should be started
+ * is not empty at the moment. This should only happen
+ * after overrun. Otherwise, hw_params would have cleared
+ * the FIFO.
+ */
+ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &cs_reg);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+ && !(cs_reg & BCM2835_I2S_TXE))
+ bcm2835_i2s_clear_fifos(dev, true, false);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
+ && (cs_reg & BCM2835_I2S_RXD))
+ bcm2835_i2s_clear_fifos(dev, false, true);
+
+ return 0;
+}
+
+static void bcm2835_i2s_stop(struct bcm2835_i2s_dev *dev,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ uint32_t mask;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mask = BCM2835_I2S_RXON;
+ else
+ mask = BCM2835_I2S_TXON;
+
+ regmap_update_bits(dev->i2s_regmap,
+ BCM2835_I2S_CS_A_REG, mask, 0);
+
+ /* Stop also the clock when not SND_SOC_DAIFMT_CONT */
+ if (!dai->active && !(dev->fmt & SND_SOC_DAIFMT_CONT))
+ bcm2835_i2s_stop_clock(dev);
+}
+
+static int bcm2835_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ uint32_t mask;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ bcm2835_i2s_start_clock(dev);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mask = BCM2835_I2S_RXON;
+ else
+ mask = BCM2835_I2S_TXON;
+
+ regmap_update_bits(dev->i2s_regmap,
+ BCM2835_I2S_CS_A_REG, mask, mask);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ bcm2835_i2s_stop(dev, substream, dai);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bcm2835_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ if (dai->active)
+ return 0;
+
+ /* Should this still be running stop it */
+ bcm2835_i2s_stop_clock(dev);
+
+ /* Enable PCM block */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+ BCM2835_I2S_EN, BCM2835_I2S_EN);
+
+ /*
+ * Disable STBY.
+ * Requires at least 4 PCM clock cycles to take effect.
+ */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+ BCM2835_I2S_STBY, BCM2835_I2S_STBY);
+
+ return 0;
+}
+
+static void bcm2835_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ bcm2835_i2s_stop(dev, substream, dai);
+
+ /* If both streams are stopped, disable module and clock */
+ if (dai->active)
+ return;
+
+ /* Disable the module */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+ BCM2835_I2S_EN, 0);
+
+ /*
+ * Stopping clock is necessary, because stop does
+ * not stop the clock when SND_SOC_DAIFMT_CONT
+ */
+ bcm2835_i2s_stop_clock(dev);
+}
+
+static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = {
+ .startup = bcm2835_i2s_startup,
+ .shutdown = bcm2835_i2s_shutdown,
+ .prepare = bcm2835_i2s_prepare,
+ .trigger = bcm2835_i2s_trigger,
+ .hw_params = bcm2835_i2s_hw_params,
+ .set_fmt = bcm2835_i2s_set_dai_fmt,
+ .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio
+};
+
+static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai,
+ &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
+ &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver bcm2835_i2s_dai = {
+ .name = "bcm2835-i2s",
+ .probe = bcm2835_i2s_dai_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S32_LE
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S32_LE
+ },
+ .ops = &bcm2835_i2s_dai_ops,
+ .symmetric_rates = 1
+};
+
+static bool bcm2835_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BCM2835_I2S_CS_A_REG:
+ case BCM2835_I2S_FIFO_A_REG:
+ case BCM2835_I2S_INTSTC_A_REG:
+ case BCM2835_I2S_GRAY_REG:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool bcm2835_i2s_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BCM2835_I2S_FIFO_A_REG:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool bcm2835_clk_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BCM2835_CLK_PCMCTL_REG:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config bcm2835_regmap_config[] = {
+ {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = BCM2835_I2S_GRAY_REG,
+ .precious_reg = bcm2835_i2s_precious_reg,
+ .volatile_reg = bcm2835_i2s_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+ },
+ {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = BCM2835_CLK_PCMDIV_REG,
+ .volatile_reg = bcm2835_clk_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+ },
+};
+
+static const struct snd_soc_component_driver bcm2835_i2s_component = {
+ .name = "bcm2835-i2s-comp",
+};
+
+static int bcm2835_i2s_probe(struct platform_device *pdev)
+{
+ struct bcm2835_i2s_dev *dev;
+ int i;
+ int ret;
+ struct regmap *regmap[2];
+ struct resource *mem[2];
+
+ /* Request both ioareas */
+ for (i = 0; i <= 1; i++) {
+ void __iomem *base;
+
+ mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ base = devm_ioremap_resource(&pdev->dev, mem[i]);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap[i] = devm_regmap_init_mmio(&pdev->dev, base,
+ &bcm2835_regmap_config[i]);
+ if (IS_ERR(regmap[i]))
+ return PTR_ERR(regmap[i]);
+ }
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev),
+ GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->i2s_regmap = regmap[0];
+ dev->clk_regmap = regmap[1];
+
+ /* Set the DMA address */
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
+ (dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG
+ + BCM2835_VCMMU_SHIFT;
+
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
+ (dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG
+ + BCM2835_VCMMU_SHIFT;
+
+ /* Set the bus width */
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ /* Set burst */
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2;
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2;
+
+ /* BCLK ratio - use default */
+ dev->bclk_ratio = 0;
+
+ /* Store the pdev */
+ dev->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &bcm2835_i2s_component, &bcm2835_i2s_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id bcm2835_i2s_of_match[] = {
+ { .compatible = "brcm,bcm2835-i2s", },
+ {},
+};
+
+static struct platform_driver bcm2835_i2s_driver = {
+ .probe = bcm2835_i2s_probe,
+ .driver = {
+ .name = "bcm2835-i2s",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm2835_i2s_of_match,
+ },
+};
+
+module_platform_driver(bcm2835_i2s_driver);
+
+MODULE_ALIAS("platform:bcm2835-i2s");
+MODULE_DESCRIPTION("BCM2835 I2S interface");
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
index 1d4c676eb6cc..cdb8ee75ded9 100644
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -107,7 +107,6 @@ static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
#endif
SNDRV_PCM_INFO_BLOCK_TRANSFER,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
.period_bytes_min = 32,
.period_bytes_max = 0x10000,
.periods_min = 1,
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
index 2a5b43417fd5..a3881c4381c9 100644
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -52,9 +52,6 @@ static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BLOCK_TRANSFER,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
.period_bytes_min = 32,
.period_bytes_max = 0x10000,
.periods_min = 1,
diff --git a/sound/soc/cirrus/edb93xx.c b/sound/soc/cirrus/edb93xx.c
index c43fb214558a..4f900efc437c 100644
--- a/sound/soc/cirrus/edb93xx.c
+++ b/sound/soc/cirrus/edb93xx.c
@@ -63,7 +63,7 @@ static struct snd_soc_ops edb93xx_ops = {
static struct snd_soc_dai_link edb93xx_dai = {
.name = "CS4271",
.stream_name = "CS4271 HiFi",
- .platform_name = "ep93xx-pcm-audio",
+ .platform_name = "ep93xx-i2s",
.cpu_dai_name = "ep93xx-i2s",
.codec_name = "spi0.0",
.codec_dai_name = "cs4271-hifi",
diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c
index efa75b5086a4..f30dadf85b99 100644
--- a/sound/soc/cirrus/ep93xx-ac97.c
+++ b/sound/soc/cirrus/ep93xx-ac97.c
@@ -19,11 +19,14 @@
#include <linux/slab.h>
#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
#include <sound/ac97_codec.h>
#include <sound/soc.h>
#include <linux/platform_data/dma-ep93xx.h>
+#include "ep93xx-pcm.h"
+
/*
* Per channel (1-4) registers.
*/
@@ -95,6 +98,8 @@ struct ep93xx_ac97_info {
struct device *dev;
void __iomem *regs;
struct completion done;
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
};
/* currently ALSA only supports a single AC97 device */
@@ -315,8 +320,13 @@ static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
static int ep93xx_ac97_dai_probe(struct snd_soc_dai *dai)
{
- dai->playback_dma_data = &ep93xx_ac97_pcm_out;
- dai->capture_dma_data = &ep93xx_ac97_pcm_in;
+ struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai);
+
+ info->dma_params_tx.filter_data = &ep93xx_ac97_pcm_out;
+ info->dma_params_rx.filter_data = &ep93xx_ac97_pcm_in;
+
+ dai->playback_dma_data = &info->dma_params_tx;
+ dai->capture_dma_data = &info->dma_params_rx;
return 0;
}
@@ -394,8 +404,14 @@ static int ep93xx_ac97_probe(struct platform_device *pdev)
if (ret)
goto fail;
+ ret = devm_ep93xx_pcm_platform_register(&pdev->dev);
+ if (ret)
+ goto fail_unregister;
+
return 0;
+fail_unregister:
+ snd_soc_unregister_component(&pdev->dev);
fail:
ep93xx_ac97_info = NULL;
snd_soc_set_ac97_ops(NULL);
diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c
index a57643d6402f..943145f9d1b6 100644
--- a/sound/soc/cirrus/ep93xx-i2s.c
+++ b/sound/soc/cirrus/ep93xx-i2s.c
@@ -21,6 +21,7 @@
#include <linux/io.h>
#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
@@ -30,6 +31,8 @@
#include <mach/ep93xx-regs.h>
#include <linux/platform_data/dma-ep93xx.h>
+#include "ep93xx-pcm.h"
+
#define EP93XX_I2S_TXCLKCFG 0x00
#define EP93XX_I2S_RXCLKCFG 0x04
#define EP93XX_I2S_GLCTRL 0x0C
@@ -61,6 +64,8 @@ struct ep93xx_i2s_info {
struct clk *sclk;
struct clk *lrclk;
void __iomem *regs;
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
};
static struct ep93xx_dma_data ep93xx_i2s_dma_data[] = {
@@ -140,8 +145,15 @@ static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai)
{
- dai->playback_dma_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_PLAYBACK];
- dai->capture_dma_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE];
+ struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
+
+ info->dma_params_tx.filter_data =
+ &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+ info->dma_params_rx.filter_data =
+ &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE];
+
+ dai->playback_dma_data = &info->dma_params_tx;
+ dai->capture_dma_data = &info->dma_params_rx;
return 0;
}
@@ -405,8 +417,14 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
if (err)
goto fail_put_lrclk;
+ err = devm_ep93xx_pcm_platform_register(&pdev->dev);
+ if (err)
+ goto fail_unregister;
+
return 0;
+fail_unregister:
+ snd_soc_unregister_component(&pdev->dev);
fail_put_lrclk:
clk_put(info->lrclk);
fail_put_sclk:
diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c
index cfe517e68009..5f664471d99e 100644
--- a/sound/soc/cirrus/ep93xx-pcm.c
+++ b/sound/soc/cirrus/ep93xx-pcm.c
@@ -23,20 +23,13 @@
#include <linux/platform_data/dma-ep93xx.h>
+#include "ep93xx-pcm.h"
+
static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER),
-
- .rates = SNDRV_PCM_RATE_8000_192000,
- .rate_min = SNDRV_PCM_RATE_8000,
- .rate_max = SNDRV_PCM_RATE_192000,
-
- .formats = (SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE),
-
.buffer_bytes_max = 131072,
.period_bytes_min = 32,
.period_bytes_max = 32768,
@@ -57,53 +50,22 @@ static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
return false;
}
-static struct dma_chan *ep93xx_compat_request_channel(
- struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_substream *substream)
-{
- struct snd_dmaengine_dai_dma_data *dma_data;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- return snd_dmaengine_pcm_request_channel(ep93xx_pcm_dma_filter,
- dma_data);
-}
-
static const struct snd_dmaengine_pcm_config ep93xx_dmaengine_pcm_config = {
.pcm_hardware = &ep93xx_pcm_hardware,
.compat_filter_fn = ep93xx_pcm_dma_filter,
- .compat_request_channel = ep93xx_compat_request_channel,
.prealloc_buffer_size = 131072,
};
-static int ep93xx_soc_platform_probe(struct platform_device *pdev)
+int devm_ep93xx_pcm_platform_register(struct device *dev)
{
- return snd_dmaengine_pcm_register(&pdev->dev,
+ return devm_snd_dmaengine_pcm_register(dev,
&ep93xx_dmaengine_pcm_config,
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
SND_DMAENGINE_PCM_FLAG_NO_DT |
SND_DMAENGINE_PCM_FLAG_COMPAT);
}
-
-static int ep93xx_soc_platform_remove(struct platform_device *pdev)
-{
- snd_dmaengine_pcm_unregister(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver ep93xx_pcm_driver = {
- .driver = {
- .name = "ep93xx-pcm-audio",
- .owner = THIS_MODULE,
- },
-
- .probe = ep93xx_soc_platform_probe,
- .remove = ep93xx_soc_platform_remove,
-};
-
-module_platform_driver(ep93xx_pcm_driver);
+EXPORT_SYMBOL_GPL(devm_ep93xx_pcm_platform_register);
MODULE_AUTHOR("Ryan Mallon");
MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ep93xx-pcm-audio");
diff --git a/sound/soc/cirrus/ep93xx-pcm.h b/sound/soc/cirrus/ep93xx-pcm.h
new file mode 100644
index 000000000000..b7a12a2fae9c
--- /dev/null
+++ b/sound/soc/cirrus/ep93xx-pcm.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef __EP93XX_PCM_H__
+#define __EP93XX_PCM_H__
+
+int devm_ep93xx_pcm_platform_register(struct device *dev);
+
+#endif
diff --git a/sound/soc/cirrus/simone.c b/sound/soc/cirrus/simone.c
index 4d094d00c34a..822a19a89e74 100644
--- a/sound/soc/cirrus/simone.c
+++ b/sound/soc/cirrus/simone.c
@@ -27,7 +27,7 @@ static struct snd_soc_dai_link simone_dai = {
.cpu_dai_name = "ep93xx-ac97",
.codec_dai_name = "ac97-hifi",
.codec_name = "ac97-codec",
- .platform_name = "ep93xx-pcm-audio",
+ .platform_name = "ep93xx-ac97",
};
static struct snd_soc_card snd_soc_simone = {
diff --git a/sound/soc/cirrus/snappercl15.c b/sound/soc/cirrus/snappercl15.c
index 69041074f2c1..29238a7476dd 100644
--- a/sound/soc/cirrus/snappercl15.c
+++ b/sound/soc/cirrus/snappercl15.c
@@ -83,7 +83,7 @@ static struct snd_soc_dai_link snappercl15_dai = {
.cpu_dai_name = "ep93xx-i2s",
.codec_dai_name = "tlv320aic23-hifi",
.codec_name = "tlv320aic23-codec.0-001a",
- .platform_name = "ep93xx-pcm-audio",
+ .platform_name = "ep93xx-i2s",
.init = snappercl15_tlv320aic23_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
SND_SOC_DAIFMT_CBS_CFS,
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index 9a92b7962f41..d7c983862cf0 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -179,6 +179,8 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream,
case SNDRV_PCM_FORMAT_S32_LE:
word_len = AD1836_WORD_LEN_24;
break;
+ default:
+ return -EINVAL;
}
regmap_update_bits(ad1836->regmap, AD1836_DAC_CTRL1,
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index aea7e52cf714..12c27eb363dd 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -413,7 +413,7 @@ static struct spi_driver ad193x_spi_driver = {
};
#endif
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static const struct regmap_config ad193x_i2c_regmap_config = {
.val_bits = 8,
@@ -470,7 +470,7 @@ static int __init ad193x_modinit(void)
{
int ret;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&ad193x_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register AD193X I2C driver: %d\n",
@@ -495,7 +495,7 @@ static void __exit ad193x_modexit(void)
spi_unregister_driver(&ad193x_spi_driver);
#endif
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&ad193x_i2c_driver);
#endif
}
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index 14a7c169d004..f7bf45552749 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -939,7 +939,7 @@ static struct spi_driver adav80x_spi_driver = {
};
#endif
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static const struct regmap_config adav80x_i2c_regmap_config = {
.val_bits = 8,
.pad_bits = 1,
@@ -985,7 +985,7 @@ static int __init adav80x_init(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&adav80x_i2c_driver);
if (ret)
return ret;
@@ -1001,7 +1001,7 @@ module_init(adav80x_init);
static void __exit adav80x_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&adav80x_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index 49cc5f6d6dba..94cbe508dd37 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -17,6 +17,7 @@
#include <linux/gpio.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -30,6 +31,7 @@
/* codec private data */
struct ak4641_priv {
+ struct regmap *regmap;
unsigned int sysclk;
int deemph;
int playback_fs;
@@ -38,12 +40,12 @@ struct ak4641_priv {
/*
* ak4641 register cache
*/
-static const u8 ak4641_reg[AK4641_CACHEREGNUM] = {
- 0x00, 0x80, 0x00, 0x80,
- 0x02, 0x00, 0x11, 0x05,
- 0x00, 0x00, 0x36, 0x10,
- 0x00, 0x00, 0x57, 0x00,
- 0x88, 0x88, 0x08, 0x08
+static const struct reg_default ak4641_reg_defaults[] = {
+ { 0, 0x00 }, { 1, 0x80 }, { 2, 0x00 }, { 3, 0x80 },
+ { 4, 0x02 }, { 5, 0x00 }, { 6, 0x11 }, { 7, 0x05 },
+ { 8, 0x00 }, { 9, 0x00 }, { 10, 0x36 }, { 11, 0x10 },
+ { 12, 0x00 }, { 13, 0x00 }, { 14, 0x57 }, { 15, 0x00 },
+ { 16, 0x88 }, { 17, 0x88 }, { 18, 0x08 }, { 19, 0x08 }
};
static const int deemph_settings[] = {44100, 0, 48000, 32000};
@@ -396,6 +398,7 @@ static int ak4641_mute(struct snd_soc_dai *dai, int mute)
static int ak4641_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
struct ak4641_platform_data *pdata = codec->dev->platform_data;
int ret;
@@ -417,7 +420,7 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec,
gpio_set_value(pdata->gpio_npdn, 1);
mdelay(1);
- ret = snd_soc_cache_sync(codec);
+ ret = regcache_sync(ak4641->regmap);
if (ret) {
dev_err(codec->dev,
"Failed to sync cache: %d\n", ret);
@@ -433,7 +436,7 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec,
gpio_set_value(pdata->gpio_npdn, 0);
if (pdata && gpio_is_valid(pdata->gpio_power))
gpio_set_value(pdata->gpio_power, 0);
- codec->cache_sync = 1;
+ regcache_mark_dirty(ak4641->regmap);
break;
}
codec->dapm.bias_level = level;
@@ -518,7 +521,7 @@ static int ak4641_probe(struct snd_soc_codec *codec)
{
int ret;
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
if (ret != 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
@@ -550,12 +553,17 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
.dapm_routes = ak4641_audio_map,
.num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
.set_bias_level = ak4641_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(ak4641_reg),
- .reg_word_size = sizeof(u8),
- .reg_cache_default = ak4641_reg,
- .reg_cache_step = 1,
};
+static const struct regmap_config ak4641_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK4641_BTIF,
+ .reg_defaults = ak4641_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4641_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
static int ak4641_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
@@ -569,6 +577,10 @@ static int ak4641_i2c_probe(struct i2c_client *i2c,
if (!ak4641)
return -ENOMEM;
+ ak4641->regmap = devm_regmap_init_i2c(i2c, &ak4641_regmap);
+ if (IS_ERR(ak4641->regmap))
+ return PTR_ERR(ak4641->regmap);
+
if (pdata) {
if (gpio_is_valid(pdata->gpio_power)) {
ret = gpio_request_one(pdata->gpio_power,
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 090d499bb7eb..1f646c6e90c6 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/of_device.h>
#include <linux/module.h>
+#include <linux/regmap.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
@@ -198,30 +199,30 @@ static const struct snd_soc_dapm_route ak4642_intercon[] = {
/*
* ak4642 register cache
*/
-static const u8 ak4642_reg[] = {
- 0x00, 0x00, 0x01, 0x00,
- 0x02, 0x00, 0x00, 0x00,
- 0xe1, 0xe1, 0x18, 0x00,
- 0xe1, 0x18, 0x11, 0x08,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00,
+static const struct reg_default ak4642_reg[] = {
+ { 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 },
+ { 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
+ { 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 },
+ { 12, 0xe1 }, { 13, 0x18 }, { 14, 0x11 }, { 15, 0x08 },
+ { 16, 0x00 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x00 },
+ { 20, 0x00 }, { 21, 0x00 }, { 22, 0x00 }, { 23, 0x00 },
+ { 24, 0x00 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0x00 },
+ { 28, 0x00 }, { 29, 0x00 }, { 30, 0x00 }, { 31, 0x00 },
+ { 32, 0x00 }, { 33, 0x00 }, { 34, 0x00 }, { 35, 0x00 },
+ { 36, 0x00 },
};
-static const u8 ak4648_reg[] = {
- 0x00, 0x00, 0x01, 0x00,
- 0x02, 0x00, 0x00, 0x00,
- 0xe1, 0xe1, 0x18, 0x00,
- 0xe1, 0x18, 0x11, 0xb8,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x88, 0x88, 0x08,
+static const struct reg_default ak4648_reg[] = {
+ { 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 },
+ { 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
+ { 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 },
+ { 12, 0xe1 }, { 13, 0x18 }, { 14, 0x11 }, { 15, 0xb8 },
+ { 16, 0x00 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x00 },
+ { 20, 0x00 }, { 21, 0x00 }, { 22, 0x00 }, { 23, 0x00 },
+ { 24, 0x00 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0x00 },
+ { 28, 0x00 }, { 29, 0x00 }, { 30, 0x00 }, { 31, 0x00 },
+ { 32, 0x00 }, { 33, 0x00 }, { 34, 0x00 }, { 35, 0x00 },
+ { 36, 0x00 }, { 37, 0x88 }, { 38, 0x88 }, { 39, 0x08 },
};
static int ak4642_dai_startup(struct snd_pcm_substream *substream,
@@ -454,7 +455,10 @@ static struct snd_soc_dai_driver ak4642_dai = {
static int ak4642_resume(struct snd_soc_codec *codec)
{
- snd_soc_cache_sync(codec);
+ struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+ regcache_mark_dirty(regmap);
+ regcache_sync(regmap);
return 0;
}
@@ -463,15 +467,12 @@ static int ak4642_probe(struct snd_soc_codec *codec)
{
int ret;
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
}
- snd_soc_add_codec_controls(codec, ak4642_snd_controls,
- ARRAY_SIZE(ak4642_snd_controls));
-
ak4642_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
@@ -488,55 +489,59 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
.remove = ak4642_remove,
.resume = ak4642_resume,
.set_bias_level = ak4642_set_bias_level,
- .reg_cache_default = ak4642_reg, /* ak4642 reg */
- .reg_cache_size = ARRAY_SIZE(ak4642_reg), /* ak4642 reg */
- .reg_word_size = sizeof(u8),
+ .controls = ak4642_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4642_snd_controls),
.dapm_widgets = ak4642_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak4642_dapm_widgets),
.dapm_routes = ak4642_intercon,
.num_dapm_routes = ARRAY_SIZE(ak4642_intercon),
};
-static struct snd_soc_codec_driver soc_codec_dev_ak4648 = {
- .probe = ak4642_probe,
- .remove = ak4642_remove,
- .resume = ak4642_resume,
- .set_bias_level = ak4642_set_bias_level,
- .reg_cache_default = ak4648_reg, /* ak4648 reg */
- .reg_cache_size = ARRAY_SIZE(ak4648_reg), /* ak4648 reg */
- .reg_word_size = sizeof(u8),
- .dapm_widgets = ak4642_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ak4642_dapm_widgets),
- .dapm_routes = ak4642_intercon,
- .num_dapm_routes = ARRAY_SIZE(ak4642_intercon),
+static const struct regmap_config ak4642_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ARRAY_SIZE(ak4642_reg) + 1,
+ .reg_defaults = ak4642_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak4642_reg),
+};
+
+static const struct regmap_config ak4648_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ARRAY_SIZE(ak4648_reg) + 1,
+ .reg_defaults = ak4648_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak4648_reg),
};
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static struct of_device_id ak4642_of_match[];
static int ak4642_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct device_node *np = i2c->dev.of_node;
- const struct snd_soc_codec_driver *driver;
+ const struct regmap_config *regmap_config = NULL;
+ struct regmap *regmap;
- driver = NULL;
if (np) {
const struct of_device_id *of_id;
of_id = of_match_device(ak4642_of_match, &i2c->dev);
if (of_id)
- driver = of_id->data;
+ regmap_config = of_id->data;
} else {
- driver = (struct snd_soc_codec_driver *)id->driver_data;
+ regmap_config = (const struct regmap_config *)id->driver_data;
}
- if (!driver) {
- dev_err(&i2c->dev, "no driver\n");
+ if (!regmap_config) {
+ dev_err(&i2c->dev, "Unknown device type\n");
return -EINVAL;
}
+ regmap = devm_regmap_init_i2c(i2c, regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
return snd_soc_register_codec(&i2c->dev,
- driver, &ak4642_dai, 1);
+ &soc_codec_dev_ak4642, &ak4642_dai, 1);
}
static int ak4642_i2c_remove(struct i2c_client *client)
@@ -546,17 +551,17 @@ static int ak4642_i2c_remove(struct i2c_client *client)
}
static struct of_device_id ak4642_of_match[] = {
- { .compatible = "asahi-kasei,ak4642", .data = &soc_codec_dev_ak4642},
- { .compatible = "asahi-kasei,ak4643", .data = &soc_codec_dev_ak4642},
- { .compatible = "asahi-kasei,ak4648", .data = &soc_codec_dev_ak4648},
+ { .compatible = "asahi-kasei,ak4642", .data = &ak4642_regmap},
+ { .compatible = "asahi-kasei,ak4643", .data = &ak4642_regmap},
+ { .compatible = "asahi-kasei,ak4648", .data = &ak4648_regmap},
{},
};
MODULE_DEVICE_TABLE(of, ak4642_of_match);
static const struct i2c_device_id ak4642_i2c_id[] = {
- { "ak4642", (kernel_ulong_t)&soc_codec_dev_ak4642 },
- { "ak4643", (kernel_ulong_t)&soc_codec_dev_ak4642 },
- { "ak4648", (kernel_ulong_t)&soc_codec_dev_ak4648 },
+ { "ak4642", (kernel_ulong_t)&ak4642_regmap },
+ { "ak4643", (kernel_ulong_t)&ak4642_regmap },
+ { "ak4648", (kernel_ulong_t)&ak4648_regmap },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id);
@@ -571,27 +576,8 @@ static struct i2c_driver ak4642_i2c_driver = {
.remove = ak4642_i2c_remove,
.id_table = ak4642_i2c_id,
};
-#endif
-
-static int __init ak4642_modinit(void)
-{
- int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- ret = i2c_add_driver(&ak4642_i2c_driver);
-#endif
- return ret;
-}
-module_init(ak4642_modinit);
-
-static void __exit ak4642_exit(void)
-{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_del_driver(&ak4642_i2c_driver);
-#endif
-
-}
-module_exit(ak4642_exit);
+module_i2c_driver(ak4642_i2c_driver);
MODULE_DESCRIPTION("Soc AK4642 driver");
MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index f6e953454bc0..ce05fd93dc74 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -675,7 +675,7 @@ static struct spi_driver cs4271_spi_driver = {
};
#endif /* defined(CONFIG_SPI_MASTER) */
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static const struct i2c_device_id cs4271_i2c_id[] = {
{"cs4271", 0},
{}
@@ -728,7 +728,7 @@ static struct i2c_driver cs4271_i2c_driver = {
.probe = cs4271_i2c_probe,
.remove = cs4271_i2c_remove,
};
-#endif /* defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) */
+#endif /* IS_ENABLED(CONFIG_I2C) */
/*
* We only register our serial bus driver here without
@@ -741,7 +741,7 @@ static int __init cs4271_modinit(void)
{
int ret;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&cs4271_i2c_driver);
if (ret) {
pr_err("Failed to register CS4271 I2C driver: %d\n", ret);
@@ -767,7 +767,7 @@ static void __exit cs4271_modexit(void)
spi_unregister_driver(&cs4271_spi_driver);
#endif
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&cs4271_i2c_driver);
#endif
}
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 8b427c977083..0bac6d5a4ac8 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -17,7 +17,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/input.h>
@@ -50,7 +50,7 @@ struct cs42l52_private {
u8 mclksel;
u32 mclk;
u8 flags;
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+#if IS_ENABLED(CONFIG_INPUT)
struct input_dev *beep;
struct work_struct beep_work;
int beep_rate;
@@ -233,7 +233,7 @@ static const struct soc_enum mic_bias_level_enum =
SOC_ENUM_SINGLE(CS42L52_IFACE_CTL2, 0,
ARRAY_SIZE(mic_bias_level_text), mic_bias_level_text);
-static const char * const cs42l52_mic_text[] = { "Single", "Differential" };
+static const char * const cs42l52_mic_text[] = { "MIC1", "MIC2" };
static const struct soc_enum mica_enum =
SOC_ENUM_SINGLE(CS42L52_MICA_CTL, 5,
@@ -243,12 +243,6 @@ static const struct soc_enum micb_enum =
SOC_ENUM_SINGLE(CS42L52_MICB_CTL, 5,
ARRAY_SIZE(cs42l52_mic_text), cs42l52_mic_text);
-static const struct snd_kcontrol_new mica_mux =
- SOC_DAPM_ENUM("Left Mic Input Capture Mux", mica_enum);
-
-static const struct snd_kcontrol_new micb_mux =
- SOC_DAPM_ENUM("Right Mic Input Capture Mux", micb_enum);
-
static const char * const digital_output_mux_text[] = {"ADC", "DSP"};
static const struct soc_enum digital_output_mux_enum =
@@ -531,6 +525,30 @@ static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
};
+static const struct snd_kcontrol_new cs42l52_mica_controls[] = {
+ SOC_ENUM("MICA Select", mica_enum),
+};
+
+static const struct snd_kcontrol_new cs42l52_micb_controls[] = {
+ SOC_ENUM("MICB Select", micb_enum),
+};
+
+static int cs42l52_add_mic_controls(struct snd_soc_codec *codec)
+{
+ struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l52_platform_data *pdata = &cs42l52->pdata;
+
+ if (!pdata->mica_diff_cfg)
+ snd_soc_add_codec_controls(codec, cs42l52_mica_controls,
+ ARRAY_SIZE(cs42l52_mica_controls));
+
+ if (!pdata->micb_diff_cfg)
+ snd_soc_add_codec_controls(codec, cs42l52_micb_controls,
+ ARRAY_SIZE(cs42l52_micb_controls));
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget cs42l52_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AIN1L"),
@@ -550,9 +568,6 @@ static const struct snd_soc_dapm_widget cs42l52_dapm_widgets[] = {
SND_SOC_DAPM_AIF_OUT("AIFOUTR", NULL, 0,
SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_MUX("MICA Mux", SND_SOC_NOPM, 0, 0, &mica_mux),
- SND_SOC_DAPM_MUX("MICB Mux", SND_SOC_NOPM, 0, 0, &micb_mux),
-
SND_SOC_DAPM_ADC("ADC Left", NULL, CS42L52_PWRCTL1, 1, 1),
SND_SOC_DAPM_ADC("ADC Right", NULL, CS42L52_PWRCTL1, 2, 1),
SND_SOC_DAPM_PGA("PGA Left", CS42L52_PWRCTL1, 3, 1, NULL, 0),
@@ -953,7 +968,7 @@ static int cs42l52_resume(struct snd_soc_codec *codec)
return 0;
}
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+#if IS_ENABLED(CONFIG_INPUT)
static int beep_rates[] = {
261, 522, 585, 667, 706, 774, 889, 1000,
1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182
@@ -1110,6 +1125,8 @@ static int cs42l52_probe(struct snd_soc_codec *codec)
}
regcache_cache_only(cs42l52->regmap, true);
+ cs42l52_add_mic_controls(codec);
+
cs42l52_init_beep(codec);
cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1176,6 +1193,7 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
int ret;
unsigned int devid = 0;
unsigned int reg;
+ u32 val32;
cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l52_private),
GFP_KERNEL);
@@ -1189,9 +1207,39 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
return ret;
}
-
- if (pdata)
+ if (pdata) {
+ cs42l52->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(&i2c_client->dev,
+ sizeof(struct cs42l52_platform_data),
+ GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&i2c_client->dev, "could not allocate pdata\n");
+ return -ENOMEM;
+ }
+ if (i2c_client->dev.of_node) {
+ if (of_property_read_bool(i2c_client->dev.of_node,
+ "cirrus,mica-differential-cfg"))
+ pdata->mica_diff_cfg = true;
+
+ if (of_property_read_bool(i2c_client->dev.of_node,
+ "cirrus,micb-differential-cfg"))
+ pdata->micb_diff_cfg = true;
+
+ if (of_property_read_u32(i2c_client->dev.of_node,
+ "cirrus,micbias-lvl", &val32) >= 0)
+ pdata->micbias_lvl = val32;
+
+ if (of_property_read_u32(i2c_client->dev.of_node,
+ "cirrus,chgfreq-divisor", &val32) >= 0)
+ pdata->chgfreq = val32;
+
+ pdata->reset_gpio =
+ of_get_named_gpio(i2c_client->dev.of_node,
+ "cirrus,reset-gpio", 0);
+ }
cs42l52->pdata = *pdata;
+ }
if (cs42l52->pdata.reset_gpio) {
ret = gpio_request_one(cs42l52->pdata.reset_gpio,
@@ -1227,29 +1275,18 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
reg & 0xFF);
/* Set Platform Data */
- if (cs42l52->pdata.mica_cfg)
+ if (cs42l52->pdata.mica_diff_cfg)
regmap_update_bits(cs42l52->regmap, CS42L52_MICA_CTL,
CS42L52_MIC_CTL_TYPE_MASK,
- cs42l52->pdata.mica_cfg <<
+ cs42l52->pdata.mica_diff_cfg <<
CS42L52_MIC_CTL_TYPE_SHIFT);
- if (cs42l52->pdata.micb_cfg)
+ if (cs42l52->pdata.micb_diff_cfg)
regmap_update_bits(cs42l52->regmap, CS42L52_MICB_CTL,
CS42L52_MIC_CTL_TYPE_MASK,
- cs42l52->pdata.micb_cfg <<
+ cs42l52->pdata.micb_diff_cfg <<
CS42L52_MIC_CTL_TYPE_SHIFT);
- if (cs42l52->pdata.mica_sel)
- regmap_update_bits(cs42l52->regmap, CS42L52_MICA_CTL,
- CS42L52_MIC_CTL_MIC_SEL_MASK,
- cs42l52->pdata.mica_sel <<
- CS42L52_MIC_CTL_MIC_SEL_SHIFT);
- if (cs42l52->pdata.micb_sel)
- regmap_update_bits(cs42l52->regmap, CS42L52_MICB_CTL,
- CS42L52_MIC_CTL_MIC_SEL_MASK,
- cs42l52->pdata.micb_sel <<
- CS42L52_MIC_CTL_MIC_SEL_SHIFT);
-
if (cs42l52->pdata.chgfreq)
regmap_update_bits(cs42l52->regmap, CS42L52_CHARGE_PUMP,
CS42L52_CHARGE_PUMP_MASK,
@@ -1274,6 +1311,13 @@ static int cs42l52_i2c_remove(struct i2c_client *client)
return 0;
}
+static const struct of_device_id cs42l52_of_match[] = {
+ { .compatible = "cirrus,cs42l52", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs42l52_of_match);
+
+
static const struct i2c_device_id cs42l52_id[] = {
{ "cs42l52", 0 },
{ }
@@ -1284,6 +1328,7 @@ static struct i2c_driver cs42l52_i2c_driver = {
.driver = {
.name = "cs42l52",
.owner = THIS_MODULE,
+ .of_match_table = cs42l52_of_match,
},
.id_table = cs42l52_id,
.probe = cs42l52_i2c_probe,
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
index 9c1231456502..8166dcb2e4a3 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -1188,7 +1188,7 @@ static struct snd_soc_codec_driver soc_codec_dev_da7210 = {
.num_dapm_routes = ARRAY_SIZE(da7210_audio_map),
};
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static struct reg_default da7210_regmap_i2c_patch[] = {
@@ -1362,7 +1362,7 @@ static struct spi_driver da7210_spi_driver = {
static int __init da7210_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&da7210_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
@@ -1378,7 +1378,7 @@ module_init(da7210_modinit);
static void __exit da7210_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&da7210_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c
index 68342b121c96..9cb1c7d3e1dc 100644
--- a/sound/soc/codecs/hdmi.c
+++ b/sound/soc/codecs/hdmi.c
@@ -20,6 +20,7 @@
*/
#include <linux/module.h>
#include <sound/soc.h>
+#include <linux/of_device.h>
#define DRV_NAME "hdmi-audio-codec"
@@ -44,7 +45,7 @@ static struct snd_soc_dai_driver hdmi_codec_dai = {
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
},
.capture = {
.stream_name = "Capture",
@@ -60,6 +61,14 @@ static struct snd_soc_dai_driver hdmi_codec_dai = {
};
+#ifdef CONFIG_OF
+static const struct of_device_id hdmi_audio_codec_ids[] = {
+ { .compatible = "linux,hdmi-audio", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hdmi_audio_codec_ids);
+#endif
+
static struct snd_soc_codec_driver hdmi_codec = {
.dapm_widgets = hdmi_widgets,
.num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
@@ -83,6 +92,7 @@ static struct platform_driver hdmi_codec_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(hdmi_audio_codec_ids),
},
.probe = hdmi_codec_probe,
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 1f4093f3f3a1..0fcbe90f3ef2 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -115,6 +115,7 @@ struct sgtl5000_priv {
struct ldo_regulator *ldo;
struct regmap *regmap;
struct clk *mclk;
+ int revision;
};
/*
@@ -1285,41 +1286,45 @@ static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec)
sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
-
- if (ret) {
- ldo_regulator_remove(codec);
- dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
- return ret;
- }
-
dev_info(codec->dev, "Using internal LDO instead of VDDD\n");
return 0;
}
static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
{
- int reg;
int ret;
- int rev;
int i;
int external_vddd = 0;
struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct regulator *vddd;
for (i = 0; i < ARRAY_SIZE(sgtl5000->supplies); i++)
sgtl5000->supplies[i].supply = supply_names[i];
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- if (!ret)
- external_vddd = 1;
- else {
+ /* External VDDD only works before revision 0x11 */
+ if (sgtl5000->revision < 0x11) {
+ vddd = regulator_get_optional(codec->dev, "VDDD");
+ if (IS_ERR(vddd)) {
+ /* See if it's just not registered yet */
+ if (PTR_ERR(vddd) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ } else {
+ external_vddd = 1;
+ regulator_put(vddd);
+ }
+ }
+
+ if (!external_vddd) {
ret = sgtl5000_replace_vddd_with_ldo(codec);
if (ret)
return ret;
}
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->supplies);
+ if (ret)
+ goto err_ldo_remove;
+
ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
if (ret)
@@ -1328,47 +1333,13 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
/* wait for all power rails bring up */
udelay(10);
- /*
- * workaround for revision 0x11 and later,
- * roll back to use internal LDO
- */
-
- ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, &reg);
- if (ret)
- goto err_regulator_disable;
-
- rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
-
- if (external_vddd && rev >= 0x11) {
- /* disable all regulator first */
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- /* free VDDD regulator */
- regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
-
- ret = sgtl5000_replace_vddd_with_ldo(codec);
- if (ret)
- return ret;
-
- ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- if (ret)
- goto err_regulator_free;
-
- /* wait for all power rails bring up */
- udelay(10);
- }
-
return 0;
-err_regulator_disable:
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
err_regulator_free:
regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
- if (external_vddd)
+err_ldo_remove:
+ if (!external_vddd)
ldo_regulator_remove(codec);
return ret;
@@ -1566,6 +1537,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev);
+ sgtl5000->revision = rev;
i2c_set_clientdata(client, sgtl5000);
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
index 95aed552139a..cc8debce752f 100644
--- a/sound/soc/codecs/ssm2518.c
+++ b/sound/soc/codecs/ssm2518.c
@@ -549,13 +549,13 @@ static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
right_slot = 0;
} else {
/* We assume the left channel < right channel */
- left_slot = ffs(tx_mask);
- tx_mask &= ~(1 << tx_mask);
+ left_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << left_slot);
if (tx_mask == 0) {
right_slot = left_slot;
} else {
- right_slot = ffs(tx_mask);
- tx_mask &= ~(1 << tx_mask);
+ right_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << right_slot);
}
}
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 492644e67ace..c6dd48561884 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -53,8 +53,6 @@ enum ssm2602_type {
struct ssm2602_priv {
unsigned int sysclk;
struct snd_pcm_hw_constraint_list *sysclk_constraints;
- struct snd_pcm_substream *master_substream;
- struct snd_pcm_substream *slave_substream;
struct regmap *regmap;
@@ -277,11 +275,6 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
unsigned int iface;
- if (substream == ssm2602->slave_substream) {
- dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n");
- return 0;
- }
-
if (srate < 0)
return srate;
@@ -314,33 +307,6 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
{
struct snd_soc_codec *codec = dai->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- struct snd_pcm_runtime *master_runtime;
-
- /* The DAI has shared clocks so if we already have a playback or
- * capture going then constrain this substream to match it.
- * TODO: the ssm2602 allows pairs of non-matching PB/REC rates
- */
- if (ssm2602->master_substream) {
- master_runtime = ssm2602->master_substream->runtime;
- dev_dbg(codec->dev, "Constraining to %d bits at %dHz\n",
- master_runtime->sample_bits,
- master_runtime->rate);
-
- if (master_runtime->rate != 0)
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE,
- master_runtime->rate,
- master_runtime->rate);
-
- if (master_runtime->sample_bits != 0)
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- master_runtime->sample_bits,
- master_runtime->sample_bits);
-
- ssm2602->slave_substream = substream;
- } else
- ssm2602->master_substream = substream;
if (ssm2602->sysclk_constraints) {
snd_pcm_hw_constraint_list(substream->runtime, 0,
@@ -351,19 +317,6 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
return 0;
}
-static void ssm2602_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
-
- if (ssm2602->master_substream == substream)
- ssm2602->master_substream = ssm2602->slave_substream;
-
- ssm2602->slave_substream = NULL;
-}
-
-
static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
{
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(dai->codec);
@@ -530,7 +483,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops ssm2602_dai_ops = {
.startup = ssm2602_startup,
.hw_params = ssm2602_hw_params,
- .shutdown = ssm2602_shutdown,
.digital_mute = ssm2602_mute,
.set_sysclk = ssm2602_set_dai_sysclk,
.set_fmt = ssm2602_set_dai_fmt,
@@ -551,6 +503,8 @@ static struct snd_soc_dai_driver ssm2602_dai = {
.rates = SSM2602_RATES,
.formats = SSM2602_FORMATS,},
.ops = &ssm2602_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
};
static int ssm2602_suspend(struct snd_soc_codec *codec)
@@ -730,7 +684,7 @@ static struct spi_driver ssm2602_spi_driver = {
};
#endif
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
/*
* ssm2602 2 wire address is determined by GPIO5
* state during powerup.
@@ -797,7 +751,7 @@ static int __init ssm2602_modinit(void)
return ret;
#endif
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&ssm2602_i2c_driver);
if (ret)
return ret;
@@ -813,7 +767,7 @@ static void __exit ssm2602_exit(void)
spi_unregister_driver(&ssm2602_spi_driver);
#endif
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&ssm2602_i2c_driver);
#endif
}
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 546d16b7d38f..470fbfb4b386 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -350,16 +350,6 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
DACL1_2_LLOPM_VOL, DACR1_2_RLOPM_VOL,
0, 118, 1, output_stage_tlv),
- SOC_DOUBLE_R_TLV("Mono Line2 Bypass Volume",
- LINE2L_2_MONOLOPM_VOL, LINE2R_2_MONOLOPM_VOL,
- 0, 118, 1, output_stage_tlv),
- SOC_DOUBLE_R_TLV("Mono PGA Bypass Volume",
- PGAL_2_MONOLOPM_VOL, PGAR_2_MONOLOPM_VOL,
- 0, 118, 1, output_stage_tlv),
- SOC_DOUBLE_R_TLV("Mono DAC Playback Volume",
- DACL1_2_MONOLOPM_VOL, DACR1_2_MONOLOPM_VOL,
- 0, 118, 1, output_stage_tlv),
-
SOC_DOUBLE_R_TLV("HP Line2 Bypass Volume",
LINE2L_2_HPLOUT_VOL, LINE2R_2_HPROUT_VOL,
0, 118, 1, output_stage_tlv),
@@ -383,7 +373,6 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
/* Output pin mute controls */
SOC_DOUBLE_R("Line Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3,
0x01, 0),
- SOC_SINGLE("Mono Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0),
SOC_DOUBLE_R("HP Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,
0x01, 0),
SOC_DOUBLE_R("HPCOM Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3,
@@ -412,6 +401,20 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
};
+static const struct snd_kcontrol_new aic3x_mono_controls[] = {
+ SOC_DOUBLE_R_TLV("Mono Line2 Bypass Volume",
+ LINE2L_2_MONOLOPM_VOL, LINE2R_2_MONOLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
+ SOC_DOUBLE_R_TLV("Mono PGA Bypass Volume",
+ PGAL_2_MONOLOPM_VOL, PGAR_2_MONOLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
+ SOC_DOUBLE_R_TLV("Mono DAC Playback Volume",
+ DACL1_2_MONOLOPM_VOL, DACR1_2_MONOLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
+
+ SOC_SINGLE("Mono Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0),
+};
+
/*
* Class-D amplifier gain. From 0 to 18 dB in 6 dB steps
*/
@@ -565,9 +568,6 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
SND_SOC_DAPM_PGA("Right HP Out", HPROUT_CTRL, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right HP Com", HPRCOM_CTRL, 0, 0, NULL, 0),
- /* Mono Output */
- SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0),
-
/* Inputs to Left ADC */
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0),
SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0,
@@ -626,9 +626,6 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Right Line Mixer", SND_SOC_NOPM, 0, 0,
&aic3x_right_line_mixer_controls[0],
ARRAY_SIZE(aic3x_right_line_mixer_controls)),
- SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0,
- &aic3x_mono_mixer_controls[0],
- ARRAY_SIZE(aic3x_mono_mixer_controls)),
SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
&aic3x_left_hp_mixer_controls[0],
ARRAY_SIZE(aic3x_left_hp_mixer_controls)),
@@ -644,7 +641,6 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("LLOUT"),
SND_SOC_DAPM_OUTPUT("RLOUT"),
- SND_SOC_DAPM_OUTPUT("MONO_LOUT"),
SND_SOC_DAPM_OUTPUT("HPLOUT"),
SND_SOC_DAPM_OUTPUT("HPROUT"),
SND_SOC_DAPM_OUTPUT("HPLCOM"),
@@ -666,6 +662,17 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("Detection"),
};
+static const struct snd_soc_dapm_widget aic3x_dapm_mono_widgets[] = {
+ /* Mono Output */
+ SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0,
+ &aic3x_mono_mixer_controls[0],
+ ARRAY_SIZE(aic3x_mono_mixer_controls)),
+
+ SND_SOC_DAPM_OUTPUT("MONO_LOUT"),
+};
+
static const struct snd_soc_dapm_widget aic3007_dapm_widgets[] = {
/* Class-D outputs */
SND_SOC_DAPM_PGA("Left Class-D Out", CLASSD_CTRL, 3, 0, NULL, 0),
@@ -754,17 +761,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Right Line Out", NULL, "Right DAC Mux"},
{"RLOUT", NULL, "Right Line Out"},
- /* Mono Output */
- {"Mono Mixer", "Line2L Bypass Switch", "Left Line2L Mux"},
- {"Mono Mixer", "PGAL Bypass Switch", "Left PGA Mixer"},
- {"Mono Mixer", "DACL1 Switch", "Left DAC Mux"},
- {"Mono Mixer", "Line2R Bypass Switch", "Right Line2R Mux"},
- {"Mono Mixer", "PGAR Bypass Switch", "Right PGA Mixer"},
- {"Mono Mixer", "DACR1 Switch", "Right DAC Mux"},
-
- {"Mono Out", NULL, "Mono Mixer"},
- {"MONO_LOUT", NULL, "Mono Out"},
-
/* Left HP Output */
{"Left HP Mixer", "Line2L Bypass Switch", "Left Line2L Mux"},
{"Left HP Mixer", "PGAL Bypass Switch", "Left PGA Mixer"},
@@ -820,6 +816,18 @@ static const struct snd_soc_dapm_route intercon[] = {
{"HPRCOM", NULL, "Right HP Com"},
};
+static const struct snd_soc_dapm_route intercon_mono[] = {
+ /* Mono Output */
+ {"Mono Mixer", "Line2L Bypass Switch", "Left Line2L Mux"},
+ {"Mono Mixer", "PGAL Bypass Switch", "Left PGA Mixer"},
+ {"Mono Mixer", "DACL1 Switch", "Left DAC Mux"},
+ {"Mono Mixer", "Line2R Bypass Switch", "Right Line2R Mux"},
+ {"Mono Mixer", "PGAR Bypass Switch", "Right PGA Mixer"},
+ {"Mono Mixer", "DACR1 Switch", "Right DAC Mux"},
+ {"Mono Out", NULL, "Mono Mixer"},
+ {"MONO_LOUT", NULL, "Mono Out"},
+};
+
static const struct snd_soc_dapm_route intercon_3007[] = {
/* Class-D outputs */
{"Left Class-D Out", NULL, "Left Line Out"},
@@ -833,11 +841,20 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec)
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
- if (aic3x->model == AIC3X_MODEL_3007) {
+ switch (aic3x->model) {
+ case AIC3X_MODEL_3X:
+ case AIC3X_MODEL_33:
+ snd_soc_dapm_new_controls(dapm, aic3x_dapm_mono_widgets,
+ ARRAY_SIZE(aic3x_dapm_mono_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon_mono,
+ ARRAY_SIZE(intercon_mono));
+ break;
+ case AIC3X_MODEL_3007:
snd_soc_dapm_new_controls(dapm, aic3007_dapm_widgets,
ARRAY_SIZE(aic3007_dapm_widgets));
snd_soc_dapm_add_routes(dapm, intercon_3007,
ARRAY_SIZE(intercon_3007));
+ break;
}
return 0;
@@ -1218,6 +1235,24 @@ static int aic3x_resume(struct snd_soc_codec *codec)
return 0;
}
+static void aic3x_mono_init(struct snd_soc_codec *codec)
+{
+ /* DAC to Mono Line Out default volume and route to Output mixer */
+ snd_soc_write(codec, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+ snd_soc_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
+
+ /* unmute all outputs */
+ snd_soc_update_bits(codec, MONOLOPM_CTRL, UNMUTE, UNMUTE);
+
+ /* PGA to Mono Line Out default volume, disconnect from Output Mixer */
+ snd_soc_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL);
+
+ /* Line2 to Mono Out default volume, disconnect from Output Mixer */
+ snd_soc_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
+ snd_soc_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
+}
+
/*
* initialise the AIC3X driver
* register the mixer and dsp interfaces with the kernel
@@ -1241,14 +1276,10 @@ static int aic3x_init(struct snd_soc_codec *codec)
/* DAC to Line Out default volume and route to Output mixer */
snd_soc_write(codec, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
snd_soc_write(codec, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
- /* DAC to Mono Line Out default volume and route to Output mixer */
- snd_soc_write(codec, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
- snd_soc_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON);
/* unmute all outputs */
snd_soc_update_bits(codec, LLOPM_CTRL, UNMUTE, UNMUTE);
snd_soc_update_bits(codec, RLOPM_CTRL, UNMUTE, UNMUTE);
- snd_soc_update_bits(codec, MONOLOPM_CTRL, UNMUTE, UNMUTE);
snd_soc_update_bits(codec, HPLOUT_CTRL, UNMUTE, UNMUTE);
snd_soc_update_bits(codec, HPROUT_CTRL, UNMUTE, UNMUTE);
snd_soc_update_bits(codec, HPLCOM_CTRL, UNMUTE, UNMUTE);
@@ -1269,9 +1300,6 @@ static int aic3x_init(struct snd_soc_codec *codec)
/* PGA to Line Out default volume, disconnect from Output Mixer */
snd_soc_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL);
snd_soc_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL);
- /* PGA to Mono Line Out default volume, disconnect from Output Mixer */
- snd_soc_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL);
- snd_soc_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL);
/* Line2 to HP Bypass default volume, disconnect from Output Mixer */
snd_soc_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL);
@@ -1281,12 +1309,15 @@ static int aic3x_init(struct snd_soc_codec *codec)
/* Line2 Line Out default volume, disconnect from Output Mixer */
snd_soc_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL);
snd_soc_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL);
- /* Line2 to Mono Out default volume, disconnect from Output Mixer */
- snd_soc_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL);
- snd_soc_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
- if (aic3x->model == AIC3X_MODEL_3007) {
+ switch (aic3x->model) {
+ case AIC3X_MODEL_3X:
+ case AIC3X_MODEL_33:
+ aic3x_mono_init(codec);
+ break;
+ case AIC3X_MODEL_3007:
snd_soc_write(codec, CLASSD_CTRL, 0);
+ break;
}
return 0;
@@ -1343,8 +1374,17 @@ static int aic3x_probe(struct snd_soc_codec *codec)
(aic3x->setup->gpio_func[1] & 0xf) << 4);
}
- if (aic3x->model == AIC3X_MODEL_3007)
- snd_soc_add_codec_controls(codec, &aic3x_classd_amp_gain_ctrl, 1);
+ switch (aic3x->model) {
+ case AIC3X_MODEL_3X:
+ case AIC3X_MODEL_33:
+ snd_soc_add_codec_controls(codec, aic3x_mono_controls,
+ ARRAY_SIZE(aic3x_mono_controls));
+ break;
+ case AIC3X_MODEL_3007:
+ snd_soc_add_codec_controls(codec,
+ &aic3x_classd_amp_gain_ctrl, 1);
+ break;
+ }
/* set mic bias voltage */
switch (aic3x->micbias_vg) {
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index f2f4bcb2ff71..0afe8bef6765 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -72,6 +72,7 @@ struct twl6040_data {
int hs_power_mode_locked;
bool dl1_unmuted;
bool dl2_unmuted;
+ u8 dl12_cache[TWL6040_REG_HFRCTL - TWL6040_REG_HSLCTL + 1];
unsigned int clk_in;
unsigned int sysclk;
struct twl6040_jack_data hs_jack;
@@ -79,75 +80,6 @@ struct twl6040_data {
struct mutex mutex;
};
-/*
- * twl6040 register cache & default register settings
- */
-static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = {
- 0x00, /* not used 0x00 */
- 0x4B, /* REG_ASICID 0x01 (ro) */
- 0x00, /* REG_ASICREV 0x02 (ro) */
- 0x00, /* REG_INTID 0x03 */
- 0x00, /* REG_INTMR 0x04 */
- 0x00, /* REG_NCPCTRL 0x05 */
- 0x00, /* REG_LDOCTL 0x06 */
- 0x60, /* REG_HPPLLCTL 0x07 */
- 0x00, /* REG_LPPLLCTL 0x08 */
- 0x4A, /* REG_LPPLLDIV 0x09 */
- 0x00, /* REG_AMICBCTL 0x0A */
- 0x00, /* REG_DMICBCTL 0x0B */
- 0x00, /* REG_MICLCTL 0x0C */
- 0x00, /* REG_MICRCTL 0x0D */
- 0x00, /* REG_MICGAIN 0x0E */
- 0x1B, /* REG_LINEGAIN 0x0F */
- 0x00, /* REG_HSLCTL 0x10 */
- 0x00, /* REG_HSRCTL 0x11 */
- 0x00, /* REG_HSGAIN 0x12 */
- 0x00, /* REG_EARCTL 0x13 */
- 0x00, /* REG_HFLCTL 0x14 */
- 0x00, /* REG_HFLGAIN 0x15 */
- 0x00, /* REG_HFRCTL 0x16 */
- 0x00, /* REG_HFRGAIN 0x17 */
- 0x00, /* REG_VIBCTLL 0x18 */
- 0x00, /* REG_VIBDATL 0x19 */
- 0x00, /* REG_VIBCTLR 0x1A */
- 0x00, /* REG_VIBDATR 0x1B */
- 0x00, /* REG_HKCTL1 0x1C */
- 0x00, /* REG_HKCTL2 0x1D */
- 0x00, /* REG_GPOCTL 0x1E */
- 0x00, /* REG_ALB 0x1F */
- 0x00, /* REG_DLB 0x20 */
- 0x00, /* not used 0x21 */
- 0x00, /* not used 0x22 */
- 0x00, /* not used 0x23 */
- 0x00, /* not used 0x24 */
- 0x00, /* not used 0x25 */
- 0x00, /* not used 0x26 */
- 0x00, /* not used 0x27 */
- 0x00, /* REG_TRIM1 0x28 */
- 0x00, /* REG_TRIM2 0x29 */
- 0x00, /* REG_TRIM3 0x2A */
- 0x00, /* REG_HSOTRIM 0x2B */
- 0x00, /* REG_HFOTRIM 0x2C */
- 0x09, /* REG_ACCCTL 0x2D */
- 0x00, /* REG_STATUS 0x2E (ro) */
-};
-
-/* List of registers to be restored after power up */
-static const int twl6040_restore_list[] = {
- TWL6040_REG_MICLCTL,
- TWL6040_REG_MICRCTL,
- TWL6040_REG_MICGAIN,
- TWL6040_REG_LINEGAIN,
- TWL6040_REG_HSLCTL,
- TWL6040_REG_HSRCTL,
- TWL6040_REG_HSGAIN,
- TWL6040_REG_EARCTL,
- TWL6040_REG_HFLCTL,
- TWL6040_REG_HFLGAIN,
- TWL6040_REG_HFRCTL,
- TWL6040_REG_HFRGAIN,
-};
-
/* set of rates for each pll: low-power and high-performance */
static unsigned int lp_rates[] = {
8000,
@@ -174,53 +106,33 @@ static struct snd_pcm_hw_constraint_list sysclk_constraints[] = {
{ .count = ARRAY_SIZE(hp_rates), .list = hp_rates, },
};
-/*
- * read twl6040 register cache
- */
-static inline unsigned int twl6040_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u8 *cache = codec->reg_cache;
-
- if (reg >= TWL6040_CACHEREGNUM)
- return -EIO;
-
- return cache[reg];
-}
-
-/*
- * write twl6040 register cache
- */
-static inline void twl6040_write_reg_cache(struct snd_soc_codec *codec,
- u8 reg, u8 value)
-{
- u8 *cache = codec->reg_cache;
-
- if (reg >= TWL6040_CACHEREGNUM)
- return;
- cache[reg] = value;
-}
-
-/*
- * read from twl6040 hardware register
- */
-static int twl6040_read_reg_volatile(struct snd_soc_codec *codec,
- unsigned int reg)
+static unsigned int twl6040_read(struct snd_soc_codec *codec, unsigned int reg)
{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
struct twl6040 *twl6040 = codec->control_data;
u8 value;
if (reg >= TWL6040_CACHEREGNUM)
return -EIO;
- value = twl6040_reg_read(twl6040, reg);
- twl6040_write_reg_cache(codec, reg, value);
+ switch (reg) {
+ case TWL6040_REG_HSLCTL:
+ case TWL6040_REG_HSRCTL:
+ case TWL6040_REG_EARCTL:
+ case TWL6040_REG_HFLCTL:
+ case TWL6040_REG_HFRCTL:
+ value = priv->dl12_cache[reg - TWL6040_REG_HSLCTL];
+ break;
+ default:
+ value = twl6040_reg_read(twl6040, reg);
+ break;
+ }
return value;
}
-static bool twl6040_is_path_unmuted(struct snd_soc_codec *codec,
- unsigned int reg)
+static bool twl6040_can_write_to_chip(struct snd_soc_codec *codec,
+ unsigned int reg)
{
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
@@ -238,9 +150,24 @@ static bool twl6040_is_path_unmuted(struct snd_soc_codec *codec,
}
}
-/*
- * write to the twl6040 register space
- */
+static inline void twl6040_update_dl12_cache(struct snd_soc_codec *codec,
+ u8 reg, u8 value)
+{
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (reg) {
+ case TWL6040_REG_HSLCTL:
+ case TWL6040_REG_HSRCTL:
+ case TWL6040_REG_EARCTL:
+ case TWL6040_REG_HFLCTL:
+ case TWL6040_REG_HFRCTL:
+ priv->dl12_cache[reg - TWL6040_REG_HSLCTL] = value;
+ break;
+ default:
+ break;
+ }
+}
+
static int twl6040_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value)
{
@@ -249,8 +176,8 @@ static int twl6040_write(struct snd_soc_codec *codec,
if (reg >= TWL6040_CACHEREGNUM)
return -EIO;
- twl6040_write_reg_cache(codec, reg, value);
- if (twl6040_is_path_unmuted(codec, reg))
+ twl6040_update_dl12_cache(codec, reg, value);
+ if (twl6040_can_write_to_chip(codec, reg))
return twl6040_reg_write(twl6040, reg, value);
else
return 0;
@@ -258,45 +185,27 @@ static int twl6040_write(struct snd_soc_codec *codec,
static void twl6040_init_chip(struct snd_soc_codec *codec)
{
- struct twl6040 *twl6040 = codec->control_data;
- u8 val;
-
- /* Update reg_cache: ASICREV, and TRIM values */
- val = twl6040_get_revid(twl6040);
- twl6040_write_reg_cache(codec, TWL6040_REG_ASICREV, val);
-
- twl6040_read_reg_volatile(codec, TWL6040_REG_TRIM1);
- twl6040_read_reg_volatile(codec, TWL6040_REG_TRIM2);
- twl6040_read_reg_volatile(codec, TWL6040_REG_TRIM3);
- twl6040_read_reg_volatile(codec, TWL6040_REG_HSOTRIM);
- twl6040_read_reg_volatile(codec, TWL6040_REG_HFOTRIM);
+ twl6040_read(codec, TWL6040_REG_TRIM1);
+ twl6040_read(codec, TWL6040_REG_TRIM2);
+ twl6040_read(codec, TWL6040_REG_TRIM3);
+ twl6040_read(codec, TWL6040_REG_HSOTRIM);
+ twl6040_read(codec, TWL6040_REG_HFOTRIM);
/* Change chip defaults */
/* No imput selected for microphone amplifiers */
- twl6040_write_reg_cache(codec, TWL6040_REG_MICLCTL, 0x18);
- twl6040_write_reg_cache(codec, TWL6040_REG_MICRCTL, 0x18);
+ twl6040_write(codec, TWL6040_REG_MICLCTL, 0x18);
+ twl6040_write(codec, TWL6040_REG_MICRCTL, 0x18);
/*
* We need to lower the default gain values, so the ramp code
* can work correctly for the first playback.
* This reduces the pop noise heard at the first playback.
*/
- twl6040_write_reg_cache(codec, TWL6040_REG_HSGAIN, 0xff);
- twl6040_write_reg_cache(codec, TWL6040_REG_EARCTL, 0x1e);
- twl6040_write_reg_cache(codec, TWL6040_REG_HFLGAIN, 0x1d);
- twl6040_write_reg_cache(codec, TWL6040_REG_HFRGAIN, 0x1d);
- twl6040_write_reg_cache(codec, TWL6040_REG_LINEGAIN, 0);
-}
-
-static void twl6040_restore_regs(struct snd_soc_codec *codec)
-{
- u8 *cache = codec->reg_cache;
- int reg, i;
-
- for (i = 0; i < ARRAY_SIZE(twl6040_restore_list); i++) {
- reg = twl6040_restore_list[i];
- twl6040_write(codec, reg, cache[reg]);
- }
+ twl6040_write(codec, TWL6040_REG_HSGAIN, 0xff);
+ twl6040_write(codec, TWL6040_REG_EARCTL, 0x1e);
+ twl6040_write(codec, TWL6040_REG_HFLGAIN, 0x1d);
+ twl6040_write(codec, TWL6040_REG_HFRGAIN, 0x1d);
+ twl6040_write(codec, TWL6040_REG_LINEGAIN, 0);
}
/* set headset dac and driver power mode */
@@ -305,8 +214,8 @@ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
int hslctl, hsrctl;
int mask = TWL6040_HSDRVMODE | TWL6040_HSDACMODE;
- hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL);
- hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL);
+ hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL);
+ hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL);
if (high_perf) {
hslctl &= ~mask;
@@ -333,8 +242,8 @@ static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w,
* Both HS DAC need to be turned on (before the HS driver) and off at
* the same time.
*/
- hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL);
- hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL);
+ hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL);
+ hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL);
if (SND_SOC_DAPM_EVENT_ON(event)) {
hslctl |= TWL6040_HSDACENA;
hsrctl |= TWL6040_HSDACENA;
@@ -379,7 +288,7 @@ static void twl6040_hs_jack_report(struct snd_soc_codec *codec,
mutex_lock(&priv->mutex);
/* Sync status */
- status = twl6040_read_reg_volatile(codec, TWL6040_REG_STATUS);
+ status = twl6040_read(codec, TWL6040_REG_STATUS);
if (status & TWL6040_PLUGCOMP)
snd_soc_jack_report(jack, report, report);
else
@@ -431,7 +340,7 @@ static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol,
unsigned int val;
/* Do not allow changes while Input/FF efect is running */
- val = twl6040_read_reg_volatile(codec, e->reg);
+ val = twl6040_read(codec, e->reg);
if (val & TWL6040_VIBENA && !(val & TWL6040_VIBSEL))
return -EBUSY;
@@ -656,7 +565,7 @@ int twl6040_get_trim_value(struct snd_soc_codec *codec, enum twl6040_trim trim)
if (unlikely(trim >= TWL6040_TRIM_INVAL))
return -EINVAL;
- return twl6040_read_reg_cache(codec, TWL6040_REG_TRIM1 + trim);
+ return twl6040_read(codec, TWL6040_REG_TRIM1 + trim);
}
EXPORT_SYMBOL_GPL(twl6040_get_trim_value);
@@ -931,8 +840,6 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
priv->codec_powered = 1;
- twl6040_restore_regs(codec);
-
/* Set external boost GPO */
twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02);
break;
@@ -1053,9 +960,9 @@ static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id i
switch (id) {
case TWL6040_DAI_DL1:
- hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL);
- hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL);
- earctl = twl6040_read_reg_cache(codec, TWL6040_REG_EARCTL);
+ hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL);
+ hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL);
+ earctl = twl6040_read(codec, TWL6040_REG_EARCTL);
if (mute) {
/* Power down drivers and DACs */
@@ -1071,8 +978,8 @@ static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id i
priv->dl1_unmuted = !mute;
break;
case TWL6040_DAI_DL2:
- hflctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFLCTL);
- hfrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFRCTL);
+ hflctl = twl6040_read(codec, TWL6040_REG_HFLCTL);
+ hfrctl = twl6040_read(codec, TWL6040_REG_HFRCTL);
if (mute) {
/* Power down drivers and DACs */
@@ -1209,6 +1116,7 @@ static int twl6040_resume(struct snd_soc_codec *codec)
static int twl6040_probe(struct snd_soc_codec *codec)
{
struct twl6040_data *priv;
+ struct twl6040 *twl6040 = dev_get_drvdata(codec->dev->parent);
struct platform_device *pdev = container_of(codec->dev,
struct platform_device, dev);
int ret = 0;
@@ -1220,7 +1128,7 @@ static int twl6040_probe(struct snd_soc_codec *codec)
snd_soc_codec_set_drvdata(codec, priv);
priv->codec = codec;
- codec->control_data = dev_get_drvdata(codec->dev->parent);
+ codec->control_data = twl6040;
priv->plug_irq = platform_get_irq(pdev, 0);
if (priv->plug_irq < 0) {
@@ -1240,10 +1148,10 @@ static int twl6040_probe(struct snd_soc_codec *codec)
return ret;
}
+ twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
twl6040_init_chip(codec);
- /* power on device */
- return twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ return 0;
}
static int twl6040_remove(struct snd_soc_codec *codec)
@@ -1261,12 +1169,9 @@ static struct snd_soc_codec_driver soc_codec_dev_twl6040 = {
.remove = twl6040_remove,
.suspend = twl6040_suspend,
.resume = twl6040_resume,
- .read = twl6040_read_reg_cache,
+ .read = twl6040_read,
.write = twl6040_write,
.set_bias_level = twl6040_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(twl6040_reg),
- .reg_word_size = sizeof(u8),
- .reg_cache_default = twl6040_reg,
.ignore_pmdown_time = true,
.controls = twl6040_snd_controls,
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index fd0a314bc209..726df6d43c2b 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -794,7 +794,7 @@ static struct snd_soc_codec_driver soc_codec_dev_uda1380 = {
.num_dapm_routes = ARRAY_SIZE(uda1380_dapm_routes),
};
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int uda1380_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -840,7 +840,7 @@ static struct i2c_driver uda1380_i2c_driver = {
static int __init uda1380_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&uda1380_i2c_driver);
if (ret != 0)
pr_err("Failed to register UDA1380 I2C driver: %d\n", ret);
@@ -851,7 +851,7 @@ module_init(uda1380_modinit);
static void __exit uda1380_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&uda1380_i2c_driver);
#endif
}
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index f3d96eae031a..22bd7dd80bba 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -313,19 +313,6 @@ ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SPKDAT2L", ARIZONA_OUT6LMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SPKDAT2R", ARIZONA_OUT6RMIX_INPUT_1_SOURCE),
-SOC_SINGLE("HPOUT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_1L,
- ARIZONA_OUT1_OSR_SHIFT, 1, 0),
-SOC_SINGLE("HPOUT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_2L,
- ARIZONA_OUT2_OSR_SHIFT, 1, 0),
-SOC_SINGLE("HPOUT3 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_3L,
- ARIZONA_OUT3_OSR_SHIFT, 1, 0),
-SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,
- ARIZONA_OUT4_OSR_SHIFT, 1, 0),
-SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
- ARIZONA_OUT5_OSR_SHIFT, 1, 0),
-SOC_SINGLE("SPKDAT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_6L,
- ARIZONA_OUT6_OSR_SHIFT, 1, 0),
-
SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
SOC_DOUBLE_R("HPOUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
@@ -358,18 +345,6 @@ SOC_DOUBLE_R_TLV("SPKDAT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_6L,
ARIZONA_DAC_DIGITAL_VOLUME_6R, ARIZONA_OUT6L_VOL_SHIFT,
0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_RANGE_TLV("HPOUT1 Volume", ARIZONA_OUTPUT_PATH_CONFIG_1L,
- ARIZONA_OUTPUT_PATH_CONFIG_1R,
- ARIZONA_OUT1L_PGA_VOL_SHIFT,
- 0x34, 0x40, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("HPOUT2 Volume", ARIZONA_OUTPUT_PATH_CONFIG_2L,
- ARIZONA_OUTPUT_PATH_CONFIG_2R,
- ARIZONA_OUT2L_PGA_VOL_SHIFT,
- 0x34, 0x40, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("HPOUT3 Volume", ARIZONA_OUTPUT_PATH_CONFIG_3L,
- ARIZONA_OUTPUT_PATH_CONFIG_3R,
- ARIZONA_OUT3L_PGA_VOL_SHIFT, 0x34, 0x40, 0, ana_tlv),
-
SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT,
@@ -1376,7 +1351,7 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "AEC Loopback", "HPOUT3L", "OUT3L" },
{ "AEC Loopback", "HPOUT3R", "OUT3R" },
{ "HPOUT3L", NULL, "OUT3L" },
- { "HPOUT3R", NULL, "OUT3L" },
+ { "HPOUT3R", NULL, "OUT3R" },
{ "AEC Loopback", "SPKOUTL", "OUT4L" },
{ "SPKOUTLN", NULL, "OUT4L" },
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 6ed5433943ea..7df7d4572755 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -684,7 +684,7 @@ static struct spi_driver wm8510_spi_driver = {
};
#endif /* CONFIG_SPI_MASTER */
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8510_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -735,7 +735,7 @@ static struct i2c_driver wm8510_i2c_driver = {
static int __init wm8510_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8510_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8510 I2C driver: %d\n",
@@ -755,7 +755,7 @@ module_init(wm8510_modinit);
static void __exit wm8510_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8510_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index 139bf9ac9407..74d106dc7667 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -452,7 +452,7 @@ static const struct regmap_config wm8523_regmap = {
.volatile_reg = wm8523_volatile_register,
};
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8523_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -555,7 +555,7 @@ static struct i2c_driver wm8523_i2c_driver = {
static int __init wm8523_modinit(void)
{
int ret;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8523_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8523 I2C driver: %d\n",
@@ -568,7 +568,7 @@ module_init(wm8523_modinit);
static void __exit wm8523_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8523_i2c_driver);
#endif
}
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 08a414b57b1e..318989acbbe5 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -941,7 +941,7 @@ static const struct regmap_config wm8580_regmap = {
.volatile_reg = wm8580_volatile,
};
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8580_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -1003,7 +1003,7 @@ static int __init wm8580_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8580_i2c_driver);
if (ret != 0) {
pr_err("Failed to register WM8580 I2C driver: %d\n", ret);
@@ -1016,7 +1016,7 @@ module_init(wm8580_modinit);
static void __exit wm8580_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8580_i2c_driver);
#endif
}
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index 5b428b060d41..d99f948c513c 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -469,7 +469,7 @@ static struct spi_driver wm8711_spi_driver = {
};
#endif /* CONFIG_SPI_MASTER */
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8711_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -520,7 +520,7 @@ static struct i2c_driver wm8711_i2c_driver = {
static int __init wm8711_modinit(void)
{
int ret;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8711_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n",
@@ -540,7 +540,7 @@ module_init(wm8711_modinit);
static void __exit wm8711_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8711_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index c6a292dcded0..cd89033e84c0 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -320,7 +320,7 @@ static struct spi_driver wm8728_spi_driver = {
};
#endif /* CONFIG_SPI_MASTER */
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8728_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -371,7 +371,7 @@ static struct i2c_driver wm8728_i2c_driver = {
static int __init wm8728_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8728_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register wm8728 I2C driver: %d\n",
@@ -391,7 +391,7 @@ module_init(wm8728_modinit);
static void __exit wm8728_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8728_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 456bb8c6d759..029720366ff8 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -447,10 +447,10 @@ static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
iface |= 0x0001;
break;
case SND_SOC_DAIFMT_DSP_A:
- iface |= 0x0003;
+ iface |= 0x0013;
break;
case SND_SOC_DAIFMT_DSP_B:
- iface |= 0x0013;
+ iface |= 0x0003;
break;
default:
return -EINVAL;
@@ -732,7 +732,7 @@ static struct spi_driver wm8731_spi_driver = {
};
#endif /* CONFIG_SPI_MASTER */
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8731_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -791,7 +791,7 @@ static struct i2c_driver wm8731_i2c_driver = {
static int __init wm8731_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8731_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n",
@@ -811,7 +811,7 @@ module_init(wm8731_modinit);
static void __exit wm8731_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8731_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index b18813cc7ba9..2895c8d3b5e4 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -500,7 +500,7 @@ static const struct regmap_config wm8741_regmap = {
.readable_reg = wm8741_readable,
};
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8741_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -617,7 +617,7 @@ static int __init wm8741_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8741_i2c_driver);
if (ret != 0)
pr_err("Failed to register WM8741 I2C driver: %d\n", ret);
@@ -639,7 +639,7 @@ static void __exit wm8741_exit(void)
#if defined(CONFIG_SPI_MASTER)
spi_unregister_driver(&wm8741_spi_driver);
#endif
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8741_i2c_driver);
#endif
}
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 50d5ff616232..78616a638a55 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -816,7 +816,7 @@ static struct spi_driver wm8750_spi_driver = {
};
#endif /* CONFIG_SPI_MASTER */
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8750_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -868,7 +868,7 @@ static struct i2c_driver wm8750_i2c_driver = {
static int __init wm8750_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8750_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register wm8750 I2C driver: %d\n",
@@ -888,7 +888,7 @@ module_init(wm8750_modinit);
static void __exit wm8750_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8750_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index d96ebf52d953..be85da93a268 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1596,7 +1596,7 @@ static struct spi_driver wm8753_spi_driver = {
};
#endif /* CONFIG_SPI_MASTER */
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8753_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -1653,7 +1653,7 @@ static struct i2c_driver wm8753_i2c_driver = {
static int __init wm8753_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8753_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register wm8753 I2C driver: %d\n",
@@ -1673,7 +1673,7 @@ module_init(wm8753_modinit);
static void __exit wm8753_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8753_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index 942d58e455f3..ef8246725232 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -532,7 +532,7 @@ static struct spi_driver wm8776_spi_driver = {
};
#endif /* CONFIG_SPI_MASTER */
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8776_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -584,7 +584,7 @@ static struct i2c_driver wm8776_i2c_driver = {
static int __init wm8776_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8776_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register wm8776 I2C driver: %d\n",
@@ -604,7 +604,7 @@ module_init(wm8776_modinit);
static void __exit wm8776_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8776_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c
index 1704b1e119cb..9bc8206a6807 100644
--- a/sound/soc/codecs/wm8804.c
+++ b/sound/soc/codecs/wm8804.c
@@ -739,7 +739,7 @@ static struct spi_driver wm8804_spi_driver = {
};
#endif
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8804_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -791,7 +791,7 @@ static int __init wm8804_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8804_i2c_driver);
if (ret) {
printk(KERN_ERR "Failed to register wm8804 I2C driver: %d\n",
@@ -811,7 +811,7 @@ module_init(wm8804_modinit);
static void __exit wm8804_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8804_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 734209e252c3..e98bc7038a08 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -1288,7 +1288,7 @@ static struct spi_driver wm8900_spi_driver = {
};
#endif /* CONFIG_SPI_MASTER */
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8900_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -1338,7 +1338,7 @@ static struct i2c_driver wm8900_i2c_driver = {
static int __init wm8900_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8900_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register wm8900 I2C driver: %d\n",
@@ -1358,7 +1358,7 @@ module_init(wm8900_modinit);
static void __exit wm8900_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8900_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 3938fb1c203e..53bbfac6a83a 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -1444,7 +1444,7 @@ static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_B:
- aif1 |= WM8904_AIF_LRCLK_INV;
+ aif1 |= 0x3 | WM8904_AIF_LRCLK_INV;
case SND_SOC_DAIFMT_DSP_A:
aif1 |= 0x3;
break;
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index b1591c61c254..b404c26c1753 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -28,7 +28,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/spi/spi.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -41,78 +41,116 @@
struct wm8940_priv {
unsigned int sysclk;
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
};
-static int wm8940_volatile_register(struct snd_soc_codec *codec,
- unsigned int reg)
+static bool wm8940_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case WM8940_SOFTRESET:
- return 1;
+ return true;
default:
- return 0;
+ return false;
+ }
+}
+
+static bool wm8940_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8940_SOFTRESET:
+ case WM8940_POWER1:
+ case WM8940_POWER2:
+ case WM8940_POWER3:
+ case WM8940_IFACE:
+ case WM8940_COMPANDINGCTL:
+ case WM8940_CLOCK:
+ case WM8940_ADDCNTRL:
+ case WM8940_GPIO:
+ case WM8940_CTLINT:
+ case WM8940_DAC:
+ case WM8940_DACVOL:
+ case WM8940_ADC:
+ case WM8940_ADCVOL:
+ case WM8940_NOTCH1:
+ case WM8940_NOTCH2:
+ case WM8940_NOTCH3:
+ case WM8940_NOTCH4:
+ case WM8940_NOTCH5:
+ case WM8940_NOTCH6:
+ case WM8940_NOTCH7:
+ case WM8940_NOTCH8:
+ case WM8940_DACLIM1:
+ case WM8940_DACLIM2:
+ case WM8940_ALC1:
+ case WM8940_ALC2:
+ case WM8940_ALC3:
+ case WM8940_NOISEGATE:
+ case WM8940_PLLN:
+ case WM8940_PLLK1:
+ case WM8940_PLLK2:
+ case WM8940_PLLK3:
+ case WM8940_ALC4:
+ case WM8940_INPUTCTL:
+ case WM8940_PGAGAIN:
+ case WM8940_ADCBOOST:
+ case WM8940_OUTPUTCTL:
+ case WM8940_SPKMIX:
+ case WM8940_SPKVOL:
+ case WM8940_MONOMIX:
+ return true;
+ default:
+ return false;
}
}
-static u16 wm8940_reg_defaults[] = {
- 0x8940, /* Soft Reset */
- 0x0000, /* Power 1 */
- 0x0000, /* Power 2 */
- 0x0000, /* Power 3 */
- 0x0010, /* Interface Control */
- 0x0000, /* Companding Control */
- 0x0140, /* Clock Control */
- 0x0000, /* Additional Controls */
- 0x0000, /* GPIO Control */
- 0x0002, /* Auto Increment Control */
- 0x0000, /* DAC Control */
- 0x00FF, /* DAC Volume */
- 0,
- 0,
- 0x0100, /* ADC Control */
- 0x00FF, /* ADC Volume */
- 0x0000, /* Notch Filter 1 Control 1 */
- 0x0000, /* Notch Filter 1 Control 2 */
- 0x0000, /* Notch Filter 2 Control 1 */
- 0x0000, /* Notch Filter 2 Control 2 */
- 0x0000, /* Notch Filter 3 Control 1 */
- 0x0000, /* Notch Filter 3 Control 2 */
- 0x0000, /* Notch Filter 4 Control 1 */
- 0x0000, /* Notch Filter 4 Control 2 */
- 0x0032, /* DAC Limit Control 1 */
- 0x0000, /* DAC Limit Control 2 */
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0x0038, /* ALC Control 1 */
- 0x000B, /* ALC Control 2 */
- 0x0032, /* ALC Control 3 */
- 0x0000, /* Noise Gate */
- 0x0041, /* PLLN */
- 0x000C, /* PLLK1 */
- 0x0093, /* PLLK2 */
- 0x00E9, /* PLLK3 */
- 0,
- 0,
- 0x0030, /* ALC Control 4 */
- 0,
- 0x0002, /* Input Control */
- 0x0050, /* PGA Gain */
- 0,
- 0x0002, /* ADC Boost Control */
- 0,
- 0x0002, /* Output Control */
- 0x0000, /* Speaker Mixer Control */
- 0,
- 0,
- 0,
- 0x0079, /* Speaker Volume */
- 0,
- 0x0000, /* Mono Mixer Control */
+static const struct reg_default wm8940_reg_defaults[] = {
+ { 0x1, 0x0000 }, /* Power 1 */
+ { 0x2, 0x0000 }, /* Power 2 */
+ { 0x3, 0x0000 }, /* Power 3 */
+ { 0x4, 0x0010 }, /* Interface Control */
+ { 0x5, 0x0000 }, /* Companding Control */
+ { 0x6, 0x0140 }, /* Clock Control */
+ { 0x7, 0x0000 }, /* Additional Controls */
+ { 0x8, 0x0000 }, /* GPIO Control */
+ { 0x9, 0x0002 }, /* Auto Increment Control */
+ { 0xa, 0x0000 }, /* DAC Control */
+ { 0xb, 0x00FF }, /* DAC Volume */
+
+ { 0xe, 0x0100 }, /* ADC Control */
+ { 0xf, 0x00FF }, /* ADC Volume */
+ { 0x10, 0x0000 }, /* Notch Filter 1 Control 1 */
+ { 0x11, 0x0000 }, /* Notch Filter 1 Control 2 */
+ { 0x12, 0x0000 }, /* Notch Filter 2 Control 1 */
+ { 0x13, 0x0000 }, /* Notch Filter 2 Control 2 */
+ { 0x14, 0x0000 }, /* Notch Filter 3 Control 1 */
+ { 0x15, 0x0000 }, /* Notch Filter 3 Control 2 */
+ { 0x16, 0x0000 }, /* Notch Filter 4 Control 1 */
+ { 0x17, 0x0000 }, /* Notch Filter 4 Control 2 */
+ { 0x18, 0x0032 }, /* DAC Limit Control 1 */
+ { 0x19, 0x0000 }, /* DAC Limit Control 2 */
+
+ { 0x20, 0x0038 }, /* ALC Control 1 */
+ { 0x21, 0x000B }, /* ALC Control 2 */
+ { 0x22, 0x0032 }, /* ALC Control 3 */
+ { 0x23, 0x0000 }, /* Noise Gate */
+ { 0x24, 0x0041 }, /* PLLN */
+ { 0x25, 0x000C }, /* PLLK1 */
+ { 0x26, 0x0093 }, /* PLLK2 */
+ { 0x27, 0x00E9 }, /* PLLK3 */
+
+ { 0x2a, 0x0030 }, /* ALC Control 4 */
+
+ { 0x2c, 0x0002 }, /* Input Control */
+ { 0x2d, 0x0050 }, /* PGA Gain */
+
+ { 0x2f, 0x0002 }, /* ADC Boost Control */
+
+ { 0x31, 0x0002 }, /* Output Control */
+ { 0x32, 0x0000 }, /* Speaker Mixer Control */
+
+ { 0x36, 0x0079 }, /* Speaker Volume */
+
+ { 0x38, 0x0000 }, /* Mono Mixer Control */
};
static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" };
@@ -264,7 +302,7 @@ static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AUX"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route wm8940_dapm_routes[] = {
/* Mono output mixer */
{"Mono Mixer", "PCM Playback Switch", "DAC"},
{"Mono Mixer", "Aux Playback Switch", "Aux Input"},
@@ -296,21 +334,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"ADC", NULL, "Boost Mixer"},
};
-static int wm8940_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
- int ret;
-
- ret = snd_soc_dapm_new_controls(dapm, wm8940_dapm_widgets,
- ARRAY_SIZE(wm8940_dapm_widgets));
- if (ret)
- goto error_ret;
- ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
-error_ret:
- return ret;
-}
-
#define wm8940_reset(c) snd_soc_write(c, WM8940_SOFTRESET, 0);
static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
@@ -446,6 +469,7 @@ static int wm8940_mute(struct snd_soc_dai *dai, int mute)
static int wm8940_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
u16 val;
u16 pwr_reg = snd_soc_read(codec, WM8940_POWER1) & 0x1F0;
int ret = 0;
@@ -469,7 +493,7 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- ret = snd_soc_cache_sync(codec);
+ ret = regcache_sync(wm8940->regmap);
if (ret < 0) {
dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
return ret;
@@ -684,12 +708,11 @@ static int wm8940_resume(struct snd_soc_codec *codec)
static int wm8940_probe(struct snd_soc_codec *codec)
{
- struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec);
struct wm8940_setup_data *pdata = codec->dev->platform_data;
int ret;
u16 reg;
- ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8940->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
@@ -716,11 +739,6 @@ static int wm8940_probe(struct snd_soc_codec *codec)
return ret;
}
- ret = snd_soc_add_codec_controls(codec, wm8940_snd_controls,
- ARRAY_SIZE(wm8940_snd_controls));
- if (ret)
- return ret;
- ret = wm8940_add_widgets(codec);
return ret;
}
@@ -736,10 +754,24 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8940 = {
.suspend = wm8940_suspend,
.resume = wm8940_resume,
.set_bias_level = wm8940_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(wm8940_reg_defaults),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8940_reg_defaults,
- .volatile_register = wm8940_volatile_register,
+ .controls = wm8940_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8940_snd_controls),
+ .dapm_widgets = wm8940_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8940_dapm_widgets),
+ .dapm_routes = wm8940_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8940_dapm_routes),
+};
+
+static const struct regmap_config wm8940_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = WM8940_MONOMIX,
+ .reg_defaults = wm8940_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8940_reg_defaults),
+
+ .readable_reg = wm8940_readable_register,
+ .volatile_reg = wm8940_volatile_register,
};
static int wm8940_i2c_probe(struct i2c_client *i2c,
@@ -753,8 +785,11 @@ static int wm8940_i2c_probe(struct i2c_client *i2c,
if (wm8940 == NULL)
return -ENOMEM;
+ wm8940->regmap = devm_regmap_init_i2c(i2c, &wm8940_regmap);
+ if (IS_ERR(wm8940->regmap))
+ return PTR_ERR(wm8940->regmap);
+
i2c_set_clientdata(i2c, wm8940);
- wm8940->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8940, &wm8940_dai, 1);
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 543c5c2631b6..97db3b45b411 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -74,7 +74,7 @@ struct wm8962_priv {
struct regulator_bulk_data supplies[WM8962_NUM_SUPPLIES];
struct notifier_block disable_nb[WM8962_NUM_SUPPLIES];
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+#if IS_ENABLED(CONFIG_INPUT)
struct input_dev *beep;
struct work_struct beep_work;
int beep_rate;
@@ -2439,7 +2439,20 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8962_CLOCKING_4,
WM8962_SYSCLK_RATE_MASK, clocking4);
+ /* DSPCLK_DIV can be only generated correctly after enabling SYSCLK.
+ * So we here provisionally enable it and then disable it afterward
+ * if current bias_level hasn't reached SND_SOC_BIAS_ON.
+ */
+ if (codec->dapm.bias_level != SND_SOC_BIAS_ON)
+ snd_soc_update_bits(codec, WM8962_CLOCKING2,
+ WM8962_SYSCLK_ENA_MASK, WM8962_SYSCLK_ENA);
+
dspclk = snd_soc_read(codec, WM8962_CLOCKING1);
+
+ if (codec->dapm.bias_level != SND_SOC_BIAS_ON)
+ snd_soc_update_bits(codec, WM8962_CLOCKING2,
+ WM8962_SYSCLK_ENA_MASK, 0);
+
if (dspclk < 0) {
dev_err(codec->dev, "Failed to read DSPCLK: %d\n", dspclk);
return;
@@ -3108,7 +3121,7 @@ int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
}
EXPORT_SYMBOL_GPL(wm8962_mic_detect);
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+#if IS_ENABLED(CONFIG_INPUT)
static int beep_rates[] = {
500, 1000, 2000, 4000,
};
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index a2d01d10a5dd..15f45c7bd833 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -17,6 +17,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -27,22 +28,22 @@
#include "wm8974.h"
-static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0050, 0x0000, 0x0140, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x00ff,
- 0x0000, 0x0000, 0x0100, 0x00ff,
- 0x0000, 0x0000, 0x012c, 0x002c,
- 0x002c, 0x002c, 0x002c, 0x0000,
- 0x0032, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0038, 0x000b, 0x0032, 0x0000,
- 0x0008, 0x000c, 0x0093, 0x00e9,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0003, 0x0010, 0x0000, 0x0000,
- 0x0000, 0x0002, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0039, 0x0000,
- 0x0000,
+static const struct reg_default wm8974_reg_defaults[] = {
+ { 0, 0x0000 }, { 1, 0x0000 }, { 2, 0x0000 }, { 3, 0x0000 },
+ { 4, 0x0050 }, { 5, 0x0000 }, { 6, 0x0140 }, { 7, 0x0000 },
+ { 8, 0x0000 }, { 9, 0x0000 }, { 10, 0x0000 }, { 11, 0x00ff },
+ { 12, 0x0000 }, { 13, 0x0000 }, { 14, 0x0100 }, { 15, 0x00ff },
+ { 16, 0x0000 }, { 17, 0x0000 }, { 18, 0x012c }, { 19, 0x002c },
+ { 20, 0x002c }, { 21, 0x002c }, { 22, 0x002c }, { 23, 0x0000 },
+ { 24, 0x0032 }, { 25, 0x0000 }, { 26, 0x0000 }, { 27, 0x0000 },
+ { 28, 0x0000 }, { 29, 0x0000 }, { 30, 0x0000 }, { 31, 0x0000 },
+ { 32, 0x0038 }, { 33, 0x000b }, { 34, 0x0032 }, { 35, 0x0000 },
+ { 36, 0x0008 }, { 37, 0x000c }, { 38, 0x0093 }, { 39, 0x00e9 },
+ { 40, 0x0000 }, { 41, 0x0000 }, { 42, 0x0000 }, { 43, 0x0000 },
+ { 44, 0x0003 }, { 45, 0x0010 }, { 46, 0x0000 }, { 47, 0x0000 },
+ { 48, 0x0000 }, { 49, 0x0002 }, { 50, 0x0000 }, { 51, 0x0000 },
+ { 52, 0x0000 }, { 53, 0x0000 }, { 54, 0x0039 }, { 55, 0x0000 },
+ { 56, 0x0000 },
};
#define WM8974_POWER1_BIASEN 0x08
@@ -514,7 +515,7 @@ static int wm8974_set_bias_level(struct snd_soc_codec *codec,
power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- snd_soc_cache_sync(codec);
+ regcache_sync(dev_get_regmap(codec->dev, NULL));
/* Initial cap charge at VMID 5k */
snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
@@ -579,11 +580,20 @@ static int wm8974_resume(struct snd_soc_codec *codec)
return 0;
}
+static const struct regmap_config wm8974_regmap = {
+ .reg_bits = 7,
+ .val_bits = 9,
+
+ .max_register = WM8974_MONOMIX,
+ .reg_defaults = wm8974_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8974_reg_defaults),
+};
+
static int wm8974_probe(struct snd_soc_codec *codec)
{
int ret = 0;
- ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
@@ -613,9 +623,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
.suspend = wm8974_suspend,
.resume = wm8974_resume,
.set_bias_level = wm8974_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(wm8974_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8974_reg,
.controls = wm8974_snd_controls,
.num_controls = ARRAY_SIZE(wm8974_snd_controls),
@@ -628,8 +635,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
static int wm8974_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
+ struct regmap *regmap;
int ret;
+ regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8974, &wm8974_dai, 1);
diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c
index 18f2babe1090..271b517911a4 100644
--- a/sound/soc/codecs/wm8985.c
+++ b/sound/soc/codecs/wm8985.c
@@ -1148,7 +1148,7 @@ static struct spi_driver wm8985_spi_driver = {
};
#endif
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8985_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -1201,7 +1201,7 @@ static int __init wm8985_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8985_i2c_driver);
if (ret) {
printk(KERN_ERR "Failed to register wm8985 I2C driver: %d\n",
@@ -1221,7 +1221,7 @@ module_init(wm8985_modinit);
static void __exit wm8985_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8985_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 39b9acceb595..a55e1c2c382e 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -912,7 +912,7 @@ static struct spi_driver wm8988_spi_driver = {
};
#endif /* CONFIG_SPI_MASTER */
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8988_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -964,7 +964,7 @@ static struct i2c_driver wm8988_i2c_driver = {
static int __init wm8988_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8988_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8988 I2C driver: %d\n",
@@ -984,7 +984,7 @@ module_init(wm8988_modinit);
static void __exit wm8988_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8988_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 253c88bb7a4c..0ccd4d8d043b 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -17,6 +17,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -30,13 +31,12 @@
/* codec private data */
struct wm8990_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
unsigned int sysclk;
unsigned int pcmclk;
};
-static int wm8990_volatile_register(struct snd_soc_codec *codec,
- unsigned int reg)
+static bool wm8990_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case WM8990_RESET:
@@ -46,71 +46,69 @@ static int wm8990_volatile_register(struct snd_soc_codec *codec,
}
}
-static const u16 wm8990_reg[] = {
- 0x8990, /* R0 - Reset */
- 0x0000, /* R1 - Power Management (1) */
- 0x6000, /* R2 - Power Management (2) */
- 0x0000, /* R3 - Power Management (3) */
- 0x4050, /* R4 - Audio Interface (1) */
- 0x4000, /* R5 - Audio Interface (2) */
- 0x01C8, /* R6 - Clocking (1) */
- 0x0000, /* R7 - Clocking (2) */
- 0x0040, /* R8 - Audio Interface (3) */
- 0x0040, /* R9 - Audio Interface (4) */
- 0x0004, /* R10 - DAC CTRL */
- 0x00C0, /* R11 - Left DAC Digital Volume */
- 0x00C0, /* R12 - Right DAC Digital Volume */
- 0x0000, /* R13 - Digital Side Tone */
- 0x0100, /* R14 - ADC CTRL */
- 0x00C0, /* R15 - Left ADC Digital Volume */
- 0x00C0, /* R16 - Right ADC Digital Volume */
- 0x0000, /* R17 */
- 0x0000, /* R18 - GPIO CTRL 1 */
- 0x1000, /* R19 - GPIO1 & GPIO2 */
- 0x1010, /* R20 - GPIO3 & GPIO4 */
- 0x1010, /* R21 - GPIO5 & GPIO6 */
- 0x8000, /* R22 - GPIOCTRL 2 */
- 0x0800, /* R23 - GPIO_POL */
- 0x008B, /* R24 - Left Line Input 1&2 Volume */
- 0x008B, /* R25 - Left Line Input 3&4 Volume */
- 0x008B, /* R26 - Right Line Input 1&2 Volume */
- 0x008B, /* R27 - Right Line Input 3&4 Volume */
- 0x0000, /* R28 - Left Output Volume */
- 0x0000, /* R29 - Right Output Volume */
- 0x0066, /* R30 - Line Outputs Volume */
- 0x0022, /* R31 - Out3/4 Volume */
- 0x0079, /* R32 - Left OPGA Volume */
- 0x0079, /* R33 - Right OPGA Volume */
- 0x0003, /* R34 - Speaker Volume */
- 0x0003, /* R35 - ClassD1 */
- 0x0000, /* R36 */
- 0x0100, /* R37 - ClassD3 */
- 0x0079, /* R38 - ClassD4 */
- 0x0000, /* R39 - Input Mixer1 */
- 0x0000, /* R40 - Input Mixer2 */
- 0x0000, /* R41 - Input Mixer3 */
- 0x0000, /* R42 - Input Mixer4 */
- 0x0000, /* R43 - Input Mixer5 */
- 0x0000, /* R44 - Input Mixer6 */
- 0x0000, /* R45 - Output Mixer1 */
- 0x0000, /* R46 - Output Mixer2 */
- 0x0000, /* R47 - Output Mixer3 */
- 0x0000, /* R48 - Output Mixer4 */
- 0x0000, /* R49 - Output Mixer5 */
- 0x0000, /* R50 - Output Mixer6 */
- 0x0180, /* R51 - Out3/4 Mixer */
- 0x0000, /* R52 - Line Mixer1 */
- 0x0000, /* R53 - Line Mixer2 */
- 0x0000, /* R54 - Speaker Mixer */
- 0x0000, /* R55 - Additional Control */
- 0x0000, /* R56 - AntiPOP1 */
- 0x0000, /* R57 - AntiPOP2 */
- 0x0000, /* R58 - MICBIAS */
- 0x0000, /* R59 */
- 0x0008, /* R60 - PLL1 */
- 0x0031, /* R61 - PLL2 */
- 0x0026, /* R62 - PLL3 */
- 0x0000, /* R63 - Driver internal */
+static const struct reg_default wm8990_reg_defaults[] = {
+ { 1, 0x0000 }, /* R1 - Power Management (1) */
+ { 2, 0x6000 }, /* R2 - Power Management (2) */
+ { 3, 0x0000 }, /* R3 - Power Management (3) */
+ { 4, 0x4050 }, /* R4 - Audio Interface (1) */
+ { 5, 0x4000 }, /* R5 - Audio Interface (2) */
+ { 6, 0x01C8 }, /* R6 - Clocking (1) */
+ { 7, 0x0000 }, /* R7 - Clocking (2) */
+ { 8, 0x0040 }, /* R8 - Audio Interface (3) */
+ { 9, 0x0040 }, /* R9 - Audio Interface (4) */
+ { 10, 0x0004 }, /* R10 - DAC CTRL */
+ { 11, 0x00C0 }, /* R11 - Left DAC Digital Volume */
+ { 12, 0x00C0 }, /* R12 - Right DAC Digital Volume */
+ { 13, 0x0000 }, /* R13 - Digital Side Tone */
+ { 14, 0x0100 }, /* R14 - ADC CTRL */
+ { 15, 0x00C0 }, /* R15 - Left ADC Digital Volume */
+ { 16, 0x00C0 }, /* R16 - Right ADC Digital Volume */
+
+ { 18, 0x0000 }, /* R18 - GPIO CTRL 1 */
+ { 19, 0x1000 }, /* R19 - GPIO1 & GPIO2 */
+ { 20, 0x1010 }, /* R20 - GPIO3 & GPIO4 */
+ { 21, 0x1010 }, /* R21 - GPIO5 & GPIO6 */
+ { 22, 0x8000 }, /* R22 - GPIOCTRL 2 */
+ { 23, 0x0800 }, /* R23 - GPIO_POL */
+ { 24, 0x008B }, /* R24 - Left Line Input 1&2 Volume */
+ { 25, 0x008B }, /* R25 - Left Line Input 3&4 Volume */
+ { 26, 0x008B }, /* R26 - Right Line Input 1&2 Volume */
+ { 27, 0x008B }, /* R27 - Right Line Input 3&4 Volume */
+ { 28, 0x0000 }, /* R28 - Left Output Volume */
+ { 29, 0x0000 }, /* R29 - Right Output Volume */
+ { 30, 0x0066 }, /* R30 - Line Outputs Volume */
+ { 31, 0x0022 }, /* R31 - Out3/4 Volume */
+ { 32, 0x0079 }, /* R32 - Left OPGA Volume */
+ { 33, 0x0079 }, /* R33 - Right OPGA Volume */
+ { 34, 0x0003 }, /* R34 - Speaker Volume */
+ { 35, 0x0003 }, /* R35 - ClassD1 */
+
+ { 37, 0x0100 }, /* R37 - ClassD3 */
+ { 38, 0x0079 }, /* R38 - ClassD4 */
+ { 39, 0x0000 }, /* R39 - Input Mixer1 */
+ { 40, 0x0000 }, /* R40 - Input Mixer2 */
+ { 41, 0x0000 }, /* R41 - Input Mixer3 */
+ { 42, 0x0000 }, /* R42 - Input Mixer4 */
+ { 43, 0x0000 }, /* R43 - Input Mixer5 */
+ { 44, 0x0000 }, /* R44 - Input Mixer6 */
+ { 45, 0x0000 }, /* R45 - Output Mixer1 */
+ { 46, 0x0000 }, /* R46 - Output Mixer2 */
+ { 47, 0x0000 }, /* R47 - Output Mixer3 */
+ { 48, 0x0000 }, /* R48 - Output Mixer4 */
+ { 49, 0x0000 }, /* R49 - Output Mixer5 */
+ { 50, 0x0000 }, /* R50 - Output Mixer6 */
+ { 51, 0x0180 }, /* R51 - Out3/4 Mixer */
+ { 52, 0x0000 }, /* R52 - Line Mixer1 */
+ { 53, 0x0000 }, /* R53 - Line Mixer2 */
+ { 54, 0x0000 }, /* R54 - Speaker Mixer */
+ { 55, 0x0000 }, /* R55 - Additional Control */
+ { 56, 0x0000 }, /* R56 - AntiPOP1 */
+ { 57, 0x0000 }, /* R57 - AntiPOP2 */
+ { 58, 0x0000 }, /* R58 - MICBIAS */
+
+ { 60, 0x0008 }, /* R60 - PLL1 */
+ { 61, 0x0031 }, /* R61 - PLL2 */
+ { 62, 0x0026 }, /* R62 - PLL3 */
};
#define wm8990_reset(c) snd_soc_write(c, WM8990_RESET, 0)
@@ -376,32 +374,6 @@ SOC_SINGLE("RIN34 Mute Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
* _DAPM_ Controls
*/
-static int inmixer_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- u16 reg, fakepower;
-
- reg = snd_soc_read(w->codec, WM8990_POWER_MANAGEMENT_2);
- fakepower = snd_soc_read(w->codec, WM8990_INTDRIVBITS);
-
- if (fakepower & ((1 << WM8990_INMIXL_PWR_BIT) |
- (1 << WM8990_AINLMUX_PWR_BIT))) {
- reg |= WM8990_AINL_ENA;
- } else {
- reg &= ~WM8990_AINL_ENA;
- }
-
- if (fakepower & ((1 << WM8990_INMIXR_PWR_BIT) |
- (1 << WM8990_AINRMUX_PWR_BIT))) {
- reg |= WM8990_AINR_ENA;
- } else {
- reg &= ~WM8990_AINR_ENA;
- }
- snd_soc_write(w->codec, WM8990_POWER_MANAGEMENT_2, reg);
-
- return 0;
-}
-
static int outmixer_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -656,6 +628,11 @@ SND_SOC_DAPM_INPUT("RIN1"),
SND_SOC_DAPM_INPUT("RIN2"),
SND_SOC_DAPM_INPUT("Internal ADC Source"),
+SND_SOC_DAPM_SUPPLY("INL", WM8990_POWER_MANAGEMENT_2, WM8990_AINL_ENA_BIT, 0,
+ NULL, 0),
+SND_SOC_DAPM_SUPPLY("INR", WM8990_POWER_MANAGEMENT_2, WM8990_AINR_ENA_BIT, 0,
+ NULL, 0),
+
/* DACs */
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8990_POWER_MANAGEMENT_2,
WM8990_ADCL_ENA_BIT, 0),
@@ -677,26 +654,20 @@ SND_SOC_DAPM_MIXER("RIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN34_ENA_BIT,
ARRAY_SIZE(wm8990_dapm_rin34_pga_controls)),
/* INMIXL */
-SND_SOC_DAPM_MIXER_E("INMIXL", WM8990_INTDRIVBITS, WM8990_INMIXL_PWR_BIT, 0,
+SND_SOC_DAPM_MIXER("INMIXL", SND_SOC_NOPM, 0, 0,
&wm8990_dapm_inmixl_controls[0],
- ARRAY_SIZE(wm8990_dapm_inmixl_controls),
- inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ ARRAY_SIZE(wm8990_dapm_inmixl_controls)),
/* AINLMUX */
-SND_SOC_DAPM_MUX_E("AINLMUX", WM8990_INTDRIVBITS, WM8990_AINLMUX_PWR_BIT, 0,
- &wm8990_dapm_ainlmux_controls, inmixer_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_MUX("AINLMUX", SND_SOC_NOPM, 0, 0, &wm8990_dapm_ainlmux_controls),
/* INMIXR */
-SND_SOC_DAPM_MIXER_E("INMIXR", WM8990_INTDRIVBITS, WM8990_INMIXR_PWR_BIT, 0,
+SND_SOC_DAPM_MIXER("INMIXR", SND_SOC_NOPM, 0, 0,
&wm8990_dapm_inmixr_controls[0],
- ARRAY_SIZE(wm8990_dapm_inmixr_controls),
- inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ ARRAY_SIZE(wm8990_dapm_inmixr_controls)),
/* AINRMUX */
-SND_SOC_DAPM_MUX_E("AINRMUX", WM8990_INTDRIVBITS, WM8990_AINRMUX_PWR_BIT, 0,
- &wm8990_dapm_ainrmux_controls, inmixer_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_MUX("AINRMUX", SND_SOC_NOPM, 0, 0, &wm8990_dapm_ainrmux_controls),
/* Output Side */
/* DACs */
@@ -787,7 +758,7 @@ SND_SOC_DAPM_OUTPUT("RON"),
SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route wm8990_dapm_routes[] = {
/* Make DACs turn on when playing even if not mixed into any outputs */
{"Internal DAC Sink", NULL, "Left DAC"},
{"Internal DAC Sink", NULL, "Right DAC"},
@@ -796,6 +767,11 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Left ADC", NULL, "Internal ADC Source"},
{"Right ADC", NULL, "Internal ADC Source"},
+ {"AINLMUX", NULL, "INL"},
+ {"INMIXL", NULL, "INL"},
+ {"AINRMUX", NULL, "INR"},
+ {"INMIXR", NULL, "INR"},
+
/* Input Side */
/* LIN12 PGA */
{"LIN12 PGA", "LIN1 Switch", "LIN1"},
@@ -912,18 +888,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"RON", NULL, "RONMIX"},
};
-static int wm8990_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8990_dapm_widgets,
- ARRAY_SIZE(wm8990_dapm_widgets));
- /* set up the WM8990 audio map */
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
/* PLL divisors */
struct _pll_div {
u32 div2;
@@ -1148,6 +1112,7 @@ static int wm8990_mute(struct snd_soc_dai *dai, int mute)
static int wm8990_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct wm8990_priv *wm8990 = snd_soc_codec_get_drvdata(codec);
int ret;
switch (level) {
@@ -1162,7 +1127,7 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- ret = snd_soc_cache_sync(codec);
+ ret = regcache_sync(wm8990->regmap);
if (ret < 0) {
dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
return ret;
@@ -1259,6 +1224,8 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
snd_soc_write(codec, WM8990_ANTIPOP2, 0x0);
+
+ regcache_mark_dirty(wm8990->regmap);
break;
}
@@ -1327,7 +1294,7 @@ static int wm8990_probe(struct snd_soc_codec *codec)
{
int ret;
- ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
if (ret < 0) {
printk(KERN_ERR "wm8990: failed to set cache I/O: %d\n", ret);
return ret;
@@ -1350,10 +1317,6 @@ static int wm8990_probe(struct snd_soc_codec *codec)
snd_soc_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
snd_soc_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
- snd_soc_add_codec_controls(codec, wm8990_snd_controls,
- ARRAY_SIZE(wm8990_snd_controls));
- wm8990_add_widgets(codec);
-
return 0;
}
@@ -1370,13 +1333,25 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8990 = {
.suspend = wm8990_suspend,
.resume = wm8990_resume,
.set_bias_level = wm8990_set_bias_level,
- .reg_cache_size = ARRAY_SIZE(wm8990_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8990_reg,
- .volatile_register = wm8990_volatile_register,
+ .controls = wm8990_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8990_snd_controls),
+ .dapm_widgets = wm8990_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8990_dapm_widgets),
+ .dapm_routes = wm8990_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8990_dapm_routes),
+};
+
+static const struct regmap_config wm8990_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = WM8990_PLL3,
+ .volatile_reg = wm8990_volatile_register,
+ .reg_defaults = wm8990_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8990_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
};
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static int wm8990_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -1418,29 +1393,8 @@ static struct i2c_driver wm8990_i2c_driver = {
.remove = wm8990_i2c_remove,
.id_table = wm8990_i2c_id,
};
-#endif
-static int __init wm8990_modinit(void)
-{
- int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- ret = i2c_add_driver(&wm8990_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register wm8990 I2C driver: %d\n",
- ret);
- }
-#endif
- return ret;
-}
-module_init(wm8990_modinit);
-
-static void __exit wm8990_exit(void)
-{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_del_driver(&wm8990_i2c_driver);
-#endif
-}
-module_exit(wm8990_exit);
+module_i2c_driver(wm8990_i2c_driver);
MODULE_DESCRIPTION("ASoC WM8990 driver");
MODULE_AUTHOR("Liam Girdwood");
diff --git a/sound/soc/codecs/wm8990.h b/sound/soc/codecs/wm8990.h
index 77c98a4bfe9c..0e9c78040c4c 100644
--- a/sound/soc/codecs/wm8990.h
+++ b/sound/soc/codecs/wm8990.h
@@ -78,7 +78,6 @@
#define WM8990_PLL1 0x3C
#define WM8990_PLL2 0x3D
#define WM8990_PLL3 0x3E
-#define WM8990_INTDRIVBITS 0x3F
#define WM8990_EXT_ACCESS_ENA 0x75
#define WM8990_EXT_CTL1 0x7a
@@ -818,14 +817,6 @@
*/
#define WM8990_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */
-/*
- * R63 (0x3F) - Internal Driver Bits
- */
-#define WM8990_INMIXL_PWR_BIT 0
-#define WM8990_AINLMUX_PWR_BIT 1
-#define WM8990_INMIXR_PWR_BIT 2
-#define WM8990_AINRMUX_PWR_BIT 3
-
#define WM8990_MCLK_DIV 0
#define WM8990_DACCLK_DIV 1
#define WM8990_ADCCLK_DIV 2
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
index 3a39df7a3829..dba0306c42a5 100644
--- a/sound/soc/codecs/wm8991.c
+++ b/sound/soc/codecs/wm8991.c
@@ -18,6 +18,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -31,77 +32,84 @@
#include "wm8991.h"
struct wm8991_priv {
- enum snd_soc_control_type control_type;
+ struct regmap *regmap;
unsigned int pcmclk;
};
-static const u16 wm8991_reg_defs[] = {
- 0x8991, /* R0 - Reset */
- 0x0000, /* R1 - Power Management (1) */
- 0x6000, /* R2 - Power Management (2) */
- 0x0000, /* R3 - Power Management (3) */
- 0x4050, /* R4 - Audio Interface (1) */
- 0x4000, /* R5 - Audio Interface (2) */
- 0x01C8, /* R6 - Clocking (1) */
- 0x0000, /* R7 - Clocking (2) */
- 0x0040, /* R8 - Audio Interface (3) */
- 0x0040, /* R9 - Audio Interface (4) */
- 0x0004, /* R10 - DAC CTRL */
- 0x00C0, /* R11 - Left DAC Digital Volume */
- 0x00C0, /* R12 - Right DAC Digital Volume */
- 0x0000, /* R13 - Digital Side Tone */
- 0x0100, /* R14 - ADC CTRL */
- 0x00C0, /* R15 - Left ADC Digital Volume */
- 0x00C0, /* R16 - Right ADC Digital Volume */
- 0x0000, /* R17 */
- 0x0000, /* R18 - GPIO CTRL 1 */
- 0x1000, /* R19 - GPIO1 & GPIO2 */
- 0x1010, /* R20 - GPIO3 & GPIO4 */
- 0x1010, /* R21 - GPIO5 & GPIO6 */
- 0x8000, /* R22 - GPIOCTRL 2 */
- 0x0800, /* R23 - GPIO_POL */
- 0x008B, /* R24 - Left Line Input 1&2 Volume */
- 0x008B, /* R25 - Left Line Input 3&4 Volume */
- 0x008B, /* R26 - Right Line Input 1&2 Volume */
- 0x008B, /* R27 - Right Line Input 3&4 Volume */
- 0x0000, /* R28 - Left Output Volume */
- 0x0000, /* R29 - Right Output Volume */
- 0x0066, /* R30 - Line Outputs Volume */
- 0x0022, /* R31 - Out3/4 Volume */
- 0x0079, /* R32 - Left OPGA Volume */
- 0x0079, /* R33 - Right OPGA Volume */
- 0x0003, /* R34 - Speaker Volume */
- 0x0003, /* R35 - ClassD1 */
- 0x0000, /* R36 */
- 0x0100, /* R37 - ClassD3 */
- 0x0000, /* R38 */
- 0x0000, /* R39 - Input Mixer1 */
- 0x0000, /* R40 - Input Mixer2 */
- 0x0000, /* R41 - Input Mixer3 */
- 0x0000, /* R42 - Input Mixer4 */
- 0x0000, /* R43 - Input Mixer5 */
- 0x0000, /* R44 - Input Mixer6 */
- 0x0000, /* R45 - Output Mixer1 */
- 0x0000, /* R46 - Output Mixer2 */
- 0x0000, /* R47 - Output Mixer3 */
- 0x0000, /* R48 - Output Mixer4 */
- 0x0000, /* R49 - Output Mixer5 */
- 0x0000, /* R50 - Output Mixer6 */
- 0x0180, /* R51 - Out3/4 Mixer */
- 0x0000, /* R52 - Line Mixer1 */
- 0x0000, /* R53 - Line Mixer2 */
- 0x0000, /* R54 - Speaker Mixer */
- 0x0000, /* R55 - Additional Control */
- 0x0000, /* R56 - AntiPOP1 */
- 0x0000, /* R57 - AntiPOP2 */
- 0x0000, /* R58 - MICBIAS */
- 0x0000, /* R59 */
- 0x0008, /* R60 - PLL1 */
- 0x0031, /* R61 - PLL2 */
- 0x0026, /* R62 - PLL3 */
+static const struct reg_default wm8991_reg_defaults[] = {
+ { 1, 0x0000 }, /* R1 - Power Management (1) */
+ { 2, 0x6000 }, /* R2 - Power Management (2) */
+ { 3, 0x0000 }, /* R3 - Power Management (3) */
+ { 4, 0x4050 }, /* R4 - Audio Interface (1) */
+ { 5, 0x4000 }, /* R5 - Audio Interface (2) */
+ { 6, 0x01C8 }, /* R6 - Clocking (1) */
+ { 7, 0x0000 }, /* R7 - Clocking (2) */
+ { 8, 0x0040 }, /* R8 - Audio Interface (3) */
+ { 9, 0x0040 }, /* R9 - Audio Interface (4) */
+ { 10, 0x0004 }, /* R10 - DAC CTRL */
+ { 11, 0x00C0 }, /* R11 - Left DAC Digital Volume */
+ { 12, 0x00C0 }, /* R12 - Right DAC Digital Volume */
+ { 13, 0x0000 }, /* R13 - Digital Side Tone */
+ { 14, 0x0100 }, /* R14 - ADC CTRL */
+ { 15, 0x00C0 }, /* R15 - Left ADC Digital Volume */
+ { 16, 0x00C0 }, /* R16 - Right ADC Digital Volume */
+
+ { 18, 0x0000 }, /* R18 - GPIO CTRL 1 */
+ { 19, 0x1000 }, /* R19 - GPIO1 & GPIO2 */
+ { 20, 0x1010 }, /* R20 - GPIO3 & GPIO4 */
+ { 21, 0x1010 }, /* R21 - GPIO5 & GPIO6 */
+ { 22, 0x8000 }, /* R22 - GPIOCTRL 2 */
+ { 23, 0x0800 }, /* R23 - GPIO_POL */
+ { 24, 0x008B }, /* R24 - Left Line Input 1&2 Volume */
+ { 25, 0x008B }, /* R25 - Left Line Input 3&4 Volume */
+ { 26, 0x008B }, /* R26 - Right Line Input 1&2 Volume */
+ { 27, 0x008B }, /* R27 - Right Line Input 3&4 Volume */
+ { 28, 0x0000 }, /* R28 - Left Output Volume */
+ { 29, 0x0000 }, /* R29 - Right Output Volume */
+ { 30, 0x0066 }, /* R30 - Line Outputs Volume */
+ { 31, 0x0022 }, /* R31 - Out3/4 Volume */
+ { 32, 0x0079 }, /* R32 - Left OPGA Volume */
+ { 33, 0x0079 }, /* R33 - Right OPGA Volume */
+ { 34, 0x0003 }, /* R34 - Speaker Volume */
+ { 35, 0x0003 }, /* R35 - ClassD1 */
+
+ { 37, 0x0100 }, /* R37 - ClassD3 */
+
+ { 39, 0x0000 }, /* R39 - Input Mixer1 */
+ { 40, 0x0000 }, /* R40 - Input Mixer2 */
+ { 41, 0x0000 }, /* R41 - Input Mixer3 */
+ { 42, 0x0000 }, /* R42 - Input Mixer4 */
+ { 43, 0x0000 }, /* R43 - Input Mixer5 */
+ { 44, 0x0000 }, /* R44 - Input Mixer6 */
+ { 45, 0x0000 }, /* R45 - Output Mixer1 */
+ { 46, 0x0000 }, /* R46 - Output Mixer2 */
+ { 47, 0x0000 }, /* R47 - Output Mixer3 */
+ { 48, 0x0000 }, /* R48 - Output Mixer4 */
+ { 49, 0x0000 }, /* R49 - Output Mixer5 */
+ { 50, 0x0000 }, /* R50 - Output Mixer6 */
+ { 51, 0x0180 }, /* R51 - Out3/4 Mixer */
+ { 52, 0x0000 }, /* R52 - Line Mixer1 */
+ { 53, 0x0000 }, /* R53 - Line Mixer2 */
+ { 54, 0x0000 }, /* R54 - Speaker Mixer */
+ { 55, 0x0000 }, /* R55 - Additional Control */
+ { 56, 0x0000 }, /* R56 - AntiPOP1 */
+ { 57, 0x0000 }, /* R57 - AntiPOP2 */
+ { 58, 0x0000 }, /* R58 - MICBIAS */
+
+ { 60, 0x0008 }, /* R60 - PLL1 */
+ { 61, 0x0031 }, /* R61 - PLL2 */
+ { 62, 0x0026 }, /* R62 - PLL3 */
};
-#define wm8991_reset(c) snd_soc_write(c, WM8991_RESET, 0)
+static bool wm8991_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case WM8991_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
static const unsigned int rec_mix_tlv[] = {
TLV_DB_RANGE_HEAD(1),
@@ -374,30 +382,6 @@ static const struct snd_kcontrol_new wm8991_snd_controls[] = {
/*
* _DAPM_ Controls
*/
-static int inmixer_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- u16 reg, fakepower;
-
- reg = snd_soc_read(w->codec, WM8991_POWER_MANAGEMENT_2);
- fakepower = snd_soc_read(w->codec, WM8991_INTDRIVBITS);
-
- if (fakepower & ((1 << WM8991_INMIXL_PWR_BIT) |
- (1 << WM8991_AINLMUX_PWR_BIT)))
- reg |= WM8991_AINL_ENA;
- else
- reg &= ~WM8991_AINL_ENA;
-
- if (fakepower & ((1 << WM8991_INMIXR_PWR_BIT) |
- (1 << WM8991_AINRMUX_PWR_BIT)))
- reg |= WM8991_AINR_ENA;
- else
- reg &= ~WM8991_AINR_ENA;
-
- snd_soc_write(w->codec, WM8991_POWER_MANAGEMENT_2, reg);
- return 0;
-}
-
static int outmixer_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -655,6 +639,11 @@ static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("RIN2"),
SND_SOC_DAPM_INPUT("Internal ADC Source"),
+ SND_SOC_DAPM_SUPPLY("INL", WM8991_POWER_MANAGEMENT_2,
+ WM8991_AINL_ENA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("INR", WM8991_POWER_MANAGEMENT_2,
+ WM8991_AINR_ENA_BIT, 0, NULL, 0),
+
/* DACs */
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8991_POWER_MANAGEMENT_2,
WM8991_ADCL_ENA_BIT, 0),
@@ -676,26 +665,22 @@ static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = {
ARRAY_SIZE(wm8991_dapm_rin34_pga_controls)),
/* INMIXL */
- SND_SOC_DAPM_MIXER_E("INMIXL", WM8991_INTDRIVBITS, WM8991_INMIXL_PWR_BIT, 0,
+ SND_SOC_DAPM_MIXER("INMIXL", SND_SOC_NOPM, 0, 0,
&wm8991_dapm_inmixl_controls[0],
- ARRAY_SIZE(wm8991_dapm_inmixl_controls),
- inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ ARRAY_SIZE(wm8991_dapm_inmixl_controls)),
/* AINLMUX */
- SND_SOC_DAPM_MUX_E("AINLMUX", WM8991_INTDRIVBITS, WM8991_AINLMUX_PWR_BIT, 0,
- &wm8991_dapm_ainlmux_controls, inmixer_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("AINLMUX", SND_SOC_NOPM, 0, 0,
+ &wm8991_dapm_ainlmux_controls),
/* INMIXR */
- SND_SOC_DAPM_MIXER_E("INMIXR", WM8991_INTDRIVBITS, WM8991_INMIXR_PWR_BIT, 0,
+ SND_SOC_DAPM_MIXER("INMIXR", SND_SOC_NOPM, 0, 0,
&wm8991_dapm_inmixr_controls[0],
- ARRAY_SIZE(wm8991_dapm_inmixr_controls),
- inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ ARRAY_SIZE(wm8991_dapm_inmixr_controls)),
/* AINRMUX */
- SND_SOC_DAPM_MUX_E("AINRMUX", WM8991_INTDRIVBITS, WM8991_AINRMUX_PWR_BIT, 0,
- &wm8991_dapm_ainrmux_controls, inmixer_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("AINRMUX", SND_SOC_NOPM, 0, 0,
+ &wm8991_dapm_ainrmux_controls),
/* Output Side */
/* DACs */
@@ -787,7 +772,7 @@ static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route wm8991_dapm_routes[] = {
/* Make DACs turn on when playing even if not mixed into any outputs */
{"Internal DAC Sink", NULL, "Left DAC"},
{"Internal DAC Sink", NULL, "Right DAC"},
@@ -797,6 +782,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Right ADC", NULL, "Internal ADC Source"},
/* Input Side */
+ {"INMIXL", NULL, "INL"},
+ {"AINLMUX", NULL, "INL"},
+ {"INMIXR", NULL, "INR"},
+ {"AINRMUX", NULL, "INR"},
/* LIN12 PGA */
{"LIN12 PGA", "LIN1 Switch", "LIN1"},
{"LIN12 PGA", "LIN2 Switch", "LIN2"},
@@ -1129,6 +1118,7 @@ static int wm8991_mute(struct snd_soc_dai *dai, int mute)
static int wm8991_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct wm8991_priv *wm8991 = snd_soc_codec_get_drvdata(codec);
u16 val;
switch (level) {
@@ -1144,7 +1134,7 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- snd_soc_cache_sync(codec);
+ regcache_sync(wm8991->regmap);
/* Enable all output discharge bits */
snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
@@ -1232,7 +1222,7 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec,
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
snd_soc_write(codec, WM8991_ANTIPOP2, 0x0);
- codec->cache_sync = 1;
+ regcache_mark_dirty(wm8991->regmap);
break;
}
@@ -1266,44 +1256,14 @@ static int wm8991_probe(struct snd_soc_codec *codec)
wm8991 = snd_soc_codec_get_drvdata(codec);
- ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8991->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret);
return ret;
}
- ret = wm8991_reset(codec);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
- return ret;
- }
-
wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- snd_soc_update_bits(codec, WM8991_AUDIO_INTERFACE_4,
- WM8991_ALRCGPIO1, WM8991_ALRCGPIO1);
-
- snd_soc_update_bits(codec, WM8991_GPIO1_GPIO2,
- WM8991_GPIO1_SEL_MASK, 1);
-
- snd_soc_update_bits(codec, WM8991_POWER_MANAGEMENT_1,
- WM8991_VREF_ENA | WM8991_VMID_MODE_MASK,
- WM8991_VREF_ENA | WM8991_VMID_MODE_MASK);
-
- snd_soc_update_bits(codec, WM8991_POWER_MANAGEMENT_2,
- WM8991_OPCLK_ENA, WM8991_OPCLK_ENA);
-
- snd_soc_write(codec, WM8991_DAC_CTRL, 0);
- snd_soc_write(codec, WM8991_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
- snd_soc_write(codec, WM8991_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
-
- snd_soc_add_codec_controls(codec, wm8991_snd_controls,
- ARRAY_SIZE(wm8991_snd_controls));
-
- snd_soc_dapm_new_controls(&codec->dapm, wm8991_dapm_widgets,
- ARRAY_SIZE(wm8991_dapm_widgets));
- snd_soc_dapm_add_routes(&codec->dapm, audio_map,
- ARRAY_SIZE(audio_map));
return 0;
}
@@ -1352,24 +1312,77 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8991 = {
.suspend = wm8991_suspend,
.resume = wm8991_resume,
.set_bias_level = wm8991_set_bias_level,
- .reg_cache_size = WM8991_MAX_REGISTER + 1,
- .reg_word_size = sizeof(u16),
- .reg_cache_default = wm8991_reg_defs
+ .controls = wm8991_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8991_snd_controls),
+ .dapm_widgets = wm8991_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8991_dapm_widgets),
+ .dapm_routes = wm8991_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8991_dapm_routes),
+};
+
+static const struct regmap_config wm8991_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .max_register = WM8991_PLL3,
+ .volatile_reg = wm8991_volatile,
+ .reg_defaults = wm8991_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(wm8991_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
};
static int wm8991_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8991_priv *wm8991;
+ unsigned int val;
int ret;
wm8991 = devm_kzalloc(&i2c->dev, sizeof(*wm8991), GFP_KERNEL);
if (!wm8991)
return -ENOMEM;
- wm8991->control_type = SND_SOC_I2C;
+ wm8991->regmap = devm_regmap_init_i2c(i2c, &wm8991_regmap);
+ if (IS_ERR(wm8991->regmap))
+ return PTR_ERR(wm8991->regmap);
+
i2c_set_clientdata(i2c, wm8991);
+ ret = regmap_read(wm8991->regmap, WM8991_RESET, &val);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to read device ID: %d\n", ret);
+ return ret;
+ }
+ if (val != 0x8991) {
+ dev_err(&i2c->dev, "Device with ID %x is not a WM8991\n", val);
+ return -EINVAL;
+ }
+
+ ret = regmap_write(wm8991->regmap, WM8991_RESET, 0);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
+
+ regmap_update_bits(wm8991->regmap, WM8991_AUDIO_INTERFACE_4,
+ WM8991_ALRCGPIO1, WM8991_ALRCGPIO1);
+
+ regmap_update_bits(wm8991->regmap, WM8991_GPIO1_GPIO2,
+ WM8991_GPIO1_SEL_MASK, 1);
+
+ regmap_update_bits(wm8991->regmap, WM8991_POWER_MANAGEMENT_1,
+ WM8991_VREF_ENA | WM8991_VMID_MODE_MASK,
+ WM8991_VREF_ENA | WM8991_VMID_MODE_MASK);
+
+ regmap_update_bits(wm8991->regmap, WM8991_POWER_MANAGEMENT_2,
+ WM8991_OPCLK_ENA, WM8991_OPCLK_ENA);
+
+ regmap_write(wm8991->regmap, WM8991_DAC_CTRL, 0);
+ regmap_write(wm8991->regmap, WM8991_LEFT_OUTPUT_VOLUME,
+ 0x50 | (1<<8));
+ regmap_write(wm8991->regmap, WM8991_RIGHT_OUTPUT_VOLUME,
+ 0x50 | (1<<8));
+
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8991, &wm8991_dai, 1);
diff --git a/sound/soc/codecs/wm8991.h b/sound/soc/codecs/wm8991.h
index 07707d8d7e20..08ed383303c0 100644
--- a/sound/soc/codecs/wm8991.h
+++ b/sound/soc/codecs/wm8991.h
@@ -76,7 +76,6 @@
#define WM8991_PLL1 0x3C
#define WM8991_PLL2 0x3D
#define WM8991_PLL3 0x3E
-#define WM8991_INTDRIVBITS 0x3F
#define WM8991_REGISTER_COUNT 60
#define WM8991_MAX_REGISTER 0x3F
@@ -807,14 +806,6 @@
*/
#define WM8991_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */
-/*
- * R63 (0x3F) - Internal Driver Bits
- */
-#define WM8991_INMIXL_PWR_BIT 0
-#define WM8991_AINLMUX_PWR_BIT 1
-#define WM8991_INMIXR_PWR_BIT 2
-#define WM8991_AINRMUX_PWR_BIT 3
-
#define WM8991_MCLK_DIV 0
#define WM8991_DACCLK_DIV 1
#define WM8991_ADCCLK_DIV 2
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 86426a117b07..b9be9cbc4603 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -4077,12 +4077,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT,
wm8994_temp_shut, "Thermal shutdown", codec);
- ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE,
- wm_hubs_dcs_done, "DC servo done",
- &wm8994->hubs);
- if (ret == 0)
- wm8994->hubs.dcs_done_irq = true;
-
switch (control->type) {
case WM8994:
if (wm8994->micdet_irq) {
@@ -4313,6 +4307,11 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
}
wm_hubs_add_analogue_routes(codec, 0, 0);
+ ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE,
+ wm_hubs_dcs_done, "DC servo done",
+ &wm8994->hubs);
+ if (ret == 0)
+ wm8994->hubs.dcs_done_irq = true;
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
switch (control->type) {
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index da2899e6c401..4300caff1783 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -2293,7 +2293,7 @@ static struct spi_driver wm8995_spi_driver = {
};
#endif
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm8995_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -2350,7 +2350,7 @@ static int __init wm8995_modinit(void)
{
int ret = 0;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
ret = i2c_add_driver(&wm8995_i2c_driver);
if (ret) {
printk(KERN_ERR "Failed to register wm8995 I2C driver: %d\n",
@@ -2371,7 +2371,7 @@ module_init(wm8995_modinit);
static void __exit wm8995_exit(void)
{
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
i2c_del_driver(&wm8995_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 630b3d776ec2..0982c1d38ec4 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -1326,7 +1326,7 @@ static const struct regmap_config wm9081_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+#if IS_ENABLED(CONFIG_I2C)
static int wm9081_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index b42f9af163c8..fb0c678939bf 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -1469,19 +1469,23 @@ static int wm_adsp2_ena(struct wm_adsp *dsp)
unsigned int val;
int ret, count;
- ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA, ADSP2_SYS_ENA);
+ ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, ADSP2_SYS_ENA);
if (ret != 0)
return ret;
/* Wait for the RAM to start, should be near instantaneous */
- count = 0;
- do {
+ for (count = 0; count < 10; ++count) {
ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
&val);
if (ret != 0)
return ret;
- } while (!(val & ADSP2_RAM_RDY) && ++count < 10);
+
+ if (val & ADSP2_RAM_RDY)
+ break;
+
+ msleep(1);
+ }
if (!(val & ADSP2_RAM_RDY)) {
adsp_err(dsp, "Failed to start DSP RAM\n");
@@ -1489,7 +1493,6 @@ static int wm_adsp2_ena(struct wm_adsp *dsp)
}
adsp_dbg(dsp, "RAM ready after %d polls\n", count);
- adsp_info(dsp, "RAM ready after %d polls\n", count);
return 0;
}
@@ -1522,9 +1525,9 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
val = (val & ARIZONA_SYSCLK_FREQ_MASK)
>> ARIZONA_SYSCLK_FREQ_SHIFT;
- ret = regmap_update_bits(dsp->regmap,
- dsp->base + ADSP2_CLOCKING,
- ADSP2_CLK_SEL_MASK, val);
+ ret = regmap_update_bits_async(dsp->regmap,
+ dsp->base + ADSP2_CLOCKING,
+ ADSP2_CLK_SEL_MASK, val);
if (ret != 0) {
adsp_err(dsp, "Failed to set clock rate: %d\n",
ret);
@@ -1587,10 +1590,10 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
- ret = regmap_update_bits(dsp->regmap,
- dsp->base + ADSP2_CONTROL,
- ADSP2_CORE_ENA | ADSP2_START,
- ADSP2_CORE_ENA | ADSP2_START);
+ ret = regmap_update_bits_async(dsp->regmap,
+ dsp->base + ADSP2_CONTROL,
+ ADSP2_CORE_ENA | ADSP2_START,
+ ADSP2_CORE_ENA | ADSP2_START);
if (ret != 0)
goto err;
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 95970f5db3ec..a8ec1fc3e4d0 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -1,11 +1,6 @@
config SND_DAVINCI_SOC
- tristate "SoC Audio for the TI DAVINCI or AM33XX chip"
- depends on ARCH_DAVINCI || SOC_AM33XX
- help
- Platform driver for daVinci or AM33xx
- Say Y or M if you want to add support for codecs attached to
- the DAVINCI AC97, I2S, or McASP interface. You will also need
- to select the audio interfaces to support below.
+ tristate "SoC Audio for TI DAVINCI or AM33XX/AM43XX chips"
+ depends on ARCH_DAVINCI || SOC_AM33XX || SOC_AM43XX
config SND_DAVINCI_SOC_I2S
tristate
@@ -16,11 +11,15 @@ config SND_DAVINCI_SOC_MCASP
config SND_DAVINCI_SOC_VCIF
tristate
+config SND_DAVINCI_SOC_GENERIC_EVM
+ tristate
+ select SND_SOC_TLV320AIC3X
+ select SND_DAVINCI_SOC_MCASP
+
config SND_AM33XX_SOC_EVM
tristate "SoC Audio for the AM33XX chip based boards"
depends on SND_DAVINCI_SOC && SOC_AM33XX
- select SND_SOC_TLV320AIC3X
- select SND_DAVINCI_SOC_MCASP
+ select SND_DAVINCI_SOC_GENERIC_EVM
help
Say Y or M if you want to add support for SoC audio on AM33XX
boards using McASP and TLV320AIC3X codec. For example AM335X-EVM,
@@ -31,8 +30,7 @@ config SND_DAVINCI_SOC_EVM
tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
depends on SND_DAVINCI_SOC
depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM
- select SND_DAVINCI_SOC_I2S
- select SND_SOC_TLV320AIC3X
+ select SND_DAVINCI_SOC_GENERIC_EVM
help
Say Y if you want to add support for SoC audio on TI
DaVinci DM6446, DM355 or DM365 EVM platforms.
@@ -59,8 +57,7 @@ endchoice
config SND_DM6467_SOC_EVM
tristate "SoC Audio support for DaVinci DM6467 EVM"
depends on SND_DAVINCI_SOC && MACH_DAVINCI_DM6467_EVM
- select SND_DAVINCI_SOC_MCASP
- select SND_SOC_TLV320AIC3X
+ select SND_DAVINCI_SOC_GENERIC_EVM
select SND_SOC_SPDIF
help
@@ -69,8 +66,7 @@ config SND_DM6467_SOC_EVM
config SND_DA830_SOC_EVM
tristate "SoC Audio support for DA830/OMAP-L137 EVM"
depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA830_EVM
- select SND_DAVINCI_SOC_MCASP
- select SND_SOC_TLV320AIC3X
+ select SND_DAVINCI_SOC_GENERIC_EVM
help
Say Y if you want to add support for SoC audio on TI
@@ -79,8 +75,7 @@ config SND_DA830_SOC_EVM
config SND_DA850_SOC_EVM
tristate "SoC Audio support for DA850/OMAP-L138 EVM"
depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM
- select SND_DAVINCI_SOC_MCASP
- select SND_SOC_TLV320AIC3X
+ select SND_DAVINCI_SOC_GENERIC_EVM
help
Say Y if you want to add support for SoC audio on TI
DA850/OMAP-L138 EVM
diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile
index bc81e79fc301..744d4d9a0184 100644
--- a/sound/soc/davinci/Makefile
+++ b/sound/soc/davinci/Makefile
@@ -9,11 +9,7 @@ obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o
obj-$(CONFIG_SND_DAVINCI_SOC_VCIF) += snd-soc-davinci-vcif.o
-# DAVINCI Machine Support
+# Generic DAVINCI/AM33xx Machine Support
snd-soc-evm-objs := davinci-evm.o
-obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o
-obj-$(CONFIG_SND_AM33XX_SOC_EVM) += snd-soc-evm.o
-obj-$(CONFIG_SND_DM6467_SOC_EVM) += snd-soc-evm.o
-obj-$(CONFIG_SND_DA830_SOC_EVM) += snd-soc-evm.o
-obj-$(CONFIG_SND_DA850_SOC_EVM) += snd-soc-evm.o
+obj-$(CONFIG_SND_DAVINCI_SOC_GENERIC_EVM) += snd-soc-evm.o
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index 623eb5e7c089..70ff3772079f 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -28,14 +28,11 @@
#include "davinci-pcm.h"
#include "davinci-i2s.h"
-#include "davinci-mcasp.h"
struct snd_soc_card_drvdata_davinci {
unsigned sysclk;
};
-#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
- SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF)
static int evm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -48,16 +45,6 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *)
snd_soc_card_get_drvdata(soc_card))->sysclk;
- /* set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT);
- if (ret < 0)
- return ret;
-
- /* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
- if (ret < 0)
- return ret;
-
/* set the codec system clock */
ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
if (ret < 0)
@@ -71,24 +58,10 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int evm_spdif_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- /* set cpu DAI configuration */
- return snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
-}
-
static struct snd_soc_ops evm_ops = {
.hw_params = evm_hw_params,
};
-static struct snd_soc_ops evm_spdif_ops = {
- .hw_params = evm_spdif_hw_params,
-};
-
/* davinci-evm machine dapm widgets */
static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
@@ -165,6 +138,8 @@ static struct snd_soc_dai_link dm6446_evm_dai = {
.platform_name = "davinci-mcbsp",
.init = evm_aic3x_init,
.ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
};
static struct snd_soc_dai_link dm355_evm_dai = {
@@ -176,6 +151,8 @@ static struct snd_soc_dai_link dm355_evm_dai = {
.platform_name = "davinci-mcbsp.1",
.init = evm_aic3x_init,
.ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
};
static struct snd_soc_dai_link dm365_evm_dai = {
@@ -184,10 +161,12 @@ static struct snd_soc_dai_link dm365_evm_dai = {
.stream_name = "AIC3X",
.cpu_dai_name = "davinci-mcbsp",
.codec_dai_name = "tlv320aic3x-hifi",
- .init = evm_aic3x_init,
.codec_name = "tlv320aic3x-codec.1-0018",
- .ops = &evm_ops,
.platform_name = "davinci-mcbsp",
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
#elif defined(CONFIG_SND_DM365_VOICE_CODEC)
.name = "Voice Codec - CQ93VC",
.stream_name = "CQ93",
@@ -208,6 +187,8 @@ static struct snd_soc_dai_link dm6467_evm_dai[] = {
.codec_name = "tlv320aic3x-codec.0-001a",
.init = evm_aic3x_init,
.ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
},
{
.name = "McASP",
@@ -216,7 +197,8 @@ static struct snd_soc_dai_link dm6467_evm_dai[] = {
.codec_dai_name = "dit-hifi",
.codec_name = "spdif_dit",
.platform_name = "davinci-mcasp.1",
- .ops = &evm_spdif_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
},
};
@@ -229,6 +211,8 @@ static struct snd_soc_dai_link da830_evm_dai = {
.platform_name = "davinci-mcasp.1",
.init = evm_aic3x_init,
.ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
};
static struct snd_soc_dai_link da850_evm_dai = {
@@ -240,6 +224,8 @@ static struct snd_soc_dai_link da850_evm_dai = {
.platform_name = "davinci-mcasp.0",
.init = evm_aic3x_init,
.ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
};
/* davinci dm6446 evm audio machine driver */
@@ -336,6 +322,8 @@ static struct snd_soc_dai_link evm_dai_tlv320aic3x = {
.codec_dai_name = "tlv320aic3x-hifi",
.ops = &evm_ops,
.init = evm_aic3x_init,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
};
static const struct of_device_id davinci_evm_dt_ids[] = {
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 71e14bb3a8cd..b7858bfa0295 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -21,6 +21,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/io.h>
+#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_platform.h>
@@ -31,351 +32,147 @@
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
#include "davinci-pcm.h"
#include "davinci-mcasp.h"
-/*
- * McASP register definitions
- */
-#define DAVINCI_MCASP_PID_REG 0x00
-#define DAVINCI_MCASP_PWREMUMGT_REG 0x04
-
-#define DAVINCI_MCASP_PFUNC_REG 0x10
-#define DAVINCI_MCASP_PDIR_REG 0x14
-#define DAVINCI_MCASP_PDOUT_REG 0x18
-#define DAVINCI_MCASP_PDSET_REG 0x1c
-
-#define DAVINCI_MCASP_PDCLR_REG 0x20
-
-#define DAVINCI_MCASP_TLGC_REG 0x30
-#define DAVINCI_MCASP_TLMR_REG 0x34
-
-#define DAVINCI_MCASP_GBLCTL_REG 0x44
-#define DAVINCI_MCASP_AMUTE_REG 0x48
-#define DAVINCI_MCASP_LBCTL_REG 0x4c
-
-#define DAVINCI_MCASP_TXDITCTL_REG 0x50
-
-#define DAVINCI_MCASP_GBLCTLR_REG 0x60
-#define DAVINCI_MCASP_RXMASK_REG 0x64
-#define DAVINCI_MCASP_RXFMT_REG 0x68
-#define DAVINCI_MCASP_RXFMCTL_REG 0x6c
-
-#define DAVINCI_MCASP_ACLKRCTL_REG 0x70
-#define DAVINCI_MCASP_AHCLKRCTL_REG 0x74
-#define DAVINCI_MCASP_RXTDM_REG 0x78
-#define DAVINCI_MCASP_EVTCTLR_REG 0x7c
-
-#define DAVINCI_MCASP_RXSTAT_REG 0x80
-#define DAVINCI_MCASP_RXTDMSLOT_REG 0x84
-#define DAVINCI_MCASP_RXCLKCHK_REG 0x88
-#define DAVINCI_MCASP_REVTCTL_REG 0x8c
-
-#define DAVINCI_MCASP_GBLCTLX_REG 0xa0
-#define DAVINCI_MCASP_TXMASK_REG 0xa4
-#define DAVINCI_MCASP_TXFMT_REG 0xa8
-#define DAVINCI_MCASP_TXFMCTL_REG 0xac
-
-#define DAVINCI_MCASP_ACLKXCTL_REG 0xb0
-#define DAVINCI_MCASP_AHCLKXCTL_REG 0xb4
-#define DAVINCI_MCASP_TXTDM_REG 0xb8
-#define DAVINCI_MCASP_EVTCTLX_REG 0xbc
-
-#define DAVINCI_MCASP_TXSTAT_REG 0xc0
-#define DAVINCI_MCASP_TXTDMSLOT_REG 0xc4
-#define DAVINCI_MCASP_TXCLKCHK_REG 0xc8
-#define DAVINCI_MCASP_XEVTCTL_REG 0xcc
-
-/* Left(even TDM Slot) Channel Status Register File */
-#define DAVINCI_MCASP_DITCSRA_REG 0x100
-/* Right(odd TDM slot) Channel Status Register File */
-#define DAVINCI_MCASP_DITCSRB_REG 0x118
-/* Left(even TDM slot) User Data Register File */
-#define DAVINCI_MCASP_DITUDRA_REG 0x130
-/* Right(odd TDM Slot) User Data Register File */
-#define DAVINCI_MCASP_DITUDRB_REG 0x148
-
-/* Serializer n Control Register */
-#define DAVINCI_MCASP_XRSRCTL_BASE_REG 0x180
-#define DAVINCI_MCASP_XRSRCTL_REG(n) (DAVINCI_MCASP_XRSRCTL_BASE_REG + \
- (n << 2))
-
-/* Transmit Buffer for Serializer n */
-#define DAVINCI_MCASP_TXBUF_REG 0x200
-/* Receive Buffer for Serializer n */
-#define DAVINCI_MCASP_RXBUF_REG 0x280
-
-/* McASP FIFO Registers */
-#define DAVINCI_MCASP_WFIFOCTL (0x1010)
-#define DAVINCI_MCASP_WFIFOSTS (0x1014)
-#define DAVINCI_MCASP_RFIFOCTL (0x1018)
-#define DAVINCI_MCASP_RFIFOSTS (0x101C)
-#define MCASP_VER3_WFIFOCTL (0x1000)
-#define MCASP_VER3_WFIFOSTS (0x1004)
-#define MCASP_VER3_RFIFOCTL (0x1008)
-#define MCASP_VER3_RFIFOSTS (0x100C)
-
-/*
- * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management
- * Register Bits
- */
-#define MCASP_FREE BIT(0)
-#define MCASP_SOFT BIT(1)
-
-/*
- * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits
- */
-#define AXR(n) (1<<n)
-#define PFUNC_AMUTE BIT(25)
-#define ACLKX BIT(26)
-#define AHCLKX BIT(27)
-#define AFSX BIT(28)
-#define ACLKR BIT(29)
-#define AHCLKR BIT(30)
-#define AFSR BIT(31)
-
-/*
- * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits
- */
-#define AXR(n) (1<<n)
-#define PDIR_AMUTE BIT(25)
-#define ACLKX BIT(26)
-#define AHCLKX BIT(27)
-#define AFSX BIT(28)
-#define ACLKR BIT(29)
-#define AHCLKR BIT(30)
-#define AFSR BIT(31)
-
-/*
- * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits
- */
-#define DITEN BIT(0) /* Transmit DIT mode enable/disable */
-#define VA BIT(2)
-#define VB BIT(3)
-
-/*
- * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits
- */
-#define TXROT(val) (val)
-#define TXSEL BIT(3)
-#define TXSSZ(val) (val<<4)
-#define TXPBIT(val) (val<<8)
-#define TXPAD(val) (val<<13)
-#define TXORD BIT(15)
-#define FSXDLY(val) (val<<16)
-
-/*
- * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits
- */
-#define RXROT(val) (val)
-#define RXSEL BIT(3)
-#define RXSSZ(val) (val<<4)
-#define RXPBIT(val) (val<<8)
-#define RXPAD(val) (val<<13)
-#define RXORD BIT(15)
-#define FSRDLY(val) (val<<16)
-
-/*
- * DAVINCI_MCASP_TXFMCTL_REG - Transmit Frame Control Register Bits
- */
-#define FSXPOL BIT(0)
-#define AFSXE BIT(1)
-#define FSXDUR BIT(4)
-#define FSXMOD(val) (val<<7)
-
-/*
- * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits
- */
-#define FSRPOL BIT(0)
-#define AFSRE BIT(1)
-#define FSRDUR BIT(4)
-#define FSRMOD(val) (val<<7)
-
-/*
- * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits
- */
-#define ACLKXDIV(val) (val)
-#define ACLKXE BIT(5)
-#define TX_ASYNC BIT(6)
-#define ACLKXPOL BIT(7)
-#define ACLKXDIV_MASK 0x1f
-
-/*
- * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits
- */
-#define ACLKRDIV(val) (val)
-#define ACLKRE BIT(5)
-#define RX_ASYNC BIT(6)
-#define ACLKRPOL BIT(7)
-#define ACLKRDIV_MASK 0x1f
-
-/*
- * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control
- * Register Bits
- */
-#define AHCLKXDIV(val) (val)
-#define AHCLKXPOL BIT(14)
-#define AHCLKXE BIT(15)
-#define AHCLKXDIV_MASK 0xfff
+struct davinci_mcasp {
+ struct davinci_pcm_dma_params dma_params[2];
+ struct snd_dmaengine_dai_dma_data dma_data[2];
+ void __iomem *base;
+ u32 fifo_base;
+ struct device *dev;
-/*
- * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control
- * Register Bits
- */
-#define AHCLKRDIV(val) (val)
-#define AHCLKRPOL BIT(14)
-#define AHCLKRE BIT(15)
-#define AHCLKRDIV_MASK 0xfff
+ /* McASP specific data */
+ int tdm_slots;
+ u8 op_mode;
+ u8 num_serializer;
+ u8 *serial_dir;
+ u8 version;
+ u16 bclk_lrclk_ratio;
+ int streams;
-/*
- * DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits
- */
-#define MODE(val) (val)
-#define DISMOD (val)(val<<2)
-#define TXSTATE BIT(4)
-#define RXSTATE BIT(5)
-#define SRMOD_MASK 3
-#define SRMOD_INACTIVE 0
-
-/*
- * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits
- */
-#define LBEN BIT(0)
-#define LBORD BIT(1)
-#define LBGENMODE(val) (val<<2)
+ /* McASP FIFO related */
+ u8 txnumevt;
+ u8 rxnumevt;
-/*
- * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration
- */
-#define TXTDMS(n) (1<<n)
-
-/*
- * DAVINCI_MCASP_RXTDMSLOT_REG - Receive TDM Slot Register configuration
- */
-#define RXTDMS(n) (1<<n)
-
-/*
- * DAVINCI_MCASP_GBLCTL_REG - Global Control Register Bits
- */
-#define RXCLKRST BIT(0) /* Receiver Clock Divider Reset */
-#define RXHCLKRST BIT(1) /* Receiver High Frequency Clock Divider */
-#define RXSERCLR BIT(2) /* Receiver Serializer Clear */
-#define RXSMRST BIT(3) /* Receiver State Machine Reset */
-#define RXFSRST BIT(4) /* Frame Sync Generator Reset */
-#define TXCLKRST BIT(8) /* Transmitter Clock Divider Reset */
-#define TXHCLKRST BIT(9) /* Transmitter High Frequency Clock Divider*/
-#define TXSERCLR BIT(10) /* Transmit Serializer Clear */
-#define TXSMRST BIT(11) /* Transmitter State Machine Reset */
-#define TXFSRST BIT(12) /* Frame Sync Generator Reset */
+ bool dat_port;
-/*
- * DAVINCI_MCASP_AMUTE_REG - Mute Control Register Bits
- */
-#define MUTENA(val) (val)
-#define MUTEINPOL BIT(2)
-#define MUTEINENA BIT(3)
-#define MUTEIN BIT(4)
-#define MUTER BIT(5)
-#define MUTEX BIT(6)
-#define MUTEFSR BIT(7)
-#define MUTEFSX BIT(8)
-#define MUTEBADCLKR BIT(9)
-#define MUTEBADCLKX BIT(10)
-#define MUTERXDMAERR BIT(11)
-#define MUTETXDMAERR BIT(12)
-
-/*
- * DAVINCI_MCASP_REVTCTL_REG - Receiver DMA Event Control Register bits
- */
-#define RXDATADMADIS BIT(0)
-
-/*
- * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits
- */
-#define TXDATADMADIS BIT(0)
-
-/*
- * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits
- */
-#define FIFO_ENABLE BIT(16)
-#define NUMEVT_MASK (0xFF << 8)
-#define NUMDMA_MASK (0xFF)
-
-#define DAVINCI_MCASP_NUM_SERIALIZER 16
+#ifdef CONFIG_PM_SLEEP
+ struct {
+ u32 txfmtctl;
+ u32 rxfmtctl;
+ u32 txfmt;
+ u32 rxfmt;
+ u32 aclkxctl;
+ u32 aclkrctl;
+ u32 pdir;
+ } context;
+#endif
+};
-static inline void mcasp_set_bits(void __iomem *reg, u32 val)
+static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset,
+ u32 val)
{
+ void __iomem *reg = mcasp->base + offset;
__raw_writel(__raw_readl(reg) | val, reg);
}
-static inline void mcasp_clr_bits(void __iomem *reg, u32 val)
+static inline void mcasp_clr_bits(struct davinci_mcasp *mcasp, u32 offset,
+ u32 val)
{
+ void __iomem *reg = mcasp->base + offset;
__raw_writel((__raw_readl(reg) & ~(val)), reg);
}
-static inline void mcasp_mod_bits(void __iomem *reg, u32 val, u32 mask)
+static inline void mcasp_mod_bits(struct davinci_mcasp *mcasp, u32 offset,
+ u32 val, u32 mask)
{
+ void __iomem *reg = mcasp->base + offset;
__raw_writel((__raw_readl(reg) & ~mask) | val, reg);
}
-static inline void mcasp_set_reg(void __iomem *reg, u32 val)
+static inline void mcasp_set_reg(struct davinci_mcasp *mcasp, u32 offset,
+ u32 val)
{
- __raw_writel(val, reg);
+ __raw_writel(val, mcasp->base + offset);
}
-static inline u32 mcasp_get_reg(void __iomem *reg)
+static inline u32 mcasp_get_reg(struct davinci_mcasp *mcasp, u32 offset)
{
- return (unsigned int)__raw_readl(reg);
+ return (u32)__raw_readl(mcasp->base + offset);
}
-static inline void mcasp_set_ctl_reg(void __iomem *regs, u32 val)
+static void mcasp_set_ctl_reg(struct davinci_mcasp *mcasp, u32 ctl_reg, u32 val)
{
int i = 0;
- mcasp_set_bits(regs, val);
+ mcasp_set_bits(mcasp, ctl_reg, val);
/* programming GBLCTL needs to read back from GBLCTL and verfiy */
/* loop count is to avoid the lock-up */
for (i = 0; i < 1000; i++) {
- if ((mcasp_get_reg(regs) & val) == val)
+ if ((mcasp_get_reg(mcasp, ctl_reg) & val) == val)
break;
}
- if (i == 1000 && ((mcasp_get_reg(regs) & val) != val))
+ if (i == 1000 && ((mcasp_get_reg(mcasp, ctl_reg) & val) != val))
printk(KERN_ERR "GBLCTL write error\n");
}
-static void mcasp_start_rx(struct davinci_audio_dev *dev)
+static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp)
{
- mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
- mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
- mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
- mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0);
+ u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
+ u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
- mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
- mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
- mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0);
+ return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE;
+}
- mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
- mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
+static void mcasp_start_rx(struct davinci_mcasp *mcasp)
+{
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
+
+ /*
+ * When ASYNC == 0 the transmit and receive sections operate
+ * synchronously from the transmit clock and frame sync. We need to make
+ * sure that the TX signlas are enabled when starting reception.
+ */
+ if (mcasp_is_synchronous(mcasp)) {
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+ }
+
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
+
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
+
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
+
+ if (mcasp_is_synchronous(mcasp))
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
}
-static void mcasp_start_tx(struct davinci_audio_dev *dev)
+static void mcasp_start_tx(struct davinci_mcasp *mcasp)
{
u8 offset = 0, i;
u32 cnt;
- mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
- mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
- mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
- mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
- mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
- mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
- mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
- for (i = 0; i < dev->num_serializer; i++) {
- if (dev->serial_dir[i] == TX_MODE) {
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
+ for (i = 0; i < mcasp->num_serializer; i++) {
+ if (mcasp->serial_dir[i] == TX_MODE) {
offset = i;
break;
}
@@ -383,156 +180,140 @@ static void mcasp_start_tx(struct davinci_audio_dev *dev)
/* wait for TX ready */
cnt = 0;
- while (!(mcasp_get_reg(dev->base + DAVINCI_MCASP_XRSRCTL_REG(offset)) &
+ while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(offset)) &
TXSTATE) && (cnt < 100000))
cnt++;
- mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
}
-static void davinci_mcasp_start(struct davinci_audio_dev *dev, int stream)
+static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream)
{
+ u32 reg;
+
+ mcasp->streams++;
+
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (dev->txnumevt) { /* enable FIFO */
- switch (dev->version) {
- case MCASP_VERSION_3:
- mcasp_clr_bits(dev->base + MCASP_VER3_WFIFOCTL,
- FIFO_ENABLE);
- mcasp_set_bits(dev->base + MCASP_VER3_WFIFOCTL,
- FIFO_ENABLE);
- break;
- default:
- mcasp_clr_bits(dev->base +
- DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE);
- mcasp_set_bits(dev->base +
- DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE);
- }
+ if (mcasp->txnumevt) { /* enable FIFO */
+ reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
}
- mcasp_start_tx(dev);
+ mcasp_start_tx(mcasp);
} else {
- if (dev->rxnumevt) { /* enable FIFO */
- switch (dev->version) {
- case MCASP_VERSION_3:
- mcasp_clr_bits(dev->base + MCASP_VER3_RFIFOCTL,
- FIFO_ENABLE);
- mcasp_set_bits(dev->base + MCASP_VER3_RFIFOCTL,
- FIFO_ENABLE);
- break;
- default:
- mcasp_clr_bits(dev->base +
- DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE);
- mcasp_set_bits(dev->base +
- DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE);
- }
+ if (mcasp->rxnumevt) { /* enable FIFO */
+ reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
}
- mcasp_start_rx(dev);
+ mcasp_start_rx(mcasp);
}
}
-static void mcasp_stop_rx(struct davinci_audio_dev *dev)
+static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
{
- mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, 0);
- mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+ /*
+ * In synchronous mode stop the TX clocks if no other stream is
+ * running
+ */
+ if (mcasp_is_synchronous(mcasp) && !mcasp->streams)
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0);
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
}
-static void mcasp_stop_tx(struct davinci_audio_dev *dev)
+static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
{
- mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, 0);
- mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+ u32 val = 0;
+
+ /*
+ * In synchronous mode keep TX clocks running if the capture stream is
+ * still running.
+ */
+ if (mcasp_is_synchronous(mcasp) && mcasp->streams)
+ val = TXHCLKRST | TXCLKRST | TXFSRST;
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
}
-static void davinci_mcasp_stop(struct davinci_audio_dev *dev, int stream)
+static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream)
{
+ u32 reg;
+
+ mcasp->streams--;
+
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (dev->txnumevt) { /* disable FIFO */
- switch (dev->version) {
- case MCASP_VERSION_3:
- mcasp_clr_bits(dev->base + MCASP_VER3_WFIFOCTL,
- FIFO_ENABLE);
- break;
- default:
- mcasp_clr_bits(dev->base +
- DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE);
- }
+ if (mcasp->txnumevt) { /* disable FIFO */
+ reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
}
- mcasp_stop_tx(dev);
+ mcasp_stop_tx(mcasp);
} else {
- if (dev->rxnumevt) { /* disable FIFO */
- switch (dev->version) {
- case MCASP_VERSION_3:
- mcasp_clr_bits(dev->base + MCASP_VER3_RFIFOCTL,
- FIFO_ENABLE);
- break;
-
- default:
- mcasp_clr_bits(dev->base +
- DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE);
- }
+ if (mcasp->rxnumevt) { /* disable FIFO */
+ reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
}
- mcasp_stop_rx(dev);
+ mcasp_stop_rx(mcasp);
}
}
static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
- struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
- void __iomem *base = dev->base;
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_B:
case SND_SOC_DAIFMT_AC97:
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
break;
default:
/* configure a full-word SYNC pulse (LRCLK) */
- mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
- mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
/* make 1st data bit occur one ACLK cycle after the frame sync */
- mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, FSXDLY(1));
- mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, FSRDLY(1));
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, FSXDLY(1));
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, FSRDLY(1));
break;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
/* codec is clock and frame slave */
- mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
- mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
- mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
- mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
- mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG,
- ACLKX | ACLKR);
- mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG,
- AFSX | AFSR);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR);
break;
case SND_SOC_DAIFMT_CBM_CFS:
/* codec is clock master and frame slave */
- mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
- mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
- mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
- mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
- mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG,
- ACLKX | ACLKR);
- mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG,
- AFSX | AFSR);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR);
break;
case SND_SOC_DAIFMT_CBM_CFM:
/* codec is clock and frame master */
- mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
- mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
- mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
- mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
- mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG,
- ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG,
+ ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR);
break;
default:
@@ -541,35 +322,35 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_IB_NF:
- mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
- mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
- mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
- mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
break;
case SND_SOC_DAIFMT_NB_IF:
- mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
- mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
- mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
- mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
break;
case SND_SOC_DAIFMT_IB_IF:
- mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
- mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
- mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
- mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
break;
case SND_SOC_DAIFMT_NB_NF:
- mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
- mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
- mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
- mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
break;
default:
@@ -581,25 +362,25 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
{
- struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
switch (div_id) {
case 0: /* MCLK divider */
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG,
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
AHCLKXDIV(div - 1), AHCLKXDIV_MASK);
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG,
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
break;
case 1: /* BCLK divider */
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG,
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
ACLKXDIV(div - 1), ACLKXDIV_MASK);
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_ACLKRCTL_REG,
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
ACLKRDIV(div - 1), ACLKRDIV_MASK);
break;
case 2: /* BCLK/LRCLK ratio */
- dev->bclk_lrclk_ratio = div;
+ mcasp->bclk_lrclk_ratio = div;
break;
default:
@@ -612,22 +393,22 @@ static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div
static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
if (dir == SND_SOC_CLOCK_OUT) {
- mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
- mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
- mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG, AHCLKX);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AHCLKX);
} else {
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_PDIR_REG, AHCLKX);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AHCLKX);
}
return 0;
}
-static int davinci_config_channel_size(struct davinci_audio_dev *dev,
+static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
int word_length)
{
u32 fmt;
@@ -644,71 +425,68 @@ static int davinci_config_channel_size(struct davinci_audio_dev *dev,
* both left and right channels), so it has to be divided by number of
* tdm-slots (for I2S - divided by 2).
*/
- if (dev->bclk_lrclk_ratio)
- word_length = dev->bclk_lrclk_ratio / dev->tdm_slots;
+ if (mcasp->bclk_lrclk_ratio)
+ word_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
/* mapping of the XSSZ bit-field as described in the datasheet */
fmt = (word_length >> 1) - 1;
- if (dev->op_mode != DAVINCI_MCASP_DIT_MODE) {
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG,
- RXSSZ(fmt), RXSSZ(0x0F));
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
- TXSSZ(fmt), TXSSZ(0x0F));
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
- TXROT(tx_rotate), TXROT(7));
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG,
- RXROT(rx_rotate), RXROT(7));
- mcasp_set_reg(dev->base + DAVINCI_MCASP_RXMASK_REG,
- mask);
+ if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
+ RXSSZ(0x0F));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt),
+ TXSSZ(0x0F));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate),
+ TXROT(7));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate),
+ RXROT(7));
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask);
}
- mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, mask);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
return 0;
}
-static int davinci_hw_common_param(struct davinci_audio_dev *dev, int stream,
+static int davinci_hw_common_param(struct davinci_mcasp *mcasp, int stream,
int channels)
{
int i;
u8 tx_ser = 0;
u8 rx_ser = 0;
u8 ser;
- u8 slots = dev->tdm_slots;
+ u8 slots = mcasp->tdm_slots;
u8 max_active_serializers = (channels + slots - 1) / slots;
+ u32 reg;
/* Default configuration */
- mcasp_set_bits(dev->base + DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
+ if (mcasp->version != MCASP_VERSION_4)
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
/* All PINS as McASP */
- mcasp_set_reg(dev->base + DAVINCI_MCASP_PFUNC_REG, 0x00000000);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG,
- TXDATADMADIS);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
} else {
- mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_REVTCTL_REG,
- RXDATADMADIS);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_REVTCTL_REG, RXDATADMADIS);
}
- for (i = 0; i < dev->num_serializer; i++) {
- mcasp_set_bits(dev->base + DAVINCI_MCASP_XRSRCTL_REG(i),
- dev->serial_dir[i]);
- if (dev->serial_dir[i] == TX_MODE &&
+ for (i = 0; i < mcasp->num_serializer; i++) {
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+ mcasp->serial_dir[i]);
+ if (mcasp->serial_dir[i] == TX_MODE &&
tx_ser < max_active_serializers) {
- mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG,
- AXR(i));
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i));
tx_ser++;
- } else if (dev->serial_dir[i] == RX_MODE &&
+ } else if (mcasp->serial_dir[i] == RX_MODE &&
rx_ser < max_active_serializers) {
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_PDIR_REG,
- AXR(i));
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i));
rx_ser++;
} else {
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_XRSRCTL_REG(i),
- SRMOD_INACTIVE, SRMOD_MASK);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+ SRMOD_INACTIVE, SRMOD_MASK);
}
}
@@ -718,127 +496,113 @@ static int davinci_hw_common_param(struct davinci_audio_dev *dev, int stream,
ser = rx_ser;
if (ser < max_active_serializers) {
- dev_warn(dev->dev, "stream has more channels (%d) than are "
+ dev_warn(mcasp->dev, "stream has more channels (%d) than are "
"enabled in mcasp (%d)\n", channels, ser * slots);
return -EINVAL;
}
- if (dev->txnumevt && stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (dev->txnumevt * tx_ser > 64)
- dev->txnumevt = 1;
+ if (mcasp->txnumevt && stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (mcasp->txnumevt * tx_ser > 64)
+ mcasp->txnumevt = 1;
- switch (dev->version) {
- case MCASP_VERSION_3:
- mcasp_mod_bits(dev->base + MCASP_VER3_WFIFOCTL, tx_ser,
- NUMDMA_MASK);
- mcasp_mod_bits(dev->base + MCASP_VER3_WFIFOCTL,
- ((dev->txnumevt * tx_ser) << 8), NUMEVT_MASK);
- break;
- default:
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
- tx_ser, NUMDMA_MASK);
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
- ((dev->txnumevt * tx_ser) << 8), NUMEVT_MASK);
- }
+ reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+ mcasp_mod_bits(mcasp, reg, tx_ser, NUMDMA_MASK);
+ mcasp_mod_bits(mcasp, reg, ((mcasp->txnumevt * tx_ser) << 8),
+ NUMEVT_MASK);
}
- if (dev->rxnumevt && stream == SNDRV_PCM_STREAM_CAPTURE) {
- if (dev->rxnumevt * rx_ser > 64)
- dev->rxnumevt = 1;
- switch (dev->version) {
- case MCASP_VERSION_3:
- mcasp_mod_bits(dev->base + MCASP_VER3_RFIFOCTL, rx_ser,
- NUMDMA_MASK);
- mcasp_mod_bits(dev->base + MCASP_VER3_RFIFOCTL,
- ((dev->rxnumevt * rx_ser) << 8), NUMEVT_MASK);
- break;
- default:
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
- rx_ser, NUMDMA_MASK);
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
- ((dev->rxnumevt * rx_ser) << 8), NUMEVT_MASK);
- }
+ if (mcasp->rxnumevt && stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (mcasp->rxnumevt * rx_ser > 64)
+ mcasp->rxnumevt = 1;
+
+ reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+ mcasp_mod_bits(mcasp, reg, rx_ser, NUMDMA_MASK);
+ mcasp_mod_bits(mcasp, reg, ((mcasp->rxnumevt * rx_ser) << 8),
+ NUMEVT_MASK);
}
return 0;
}
-static void davinci_hw_param(struct davinci_audio_dev *dev, int stream)
+static void davinci_hw_param(struct davinci_mcasp *mcasp, int stream)
{
int i, active_slots;
u32 mask = 0;
+ u32 busel = 0;
- active_slots = (dev->tdm_slots > 31) ? 32 : dev->tdm_slots;
+ active_slots = (mcasp->tdm_slots > 31) ? 32 : mcasp->tdm_slots;
for (i = 0; i < active_slots; i++)
mask |= (1 << i);
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
+
+ if (!mcasp->dat_port)
+ busel = TXSEL;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* bit stream is MSB first with no delay */
/* DSP_B mode */
- mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, mask);
- mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXORD);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
- if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32))
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG,
- FSXMOD(dev->tdm_slots), FSXMOD(0x1FF));
+ if ((mcasp->tdm_slots >= 2) && (mcasp->tdm_slots <= 32))
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
+ FSXMOD(mcasp->tdm_slots), FSXMOD(0x1FF));
else
printk(KERN_ERR "playback tdm slot %d not supported\n",
- dev->tdm_slots);
+ mcasp->tdm_slots);
} else {
/* bit stream is MSB first with no delay */
/* DSP_B mode */
- mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, RXORD);
- mcasp_set_reg(dev->base + DAVINCI_MCASP_RXTDM_REG, mask);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
- if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32))
- mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG,
- FSRMOD(dev->tdm_slots), FSRMOD(0x1FF));
+ if ((mcasp->tdm_slots >= 2) && (mcasp->tdm_slots <= 32))
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
+ FSRMOD(mcasp->tdm_slots), FSRMOD(0x1FF));
else
printk(KERN_ERR "capture tdm slot %d not supported\n",
- dev->tdm_slots);
+ mcasp->tdm_slots);
}
}
/* S/PDIF */
-static void davinci_hw_dit_param(struct davinci_audio_dev *dev)
+static void davinci_hw_dit_param(struct davinci_mcasp *mcasp)
{
/* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0
and LSB first */
- mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
- TXROT(6) | TXSSZ(15));
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(6) | TXSSZ(15));
/* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */
- mcasp_set_reg(dev->base + DAVINCI_MCASP_TXFMCTL_REG,
- AFSXE | FSXMOD(0x180));
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE | FSXMOD(0x180));
/* Set the TX tdm : for all the slots */
- mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF);
/* Set the TX clock controls : div = 1 and internal */
- mcasp_set_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG,
- ACLKXE | TX_ASYNC);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE | TX_ASYNC);
- mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
/* Only 44100 and 48000 are valid, both have the same setting */
- mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3));
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3));
/* Enable the DIT */
- mcasp_set_bits(dev->base + DAVINCI_MCASP_TXDITCTL_REG, DITEN);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXDITCTL_REG, DITEN);
}
static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai)
{
- struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
struct davinci_pcm_dma_params *dma_params =
- &dev->dma_params[substream->stream];
+ &mcasp->dma_params[substream->stream];
+ struct snd_dmaengine_dai_dma_data *dma_data =
+ &mcasp->dma_data[substream->stream];
int word_length;
u8 fifo_level;
- u8 slots = dev->tdm_slots;
+ u8 slots = mcasp->tdm_slots;
u8 active_serializers;
int channels;
struct snd_interval *pcm_channels = hw_param_interval(params,
@@ -847,17 +611,17 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
active_serializers = (channels + slots - 1) / slots;
- if (davinci_hw_common_param(dev, substream->stream, channels) == -EINVAL)
+ if (davinci_hw_common_param(mcasp, substream->stream, channels) == -EINVAL)
return -EINVAL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- fifo_level = dev->txnumevt * active_serializers;
+ fifo_level = mcasp->txnumevt * active_serializers;
else
- fifo_level = dev->rxnumevt * active_serializers;
+ fifo_level = mcasp->rxnumevt * active_serializers;
- if (dev->op_mode == DAVINCI_MCASP_DIT_MODE)
- davinci_hw_dit_param(dev);
+ if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+ davinci_hw_dit_param(mcasp);
else
- davinci_hw_param(dev, substream->stream);
+ davinci_hw_param(mcasp, substream->stream);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_U8:
@@ -891,13 +655,15 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- if (dev->version == MCASP_VERSION_2 && !fifo_level)
+ if (mcasp->version == MCASP_VERSION_2 && !fifo_level)
dma_params->acnt = 4;
else
dma_params->acnt = dma_params->data_type;
dma_params->fifo_level = fifo_level;
- davinci_config_channel_size(dev, word_length);
+ dma_data->maxburst = fifo_level;
+
+ davinci_config_channel_size(mcasp, word_length);
return 0;
}
@@ -905,29 +671,29 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *cpu_dai)
{
- struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ret = pm_runtime_get_sync(dev->dev);
+ ret = pm_runtime_get_sync(mcasp->dev);
if (IS_ERR_VALUE(ret))
- dev_err(dev->dev, "pm_runtime_get_sync() failed\n");
- davinci_mcasp_start(dev, substream->stream);
+ dev_err(mcasp->dev, "pm_runtime_get_sync() failed\n");
+ davinci_mcasp_start(mcasp, substream->stream);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
- davinci_mcasp_stop(dev, substream->stream);
- ret = pm_runtime_put_sync(dev->dev);
+ davinci_mcasp_stop(mcasp, substream->stream);
+ ret = pm_runtime_put_sync(mcasp->dev);
if (IS_ERR_VALUE(ret))
- dev_err(dev->dev, "pm_runtime_put_sync() failed\n");
+ dev_err(mcasp->dev, "pm_runtime_put_sync() failed\n");
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- davinci_mcasp_stop(dev, substream->stream);
+ davinci_mcasp_stop(mcasp, substream->stream);
break;
default:
@@ -940,9 +706,14 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+ if (mcasp->version == MCASP_VERSION_4)
+ snd_soc_dai_set_dma_data(dai, substream,
+ &mcasp->dma_data[substream->stream]);
+ else
+ snd_soc_dai_set_dma_data(dai, substream, mcasp->dma_params);
- snd_soc_dai_set_dma_data(dai, substream, dev->dma_params);
return 0;
}
@@ -955,6 +726,8 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
.set_sysclk = davinci_mcasp_set_sysclk,
};
+#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000
+
#define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_U8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
@@ -985,7 +758,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
},
{
- "davinci-mcasp.1",
+ .name = "davinci-mcasp.1",
.playback = {
.channels_min = 1,
.channels_max = 384,
@@ -1016,13 +789,20 @@ static struct snd_platform_data da830_mcasp_pdata = {
.version = MCASP_VERSION_2,
};
-static struct snd_platform_data omap2_mcasp_pdata = {
+static struct snd_platform_data am33xx_mcasp_pdata = {
.tx_dma_offset = 0,
.rx_dma_offset = 0,
.asp_chan_q = EVENTQ_0,
.version = MCASP_VERSION_3,
};
+static struct snd_platform_data dra7_mcasp_pdata = {
+ .tx_dma_offset = 0x200,
+ .rx_dma_offset = 0x284,
+ .asp_chan_q = EVENTQ_0,
+ .version = MCASP_VERSION_4,
+};
+
static const struct of_device_id mcasp_dt_ids[] = {
{
.compatible = "ti,dm646x-mcasp-audio",
@@ -1034,12 +814,56 @@ static const struct of_device_id mcasp_dt_ids[] = {
},
{
.compatible = "ti,am33xx-mcasp-audio",
- .data = &omap2_mcasp_pdata,
+ .data = &am33xx_mcasp_pdata,
+ },
+ {
+ .compatible = "ti,dra7-mcasp-audio",
+ .data = &dra7_mcasp_pdata,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mcasp_dt_ids);
+static int mcasp_reparent_fck(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk *gfclk, *parent_clk;
+ const char *parent_name;
+ int ret;
+
+ if (!node)
+ return 0;
+
+ parent_name = of_get_property(node, "fck_parent", NULL);
+ if (!parent_name)
+ return 0;
+
+ gfclk = clk_get(&pdev->dev, "fck");
+ if (IS_ERR(gfclk)) {
+ dev_err(&pdev->dev, "failed to get fck\n");
+ return PTR_ERR(gfclk);
+ }
+
+ parent_clk = clk_get(NULL, parent_name);
+ if (IS_ERR(parent_clk)) {
+ dev_err(&pdev->dev, "failed to get parent clock\n");
+ ret = PTR_ERR(parent_clk);
+ goto err1;
+ }
+
+ ret = clk_set_parent(gfclk, parent_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to reparent fck\n");
+ goto err2;
+ }
+
+err2:
+ clk_put(parent_clk);
+err1:
+ clk_put(gfclk);
+ return ret;
+}
+
static struct snd_platform_data *davinci_mcasp_set_pdata_from_of(
struct platform_device *pdev)
{
@@ -1152,7 +976,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
struct davinci_pcm_dma_params *dma_data;
struct resource *mem, *ioarea, *res, *dat;
struct snd_platform_data *pdata;
- struct davinci_audio_dev *dev;
+ struct davinci_mcasp *mcasp;
int ret;
if (!pdev->dev.platform_data && !pdev->dev.of_node) {
@@ -1160,9 +984,9 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
return -EINVAL;
}
- dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_audio_dev),
+ mcasp = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcasp),
GFP_KERNEL);
- if (!dev)
+ if (!mcasp)
return -ENOMEM;
pdata = davinci_mcasp_set_pdata_from_of(pdev);
@@ -1173,7 +997,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
if (!mem) {
- dev_warn(dev->dev,
+ dev_warn(mcasp->dev,
"\"mpu\" mem resource not found, using index 0\n");
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
@@ -1197,32 +1021,39 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
return ret;
}
- dev->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
- if (!dev->base) {
+ mcasp->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!mcasp->base) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
goto err_release_clk;
}
- dev->op_mode = pdata->op_mode;
- dev->tdm_slots = pdata->tdm_slots;
- dev->num_serializer = pdata->num_serializer;
- dev->serial_dir = pdata->serial_dir;
- dev->version = pdata->version;
- dev->txnumevt = pdata->txnumevt;
- dev->rxnumevt = pdata->rxnumevt;
- dev->dev = &pdev->dev;
+ mcasp->op_mode = pdata->op_mode;
+ mcasp->tdm_slots = pdata->tdm_slots;
+ mcasp->num_serializer = pdata->num_serializer;
+ mcasp->serial_dir = pdata->serial_dir;
+ mcasp->version = pdata->version;
+ mcasp->txnumevt = pdata->txnumevt;
+ mcasp->rxnumevt = pdata->rxnumevt;
+
+ mcasp->dev = &pdev->dev;
dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
- if (!dat)
- dat = mem;
+ if (dat)
+ mcasp->dat_port = true;
- dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
+ dma_data = &mcasp->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q;
dma_data->sram_pool = pdata->sram_pool;
dma_data->sram_size = pdata->sram_size_playback;
- dma_data->dma_addr = dat->start + pdata->tx_dma_offset;
+ if (dat)
+ dma_data->dma_addr = dat->start;
+ else
+ dma_data->dma_addr = mem->start + pdata->tx_dma_offset;
+
+ /* Unconditional dmaengine stuff */
+ mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = dma_data->dma_addr;
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (res)
@@ -1230,12 +1061,26 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
else
dma_data->channel = pdata->tx_dma_channel;
- dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE];
+ dma_data = &mcasp->dma_params[SNDRV_PCM_STREAM_CAPTURE];
dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q;
dma_data->sram_pool = pdata->sram_pool;
dma_data->sram_size = pdata->sram_size_capture;
- dma_data->dma_addr = dat->start + pdata->rx_dma_offset;
+ if (dat)
+ dma_data->dma_addr = dat->start;
+ else
+ dma_data->dma_addr = mem->start + pdata->rx_dma_offset;
+
+ /* Unconditional dmaengine stuff */
+ mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = dma_data->dma_addr;
+
+ if (mcasp->version < MCASP_VERSION_3) {
+ mcasp->fifo_base = DAVINCI_MCASP_V2_AFIFO_BASE;
+ /* dma_data->dma_addr is pointing to the data port address */
+ mcasp->dat_port = true;
+ } else {
+ mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
+ }
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (res)
@@ -1243,17 +1088,26 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
else
dma_data->channel = pdata->rx_dma_channel;
- dev_set_drvdata(&pdev->dev, dev);
+ /* Unconditional dmaengine stuff */
+ mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = "tx";
+ mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = "rx";
+
+ dev_set_drvdata(&pdev->dev, mcasp);
+
+ mcasp_reparent_fck(pdev);
+
ret = snd_soc_register_component(&pdev->dev, &davinci_mcasp_component,
&davinci_mcasp_dai[pdata->op_mode], 1);
if (ret != 0)
goto err_release_clk;
- ret = davinci_soc_platform_register(&pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
- goto err_unregister_component;
+ if (mcasp->version != MCASP_VERSION_4) {
+ ret = davinci_soc_platform_register(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+ goto err_unregister_component;
+ }
}
return 0;
@@ -1268,9 +1122,11 @@ err_release_clk:
static int davinci_mcasp_remove(struct platform_device *pdev)
{
+ struct davinci_mcasp *mcasp = dev_get_drvdata(&pdev->dev);
snd_soc_unregister_component(&pdev->dev);
- davinci_soc_platform_unregister(&pdev->dev);
+ if (mcasp->version != MCASP_VERSION_4)
+ davinci_soc_platform_unregister(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -1281,32 +1137,30 @@ static int davinci_mcasp_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int davinci_mcasp_suspend(struct device *dev)
{
- struct davinci_audio_dev *a = dev_get_drvdata(dev);
- void __iomem *base = a->base;
+ struct davinci_mcasp *mcasp = dev_get_drvdata(dev);
- a->context.txfmtctl = mcasp_get_reg(base + DAVINCI_MCASP_TXFMCTL_REG);
- a->context.rxfmtctl = mcasp_get_reg(base + DAVINCI_MCASP_RXFMCTL_REG);
- a->context.txfmt = mcasp_get_reg(base + DAVINCI_MCASP_TXFMT_REG);
- a->context.rxfmt = mcasp_get_reg(base + DAVINCI_MCASP_RXFMT_REG);
- a->context.aclkxctl = mcasp_get_reg(base + DAVINCI_MCASP_ACLKXCTL_REG);
- a->context.aclkrctl = mcasp_get_reg(base + DAVINCI_MCASP_ACLKRCTL_REG);
- a->context.pdir = mcasp_get_reg(base + DAVINCI_MCASP_PDIR_REG);
+ mcasp->context.txfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG);
+ mcasp->context.rxfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
+ mcasp->context.txfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMT_REG);
+ mcasp->context.rxfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMT_REG);
+ mcasp->context.aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
+ mcasp->context.aclkrctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG);
+ mcasp->context.pdir = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG);
return 0;
}
static int davinci_mcasp_resume(struct device *dev)
{
- struct davinci_audio_dev *a = dev_get_drvdata(dev);
- void __iomem *base = a->base;
-
- mcasp_set_reg(base + DAVINCI_MCASP_TXFMCTL_REG, a->context.txfmtctl);
- mcasp_set_reg(base + DAVINCI_MCASP_RXFMCTL_REG, a->context.rxfmtctl);
- mcasp_set_reg(base + DAVINCI_MCASP_TXFMT_REG, a->context.txfmt);
- mcasp_set_reg(base + DAVINCI_MCASP_RXFMT_REG, a->context.rxfmt);
- mcasp_set_reg(base + DAVINCI_MCASP_ACLKXCTL_REG, a->context.aclkxctl);
- mcasp_set_reg(base + DAVINCI_MCASP_ACLKRCTL_REG, a->context.aclkrctl);
- mcasp_set_reg(base + DAVINCI_MCASP_PDIR_REG, a->context.pdir);
+ struct davinci_mcasp *mcasp = dev_get_drvdata(dev);
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, mcasp->context.txfmtctl);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG, mcasp->context.rxfmtctl);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMT_REG, mcasp->context.txfmt);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMT_REG, mcasp->context.rxfmt);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, mcasp->context.aclkxctl);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, mcasp->context.aclkrctl);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_PDIR_REG, mcasp->context.pdir);
return 0;
}
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
index a2e27e1c32f3..8fed757d6087 100644
--- a/sound/soc/davinci/davinci-mcasp.h
+++ b/sound/soc/davinci/davinci-mcasp.h
@@ -18,43 +18,271 @@
#ifndef DAVINCI_MCASP_H
#define DAVINCI_MCASP_H
-#include <linux/io.h>
-#include <linux/platform_data/davinci_asp.h>
-
-#include "davinci-pcm.h"
-
-#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000
-#define DAVINCI_MCASP_I2S_DAI 0
-#define DAVINCI_MCASP_DIT_DAI 1
-
-struct davinci_audio_dev {
- struct davinci_pcm_dma_params dma_params[2];
- void __iomem *base;
- struct device *dev;
-
- /* McASP specific data */
- int tdm_slots;
- u8 op_mode;
- u8 num_serializer;
- u8 *serial_dir;
- u8 version;
- u16 bclk_lrclk_ratio;
-
- /* McASP FIFO related */
- u8 txnumevt;
- u8 rxnumevt;
-
-#ifdef CONFIG_PM_SLEEP
- struct {
- u32 txfmtctl;
- u32 rxfmtctl;
- u32 txfmt;
- u32 rxfmt;
- u32 aclkxctl;
- u32 aclkrctl;
- u32 pdir;
- } context;
-#endif
-};
+/*
+ * McASP register definitions
+ */
+#define DAVINCI_MCASP_PID_REG 0x00
+#define DAVINCI_MCASP_PWREMUMGT_REG 0x04
+
+#define DAVINCI_MCASP_PFUNC_REG 0x10
+#define DAVINCI_MCASP_PDIR_REG 0x14
+#define DAVINCI_MCASP_PDOUT_REG 0x18
+#define DAVINCI_MCASP_PDSET_REG 0x1c
+
+#define DAVINCI_MCASP_PDCLR_REG 0x20
+
+#define DAVINCI_MCASP_TLGC_REG 0x30
+#define DAVINCI_MCASP_TLMR_REG 0x34
+
+#define DAVINCI_MCASP_GBLCTL_REG 0x44
+#define DAVINCI_MCASP_AMUTE_REG 0x48
+#define DAVINCI_MCASP_LBCTL_REG 0x4c
+
+#define DAVINCI_MCASP_TXDITCTL_REG 0x50
+
+#define DAVINCI_MCASP_GBLCTLR_REG 0x60
+#define DAVINCI_MCASP_RXMASK_REG 0x64
+#define DAVINCI_MCASP_RXFMT_REG 0x68
+#define DAVINCI_MCASP_RXFMCTL_REG 0x6c
+
+#define DAVINCI_MCASP_ACLKRCTL_REG 0x70
+#define DAVINCI_MCASP_AHCLKRCTL_REG 0x74
+#define DAVINCI_MCASP_RXTDM_REG 0x78
+#define DAVINCI_MCASP_EVTCTLR_REG 0x7c
+
+#define DAVINCI_MCASP_RXSTAT_REG 0x80
+#define DAVINCI_MCASP_RXTDMSLOT_REG 0x84
+#define DAVINCI_MCASP_RXCLKCHK_REG 0x88
+#define DAVINCI_MCASP_REVTCTL_REG 0x8c
+
+#define DAVINCI_MCASP_GBLCTLX_REG 0xa0
+#define DAVINCI_MCASP_TXMASK_REG 0xa4
+#define DAVINCI_MCASP_TXFMT_REG 0xa8
+#define DAVINCI_MCASP_TXFMCTL_REG 0xac
+
+#define DAVINCI_MCASP_ACLKXCTL_REG 0xb0
+#define DAVINCI_MCASP_AHCLKXCTL_REG 0xb4
+#define DAVINCI_MCASP_TXTDM_REG 0xb8
+#define DAVINCI_MCASP_EVTCTLX_REG 0xbc
+
+#define DAVINCI_MCASP_TXSTAT_REG 0xc0
+#define DAVINCI_MCASP_TXTDMSLOT_REG 0xc4
+#define DAVINCI_MCASP_TXCLKCHK_REG 0xc8
+#define DAVINCI_MCASP_XEVTCTL_REG 0xcc
+
+/* Left(even TDM Slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRA_REG 0x100
+/* Right(odd TDM slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRB_REG 0x118
+/* Left(even TDM slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRA_REG 0x130
+/* Right(odd TDM Slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRB_REG 0x148
+
+/* Serializer n Control Register */
+#define DAVINCI_MCASP_XRSRCTL_BASE_REG 0x180
+#define DAVINCI_MCASP_XRSRCTL_REG(n) (DAVINCI_MCASP_XRSRCTL_BASE_REG + \
+ (n << 2))
+
+/* Transmit Buffer for Serializer n */
+#define DAVINCI_MCASP_TXBUF_REG 0x200
+/* Receive Buffer for Serializer n */
+#define DAVINCI_MCASP_RXBUF_REG 0x280
+
+/* McASP FIFO Registers */
+#define DAVINCI_MCASP_V2_AFIFO_BASE (0x1010)
+#define DAVINCI_MCASP_V3_AFIFO_BASE (0x1000)
+
+/* FIFO register offsets from AFIFO base */
+#define MCASP_WFIFOCTL_OFFSET (0x0)
+#define MCASP_WFIFOSTS_OFFSET (0x4)
+#define MCASP_RFIFOCTL_OFFSET (0x8)
+#define MCASP_RFIFOSTS_OFFSET (0xc)
+
+/*
+ * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management
+ * Register Bits
+ */
+#define MCASP_FREE BIT(0)
+#define MCASP_SOFT BIT(1)
+
+/*
+ * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits
+ */
+#define AXR(n) (1<<n)
+#define PFUNC_AMUTE BIT(25)
+#define ACLKX BIT(26)
+#define AHCLKX BIT(27)
+#define AFSX BIT(28)
+#define ACLKR BIT(29)
+#define AHCLKR BIT(30)
+#define AFSR BIT(31)
+
+/*
+ * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits
+ */
+#define AXR(n) (1<<n)
+#define PDIR_AMUTE BIT(25)
+#define ACLKX BIT(26)
+#define AHCLKX BIT(27)
+#define AFSX BIT(28)
+#define ACLKR BIT(29)
+#define AHCLKR BIT(30)
+#define AFSR BIT(31)
+
+/*
+ * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits
+ */
+#define DITEN BIT(0) /* Transmit DIT mode enable/disable */
+#define VA BIT(2)
+#define VB BIT(3)
+
+/*
+ * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits
+ */
+#define TXROT(val) (val)
+#define TXSEL BIT(3)
+#define TXSSZ(val) (val<<4)
+#define TXPBIT(val) (val<<8)
+#define TXPAD(val) (val<<13)
+#define TXORD BIT(15)
+#define FSXDLY(val) (val<<16)
+
+/*
+ * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits
+ */
+#define RXROT(val) (val)
+#define RXSEL BIT(3)
+#define RXSSZ(val) (val<<4)
+#define RXPBIT(val) (val<<8)
+#define RXPAD(val) (val<<13)
+#define RXORD BIT(15)
+#define FSRDLY(val) (val<<16)
+
+/*
+ * DAVINCI_MCASP_TXFMCTL_REG - Transmit Frame Control Register Bits
+ */
+#define FSXPOL BIT(0)
+#define AFSXE BIT(1)
+#define FSXDUR BIT(4)
+#define FSXMOD(val) (val<<7)
+
+/*
+ * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits
+ */
+#define FSRPOL BIT(0)
+#define AFSRE BIT(1)
+#define FSRDUR BIT(4)
+#define FSRMOD(val) (val<<7)
+
+/*
+ * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits
+ */
+#define ACLKXDIV(val) (val)
+#define ACLKXE BIT(5)
+#define TX_ASYNC BIT(6)
+#define ACLKXPOL BIT(7)
+#define ACLKXDIV_MASK 0x1f
+
+/*
+ * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits
+ */
+#define ACLKRDIV(val) (val)
+#define ACLKRE BIT(5)
+#define RX_ASYNC BIT(6)
+#define ACLKRPOL BIT(7)
+#define ACLKRDIV_MASK 0x1f
+
+/*
+ * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control
+ * Register Bits
+ */
+#define AHCLKXDIV(val) (val)
+#define AHCLKXPOL BIT(14)
+#define AHCLKXE BIT(15)
+#define AHCLKXDIV_MASK 0xfff
+
+/*
+ * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control
+ * Register Bits
+ */
+#define AHCLKRDIV(val) (val)
+#define AHCLKRPOL BIT(14)
+#define AHCLKRE BIT(15)
+#define AHCLKRDIV_MASK 0xfff
+
+/*
+ * DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits
+ */
+#define MODE(val) (val)
+#define DISMOD (val)(val<<2)
+#define TXSTATE BIT(4)
+#define RXSTATE BIT(5)
+#define SRMOD_MASK 3
+#define SRMOD_INACTIVE 0
+
+/*
+ * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits
+ */
+#define LBEN BIT(0)
+#define LBORD BIT(1)
+#define LBGENMODE(val) (val<<2)
+
+/*
+ * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration
+ */
+#define TXTDMS(n) (1<<n)
+
+/*
+ * DAVINCI_MCASP_RXTDMSLOT_REG - Receive TDM Slot Register configuration
+ */
+#define RXTDMS(n) (1<<n)
+
+/*
+ * DAVINCI_MCASP_GBLCTL_REG - Global Control Register Bits
+ */
+#define RXCLKRST BIT(0) /* Receiver Clock Divider Reset */
+#define RXHCLKRST BIT(1) /* Receiver High Frequency Clock Divider */
+#define RXSERCLR BIT(2) /* Receiver Serializer Clear */
+#define RXSMRST BIT(3) /* Receiver State Machine Reset */
+#define RXFSRST BIT(4) /* Frame Sync Generator Reset */
+#define TXCLKRST BIT(8) /* Transmitter Clock Divider Reset */
+#define TXHCLKRST BIT(9) /* Transmitter High Frequency Clock Divider*/
+#define TXSERCLR BIT(10) /* Transmit Serializer Clear */
+#define TXSMRST BIT(11) /* Transmitter State Machine Reset */
+#define TXFSRST BIT(12) /* Frame Sync Generator Reset */
+
+/*
+ * DAVINCI_MCASP_AMUTE_REG - Mute Control Register Bits
+ */
+#define MUTENA(val) (val)
+#define MUTEINPOL BIT(2)
+#define MUTEINENA BIT(3)
+#define MUTEIN BIT(4)
+#define MUTER BIT(5)
+#define MUTEX BIT(6)
+#define MUTEFSR BIT(7)
+#define MUTEFSX BIT(8)
+#define MUTEBADCLKR BIT(9)
+#define MUTEBADCLKX BIT(10)
+#define MUTERXDMAERR BIT(11)
+#define MUTETXDMAERR BIT(12)
+
+/*
+ * DAVINCI_MCASP_REVTCTL_REG - Receiver DMA Event Control Register bits
+ */
+#define RXDATADMADIS BIT(0)
+
+/*
+ * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits
+ */
+#define TXDATADMADIS BIT(0)
+
+/*
+ * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits
+ */
+#define FIFO_ENABLE BIT(16)
+#define NUMEVT_MASK (0xFF << 8)
+#define NUMDMA_MASK (0xFF)
#endif /* DAVINCI_MCASP_H */
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index fb5d107f5603..14145cdf8a11 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -46,33 +46,11 @@ static void print_buf_info(int slot, char *name)
}
#endif
-#define DAVINCI_PCM_FMTBITS (\
- SNDRV_PCM_FMTBIT_S8 |\
- SNDRV_PCM_FMTBIT_U8 |\
- SNDRV_PCM_FMTBIT_S16_LE |\
- SNDRV_PCM_FMTBIT_S16_BE |\
- SNDRV_PCM_FMTBIT_U16_LE |\
- SNDRV_PCM_FMTBIT_U16_BE |\
- SNDRV_PCM_FMTBIT_S24_LE |\
- SNDRV_PCM_FMTBIT_S24_BE |\
- SNDRV_PCM_FMTBIT_U24_LE |\
- SNDRV_PCM_FMTBIT_U24_BE |\
- SNDRV_PCM_FMTBIT_S32_LE |\
- SNDRV_PCM_FMTBIT_S32_BE |\
- SNDRV_PCM_FMTBIT_U32_LE |\
- SNDRV_PCM_FMTBIT_U32_BE)
-
static struct snd_pcm_hardware pcm_hardware_playback = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME|
SNDRV_PCM_INFO_BATCH),
- .formats = DAVINCI_PCM_FMTBITS,
- .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT,
- .rate_min = 8000,
- .rate_max = 192000,
- .channels_min = 2,
- .channels_max = 384,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
@@ -86,12 +64,6 @@ static struct snd_pcm_hardware pcm_hardware_capture = {
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_BATCH),
- .formats = DAVINCI_PCM_FMTBITS,
- .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT,
- .rate_min = 8000,
- .rate_max = 192000,
- .channels_min = 2,
- .channels_max = 384,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index b7ab71f2ccc1..514c275c6108 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,3 +1,7 @@
+config SND_SOC_FSL_SAI
+ tristate
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+
config SND_SOC_FSL_SSI
tristate
@@ -197,7 +201,6 @@ config SND_SOC_IMX_SPDIF
tristate "SoC Audio support for i.MX boards with S/PDIF"
select SND_SOC_IMX_PCM_DMA
select SND_SOC_FSL_SPDIF
- select SND_SOC_SPDIF
select REGMAP_MMIO
help
SoC Audio support for i.MX boards with S/PDIF
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 8db705b0fdf9..aaccbee17006 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -10,11 +10,13 @@ obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o
snd-soc-p1022-rdk-objs := p1022_rdk.o
obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
-# Freescale PowerPC SSI/DMA Platform Support
+# Freescale SSI/DMA/SAI/SPDIF Support
+snd-soc-fsl-sai-objs := fsl_sai.o
snd-soc-fsl-ssi-objs := fsl_ssi.o
snd-soc-fsl-spdif-objs := fsl_spdif.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
+obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index fb9bb9eb5ca3..d570f8c81dc6 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -852,7 +852,7 @@ static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm)
}
/**
- * find_ssi_node -- returns the SSI node that points to his DMA channel node
+ * find_ssi_node -- returns the SSI node that points to its DMA channel node
*
* Although this DMA driver attempts to operate independently of the other
* devices, it still needs to determine some information about the SSI device
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
new file mode 100644
index 000000000000..5d38a6749b9f
--- /dev/null
+++ b/sound/soc/fsl/fsl_sai.c
@@ -0,0 +1,460 @@
+/*
+ * Freescale ALSA SoC Digital Audio Interface (SAI) driver.
+ *
+ * Copyright 2012-2013 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_sai.h"
+
+static inline u32 sai_readl(struct fsl_sai *sai,
+ const void __iomem *addr)
+{
+ u32 val;
+
+ val = __raw_readl(addr);
+
+ if (likely(sai->big_endian_regs))
+ val = be32_to_cpu(val);
+ else
+ val = le32_to_cpu(val);
+ rmb();
+
+ return val;
+}
+
+static inline void sai_writel(struct fsl_sai *sai,
+ u32 val, void __iomem *addr)
+{
+ wmb();
+ if (likely(sai->big_endian_regs))
+ val = cpu_to_be32(val);
+ else
+ val = cpu_to_le32(val);
+
+ __raw_writel(val, addr);
+}
+
+static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int fsl_dir)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ u32 val_cr2, reg_cr2;
+
+ if (fsl_dir == FSL_FMT_TRANSMITTER)
+ reg_cr2 = FSL_SAI_TCR2;
+ else
+ reg_cr2 = FSL_SAI_RCR2;
+
+ val_cr2 = sai_readl(sai, sai->base + reg_cr2);
+ switch (clk_id) {
+ case FSL_SAI_CLK_BUS:
+ val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
+ val_cr2 |= FSL_SAI_CR2_MSEL_BUS;
+ break;
+ case FSL_SAI_CLK_MAST1:
+ val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
+ val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1;
+ break;
+ case FSL_SAI_CLK_MAST2:
+ val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
+ val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2;
+ break;
+ case FSL_SAI_CLK_MAST3:
+ val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
+ val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ sai_writel(sai, val_cr2, sai->base + reg_cr2);
+
+ return 0;
+}
+
+static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret;
+
+ if (dir == SND_SOC_CLOCK_IN)
+ return 0;
+
+ ret = clk_prepare_enable(sai->clk);
+ if (ret)
+ return ret;
+
+ ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
+ FSL_FMT_TRANSMITTER);
+ if (ret) {
+ dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret);
+ goto err_clk;
+ }
+
+ ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
+ FSL_FMT_RECEIVER);
+ if (ret) {
+ dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret);
+ goto err_clk;
+ }
+
+err_clk:
+ clk_disable_unprepare(sai->clk);
+
+ return ret;
+}
+
+static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt, int fsl_dir)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ u32 val_cr2, val_cr4, reg_cr2, reg_cr4;
+
+ if (fsl_dir == FSL_FMT_TRANSMITTER) {
+ reg_cr2 = FSL_SAI_TCR2;
+ reg_cr4 = FSL_SAI_TCR4;
+ } else {
+ reg_cr2 = FSL_SAI_RCR2;
+ reg_cr4 = FSL_SAI_RCR4;
+ }
+
+ val_cr2 = sai_readl(sai, sai->base + reg_cr2);
+ val_cr4 = sai_readl(sai, sai->base + reg_cr4);
+
+ if (sai->big_endian_data)
+ val_cr4 &= ~FSL_SAI_CR4_MF;
+ else
+ val_cr4 |= FSL_SAI_CR4_MF;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val_cr4 |= FSL_SAI_CR4_FSE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ val_cr4 |= FSL_SAI_CR4_FSP;
+ val_cr2 &= ~FSL_SAI_CR2_BCP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val_cr4 &= ~FSL_SAI_CR4_FSP;
+ val_cr2 &= ~FSL_SAI_CR2_BCP;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val_cr4 |= FSL_SAI_CR4_FSP;
+ val_cr2 |= FSL_SAI_CR2_BCP;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ val_cr4 &= ~FSL_SAI_CR4_FSP;
+ val_cr2 |= FSL_SAI_CR2_BCP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
+ val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
+ val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sai_writel(sai, val_cr2, sai->base + reg_cr2);
+ sai_writel(sai, val_cr4, sai->base + reg_cr4);
+
+ return 0;
+}
+
+static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret;
+
+ ret = clk_prepare_enable(sai->clk);
+ if (ret)
+ return ret;
+
+ ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER);
+ if (ret) {
+ dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret);
+ goto err_clk;
+ }
+
+ ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER);
+ if (ret) {
+ dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret);
+ goto err_clk;
+ }
+
+err_clk:
+ clk_disable_unprepare(sai->clk);
+
+ return ret;
+}
+
+static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr;
+ unsigned int channels = params_channels(params);
+ u32 word_width = snd_pcm_format_width(params_format(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_cr4 = FSL_SAI_TCR4;
+ reg_cr5 = FSL_SAI_TCR5;
+ reg_mr = FSL_SAI_TMR;
+ } else {
+ reg_cr4 = FSL_SAI_RCR4;
+ reg_cr5 = FSL_SAI_RCR5;
+ reg_mr = FSL_SAI_RMR;
+ }
+
+ val_cr4 = sai_readl(sai, sai->base + reg_cr4);
+ val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK;
+ val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK;
+
+ val_cr5 = sai_readl(sai, sai->base + reg_cr5);
+ val_cr5 &= ~FSL_SAI_CR5_WNW_MASK;
+ val_cr5 &= ~FSL_SAI_CR5_W0W_MASK;
+ val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
+
+ val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
+ val_cr5 |= FSL_SAI_CR5_WNW(word_width);
+ val_cr5 |= FSL_SAI_CR5_W0W(word_width);
+
+ val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
+ if (sai->big_endian_data)
+ val_cr5 |= FSL_SAI_CR5_FBT(0);
+ else
+ val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
+
+ val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
+ val_mr = ~0UL - ((1 << channels) - 1);
+
+ sai_writel(sai, val_cr4, sai->base + reg_cr4);
+ sai_writel(sai, val_cr5, sai->base + reg_cr5);
+ sai_writel(sai, val_mr, sai->base + reg_mr);
+
+ return 0;
+}
+
+static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ u32 tcsr, rcsr, val_cr2, val_cr3, reg_cr3;
+
+ val_cr2 = sai_readl(sai, sai->base + FSL_SAI_TCR2);
+ val_cr2 &= ~FSL_SAI_CR2_SYNC;
+ sai_writel(sai, val_cr2, sai->base + FSL_SAI_TCR2);
+
+ val_cr2 = sai_readl(sai, sai->base + FSL_SAI_RCR2);
+ val_cr2 |= FSL_SAI_CR2_SYNC;
+ sai_writel(sai, val_cr2, sai->base + FSL_SAI_RCR2);
+
+ tcsr = sai_readl(sai, sai->base + FSL_SAI_TCSR);
+ rcsr = sai_readl(sai, sai->base + FSL_SAI_RCSR);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ tcsr |= FSL_SAI_CSR_FRDE;
+ rcsr &= ~FSL_SAI_CSR_FRDE;
+ reg_cr3 = FSL_SAI_TCR3;
+ } else {
+ rcsr |= FSL_SAI_CSR_FRDE;
+ tcsr &= ~FSL_SAI_CSR_FRDE;
+ reg_cr3 = FSL_SAI_RCR3;
+ }
+
+ val_cr3 = sai_readl(sai, sai->base + reg_cr3);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ tcsr |= FSL_SAI_CSR_TERE;
+ rcsr |= FSL_SAI_CSR_TERE;
+ val_cr3 |= FSL_SAI_CR3_TRCE;
+
+ sai_writel(sai, val_cr3, sai->base + reg_cr3);
+ sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR);
+ sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (!(cpu_dai->playback_active || cpu_dai->capture_active)) {
+ tcsr &= ~FSL_SAI_CSR_TERE;
+ rcsr &= ~FSL_SAI_CSR_TERE;
+ }
+
+ val_cr3 &= ~FSL_SAI_CR3_TRCE;
+
+ sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR);
+ sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR);
+ sai_writel(sai, val_cr3, sai->base + reg_cr3);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fsl_sai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+
+ return clk_prepare_enable(sai->clk);
+}
+
+static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+
+ clk_disable_unprepare(sai->clk);
+}
+
+static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
+ .set_sysclk = fsl_sai_set_dai_sysclk,
+ .set_fmt = fsl_sai_set_dai_fmt,
+ .hw_params = fsl_sai_hw_params,
+ .trigger = fsl_sai_trigger,
+ .startup = fsl_sai_startup,
+ .shutdown = fsl_sai_shutdown,
+};
+
+static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+ struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
+ int ret;
+
+ ret = clk_prepare_enable(sai->clk);
+ if (ret)
+ return ret;
+
+ sai_writel(sai, 0x0, sai->base + FSL_SAI_RCSR);
+ sai_writel(sai, 0x0, sai->base + FSL_SAI_TCSR);
+ sai_writel(sai, FSL_SAI_MAXBURST_TX * 2, sai->base + FSL_SAI_TCR1);
+ sai_writel(sai, FSL_SAI_MAXBURST_RX - 1, sai->base + FSL_SAI_RCR1);
+
+ clk_disable_unprepare(sai->clk);
+
+ snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx,
+ &sai->dma_params_rx);
+
+ snd_soc_dai_set_drvdata(cpu_dai, sai);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver fsl_sai_dai = {
+ .probe = fsl_sai_dai_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = FSL_SAI_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = FSL_SAI_FORMATS,
+ },
+ .ops = &fsl_sai_pcm_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_component = {
+ .name = "fsl-sai",
+};
+
+static int fsl_sai_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_sai *sai;
+ struct resource *res;
+ int ret;
+
+ sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+ if (!sai)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ sai->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(sai->base))
+ return PTR_ERR(sai->base);
+
+ sai->clk = devm_clk_get(&pdev->dev, "sai");
+ if (IS_ERR(sai->clk)) {
+ dev_err(&pdev->dev, "Cannot get SAI's clock\n");
+ return PTR_ERR(sai->clk);
+ }
+
+ sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
+ sai->dma_params_tx.addr = res->start + FSL_SAI_TDR;
+ sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
+ sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX;
+
+ sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
+ sai->big_endian_data = of_property_read_bool(np, "big-endian-data");
+
+ platform_set_drvdata(pdev, sai);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
+ &fsl_sai_dai, 1);
+ if (ret)
+ return ret;
+
+ return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+ SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
+}
+
+static const struct of_device_id fsl_sai_ids[] = {
+ { .compatible = "fsl,vf610-sai", },
+ { /* sentinel */ }
+};
+
+static struct platform_driver fsl_sai_driver = {
+ .probe = fsl_sai_probe,
+ .driver = {
+ .name = "fsl-sai",
+ .owner = THIS_MODULE,
+ .of_match_table = fsl_sai_ids,
+ },
+};
+module_platform_driver(fsl_sai_driver);
+
+MODULE_DESCRIPTION("Freescale Soc SAI Interface");
+MODULE_AUTHOR("Xiubo Li, <Li.Xiubo@freescale.com>");
+MODULE_ALIAS("platform:fsl-sai");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
new file mode 100644
index 000000000000..41bb62e69361
--- /dev/null
+++ b/sound/soc/fsl/fsl_sai.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2012-2013 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __FSL_SAI_H
+#define __FSL_SAI_H
+
+#include <sound/dmaengine_pcm.h>
+
+#define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+/* SAI Transmit/Recieve Control Register */
+#define FSL_SAI_TCSR 0x00
+#define FSL_SAI_RCSR 0x80
+#define FSL_SAI_CSR_TERE BIT(31)
+#define FSL_SAI_CSR_FWF BIT(17)
+#define FSL_SAI_CSR_FRIE BIT(8)
+#define FSL_SAI_CSR_FRDE BIT(0)
+
+/* SAI Transmit Data/FIFO/MASK Register */
+#define FSL_SAI_TDR 0x20
+#define FSL_SAI_TFR 0x40
+#define FSL_SAI_TMR 0x60
+
+/* SAI Recieve Data/FIFO/MASK Register */
+#define FSL_SAI_RDR 0xa0
+#define FSL_SAI_RFR 0xc0
+#define FSL_SAI_RMR 0xe0
+
+/* SAI Transmit and Recieve Configuration 1 Register */
+#define FSL_SAI_TCR1 0x04
+#define FSL_SAI_RCR1 0x84
+
+/* SAI Transmit and Recieve Configuration 2 Register */
+#define FSL_SAI_TCR2 0x08
+#define FSL_SAI_RCR2 0x88
+#define FSL_SAI_CR2_SYNC BIT(30)
+#define FSL_SAI_CR2_MSEL_MASK (0xff << 26)
+#define FSL_SAI_CR2_MSEL_BUS 0
+#define FSL_SAI_CR2_MSEL_MCLK1 BIT(26)
+#define FSL_SAI_CR2_MSEL_MCLK2 BIT(27)
+#define FSL_SAI_CR2_MSEL_MCLK3 (BIT(26) | BIT(27))
+#define FSL_SAI_CR2_BCP BIT(25)
+#define FSL_SAI_CR2_BCD_MSTR BIT(24)
+
+/* SAI Transmit and Recieve Configuration 3 Register */
+#define FSL_SAI_TCR3 0x0c
+#define FSL_SAI_RCR3 0x8c
+#define FSL_SAI_CR3_TRCE BIT(16)
+#define FSL_SAI_CR3_WDFL(x) (x)
+#define FSL_SAI_CR3_WDFL_MASK 0x1f
+
+/* SAI Transmit and Recieve Configuration 4 Register */
+#define FSL_SAI_TCR4 0x10
+#define FSL_SAI_RCR4 0x90
+#define FSL_SAI_CR4_FRSZ(x) (((x) - 1) << 16)
+#define FSL_SAI_CR4_FRSZ_MASK (0x1f << 16)
+#define FSL_SAI_CR4_SYWD(x) (((x) - 1) << 8)
+#define FSL_SAI_CR4_SYWD_MASK (0x1f << 8)
+#define FSL_SAI_CR4_MF BIT(4)
+#define FSL_SAI_CR4_FSE BIT(3)
+#define FSL_SAI_CR4_FSP BIT(1)
+#define FSL_SAI_CR4_FSD_MSTR BIT(0)
+
+/* SAI Transmit and Recieve Configuration 5 Register */
+#define FSL_SAI_TCR5 0x14
+#define FSL_SAI_RCR5 0x94
+#define FSL_SAI_CR5_WNW(x) (((x) - 1) << 24)
+#define FSL_SAI_CR5_WNW_MASK (0x1f << 24)
+#define FSL_SAI_CR5_W0W(x) (((x) - 1) << 16)
+#define FSL_SAI_CR5_W0W_MASK (0x1f << 16)
+#define FSL_SAI_CR5_FBT(x) ((x) << 8)
+#define FSL_SAI_CR5_FBT_MASK (0x1f << 8)
+
+/* SAI type */
+#define FSL_SAI_DMA BIT(0)
+#define FSL_SAI_USE_AC97 BIT(1)
+#define FSL_SAI_NET BIT(2)
+#define FSL_SAI_TRA_SYN BIT(3)
+#define FSL_SAI_REC_SYN BIT(4)
+#define FSL_SAI_USE_I2S_SLAVE BIT(5)
+
+#define FSL_FMT_TRANSMITTER 0
+#define FSL_FMT_RECEIVER 1
+
+/* SAI clock sources */
+#define FSL_SAI_CLK_BUS 0
+#define FSL_SAI_CLK_MAST1 1
+#define FSL_SAI_CLK_MAST2 2
+#define FSL_SAI_CLK_MAST3 3
+
+/* SAI data transfer numbers per DMA request */
+#define FSL_SAI_MAXBURST_TX 6
+#define FSL_SAI_MAXBURST_RX 6
+
+struct fsl_sai {
+ struct clk *clk;
+
+ void __iomem *base;
+
+ bool big_endian_regs;
+ bool big_endian_data;
+
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
+};
+
+#endif /* __FSL_SAI_H */
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index 55193a5596ca..4d075f1abe78 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -1181,13 +1181,6 @@ static int fsl_spdif_probe(struct platform_device *pdev)
return ret;
}
-static int fsl_spdif_remove(struct platform_device *pdev)
-{
- imx_pcm_dma_exit(pdev);
-
- return 0;
-}
-
static const struct of_device_id fsl_spdif_dt_ids[] = {
{ .compatible = "fsl,imx35-spdif", },
{}
@@ -1201,7 +1194,6 @@ static struct platform_driver fsl_spdif_driver = {
.of_match_table = fsl_spdif_dt_ids,
},
.probe = fsl_spdif_probe,
- .remove = fsl_spdif_remove,
};
module_platform_driver(fsl_spdif_driver);
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 35e277379b86..b2ebaf811599 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -38,6 +38,7 @@
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
@@ -119,8 +120,6 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
* @ssi: pointer to the SSI's registers
* @ssi_phys: physical address of the SSI registers
* @irq: IRQ of this SSI
- * @first_stream: pointer to the stream that was opened first
- * @second_stream: pointer to second stream
* @playback: the number of playback streams opened
* @capture: the number of capture streams opened
* @cpu_dai: the CPU DAI for this device
@@ -132,8 +131,6 @@ struct fsl_ssi_private {
struct ccsr_ssi __iomem *ssi;
dma_addr_t ssi_phys;
unsigned int irq;
- struct snd_pcm_substream *first_stream;
- struct snd_pcm_substream *second_stream;
unsigned int fifo_depth;
struct snd_soc_dai_driver cpu_dai_drv;
struct device_attribute dev_attr;
@@ -143,6 +140,10 @@ struct fsl_ssi_private {
bool ssi_on_imx;
bool imx_ac97;
bool use_dma;
+ bool baudclk_locked;
+ u8 i2s_mode;
+ spinlock_t baudclk_lock;
+ struct clk *baudclk;
struct clk *clk;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
@@ -321,17 +322,46 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
return ret;
}
+static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private)
+{
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+
+ /*
+ * Setup the clock control register
+ */
+ write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
+ &ssi->stccr);
+ write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
+ &ssi->srccr);
+
+ /*
+ * Enable AC97 mode and startup the SSI
+ */
+ write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV,
+ &ssi->sacnt);
+ write_ssi(0xff, &ssi->saccdis);
+ write_ssi(0x300, &ssi->saccen);
+
+ /*
+ * Enable SSI, Transmit and Receive. AC97 has to communicate with the
+ * codec before a stream is started.
+ */
+ write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN |
+ CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE);
+
+ write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor);
+}
+
static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
{
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
- u8 i2s_mode;
u8 wm;
int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
if (ssi_private->imx_ac97)
- i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET;
+ ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET;
else
- i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
+ ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
/*
* Section 16.5 of the MPC8610 reference manual says that the SSI needs
@@ -348,7 +378,7 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
write_ssi_mask(&ssi->scr,
CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
CCSR_SSI_SCR_TFR_CLK_DIS |
- i2s_mode |
+ ssi_private->i2s_mode |
(synchronous ? CCSR_SSI_SCR_SYN : 0));
write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
@@ -387,31 +417,8 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
* because it is also running without an active substream. Normally SSI
* is only enabled when there is a substream.
*/
- if (ssi_private->imx_ac97) {
- /*
- * Setup the clock control register
- */
- write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
- &ssi->stccr);
- write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
- &ssi->srccr);
-
- /*
- * Enable AC97 mode and startup the SSI
- */
- write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV,
- &ssi->sacnt);
- write_ssi(0xff, &ssi->saccdis);
- write_ssi(0x300, &ssi->saccen);
-
- /*
- * Enable SSI, Transmit and Receive
- */
- write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN |
- CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE);
-
- write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor);
- }
+ if (ssi_private->imx_ac97)
+ fsl_ssi_setup_ac97(ssi_private);
return 0;
}
@@ -431,53 +438,17 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi_private *ssi_private =
snd_soc_dai_get_drvdata(rtd->cpu_dai);
- int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
+ unsigned long flags;
- /*
- * If this is the first stream opened, then request the IRQ
- * and initialize the SSI registers.
+ /* First, we only do fsl_ssi_setup() when SSI is going to be active.
+ * Second, fsl_ssi_setup was already called by ac97_init earlier if
+ * the driver is in ac97 mode.
*/
- if (!ssi_private->first_stream) {
- ssi_private->first_stream = substream;
-
- /*
- * fsl_ssi_setup was already called by ac97_init earlier if
- * the driver is in ac97 mode.
- */
- if (!ssi_private->imx_ac97)
- fsl_ssi_setup(ssi_private);
- } else {
- if (synchronous) {
- struct snd_pcm_runtime *first_runtime =
- ssi_private->first_stream->runtime;
- /*
- * This is the second stream open, and we're in
- * synchronous mode, so we need to impose sample
- * sample size constraints. This is because STCCR is
- * used for playback and capture in synchronous mode,
- * so there's no way to specify different word
- * lengths.
- *
- * Note that this can cause a race condition if the
- * second stream is opened before the first stream is
- * fully initialized. We provide some protection by
- * checking to make sure the first stream is
- * initialized, but it's not perfect. ALSA sometimes
- * re-initializes the driver with a different sample
- * rate or size. If the second stream is opened
- * before the first stream has received its final
- * parameters, then the second stream may be
- * constrained to the wrong sample rate or size.
- */
- if (first_runtime->sample_bits) {
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- first_runtime->sample_bits,
- first_runtime->sample_bits);
- }
- }
-
- ssi_private->second_stream = substream;
+ if (!dai->active && !ssi_private->imx_ac97) {
+ fsl_ssi_setup(ssi_private);
+ spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
+ ssi_private->baudclk_locked = false;
+ spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
}
return 0;
@@ -501,6 +472,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
{
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ unsigned int channels = params_channels(hw_params);
unsigned int sample_size =
snd_pcm_format_width(params_format(hw_params));
u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
@@ -530,6 +502,248 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
else
write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
+ if (!ssi_private->imx_ac97)
+ write_ssi_mask(&ssi->scr,
+ CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK,
+ channels == 1 ? 0 : ssi_private->i2s_mode);
+
+ return 0;
+}
+
+/**
+ * fsl_ssi_set_dai_fmt - configure Digital Audio Interface Format.
+ */
+static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ u32 strcr = 0, stcr, srcr, scr, mask;
+
+ scr = read_ssi(&ssi->scr) & ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_I2S_MODE_MASK);
+ scr |= CCSR_SSI_SCR_NET;
+
+ mask = CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR |
+ CCSR_SSI_STCR_TSCKP | CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TFSL |
+ CCSR_SSI_STCR_TEFS;
+ stcr = read_ssi(&ssi->stcr) & ~mask;
+ srcr = read_ssi(&ssi->srcr) & ~mask;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ scr |= ssi_private->i2s_mode;
+
+ /* Data on rising edge of bclk, frame low, 1clk before data */
+ strcr |= CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TSCKP |
+ CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TEFS;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ /* Data on rising edge of bclk, frame high */
+ strcr |= CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TSCKP;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /* Data on rising edge of bclk, frame high, 1clk before data */
+ strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP |
+ CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TEFS;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ /* Data on rising edge of bclk, frame high */
+ strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP |
+ CCSR_SSI_STCR_TXBIT0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* DAI clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ /* Nothing to do for both normal cases */
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ /* Invert bit clock */
+ strcr ^= CCSR_SSI_STCR_TSCKP;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ /* Invert frame clock */
+ strcr ^= CCSR_SSI_STCR_TFSI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ /* Invert both clocks */
+ strcr ^= CCSR_SSI_STCR_TSCKP;
+ strcr ^= CCSR_SSI_STCR_TFSI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ strcr |= CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR;
+ scr |= CCSR_SSI_SCR_SYS_CLK_EN;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ stcr |= strcr;
+ srcr |= strcr;
+
+ if (ssi_private->cpu_dai_drv.symmetric_rates) {
+ /* Need to clear RXDIR when using SYNC mode */
+ srcr &= ~CCSR_SSI_SRCR_RXDIR;
+ scr |= CCSR_SSI_SCR_SYN;
+ }
+
+ write_ssi(stcr, &ssi->stcr);
+ write_ssi(srcr, &ssi->srcr);
+ write_ssi(scr, &ssi->scr);
+
+ return 0;
+}
+
+/**
+ * fsl_ssi_set_dai_sysclk - configure Digital Audio Interface bit clock
+ *
+ * Note: This function can be only called when using SSI as DAI master
+ *
+ * Quick instruction for parameters:
+ * freq: Output BCLK frequency = samplerate * 32 (fixed) * channels
+ * dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK.
+ */
+static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret;
+ u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
+ unsigned long flags, clkrate, baudrate, tmprate;
+ u64 sub, savesub = 100000;
+
+ /* Don't apply it to any non-baudclk circumstance */
+ if (IS_ERR(ssi_private->baudclk))
+ return -EINVAL;
+
+ /* It should be already enough to divide clock by setting pm alone */
+ psr = 0;
+ div2 = 0;
+
+ factor = (div2 + 1) * (7 * psr + 1) * 2;
+
+ for (i = 0; i < 255; i++) {
+ /* The bclk rate must be smaller than 1/5 sysclk rate */
+ if (factor * (i + 1) < 5)
+ continue;
+
+ tmprate = freq * factor * (i + 2);
+ clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
+
+ do_div(clkrate, factor);
+ afreq = (u32)clkrate / (i + 1);
+
+ if (freq == afreq)
+ sub = 0;
+ else if (freq / afreq == 1)
+ sub = freq - afreq;
+ else if (afreq / freq == 1)
+ sub = afreq - freq;
+ else
+ continue;
+
+ /* Calculate the fraction */
+ sub *= 100000;
+ do_div(sub, freq);
+
+ if (sub < savesub) {
+ baudrate = tmprate;
+ savesub = sub;
+ pm = i;
+ }
+
+ /* We are lucky */
+ if (savesub == 0)
+ break;
+ }
+
+ /* No proper pm found if it is still remaining the initial value */
+ if (pm == 999) {
+ dev_err(cpu_dai->dev, "failed to handle the required sysclk\n");
+ return -EINVAL;
+ }
+
+ stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0) |
+ (psr ? CCSR_SSI_SxCCR_PSR : 0);
+ mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2 | CCSR_SSI_SxCCR_PSR;
+
+ if (dir == SND_SOC_CLOCK_OUT || synchronous)
+ write_ssi_mask(&ssi->stccr, mask, stccr);
+ else
+ write_ssi_mask(&ssi->srccr, mask, stccr);
+
+ spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
+ if (!ssi_private->baudclk_locked) {
+ ret = clk_set_rate(ssi_private->baudclk, baudrate);
+ if (ret) {
+ spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
+ dev_err(cpu_dai->dev, "failed to set baudclk rate\n");
+ return -EINVAL;
+ }
+ ssi_private->baudclk_locked = true;
+ }
+ spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
+
+ return 0;
+}
+
+/**
+ * fsl_ssi_set_dai_tdm_slot - set TDM slot number
+ *
+ * Note: This function can be only called when using SSI as DAI master
+ */
+static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
+{
+ struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ u32 val;
+
+ /* The slot number should be >= 2 if using Network mode or I2S mode */
+ val = read_ssi(&ssi->scr) & (CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_NET);
+ if (val && slots < 2) {
+ dev_err(cpu_dai->dev, "slot number should be >= 2 in I2S or NET\n");
+ return -EINVAL;
+ }
+
+ write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_DC_MASK,
+ CCSR_SSI_SxCCR_DC(slots));
+ write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_DC_MASK,
+ CCSR_SSI_SxCCR_DC(slots));
+
+ /* The register SxMSKs needs SSI to provide essential clock due to
+ * hardware design. So we here temporarily enable SSI to set them.
+ */
+ val = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
+ write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN);
+
+ write_ssi(tx_mask, &ssi->stmsk);
+ write_ssi(rx_mask, &ssi->srmsk);
+
+ write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, val);
+
return 0;
}
@@ -549,6 +763,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
unsigned int sier_bits;
+ unsigned long flags;
/*
* Enable only the interrupts and DMA requests
@@ -589,8 +804,12 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) &
- (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0)
+ (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) {
write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
+ spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
+ ssi_private->baudclk_locked = false;
+ spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
+ }
break;
default:
@@ -602,23 +821,6 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
-/**
- * fsl_ssi_shutdown: shutdown the SSI
- *
- * Shutdown the SSI if there are no other substreams open.
- */
-static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
-
- if (ssi_private->first_stream == substream)
- ssi_private->first_stream = ssi_private->second_stream;
-
- ssi_private->second_stream = NULL;
-}
-
static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
{
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai);
@@ -634,7 +836,9 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
.startup = fsl_ssi_startup,
.hw_params = fsl_ssi_hw_params,
- .shutdown = fsl_ssi_shutdown,
+ .set_fmt = fsl_ssi_set_dai_fmt,
+ .set_sysclk = fsl_ssi_set_dai_sysclk,
+ .set_tdm_slot = fsl_ssi_set_dai_tdm_slot,
.trigger = fsl_ssi_trigger,
};
@@ -642,14 +846,13 @@ static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
static struct snd_soc_dai_driver fsl_ssi_dai_template = {
.probe = fsl_ssi_dai_probe,
.playback = {
- /* The SSI does not support monaural audio. */
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = FSLSSI_I2S_RATES,
.formats = FSLSSI_I2S_FORMATS,
},
.capture = {
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = FSLSSI_I2S_RATES,
.formats = FSLSSI_I2S_FORMATS,
@@ -710,7 +913,6 @@ static int fsl_ssi_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
static const struct snd_soc_dai_ops fsl_ssi_ac97_dai_ops = {
.startup = fsl_ssi_startup,
- .shutdown = fsl_ssi_shutdown,
.trigger = fsl_ssi_ac97_trigger,
};
@@ -935,8 +1137,11 @@ static int fsl_ssi_probe(struct platform_device *pdev)
}
/* Are the RX and the TX clocks locked? */
- if (!of_find_property(np, "fsl,ssi-asynchronous", NULL))
+ if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) {
ssi_private->cpu_dai_drv.symmetric_rates = 1;
+ ssi_private->cpu_dai_drv.symmetric_channels = 1;
+ ssi_private->cpu_dai_drv.symmetric_samplebits = 1;
+ }
/* Determine the FIFO depth. */
iprop = of_get_property(np, "fsl,fifo-depth", NULL);
@@ -946,6 +1151,9 @@ static int fsl_ssi_probe(struct platform_device *pdev)
/* Older 8610 DTs didn't have the fifo-depth property */
ssi_private->fifo_depth = 8;
+ ssi_private->baudclk_locked = false;
+ spin_lock_init(&ssi_private->baudclk_lock);
+
if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) {
u32 dma_events[2];
ssi_private->ssi_on_imx = true;
@@ -963,6 +1171,15 @@ static int fsl_ssi_probe(struct platform_device *pdev)
goto error_irqmap;
}
+ /* For those SLAVE implementations, we ingore non-baudclk cases
+ * and, instead, abandon MASTER mode that needs baud clock.
+ */
+ ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
+ if (IS_ERR(ssi_private->baudclk))
+ dev_warn(&pdev->dev, "could not get baud clock: %d\n", ret);
+ else
+ clk_prepare_enable(ssi_private->baudclk);
+
/*
* We have burstsize be "fifo_depth - 2" to match the SSI
* watermark setting in fsl_ssi_startup().
@@ -1102,16 +1319,17 @@ done:
return 0;
error_dai:
- if (ssi_private->ssi_on_imx)
- imx_pcm_dma_exit(pdev);
snd_soc_unregister_component(&pdev->dev);
error_dev:
device_remove_file(&pdev->dev, dev_attr);
error_clk:
- if (ssi_private->ssi_on_imx)
+ if (ssi_private->ssi_on_imx) {
+ if (!IS_ERR(ssi_private->baudclk))
+ clk_disable_unprepare(ssi_private->baudclk);
clk_disable_unprepare(ssi_private->clk);
+ }
error_irqmap:
irq_dispose_mapping(ssi_private->irq);
@@ -1125,12 +1343,13 @@ static int fsl_ssi_remove(struct platform_device *pdev)
if (!ssi_private->new_binding)
platform_device_unregister(ssi_private->pdev);
- if (ssi_private->ssi_on_imx)
- imx_pcm_dma_exit(pdev);
snd_soc_unregister_component(&pdev->dev);
device_remove_file(&pdev->dev, &ssi_private->dev_attr);
- if (ssi_private->ssi_on_imx)
+ if (ssi_private->ssi_on_imx) {
+ if (!IS_ERR(ssi_private->baudclk))
+ clk_disable_unprepare(ssi_private->baudclk);
clk_disable_unprepare(ssi_private->clk);
+ }
irq_dispose_mapping(ssi_private->irq);
return 0;
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h
index e6b9a69e2a68..e6b63240a3d7 100644
--- a/sound/soc/fsl/fsl_ssi.h
+++ b/sound/soc/fsl/fsl_ssi.h
@@ -125,7 +125,9 @@ struct ccsr_ssi {
#define CCSR_SSI_SRCR_REFS 0x00000001
/* STCCR and SRCCR */
+#define CCSR_SSI_SxCCR_DIV2_SHIFT 18
#define CCSR_SSI_SxCCR_DIV2 0x00040000
+#define CCSR_SSI_SxCCR_PSR_SHIFT 17
#define CCSR_SSI_SxCCR_PSR 0x00020000
#define CCSR_SSI_SxCCR_WL_SHIFT 13
#define CCSR_SSI_SxCCR_WL_MASK 0x0001E000
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index aee23077080a..c5e47f866b4b 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -61,16 +61,11 @@ static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = {
int imx_pcm_dma_init(struct platform_device *pdev)
{
- return snd_dmaengine_pcm_register(&pdev->dev, &imx_dmaengine_pcm_config,
+ return devm_snd_dmaengine_pcm_register(&pdev->dev,
+ &imx_dmaengine_pcm_config,
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
SND_DMAENGINE_PCM_FLAG_COMPAT);
}
EXPORT_SYMBOL_GPL(imx_pcm_dma_init);
-void imx_pcm_dma_exit(struct platform_device *pdev)
-{
- snd_dmaengine_pcm_unregister(&pdev->dev);
-}
-EXPORT_SYMBOL_GPL(imx_pcm_dma_exit);
-
MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h
index 5d5b73303e11..c79cb27473be 100644
--- a/sound/soc/fsl/imx-pcm.h
+++ b/sound/soc/fsl/imx-pcm.h
@@ -40,16 +40,11 @@ struct imx_pcm_fiq_params {
#if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA)
int imx_pcm_dma_init(struct platform_device *pdev);
-void imx_pcm_dma_exit(struct platform_device *pdev);
#else
static inline int imx_pcm_dma_init(struct platform_device *pdev)
{
return -ENODEV;
}
-
-static inline void imx_pcm_dma_exit(struct platform_device *pdev)
-{
-}
#endif
#if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_FIQ)
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
index 8499d5292f08..e1dc40143600 100644
--- a/sound/soc/fsl/imx-spdif.c
+++ b/sound/soc/fsl/imx-spdif.c
@@ -14,17 +14,15 @@
#include <sound/soc.h>
struct imx_spdif_data {
- struct snd_soc_dai_link dai[2];
+ struct snd_soc_dai_link dai;
struct snd_soc_card card;
- struct platform_device *txdev;
- struct platform_device *rxdev;
};
static int imx_spdif_audio_probe(struct platform_device *pdev)
{
struct device_node *spdif_np, *np = pdev->dev.of_node;
struct imx_spdif_data *data;
- int ret = 0, num_links = 0;
+ int ret = 0;
spdif_np = of_parse_phandle(np, "spdif-controller", 0);
if (!spdif_np) {
@@ -35,74 +33,46 @@ static int imx_spdif_audio_probe(struct platform_device *pdev)
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
- dev_err(&pdev->dev, "failed to allocate memory\n");
ret = -ENOMEM;
goto end;
}
- if (of_property_read_bool(np, "spdif-out")) {
- data->dai[num_links].name = "S/PDIF TX";
- data->dai[num_links].stream_name = "S/PDIF PCM Playback";
- data->dai[num_links].codec_dai_name = "dit-hifi";
- data->dai[num_links].codec_name = "spdif-dit";
- data->dai[num_links].cpu_of_node = spdif_np;
- data->dai[num_links].platform_of_node = spdif_np;
- num_links++;
-
- data->txdev = platform_device_register_simple("spdif-dit", -1, NULL, 0);
- if (IS_ERR(data->txdev)) {
- ret = PTR_ERR(data->txdev);
- dev_err(&pdev->dev, "register dit failed: %d\n", ret);
- goto end;
- }
- }
+ data->dai.name = "S/PDIF PCM";
+ data->dai.stream_name = "S/PDIF PCM";
+ data->dai.codec_dai_name = "snd-soc-dummy-dai";
+ data->dai.codec_name = "snd-soc-dummy";
+ data->dai.cpu_of_node = spdif_np;
+ data->dai.platform_of_node = spdif_np;
+ data->dai.playback_only = true;
+ data->dai.capture_only = true;
- if (of_property_read_bool(np, "spdif-in")) {
- data->dai[num_links].name = "S/PDIF RX";
- data->dai[num_links].stream_name = "S/PDIF PCM Capture";
- data->dai[num_links].codec_dai_name = "dir-hifi";
- data->dai[num_links].codec_name = "spdif-dir";
- data->dai[num_links].cpu_of_node = spdif_np;
- data->dai[num_links].platform_of_node = spdif_np;
- num_links++;
-
- data->rxdev = platform_device_register_simple("spdif-dir", -1, NULL, 0);
- if (IS_ERR(data->rxdev)) {
- ret = PTR_ERR(data->rxdev);
- dev_err(&pdev->dev, "register dir failed: %d\n", ret);
- goto error_dit;
- }
- }
+ if (of_property_read_bool(np, "spdif-out"))
+ data->dai.capture_only = false;
+
+ if (of_property_read_bool(np, "spdif-in"))
+ data->dai.playback_only = false;
- if (!num_links) {
+ if (data->dai.playback_only && data->dai.capture_only) {
dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n");
- goto error_dir;
+ goto end;
}
data->card.dev = &pdev->dev;
- data->card.num_links = num_links;
- data->card.dai_link = data->dai;
+ data->card.dai_link = &data->dai;
+ data->card.num_links = 1;
ret = snd_soc_of_parse_card_name(&data->card, "model");
if (ret)
- goto error_dir;
+ goto end;
ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret);
- goto error_dir;
+ goto end;
}
platform_set_drvdata(pdev, data);
- goto end;
-
-error_dir:
- if (data->rxdev)
- platform_device_unregister(data->rxdev);
-error_dit:
- if (data->txdev)
- platform_device_unregister(data->txdev);
end:
if (spdif_np)
of_node_put(spdif_np);
@@ -110,18 +80,6 @@ end:
return ret;
}
-static int imx_spdif_audio_remove(struct platform_device *pdev)
-{
- struct imx_spdif_data *data = platform_get_drvdata(pdev);
-
- if (data->rxdev)
- platform_device_unregister(data->rxdev);
- if (data->txdev)
- platform_device_unregister(data->txdev);
-
- return 0;
-}
-
static const struct of_device_id imx_spdif_dt_ids[] = {
{ .compatible = "fsl,imx-audio-spdif", },
{ /* sentinel */ }
@@ -135,7 +93,6 @@ static struct platform_driver imx_spdif_driver = {
.of_match_table = imx_spdif_dt_ids,
},
.probe = imx_spdif_audio_probe,
- .remove = imx_spdif_audio_remove,
};
module_platform_driver(imx_spdif_driver);
diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
index f5f248c91c16..df552fa1aa65 100644
--- a/sound/soc/fsl/imx-ssi.c
+++ b/sound/soc/fsl/imx-ssi.c
@@ -304,8 +304,7 @@ static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
scr |= SSI_SCR_RE;
sier |= sier_bits;
- if (++ssi->enabled == 1)
- scr |= SSI_SCR_SSIEN;
+ scr |= SSI_SCR_SSIEN;
break;
@@ -318,7 +317,7 @@ static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
scr &= ~SSI_SCR_RE;
sier &= ~sier_bits;
- if (--ssi->enabled == 0)
+ if (!(scr & (SSI_SCR_TE | SSI_SCR_RE)))
scr &= ~SSI_SCR_SSIEN;
break;
@@ -536,7 +535,9 @@ static int imx_ssi_probe(struct platform_device *pdev)
ret);
goto failed_clk;
}
- clk_prepare_enable(ssi->clk);
+ ret = clk_prepare_enable(ssi->clk);
+ if (ret)
+ goto failed_clk;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ssi->base = devm_ioremap_resource(&pdev->dev, res);
@@ -624,9 +625,6 @@ static int imx_ssi_remove(struct platform_device *pdev)
{
struct imx_ssi *ssi = platform_get_drvdata(pdev);
- if (!ssi->dma_init)
- imx_pcm_dma_exit(pdev);
-
if (!ssi->fiq_init)
imx_pcm_fiq_exit(pdev);
diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h
index 560c40fc9ebb..be6562365b6a 100644
--- a/sound/soc/fsl/imx-ssi.h
+++ b/sound/soc/fsl/imx-ssi.h
@@ -213,7 +213,6 @@ struct imx_ssi {
int fiq_init;
int dma_init;
- int enabled;
};
#endif /* _IMX_SSI_H */
diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c
index 61e48852b9e8..3fd76bc391de 100644
--- a/sound/soc/fsl/imx-wm8962.c
+++ b/sound/soc/fsl/imx-wm8962.c
@@ -130,8 +130,6 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
break;
}
- dapm->bias_level = level;
-
return 0;
}
diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c
index eb4373840bb6..3665f612819d 100644
--- a/sound/soc/fsl/pcm030-audio-fabric.c
+++ b/sound/soc/fsl/pcm030-audio-fabric.c
@@ -69,7 +69,6 @@ static int pcm030_fabric_probe(struct platform_device *op)
return -ENOMEM;
card->dev = &op->dev;
- platform_set_drvdata(op, pdata);
pdata->card = card;
@@ -98,6 +97,8 @@ static int pcm030_fabric_probe(struct platform_device *op)
if (ret)
dev_err(&op->dev, "snd_soc_register_card() failed: %d\n", ret);
+ platform_set_drvdata(op, pdata);
+
return ret;
}
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index b2fbb7075a6c..c0d928138c88 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -8,7 +8,8 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-
+#include <linux/clk.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <sound/simple_card.h>
@@ -24,7 +25,7 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
daifmt |= set->fmt;
- if (!ret && daifmt)
+ if (daifmt)
ret = snd_soc_dai_set_fmt(dai, daifmt);
if (ret == -ENOTSUPP) {
@@ -57,11 +58,170 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
+static int
+asoc_simple_card_sub_parse_of(struct device_node *np,
+ struct asoc_simple_dai *dai,
+ struct device_node **node)
+{
+ struct clk *clk;
+ int ret;
+
+ /*
+ * get node via "sound-dai = <&phandle port>"
+ * it will be used as xxx_of_node on soc_bind_dai_link()
+ */
+ *node = of_parse_phandle(np, "sound-dai", 0);
+ if (!*node)
+ return -ENODEV;
+
+ /* get dai->name */
+ ret = snd_soc_of_get_dai_name(np, &dai->name);
+ if (ret < 0)
+ goto parse_error;
+
+ /*
+ * bitclock-inversion, frame-inversion
+ * bitclock-master, frame-master
+ * and specific "format" if it has
+ */
+ dai->fmt = snd_soc_of_parse_daifmt(np, NULL);
+
+ /*
+ * dai->sysclk come from
+ * "clocks = <&xxx>" (if system has common clock)
+ * or "system-clock-frequency = <xxx>"
+ * or device's module clock.
+ */
+ if (of_property_read_bool(np, "clocks")) {
+ clk = of_clk_get(np, 0);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto parse_error;
+ }
+
+ dai->sysclk = clk_get_rate(clk);
+ } else if (of_property_read_bool(np, "system-clock-frequency")) {
+ of_property_read_u32(np,
+ "system-clock-frequency",
+ &dai->sysclk);
+ } else {
+ clk = of_clk_get(*node, 0);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto parse_error;
+ }
+
+ dai->sysclk = clk_get_rate(clk);
+ }
+
+ ret = 0;
+
+parse_error:
+ of_node_put(*node);
+
+ return ret;
+}
+
+static int asoc_simple_card_parse_of(struct device_node *node,
+ struct asoc_simple_card_info *info,
+ struct device *dev,
+ struct device_node **of_cpu,
+ struct device_node **of_codec,
+ struct device_node **of_platform)
+{
+ struct device_node *np;
+ char *name;
+ int ret;
+
+ /* get CPU/CODEC common format via simple-audio-card,format */
+ info->daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,") &
+ (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK);
+
+ /* DAPM routes */
+ ret = snd_soc_of_parse_audio_routing(&info->snd_card,
+ "simple-audio-routing");
+ if (ret)
+ return ret;
+
+ /* CPU sub-node */
+ ret = -EINVAL;
+ np = of_get_child_by_name(node, "simple-audio-card,cpu");
+ if (np)
+ ret = asoc_simple_card_sub_parse_of(np,
+ &info->cpu_dai,
+ of_cpu);
+ if (ret < 0)
+ return ret;
+
+ /* CODEC sub-node */
+ ret = -EINVAL;
+ np = of_get_child_by_name(node, "simple-audio-card,codec");
+ if (np)
+ ret = asoc_simple_card_sub_parse_of(np,
+ &info->codec_dai,
+ of_codec);
+ if (ret < 0)
+ return ret;
+
+ if (!info->cpu_dai.name || !info->codec_dai.name)
+ return -EINVAL;
+
+ /* card name is created from CPU/CODEC dai name */
+ name = devm_kzalloc(dev,
+ strlen(info->cpu_dai.name) +
+ strlen(info->codec_dai.name) + 2,
+ GFP_KERNEL);
+ sprintf(name, "%s-%s", info->cpu_dai.name, info->codec_dai.name);
+ info->name = info->card = name;
+
+ /* simple-card assumes platform == cpu */
+ *of_platform = *of_cpu;
+
+ dev_dbg(dev, "card-name : %s\n", info->card);
+ dev_dbg(dev, "platform : %04x\n", info->daifmt);
+ dev_dbg(dev, "cpu : %s / %04x / %d\n",
+ info->cpu_dai.name,
+ info->cpu_dai.fmt,
+ info->cpu_dai.sysclk);
+ dev_dbg(dev, "codec : %s / %04x / %d\n",
+ info->codec_dai.name,
+ info->codec_dai.fmt,
+ info->codec_dai.sysclk);
+
+ return 0;
+}
+
static int asoc_simple_card_probe(struct platform_device *pdev)
{
- struct asoc_simple_card_info *cinfo = pdev->dev.platform_data;
+ struct asoc_simple_card_info *cinfo;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *of_cpu, *of_codec, *of_platform;
struct device *dev = &pdev->dev;
+ cinfo = NULL;
+ of_cpu = NULL;
+ of_codec = NULL;
+ of_platform = NULL;
+ if (np && of_device_is_available(np)) {
+ cinfo = devm_kzalloc(dev, sizeof(*cinfo), GFP_KERNEL);
+ if (cinfo) {
+ int ret;
+ cinfo->snd_card.dev = &pdev->dev;
+ ret = asoc_simple_card_parse_of(np, cinfo, dev,
+ &of_cpu,
+ &of_codec,
+ &of_platform);
+ if (ret < 0) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "parse error %d\n", ret);
+ return ret;
+ }
+ }
+ } else {
+ cinfo->snd_card.dev = &pdev->dev;
+ cinfo = pdev->dev.platform_data;
+ }
+
if (!cinfo) {
dev_err(dev, "no info for asoc-simple-card\n");
return -EINVAL;
@@ -69,10 +229,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
if (!cinfo->name ||
!cinfo->card ||
- !cinfo->codec ||
- !cinfo->platform ||
- !cinfo->cpu_dai.name ||
- !cinfo->codec_dai.name) {
+ !cinfo->codec_dai.name ||
+ !(cinfo->codec || of_codec) ||
+ !(cinfo->platform || of_platform) ||
+ !(cinfo->cpu_dai.name || of_cpu)) {
dev_err(dev, "insufficient asoc_simple_card_info settings\n");
return -EINVAL;
}
@@ -86,6 +246,9 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
cinfo->snd_link.platform_name = cinfo->platform;
cinfo->snd_link.codec_name = cinfo->codec;
cinfo->snd_link.codec_dai_name = cinfo->codec_dai.name;
+ cinfo->snd_link.cpu_of_node = of_cpu;
+ cinfo->snd_link.codec_of_node = of_codec;
+ cinfo->snd_link.platform_of_node = of_platform;
cinfo->snd_link.init = asoc_simple_card_dai_init;
/*
@@ -95,25 +258,23 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
cinfo->snd_card.owner = THIS_MODULE;
cinfo->snd_card.dai_link = &cinfo->snd_link;
cinfo->snd_card.num_links = 1;
- cinfo->snd_card.dev = &pdev->dev;
- return snd_soc_register_card(&cinfo->snd_card);
+ return devm_snd_soc_register_card(&pdev->dev, &cinfo->snd_card);
}
-static int asoc_simple_card_remove(struct platform_device *pdev)
-{
- struct asoc_simple_card_info *cinfo = pdev->dev.platform_data;
-
- return snd_soc_unregister_card(&cinfo->snd_card);
-}
+static const struct of_device_id asoc_simple_of_match[] = {
+ { .compatible = "simple-audio-card", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
static struct platform_driver asoc_simple_card = {
.driver = {
.name = "asoc-simple-card",
.owner = THIS_MODULE,
+ .of_match_table = asoc_simple_of_match,
},
.probe = asoc_simple_card_probe,
- .remove = asoc_simple_card_remove,
};
module_platform_driver(asoc_simple_card);
diff --git a/sound/soc/mid-x86/Kconfig b/sound/soc/intel/Kconfig
index 61c10bf503d2..61c10bf503d2 100644
--- a/sound/soc/mid-x86/Kconfig
+++ b/sound/soc/intel/Kconfig
diff --git a/sound/soc/mid-x86/Makefile b/sound/soc/intel/Makefile
index 639883339465..639883339465 100644
--- a/sound/soc/mid-x86/Makefile
+++ b/sound/soc/intel/Makefile
diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/intel/mfld_machine.c
index d3d4c32434f7..d3d4c32434f7 100644
--- a/sound/soc/mid-x86/mfld_machine.c
+++ b/sound/soc/intel/mfld_machine.c
diff --git a/sound/soc/mid-x86/sst_dsp.h b/sound/soc/intel/sst_dsp.h
index 0fce1de284ff..0fce1de284ff 100644
--- a/sound/soc/mid-x86/sst_dsp.h
+++ b/sound/soc/intel/sst_dsp.h
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/intel/sst_platform.c
index b6b5eb698d33..b6b5eb698d33 100644
--- a/sound/soc/mid-x86/sst_platform.c
+++ b/sound/soc/intel/sst_platform.c
diff --git a/sound/soc/mid-x86/sst_platform.h b/sound/soc/intel/sst_platform.h
index cacc9066ec52..cacc9066ec52 100644
--- a/sound/soc/mid-x86/sst_platform.h
+++ b/sound/soc/intel/sst_platform.h
diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
index 5351cba66c9e..29f76af5d963 100644
--- a/sound/soc/jz4740/Kconfig
+++ b/sound/soc/jz4740/Kconfig
@@ -1,6 +1,7 @@
config SND_JZ4740_SOC
tristate "SoC Audio for Ingenic JZ4740 SoC"
depends on MACH_JZ4740 && SND_SOC
+ select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for codecs attached to
the JZ4740 I2S interface. You will also need to select the audio
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index 4c849a49c72a..8f220009e0f6 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -29,9 +29,11 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
+#include <sound/dmaengine_pcm.h>
+
+#include <asm/mach-jz4740/dma.h>
#include "jz4740-i2s.h"
-#include "jz4740-pcm.h"
#define JZ_REG_AIC_CONF 0x00
#define JZ_REG_AIC_CTRL 0x04
@@ -89,8 +91,8 @@ struct jz4740_i2s {
struct clk *clk_aic;
struct clk *clk_i2s;
- struct jz4740_pcm_config pcm_config_playback;
- struct jz4740_pcm_config pcm_config_capture;
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
};
static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
@@ -233,8 +235,6 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- enum jz4740_dma_width dma_width;
- struct jz4740_pcm_config *pcm_config;
unsigned int sample_size;
uint32_t ctrl;
@@ -243,11 +243,9 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
sample_size = 0;
- dma_width = JZ4740_DMA_WIDTH_8BIT;
break;
case SNDRV_PCM_FORMAT_S16:
sample_size = 1;
- dma_width = JZ4740_DMA_WIDTH_16BIT;
break;
default:
return -EINVAL;
@@ -260,22 +258,13 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
else
ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO;
-
- pcm_config = &i2s->pcm_config_playback;
- pcm_config->dma_config.dst_width = dma_width;
-
} else {
ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
-
- pcm_config = &i2s->pcm_config_capture;
- pcm_config->dma_config.src_width = dma_width;
}
jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
- snd_soc_dai_set_dma_data(dai, substream, pcm_config);
-
return 0;
}
@@ -342,25 +331,19 @@ static int jz4740_i2s_resume(struct snd_soc_dai *dai)
static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
{
- struct jz4740_dma_config *dma_config;
+ struct snd_dmaengine_dai_dma_data *dma_data;
/* Playback */
- dma_config = &i2s->pcm_config_playback.dma_config;
- dma_config->src_width = JZ4740_DMA_WIDTH_32BIT;
- dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
- dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
- dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
- dma_config->mode = JZ4740_DMA_MODE_SINGLE;
- i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
+ dma_data = &i2s->playback_dma_data;
+ dma_data->maxburst = 16;
+ dma_data->slave_id = JZ4740_DMA_TYPE_AIC_TRANSMIT;
+ dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO;
/* Capture */
- dma_config = &i2s->pcm_config_capture.dma_config;
- dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT;
- dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
- dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
- dma_config->flags = JZ4740_DMA_DST_AUTOINC;
- dma_config->mode = JZ4740_DMA_MODE_SINGLE;
- i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
+ dma_data = &i2s->capture_dma_data;
+ dma_data->maxburst = 16;
+ dma_data->slave_id = JZ4740_DMA_TYPE_AIC_RECEIVE;
+ dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO;
}
static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
@@ -371,6 +354,8 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
clk_prepare_enable(i2s->clk_aic);
jz4740_i2c_init_pcm_config(i2s);
+ snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
+ &i2s->capture_dma_data);
conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
(8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
@@ -432,91 +417,41 @@ static const struct snd_soc_component_driver jz4740_i2s_component = {
static int jz4740_i2s_dev_probe(struct platform_device *pdev)
{
struct jz4740_i2s *i2s;
+ struct resource *mem;
int ret;
- i2s = kzalloc(sizeof(*i2s), GFP_KERNEL);
-
+ i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s)
return -ENOMEM;
- i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!i2s->mem) {
- ret = -ENOENT;
- goto err_free;
- }
-
- i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem),
- pdev->name);
- if (!i2s->mem) {
- ret = -EBUSY;
- goto err_free;
- }
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ i2s->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(i2s->base))
+ return PTR_ERR(i2s->base);
- i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem));
- if (!i2s->base) {
- ret = -EBUSY;
- goto err_release_mem_region;
- }
+ i2s->phys_base = mem->start;
- i2s->phys_base = i2s->mem->start;
+ i2s->clk_aic = devm_clk_get(&pdev->dev, "aic");
+ if (IS_ERR(i2s->clk_aic))
+ return PTR_ERR(i2s->clk_aic);
- i2s->clk_aic = clk_get(&pdev->dev, "aic");
- if (IS_ERR(i2s->clk_aic)) {
- ret = PTR_ERR(i2s->clk_aic);
- goto err_iounmap;
- }
-
- i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
- if (IS_ERR(i2s->clk_i2s)) {
- ret = PTR_ERR(i2s->clk_i2s);
- goto err_clk_put_aic;
- }
+ i2s->clk_i2s = devm_clk_get(&pdev->dev, "i2s");
+ if (IS_ERR(i2s->clk_i2s))
+ return PTR_ERR(i2s->clk_i2s);
platform_set_drvdata(pdev, i2s);
- ret = snd_soc_register_component(&pdev->dev, &jz4740_i2s_component,
- &jz4740_i2s_dai, 1);
- if (ret) {
- dev_err(&pdev->dev, "Failed to register DAI\n");
- goto err_clk_put_i2s;
- }
-
- return 0;
-
-err_clk_put_i2s:
- clk_put(i2s->clk_i2s);
-err_clk_put_aic:
- clk_put(i2s->clk_aic);
-err_iounmap:
- iounmap(i2s->base);
-err_release_mem_region:
- release_mem_region(i2s->mem->start, resource_size(i2s->mem));
-err_free:
- kfree(i2s);
-
- return ret;
-}
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &jz4740_i2s_component, &jz4740_i2s_dai, 1);
+ if (ret)
+ return ret;
-static int jz4740_i2s_dev_remove(struct platform_device *pdev)
-{
- struct jz4740_i2s *i2s = platform_get_drvdata(pdev);
-
- snd_soc_unregister_component(&pdev->dev);
-
- clk_put(i2s->clk_i2s);
- clk_put(i2s->clk_aic);
-
- iounmap(i2s->base);
- release_mem_region(i2s->mem->start, resource_size(i2s->mem));
-
- kfree(i2s);
-
- return 0;
+ return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
}
static struct platform_driver jz4740_i2s_driver = {
.probe = jz4740_i2s_dev_probe,
- .remove = jz4740_i2s_dev_remove,
.driver = {
.name = "jz4740-i2s",
.owner = THIS_MODULE,
diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c
deleted file mode 100644
index 1d7ef28585e1..000000000000
--- a/sound/soc/jz4740/jz4740-pcm.c
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include <linux/dma-mapping.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <asm/mach-jz4740/dma.h>
-#include "jz4740-pcm.h"
-
-struct jz4740_runtime_data {
- unsigned long dma_period;
- dma_addr_t dma_start;
- dma_addr_t dma_pos;
- dma_addr_t dma_end;
-
- struct jz4740_dma_chan *dma;
-
- dma_addr_t fifo_addr;
-};
-
-/* identify hardware playback capabilities */
-static const struct snd_pcm_hardware jz4740_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
-
- .rates = SNDRV_PCM_RATE_8000_48000,
- .channels_min = 1,
- .channels_max = 2,
- .period_bytes_min = 16,
- .period_bytes_max = 2 * PAGE_SIZE,
- .periods_min = 2,
- .periods_max = 128,
- .buffer_bytes_max = 128 * 2 * PAGE_SIZE,
- .fifo_size = 32,
-};
-
-static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd,
- struct snd_pcm_substream *substream)
-{
- unsigned long count;
-
- if (prtd->dma_pos == prtd->dma_end)
- prtd->dma_pos = prtd->dma_start;
-
- if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
- count = prtd->dma_end - prtd->dma_pos;
- else
- count = prtd->dma_period;
-
- jz4740_dma_disable(prtd->dma);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
- jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
- } else {
- jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
- jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
- }
-
- jz4740_dma_set_transfer_count(prtd->dma, count);
-
- prtd->dma_pos += count;
-
- jz4740_dma_enable(prtd->dma);
-}
-
-static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
- void *dev_id)
-{
- struct snd_pcm_substream *substream = dev_id;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct jz4740_runtime_data *prtd = runtime->private_data;
-
- snd_pcm_period_elapsed(substream);
-
- jz4740_pcm_start_transfer(prtd, substream);
-}
-
-static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct jz4740_runtime_data *prtd = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct jz4740_pcm_config *config;
-
- config = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- if (!config)
- return 0;
-
- if (!prtd->dma) {
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- prtd->dma = jz4740_dma_request(substream, "PCM Capture");
- else
- prtd->dma = jz4740_dma_request(substream, "PCM Playback");
- }
-
- if (!prtd->dma)
- return -EBUSY;
-
- jz4740_dma_configure(prtd->dma, &config->dma_config);
- prtd->fifo_addr = config->fifo_addr;
-
- jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
-
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = params_buffer_bytes(params);
-
- prtd->dma_period = params_period_bytes(params);
- prtd->dma_start = runtime->dma_addr;
- prtd->dma_pos = prtd->dma_start;
- prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
-
- return 0;
-}
-
-static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct jz4740_runtime_data *prtd = substream->runtime->private_data;
-
- snd_pcm_set_runtime_buffer(substream, NULL);
- if (prtd->dma) {
- jz4740_dma_free(prtd->dma);
- prtd->dma = NULL;
- }
-
- return 0;
-}
-
-static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct jz4740_runtime_data *prtd = substream->runtime->private_data;
-
- if (!prtd->dma)
- return -EBUSY;
-
- prtd->dma_pos = prtd->dma_start;
-
- return 0;
-}
-
-static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct jz4740_runtime_data *prtd = runtime->private_data;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- jz4740_pcm_start_transfer(prtd, substream);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- jz4740_dma_disable(prtd->dma);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct jz4740_runtime_data *prtd = runtime->private_data;
- unsigned long byte_offset;
- snd_pcm_uframes_t offset;
- struct jz4740_dma_chan *dma = prtd->dma;
-
- /* prtd->dma_pos points to the end of the current transfer. So by
- * subtracting prdt->dma_start we get the offset to the end of the
- * current period in bytes. By subtracting the residue of the transfer
- * we get the current offset in bytes. */
- byte_offset = prtd->dma_pos - prtd->dma_start;
- byte_offset -= jz4740_dma_get_residue(dma);
-
- offset = bytes_to_frames(runtime, byte_offset);
- if (offset >= runtime->buffer_size)
- offset = 0;
-
- return offset;
-}
-
-static int jz4740_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct jz4740_runtime_data *prtd;
-
- prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
- if (prtd == NULL)
- return -ENOMEM;
-
- snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
-
- runtime->private_data = prtd;
-
- return 0;
-}
-
-static int jz4740_pcm_close(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct jz4740_runtime_data *prtd = runtime->private_data;
-
- kfree(prtd);
-
- return 0;
-}
-
-static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- return remap_pfn_range(vma, vma->vm_start,
- substream->dma_buffer.addr >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start, vma->vm_page_prot);
-}
-
-static struct snd_pcm_ops jz4740_pcm_ops = {
- .open = jz4740_pcm_open,
- .close = jz4740_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = jz4740_pcm_hw_params,
- .hw_free = jz4740_pcm_hw_free,
- .prepare = jz4740_pcm_prepare,
- .trigger = jz4740_pcm_trigger,
- .pointer = jz4740_pcm_pointer,
- .mmap = jz4740_pcm_mmap,
-};
-
-static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = jz4740_pcm_hardware.buffer_bytes_max;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
-
- buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
-
- buf->bytes = size;
-
- return 0;
-}
-
-static void jz4740_pcm_free(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < SNDRV_PCM_STREAM_LAST; ++stream) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
-
- dma_free_noncoherent(pcm->card->dev, buf->bytes, buf->area,
- buf->addr);
- buf->area = NULL;
- }
-}
-
-static int jz4740_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
- int ret;
-
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = jz4740_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto err;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = jz4740_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto err;
- }
-
-err:
- return ret;
-}
-
-static struct snd_soc_platform_driver jz4740_soc_platform = {
- .ops = &jz4740_pcm_ops,
- .pcm_new = jz4740_pcm_new,
- .pcm_free = jz4740_pcm_free,
-};
-
-static int jz4740_pcm_probe(struct platform_device *pdev)
-{
- return snd_soc_register_platform(&pdev->dev, &jz4740_soc_platform);
-}
-
-static int jz4740_pcm_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver jz4740_pcm_driver = {
- .probe = jz4740_pcm_probe,
- .remove = jz4740_pcm_remove,
- .driver = {
- .name = "jz4740-pcm-audio",
- .owner = THIS_MODULE,
- },
-};
-
-module_platform_driver(jz4740_pcm_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h
deleted file mode 100644
index 1220cbb4382c..000000000000
--- a/sound/soc/jz4740/jz4740-pcm.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _JZ4740_PCM_H
-#define _JZ4740_PCM_H
-
-#include <linux/dma-mapping.h>
-#include <asm/mach-jz4740/dma.h>
-
-
-struct jz4740_pcm_config {
- struct jz4740_dma_config dma_config;
- phys_addr_t fifo_addr;
-};
-
-#endif
diff --git a/sound/soc/jz4740/qi_lb60.c b/sound/soc/jz4740/qi_lb60.c
index 55fd6b5df55f..82b5f37cd2c7 100644
--- a/sound/soc/jz4740/qi_lb60.c
+++ b/sound/soc/jz4740/qi_lb60.c
@@ -73,7 +73,7 @@ static struct snd_soc_dai_link qi_lb60_dai = {
.name = "jz4740",
.stream_name = "jz4740",
.cpu_dai_name = "jz4740-i2s",
- .platform_name = "jz4740-pcm-audio",
+ .platform_name = "jz4740-i2s",
.codec_dai_name = "jz4740-hifi",
.codec_name = "jz4740-codec",
.init = qi_lb60_codec_init,
diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c
index d34d91743e3f..3920a5e8125f 100644
--- a/sound/soc/kirkwood/kirkwood-i2s.c
+++ b/sound/soc/kirkwood/kirkwood-i2s.c
@@ -33,6 +33,10 @@
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
+#define KIRKWOOD_SPDIF_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
@@ -244,15 +248,15 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
ctl);
}
- if (dai->id == 0)
- ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN; /* i2s */
- else
- ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN; /* spdif */
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
/* configure */
ctl = priv->ctl_play;
+ if (dai->id == 0)
+ ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN; /* i2s */
+ else
+ ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN; /* spdif */
+
value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
writel(value, priv->io + KIRKWOOD_PLAYCTL);
@@ -449,14 +453,14 @@ static struct snd_soc_dai_driver kirkwood_i2s_dai[2] = {
.channels_max = 2,
.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000,
- .formats = KIRKWOOD_I2S_FORMATS,
+ .formats = KIRKWOOD_SPDIF_FORMATS,
},
.capture = {
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000,
- .formats = KIRKWOOD_I2S_FORMATS,
+ .formats = KIRKWOOD_SPDIF_FORMATS,
},
.ops = &kirkwood_i2s_dai_ops,
},
@@ -469,17 +473,17 @@ static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = {
.playback = {
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000 |
- SNDRV_PCM_RATE_CONTINUOUS |
- SNDRV_PCM_RATE_KNOT,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 192000,
.formats = KIRKWOOD_I2S_FORMATS,
},
.capture = {
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000 |
- SNDRV_PCM_RATE_CONTINUOUS |
- SNDRV_PCM_RATE_KNOT,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 192000,
.formats = KIRKWOOD_I2S_FORMATS,
},
.ops = &kirkwood_i2s_dai_ops,
@@ -490,18 +494,18 @@ static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk[2] = {
.playback = {
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000 |
- SNDRV_PCM_RATE_CONTINUOUS |
- SNDRV_PCM_RATE_KNOT,
- .formats = KIRKWOOD_I2S_FORMATS,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .formats = KIRKWOOD_SPDIF_FORMATS,
},
.capture = {
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000 |
- SNDRV_PCM_RATE_CONTINUOUS |
- SNDRV_PCM_RATE_KNOT,
- .formats = KIRKWOOD_I2S_FORMATS,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .formats = KIRKWOOD_SPDIF_FORMATS,
},
.ops = &kirkwood_i2s_dai_ops,
},
diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c
index b16abbbf7764..04a6b0d60944 100644
--- a/sound/soc/mxs/mxs-pcm.c
+++ b/sound/soc/mxs/mxs-pcm.c
@@ -56,16 +56,10 @@ static const struct snd_dmaengine_pcm_config mxs_dmaengine_pcm_config = {
int mxs_pcm_platform_register(struct device *dev)
{
- return snd_dmaengine_pcm_register(dev, &mxs_dmaengine_pcm_config,
+ return devm_snd_dmaengine_pcm_register(dev, &mxs_dmaengine_pcm_config,
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX);
}
EXPORT_SYMBOL_GPL(mxs_pcm_platform_register);
-void mxs_pcm_platform_unregister(struct device *dev)
-{
- snd_dmaengine_pcm_unregister(dev);
-}
-EXPORT_SYMBOL_GPL(mxs_pcm_platform_unregister);
-
MODULE_LICENSE("GPL");
diff --git a/sound/soc/mxs/mxs-pcm.h b/sound/soc/mxs/mxs-pcm.h
index bc685b67cac7..035ea0436ca5 100644
--- a/sound/soc/mxs/mxs-pcm.h
+++ b/sound/soc/mxs/mxs-pcm.h
@@ -20,6 +20,5 @@
#define _MXS_PCM_H
int mxs_pcm_platform_register(struct device *dev);
-void mxs_pcm_platform_unregister(struct device *dev);
#endif
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index 54e622acac33..231d7e7b0711 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -50,9 +50,9 @@ static struct mxs_saif *mxs_saif[2];
* This also means that both SAIFs must operate at the same sample rate.
*
* We abstract this as each saif has a master, the master could be
- * himself or other saifs. In the generic saif driver, saif does not need
- * to know the different clkmux. Saif only needs to know who is his master
- * and operating his master to generate the proper clock rate for him.
+ * itself or other saifs. In the generic saif driver, saif does not need
+ * to know the different clkmux. Saif only needs to know who is its master
+ * and operating its master to generate the proper clock rate for it.
* The master id is provided in mach-specific layer according to different
* clkmux setting.
*/
@@ -76,7 +76,7 @@ static int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
* Since SAIF may work on EXTMASTER mode, IOW, it's working BITCLK&LRCLK
* is provided by other SAIF, we provide a interface here to get its master
* from its master_id.
- * Note that the master could be himself.
+ * Note that the master could be itself.
*/
static inline struct mxs_saif *mxs_saif_get_master(struct mxs_saif * saif)
{
@@ -516,7 +516,7 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd,
}
/*
- * If the saif's master is not himself, we also need to enable
+ * If the saif's master is not itself, we also need to enable
* itself clk for its internal basic logic to work.
*/
if (saif != master_saif) {
@@ -804,13 +804,6 @@ static int mxs_saif_probe(struct platform_device *pdev)
return 0;
}
-static int mxs_saif_remove(struct platform_device *pdev)
-{
- mxs_pcm_platform_unregister(&pdev->dev);
-
- return 0;
-}
-
static const struct of_device_id mxs_saif_dt_ids[] = {
{ .compatible = "fsl,imx28-saif", },
{ /* sentinel */ }
@@ -819,7 +812,6 @@ MODULE_DEVICE_TABLE(of, mxs_saif_dt_ids);
static struct platform_driver mxs_saif_driver = {
.probe = mxs_saif_probe,
- .remove = mxs_saif_remove,
.driver = {
.name = "mxs-saif",
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c
index 83433fdea32a..86c75384c3c8 100644
--- a/sound/soc/omap/mcbsp.c
+++ b/sound/soc/omap/mcbsp.c
@@ -36,10 +36,10 @@ static void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
if (mcbsp->pdata->reg_size == 2) {
((u16 *)mcbsp->reg_cache)[reg] = (u16)val;
- __raw_writew((u16)val, addr);
+ writew_relaxed((u16)val, addr);
} else {
((u32 *)mcbsp->reg_cache)[reg] = val;
- __raw_writel(val, addr);
+ writel_relaxed(val, addr);
}
}
@@ -48,22 +48,22 @@ static int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache)
void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step;
if (mcbsp->pdata->reg_size == 2) {
- return !from_cache ? __raw_readw(addr) :
+ return !from_cache ? readw_relaxed(addr) :
((u16 *)mcbsp->reg_cache)[reg];
} else {
- return !from_cache ? __raw_readl(addr) :
+ return !from_cache ? readl_relaxed(addr) :
((u32 *)mcbsp->reg_cache)[reg];
}
}
static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
{
- __raw_writel(val, mcbsp->st_data->io_base_st + reg);
+ writel_relaxed(val, mcbsp->st_data->io_base_st + reg);
}
static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg)
{
- return __raw_readl(mcbsp->st_data->io_base_st + reg);
+ return readl_relaxed(mcbsp->st_data->io_base_st + reg);
}
#define MCBSP_READ(mcbsp, reg) \
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index 6d216cb6c19b..3fde9e402710 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -100,12 +100,12 @@ static int n810_startup(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
n810_ext_control(&codec->dapm);
- return clk_enable(sys_clkout2);
+ return clk_prepare_enable(sys_clkout2);
}
static void n810_shutdown(struct snd_pcm_substream *substream)
{
- clk_disable(sys_clkout2);
+ clk_disable_unprepare(sys_clkout2);
}
static int n810_hw_params(struct snd_pcm_substream *substream,
diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c
index 12e566be3793..1bd531d718f9 100644
--- a/sound/soc/omap/omap-dmic.c
+++ b/sound/soc/omap/omap-dmic.c
@@ -61,12 +61,12 @@ struct omap_dmic {
static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val)
{
- __raw_writel(val, dmic->io_base + reg);
+ writel_relaxed(val, dmic->io_base + reg);
}
static inline int omap_dmic_read(struct omap_dmic *dmic, u16 reg)
{
- return __raw_readl(dmic->io_base + reg);
+ return readl_relaxed(dmic->io_base + reg);
}
static inline void omap_dmic_start(struct omap_dmic *dmic)
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c
index cd9ee167959d..2f5b1536477e 100644
--- a/sound/soc/omap/omap-mcpdm.c
+++ b/sound/soc/omap/omap-mcpdm.c
@@ -74,12 +74,12 @@ struct omap_mcpdm {
static inline void omap_mcpdm_write(struct omap_mcpdm *mcpdm, u16 reg, u32 val)
{
- __raw_writel(val, mcpdm->io_base + reg);
+ writel_relaxed(val, mcpdm->io_base + reg);
}
static inline int omap_mcpdm_read(struct omap_mcpdm *mcpdm, u16 reg)
{
- return __raw_readl(mcpdm->io_base + reg);
+ return readl_relaxed(mcpdm->io_base + reg);
}
#ifdef DEBUG
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index b8fa9862e54c..07b8b7bc9d20 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -45,8 +45,6 @@ static const struct snd_pcm_hardware omap_pcm_hardware = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
.period_bytes_min = 32,
.period_bytes_max = 64 * 1024,
.periods_min = 2,
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 4db74a083db1..6473052b6899 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -11,7 +11,7 @@ config SND_PXA2XX_SOC
config SND_MMP_SOC
bool "Soc Audio for Marvell MMP chips"
depends on ARCH_MMP
- select SND_DMAENGINE_PCM
+ select SND_SOC_GENERIC_DMAENGINE_PCM
select SND_ARM
help
Say Y if you want to add support for codecs attached to
diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c
index 7929e19b0ef5..5e8d81330173 100644
--- a/sound/soc/pxa/mmp-pcm.c
+++ b/sound/soc/pxa/mmp-pcm.c
@@ -36,14 +36,9 @@ struct mmp_dma_data {
SNDRV_PCM_INFO_PAUSE | \
SNDRV_PCM_INFO_RESUME)
-#define MMP_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE)
-
static struct snd_pcm_hardware mmp_pcm_hardware[] = {
{
.info = MMP_PCM_INFO,
- .formats = MMP_PCM_FORMATS,
.period_bytes_min = 1024,
.period_bytes_max = 2048,
.periods_min = 2,
@@ -53,7 +48,6 @@ static struct snd_pcm_hardware mmp_pcm_hardware[] = {
},
{
.info = MMP_PCM_INFO,
- .formats = MMP_PCM_FORMATS,
.period_bytes_min = 1024,
.period_bytes_max = 2048,
.periods_min = 2,
@@ -67,27 +61,15 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_dmaengine_dai_dma_data *dma_params;
struct dma_slave_config slave_config;
int ret;
- dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
- if (!dma_params)
- return 0;
-
- ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config);
+ ret =
+ snd_dmaengine_pcm_prepare_slave_config(substream, params,
+ &slave_config);
if (ret)
return ret;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- slave_config.dst_addr = dma_params->addr;
- slave_config.dst_maxburst = 4;
- } else {
- slave_config.src_addr = dma_params->addr;
- slave_config.src_maxburst = 4;
- }
-
ret = dmaengine_slave_config(chan, &slave_config);
if (ret)
return ret;
diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c
index d219880815c0..fb8461e1b1f6 100644
--- a/sound/soc/s6000/s6000-pcm.c
+++ b/sound/soc/s6000/s6000-pcm.c
@@ -33,13 +33,6 @@ static struct snd_pcm_hardware s6000_pcm_hardware = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_JOINT_DUPLEX),
- .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE),
- .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \
- SNDRV_PCM_RATE_8000_192000),
- .rate_min = 0,
- .rate_max = 1562500,
- .channels_min = 2,
- .channels_max = 8,
.buffer_bytes_max = 0x7ffffff0,
.period_bytes_min = 16,
.period_bytes_max = 0xfffff0,
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 37459dfd168d..27930fc432dc 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -1,13 +1,22 @@
config SND_SOC_SAMSUNG
tristate "ASoC support for Samsung"
depends on PLAT_SAMSUNG
- select S3C64XX_DMA if ARCH_S3C64XX
- select S3C24XX_DMA if ARCH_S3C24XX
+ select S3C2410_DMA if ARCH_S3C24XX
+ select S3C64XX_PL080 if ARCH_S3C64XX
+ select SND_S3C_DMA if !ARCH_S3C24XX
+ select SND_S3C_DMA_LEGACY if ARCH_S3C24XX
+ select SND_SOC_GENERIC_DMAENGINE_PCM if !ARCH_S3C24XX
help
Say Y or M if you want to add support for codecs attached to
the Samsung SoCs' Audio interfaces. You will also need to
select the audio interfaces to support below.
+config SND_S3C_DMA
+ tristate
+
+config SND_S3C_DMA_LEGACY
+ tristate
+
config SND_S3C24XX_I2S
tristate
select S3C2410_DMA
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index 709f6059ad67..86715d8efee6 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -1,5 +1,6 @@
# S3c24XX Platform Support
-snd-soc-s3c24xx-objs := dma.o
+snd-soc-s3c-dma-objs := dmaengine.o
+snd-soc-s3c-dma-legacy-objs := dma.o
snd-soc-idma-objs := idma.o
snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o
@@ -9,7 +10,8 @@ snd-soc-samsung-spdif-objs := spdif.o
snd-soc-pcm-objs := pcm.o
snd-soc-i2s-objs := i2s.o
-obj-$(CONFIG_SND_SOC_SAMSUNG) += snd-soc-s3c24xx.o
+obj-$(CONFIG_SND_S3C_DMA) += snd-soc-s3c-dma.o
+obj-$(CONFIG_SND_S3C_DMA_LEGACY) += snd-soc-s3c-dma-legacy.o
obj-$(CONFIG_SND_S3C24XX_I2S) += snd-soc-s3c24xx-i2s.o
obj-$(CONFIG_SND_SAMSUNG_AC97) += snd-soc-ac97.o
obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index 350ba23a9893..4a88e36c82ec 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -221,24 +221,6 @@ static struct snd_ac97_bus_ops s3c_ac97_ops = {
.reset = s3c_ac97_cold_reset,
};
-static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct s3c_dma_params *dma_data;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dma_data = &s3c_ac97_pcm_out;
- else
- dma_data = &s3c_ac97_pcm_in;
-
- snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
-
- return 0;
-}
-
static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
@@ -279,21 +261,6 @@ static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
-static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- return -ENODEV;
- else
- snd_soc_dai_set_dma_data(cpu_dai, substream, &s3c_ac97_mic_in);
-
- return 0;
-}
-
static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
@@ -329,15 +296,27 @@ static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
}
static const struct snd_soc_dai_ops s3c_ac97_dai_ops = {
- .hw_params = s3c_ac97_hw_params,
.trigger = s3c_ac97_trigger,
};
static const struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
- .hw_params = s3c_ac97_hw_mic_params,
.trigger = s3c_ac97_mic_trigger,
};
+static int s3c_ac97_dai_probe(struct snd_soc_dai *dai)
+{
+ samsung_asoc_init_dma_data(dai, &s3c_ac97_pcm_out, &s3c_ac97_pcm_in);
+
+ return 0;
+}
+
+static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai)
+{
+ samsung_asoc_init_dma_data(dai, NULL, &s3c_ac97_mic_in);
+
+ return 0;
+}
+
static struct snd_soc_dai_driver s3c_ac97_dai[] = {
[S3C_AC97_DAI_PCM] = {
.name = "samsung-ac97",
@@ -354,6 +333,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = {
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .probe = s3c_ac97_dai_probe,
.ops = &s3c_ac97_dai_ops,
},
[S3C_AC97_DAI_MIC] = {
@@ -365,6 +345,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = {
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .probe = s3c_ac97_mic_dai_probe,
.ops = &s3c_ac97_mic_dai_ops,
},
};
diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c
index fe2748b494d4..dc09b71b7d9f 100644
--- a/sound/soc/samsung/dma.c
+++ b/sound/soc/samsung/dma.c
@@ -35,12 +35,6 @@ static const struct snd_pcm_hardware dma_hardware = {
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_U16_LE |
- SNDRV_PCM_FMTBIT_U8 |
- SNDRV_PCM_FMTBIT_S8,
- .channels_min = 2,
- .channels_max = 2,
.buffer_bytes_max = 128*1024,
.period_bytes_min = PAGE_SIZE,
.period_bytes_max = PAGE_SIZE*2,
@@ -441,6 +435,14 @@ static struct snd_soc_platform_driver samsung_asoc_platform = {
.pcm_free = dma_free_dma_buffers,
};
+void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
+ struct s3c_dma_params *playback,
+ struct s3c_dma_params *capture)
+{
+ snd_soc_dai_init_dma_data(dai, playback, capture);
+}
+EXPORT_SYMBOL_GPL(samsung_asoc_init_dma_data);
+
int samsung_asoc_dma_platform_register(struct device *dev)
{
return snd_soc_register_platform(dev, &samsung_asoc_platform);
diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h
index 0e86315a3eaf..225e5378014e 100644
--- a/sound/soc/samsung/dma.h
+++ b/sound/soc/samsung/dma.h
@@ -12,6 +12,8 @@
#ifndef _S3C_AUDIO_H
#define _S3C_AUDIO_H
+#include <sound/dmaengine_pcm.h>
+
struct s3c_dma_params {
struct s3c2410_dma_client *client; /* stream identifier */
int channel; /* Channel ID */
@@ -20,8 +22,12 @@ struct s3c_dma_params {
unsigned ch;
struct samsung_dma_ops *ops;
char *ch_name;
+ struct snd_dmaengine_dai_dma_data dma_data;
};
+void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
+ struct s3c_dma_params *playback,
+ struct s3c_dma_params *capture);
int samsung_asoc_dma_platform_register(struct device *dev);
void samsung_asoc_dma_platform_unregister(struct device *dev);
diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c
new file mode 100644
index 000000000000..3be479d51b9b
--- /dev/null
+++ b/sound/soc/samsung/dmaengine.c
@@ -0,0 +1,84 @@
+/*
+ * dmaengine.c - Samsung dmaengine wrapper
+ *
+ * Author: Mark Brown <broonie@linaro.org>
+ * Copyright 2013 Linaro
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/amba/pl08x.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "dma.h"
+
+#ifdef CONFIG_ARCH_S3C64XX
+#define filter_fn pl08x_filter_id
+#else
+#define filter_fn NULL
+#endif
+
+static const struct snd_dmaengine_pcm_config samsung_dmaengine_pcm_config = {
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ .compat_filter_fn = filter_fn,
+};
+
+void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
+ struct s3c_dma_params *playback,
+ struct s3c_dma_params *capture)
+{
+ struct snd_dmaengine_dai_dma_data *playback_data = NULL;
+ struct snd_dmaengine_dai_dma_data *capture_data = NULL;
+
+ if (playback) {
+ playback_data = &playback->dma_data;
+ playback_data->filter_data = (void *)playback->channel;
+ playback_data->chan_name = playback->ch_name;
+ playback_data->addr = playback->dma_addr;
+ playback_data->addr_width = playback->dma_size;
+ }
+ if (capture) {
+ capture_data = &capture->dma_data;
+ capture_data->filter_data = (void *)capture->channel;
+ capture_data->chan_name = capture->ch_name;
+ capture_data->addr = capture->dma_addr;
+ capture_data->addr_width = capture->dma_size;
+ }
+
+ snd_soc_dai_init_dma_data(dai, playback_data, capture_data);
+}
+EXPORT_SYMBOL_GPL(samsung_asoc_init_dma_data);
+
+int samsung_asoc_dma_platform_register(struct device *dev)
+{
+ return snd_dmaengine_pcm_register(dev, &samsung_dmaengine_pcm_config,
+ SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME |
+ SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
+}
+EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
+
+void samsung_asoc_dma_platform_unregister(struct device *dev)
+{
+ return snd_dmaengine_pcm_unregister(dev);
+}
+EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_unregister);
+
+MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
+MODULE_DESCRIPTION("Samsung dmaengine ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index a5cbdb4f1655..92f64363427d 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -702,6 +702,8 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
}
writel(mod, i2s->addr + I2SMOD);
+ samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
+
i2s->frmclk = params_rate(params);
return 0;
@@ -946,8 +948,11 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
- if (other && other->clk) /* If this is probe on secondary */
+ if (other && other->clk) { /* If this is probe on secondary */
+ samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback,
+ NULL);
goto probe_exit;
+ }
i2s->addr = ioremap(i2s->base, 0x100);
if (i2s->addr == NULL) {
@@ -963,7 +968,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
}
clk_prepare_enable(i2s->clk);
- snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
+ samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
if (other) {
other->addr = i2s->addr;
diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c
index e4f318fc2f82..3d5cf1530b6f 100644
--- a/sound/soc/samsung/idma.c
+++ b/sound/soc/samsung/idma.c
@@ -35,14 +35,6 @@ static const struct snd_pcm_hardware idma_hardware = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_U16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_U24_LE |
- SNDRV_PCM_FMTBIT_U8 |
- SNDRV_PCM_FMTBIT_S8,
- .channels_min = 2,
- .channels_max = 2,
.buffer_bytes_max = MAX_IDMA_BUFFER,
.period_bytes_min = 128,
.period_bytes_max = MAX_IDMA_PERIOD,
diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c
index e54256fc4b2c..6a5e4bf6ac96 100644
--- a/sound/soc/samsung/pcm.c
+++ b/sound/soc/samsung/pcm.c
@@ -275,7 +275,6 @@ static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai);
- struct s3c_dma_params *dma_data;
void __iomem *regs = pcm->regs;
struct clk *clk;
int sclk_div, sync_div;
@@ -284,13 +283,6 @@ static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
dev_dbg(pcm->dev, "Entered %s\n", __func__);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dma_data = pcm->dma_playback;
- else
- dma_data = pcm->dma_capture;
-
- snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
-
/* Strictly check for sample size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -461,10 +453,20 @@ static const struct snd_soc_dai_ops s3c_pcm_dai_ops = {
.set_fmt = s3c_pcm_set_fmt,
};
+static int s3c_pcm_dai_probe(struct snd_soc_dai *dai)
+{
+ struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, pcm->dma_playback, pcm->dma_capture);
+
+ return 0;
+}
+
#define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000
#define S3C_PCM_DAI_DECLARE \
.symmetric_rates = 1, \
+ .probe = s3c_pcm_dai_probe, \
.ops = &s3c_pcm_dai_ops, \
.playback = { \
.channels_min = 2, \
diff --git a/sound/soc/samsung/regs-ac97.h b/sound/soc/samsung/regs-ac97.h
index c3878f7acb83..a71be45bbffc 100644
--- a/sound/soc/samsung/regs-ac97.h
+++ b/sound/soc/samsung/regs-ac97.h
@@ -1,5 +1,4 @@
-/* arch/arm/mach-s3c2410/include/mach/regs-ac97.h
- *
+/*
* Copyright (c) 2006 Simtec Electronics <linux@simtec.co.uk>
* http://www.simtec.co.uk/products/SWLINUX/
*
@@ -10,8 +9,8 @@
* S3C2440 AC97 Controller
*/
-#ifndef __ASM_ARCH_REGS_AC97_H
-#define __ASM_ARCH_REGS_AC97_H __FILE__
+#ifndef __SAMSUNG_REGS_AC97_H__
+#define __SAMSUNG_REGS_AC97_H__
#define S3C_AC97_GLBCTRL (0x00)
@@ -64,4 +63,4 @@
#define S3C_AC97_PCM_DATA (0x18)
#define S3C_AC97_MIC_DATA (0x1C)
-#endif /* __ASM_ARCH_REGS_AC97_H */
+#endif /* __SAMSUNG_REGS_AC97_H__ */
diff --git a/sound/soc/samsung/regs-iis.h b/sound/soc/samsung/regs-iis.h
index a18d35e7a735..dc6cbbe9c4f0 100644
--- a/sound/soc/samsung/regs-iis.h
+++ b/sound/soc/samsung/regs-iis.h
@@ -1,5 +1,4 @@
-/* arch/arm/plat-samsung/include/plat/regs-iis.h
- *
+/*
* Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
* http://www.simtec.co.uk/products/SWLINUX/
*
@@ -10,8 +9,8 @@
* S3C2410 IIS register definition
*/
-#ifndef __ASM_ARCH_REGS_IIS_H
-#define __ASM_ARCH_REGS_IIS_H
+#ifndef __SAMSUNG_REGS_IIS_H__
+#define __SAMSUNG_REGS_IIS_H__
#define S3C2410_IISCON (0x00)
@@ -67,4 +66,4 @@
#define S3C2410_IISFIFO (0x10)
-#endif /* __ASM_ARCH_REGS_IIS_H */
+#endif /* __SAMSUNG_REGS_IIS_H__ */
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 14011d90d70a..ff60e11ecb56 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -37,6 +37,7 @@ config SND_SOC_SH4_SIU
config SND_SOC_RCAR
tristate "R-Car series SRU/SCU/SSIU/SSI support"
select SND_SIMPLE_CARD
+ select REGMAP
help
This option enables R-Car SUR/SCU/SSIU/SSI sound support
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index b33ca7cd085b..6101055aae1d 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -232,9 +232,9 @@ struct fsi_stream {
* these are for DMAEngine
*/
struct dma_chan *chan;
- struct sh_dmae_slave slave; /* see fsi_handler_init() */
struct work_struct work;
dma_addr_t dma;
+ int dma_id;
int loop_cnt;
int additional_pos;
};
@@ -1410,15 +1410,6 @@ static void fsi_dma_do_work(struct work_struct *work)
}
}
-static bool fsi_dma_filter(struct dma_chan *chan, void *param)
-{
- struct sh_dmae_slave *slave = param;
-
- chan->private = slave;
-
- return true;
-}
-
static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io)
{
schedule_work(&io->work);
@@ -1446,15 +1437,34 @@ static int fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev)
{
dma_cap_mask_t mask;
+ int is_play = fsi_stream_is_play(fsi, io);
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
- io->chan = dma_request_channel(mask, fsi_dma_filter, &io->slave);
+ io->chan = dma_request_slave_channel_compat(mask,
+ shdma_chan_filter, (void *)io->dma_id,
+ dev, is_play ? "tx" : "rx");
+ if (io->chan) {
+ struct dma_slave_config cfg;
+ int ret;
+
+ cfg.slave_id = io->dma_id;
+ cfg.dst_addr = 0; /* use default addr */
+ cfg.src_addr = 0; /* use default addr */
+ cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+
+ ret = dmaengine_slave_config(io->chan, &cfg);
+ if (ret < 0) {
+ dma_release_channel(io->chan);
+ io->chan = NULL;
+ }
+ }
+
if (!io->chan) {
/* switch to PIO handler */
- if (fsi_stream_is_play(fsi, io))
+ if (is_play)
fsi->playback.handler = &fsi_pio_push_handler;
else
fsi->capture.handler = &fsi_pio_pop_handler;
@@ -1960,7 +1970,7 @@ static void fsi_handler_init(struct fsi_priv *fsi,
fsi->capture.priv = fsi;
if (info->tx_id) {
- fsi->playback.slave.shdma_slave.slave_id = info->tx_id;
+ fsi->playback.dma_id = info->tx_id;
fsi->playback.handler = &fsi_dma_push_handler;
}
}
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 9430097979a5..a53235c4d1b0 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -19,8 +19,8 @@
struct rsnd_adg {
struct clk *clk[CLKMAX];
- int rate_of_441khz_div_6;
- int rate_of_48khz_div_6;
+ int rbga_rate_for_441khz_div_6; /* RBGA */
+ int rbgb_rate_for_48khz_div_6; /* RBGB */
u32 ckr;
};
@@ -30,41 +30,114 @@ struct rsnd_adg {
i++, (pos) = adg->clk[i])
#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
-static enum rsnd_reg rsnd_adg_ssi_reg_get(int id)
+static int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ unsigned int src_rate,
+ unsigned int dst_rate)
{
- enum rsnd_reg reg;
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int idx, sel, div, shift;
+ u32 mask, val;
+ int id = rsnd_mod_id(mod);
+ unsigned int sel_rate [] = {
+ clk_get_rate(adg->clk[CLKA]), /* 000: CLKA */
+ clk_get_rate(adg->clk[CLKB]), /* 001: CLKB */
+ clk_get_rate(adg->clk[CLKC]), /* 010: CLKC */
+ 0, /* 011: MLBCLK (not used) */
+ adg->rbga_rate_for_441khz_div_6,/* 100: RBGA */
+ adg->rbgb_rate_for_48khz_div_6, /* 101: RBGB */
+ };
+
+ /* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */
+ for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
+ for (div = 128, idx = 0;
+ div <= 2048;
+ div *= 2, idx++) {
+ if (src_rate == sel_rate[sel] / div) {
+ val = (idx << 4) | sel;
+ goto find_rate;
+ }
+ }
+ }
+ dev_err(dev, "can't find convert src clk\n");
+ return -EINVAL;
+
+find_rate:
+ shift = (id % 4) * 8;
+ mask = 0xFF << shift;
+ val = val << shift;
+
+ dev_dbg(dev, "adg convert src clk = %02x\n", val);
+
+ switch (id / 4) {
+ case 0:
+ rsnd_mod_bset(mod, AUDIO_CLK_SEL3, mask, val);
+ break;
+ case 1:
+ rsnd_mod_bset(mod, AUDIO_CLK_SEL4, mask, val);
+ break;
+ case 2:
+ rsnd_mod_bset(mod, AUDIO_CLK_SEL5, mask, val);
+ break;
+ }
+
+ /*
+ * Gen1 doesn't need dst_rate settings,
+ * since it uses SSI WS pin.
+ * see also rsnd_src_set_route_if_gen1()
+ */
+
+ return 0;
+}
+
+int rsnd_adg_set_convert_clk(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ unsigned int src_rate,
+ unsigned int dst_rate)
+{
+ if (rsnd_is_gen1(priv))
+ return rsnd_adg_set_convert_clk_gen1(priv, mod,
+ src_rate, dst_rate);
+
+ return -EINVAL;
+}
+
+static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
+{
+ int id = rsnd_mod_id(mod);
+ int shift = (id % 4) * 8;
+ u32 mask = 0xFF << shift;
+
+ val = val << shift;
/*
* SSI 8 is not connected to ADG.
* it works with SSI 7
*/
if (id == 8)
- return RSND_REG_MAX;
-
- if (0 <= id && id <= 3)
- reg = RSND_REG_AUDIO_CLK_SEL0;
- else if (4 <= id && id <= 7)
- reg = RSND_REG_AUDIO_CLK_SEL1;
- else
- reg = RSND_REG_AUDIO_CLK_SEL2;
-
- return reg;
+ return;
+
+ switch (id / 4) {
+ case 0:
+ rsnd_mod_bset(mod, AUDIO_CLK_SEL0, mask, val);
+ break;
+ case 1:
+ rsnd_mod_bset(mod, AUDIO_CLK_SEL1, mask, val);
+ break;
+ case 2:
+ rsnd_mod_bset(mod, AUDIO_CLK_SEL2, mask, val);
+ break;
+ }
}
int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod)
{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- enum rsnd_reg reg;
- int id;
-
/*
* "mod" = "ssi" here.
* we can get "ssi id" from mod
*/
- id = rsnd_mod_id(mod);
- reg = rsnd_adg_ssi_reg_get(id);
-
- rsnd_write(priv, mod, reg, 0);
+ rsnd_adg_set_ssi_clk(mod, 0);
return 0;
}
@@ -75,8 +148,7 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct device *dev = rsnd_priv_to_dev(priv);
struct clk *clk;
- enum rsnd_reg reg;
- int id, shift, i;
+ int i;
u32 data;
int sel_table[] = {
[CLKA] = 0x1,
@@ -102,12 +174,12 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
/*
* find 1/6 clock from BRGA/BRGB
*/
- if (rate == adg->rate_of_441khz_div_6) {
+ if (rate == adg->rbga_rate_for_441khz_div_6) {
data = 0x10;
goto found_clock;
}
- if (rate == adg->rate_of_48khz_div_6) {
+ if (rate == adg->rbgb_rate_for_48khz_div_6) {
data = 0x20;
goto found_clock;
}
@@ -125,19 +197,10 @@ found_clock:
* This "mod" = "ssi" here.
* we can get "ssi id" from mod
*/
- id = rsnd_mod_id(mod);
- reg = rsnd_adg_ssi_reg_get(id);
-
- dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", id, i, rate);
-
- /*
- * Enable SSIx clock
- */
- shift = (id % 4) * 8;
+ rsnd_adg_set_ssi_clk(mod, data);
- rsnd_bset(priv, mod, reg,
- 0xFF << shift,
- data << shift);
+ dev_dbg(dev, "ADG: ssi%d selects clk%d = %d",
+ rsnd_mod_id(mod), i, rate);
return 0;
}
@@ -166,8 +229,8 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
* rsnd_adg_ssi_clk_try_start()
*/
ckr = 0;
- adg->rate_of_441khz_div_6 = 0;
- adg->rate_of_48khz_div_6 = 0;
+ adg->rbga_rate_for_441khz_div_6 = 0;
+ adg->rbgb_rate_for_48khz_div_6 = 0;
for_each_rsnd_clk(clk, adg, i) {
rate = clk_get_rate(clk);
@@ -175,14 +238,14 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
continue;
/* RBGA */
- if (!adg->rate_of_441khz_div_6 && (0 == rate % 44100)) {
- adg->rate_of_441khz_div_6 = rate / 6;
+ if (!adg->rbga_rate_for_441khz_div_6 && (0 == rate % 44100)) {
+ adg->rbga_rate_for_441khz_div_6 = rate / 6;
ckr |= brg_table[i] << 20;
}
/* RBGB */
- if (!adg->rate_of_48khz_div_6 && (0 == rate % 48000)) {
- adg->rate_of_48khz_div_6 = rate / 6;
+ if (!adg->rbgb_rate_for_48khz_div_6 && (0 == rate % 48000)) {
+ adg->rbgb_rate_for_48khz_div_6 = rate / 6;
ckr |= brg_table[i] << 16;
}
}
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index 61212ee97c28..add088bd4b2a 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -10,20 +10,6 @@
*/
#include "rsnd.h"
-struct rsnd_gen_ops {
- int (*probe)(struct platform_device *pdev,
- struct rcar_snd_info *info,
- struct rsnd_priv *priv);
- void (*remove)(struct platform_device *pdev,
- struct rsnd_priv *priv);
- int (*path_init)(struct rsnd_priv *priv,
- struct rsnd_dai *rdai,
- struct rsnd_dai_stream *io);
- int (*path_exit)(struct rsnd_priv *priv,
- struct rsnd_dai *rdai,
- struct rsnd_dai_stream *io);
-};
-
struct rsnd_gen {
void __iomem *base[RSND_BASE_MAX];
@@ -86,12 +72,28 @@ static struct regmap_bus rsnd_regmap_bus = {
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
+static int rsnd_is_accessible_reg(struct rsnd_priv *priv,
+ struct rsnd_gen *gen, enum rsnd_reg reg)
+{
+ if (!gen->regs[reg]) {
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_err(dev, "unsupported register access %x\n", reg);
+ return 0;
+ }
+
+ return 1;
+}
+
u32 rsnd_read(struct rsnd_priv *priv,
struct rsnd_mod *mod, enum rsnd_reg reg)
{
struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
u32 val;
+ if (!rsnd_is_accessible_reg(priv, gen, reg))
+ return 0;
+
regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
return val;
@@ -103,6 +105,9 @@ void rsnd_write(struct rsnd_priv *priv,
{
struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+ if (!rsnd_is_accessible_reg(priv, gen, reg))
+ return;
+
regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
}
@@ -111,21 +116,48 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
{
struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+ if (!rsnd_is_accessible_reg(priv, gen, reg))
+ return;
+
regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
mask, data);
}
-/*
- * Gen2
- * will be filled in the future
- */
+static int rsnd_gen_regmap_init(struct rsnd_priv *priv,
+ struct rsnd_gen *gen,
+ struct reg_field *regf)
+{
+ int i;
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct regmap_config regc;
-/*
- * Gen1
- */
-static int rsnd_gen1_path_init(struct rsnd_priv *priv,
- struct rsnd_dai *rdai,
- struct rsnd_dai_stream *io)
+ memset(&regc, 0, sizeof(regc));
+ regc.reg_bits = 32;
+ regc.val_bits = 32;
+
+ gen->regmap = devm_regmap_init(dev, &rsnd_regmap_bus, priv, &regc);
+ if (IS_ERR(gen->regmap)) {
+ dev_err(dev, "regmap error %ld\n", PTR_ERR(gen->regmap));
+ return PTR_ERR(gen->regmap);
+ }
+
+ for (i = 0; i < RSND_REG_MAX; i++) {
+ gen->regs[i] = NULL;
+ if (!regf[i].reg)
+ continue;
+
+ gen->regs[i] = devm_regmap_field_alloc(dev, gen->regmap, regf[i]);
+ if (IS_ERR(gen->regs[i]))
+ return PTR_ERR(gen->regs[i]);
+
+ }
+
+ return 0;
+}
+
+int rsnd_gen_path_init(struct rsnd_priv *priv,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod;
int ret;
@@ -163,9 +195,9 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv,
return ret;
}
-static int rsnd_gen1_path_exit(struct rsnd_priv *priv,
- struct rsnd_dai *rdai,
- struct rsnd_dai_stream *io)
+int rsnd_gen_path_exit(struct rsnd_priv *priv,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod, *n;
int ret = 0;
@@ -179,6 +211,94 @@ static int rsnd_gen1_path_exit(struct rsnd_priv *priv,
return ret;
}
+/*
+ * Gen2
+ */
+
+/* single address mapping */
+#define RSND_GEN2_S_REG(gen, reg, id, offset) \
+ RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN2_##reg, offset, 0, 10)
+
+/* multi address mapping */
+#define RSND_GEN2_M_REG(gen, reg, id, offset, _id_offset) \
+ RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN2_##reg, offset, _id_offset, 10)
+
+static int rsnd_gen2_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)
+{
+ struct reg_field regf[RSND_REG_MAX] = {
+ RSND_GEN2_S_REG(gen, SSIU, SSI_MODE0, 0x800),
+ RSND_GEN2_S_REG(gen, SSIU, SSI_MODE1, 0x804),
+ /* FIXME: it needs SSI_MODE2/3 in the future */
+ RSND_GEN2_M_REG(gen, SSIU, INT_ENABLE, 0x18, 0x80),
+
+ RSND_GEN2_S_REG(gen, ADG, BRRA, 0x00),
+ RSND_GEN2_S_REG(gen, ADG, BRRB, 0x04),
+ RSND_GEN2_S_REG(gen, ADG, SSICKR, 0x08),
+ RSND_GEN2_S_REG(gen, ADG, AUDIO_CLK_SEL0, 0x0c),
+ RSND_GEN2_S_REG(gen, ADG, AUDIO_CLK_SEL1, 0x10),
+ RSND_GEN2_S_REG(gen, ADG, AUDIO_CLK_SEL2, 0x14),
+
+ RSND_GEN2_M_REG(gen, SSI, SSICR, 0x00, 0x40),
+ RSND_GEN2_M_REG(gen, SSI, SSISR, 0x04, 0x40),
+ RSND_GEN2_M_REG(gen, SSI, SSITDR, 0x08, 0x40),
+ RSND_GEN2_M_REG(gen, SSI, SSIRDR, 0x0c, 0x40),
+ RSND_GEN2_M_REG(gen, SSI, SSIWSR, 0x20, 0x40),
+ };
+
+ return rsnd_gen_regmap_init(priv, gen, regf);
+}
+
+static int rsnd_gen2_probe(struct platform_device *pdev,
+ struct rcar_snd_info *info,
+ struct rsnd_priv *priv)
+{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+ struct resource *scu_res;
+ struct resource *adg_res;
+ struct resource *ssiu_res;
+ struct resource *ssi_res;
+ int ret;
+
+ /*
+ * map address
+ */
+ scu_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_SCU);
+ adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_ADG);
+ ssiu_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_SSIU);
+ ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_SSI);
+
+ gen->base[RSND_GEN2_SCU] = devm_ioremap_resource(dev, scu_res);
+ gen->base[RSND_GEN2_ADG] = devm_ioremap_resource(dev, adg_res);
+ gen->base[RSND_GEN2_SSIU] = devm_ioremap_resource(dev, ssiu_res);
+ gen->base[RSND_GEN2_SSI] = devm_ioremap_resource(dev, ssi_res);
+ if (IS_ERR(gen->base[RSND_GEN2_SCU]) ||
+ IS_ERR(gen->base[RSND_GEN2_ADG]) ||
+ IS_ERR(gen->base[RSND_GEN2_SSIU]) ||
+ IS_ERR(gen->base[RSND_GEN2_SSI]))
+ return -ENODEV;
+
+ ret = rsnd_gen2_regmap_init(priv, gen);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "Gen2 device probed\n");
+ dev_dbg(dev, "SRU : %08x => %p\n", scu_res->start,
+ gen->base[RSND_GEN2_SCU]);
+ dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start,
+ gen->base[RSND_GEN2_ADG]);
+ dev_dbg(dev, "SSIU : %08x => %p\n", ssiu_res->start,
+ gen->base[RSND_GEN2_SSIU]);
+ dev_dbg(dev, "SSI : %08x => %p\n", ssi_res->start,
+ gen->base[RSND_GEN2_SSI]);
+
+ return 0;
+}
+
+/*
+ * Gen1
+ */
+
/* single address mapping */
#define RSND_GEN1_S_REG(gen, reg, id, offset) \
RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN1_##reg, offset, 0, 9)
@@ -189,19 +309,23 @@ static int rsnd_gen1_path_exit(struct rsnd_priv *priv,
static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)
{
- int i;
- struct device *dev = rsnd_priv_to_dev(priv);
- struct regmap_config regc;
struct reg_field regf[RSND_REG_MAX] = {
RSND_GEN1_S_REG(gen, SRU, SRC_ROUTE_SEL, 0x00),
RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL0, 0x08),
RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL1, 0x0c),
RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL2, 0x10),
- RSND_GEN1_S_REG(gen, SRU, SRC_CTRL, 0xc0),
+ RSND_GEN1_S_REG(gen, SRU, SRC_ROUTE_CTRL, 0xc0),
RSND_GEN1_S_REG(gen, SRU, SSI_MODE0, 0xD0),
RSND_GEN1_S_REG(gen, SRU, SSI_MODE1, 0xD4),
RSND_GEN1_M_REG(gen, SRU, BUSIF_MODE, 0x20, 0x4),
- RSND_GEN1_M_REG(gen, SRU, BUSIF_ADINR, 0x214, 0x40),
+ RSND_GEN1_M_REG(gen, SRU, SRC_ROUTE_MODE0,0x50, 0x8),
+ RSND_GEN1_M_REG(gen, SRU, SRC_SWRSR, 0x200, 0x40),
+ RSND_GEN1_M_REG(gen, SRU, SRC_SRCIR, 0x204, 0x40),
+ RSND_GEN1_M_REG(gen, SRU, SRC_ADINR, 0x214, 0x40),
+ RSND_GEN1_M_REG(gen, SRU, SRC_IFSCR, 0x21c, 0x40),
+ RSND_GEN1_M_REG(gen, SRU, SRC_IFSVR, 0x220, 0x40),
+ RSND_GEN1_M_REG(gen, SRU, SRC_SRCCR, 0x224, 0x40),
+ RSND_GEN1_M_REG(gen, SRU, SRC_MNFSR, 0x228, 0x40),
RSND_GEN1_S_REG(gen, ADG, BRRA, 0x00),
RSND_GEN1_S_REG(gen, ADG, BRRB, 0x04),
@@ -219,24 +343,7 @@ static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)
RSND_GEN1_M_REG(gen, SSI, SSIWSR, 0x20, 0x40),
};
- memset(&regc, 0, sizeof(regc));
- regc.reg_bits = 32;
- regc.val_bits = 32;
-
- gen->regmap = devm_regmap_init(dev, &rsnd_regmap_bus, priv, &regc);
- if (IS_ERR(gen->regmap)) {
- dev_err(dev, "regmap error %ld\n", PTR_ERR(gen->regmap));
- return PTR_ERR(gen->regmap);
- }
-
- for (i = 0; i < RSND_REG_MAX; i++) {
- gen->regs[i] = devm_regmap_field_alloc(dev, gen->regmap, regf[i]);
- if (IS_ERR(gen->regs[i]))
- return PTR_ERR(gen->regs[i]);
-
- }
-
- return 0;
+ return rsnd_gen_regmap_init(priv, gen, regf);
}
static int rsnd_gen1_probe(struct platform_device *pdev,
@@ -281,45 +388,16 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
}
-static void rsnd_gen1_remove(struct platform_device *pdev,
- struct rsnd_priv *priv)
-{
-}
-
-static struct rsnd_gen_ops rsnd_gen1_ops = {
- .probe = rsnd_gen1_probe,
- .remove = rsnd_gen1_remove,
- .path_init = rsnd_gen1_path_init,
- .path_exit = rsnd_gen1_path_exit,
-};
-
/*
* Gen
*/
-int rsnd_gen_path_init(struct rsnd_priv *priv,
- struct rsnd_dai *rdai,
- struct rsnd_dai_stream *io)
-{
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
-
- return gen->ops->path_init(priv, rdai, io);
-}
-
-int rsnd_gen_path_exit(struct rsnd_priv *priv,
- struct rsnd_dai *rdai,
- struct rsnd_dai_stream *io)
-{
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
-
- return gen->ops->path_exit(priv, rdai, io);
-}
-
int rsnd_gen_probe(struct platform_device *pdev,
struct rcar_snd_info *info,
struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_gen *gen;
+ int ret;
gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL);
if (!gen) {
@@ -327,23 +405,21 @@ int rsnd_gen_probe(struct platform_device *pdev,
return -ENOMEM;
}
+ priv->gen = gen;
+
+ ret = -ENODEV;
if (rsnd_is_gen1(priv))
- gen->ops = &rsnd_gen1_ops;
+ ret = rsnd_gen1_probe(pdev, info, priv);
+ else if (rsnd_is_gen2(priv))
+ ret = rsnd_gen2_probe(pdev, info, priv);
- if (!gen->ops) {
+ if (ret < 0)
dev_err(dev, "unknown generation R-Car sound device\n");
- return -ENODEV;
- }
-
- priv->gen = gen;
- return gen->ops->probe(pdev, info, priv);
+ return ret;
}
void rsnd_gen_remove(struct platform_device *pdev,
struct rsnd_priv *priv)
{
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
-
- gen->ops->remove(pdev, priv);
}
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 9e463e50e7e6..4ca66cd899c8 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -31,16 +31,24 @@
* see gen1/gen2 for detail
*/
enum rsnd_reg {
- /* SRU/SCU */
- RSND_REG_SRC_ROUTE_SEL,
- RSND_REG_SRC_TMG_SEL0,
- RSND_REG_SRC_TMG_SEL1,
- RSND_REG_SRC_TMG_SEL2,
- RSND_REG_SRC_CTRL,
+ /* SRU/SCU/SSIU */
+ RSND_REG_SRC_ROUTE_SEL, /* for Gen1 */
+ RSND_REG_SRC_TMG_SEL0, /* for Gen1 */
+ RSND_REG_SRC_TMG_SEL1, /* for Gen1 */
+ RSND_REG_SRC_TMG_SEL2, /* for Gen1 */
+ RSND_REG_SRC_ROUTE_CTRL, /* for Gen1 */
RSND_REG_SSI_MODE0,
RSND_REG_SSI_MODE1,
RSND_REG_BUSIF_MODE,
- RSND_REG_BUSIF_ADINR,
+ RSND_REG_INT_ENABLE, /* for Gen2 */
+ RSND_REG_SRC_ROUTE_MODE0,
+ RSND_REG_SRC_SWRSR,
+ RSND_REG_SRC_SRCIR,
+ RSND_REG_SRC_ADINR,
+ RSND_REG_SRC_IFSCR,
+ RSND_REG_SRC_IFSVR,
+ RSND_REG_SRC_SRCCR,
+ RSND_REG_SRC_MNFSR,
/* ADG */
RSND_REG_BRRA,
@@ -49,9 +57,9 @@ enum rsnd_reg {
RSND_REG_AUDIO_CLK_SEL0,
RSND_REG_AUDIO_CLK_SEL1,
RSND_REG_AUDIO_CLK_SEL2,
- RSND_REG_AUDIO_CLK_SEL3,
- RSND_REG_AUDIO_CLK_SEL4,
- RSND_REG_AUDIO_CLK_SEL5,
+ RSND_REG_AUDIO_CLK_SEL3, /* for Gen1 */
+ RSND_REG_AUDIO_CLK_SEL4, /* for Gen1 */
+ RSND_REG_AUDIO_CLK_SEL5, /* for Gen1 */
/* SSI */
RSND_REG_SSICR,
@@ -174,11 +182,11 @@ struct rsnd_dai {
struct rsnd_dai_stream playback;
struct rsnd_dai_stream capture;
- int clk_master:1;
- int bit_clk_inv:1;
- int frm_clk_inv:1;
- int sys_delay:1;
- int data_alignment:1;
+ unsigned int clk_master:1;
+ unsigned int bit_clk_inv:1;
+ unsigned int frm_clk_inv:1;
+ unsigned int sys_delay:1;
+ unsigned int data_alignment:1;
};
#define rsnd_dai_nr(priv) ((priv)->dai_nr)
@@ -229,6 +237,10 @@ int rsnd_adg_probe(struct platform_device *pdev,
struct rsnd_priv *priv);
void rsnd_adg_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
+int rsnd_adg_set_convert_clk(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ unsigned int src_rate,
+ unsigned int dst_rate);
/*
* R-Car sound priv
@@ -282,6 +294,10 @@ void rsnd_scu_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id);
bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod);
+unsigned int rsnd_scu_get_ssi_rate(struct rsnd_priv *priv,
+ struct rsnd_mod *ssi_mod,
+ struct snd_pcm_runtime *runtime);
+
#define rsnd_scu_nr(priv) ((priv)->scu_nr)
/*
diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c
index fa8fa15860b9..9bb08bb1d455 100644
--- a/sound/soc/sh/rcar/scu.c
+++ b/sound/soc/sh/rcar/scu.c
@@ -13,9 +13,13 @@
struct rsnd_scu {
struct rsnd_scu_platform_info *info; /* rcar_snd.h */
struct rsnd_mod mod;
+ struct clk *clk;
};
#define rsnd_scu_mode_flags(p) ((p)->info->flags)
+#define rsnd_scu_convert_rate(p) ((p)->info->convert_rate)
+
+#define RSND_SCU_NAME_SIZE 16
/*
* ADINR
@@ -26,6 +30,15 @@ struct rsnd_scu {
#define OTBL_18 (6 << 16)
#define OTBL_16 (8 << 16)
+/*
+ * image of SRC (Sampling Rate Converter)
+ *
+ * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+
+ * 48kHz <-> | SRC | <------> | SSI | <-----> | codec |
+ * 44.1kHz <-> +-----+ +-----+ +-------+
+ * ...
+ *
+ */
#define rsnd_mod_to_scu(_mod) \
container_of((_mod), struct rsnd_scu, mod)
@@ -36,7 +49,8 @@ struct rsnd_scu {
((pos) = (struct rsnd_scu *)(priv)->scu + i); \
i++)
-static int rsnd_scu_set_route(struct rsnd_priv *priv,
+/* Gen1 only */
+static int rsnd_src_set_route_if_gen1(struct rsnd_priv *priv,
struct rsnd_mod *mod,
struct rsnd_dai *rdai,
struct rsnd_dai_stream *io)
@@ -55,7 +69,7 @@ static int rsnd_scu_set_route(struct rsnd_priv *priv,
{ 0x3, 28, }, /* 7 */
{ 0x3, 30, }, /* 8 */
};
-
+ struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
u32 mask;
u32 val;
int shift;
@@ -85,9 +99,18 @@ static int rsnd_scu_set_route(struct rsnd_priv *priv,
*/
shift = (id % 4) * 8;
mask = 0x1F << shift;
- if (8 == id) /* SRU8 is very special */
+
+ /*
+ * ADG is used as source clock if SRC was used,
+ * then, SSI WS is used as destination clock.
+ * SSI WS is used as source clock if SRC is not used
+ * (when playback, source/destination become reverse when capture)
+ */
+ if (rsnd_scu_convert_rate(scu)) /* use ADG */
+ val = 0;
+ else if (8 == id) /* use SSI WS, but SRU8 is special */
val = id << shift;
- else
+ else /* use SSI WS */
val = (id + 1) << shift;
switch (id / 4) {
@@ -105,30 +128,45 @@ static int rsnd_scu_set_route(struct rsnd_priv *priv,
return 0;
}
-static int rsnd_scu_set_mode(struct rsnd_priv *priv,
- struct rsnd_mod *mod,
- struct rsnd_dai *rdai,
- struct rsnd_dai_stream *io)
+unsigned int rsnd_scu_get_ssi_rate(struct rsnd_priv *priv,
+ struct rsnd_mod *ssi_mod,
+ struct snd_pcm_runtime *runtime)
{
- int id = rsnd_mod_id(mod);
- u32 val;
+ struct rsnd_scu *scu;
+ unsigned int rate;
- if (rsnd_is_gen1(priv)) {
- val = (1 << id);
- rsnd_mod_bset(mod, SRC_CTRL, val, val);
- }
+ /* this function is assuming SSI id = SCU id here */
+ scu = rsnd_mod_to_scu(rsnd_scu_mod_get(priv, rsnd_mod_id(ssi_mod)));
- return 0;
+ /*
+ * return convert rate if SRC is used,
+ * otherwise, return runtime->rate as usual
+ */
+ rate = rsnd_scu_convert_rate(scu);
+ if (!rate)
+ rate = runtime->rate;
+
+ return rate;
}
-static int rsnd_scu_set_hpbif(struct rsnd_priv *priv,
+static int rsnd_scu_convert_rate_ctrl(struct rsnd_priv *priv,
struct rsnd_mod *mod,
struct rsnd_dai *rdai,
struct rsnd_dai_stream *io)
{
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
+ u32 convert_rate = rsnd_scu_convert_rate(scu);
u32 adinr = runtime->channels;
+ /* set/clear soft reset */
+ rsnd_mod_write(mod, SRC_SWRSR, 0);
+ rsnd_mod_write(mod, SRC_SWRSR, 1);
+
+ /* Initialize the operation of the SRC internal circuits */
+ rsnd_mod_write(mod, SRC_SRCIR, 1);
+
+ /* Set channel number and output bit length */
switch (runtime->sample_bits) {
case 16:
adinr |= OTBL_16;
@@ -139,9 +177,81 @@ static int rsnd_scu_set_hpbif(struct rsnd_priv *priv,
default:
return -EIO;
}
+ rsnd_mod_write(mod, SRC_ADINR, adinr);
+
+ if (convert_rate) {
+ u32 fsrate = 0x0400000 / convert_rate * runtime->rate;
+ int ret;
+
+ /* Enable the initial value of IFS */
+ rsnd_mod_write(mod, SRC_IFSCR, 1);
+
+ /* Set initial value of IFS */
+ rsnd_mod_write(mod, SRC_IFSVR, fsrate);
+
+ /* Select SRC mode (fixed value) */
+ rsnd_mod_write(mod, SRC_SRCCR, 0x00010110);
+
+ /* Set the restriction value of the FS ratio (98%) */
+ rsnd_mod_write(mod, SRC_MNFSR, fsrate / 100 * 98);
+
+ if (rsnd_is_gen1(priv)) {
+ /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
+ }
+ /* set convert clock */
+ ret = rsnd_adg_set_convert_clk(priv, mod,
+ runtime->rate,
+ convert_rate);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Cancel the initialization and operate the SRC function */
+ rsnd_mod_write(mod, SRC_SRCIR, 0);
+
+ /* use DMA transfer */
rsnd_mod_write(mod, BUSIF_MODE, 1);
- rsnd_mod_write(mod, BUSIF_ADINR, adinr);
+
+ return 0;
+}
+
+static int rsnd_scu_transfer_start(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
+ int id = rsnd_mod_id(mod);
+ u32 val;
+
+ if (rsnd_is_gen1(priv)) {
+ val = (1 << id);
+ rsnd_mod_bset(mod, SRC_ROUTE_CTRL, val, val);
+ }
+
+ if (rsnd_scu_convert_rate(scu))
+ rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+
+ return 0;
+}
+
+static int rsnd_scu_transfer_stop(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
+ int id = rsnd_mod_id(mod);
+ u32 mask;
+
+ if (rsnd_is_gen1(priv)) {
+ mask = (1 << id);
+ rsnd_mod_bset(mod, SRC_ROUTE_CTRL, mask, 0);
+ }
+
+ if (rsnd_scu_convert_rate(scu))
+ rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0);
return 0;
}
@@ -159,6 +269,7 @@ static int rsnd_scu_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
struct device *dev = rsnd_priv_to_dev(priv);
int ret;
@@ -173,16 +284,19 @@ static int rsnd_scu_start(struct rsnd_mod *mod,
return 0;
}
+ clk_enable(scu->clk);
+
/* it use DMA transter */
- ret = rsnd_scu_set_route(priv, mod, rdai, io);
+
+ ret = rsnd_src_set_route_if_gen1(priv, mod, rdai, io);
if (ret < 0)
return ret;
- ret = rsnd_scu_set_mode(priv, mod, rdai, io);
+ ret = rsnd_scu_convert_rate_ctrl(priv, mod, rdai, io);
if (ret < 0)
return ret;
- ret = rsnd_scu_set_hpbif(priv, mod, rdai, io);
+ ret = rsnd_scu_transfer_start(priv, mod, rdai, io);
if (ret < 0)
return ret;
@@ -191,9 +305,27 @@ static int rsnd_scu_start(struct rsnd_mod *mod,
return 0;
}
+static int rsnd_scu_stop(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
+
+ if (!rsnd_scu_hpbif_is_enable(mod))
+ return 0;
+
+ rsnd_scu_transfer_stop(priv, mod, rdai, io);
+
+ clk_disable(scu->clk);
+
+ return 0;
+}
+
static struct rsnd_mod_ops rsnd_scu_ops = {
.name = "scu",
.start = rsnd_scu_start,
+ .stop = rsnd_scu_stop,
};
struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id)
@@ -210,6 +342,8 @@ int rsnd_scu_probe(struct platform_device *pdev,
{
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_scu *scu;
+ struct clk *clk;
+ char name[RSND_SCU_NAME_SIZE];
int i, nr;
/*
@@ -226,9 +360,16 @@ int rsnd_scu_probe(struct platform_device *pdev,
priv->scu = scu;
for_each_rsnd_scu(scu, priv, i) {
+ snprintf(name, RSND_SCU_NAME_SIZE, "scu.%d", i);
+
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
rsnd_mod_init(priv, &scu->mod,
&rsnd_scu_ops, i);
scu->info = &info->scu_info[i];
+ scu->clk = clk;
dev_dbg(dev, "SCU%d probed\n", i);
}
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 5ac20cd5e006..4b8cf7ca9d19 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -187,9 +187,10 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod,
}
static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
- unsigned int rate)
+ struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct device *dev = rsnd_priv_to_dev(priv);
int i, j, ret;
int adg_clk_div_table[] = {
@@ -199,6 +200,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
1, 2, 4, 8, 16, 6, 12,
};
unsigned int main_rate;
+ unsigned int rate = rsnd_scu_get_ssi_rate(priv, &ssi->mod, runtime);
/*
* Find best clock, and try to start ADG
@@ -209,7 +211,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
/*
* this driver is assuming that
* system word is 64fs (= 2 x 32bit)
- * see rsnd_ssi_start()
+ * see rsnd_ssi_init()
*/
main_rate = rate / adg_clk_div_table[i]
* 32 * 2 * ssi_clk_mul_table[j];
@@ -251,14 +253,10 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
clk_enable(ssi->clk);
if (rsnd_rdai_is_clk_master(rdai)) {
- struct snd_pcm_runtime *runtime;
-
- runtime = rsnd_io_to_runtime(io);
-
if (rsnd_ssi_clk_from_parent(ssi))
rsnd_ssi_hw_start(ssi->parent, rdai, io);
else
- rsnd_ssi_master_clk_start(ssi, runtime->rate);
+ rsnd_ssi_master_clk_start(ssi, io);
}
}
@@ -457,6 +455,10 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
/* enable PIO IRQ */
ssi->cr_etc = UIEN | OIEN | DIEN;
+ /* enable PIO interrupt if gen2 */
+ if (rsnd_is_gen2(priv))
+ rsnd_mod_write(&ssi->mod, INT_ENABLE, 0x0f000000);
+
rsnd_ssi_hw_start(ssi, rdai, io);
dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
@@ -650,7 +652,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
snprintf(name, RSND_SSI_NAME_SIZE, "ssi.%d", i);
- clk = clk_get(dev, name);
+ clk = devm_clk_get(dev, name);
if (IS_ERR(clk))
return PTR_ERR(clk);
@@ -711,7 +713,6 @@ void rsnd_ssi_remove(struct platform_device *pdev,
int i;
for_each_rsnd_ssi(ssi, priv, i) {
- clk_put(ssi->clk);
if (rsnd_ssi_dma_available(ssi))
rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod));
}
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 4e53d87e881d..be88df5eeaf7 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -3212,11 +3212,11 @@ int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
break;
case 2:
((u16 *)(&ucontrol->value.bytes.data))[0]
- &= ~params->mask;
+ &= cpu_to_be16(~params->mask);
break;
case 4:
((u32 *)(&ucontrol->value.bytes.data))[0]
- &= ~params->mask;
+ &= cpu_to_be32(~params->mask);
break;
default:
return -EINVAL;
@@ -4617,10 +4617,14 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
if (id < 0 || id >= pos->num_dai) {
ret = -EINVAL;
- } else {
- *dai_name = pos->dai_drv[id].name;
- ret = 0;
+ break;
}
+
+ ret = 0;
+
+ *dai_name = pos->dai_drv[id].name;
+ if (!*dai_name)
+ *dai_name = pos->name;
}
break;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index dcade130157f..67e63ab1f11e 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -2868,6 +2868,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
unsigned int val;
int connect, change;
struct snd_soc_dapm_update update;
+ int ret = 0;
if (snd_soc_volsw_is_stereo(mc))
dev_warn(codec->dapm.dev,
@@ -2901,12 +2902,16 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
card->update = &update;
}
- soc_dapm_mixer_update_power(card, kcontrol, connect);
+ ret = soc_dapm_mixer_update_power(card, kcontrol, connect);
card->update = NULL;
}
mutex_unlock(&card->dapm_mutex);
+
+ if (ret > 0)
+ soc_dpcm_runtime_update(card);
+
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
@@ -2955,6 +2960,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
unsigned int val, mux, change;
unsigned int mask;
struct snd_soc_dapm_update update;
+ int ret = 0;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
@@ -2978,12 +2984,16 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
update.val = val;
card->update = &update;
- soc_dapm_mux_update_power(card, kcontrol, mux, e);
+ ret = soc_dapm_mux_update_power(card, kcontrol, mux, e);
card->update = NULL;
}
mutex_unlock(&card->dapm_mutex);
+
+ if (ret > 0)
+ soc_dpcm_runtime_update(card);
+
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
@@ -3019,6 +3029,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
struct soc_enum *e =
(struct soc_enum *)kcontrol->private_value;
int change;
+ int ret = 0;
if (ucontrol->value.enumerated.item[0] >= e->max)
return -EINVAL;
@@ -3028,9 +3039,13 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
value = ucontrol->value.enumerated.item[0];
change = dapm_kcontrol_set_value(kcontrol, value);
if (change)
- soc_dapm_mux_update_power(card, kcontrol, value, e);
+ ret = soc_dapm_mux_update_power(card, kcontrol, value, e);
mutex_unlock(&card->dapm_mutex);
+
+ if (ret > 0)
+ soc_dpcm_runtime_update(card);
+
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
@@ -3097,6 +3112,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
unsigned int val, mux, change;
unsigned int mask;
struct snd_soc_dapm_update update;
+ int ret = 0;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
@@ -3120,12 +3136,16 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
update.val = val;
card->update = &update;
- soc_dapm_mux_update_power(card, kcontrol, mux, e);
+ ret = soc_dapm_mux_update_power(card, kcontrol, mux, e);
card->update = NULL;
}
mutex_unlock(&card->dapm_mutex);
+
+ if (ret > 0)
+ soc_dpcm_runtime_update(card);
+
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
diff --git a/sound/soc/soc-devres.c b/sound/soc/soc-devres.c
index b1d732255c02..7ac745df1412 100644
--- a/sound/soc/soc-devres.c
+++ b/sound/soc/soc-devres.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
static void devm_component_release(struct device *dev, void *res)
{
@@ -66,7 +67,7 @@ static void devm_card_release(struct device *dev, void *res)
*/
int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card)
{
- struct device **ptr;
+ struct snd_soc_card **ptr;
int ret;
ptr = devres_alloc(devm_card_release, sizeof(*ptr), GFP_KERNEL);
@@ -75,7 +76,7 @@ int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card)
ret = snd_soc_register_card(card);
if (ret == 0) {
- *ptr = dev;
+ *ptr = card;
devres_add(dev, ptr);
} else {
devres_free(ptr);
@@ -84,3 +85,43 @@ int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card)
return ret;
}
EXPORT_SYMBOL_GPL(devm_snd_soc_register_card);
+
+#ifdef CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM
+
+static void devm_dmaengine_pcm_release(struct device *dev, void *res)
+{
+ snd_dmaengine_pcm_unregister(*(struct device **)res);
+}
+
+/**
+ * devm_snd_dmaengine_pcm_register - resource managed dmaengine PCM registration
+ * @dev: The parent device for the PCM device
+ * @config: Platform specific PCM configuration
+ * @flags: Platform specific quirks
+ *
+ * Register a dmaengine based PCM device with automatic unregistration when the
+ * device is unregistered.
+ */
+int devm_snd_dmaengine_pcm_register(struct device *dev,
+ const struct snd_dmaengine_pcm_config *config, unsigned int flags)
+{
+ struct device **ptr;
+ int ret;
+
+ ptr = devres_alloc(devm_dmaengine_pcm_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ ret = snd_dmaengine_pcm_register(dev, config, flags);
+ if (ret == 0) {
+ *ptr = dev;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(devm_snd_dmaengine_pcm_register);
+
+#endif
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index cbc9c96ce1f4..2a6c569d991f 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -137,6 +137,9 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
hw.buffer_bytes_max = SIZE_MAX;
hw.fifo_size = dma_data->fifo_size;
+ if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
+ hw.info |= SNDRV_PCM_INFO_BATCH;
+
ret = dma_get_slave_caps(chan, &dma_caps);
if (ret == 0) {
if (dma_caps.cmd_pause)
@@ -284,24 +287,67 @@ static const char * const dmaengine_pcm_dma_channel_names[] = {
[SNDRV_PCM_STREAM_CAPTURE] = "rx",
};
-static void dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
- struct device *dev)
+static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
+ struct device *dev, const struct snd_dmaengine_pcm_config *config)
{
unsigned int i;
+ const char *name;
+ struct dma_chan *chan;
if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT |
SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) ||
!dev->of_node)
- return;
+ return 0;
+
+ if (config && config->dma_dev) {
+ /*
+ * If this warning is seen, it probably means that your Linux
+ * device structure does not match your HW device structure.
+ * It would be best to refactor the Linux device structure to
+ * correctly match the HW structure.
+ */
+ dev_warn(dev, "DMA channels sourced from device %s",
+ dev_name(config->dma_dev));
+ dev = config->dma_dev;
+ }
- if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) {
- pcm->chan[0] = dma_request_slave_channel(dev, "rx-tx");
- pcm->chan[1] = pcm->chan[0];
- } else {
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
- pcm->chan[i] = dma_request_slave_channel(dev,
- dmaengine_pcm_dma_channel_names[i]);
+ for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE;
+ i++) {
+ if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
+ name = "rx-tx";
+ else
+ name = dmaengine_pcm_dma_channel_names[i];
+ if (config && config->chan_names[i])
+ name = config->chan_names[i];
+ chan = dma_request_slave_channel_reason(dev, name);
+ if (IS_ERR(chan)) {
+ if (PTR_ERR(chan) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ pcm->chan[i] = NULL;
+ } else {
+ pcm->chan[i] = chan;
}
+ if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
+ break;
+ }
+
+ if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
+ pcm->chan[1] = pcm->chan[0];
+
+ return 0;
+}
+
+static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm)
+{
+ unsigned int i;
+
+ for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE;
+ i++) {
+ if (!pcm->chan[i])
+ continue;
+ dma_release_channel(pcm->chan[i]);
+ if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
+ break;
}
}
@@ -315,6 +361,7 @@ int snd_dmaengine_pcm_register(struct device *dev,
const struct snd_dmaengine_pcm_config *config, unsigned int flags)
{
struct dmaengine_pcm *pcm;
+ int ret;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
if (!pcm)
@@ -323,14 +370,25 @@ int snd_dmaengine_pcm_register(struct device *dev,
pcm->config = config;
pcm->flags = flags;
- dmaengine_pcm_request_chan_of(pcm, dev);
+ ret = dmaengine_pcm_request_chan_of(pcm, dev, config);
+ if (ret)
+ goto err_free_dma;
if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
- return snd_soc_add_platform(dev, &pcm->platform,
+ ret = snd_soc_add_platform(dev, &pcm->platform,
&dmaengine_no_residue_pcm_platform);
else
- return snd_soc_add_platform(dev, &pcm->platform,
+ ret = snd_soc_add_platform(dev, &pcm->platform,
&dmaengine_pcm_platform);
+ if (ret)
+ goto err_free_dma;
+
+ return 0;
+
+err_free_dma:
+ dmaengine_pcm_release_chan(pcm);
+ kfree(pcm);
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register);
@@ -345,7 +403,6 @@ void snd_dmaengine_pcm_unregister(struct device *dev)
{
struct snd_soc_platform *platform;
struct dmaengine_pcm *pcm;
- unsigned int i;
platform = snd_soc_lookup_platform(dev);
if (!platform)
@@ -353,15 +410,8 @@ void snd_dmaengine_pcm_unregister(struct device *dev)
pcm = soc_platform_to_pcm(platform);
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
- if (pcm->chan[i]) {
- dma_release_channel(pcm->chan[i]);
- if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
- break;
- }
- }
-
snd_soc_remove_platform(platform);
+ dmaengine_pcm_release_chan(pcm);
kfree(pcm);
}
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister);
diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c
index 4f11d23f2062..aa886cca3ecf 100644
--- a/sound/soc/soc-io.c
+++ b/sound/soc/soc-io.c
@@ -99,14 +99,14 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
config.val_bits = data_bits;
switch (control) {
-#if defined(CONFIG_REGMAP_I2C) || defined(CONFIG_REGMAP_I2C_MODULE)
+#if IS_ENABLED(CONFIG_REGMAP_I2C)
case SND_SOC_I2C:
codec->control_data = regmap_init_i2c(to_i2c_client(codec->dev),
&config);
break;
#endif
-#if defined(CONFIG_REGMAP_SPI) || defined(CONFIG_REGMAP_SPI_MODULE)
+#if IS_ENABLED(CONFIG_REGMAP_SPI)
case SND_SOC_SPI:
codec->control_data = regmap_init_spi(to_spi_device(codec->dev),
&config);
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 42782c01e413..604e7e9a2ef8 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -84,35 +84,117 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int ret;
- if (!soc_dai->driver->symmetric_rates &&
- !rtd->dai_link->symmetric_rates)
- return 0;
+ if (soc_dai->rate && (soc_dai->driver->symmetric_rates ||
+ rtd->dai_link->symmetric_rates)) {
+ dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n",
+ soc_dai->rate);
+
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ soc_dai->rate, soc_dai->rate);
+ if (ret < 0) {
+ dev_err(soc_dai->dev,
+ "ASoC: Unable to apply rate constraint: %d\n",
+ ret);
+ return ret;
+ }
+ }
- /* This can happen if multiple streams are starting simultaneously -
- * the second can need to get its constraints before the first has
- * picked a rate. Complain and allow the application to carry on.
- */
- if (!soc_dai->rate) {
- dev_warn(soc_dai->dev,
- "ASoC: Not enforcing symmetric_rates due to race\n");
- return 0;
+ if (soc_dai->channels && (soc_dai->driver->symmetric_channels ||
+ rtd->dai_link->symmetric_channels)) {
+ dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n",
+ soc_dai->channels);
+
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ soc_dai->channels,
+ soc_dai->channels);
+ if (ret < 0) {
+ dev_err(soc_dai->dev,
+ "ASoC: Unable to apply channel symmetry constraint: %d\n",
+ ret);
+ return ret;
+ }
}
- dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", soc_dai->rate);
+ if (soc_dai->sample_bits && (soc_dai->driver->symmetric_samplebits ||
+ rtd->dai_link->symmetric_samplebits)) {
+ dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n",
+ soc_dai->sample_bits);
- ret = snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE,
- soc_dai->rate, soc_dai->rate);
- if (ret < 0) {
- dev_err(soc_dai->dev,
- "ASoC: Unable to apply rate symmetry constraint: %d\n",
- ret);
- return ret;
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ soc_dai->sample_bits,
+ soc_dai->sample_bits);
+ if (ret < 0) {
+ dev_err(soc_dai->dev,
+ "ASoC: Unable to apply sample bits symmetry constraint: %d\n",
+ ret);
+ return ret;
+ }
}
return 0;
}
+static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ unsigned int rate, channels, sample_bits, symmetry;
+
+ rate = params_rate(params);
+ channels = params_channels(params);
+ sample_bits = snd_pcm_format_physical_width(params_format(params));
+
+ /* reject unmatched parameters when applying symmetry */
+ symmetry = cpu_dai->driver->symmetric_rates ||
+ codec_dai->driver->symmetric_rates ||
+ rtd->dai_link->symmetric_rates;
+ if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) {
+ dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n",
+ cpu_dai->rate, rate);
+ return -EINVAL;
+ }
+
+ symmetry = cpu_dai->driver->symmetric_channels ||
+ codec_dai->driver->symmetric_channels ||
+ rtd->dai_link->symmetric_channels;
+ if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) {
+ dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n",
+ cpu_dai->channels, channels);
+ return -EINVAL;
+ }
+
+ symmetry = cpu_dai->driver->symmetric_samplebits ||
+ codec_dai->driver->symmetric_samplebits ||
+ rtd->dai_link->symmetric_samplebits;
+ if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) {
+ dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n",
+ cpu_dai->sample_bits, sample_bits);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver;
+ struct snd_soc_dai_driver *codec_driver = rtd->codec_dai->driver;
+ struct snd_soc_dai_link *link = rtd->dai_link;
+
+ return cpu_driver->symmetric_rates || codec_driver->symmetric_rates ||
+ link->symmetric_rates || cpu_driver->symmetric_channels ||
+ codec_driver->symmetric_channels || link->symmetric_channels ||
+ cpu_driver->symmetric_samplebits ||
+ codec_driver->symmetric_samplebits ||
+ link->symmetric_samplebits;
+}
+
/*
* List of sample sizes that might go over the bus for parameter
* application. There ought to be a wildcard sample size for things
@@ -148,12 +230,12 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream,
}
}
-static void soc_pcm_init_runtime_hw(struct snd_pcm_hardware *hw,
+static void soc_pcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
struct snd_soc_pcm_stream *codec_stream,
struct snd_soc_pcm_stream *cpu_stream)
{
- hw->rate_min = max(codec_stream->rate_min, cpu_stream->rate_min);
- hw->rate_max = max(codec_stream->rate_max, cpu_stream->rate_max);
+ struct snd_pcm_hardware *hw = &runtime->hw;
+
hw->channels_min = max(codec_stream->channels_min,
cpu_stream->channels_min);
hw->channels_max = min(codec_stream->channels_max,
@@ -166,6 +248,13 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_hardware *hw,
if (cpu_stream->rates
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
hw->rates |= codec_stream->rates;
+
+ snd_pcm_limit_hw_rates(runtime);
+
+ hw->rate_min = max(hw->rate_min, cpu_stream->rate_min);
+ hw->rate_min = max(hw->rate_min, codec_stream->rate_min);
+ hw->rate_max = min_not_zero(hw->rate_max, cpu_stream->rate_max);
+ hw->rate_max = min_not_zero(hw->rate_max, codec_stream->rate_max);
}
/*
@@ -235,15 +324,17 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
/* Check that the codec and cpu DAIs are compatible */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->playback,
+ soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->playback,
&cpu_dai_drv->playback);
} else {
- soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->capture,
+ soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->capture,
&cpu_dai_drv->capture);
}
+ if (soc_pcm_has_symmetry(substream))
+ runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+
ret = -EINVAL;
- snd_pcm_limit_hw_rates(runtime);
if (!runtime->hw.rates) {
printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n",
codec_dai->name, cpu_dai->name);
@@ -390,11 +481,6 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
if (!codec_dai->active)
codec_dai->rate = 0;
- /* Muting the DAC suppresses artifacts caused during digital
- * shutdown, for example from stopping clocks.
- */
- snd_soc_dai_digital_mute(codec_dai, 1, substream->stream);
-
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
@@ -525,6 +611,10 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+ ret = soc_pcm_params_symmetry(substream, params);
+ if (ret)
+ goto out;
+
if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
ret = rtd->dai_link->ops->hw_params(substream, params);
if (ret < 0) {
@@ -561,9 +651,16 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
}
}
- /* store the rate for each DAIs */
+ /* store the parameters for each DAIs */
cpu_dai->rate = params_rate(params);
+ cpu_dai->channels = params_channels(params);
+ cpu_dai->sample_bits =
+ snd_pcm_format_physical_width(params_format(params));
+
codec_dai->rate = params_rate(params);
+ codec_dai->channels = params_channels(params);
+ codec_dai->sample_bits =
+ snd_pcm_format_physical_width(params_format(params));
out:
mutex_unlock(&rtd->pcm_mutex);
@@ -594,12 +691,26 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_codec *codec = rtd->codec;
+ bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+ /* clear the corresponding DAIs parameters when going to be inactive */
+ if (cpu_dai->active == 1) {
+ cpu_dai->rate = 0;
+ cpu_dai->channels = 0;
+ cpu_dai->sample_bits = 0;
+ }
+
+ if (codec_dai->active == 1) {
+ codec_dai->rate = 0;
+ codec_dai->channels = 0;
+ codec_dai->sample_bits = 0;
+ }
+
/* apply codec digital mute */
- if (!codec->active)
+ if ((playback && codec_dai->playback_active == 1) ||
+ (!playback && codec_dai->capture_active == 1))
snd_soc_dai_digital_mute(codec_dai, 1, substream->stream);
/* free any machine hw params */
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 5e633659c1b3..6ebdfd9a1a1d 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -59,10 +59,6 @@ int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params)
EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk);
static const struct snd_pcm_hardware dummy_dma_hardware = {
- .formats = 0xffffffff,
- .channels_min = 1,
- .channels_max = UINT_MAX,
-
/* Random values to keep userspace happy when checking constraints */
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER,
diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c
index 21a8c954af1c..4ab442a63d7e 100644
--- a/sound/soc/spear/spdif_in.c
+++ b/sound/soc/spear/spdif_in.c
@@ -18,12 +18,14 @@
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <sound/dmaengine_pcm.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/spear_dma.h>
#include <sound/spear_spdif.h>
#include "spdif_in_regs.h"
+#include "spear_pcm.h"
struct spdif_in_params {
u32 format;
@@ -37,6 +39,8 @@ struct spdif_in_dev {
struct device *dev;
void (*reset_perip)(void);
int irq;
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+ struct snd_dmaengine_pcm_config config;
};
static void spdif_in_configure(struct spdif_in_dev *host)
@@ -53,7 +57,8 @@ static int spdif_in_dai_probe(struct snd_soc_dai *dai)
{
struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai);
- dai->capture_dma_data = &host->dma_params;
+ host->dma_params_rx.filter_data = &host->dma_params;
+ dai->capture_dma_data = &host->dma_params_rx;
return 0;
}
@@ -244,7 +249,6 @@ static int spdif_in_probe(struct platform_device *pdev)
host->dma_params.addr = res_fifo->start;
host->dma_params.max_burst = 16;
host->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- host->dma_params.filter = pdata->filter;
host->reset_perip = pdata->reset_perip;
host->dev = &pdev->dev;
@@ -257,8 +261,13 @@ static int spdif_in_probe(struct platform_device *pdev)
return ret;
}
- return devm_snd_soc_register_component(&pdev->dev, &spdif_in_component,
- &spdif_in_dai, 1);
+ ret = devm_snd_soc_register_component(&pdev->dev, &spdif_in_component,
+ &spdif_in_dai, 1);
+ if (ret)
+ return ret;
+
+ return devm_spear_pcm_platform_register(&pdev->dev, &host->config,
+ pdata->filter);
}
static struct platform_driver spdif_in_driver = {
diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c
index b6ef6f78dc78..fe99f461aff0 100644
--- a/sound/soc/spear/spdif_out.c
+++ b/sound/soc/spear/spdif_out.c
@@ -18,10 +18,12 @@
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <sound/dmaengine_pcm.h>
#include <sound/soc.h>
#include <sound/spear_dma.h>
#include <sound/spear_spdif.h>
#include "spdif_out_regs.h"
+#include "spear_pcm.h"
struct spdif_out_params {
u32 rate;
@@ -35,6 +37,8 @@ struct spdif_out_dev {
struct spdif_out_params saved_params;
u32 running;
void __iomem *io_base;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
+ struct snd_dmaengine_pcm_config config;
};
static void spdif_out_configure(struct spdif_out_dev *host)
@@ -244,7 +248,8 @@ static int spdif_soc_dai_probe(struct snd_soc_dai *dai)
{
struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai);
- dai->playback_dma_data = &host->dma_params;
+ host->dma_params_tx.filter_data = &host->dma_params;
+ dai->playback_dma_data = &host->dma_params_tx;
return snd_soc_add_dai_controls(dai, spdif_out_controls,
ARRAY_SIZE(spdif_out_controls));
@@ -280,6 +285,7 @@ static int spdif_out_probe(struct platform_device *pdev)
struct spdif_out_dev *host;
struct spear_spdif_platform_data *pdata;
struct resource *res;
+ int ret;
host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
if (!host) {
@@ -302,12 +308,16 @@ static int spdif_out_probe(struct platform_device *pdev)
host->dma_params.addr = res->start + SPDIF_OUT_FIFO_DATA;
host->dma_params.max_burst = 16;
host->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- host->dma_params.filter = pdata->filter;
dev_set_drvdata(&pdev->dev, host);
- return devm_snd_soc_register_component(&pdev->dev, &spdif_out_component,
- &spdif_out_dai, 1);
+ ret = devm_snd_soc_register_component(&pdev->dev, &spdif_out_component,
+ &spdif_out_dai, 1);
+ if (ret)
+ return ret;
+
+ return devm_spear_pcm_platform_register(&pdev->dev, &host->config,
+ pdata->filter);
}
#ifdef CONFIG_PM
diff --git a/sound/soc/spear/spear_pcm.c b/sound/soc/spear/spear_pcm.c
index 4707f2b862c3..0e5a8f35d0ad 100644
--- a/sound/soc/spear/spear_pcm.c
+++ b/sound/soc/spear/spear_pcm.c
@@ -18,6 +18,7 @@
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/spear_dma.h>
+#include "spear_pcm.h"
static const struct snd_pcm_hardware spear_pcm_hardware = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -31,49 +32,24 @@ static const struct snd_pcm_hardware spear_pcm_hardware = {
.fifo_size = 0, /* fifo size in bytes */
};
-static struct dma_chan *spear_pcm_request_chan(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_substream *substream)
-{
- struct spear_dma_data *dma_data;
-
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- return snd_dmaengine_pcm_request_channel(dma_data->filter, dma_data);
-}
-
static const struct snd_dmaengine_pcm_config spear_dmaengine_pcm_config = {
.pcm_hardware = &spear_pcm_hardware,
- .compat_request_channel = spear_pcm_request_chan,
.prealloc_buffer_size = 16 * 1024,
};
-static int spear_soc_platform_probe(struct platform_device *pdev)
+int devm_spear_pcm_platform_register(struct device *dev,
+ struct snd_dmaengine_pcm_config *config,
+ bool (*filter)(struct dma_chan *chan, void *slave))
{
- return snd_dmaengine_pcm_register(&pdev->dev,
- &spear_dmaengine_pcm_config,
+ *config = spear_dmaengine_pcm_config;
+ config->compat_filter_fn = filter;
+
+ return snd_dmaengine_pcm_register(dev, config,
SND_DMAENGINE_PCM_FLAG_NO_DT |
SND_DMAENGINE_PCM_FLAG_COMPAT);
}
-
-static int spear_soc_platform_remove(struct platform_device *pdev)
-{
- snd_dmaengine_pcm_unregister(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver spear_pcm_driver = {
- .driver = {
- .name = "spear-pcm-audio",
- .owner = THIS_MODULE,
- },
-
- .probe = spear_soc_platform_probe,
- .remove = spear_soc_platform_remove,
-};
-
-module_platform_driver(spear_pcm_driver);
+EXPORT_SYMBOL_GPL(devm_spear_pcm_platform_register);
MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>");
MODULE_DESCRIPTION("SPEAr PCM DMA module");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:spear-pcm-audio");
diff --git a/sound/soc/spear/spear_pcm.h b/sound/soc/spear/spear_pcm.h
new file mode 100644
index 000000000000..9b0ca62d6f02
--- /dev/null
+++ b/sound/soc/spear/spear_pcm.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef __SPEAR_PCM_H__
+#define __SPEAR_PCM_H__
+
+int devm_spear_pcm_platform_register(struct device *dev,
+ struct snd_dmaengine_pcm_config *config,
+ bool (*filter)(struct dma_chan *chan, void *slave));
+
+#endif
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 8fc653ca3ab4..65a85f542521 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -116,3 +116,13 @@ config SND_SOC_TEGRA_ALC5632
help
Say Y or M here if you want to add support for SoC audio on the
Toshiba AC100 netbook.
+
+config SND_SOC_TEGRA_MAX98090
+ tristate "SoC Audio support for Tegra boards using a MAX98090 codec"
+ depends on SND_SOC_TEGRA && I2C && GPIOLIB
+ select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
+ select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
+ select SND_SOC_MAX98090
+ help
+ Say Y or M here if you want to add support for SoC audio on Tegra
+ boards using the MAX98090 codec, such as Venice2.
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index 21d2550a08a4..5ae588cd96c4 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -24,6 +24,7 @@ snd-soc-tegra-wm8903-objs := tegra_wm8903.o
snd-soc-tegra-wm9712-objs := tegra_wm9712.o
snd-soc-tegra-trimslice-objs := trimslice.o
snd-soc-tegra-alc5632-objs := tegra_alc5632.o
+snd-soc-tegra-max98090-objs := tegra_max98090.o
obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o
obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o
@@ -31,3 +32,4 @@ obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o
obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o
+obj-$(CONFIG_SND_SOC_TEGRA_MAX98090) += snd-soc-tegra-max98090.o
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index ae27bcd586d2..088518d7694a 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -404,7 +404,7 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
ret = snd_soc_set_ac97_ops(&tegra20_ac97_ops);
if (ret) {
dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
- goto err_asoc_utils_fini;
+ goto err_clk_disable_unprepare;
}
ret = snd_soc_register_component(&pdev->dev, &tegra20_ac97_component,
@@ -412,7 +412,7 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
ret = -ENOMEM;
- goto err_asoc_utils_fini;
+ goto err_clk_disable_unprepare;
}
ret = tegra_pcm_platform_register(&pdev->dev);
@@ -428,6 +428,8 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
err_unregister_component:
snd_soc_unregister_component(&pdev->dev);
+err_clk_disable_unprepare:
+ clk_disable_unprepare(ac97->clk_ac97);
err_asoc_utils_fini:
tegra_asoc_utils_fini(&ac97->util_data);
err_clk_put:
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c
index 364bf6a907e1..8c819f811470 100644
--- a/sound/soc/tegra/tegra20_i2s.c
+++ b/sound/soc/tegra/tegra20_i2s.c
@@ -74,7 +74,7 @@ static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- unsigned int mask, val;
+ unsigned int mask = 0, val = 0;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
@@ -83,10 +83,10 @@ static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
- mask = TEGRA20_I2S_CTRL_MASTER_ENABLE;
+ mask |= TEGRA20_I2S_CTRL_MASTER_ENABLE;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
- val = TEGRA20_I2S_CTRL_MASTER_ENABLE;
+ val |= TEGRA20_I2S_CTRL_MASTER_ENABLE;
break;
case SND_SOC_DAIFMT_CBM_CFM:
break;
diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c
index 08bc6931c7c7..8c7c1028e579 100644
--- a/sound/soc/tegra/tegra20_spdif.c
+++ b/sound/soc/tegra/tegra20_spdif.c
@@ -67,15 +67,15 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
{
struct device *dev = dai->dev;
struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
- unsigned int mask, val;
+ unsigned int mask = 0, val = 0;
int ret, spdifclock;
- mask = TEGRA20_SPDIF_CTRL_PACK |
- TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
+ mask |= TEGRA20_SPDIF_CTRL_PACK |
+ TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
- val = TEGRA20_SPDIF_CTRL_PACK |
- TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;
+ val |= TEGRA20_SPDIF_CTRL_PACK |
+ TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;
break;
default:
return -EINVAL;
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index 231a785b3921..02247fee1cf7 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -118,7 +118,7 @@ static int tegra30_i2s_set_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- unsigned int mask, val;
+ unsigned int mask = 0, val = 0;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
@@ -127,10 +127,10 @@ static int tegra30_i2s_set_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
- mask = TEGRA30_I2S_CTRL_MASTER_ENABLE;
+ mask |= TEGRA30_I2S_CTRL_MASTER_ENABLE;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
- val = TEGRA30_I2S_CTRL_MASTER_ENABLE;
+ val |= TEGRA30_I2S_CTRL_MASTER_ENABLE;
break;
case SND_SOC_DAIFMT_CBM_CFM:
break;
diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c
new file mode 100644
index 000000000000..0283cfb7c031
--- /dev/null
+++ b/sound/soc/tegra/tegra_max98090.c
@@ -0,0 +1,275 @@
+/*
+ * Tegra machine ASoC driver for boards using a MAX90809 CODEC.
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (C) 2010-2012 - NVIDIA, Inc.
+ * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
+ * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-snd-max98090"
+
+struct tegra_max98090 {
+ struct tegra_asoc_utils_data util_data;
+ int gpio_hp_det;
+};
+
+static int tegra_max98090_asoc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card);
+ int srate, mclk;
+ int err;
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 8000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ case 64000:
+ case 96000:
+ mclk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ mclk = 11289600;
+ break;
+ default:
+ mclk = 12000000;
+ break;
+ }
+
+ err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
+ if (err < 0) {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops tegra_max98090_ops = {
+ .hw_params = tegra_max98090_asoc_hw_params,
+};
+
+static struct snd_soc_jack tegra_max98090_hp_jack;
+
+static struct snd_soc_jack_pin tegra_max98090_hp_jack_pins[] = {
+ {
+ .pin = "Headphones",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static struct snd_soc_jack_gpio tegra_max98090_hp_jack_gpio = {
+ .name = "Headphone detection",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 150,
+ .invert = 1,
+};
+
+static const struct snd_soc_dapm_widget tegra_max98090_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_kcontrol_new tegra_max98090_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+};
+
+static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct tegra_max98090 *machine = snd_soc_card_get_drvdata(codec->card);
+
+ if (gpio_is_valid(machine->gpio_hp_det)) {
+ snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE,
+ &tegra_max98090_hp_jack);
+ snd_soc_jack_add_pins(&tegra_max98090_hp_jack,
+ ARRAY_SIZE(tegra_max98090_hp_jack_pins),
+ tegra_max98090_hp_jack_pins);
+
+ tegra_max98090_hp_jack_gpio.gpio = machine->gpio_hp_det;
+ snd_soc_jack_add_gpios(&tegra_max98090_hp_jack,
+ 1,
+ &tegra_max98090_hp_jack_gpio);
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_link tegra_max98090_dai = {
+ .name = "max98090",
+ .stream_name = "max98090 PCM",
+ .codec_dai_name = "HiFi",
+ .init = tegra_max98090_asoc_init,
+ .ops = &tegra_max98090_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+};
+
+static struct snd_soc_card snd_soc_tegra_max98090 = {
+ .name = "tegra-max98090",
+ .owner = THIS_MODULE,
+ .dai_link = &tegra_max98090_dai,
+ .num_links = 1,
+ .controls = tegra_max98090_controls,
+ .num_controls = ARRAY_SIZE(tegra_max98090_controls),
+ .dapm_widgets = tegra_max98090_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra_max98090_dapm_widgets),
+ .fully_routed = true,
+};
+
+static int tegra_max98090_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct snd_soc_card *card = &snd_soc_tegra_max98090;
+ struct tegra_max98090 *machine;
+ int ret;
+
+ machine = devm_kzalloc(&pdev->dev,
+ sizeof(struct tegra_max98090), GFP_KERNEL);
+ if (!machine) {
+ dev_err(&pdev->dev, "Can't allocate tegra_max98090\n");
+ return -ENOMEM;
+ }
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+ machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
+ if (machine->gpio_hp_det == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ ret = snd_soc_of_parse_card_name(card, "nvidia,model");
+ if (ret)
+ goto err;
+
+ ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
+ if (ret)
+ goto err;
+
+ tegra_max98090_dai.codec_of_node = of_parse_phandle(np,
+ "nvidia,audio-codec", 0);
+ if (!tegra_max98090_dai.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tegra_max98090_dai.cpu_of_node = of_parse_phandle(np,
+ "nvidia,i2s-controller", 0);
+ if (!tegra_max98090_dai.cpu_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,i2s-controller' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tegra_max98090_dai.platform_of_node = tegra_max98090_dai.cpu_of_node;
+
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ if (ret)
+ goto err;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err_fini_utils;
+ }
+
+ return 0;
+
+err_fini_utils:
+ tegra_asoc_utils_fini(&machine->util_data);
+err:
+ return ret;
+}
+
+static int tegra_max98090_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card);
+
+ snd_soc_jack_free_gpios(&tegra_max98090_hp_jack, 1,
+ &tegra_max98090_hp_jack_gpio);
+
+ snd_soc_unregister_card(card);
+
+ tegra_asoc_utils_fini(&machine->util_data);
+
+ return 0;
+}
+
+static const struct of_device_id tegra_max98090_of_match[] = {
+ { .compatible = "nvidia,tegra-audio-max98090", },
+ {},
+};
+
+static struct platform_driver tegra_max98090_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = tegra_max98090_of_match,
+ },
+ .probe = tegra_max98090_probe,
+ .remove = tegra_max98090_remove,
+};
+module_platform_driver(tegra_max98090_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra max98090 machine ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra_max98090_of_match);
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index 7b2d23ba69b3..c09ffd18791b 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -42,9 +42,6 @@ static const struct snd_pcm_hardware tegra_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .channels_min = 2,
- .channels_max = 2,
.period_bytes_min = 1024,
.period_bytes_max = PAGE_SIZE,
.periods_min = 2,
diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c
index 5e119630b0e0..45b57892b6a5 100644
--- a/sound/soc/tegra/tegra_wm9712.c
+++ b/sound/soc/tegra/tegra_wm9712.c
@@ -55,7 +55,6 @@ static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd)
static struct snd_soc_dai_link tegra_wm9712_dai = {
.name = "AC97 HiFi",
.stream_name = "AC97 HiFi",
- .cpu_dai_name = "tegra20-ac97",
.codec_dai_name = "wm9712-hifi",
.codec_name = "wm9712-codec",
.init = tegra_wm9712_init,
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c
index fbd077f4de72..f0829de28708 100644
--- a/sound/soc/txx9/txx9aclc.c
+++ b/sound/soc/txx9/txx9aclc.c
@@ -40,11 +40,6 @@ static const struct snd_pcm_hardware txx9aclc_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_PAUSE,
-#ifdef __BIG_ENDIAN
- .formats = SNDRV_PCM_FMTBIT_S16_BE,
-#else
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
-#endif
.period_bytes_min = 1024,
.period_bytes_max = 8 * 1024,
.periods_min = 2,