diff options
Diffstat (limited to 'sound/soc')
306 files changed, 25187 insertions, 8088 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 5138b8493051..0060b31cc3f3 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,12 +44,13 @@ 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" source "sound/soc/s6000/Kconfig" source "sound/soc/sh/Kconfig" +source "sound/soc/sirf/Kconfig" source "sound/soc/spear/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 8b9e70105dd2..5f1df02984f8 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/ @@ -25,6 +27,7 @@ obj-$(CONFIG_SND_SOC) += pxa/ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += s6000/ obj-$(CONFIG_SND_SOC) += sh/ +obj-$(CONFIG_SND_SOC) += sirf/ obj-$(CONFIG_SND_SOC) += spear/ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ 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..6058c1fd5070 --- /dev/null +++ b/sound/soc/adi/axi-i2s.c @@ -0,0 +1,276 @@ +/* + * 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, 0); + 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..198e3a4640f6 --- /dev/null +++ b/sound/soc/adi/axi-spdif.c @@ -0,0 +1,271 @@ +/* + * 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, 0); + 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/Kconfig b/sound/soc/atmel/Kconfig index e48d38a1b95c..4789619a52d8 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -25,7 +25,7 @@ config SND_ATMEL_SOC_SSC config SND_AT91_SOC_SAM9G20_WM8731 tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" - depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS + depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC select SND_ATMEL_SOC_PDC select SND_ATMEL_SOC_SSC select SND_SOC_WM8731 @@ -58,6 +58,6 @@ config SND_AT91_SOC_AFEB9260 depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC select SND_ATMEL_SOC_PDC select SND_ATMEL_SOC_SSC - select SND_SOC_TLV320AIC23 + select SND_SOC_TLV320AIC23_I2C help Say Y here to support sound on AFEB9260 board. diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c index 06082e5e5dcb..b79a2a864513 100644 --- a/sound/soc/atmel/atmel-pcm-dma.c +++ b/sound/soc/atmel/atmel-pcm-dma.c @@ -50,7 +50,6 @@ static const struct snd_pcm_hardware atmel_pcm_dma_hardware = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_PAUSE, - .formats = SNDRV_PCM_FMTBIT_S16_LE, .period_bytes_min = 256, /* lighting DMA overhead */ .period_bytes_max = 2 * 0xffff, /* if 2 bytes format */ .periods_min = 8, diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c index 054ea4d9326a..33ec592ecd75 100644 --- a/sound/soc/atmel/atmel-pcm-pdc.c +++ b/sound/soc/atmel/atmel-pcm-pdc.c @@ -58,7 +58,6 @@ static const struct snd_pcm_hardware atmel_pcm_hardware = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE, - .formats = SNDRV_PCM_FMTBIT_S16_LE, .period_bytes_min = 32, .period_bytes_max = 8192, .periods_min = 2, diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 8697cedccd21..de433cfd044c 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -341,6 +341,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, { int id = dai->id; struct atmel_ssc_info *ssc_p = &ssc_info[id]; + struct ssc_device *ssc = ssc_p->ssc; struct atmel_pcm_dma_params *dma_params; int dir, channels, bits; u32 tfmr, rfmr, tcmr, rcmr; @@ -466,7 +467,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(RCMR_START, start_event) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) - | SSC_BF(RCMR_CKS, SSC_CKS_CLOCK); + | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? + SSC_CKS_PIN : SSC_CKS_CLOCK); rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) @@ -481,7 +483,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(TCMR_START, start_event) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_NONE) - | SSC_BF(TCMR_CKS, SSC_CKS_PIN); + | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ? + SSC_CKS_CLOCK : SSC_CKS_PIN); tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(TFMR_FSDEN, 0) @@ -550,7 +553,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(RCMR_START, SSC_START_RISING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) - | SSC_BF(RCMR_CKS, SSC_CKS_PIN); + | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? + SSC_CKS_PIN : SSC_CKS_CLOCK); rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) @@ -565,7 +569,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(TCMR_START, SSC_START_RISING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_NONE) - | SSC_BF(TCMR_CKS, SSC_CKS_PIN); + | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? + SSC_CKS_CLOCK : SSC_CKS_PIN); tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(TFMR_FSDEN, 0) @@ -648,7 +653,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 +662,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 +763,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/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index f15bff1548f8..174bd546c08b 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -155,25 +155,14 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) return ret; } - /* Add specific widgets */ - snd_soc_dapm_new_controls(dapm, at91sam9g20ek_dapm_widgets, - ARRAY_SIZE(at91sam9g20ek_dapm_widgets)); - /* Set up specific audio path interconnects */ - snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); - /* not connected */ snd_soc_dapm_nc_pin(dapm, "RLINEIN"); snd_soc_dapm_nc_pin(dapm, "LLINEIN"); -#ifdef ENABLE_MIC_INPUT - snd_soc_dapm_enable_pin(dapm, "Int Mic"); -#else - snd_soc_dapm_nc_pin(dapm, "Int Mic"); +#ifndef ENABLE_MIC_INPUT + snd_soc_dapm_nc_pin(&rtd->card->dapm, "Int Mic"); #endif - /* always connected */ - snd_soc_dapm_enable_pin(dapm, "Ext Spk"); - return 0; } @@ -194,6 +183,11 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = { .dai_link = &at91sam9g20ek_dai, .num_links = 1, .set_bias_level = at91sam9g20ek_set_bias_level, + + .dapm_widgets = at91sam9g20ek_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(at91sam9g20ek_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), }; static int at91sam9g20ek_audio_probe(struct platform_device *pdev) 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..6a834e109f1d --- /dev/null +++ b/sound/soc/bcm/Kconfig @@ -0,0 +1,9 @@ +config SND_BCM2835_SOC_I2S + tristate "SoC Audio support for the Broadcom BCM2835 I2S module" + depends on ARCH_BCM2835 || COMPILE_TEST + 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/Kconfig b/sound/soc/blackfin/Kconfig index 54f74f8cbb75..6347d5910138 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -11,20 +11,20 @@ config SND_BF5XX_I2S config SND_BF5XX_SOC_SSM2602 tristate "SoC SSM2602 Audio Codec Add-On Card support" - depends on SND_BF5XX_I2S && (SPI_MASTER || I2C) + depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI select SND_BF5XX_SOC_I2S if !BF60x select SND_BF6XX_SOC_I2S if BF60x - select SND_SOC_SSM2602 + select SND_SOC_SSM2602_SPI if SPI_MASTER + select SND_SOC_SSM2602_I2C if I2C help Say Y if you want to add support for the Analog Devices SSM2602 Audio Codec Add-On Card. config SND_SOC_BFIN_EVAL_ADAU1701 tristate "Support for the EVAL-ADAU1701MINIZ board on Blackfin eval boards" - depends on SND_BF5XX_I2S + depends on SND_BF5XX_I2S && I2C select SND_BF5XX_SOC_I2S select SND_SOC_ADAU1701 - select I2C help Say Y if you want to add support for the Analog Devices EVAL-ADAU1701MINIZ board connected to one of the Blackfin evaluation boards like the @@ -45,9 +45,10 @@ config SND_SOC_BFIN_EVAL_ADAU1373 config SND_SOC_BFIN_EVAL_ADAV80X tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards" - depends on SND_BF5XX_I2S && (SPI_MASTER || I2C) + depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI select SND_BF5XX_SOC_I2S - select SND_SOC_ADAV80X + select SND_SOC_ADAV801 if SPI_MASTER + select SND_SOC_ADAV803 if I2C help Say Y if you want to add support for the Analog Devices EVAL-ADAV801 or EVAL-ADAV803 board connected to one of the Blackfin evaluation boards @@ -58,7 +59,7 @@ config SND_SOC_BFIN_EVAL_ADAV80X config SND_BF5XX_SOC_AD1836 tristate "SoC AD1836 Audio support for BF5xx" - depends on SND_BF5XX_I2S + depends on SND_BF5XX_I2S && SPI_MASTER select SND_BF5XX_SOC_I2S select SND_SOC_AD1836 help @@ -66,9 +67,10 @@ config SND_BF5XX_SOC_AD1836 config SND_BF5XX_SOC_AD193X tristate "SoC AD193X Audio support for Blackfin" - depends on SND_BF5XX_I2S + depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI select SND_BF5XX_SOC_I2S - select SND_SOC_AD193X + select SND_SOC_AD193X_I2C if I2C + select SND_SOC_AD193X_SPI if SPI_MASTER help Say Y if you want to add support for AD193X codec on Blackfin. This driver supports AD1936, AD1937, AD1938 and AD1939. 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/Kconfig b/sound/soc/cirrus/Kconfig index 06f938deda15..5477c5475923 100644 --- a/sound/soc/cirrus/Kconfig +++ b/sound/soc/cirrus/Kconfig @@ -1,6 +1,6 @@ config SND_EP93XX_SOC tristate "SoC Audio support for the Cirrus Logic EP93xx series" - depends on (ARCH_EP93XX || COMPILE_TEST) && SND_SOC + depends on ARCH_EP93XX || COMPILE_TEST select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to @@ -18,7 +18,7 @@ config SND_EP93XX_SOC_SNAPPERCL15 tristate "SoC Audio support for Bluewater Systems Snapper CL15 module" depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15 select SND_EP93XX_SOC_I2S - select SND_SOC_TLV320AIC23 + select SND_SOC_TLV320AIC23_I2C help Say Y or M here if you want to add support for I2S audio on the Bluewater Systems Snapper CL15 module. 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/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index 75d0ad5d2dcb..8703244ee9fb 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -448,38 +448,38 @@ static const char *pm860x_opamp_texts[] = {"-50%", "-25%", "0%", "75%"}; static const char *pm860x_pa_texts[] = {"-33%", "0%", "33%", "66%"}; -static const struct soc_enum pm860x_hs1_opamp_enum = - SOC_ENUM_SINGLE(PM860X_HS1_CTRL, 5, 4, pm860x_opamp_texts); +static SOC_ENUM_SINGLE_DECL(pm860x_hs1_opamp_enum, + PM860X_HS1_CTRL, 5, pm860x_opamp_texts); -static const struct soc_enum pm860x_hs2_opamp_enum = - SOC_ENUM_SINGLE(PM860X_HS2_CTRL, 5, 4, pm860x_opamp_texts); +static SOC_ENUM_SINGLE_DECL(pm860x_hs2_opamp_enum, + PM860X_HS2_CTRL, 5, pm860x_opamp_texts); -static const struct soc_enum pm860x_hs1_pa_enum = - SOC_ENUM_SINGLE(PM860X_HS1_CTRL, 3, 4, pm860x_pa_texts); +static SOC_ENUM_SINGLE_DECL(pm860x_hs1_pa_enum, + PM860X_HS1_CTRL, 3, pm860x_pa_texts); -static const struct soc_enum pm860x_hs2_pa_enum = - SOC_ENUM_SINGLE(PM860X_HS2_CTRL, 3, 4, pm860x_pa_texts); +static SOC_ENUM_SINGLE_DECL(pm860x_hs2_pa_enum, + PM860X_HS2_CTRL, 3, pm860x_pa_texts); -static const struct soc_enum pm860x_lo1_opamp_enum = - SOC_ENUM_SINGLE(PM860X_LO1_CTRL, 5, 4, pm860x_opamp_texts); +static SOC_ENUM_SINGLE_DECL(pm860x_lo1_opamp_enum, + PM860X_LO1_CTRL, 5, pm860x_opamp_texts); -static const struct soc_enum pm860x_lo2_opamp_enum = - SOC_ENUM_SINGLE(PM860X_LO2_CTRL, 5, 4, pm860x_opamp_texts); +static SOC_ENUM_SINGLE_DECL(pm860x_lo2_opamp_enum, + PM860X_LO2_CTRL, 5, pm860x_opamp_texts); -static const struct soc_enum pm860x_lo1_pa_enum = - SOC_ENUM_SINGLE(PM860X_LO1_CTRL, 3, 4, pm860x_pa_texts); +static SOC_ENUM_SINGLE_DECL(pm860x_lo1_pa_enum, + PM860X_LO1_CTRL, 3, pm860x_pa_texts); -static const struct soc_enum pm860x_lo2_pa_enum = - SOC_ENUM_SINGLE(PM860X_LO2_CTRL, 3, 4, pm860x_pa_texts); +static SOC_ENUM_SINGLE_DECL(pm860x_lo2_pa_enum, + PM860X_LO2_CTRL, 3, pm860x_pa_texts); -static const struct soc_enum pm860x_spk_pa_enum = - SOC_ENUM_SINGLE(PM860X_EAR_CTRL_1, 5, 4, pm860x_pa_texts); +static SOC_ENUM_SINGLE_DECL(pm860x_spk_pa_enum, + PM860X_EAR_CTRL_1, 5, pm860x_pa_texts); -static const struct soc_enum pm860x_ear_pa_enum = - SOC_ENUM_SINGLE(PM860X_EAR_CTRL_2, 0, 4, pm860x_pa_texts); +static SOC_ENUM_SINGLE_DECL(pm860x_ear_pa_enum, + PM860X_EAR_CTRL_2, 0, pm860x_pa_texts); -static const struct soc_enum pm860x_spk_ear_opamp_enum = - SOC_ENUM_SINGLE(PM860X_EAR_CTRL_1, 3, 4, pm860x_opamp_texts); +static SOC_ENUM_SINGLE_DECL(pm860x_spk_ear_opamp_enum, + PM860X_EAR_CTRL_1, 3, pm860x_opamp_texts); static const struct snd_kcontrol_new pm860x_snd_controls[] = { SOC_DOUBLE_R_TLV("ADC Capture Volume", PM860X_ADC_ANA_2, @@ -561,8 +561,8 @@ static const char *aif1_text[] = { "PCM L", "PCM R", }; -static const struct soc_enum aif1_enum = - SOC_ENUM_SINGLE(PM860X_PCM_IFACE_3, 6, 2, aif1_text); +static SOC_ENUM_SINGLE_DECL(aif1_enum, + PM860X_PCM_IFACE_3, 6, aif1_text); static const struct snd_kcontrol_new aif1_mux = SOC_DAPM_ENUM("PCM Mux", aif1_enum); @@ -572,8 +572,8 @@ static const char *i2s_din_text[] = { "DIN", "DIN1", }; -static const struct soc_enum i2s_din_enum = - SOC_ENUM_SINGLE(PM860X_I2S_IFACE_3, 1, 2, i2s_din_text); +static SOC_ENUM_SINGLE_DECL(i2s_din_enum, + PM860X_I2S_IFACE_3, 1, i2s_din_text); static const struct snd_kcontrol_new i2s_din_mux = SOC_DAPM_ENUM("I2S DIN Mux", i2s_din_enum); @@ -583,8 +583,8 @@ static const char *i2s_mic_text[] = { "Ex PA", "ADC", }; -static const struct soc_enum i2s_mic_enum = - SOC_ENUM_SINGLE(PM860X_I2S_IFACE_3, 4, 2, i2s_mic_text); +static SOC_ENUM_SINGLE_DECL(i2s_mic_enum, + PM860X_I2S_IFACE_3, 4, i2s_mic_text); static const struct snd_kcontrol_new i2s_mic_mux = SOC_DAPM_ENUM("I2S Mic Mux", i2s_mic_enum); @@ -594,8 +594,8 @@ static const char *adcl_text[] = { "ADCR", "ADCL", }; -static const struct soc_enum adcl_enum = - SOC_ENUM_SINGLE(PM860X_PCM_IFACE_3, 4, 2, adcl_text); +static SOC_ENUM_SINGLE_DECL(adcl_enum, + PM860X_PCM_IFACE_3, 4, adcl_text); static const struct snd_kcontrol_new adcl_mux = SOC_DAPM_ENUM("ADC Left Mux", adcl_enum); @@ -605,8 +605,8 @@ static const char *adcr_text[] = { "ADCL", "ADCR", }; -static const struct soc_enum adcr_enum = - SOC_ENUM_SINGLE(PM860X_PCM_IFACE_3, 2, 2, adcr_text); +static SOC_ENUM_SINGLE_DECL(adcr_enum, + PM860X_PCM_IFACE_3, 2, adcr_text); static const struct snd_kcontrol_new adcr_mux = SOC_DAPM_ENUM("ADC Right Mux", adcr_enum); @@ -616,8 +616,8 @@ static const char *adcr_ec_text[] = { "ADCR", "EC", }; -static const struct soc_enum adcr_ec_enum = - SOC_ENUM_SINGLE(PM860X_ADC_EN_2, 3, 2, adcr_ec_text); +static SOC_ENUM_SINGLE_DECL(adcr_ec_enum, + PM860X_ADC_EN_2, 3, adcr_ec_text); static const struct snd_kcontrol_new adcr_ec_mux = SOC_DAPM_ENUM("ADCR EC Mux", adcr_ec_enum); @@ -627,8 +627,8 @@ static const char *ec_text[] = { "Left", "Right", "Left + Right", }; -static const struct soc_enum ec_enum = - SOC_ENUM_SINGLE(PM860X_EC_PATH, 1, 3, ec_text); +static SOC_ENUM_SINGLE_DECL(ec_enum, + PM860X_EC_PATH, 1, ec_text); static const struct snd_kcontrol_new ec_mux = SOC_DAPM_ENUM("EC Mux", ec_enum); @@ -638,36 +638,36 @@ static const char *dac_text[] = { }; /* DAC Headset 1 Mux / Mux10 */ -static const struct soc_enum dac_hs1_enum = - SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 0, 4, dac_text); +static SOC_ENUM_SINGLE_DECL(dac_hs1_enum, + PM860X_ANA_INPUT_SEL_1, 0, dac_text); static const struct snd_kcontrol_new dac_hs1_mux = SOC_DAPM_ENUM("DAC HS1 Mux", dac_hs1_enum); /* DAC Headset 2 Mux / Mux11 */ -static const struct soc_enum dac_hs2_enum = - SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 2, 4, dac_text); +static SOC_ENUM_SINGLE_DECL(dac_hs2_enum, + PM860X_ANA_INPUT_SEL_1, 2, dac_text); static const struct snd_kcontrol_new dac_hs2_mux = SOC_DAPM_ENUM("DAC HS2 Mux", dac_hs2_enum); /* DAC Lineout 1 Mux / Mux12 */ -static const struct soc_enum dac_lo1_enum = - SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 4, 4, dac_text); +static SOC_ENUM_SINGLE_DECL(dac_lo1_enum, + PM860X_ANA_INPUT_SEL_1, 4, dac_text); static const struct snd_kcontrol_new dac_lo1_mux = SOC_DAPM_ENUM("DAC LO1 Mux", dac_lo1_enum); /* DAC Lineout 2 Mux / Mux13 */ -static const struct soc_enum dac_lo2_enum = - SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 6, 4, dac_text); +static SOC_ENUM_SINGLE_DECL(dac_lo2_enum, + PM860X_ANA_INPUT_SEL_1, 6, dac_text); static const struct snd_kcontrol_new dac_lo2_mux = SOC_DAPM_ENUM("DAC LO2 Mux", dac_lo2_enum); /* DAC Spearker Earphone Mux / Mux14 */ -static const struct soc_enum dac_spk_ear_enum = - SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_2, 0, 4, dac_text); +static SOC_ENUM_SINGLE_DECL(dac_spk_ear_enum, + PM860X_ANA_INPUT_SEL_2, 0, dac_text); static const struct snd_kcontrol_new dac_spk_ear_mux = SOC_DAPM_ENUM("DAC SP Mux", dac_spk_ear_enum); @@ -677,29 +677,29 @@ static const char *in_text[] = { "Digital", "Analog", }; -static const struct soc_enum hs1_enum = - SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 0, 2, in_text); +static SOC_ENUM_SINGLE_DECL(hs1_enum, + PM860X_ANA_TO_ANA, 0, in_text); static const struct snd_kcontrol_new hs1_mux = SOC_DAPM_ENUM("Headset1 Mux", hs1_enum); /* Headset 2 Mux / Mux16 */ -static const struct soc_enum hs2_enum = - SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 1, 2, in_text); +static SOC_ENUM_SINGLE_DECL(hs2_enum, + PM860X_ANA_TO_ANA, 1, in_text); static const struct snd_kcontrol_new hs2_mux = SOC_DAPM_ENUM("Headset2 Mux", hs2_enum); /* Lineout 1 Mux / Mux17 */ -static const struct soc_enum lo1_enum = - SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 2, 2, in_text); +static SOC_ENUM_SINGLE_DECL(lo1_enum, + PM860X_ANA_TO_ANA, 2, in_text); static const struct snd_kcontrol_new lo1_mux = SOC_DAPM_ENUM("Lineout1 Mux", lo1_enum); /* Lineout 2 Mux / Mux18 */ -static const struct soc_enum lo2_enum = - SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 3, 2, in_text); +static SOC_ENUM_SINGLE_DECL(lo2_enum, + PM860X_ANA_TO_ANA, 3, in_text); static const struct snd_kcontrol_new lo2_mux = SOC_DAPM_ENUM("Lineout2 Mux", lo2_enum); @@ -709,8 +709,8 @@ static const char *spk_text[] = { "Earpiece", "Speaker", }; -static const struct soc_enum spk_enum = - SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 6, 2, spk_text); +static SOC_ENUM_SINGLE_DECL(spk_enum, + PM860X_ANA_TO_ANA, 6, spk_text); static const struct snd_kcontrol_new spk_demux = SOC_DAPM_ENUM("Speaker Earpiece Demux", spk_enum); @@ -720,8 +720,8 @@ static const char *mic_text[] = { "Mic 1", "Mic 2", }; -static const struct soc_enum mic_enum = - SOC_ENUM_SINGLE(PM860X_ADC_ANA_4, 4, 2, mic_text); +static SOC_ENUM_SINGLE_DECL(mic_enum, + PM860X_ADC_ANA_4, 4, mic_text); static const struct snd_kcontrol_new mic_mux = SOC_DAPM_ENUM("MIC Mux", mic_enum); @@ -1328,6 +1328,9 @@ static int pm860x_probe(struct snd_soc_codec *codec) pm860x->codec = codec; codec->control_data = pm860x->regmap; + ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP); + if (ret) + return ret; for (i = 0; i < 4; i++) { ret = request_threaded_irq(pm860x->irq[i], NULL, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b33b45dfceec..32d7a6f04b7d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -8,6 +8,8 @@ config SND_SOC_I2C_AND_SPI default y if I2C=y default y if SPI_MASTER=y +menu "CODEC drivers" + config SND_SOC_ALL_CODECS tristate "Build all ASoC CODEC drivers" depends on COMPILE_TEST @@ -16,15 +18,20 @@ config SND_SOC_ALL_CODECS select SND_SOC_AB8500_CODEC if ABX500_CORE select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS select SND_SOC_AD1836 if SPI_MASTER - select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI + select SND_SOC_AD193X_SPI if SPI_MASTER + select SND_SOC_AD193X_I2C if I2C select SND_SOC_AD1980 if SND_SOC_AC97_BUS select SND_SOC_AD73311 select SND_SOC_ADAU1373 if I2C - select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI + select SND_SOC_ADAV801 if SPI_MASTER + select SND_SOC_ADAV803 if I2C + select SND_SOC_ADAU1977_SPI if SPI_MASTER + select SND_SOC_ADAU1977_I2C if I2C select SND_SOC_ADAU1701 if I2C select SND_SOC_ADS117X select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C + select SND_SOC_AK4554 select SND_SOC_AK4641 if I2C select SND_SOC_AK4642 if I2C select SND_SOC_AK4671 if I2C @@ -59,19 +66,24 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM1681 if I2C select SND_SOC_PCM1792A if SPI_MASTER select SND_SOC_PCM3008 + select SND_SOC_PCM512x_I2C if I2C + select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_RT5631 if I2C select SND_SOC_RT5640 if I2C select SND_SOC_SGTL5000 if I2C select SND_SOC_SI476X if MFD_SI476X_CORE + select SND_SOC_SIRF_AUDIO_CODEC select SND_SOC_SN95031 if INTEL_SCU_IPC select SND_SOC_SPDIF select SND_SOC_SSM2518 if I2C - select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI + select SND_SOC_SSM2602_SPI if SPI_MASTER + select SND_SOC_SSM2602_I2C if I2C select SND_SOC_STA32X if I2C select SND_SOC_STA529 if I2C select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TAS5086 if I2C - select SND_SOC_TLV320AIC23 if I2C + select SND_SOC_TLV320AIC23_I2C if I2C + select SND_SOC_TLV320AIC23_SPI if SPI_MASTER select SND_SOC_TLV320AIC26 if SPI_MASTER select SND_SOC_TLV320AIC32X4 if I2C select SND_SOC_TLV320AIC3X if I2C @@ -163,8 +175,10 @@ config SND_SOC_WM_HUBS config SND_SOC_WM_ADSP tristate default y if SND_SOC_WM5102=y + default y if SND_SOC_WM5110=y default y if SND_SOC_WM2200=y default m if SND_SOC_WM5102=m + default m if SND_SOC_WM5110=m default m if SND_SOC_WM2200=m config SND_SOC_AB8500_CODEC @@ -180,6 +194,14 @@ config SND_SOC_AD1836 config SND_SOC_AD193X tristate +config SND_SOC_AD193X_SPI + tristate + select SND_SOC_AD193X + +config SND_SOC_AD193X_I2C + tristate + select SND_SOC_AD193X + config SND_SOC_AD1980 tristate @@ -187,41 +209,66 @@ config SND_SOC_AD73311 tristate config SND_SOC_ADAU1701 + tristate "Analog Devices ADAU1701 CODEC" + depends on I2C select SND_SOC_SIGMADSP - tristate config SND_SOC_ADAU1373 tristate +config SND_SOC_ADAU1977 + tristate + +config SND_SOC_ADAU1977_SPI + tristate + select SND_SOC_ADAU1977 + select REGMAP_SPI + +config SND_SOC_ADAU1977_I2C + tristate + select SND_SOC_ADAU1977 + select REGMAP_I2C + config SND_SOC_ADAV80X tristate +config SND_SOC_ADAV801 + tristate + select SND_SOC_ADAV80X + +config SND_SOC_ADAV803 + tristate + select SND_SOC_ADAV80X + config SND_SOC_ADS117X tristate config SND_SOC_AK4104 - tristate + tristate "AKM AK4104 CODEC" + depends on SPI_MASTER config SND_SOC_AK4535 tristate config SND_SOC_AK4554 - tristate + tristate "AKM AK4554 CODEC" config SND_SOC_AK4641 tristate config SND_SOC_AK4642 - tristate + tristate "AKM AK4642 CODEC" + depends on I2C config SND_SOC_AK4671 tristate config SND_SOC_AK5386 - tristate + tristate "AKM AK5638 CODEC" config SND_SOC_ALC5623 tristate + config SND_SOC_ALC5632 tristate @@ -232,14 +279,17 @@ config SND_SOC_CS42L51 tristate config SND_SOC_CS42L52 - tristate + tristate "Cirrus Logic CS42L52 CODEC" + depends on I2C config SND_SOC_CS42L73 - tristate + tristate "Cirrus Logic CS42L73 CODEC" + depends on I2C # Cirrus Logic CS4270 Codec config SND_SOC_CS4270 - tristate + tristate "Cirrus Logic CS4270 CODEC" + depends on I2C # Cirrus Logic CS4270 Codec VD = 3.3V Errata # Select if you are affected by the errata where the part will not function @@ -250,7 +300,8 @@ config SND_SOC_CS4270_VD33_ERRATA depends on SND_SOC_CS4270 config SND_SOC_CS4271 - tristate + tristate "Cirrus Logic CS4271 CODEC" + depends on SND_SOC_I2C_AND_SPI config SND_SOC_CX20442 tristate @@ -281,6 +332,9 @@ config SND_SOC_BT_SCO config SND_SOC_DMIC tristate +config SND_SOC_HDMI_CODEC + tristate "HDMI stub CODEC" + config SND_SOC_ISABELLE tristate @@ -299,18 +353,32 @@ config SND_SOC_MAX98095 config SND_SOC_MAX9850 tristate -config SND_SOC_HDMI_CODEC - tristate - config SND_SOC_PCM1681 - tristate + tristate "Texas Instruments PCM1681 CODEC" + depends on I2C config SND_SOC_PCM1792A - tristate + tristate "Texas Instruments PCM1792A CODEC" + depends on SPI_MASTER config SND_SOC_PCM3008 tristate +config SND_SOC_PCM512x + tristate + +config SND_SOC_PCM512x_I2C + tristate "Texas Instruments PCM512x CODECs - I2C" + depends on I2C + select SND_SOC_PCM512x + select REGMAP_I2C + +config SND_SOC_PCM512x_SPI + tristate "Texas Instruments PCM512x CODECs - SPI" + depends on SPI_MASTER + select SND_SOC_PCM512x + select REGMAP_SPI + config SND_SOC_RT5631 tristate @@ -319,7 +387,8 @@ config SND_SOC_RT5640 #Freescale sgtl5000 codec config SND_SOC_SGTL5000 - tristate + tristate "Freescale SGTL5000 CODEC" + depends on I2C config SND_SOC_SI476X tristate @@ -328,11 +397,15 @@ config SND_SOC_SIGMADSP tristate select CRC32 +config SND_SOC_SIRF_AUDIO_CODEC + tristate "SiRF SoC internal audio codec" + select REGMAP_MMIO + config SND_SOC_SN95031 tristate config SND_SOC_SPDIF - tristate + tristate "S/PDIF CODEC" config SND_SOC_SSM2518 tristate @@ -340,6 +413,14 @@ config SND_SOC_SSM2518 config SND_SOC_SSM2602 tristate +config SND_SOC_SSM2602_SPI + select SND_SOC_SSM2602 + tristate + +config SND_SOC_SSM2602_I2C + select SND_SOC_SSM2602 + tristate + config SND_SOC_STA32X tristate @@ -350,11 +431,20 @@ config SND_SOC_STAC9766 tristate config SND_SOC_TAS5086 - tristate + tristate "Texas Instruments TAS5086 speaker amplifier" + depends on I2C config SND_SOC_TLV320AIC23 tristate +config SND_SOC_TLV320AIC23_I2C + tristate + select SND_SOC_TLV320AIC23 + +config SND_SOC_TLV320AIC23_SPI + tristate + select SND_SOC_TLV320AIC23 + config SND_SOC_TLV320AIC26 tristate depends on SPI @@ -363,7 +453,8 @@ config SND_SOC_TLV320AIC32X4 tristate config SND_SOC_TLV320AIC3X - tristate + tristate "Texas Instruments TLV320AIC3x CODECs" + depends on I2C config SND_SOC_TLV320DAC33 tristate @@ -412,55 +503,69 @@ config SND_SOC_WM8400 tristate config SND_SOC_WM8510 - tristate + tristate "Wolfson Microelectronics WM8510 CODEC" + depends on SND_SOC_I2C_AND_SPI config SND_SOC_WM8523 - tristate + tristate "Wolfson Microelectronics WM8523 DAC" + depends on I2C config SND_SOC_WM8580 - tristate + tristate "Wolfson Microelectronics WM8523 CODEC" + depends on I2C config SND_SOC_WM8711 - tristate + tristate "Wolfson Microelectronics WM8711 CODEC" + depends on SND_SOC_I2C_AND_SPI config SND_SOC_WM8727 tristate config SND_SOC_WM8728 - tristate + tristate "Wolfson Microelectronics WM8728 DAC" + depends on SND_SOC_I2C_AND_SPI config SND_SOC_WM8731 - tristate + tristate "Wolfson Microelectronics WM8731 CODEC" + depends on SND_SOC_I2C_AND_SPI config SND_SOC_WM8737 - tristate + tristate "Wolfson Microelectronics WM8737 ADC" + depends on SND_SOC_I2C_AND_SPI config SND_SOC_WM8741 - tristate + tristate "Wolfson Microelectronics WM8737 DAC" + depends on SND_SOC_I2C_AND_SPI config SND_SOC_WM8750 - tristate + tristate "Wolfson Microelectronics WM8750 CODEC" + depends on SND_SOC_I2C_AND_SPI config SND_SOC_WM8753 - tristate + tristate "Wolfson Microelectronics WM8753 CODEC" + depends on SND_SOC_I2C_AND_SPI config SND_SOC_WM8770 - tristate + tristate "Wolfson Microelectronics WM8770 CODEC" + depends on SPI_MASTER config SND_SOC_WM8776 - tristate + tristate "Wolfson Microelectronics WM8776 CODEC" + depends on SND_SOC_I2C_AND_SPI config SND_SOC_WM8782 tristate config SND_SOC_WM8804 - tristate + tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver" + depends on SND_SOC_I2C_AND_SPI config SND_SOC_WM8900 tristate config SND_SOC_WM8903 - tristate + tristate "Wolfson Microelectronics WM8903 CODEC" + depends on I2C config SND_SOC_WM8904 tristate @@ -478,7 +583,8 @@ config SND_SOC_WM8961 tristate config SND_SOC_WM8962 - tristate + tristate "Wolfson Microelectronics WM8962 CODEC" + depends on I2C config SND_SOC_WM8971 tristate @@ -551,4 +657,7 @@ config SND_SOC_ML26124 tristate config SND_SOC_TPA6130A2 - tristate + tristate "Texas Instruments TPA6130A2 headphone amplifier" + depends on I2C + +endmenu diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index bc126764a44d..cb46c4c78dc2 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -3,11 +3,18 @@ snd-soc-ab8500-codec-objs := ab8500-codec.o snd-soc-ac97-objs := ac97.o snd-soc-ad1836-objs := ad1836.o snd-soc-ad193x-objs := ad193x.o +snd-soc-ad193x-spi-objs := ad193x-spi.o +snd-soc-ad193x-i2c-objs := ad193x-i2c.o snd-soc-ad1980-objs := ad1980.o snd-soc-ad73311-objs := ad73311.o snd-soc-adau1701-objs := adau1701.o snd-soc-adau1373-objs := adau1373.o +snd-soc-adau1977-objs := adau1977.o +snd-soc-adau1977-spi-objs := adau1977-spi.o +snd-soc-adau1977-i2c-objs := adau1977-i2c.o snd-soc-adav80x-objs := adav80x.o +snd-soc-adav801-objs := adav801.o +snd-soc-adav803-objs := adav803.o snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o @@ -46,6 +53,9 @@ snd-soc-hdmi-codec-objs := hdmi.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm1792a-codec-objs := pcm1792a.o snd-soc-pcm3008-objs := pcm3008.o +snd-soc-pcm512x-objs := pcm512x.o +snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o +snd-soc-pcm512x-spi-objs := pcm512x-spi.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o snd-soc-sgtl5000-objs := sgtl5000.o @@ -53,19 +63,24 @@ snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o snd-soc-sigmadsp-objs := sigmadsp.o snd-soc-si476x-objs := si476x.o +snd-soc-sirf-audio-codec-objs := sirf-audio-codec.o snd-soc-sn95031-objs := sn95031.o snd-soc-spdif-tx-objs := spdif_transmitter.o snd-soc-spdif-rx-objs := spdif_receiver.o snd-soc-ssm2518-objs := ssm2518.o snd-soc-ssm2602-objs := ssm2602.o +snd-soc-ssm2602-spi-objs := ssm2602-spi.o +snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o snd-soc-sta32x-objs := sta32x.o snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o snd-soc-tlv320aic23-objs := tlv320aic23.o +snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o +snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o snd-soc-tlv320aic26-objs := tlv320aic26.o -snd-soc-tlv320aic3x-objs := tlv320aic3x.o snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o +snd-soc-tlv320aic3x-objs := tlv320aic3x.o snd-soc-tlv320dac33-objs := tlv320dac33.o snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o @@ -134,11 +149,18 @@ obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o +obj-$(CONFIG_SND_SOC_AD193X_SPI) += snd-soc-ad193x-spi.o +obj-$(CONFIG_SND_SOC_AD193X_I2C) += snd-soc-ad193x-i2c.o obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o +obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o +obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o +obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o +obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o +obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o @@ -179,6 +201,9 @@ obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o +obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o +obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o +obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o @@ -188,14 +213,18 @@ obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o +obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o +obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o +obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o +obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o -obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o +obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 9a92b7962f41..685998dd086e 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -40,8 +40,8 @@ struct ad1836_priv { */ static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"}; -static const struct soc_enum ad1836_deemp_enum = - SOC_ENUM_SINGLE(AD1836_DAC_CTRL1, 8, 4, ad1836_deemp); +static SOC_ENUM_SINGLE_DECL(ad1836_deemp_enum, + AD1836_DAC_CTRL1, 8, ad1836_deemp); #define AD1836_DAC_VOLUME(x) \ SOC_DOUBLE_R("DAC" #x " Playback Volume", AD1836_DAC_L_VOL(x), \ @@ -168,17 +168,19 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream, int word_len = 0; /* bit size */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: word_len = AD1836_WORD_LEN_16; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: word_len = AD1836_WORD_LEN_20; break; - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S32_LE: + case 24: + case 32: 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-i2c.c b/sound/soc/codecs/ad193x-i2c.c new file mode 100644 index 000000000000..df3a1a415825 --- /dev/null +++ b/sound/soc/codecs/ad193x-i2c.c @@ -0,0 +1,54 @@ +/* + * AD1936/AD1937 audio driver + * + * Copyright 2014 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> + +#include <sound/soc.h> + +#include "ad193x.h" + +static const struct i2c_device_id ad193x_id[] = { + { "ad1936", 0 }, + { "ad1937", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad193x_id); + +static int ad193x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap_config config; + + config = ad193x_regmap_config; + config.val_bits = 8; + config.reg_bits = 8; + + return ad193x_probe(&client->dev, devm_regmap_init_i2c(client, &config)); +} + +static int ad193x_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static struct i2c_driver ad193x_i2c_driver = { + .driver = { + .name = "ad193x", + }, + .probe = ad193x_i2c_probe, + .remove = ad193x_i2c_remove, + .id_table = ad193x_id, +}; +module_i2c_driver(ad193x_i2c_driver); + +MODULE_DESCRIPTION("ASoC AD1936/AD1937 audio CODEC driver"); +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c new file mode 100644 index 000000000000..390cef9b9dc2 --- /dev/null +++ b/sound/soc/codecs/ad193x-spi.c @@ -0,0 +1,48 @@ +/* + * AD1938/AD1939 audio driver + * + * Copyright 2014 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/regmap.h> + +#include <sound/soc.h> + +#include "ad193x.h" + +static int ad193x_spi_probe(struct spi_device *spi) +{ + struct regmap_config config; + + config = ad193x_regmap_config; + config.val_bits = 8; + config.reg_bits = 16; + config.read_flag_mask = 0x09; + config.write_flag_mask = 0x08; + + return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config)); +} + +static int ad193x_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static struct spi_driver ad193x_spi_driver = { + .driver = { + .name = "ad193x", + .owner = THIS_MODULE, + }, + .probe = ad193x_spi_probe, + .remove = ad193x_spi_remove, +}; +module_spi_driver(ad193x_spi_driver); + +MODULE_DESCRIPTION("ASoC AD1938/AD1939 audio CODEC driver"); +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index aea7e52cf714..9381a767e75f 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -6,12 +6,10 @@ * Licensed under the GPL-2 or later. */ -#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/device.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> @@ -19,6 +17,7 @@ #include <sound/initval.h> #include <sound/soc.h> #include <sound/tlv.h> + #include "ad193x.h" /* codec private data */ @@ -32,8 +31,8 @@ struct ad193x_priv { */ static const char * const ad193x_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"}; -static const struct soc_enum ad193x_deemp_enum = - SOC_ENUM_SINGLE(AD193X_DAC_CTRL2, 1, 4, ad193x_deemp); +static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1, + ad193x_deemp); static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0); @@ -249,15 +248,15 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream, struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec); /* bit size */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: word_len = 3; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: word_len = 1; break; - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S32_LE: + case 24: + case 32: word_len = 0; break; } @@ -320,7 +319,7 @@ static struct snd_soc_dai_driver ad193x_dai = { .ops = &ad193x_dai_ops, }; -static int ad193x_probe(struct snd_soc_codec *codec) +static int ad193x_codec_probe(struct snd_soc_codec *codec) { struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec); int ret; @@ -352,7 +351,7 @@ static int ad193x_probe(struct snd_soc_codec *codec) } static struct snd_soc_codec_driver soc_codec_dev_ad193x = { - .probe = ad193x_probe, + .probe = ad193x_codec_probe, .controls = ad193x_snd_controls, .num_controls = ARRAY_SIZE(ad193x_snd_controls), .dapm_widgets = ad193x_dapm_widgets, @@ -366,140 +365,31 @@ static bool adau193x_reg_volatile(struct device *dev, unsigned int reg) return false; } -#if defined(CONFIG_SPI_MASTER) - -static const struct regmap_config ad193x_spi_regmap_config = { - .val_bits = 8, - .reg_bits = 16, - .read_flag_mask = 0x09, - .write_flag_mask = 0x08, - +const struct regmap_config ad193x_regmap_config = { .max_register = AD193X_NUM_REGS - 1, .volatile_reg = adau193x_reg_volatile, }; +EXPORT_SYMBOL_GPL(ad193x_regmap_config); -static int ad193x_spi_probe(struct spi_device *spi) +int ad193x_probe(struct device *dev, struct regmap *regmap) { struct ad193x_priv *ad193x; - ad193x = devm_kzalloc(&spi->dev, sizeof(struct ad193x_priv), - GFP_KERNEL); - if (ad193x == NULL) - return -ENOMEM; - - ad193x->regmap = devm_regmap_init_spi(spi, &ad193x_spi_regmap_config); - if (IS_ERR(ad193x->regmap)) - return PTR_ERR(ad193x->regmap); - - spi_set_drvdata(spi, ad193x); - - return snd_soc_register_codec(&spi->dev, &soc_codec_dev_ad193x, - &ad193x_dai, 1); -} - -static int ad193x_spi_remove(struct spi_device *spi) -{ - snd_soc_unregister_codec(&spi->dev); - return 0; -} - -static struct spi_driver ad193x_spi_driver = { - .driver = { - .name = "ad193x", - .owner = THIS_MODULE, - }, - .probe = ad193x_spi_probe, - .remove = ad193x_spi_remove, -}; -#endif - -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - -static const struct regmap_config ad193x_i2c_regmap_config = { - .val_bits = 8, - .reg_bits = 8, - - .max_register = AD193X_NUM_REGS - 1, - .volatile_reg = adau193x_reg_volatile, -}; - -static const struct i2c_device_id ad193x_id[] = { - { "ad1936", 0 }, - { "ad1937", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ad193x_id); - -static int ad193x_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct ad193x_priv *ad193x; + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - ad193x = devm_kzalloc(&client->dev, sizeof(struct ad193x_priv), - GFP_KERNEL); + ad193x = devm_kzalloc(dev, sizeof(*ad193x), GFP_KERNEL); if (ad193x == NULL) return -ENOMEM; - ad193x->regmap = devm_regmap_init_i2c(client, &ad193x_i2c_regmap_config); - if (IS_ERR(ad193x->regmap)) - return PTR_ERR(ad193x->regmap); - - i2c_set_clientdata(client, ad193x); - - return snd_soc_register_codec(&client->dev, &soc_codec_dev_ad193x, - &ad193x_dai, 1); -} - -static int ad193x_i2c_remove(struct i2c_client *client) -{ - snd_soc_unregister_codec(&client->dev); - return 0; -} - -static struct i2c_driver ad193x_i2c_driver = { - .driver = { - .name = "ad193x", - }, - .probe = ad193x_i2c_probe, - .remove = ad193x_i2c_remove, - .id_table = ad193x_id, -}; -#endif - -static int __init ad193x_modinit(void) -{ - int ret; - -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - ret = i2c_add_driver(&ad193x_i2c_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register AD193X I2C driver: %d\n", - ret); - } -#endif - -#if defined(CONFIG_SPI_MASTER) - ret = spi_register_driver(&ad193x_spi_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register AD193X SPI driver: %d\n", - ret); - } -#endif - return ret; -} -module_init(ad193x_modinit); + ad193x->regmap = regmap; -static void __exit ad193x_modexit(void) -{ -#if defined(CONFIG_SPI_MASTER) - spi_unregister_driver(&ad193x_spi_driver); -#endif + dev_set_drvdata(dev, ad193x); -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_del_driver(&ad193x_i2c_driver); -#endif + return snd_soc_register_codec(dev, &soc_codec_dev_ad193x, + &ad193x_dai, 1); } -module_exit(ad193x_modexit); +EXPORT_SYMBOL_GPL(ad193x_probe); MODULE_DESCRIPTION("ASoC ad193x driver"); MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h index 473388049992..ab9a998f15be 100644 --- a/sound/soc/codecs/ad193x.h +++ b/sound/soc/codecs/ad193x.h @@ -9,6 +9,13 @@ #ifndef __AD193X_H__ #define __AD193X_H__ +#include <linux/regmap.h> + +struct device; + +extern const struct regmap_config ad193x_regmap_config; +int ad193x_probe(struct device *dev, struct regmap *regmap); + #define AD193X_PLL_CLK_CTRL0 0x00 #define AD193X_PLL_POWERDOWN 0x01 #define AD193X_PLL_INPUT_MASK 0x6 diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 7257a8885f42..34d965a4a040 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -57,8 +57,8 @@ static const u16 ad1980_reg[] = { static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line", "Stereo Mix", "Mono Mix", "Phone"}; -static const struct soc_enum ad1980_cap_src = - SOC_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 7, ad1980_rec_sel); +static SOC_ENUM_DOUBLE_DECL(ad1980_cap_src, + AC97_REC_SEL, 8, 0, ad1980_rec_sel); static const struct snd_kcontrol_new ad1980_snd_ac97_controls[] = { SOC_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1), diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 59654b1e7f3f..5223800775ad 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -345,15 +345,15 @@ static const char *adau1373_fdsp_sel_text[] = { "Channel 5", }; -static const SOC_ENUM_SINGLE_DECL(adau1373_drc1_channel_enum, +static SOC_ENUM_SINGLE_DECL(adau1373_drc1_channel_enum, ADAU1373_FDSP_SEL1, 4, adau1373_fdsp_sel_text); -static const SOC_ENUM_SINGLE_DECL(adau1373_drc2_channel_enum, +static SOC_ENUM_SINGLE_DECL(adau1373_drc2_channel_enum, ADAU1373_FDSP_SEL1, 0, adau1373_fdsp_sel_text); -static const SOC_ENUM_SINGLE_DECL(adau1373_drc3_channel_enum, +static SOC_ENUM_SINGLE_DECL(adau1373_drc3_channel_enum, ADAU1373_FDSP_SEL2, 0, adau1373_fdsp_sel_text); -static const SOC_ENUM_SINGLE_DECL(adau1373_hpf_channel_enum, +static SOC_ENUM_SINGLE_DECL(adau1373_hpf_channel_enum, ADAU1373_FDSP_SEL3, 0, adau1373_fdsp_sel_text); -static const SOC_ENUM_SINGLE_DECL(adau1373_bass_channel_enum, +static SOC_ENUM_SINGLE_DECL(adau1373_bass_channel_enum, ADAU1373_FDSP_SEL4, 4, adau1373_fdsp_sel_text); static const char *adau1373_hpf_cutoff_text[] = { @@ -362,7 +362,7 @@ static const char *adau1373_hpf_cutoff_text[] = { "800Hz", }; -static const SOC_ENUM_SINGLE_DECL(adau1373_hpf_cutoff_enum, +static SOC_ENUM_SINGLE_DECL(adau1373_hpf_cutoff_enum, ADAU1373_HPF_CTRL, 3, adau1373_hpf_cutoff_text); static const char *adau1373_bass_lpf_cutoff_text[] = { @@ -388,14 +388,14 @@ static const unsigned int adau1373_bass_tlv[] = { 5, 7, TLV_DB_SCALE_ITEM(1400, 150, 0), }; -static const SOC_ENUM_SINGLE_DECL(adau1373_bass_lpf_cutoff_enum, +static SOC_ENUM_SINGLE_DECL(adau1373_bass_lpf_cutoff_enum, ADAU1373_BASS1, 5, adau1373_bass_lpf_cutoff_text); -static const SOC_VALUE_ENUM_SINGLE_DECL(adau1373_bass_clip_level_enum, +static SOC_VALUE_ENUM_SINGLE_DECL(adau1373_bass_clip_level_enum, ADAU1373_BASS1, 2, 7, adau1373_bass_clip_level_text, adau1373_bass_clip_level_values); -static const SOC_ENUM_SINGLE_DECL(adau1373_bass_hpf_cutoff_enum, +static SOC_ENUM_SINGLE_DECL(adau1373_bass_hpf_cutoff_enum, ADAU1373_BASS1, 0, adau1373_bass_hpf_cutoff_text); static const char *adau1373_3d_level_text[] = { @@ -409,9 +409,9 @@ static const char *adau1373_3d_cutoff_text[] = { "0.16875 fs", "0.27083 fs" }; -static const SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum, +static SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum, ADAU1373_3D_CTRL1, 4, adau1373_3d_level_text); -static const SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum, +static SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum, ADAU1373_3D_CTRL1, 0, adau1373_3d_cutoff_text); static const unsigned int adau1373_3d_tlv[] = { @@ -427,11 +427,11 @@ static const char *adau1373_lr_mux_text[] = { "Stereo", }; -static const SOC_ENUM_SINGLE_DECL(adau1373_lineout1_lr_mux_enum, +static SOC_ENUM_SINGLE_DECL(adau1373_lineout1_lr_mux_enum, ADAU1373_OUTPUT_CTRL, 4, adau1373_lr_mux_text); -static const SOC_ENUM_SINGLE_DECL(adau1373_lineout2_lr_mux_enum, +static SOC_ENUM_SINGLE_DECL(adau1373_lineout2_lr_mux_enum, ADAU1373_OUTPUT_CTRL, 6, adau1373_lr_mux_text); -static const SOC_ENUM_SINGLE_DECL(adau1373_speaker_lr_mux_enum, +static SOC_ENUM_SINGLE_DECL(adau1373_speaker_lr_mux_enum, ADAU1373_LS_CTRL, 4, adau1373_lr_mux_text); static const struct snd_kcontrol_new adau1373_controls[] = { @@ -576,8 +576,8 @@ static const char *adau1373_decimator_text[] = { "DMIC1", }; -static const struct soc_enum adau1373_decimator_enum = - SOC_ENUM_SINGLE(0, 0, 2, adau1373_decimator_text); +static SOC_ENUM_SINGLE_VIRT_DECL(adau1373_decimator_enum, + adau1373_decimator_text); static const struct snd_kcontrol_new adau1373_decimator_mux = SOC_DAPM_ENUM_VIRT("Decimator Mux", adau1373_decimator_enum); @@ -1078,17 +1078,17 @@ static int adau1373_hw_params(struct snd_pcm_substream *substream, ADAU1373_BCLKDIV_SR_MASK | ADAU1373_BCLKDIV_BCLK_MASK, (div << 2) | ADAU1373_BCLKDIV_64); - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: ctrl = ADAU1373_DAI_WLEN_16; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: ctrl = ADAU1373_DAI_WLEN_20; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: ctrl = ADAU1373_DAI_WLEN_24; break; - case SNDRV_PCM_FORMAT_S32_LE: + case 32: ctrl = ADAU1373_DAI_WLEN_32; break; default: diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index ebff1128be59..d71c59cf7bdd 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -71,7 +71,7 @@ #define ADAU1701_SEROCTL_WORD_LEN_24 0x0000 #define ADAU1701_SEROCTL_WORD_LEN_20 0x0001 -#define ADAU1701_SEROCTL_WORD_LEN_16 0x0010 +#define ADAU1701_SEROCTL_WORD_LEN_16 0x0002 #define ADAU1701_SEROCTL_WORD_LEN_MASK 0x0003 #define ADAU1701_AUXNPOW_VBPD 0x40 @@ -299,20 +299,20 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv) } static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec, - snd_pcm_format_t format) + struct snd_pcm_hw_params *params) { struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK; unsigned int val; - switch (format) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: val = ADAU1701_SEROCTL_WORD_LEN_16; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: val = ADAU1701_SEROCTL_WORD_LEN_20; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: val = ADAU1701_SEROCTL_WORD_LEN_24; break; default: @@ -320,14 +320,14 @@ static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec, } if (adau1701->dai_fmt == SND_SOC_DAIFMT_RIGHT_J) { - switch (format) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: val |= ADAU1701_SEROCTL_MSB_DEALY16; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: val |= ADAU1701_SEROCTL_MSB_DEALY12; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: val |= ADAU1701_SEROCTL_MSB_DEALY8; break; } @@ -340,7 +340,7 @@ static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec, } static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec, - snd_pcm_format_t format) + struct snd_pcm_hw_params *params) { struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); unsigned int val; @@ -348,14 +348,14 @@ static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec, if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J) return 0; - switch (format) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: val = ADAU1701_SERICTL_RIGHTJ_16; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: val = ADAU1701_SERICTL_RIGHTJ_20; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: val = ADAU1701_SERICTL_RIGHTJ_24; break; default: @@ -374,7 +374,6 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); unsigned int clkdiv = adau1701->sysclk / params_rate(params); - snd_pcm_format_t format; unsigned int val; int ret; @@ -406,11 +405,10 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_SR_MASK, val); - format = params_format(params); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - return adau1701_set_playback_pcm_format(codec, format); + return adau1701_set_playback_pcm_format(codec, params); else - return adau1701_set_capture_pcm_format(codec, format); + return adau1701_set_capture_pcm_format(codec, params); } static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai, diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c new file mode 100644 index 000000000000..9700e8c838c9 --- /dev/null +++ b/sound/soc/codecs/adau1977-i2c.c @@ -0,0 +1,59 @@ +/* + * ADAU1977/ADAU1978/ADAU1979 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2. + */ + +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/soc.h> + +#include "adau1977.h" + +static int adau1977_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap_config config; + + config = adau1977_regmap_config; + config.val_bits = 8; + config.reg_bits = 8; + + return adau1977_probe(&client->dev, + devm_regmap_init_i2c(client, &config), + id->driver_data, NULL); +} + +static int adau1977_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id adau1977_i2c_ids[] = { + { "adau1977", ADAU1977 }, + { "adau1978", ADAU1978 }, + { "adau1979", ADAU1978 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids); + +static struct i2c_driver adau1977_i2c_driver = { + .driver = { + .name = "adau1977", + .owner = THIS_MODULE, + }, + .probe = adau1977_i2c_probe, + .remove = adau1977_i2c_remove, + .id_table = adau1977_i2c_ids, +}; +module_i2c_driver(adau1977_i2c_driver); + +MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c new file mode 100644 index 000000000000..b05cf5da3a94 --- /dev/null +++ b/sound/soc/codecs/adau1977-spi.c @@ -0,0 +1,76 @@ +/* + * ADAU1977/ADAU1978/ADAU1979 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2. + */ + +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <sound/soc.h> + +#include "adau1977.h" + +static void adau1977_spi_switch_mode(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + /* + * To get the device into SPI mode CLATCH has to be pulled low three + * times. Do this by issuing three dummy reads. + */ + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); +} + +static int adau1977_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap_config config; + + if (!id) + return -EINVAL; + + config = adau1977_regmap_config; + config.val_bits = 8; + config.reg_bits = 16; + config.read_flag_mask = 0x1; + + return adau1977_probe(&spi->dev, + devm_regmap_init_spi(spi, &config), + id->driver_data, adau1977_spi_switch_mode); +} + +static int adau1977_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static const struct spi_device_id adau1977_spi_ids[] = { + { "adau1977", ADAU1977 }, + { "adau1978", ADAU1978 }, + { "adau1979", ADAU1978 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adau1977_spi_ids); + +static struct spi_driver adau1977_spi_driver = { + .driver = { + .name = "adau1977", + .owner = THIS_MODULE, + }, + .probe = adau1977_spi_probe, + .remove = adau1977_spi_remove, + .id_table = adau1977_spi_ids, +}; +module_spi_driver(adau1977_spi_driver); + +MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c new file mode 100644 index 000000000000..fd55da7cb9d4 --- /dev/null +++ b/sound/soc/codecs/adau1977.c @@ -0,0 +1,1018 @@ +/* + * ADAU1977/ADAU1978/ADAU1979 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_data/adau1977.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "adau1977.h" + +#define ADAU1977_REG_POWER 0x00 +#define ADAU1977_REG_PLL 0x01 +#define ADAU1977_REG_BOOST 0x02 +#define ADAU1977_REG_MICBIAS 0x03 +#define ADAU1977_REG_BLOCK_POWER_SAI 0x04 +#define ADAU1977_REG_SAI_CTRL0 0x05 +#define ADAU1977_REG_SAI_CTRL1 0x06 +#define ADAU1977_REG_CMAP12 0x07 +#define ADAU1977_REG_CMAP34 0x08 +#define ADAU1977_REG_SAI_OVERTEMP 0x09 +#define ADAU1977_REG_POST_ADC_GAIN(x) (0x0a + (x)) +#define ADAU1977_REG_MISC_CONTROL 0x0e +#define ADAU1977_REG_DIAG_CONTROL 0x10 +#define ADAU1977_REG_STATUS(x) (0x11 + (x)) +#define ADAU1977_REG_DIAG_IRQ1 0x15 +#define ADAU1977_REG_DIAG_IRQ2 0x16 +#define ADAU1977_REG_ADJUST1 0x17 +#define ADAU1977_REG_ADJUST2 0x18 +#define ADAU1977_REG_ADC_CLIP 0x19 +#define ADAU1977_REG_DC_HPF_CAL 0x1a + +#define ADAU1977_POWER_RESET BIT(7) +#define ADAU1977_POWER_PWUP BIT(0) + +#define ADAU1977_PLL_CLK_S BIT(4) +#define ADAU1977_PLL_MCS_MASK 0x7 + +#define ADAU1977_MICBIAS_MB_VOLTS_MASK 0xf0 +#define ADAU1977_MICBIAS_MB_VOLTS_OFFSET 4 + +#define ADAU1977_BLOCK_POWER_SAI_LR_POL BIT(7) +#define ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE BIT(6) +#define ADAU1977_BLOCK_POWER_SAI_LDO_EN BIT(5) + +#define ADAU1977_SAI_CTRL0_FMT_MASK (0x3 << 6) +#define ADAU1977_SAI_CTRL0_FMT_I2S (0x0 << 6) +#define ADAU1977_SAI_CTRL0_FMT_LJ (0x1 << 6) +#define ADAU1977_SAI_CTRL0_FMT_RJ_24BIT (0x2 << 6) +#define ADAU1977_SAI_CTRL0_FMT_RJ_16BIT (0x3 << 6) + +#define ADAU1977_SAI_CTRL0_SAI_MASK (0x7 << 3) +#define ADAU1977_SAI_CTRL0_SAI_I2S (0x0 << 3) +#define ADAU1977_SAI_CTRL0_SAI_TDM_2 (0x1 << 3) +#define ADAU1977_SAI_CTRL0_SAI_TDM_4 (0x2 << 3) +#define ADAU1977_SAI_CTRL0_SAI_TDM_8 (0x3 << 3) +#define ADAU1977_SAI_CTRL0_SAI_TDM_16 (0x4 << 3) + +#define ADAU1977_SAI_CTRL0_FS_MASK (0x7) +#define ADAU1977_SAI_CTRL0_FS_8000_12000 (0x0) +#define ADAU1977_SAI_CTRL0_FS_16000_24000 (0x1) +#define ADAU1977_SAI_CTRL0_FS_32000_48000 (0x2) +#define ADAU1977_SAI_CTRL0_FS_64000_96000 (0x3) +#define ADAU1977_SAI_CTRL0_FS_128000_192000 (0x4) + +#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_MASK (0x3 << 5) +#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_32 (0x0 << 5) +#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_24 (0x1 << 5) +#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_16 (0x2 << 5) +#define ADAU1977_SAI_CTRL1_DATA_WIDTH_MASK (0x1 << 4) +#define ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT (0x1 << 4) +#define ADAU1977_SAI_CTRL1_DATA_WIDTH_24BIT (0x0 << 4) +#define ADAU1977_SAI_CTRL1_LRCLK_PULSE BIT(3) +#define ADAU1977_SAI_CTRL1_MSB BIT(2) +#define ADAU1977_SAI_CTRL1_BCLKRATE_16 (0x1 << 1) +#define ADAU1977_SAI_CTRL1_BCLKRATE_32 (0x0 << 1) +#define ADAU1977_SAI_CTRL1_BCLKRATE_MASK (0x1 << 1) +#define ADAU1977_SAI_CTRL1_MASTER BIT(0) + +#define ADAU1977_SAI_OVERTEMP_DRV_C(x) BIT(4 + (x)) +#define ADAU1977_SAI_OVERTEMP_DRV_HIZ BIT(3) + +#define ADAU1977_MISC_CONTROL_SUM_MODE_MASK (0x3 << 6) +#define ADAU1977_MISC_CONTROL_SUM_MODE_1CH (0x2 << 6) +#define ADAU1977_MISC_CONTROL_SUM_MODE_2CH (0x1 << 6) +#define ADAU1977_MISC_CONTROL_SUM_MODE_4CH (0x0 << 6) +#define ADAU1977_MISC_CONTROL_MMUTE BIT(4) +#define ADAU1977_MISC_CONTROL_DC_CAL BIT(0) + +#define ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET 4 +#define ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET 0 + +struct adau1977 { + struct regmap *regmap; + bool right_j; + unsigned int sysclk; + enum adau1977_sysclk_src sysclk_src; + struct gpio_desc *reset_gpio; + enum adau1977_type type; + + struct regulator *avdd_reg; + struct regulator *dvdd_reg; + + struct snd_pcm_hw_constraint_list constraints; + + struct device *dev; + void (*switch_mode)(struct device *dev); + + unsigned int max_master_fs; + unsigned int slot_width; + bool enabled; + bool master; +}; + +static const struct reg_default adau1977_reg_defaults[] = { + { 0x00, 0x00 }, + { 0x01, 0x41 }, + { 0x02, 0x4a }, + { 0x03, 0x7d }, + { 0x04, 0x3d }, + { 0x05, 0x02 }, + { 0x06, 0x00 }, + { 0x07, 0x10 }, + { 0x08, 0x32 }, + { 0x09, 0xf0 }, + { 0x0a, 0xa0 }, + { 0x0b, 0xa0 }, + { 0x0c, 0xa0 }, + { 0x0d, 0xa0 }, + { 0x0e, 0x02 }, + { 0x10, 0x0f }, + { 0x15, 0x20 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, + { 0x18, 0x00 }, + { 0x1a, 0x00 }, +}; + +static const DECLARE_TLV_DB_MINMAX_MUTE(adau1977_adc_gain, -3562, 6000); + +static const struct snd_soc_dapm_widget adau1977_micbias_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("MICBIAS", ADAU1977_REG_MICBIAS, + 3, 0, NULL, 0) +}; + +static const struct snd_soc_dapm_widget adau1977_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("Vref", ADAU1977_REG_BLOCK_POWER_SAI, + 4, 0, NULL, 0), + + SND_SOC_DAPM_ADC("ADC1", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 0, 0), + SND_SOC_DAPM_ADC("ADC2", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 1, 0), + SND_SOC_DAPM_ADC("ADC3", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 2, 0), + SND_SOC_DAPM_ADC("ADC4", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 3, 0), + + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), + SND_SOC_DAPM_INPUT("AIN4"), + + SND_SOC_DAPM_OUTPUT("VREF"), +}; + +static const struct snd_soc_dapm_route adau1977_dapm_routes[] = { + { "ADC1", NULL, "AIN1" }, + { "ADC2", NULL, "AIN2" }, + { "ADC3", NULL, "AIN3" }, + { "ADC4", NULL, "AIN4" }, + + { "ADC1", NULL, "Vref" }, + { "ADC2", NULL, "Vref" }, + { "ADC3", NULL, "Vref" }, + { "ADC4", NULL, "Vref" }, + + { "VREF", NULL, "Vref" }, +}; + +#define ADAU1977_VOLUME(x) \ + SOC_SINGLE_TLV("ADC" #x " Capture Volume", \ + ADAU1977_REG_POST_ADC_GAIN((x) - 1), \ + 0, 255, 1, adau1977_adc_gain) + +#define ADAU1977_HPF_SWITCH(x) \ + SOC_SINGLE("ADC" #x " Highpass-Filter Capture Switch", \ + ADAU1977_REG_DC_HPF_CAL, (x) - 1, 1, 0) + +#define ADAU1977_DC_SUB_SWITCH(x) \ + SOC_SINGLE("ADC" #x " DC Substraction Capture Switch", \ + ADAU1977_REG_DC_HPF_CAL, (x) + 3, 1, 0) + +static const struct snd_kcontrol_new adau1977_snd_controls[] = { + ADAU1977_VOLUME(1), + ADAU1977_VOLUME(2), + ADAU1977_VOLUME(3), + ADAU1977_VOLUME(4), + + ADAU1977_HPF_SWITCH(1), + ADAU1977_HPF_SWITCH(2), + ADAU1977_HPF_SWITCH(3), + ADAU1977_HPF_SWITCH(4), + + ADAU1977_DC_SUB_SWITCH(1), + ADAU1977_DC_SUB_SWITCH(2), + ADAU1977_DC_SUB_SWITCH(3), + ADAU1977_DC_SUB_SWITCH(4), +}; + +static int adau1977_reset(struct adau1977 *adau1977) +{ + int ret; + + /* + * The reset bit is obviously volatile, but we need to be able to cache + * the other bits in the register, so we can't just mark the whole + * register as volatile. Since this is the only place where we'll ever + * touch the reset bit just bypass the cache for this operation. + */ + regcache_cache_bypass(adau1977->regmap, true); + ret = regmap_write(adau1977->regmap, ADAU1977_REG_POWER, + ADAU1977_POWER_RESET); + regcache_cache_bypass(adau1977->regmap, false); + if (ret) + return ret; + + return ret; +} + +/* + * Returns the appropriate setting for ths FS field in the CTRL0 register + * depending on the rate. + */ +static int adau1977_lookup_fs(unsigned int rate) +{ + if (rate >= 8000 && rate <= 12000) + return ADAU1977_SAI_CTRL0_FS_8000_12000; + else if (rate >= 16000 && rate <= 24000) + return ADAU1977_SAI_CTRL0_FS_16000_24000; + else if (rate >= 32000 && rate <= 48000) + return ADAU1977_SAI_CTRL0_FS_32000_48000; + else if (rate >= 64000 && rate <= 96000) + return ADAU1977_SAI_CTRL0_FS_64000_96000; + else if (rate >= 128000 && rate <= 192000) + return ADAU1977_SAI_CTRL0_FS_128000_192000; + else + return -EINVAL; +} + +static int adau1977_lookup_mcs(struct adau1977 *adau1977, unsigned int rate, + unsigned int fs) +{ + unsigned int mcs; + + /* + * rate = sysclk / (512 * mcs_lut[mcs]) * 2**fs + * => mcs_lut[mcs] = sysclk / (512 * rate) * 2**fs + * => mcs_lut[mcs] = sysclk / ((512 / 2**fs) * rate) + */ + + rate *= 512 >> fs; + + if (adau1977->sysclk % rate != 0) + return -EINVAL; + + mcs = adau1977->sysclk / rate; + + /* The factors configured by MCS are 1, 2, 3, 4, 6 */ + if (mcs < 1 || mcs > 6 || mcs == 5) + return -EINVAL; + + mcs = mcs - 1; + if (mcs == 5) + mcs = 4; + + return mcs; +} + +static int adau1977_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec); + unsigned int rate = params_rate(params); + unsigned int slot_width; + unsigned int ctrl0, ctrl0_mask; + unsigned int ctrl1; + int mcs, fs; + int ret; + + fs = adau1977_lookup_fs(rate); + if (fs < 0) + return fs; + + if (adau1977->sysclk_src == ADAU1977_SYSCLK_SRC_MCLK) { + mcs = adau1977_lookup_mcs(adau1977, rate, fs); + if (mcs < 0) + return mcs; + } else { + mcs = 0; + } + + ctrl0_mask = ADAU1977_SAI_CTRL0_FS_MASK; + ctrl0 = fs; + + if (adau1977->right_j) { + switch (params_width(params)) { + case 16: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_16BIT; + break; + case 24: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_24BIT; + break; + default: + return -EINVAL; + } + ctrl0_mask |= ADAU1977_SAI_CTRL0_FMT_MASK; + } + + if (adau1977->master) { + switch (params_width(params)) { + case 16: + ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT; + slot_width = 16; + break; + case 24: + case 32: + ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_24BIT; + slot_width = 32; + break; + default: + return -EINVAL; + } + + /* In TDM mode there is a fixed slot width */ + if (adau1977->slot_width) + slot_width = adau1977->slot_width; + + if (slot_width == 16) + ctrl1 |= ADAU1977_SAI_CTRL1_BCLKRATE_16; + else + ctrl1 |= ADAU1977_SAI_CTRL1_BCLKRATE_32; + + ret = regmap_update_bits(adau1977->regmap, + ADAU1977_REG_SAI_CTRL1, + ADAU1977_SAI_CTRL1_DATA_WIDTH_MASK | + ADAU1977_SAI_CTRL1_BCLKRATE_MASK, + ctrl1); + if (ret < 0) + return ret; + } + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0, + ctrl0_mask, ctrl0); + if (ret < 0) + return ret; + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_PLL, + ADAU1977_PLL_MCS_MASK, mcs); +} + +static int adau1977_power_disable(struct adau1977 *adau1977) +{ + int ret = 0; + + if (!adau1977->enabled) + return 0; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_POWER, + ADAU1977_POWER_PWUP, 0); + if (ret) + return ret; + + regcache_mark_dirty(adau1977->regmap); + + if (adau1977->reset_gpio) + gpiod_set_value_cansleep(adau1977->reset_gpio, 0); + + regcache_cache_only(adau1977->regmap, true); + + regulator_disable(adau1977->avdd_reg); + if (adau1977->dvdd_reg) + regulator_disable(adau1977->dvdd_reg); + + adau1977->enabled = false; + + return 0; +} + +static int adau1977_power_enable(struct adau1977 *adau1977) +{ + unsigned int val; + int ret = 0; + + if (adau1977->enabled) + return 0; + + ret = regulator_enable(adau1977->avdd_reg); + if (ret) + return ret; + + if (adau1977->dvdd_reg) { + ret = regulator_enable(adau1977->dvdd_reg); + if (ret) + goto err_disable_avdd; + } + + if (adau1977->reset_gpio) + gpiod_set_value_cansleep(adau1977->reset_gpio, 1); + + regcache_cache_only(adau1977->regmap, false); + + if (adau1977->switch_mode) + adau1977->switch_mode(adau1977->dev); + + ret = adau1977_reset(adau1977); + if (ret) + goto err_disable_dvdd; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_POWER, + ADAU1977_POWER_PWUP, ADAU1977_POWER_PWUP); + if (ret) + goto err_disable_dvdd; + + ret = regcache_sync(adau1977->regmap); + if (ret) + goto err_disable_dvdd; + + /* + * The PLL register is not affected by the software reset. It is + * possible that the value of the register was changed to the + * default value while we were in cache only mode. In this case + * regcache_sync will skip over it and we have to manually sync + * it. + */ + ret = regmap_read(adau1977->regmap, ADAU1977_REG_PLL, &val); + if (ret) + goto err_disable_dvdd; + + if (val == 0x41) { + regcache_cache_bypass(adau1977->regmap, true); + ret = regmap_write(adau1977->regmap, ADAU1977_REG_PLL, + 0x41); + if (ret) + goto err_disable_dvdd; + regcache_cache_bypass(adau1977->regmap, false); + } + + adau1977->enabled = true; + + return ret; + +err_disable_dvdd: + if (adau1977->dvdd_reg) + regulator_disable(adau1977->dvdd_reg); +err_disable_avdd: + regulator_disable(adau1977->avdd_reg); + return ret; +} + +static int adau1977_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + ret = adau1977_power_enable(adau1977); + break; + case SND_SOC_BIAS_OFF: + ret = adau1977_power_disable(adau1977); + break; + } + + if (ret) + return ret; + + codec->dapm.bias_level = level; + + return 0; +} + +static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int ctrl0, ctrl1, drv; + unsigned int slot[4]; + unsigned int i; + int ret; + + if (slots == 0) { + /* 0 = No fixed slot width */ + adau1977->slot_width = 0; + adau1977->max_master_fs = 192000; + return regmap_update_bits(adau1977->regmap, + ADAU1977_REG_SAI_CTRL0, ADAU1977_SAI_CTRL0_SAI_MASK, + ADAU1977_SAI_CTRL0_SAI_I2S); + } + + if (rx_mask == 0 || tx_mask != 0) + return -EINVAL; + + drv = 0; + for (i = 0; i < 4; i++) { + slot[i] = __ffs(rx_mask); + drv |= ADAU1977_SAI_OVERTEMP_DRV_C(i); + rx_mask &= ~(1 << slot[i]); + if (slot[i] >= slots) + return -EINVAL; + if (rx_mask == 0) + break; + } + + if (rx_mask != 0) + return -EINVAL; + + switch (width) { + case 16: + ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_16; + break; + case 24: + /* We can only generate 16 bit or 32 bit wide slots */ + if (adau1977->master) + return -EINVAL; + ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_24; + break; + case 32: + ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_32; + break; + default: + return -EINVAL; + } + + switch (slots) { + case 2: + ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_2; + break; + case 4: + ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_4; + break; + case 8: + ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_8; + break; + case 16: + ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_16; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_OVERTEMP, + ADAU1977_SAI_OVERTEMP_DRV_C(0) | + ADAU1977_SAI_OVERTEMP_DRV_C(1) | + ADAU1977_SAI_OVERTEMP_DRV_C(2) | + ADAU1977_SAI_OVERTEMP_DRV_C(3), drv); + if (ret) + return ret; + + ret = regmap_write(adau1977->regmap, ADAU1977_REG_CMAP12, + (slot[1] << ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET) | + (slot[0] << ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET)); + if (ret) + return ret; + + ret = regmap_write(adau1977->regmap, ADAU1977_REG_CMAP34, + (slot[3] << ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET) | + (slot[2] << ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET)); + if (ret) + return ret; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0, + ADAU1977_SAI_CTRL0_SAI_MASK, ctrl0); + if (ret) + return ret; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL1, + ADAU1977_SAI_CTRL1_SLOT_WIDTH_MASK, ctrl1); + if (ret) + return ret; + + adau1977->slot_width = width; + + /* In master mode the maximum bitclock is 24.576 MHz */ + adau1977->max_master_fs = min(192000, 24576000 / width / slots); + + return 0; +} + +static int adau1977_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int val; + + if (mute) + val = ADAU1977_MISC_CONTROL_MMUTE; + else + val = 0; + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MISC_CONTROL, + ADAU1977_MISC_CONTROL_MMUTE, val); +} + +static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int ctrl0 = 0, ctrl1 = 0, block_power = 0; + bool invert_lrclk; + int ret; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + adau1977->master = false; + break; + case SND_SOC_DAIFMT_CBM_CFM: + ctrl1 |= ADAU1977_SAI_CTRL1_MASTER; + adau1977->master = true; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_IB_NF: + block_power |= ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE; + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + invert_lrclk = true; + break; + case SND_SOC_DAIFMT_IB_IF: + block_power |= ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE; + invert_lrclk = true; + break; + default: + return -EINVAL; + } + + adau1977->right_j = false; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_LJ; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_24BIT; + adau1977->right_j = true; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1 |= ADAU1977_SAI_CTRL1_LRCLK_PULSE; + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_I2S; + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1 |= ADAU1977_SAI_CTRL1_LRCLK_PULSE; + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_LJ; + invert_lrclk = false; + break; + default: + return -EINVAL; + } + + if (invert_lrclk) + block_power |= ADAU1977_BLOCK_POWER_SAI_LR_POL; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI, + ADAU1977_BLOCK_POWER_SAI_LR_POL | + ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE, block_power); + if (ret) + return ret; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0, + ADAU1977_SAI_CTRL0_FMT_MASK, + ctrl0); + if (ret) + return ret; + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL1, + ADAU1977_SAI_CTRL1_MASTER | ADAU1977_SAI_CTRL1_LRCLK_PULSE, + ctrl1); +} + +static int adau1977_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec); + u64 formats = 0; + + if (adau1977->slot_width == 16) + formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE; + else if (adau1977->right_j || adau1977->slot_width == 24) + formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE; + + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &adau1977->constraints); + + if (adau1977->master) + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, 8000, adau1977->max_master_fs); + + if (formats != 0) + snd_pcm_hw_constraint_mask64(substream->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, formats); + + return 0; +} + +static int adau1977_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int val; + + if (tristate) + val = ADAU1977_SAI_OVERTEMP_DRV_HIZ; + else + val = 0; + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_OVERTEMP, + ADAU1977_SAI_OVERTEMP_DRV_HIZ, val); +} + +static const struct snd_soc_dai_ops adau1977_dai_ops = { + .startup = adau1977_startup, + .hw_params = adau1977_hw_params, + .mute_stream = adau1977_mute, + .set_fmt = adau1977_set_dai_fmt, + .set_tdm_slot = adau1977_set_tdm_slot, + .set_tristate = adau1977_set_tristate, +}; + +static struct snd_soc_dai_driver adau1977_dai = { + .name = "adau1977-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .sig_bits = 24, + }, + .ops = &adau1977_dai_ops, +}; + +static const unsigned int adau1977_rates[] = { + 8000, 16000, 32000, 64000, 128000, + 11025, 22050, 44100, 88200, 172400, + 12000, 24000, 48000, 96000, 192000, +}; + +#define ADAU1977_RATE_CONSTRAINT_MASK_32000 0x001f +#define ADAU1977_RATE_CONSTRAINT_MASK_44100 0x03e0 +#define ADAU1977_RATE_CONSTRAINT_MASK_48000 0x7c00 +/* All rates >= 32000 */ +#define ADAU1977_RATE_CONSTRAINT_MASK_LRCLK 0x739c + +static bool adau1977_check_sysclk(unsigned int mclk, unsigned int base_freq) +{ + unsigned int mcs; + + if (mclk % (base_freq * 128) != 0) + return false; + + mcs = mclk / (128 * base_freq); + if (mcs < 1 || mcs > 6 || mcs == 5) + return false; + + return true; +} + +static int adau1977_set_sysclk(struct snd_soc_codec *codec, + int clk_id, int source, unsigned int freq, int dir) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec); + unsigned int mask = 0; + unsigned int clk_src; + unsigned int ret; + + if (dir != SND_SOC_CLOCK_IN) + return -EINVAL; + + if (clk_id != ADAU1977_SYSCLK) + return -EINVAL; + + switch (source) { + case ADAU1977_SYSCLK_SRC_MCLK: + clk_src = 0; + break; + case ADAU1977_SYSCLK_SRC_LRCLK: + clk_src = ADAU1977_PLL_CLK_S; + break; + default: + return -EINVAL; + } + + if (freq != 0 && source == ADAU1977_SYSCLK_SRC_MCLK) { + if (freq < 4000000 || freq > 36864000) + return -EINVAL; + + if (adau1977_check_sysclk(freq, 32000)) + mask |= ADAU1977_RATE_CONSTRAINT_MASK_32000; + if (adau1977_check_sysclk(freq, 44100)) + mask |= ADAU1977_RATE_CONSTRAINT_MASK_44100; + if (adau1977_check_sysclk(freq, 48000)) + mask |= ADAU1977_RATE_CONSTRAINT_MASK_48000; + + if (mask == 0) + return -EINVAL; + } else if (source == ADAU1977_SYSCLK_SRC_LRCLK) { + mask = ADAU1977_RATE_CONSTRAINT_MASK_LRCLK; + } + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_PLL, + ADAU1977_PLL_CLK_S, clk_src); + if (ret) + return ret; + + adau1977->constraints.mask = mask; + adau1977->sysclk_src = source; + adau1977->sysclk = freq; + + return 0; +} + +static int adau1977_codec_probe(struct snd_soc_codec *codec) +{ + struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (adau1977->type) { + case ADAU1977: + ret = snd_soc_dapm_new_controls(&codec->dapm, + adau1977_micbias_dapm_widgets, + ARRAY_SIZE(adau1977_micbias_dapm_widgets)); + if (ret < 0) + return ret; + break; + default: + break; + } + + return 0; +} + +static struct snd_soc_codec_driver adau1977_codec_driver = { + .probe = adau1977_codec_probe, + .set_bias_level = adau1977_set_bias_level, + .set_sysclk = adau1977_set_sysclk, + .idle_bias_off = true, + + .controls = adau1977_snd_controls, + .num_controls = ARRAY_SIZE(adau1977_snd_controls), + .dapm_widgets = adau1977_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1977_dapm_widgets), + .dapm_routes = adau1977_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1977_dapm_routes), +}; + +static int adau1977_setup_micbias(struct adau1977 *adau1977) +{ + struct adau1977_platform_data *pdata = adau1977->dev->platform_data; + unsigned int micbias; + + if (pdata) { + micbias = pdata->micbias; + if (micbias > ADAU1977_MICBIAS_9V0) + return -EINVAL; + + } else { + micbias = ADAU1977_MICBIAS_8V5; + } + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MICBIAS, + ADAU1977_MICBIAS_MB_VOLTS_MASK, + micbias << ADAU1977_MICBIAS_MB_VOLTS_OFFSET); +} + +int adau1977_probe(struct device *dev, struct regmap *regmap, + enum adau1977_type type, void (*switch_mode)(struct device *dev)) +{ + unsigned int power_off_mask; + struct adau1977 *adau1977; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + adau1977 = devm_kzalloc(dev, sizeof(*adau1977), GFP_KERNEL); + if (adau1977 == NULL) + return -ENOMEM; + + adau1977->dev = dev; + adau1977->type = type; + adau1977->regmap = regmap; + adau1977->switch_mode = switch_mode; + adau1977->max_master_fs = 192000; + + adau1977->constraints.list = adau1977_rates; + adau1977->constraints.count = ARRAY_SIZE(adau1977_rates); + + adau1977->avdd_reg = devm_regulator_get(dev, "AVDD"); + if (IS_ERR(adau1977->avdd_reg)) + return PTR_ERR(adau1977->avdd_reg); + + adau1977->dvdd_reg = devm_regulator_get_optional(dev, "DVDD"); + if (IS_ERR(adau1977->dvdd_reg)) { + if (PTR_ERR(adau1977->dvdd_reg) != -ENODEV) + return PTR_ERR(adau1977->dvdd_reg); + adau1977->dvdd_reg = NULL; + } + + adau1977->reset_gpio = devm_gpiod_get(dev, "reset"); + if (IS_ERR(adau1977->reset_gpio)) { + ret = PTR_ERR(adau1977->reset_gpio); + if (ret != -ENOENT && ret != -ENOSYS) + return PTR_ERR(adau1977->reset_gpio); + adau1977->reset_gpio = NULL; + } + + dev_set_drvdata(dev, adau1977); + + if (adau1977->reset_gpio) { + ret = gpiod_direction_output(adau1977->reset_gpio, 0); + if (ret) + return ret; + ndelay(100); + } + + ret = adau1977_power_enable(adau1977); + if (ret) + return ret; + + if (type == ADAU1977) { + ret = adau1977_setup_micbias(adau1977); + if (ret) + goto err_poweroff; + } + + if (adau1977->dvdd_reg) + power_off_mask = ~0; + else + power_off_mask = ~ADAU1977_BLOCK_POWER_SAI_LDO_EN; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI, + power_off_mask, 0x00); + if (ret) + goto err_poweroff; + + ret = adau1977_power_disable(adau1977); + if (ret) + return ret; + + return snd_soc_register_codec(dev, &adau1977_codec_driver, + &adau1977_dai, 1); + +err_poweroff: + adau1977_power_disable(adau1977); + return ret; + +} +EXPORT_SYMBOL_GPL(adau1977_probe); + +static bool adau1977_register_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADAU1977_REG_STATUS(0): + case ADAU1977_REG_STATUS(1): + case ADAU1977_REG_STATUS(2): + case ADAU1977_REG_STATUS(3): + case ADAU1977_REG_ADC_CLIP: + return true; + } + + return false; +} + +const struct regmap_config adau1977_regmap_config = { + .max_register = ADAU1977_REG_DC_HPF_CAL, + .volatile_reg = adau1977_register_volatile, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = adau1977_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adau1977_reg_defaults), +}; +EXPORT_SYMBOL_GPL(adau1977_regmap_config); + +MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1977.h b/sound/soc/codecs/adau1977.h new file mode 100644 index 000000000000..95e714345a86 --- /dev/null +++ b/sound/soc/codecs/adau1977.h @@ -0,0 +1,37 @@ +/* + * ADAU1977/ADAU1978/ADAU1979 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2. + */ + +#ifndef __SOUND_SOC_CODECS_ADAU1977_H__ +#define __SOUND_SOC_CODECS_ADAU1977_H__ + +#include <linux/regmap.h> + +struct device; + +enum adau1977_type { + ADAU1977, + ADAU1978, + ADAU1979, +}; + +int adau1977_probe(struct device *dev, struct regmap *regmap, + enum adau1977_type type, void (*switch_mode)(struct device *dev)); + +extern const struct regmap_config adau1977_regmap_config; + +enum adau1977_clk_id { + ADAU1977_SYSCLK, +}; + +enum adau1977_sysclk_src { + ADAU1977_SYSCLK_SRC_MCLK, + ADAU1977_SYSCLK_SRC_LRCLK, +}; + +#endif diff --git a/sound/soc/codecs/adav801.c b/sound/soc/codecs/adav801.c new file mode 100644 index 000000000000..790fce33ab10 --- /dev/null +++ b/sound/soc/codecs/adav801.c @@ -0,0 +1,53 @@ +/* + * ADAV801 audio driver + * + * Copyright 2014 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/regmap.h> + +#include <sound/soc.h> + +#include "adav80x.h" + +static const struct spi_device_id adav80x_spi_id[] = { + { "adav801", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adav80x_spi_id); + +static int adav80x_spi_probe(struct spi_device *spi) +{ + struct regmap_config config; + + config = adav80x_regmap_config; + config.read_flag_mask = 0x01; + + return adav80x_bus_probe(&spi->dev, devm_regmap_init_spi(spi, &config)); +} + +static int adav80x_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static struct spi_driver adav80x_spi_driver = { + .driver = { + .name = "adav801", + .owner = THIS_MODULE, + }, + .probe = adav80x_spi_probe, + .remove = adav80x_spi_remove, + .id_table = adav80x_spi_id, +}; +module_spi_driver(adav80x_spi_driver); + +MODULE_DESCRIPTION("ASoC ADAV801 driver"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_AUTHOR("Yi Li <yi.li@analog.com>>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c new file mode 100644 index 000000000000..66d9fce34e62 --- /dev/null +++ b/sound/soc/codecs/adav803.c @@ -0,0 +1,50 @@ +/* + * ADAV803 audio driver + * + * Copyright 2014 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> + +#include <sound/soc.h> + +#include "adav80x.h" + +static const struct i2c_device_id adav803_id[] = { + { "adav803", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adav803_id); + +static int adav803_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return adav80x_bus_probe(&client->dev, + devm_regmap_init_i2c(client, &adav80x_regmap_config)); +} + +static int adav803_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static struct i2c_driver adav803_driver = { + .driver = { + .name = "adav803", + .owner = THIS_MODULE, + }, + .probe = adav803_probe, + .remove = adav803_remove, + .id_table = adav803_id, +}; +module_i2c_driver(adav803_driver); + +MODULE_DESCRIPTION("ASoC ADAV803 driver"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_AUTHOR("Yi Li <yi.li@analog.com>>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 14a7c169d004..7470831ba756 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -8,17 +8,15 @@ * Licensed under the GPL-2 or later. */ -#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.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> #include <sound/pcm_params.h> -#include <sound/tlv.h> #include <sound/soc.h> +#include <sound/tlv.h> #include "adav80x.h" @@ -453,22 +451,22 @@ static int adav80x_set_dac_clock(struct snd_soc_codec *codec, } static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec, - struct snd_soc_dai *dai, snd_pcm_format_t format) + struct snd_soc_dai *dai, struct snd_pcm_hw_params *params) { struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); unsigned int val; - switch (format) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: val = ADAV80X_CAPTURE_WORD_LEN16; break; - case SNDRV_PCM_FORMAT_S18_3LE: + case 18: val = ADAV80X_CAPTRUE_WORD_LEN18; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: val = ADAV80X_CAPTURE_WORD_LEN20; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: val = ADAV80X_CAPTURE_WORD_LEN24; break; default: @@ -482,7 +480,7 @@ static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec, } static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec, - struct snd_soc_dai *dai, snd_pcm_format_t format) + struct snd_soc_dai *dai, struct snd_pcm_hw_params *params) { struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); unsigned int val; @@ -490,17 +488,17 @@ static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec, if (adav80x->dai_fmt[dai->id] != SND_SOC_DAIFMT_RIGHT_J) return 0; - switch (format) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: val = ADAV80X_PLAYBACK_MODE_RIGHT_J_16; break; - case SNDRV_PCM_FORMAT_S18_3LE: + case 18: val = ADAV80X_PLAYBACK_MODE_RIGHT_J_18; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: val = ADAV80X_PLAYBACK_MODE_RIGHT_J_20; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: val = ADAV80X_PLAYBACK_MODE_RIGHT_J_24; break; default: @@ -524,12 +522,10 @@ static int adav80x_hw_params(struct snd_pcm_substream *substream, return -EINVAL; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - adav80x_set_playback_pcm_format(codec, dai, - params_format(params)); + adav80x_set_playback_pcm_format(codec, dai, params); adav80x_set_dac_clock(codec, rate); } else { - adav80x_set_capture_pcm_format(codec, dai, - params_format(params)); + adav80x_set_capture_pcm_format(codec, dai, params); adav80x_set_adc_clock(codec, rate); } adav80x->rate = rate; @@ -543,6 +539,7 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec, unsigned int freq, int dir) { struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; if (dir == SND_SOC_CLOCK_IN) { switch (clk_id) { @@ -575,7 +572,7 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec, regmap_write(adav80x->regmap, ADAV80X_ICLK_CTRL2, iclk_ctrl2); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); } } else { unsigned int mask; @@ -602,17 +599,21 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec, adav80x->sysclk_pd[clk_id] = false; } + snd_soc_dapm_mutex_lock(dapm); + if (adav80x->sysclk_pd[0]) - snd_soc_dapm_disable_pin(&codec->dapm, "PLL1"); + snd_soc_dapm_disable_pin_unlocked(dapm, "PLL1"); else - snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL1"); if (adav80x->sysclk_pd[1] || adav80x->sysclk_pd[2]) - snd_soc_dapm_disable_pin(&codec->dapm, "PLL2"); + snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2"); else - snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); } return 0; @@ -724,7 +725,7 @@ static int adav80x_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); - if (!codec->active || !adav80x->rate) + if (!snd_soc_codec_is_active(codec) || !adav80x->rate) return 0; return snd_pcm_hw_constraint_minmax(substream->runtime, @@ -737,7 +738,7 @@ static void adav80x_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); - if (!codec->active) + if (!snd_soc_codec_is_active(codec)) adav80x->rate = 0; } @@ -866,39 +867,26 @@ static struct snd_soc_codec_driver adav80x_codec_driver = { .num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes), }; -static int adav80x_bus_probe(struct device *dev, struct regmap *regmap) +int adav80x_bus_probe(struct device *dev, struct regmap *regmap) { struct adav80x *adav80x; - int ret; if (IS_ERR(regmap)) return PTR_ERR(regmap); - adav80x = kzalloc(sizeof(*adav80x), GFP_KERNEL); + adav80x = devm_kzalloc(dev, sizeof(*adav80x), GFP_KERNEL); if (!adav80x) return -ENOMEM; - dev_set_drvdata(dev, adav80x); adav80x->regmap = regmap; - ret = snd_soc_register_codec(dev, &adav80x_codec_driver, + return snd_soc_register_codec(dev, &adav80x_codec_driver, adav80x_dais, ARRAY_SIZE(adav80x_dais)); - if (ret) - kfree(adav80x); - - return ret; } +EXPORT_SYMBOL_GPL(adav80x_bus_probe); -static int adav80x_bus_remove(struct device *dev) -{ - snd_soc_unregister_codec(dev); - kfree(dev_get_drvdata(dev)); - return 0; -} - -#if defined(CONFIG_SPI_MASTER) -static const struct regmap_config adav80x_spi_regmap_config = { +const struct regmap_config adav80x_regmap_config = { .val_bits = 8, .pad_bits = 1, .reg_bits = 7, @@ -910,105 +898,7 @@ static const struct regmap_config adav80x_spi_regmap_config = { .reg_defaults = adav80x_reg_defaults, .num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults), }; - -static const struct spi_device_id adav80x_spi_id[] = { - { "adav801", 0 }, - { } -}; -MODULE_DEVICE_TABLE(spi, adav80x_spi_id); - -static int adav80x_spi_probe(struct spi_device *spi) -{ - return adav80x_bus_probe(&spi->dev, - devm_regmap_init_spi(spi, &adav80x_spi_regmap_config)); -} - -static int adav80x_spi_remove(struct spi_device *spi) -{ - return adav80x_bus_remove(&spi->dev); -} - -static struct spi_driver adav80x_spi_driver = { - .driver = { - .name = "adav801", - .owner = THIS_MODULE, - }, - .probe = adav80x_spi_probe, - .remove = adav80x_spi_remove, - .id_table = adav80x_spi_id, -}; -#endif - -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -static const struct regmap_config adav80x_i2c_regmap_config = { - .val_bits = 8, - .pad_bits = 1, - .reg_bits = 7, - - .max_register = ADAV80X_PLL_OUTE, - - .cache_type = REGCACHE_RBTREE, - .reg_defaults = adav80x_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults), -}; - -static const struct i2c_device_id adav80x_i2c_id[] = { - { "adav803", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, adav80x_i2c_id); - -static int adav80x_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - return adav80x_bus_probe(&client->dev, - devm_regmap_init_i2c(client, &adav80x_i2c_regmap_config)); -} - -static int adav80x_i2c_remove(struct i2c_client *client) -{ - return adav80x_bus_remove(&client->dev); -} - -static struct i2c_driver adav80x_i2c_driver = { - .driver = { - .name = "adav803", - .owner = THIS_MODULE, - }, - .probe = adav80x_i2c_probe, - .remove = adav80x_i2c_remove, - .id_table = adav80x_i2c_id, -}; -#endif - -static int __init adav80x_init(void) -{ - int ret = 0; - -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - ret = i2c_add_driver(&adav80x_i2c_driver); - if (ret) - return ret; -#endif - -#if defined(CONFIG_SPI_MASTER) - ret = spi_register_driver(&adav80x_spi_driver); -#endif - - return ret; -} -module_init(adav80x_init); - -static void __exit adav80x_exit(void) -{ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_del_driver(&adav80x_i2c_driver); -#endif -#if defined(CONFIG_SPI_MASTER) - spi_unregister_driver(&adav80x_spi_driver); -#endif -} -module_exit(adav80x_exit); +EXPORT_SYMBOL_GPL(adav80x_regmap_config); MODULE_DESCRIPTION("ASoC ADAV80x driver"); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); diff --git a/sound/soc/codecs/adav80x.h b/sound/soc/codecs/adav80x.h index adb0fc76d4e3..8a1d7c09dca5 100644 --- a/sound/soc/codecs/adav80x.h +++ b/sound/soc/codecs/adav80x.h @@ -9,6 +9,13 @@ #ifndef _ADAV80X_H #define _ADAV80X_H +#include <linux/regmap.h> + +struct device; + +extern const struct regmap_config adav80x_regmap_config; +int adav80x_bus_probe(struct device *dev, struct regmap *regmap); + enum adav80x_pll_src { ADAV80X_PLL_SRC_XIN, ADAV80X_PLL_SRC_XTAL, diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index b4819dcd4f4d..10adf25d4c14 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -174,8 +174,6 @@ static int ak4104_probe(struct snd_soc_codec *codec) struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec); int ret; - codec->control_data = ak4104->regmap; - /* set power-up and non-reset bits */ ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1, AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN, diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 49cc5f6d6dba..684b56f2856a 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}; @@ -111,14 +113,14 @@ static const DECLARE_TLV_DB_SCALE(alc_tlv, -800, 50, 0); static const DECLARE_TLV_DB_SCALE(aux_in_tlv, -2100, 300, 0); -static const struct soc_enum ak4641_mono_out_enum = - SOC_ENUM_SINGLE(AK4641_SIG1, 6, 2, ak4641_mono_out); -static const struct soc_enum ak4641_hp_out_enum = - SOC_ENUM_SINGLE(AK4641_MODE2, 2, 2, ak4641_hp_out); -static const struct soc_enum ak4641_mic_select_enum = - SOC_ENUM_SINGLE(AK4641_MIC, 1, 2, ak4641_mic_select); -static const struct soc_enum ak4641_mic_or_dac_enum = - SOC_ENUM_SINGLE(AK4641_BTIF, 4, 2, ak4641_mic_or_dac); +static SOC_ENUM_SINGLE_DECL(ak4641_mono_out_enum, + AK4641_SIG1, 6, ak4641_mono_out); +static SOC_ENUM_SINGLE_DECL(ak4641_hp_out_enum, + AK4641_MODE2, 2, ak4641_hp_out); +static SOC_ENUM_SINGLE_DECL(ak4641_mic_select_enum, + AK4641_MIC, 1, ak4641_mic_select); +static SOC_ENUM_SINGLE_DECL(ak4641_mic_or_dac_enum, + AK4641_BTIF, 4, ak4641_mic_or_dac); static const struct snd_kcontrol_new ak4641_snd_controls[] = { SOC_ENUM("Mono 1 Output", ak4641_mono_out_enum), @@ -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/ak4671.c b/sound/soc/codecs/ak4671.c index 25bdf6ad4a54..deb2b44669de 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -15,6 +15,7 @@ #include <linux/init.h> #include <linux/i2c.h> #include <linux/delay.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <sound/soc.h> #include <sound/initval.h> @@ -23,104 +24,99 @@ #include "ak4671.h" -/* codec private data */ -struct ak4671_priv { - enum snd_soc_control_type control_type; -}; - /* ak4671 register cache & default register settings */ -static const u8 ak4671_reg[AK4671_CACHEREGNUM] = { - 0x00, /* AK4671_AD_DA_POWER_MANAGEMENT (0x00) */ - 0xf6, /* AK4671_PLL_MODE_SELECT0 (0x01) */ - 0x00, /* AK4671_PLL_MODE_SELECT1 (0x02) */ - 0x02, /* AK4671_FORMAT_SELECT (0x03) */ - 0x00, /* AK4671_MIC_SIGNAL_SELECT (0x04) */ - 0x55, /* AK4671_MIC_AMP_GAIN (0x05) */ - 0x00, /* AK4671_MIXING_POWER_MANAGEMENT0 (0x06) */ - 0x00, /* AK4671_MIXING_POWER_MANAGEMENT1 (0x07) */ - 0xb5, /* AK4671_OUTPUT_VOLUME_CONTROL (0x08) */ - 0x00, /* AK4671_LOUT1_SIGNAL_SELECT (0x09) */ - 0x00, /* AK4671_ROUT1_SIGNAL_SELECT (0x0a) */ - 0x00, /* AK4671_LOUT2_SIGNAL_SELECT (0x0b) */ - 0x00, /* AK4671_ROUT2_SIGNAL_SELECT (0x0c) */ - 0x00, /* AK4671_LOUT3_SIGNAL_SELECT (0x0d) */ - 0x00, /* AK4671_ROUT3_SIGNAL_SELECT (0x0e) */ - 0x00, /* AK4671_LOUT1_POWER_MANAGERMENT (0x0f) */ - 0x00, /* AK4671_LOUT2_POWER_MANAGERMENT (0x10) */ - 0x80, /* AK4671_LOUT3_POWER_MANAGERMENT (0x11) */ - 0x91, /* AK4671_LCH_INPUT_VOLUME_CONTROL (0x12) */ - 0x91, /* AK4671_RCH_INPUT_VOLUME_CONTROL (0x13) */ - 0xe1, /* AK4671_ALC_REFERENCE_SELECT (0x14) */ - 0x00, /* AK4671_DIGITAL_MIXING_CONTROL (0x15) */ - 0x00, /* AK4671_ALC_TIMER_SELECT (0x16) */ - 0x00, /* AK4671_ALC_MODE_CONTROL (0x17) */ - 0x02, /* AK4671_MODE_CONTROL1 (0x18) */ - 0x01, /* AK4671_MODE_CONTROL2 (0x19) */ - 0x18, /* AK4671_LCH_OUTPUT_VOLUME_CONTROL (0x1a) */ - 0x18, /* AK4671_RCH_OUTPUT_VOLUME_CONTROL (0x1b) */ - 0x00, /* AK4671_SIDETONE_A_CONTROL (0x1c) */ - 0x02, /* AK4671_DIGITAL_FILTER_SELECT (0x1d) */ - 0x00, /* AK4671_FIL3_COEFFICIENT0 (0x1e) */ - 0x00, /* AK4671_FIL3_COEFFICIENT1 (0x1f) */ - 0x00, /* AK4671_FIL3_COEFFICIENT2 (0x20) */ - 0x00, /* AK4671_FIL3_COEFFICIENT3 (0x21) */ - 0x00, /* AK4671_EQ_COEFFICIENT0 (0x22) */ - 0x00, /* AK4671_EQ_COEFFICIENT1 (0x23) */ - 0x00, /* AK4671_EQ_COEFFICIENT2 (0x24) */ - 0x00, /* AK4671_EQ_COEFFICIENT3 (0x25) */ - 0x00, /* AK4671_EQ_COEFFICIENT4 (0x26) */ - 0x00, /* AK4671_EQ_COEFFICIENT5 (0x27) */ - 0xa9, /* AK4671_FIL1_COEFFICIENT0 (0x28) */ - 0x1f, /* AK4671_FIL1_COEFFICIENT1 (0x29) */ - 0xad, /* AK4671_FIL1_COEFFICIENT2 (0x2a) */ - 0x20, /* AK4671_FIL1_COEFFICIENT3 (0x2b) */ - 0x00, /* AK4671_FIL2_COEFFICIENT0 (0x2c) */ - 0x00, /* AK4671_FIL2_COEFFICIENT1 (0x2d) */ - 0x00, /* AK4671_FIL2_COEFFICIENT2 (0x2e) */ - 0x00, /* AK4671_FIL2_COEFFICIENT3 (0x2f) */ - 0x00, /* AK4671_DIGITAL_FILTER_SELECT2 (0x30) */ - 0x00, /* this register not used */ - 0x00, /* AK4671_E1_COEFFICIENT0 (0x32) */ - 0x00, /* AK4671_E1_COEFFICIENT1 (0x33) */ - 0x00, /* AK4671_E1_COEFFICIENT2 (0x34) */ - 0x00, /* AK4671_E1_COEFFICIENT3 (0x35) */ - 0x00, /* AK4671_E1_COEFFICIENT4 (0x36) */ - 0x00, /* AK4671_E1_COEFFICIENT5 (0x37) */ - 0x00, /* AK4671_E2_COEFFICIENT0 (0x38) */ - 0x00, /* AK4671_E2_COEFFICIENT1 (0x39) */ - 0x00, /* AK4671_E2_COEFFICIENT2 (0x3a) */ - 0x00, /* AK4671_E2_COEFFICIENT3 (0x3b) */ - 0x00, /* AK4671_E2_COEFFICIENT4 (0x3c) */ - 0x00, /* AK4671_E2_COEFFICIENT5 (0x3d) */ - 0x00, /* AK4671_E3_COEFFICIENT0 (0x3e) */ - 0x00, /* AK4671_E3_COEFFICIENT1 (0x3f) */ - 0x00, /* AK4671_E3_COEFFICIENT2 (0x40) */ - 0x00, /* AK4671_E3_COEFFICIENT3 (0x41) */ - 0x00, /* AK4671_E3_COEFFICIENT4 (0x42) */ - 0x00, /* AK4671_E3_COEFFICIENT5 (0x43) */ - 0x00, /* AK4671_E4_COEFFICIENT0 (0x44) */ - 0x00, /* AK4671_E4_COEFFICIENT1 (0x45) */ - 0x00, /* AK4671_E4_COEFFICIENT2 (0x46) */ - 0x00, /* AK4671_E4_COEFFICIENT3 (0x47) */ - 0x00, /* AK4671_E4_COEFFICIENT4 (0x48) */ - 0x00, /* AK4671_E4_COEFFICIENT5 (0x49) */ - 0x00, /* AK4671_E5_COEFFICIENT0 (0x4a) */ - 0x00, /* AK4671_E5_COEFFICIENT1 (0x4b) */ - 0x00, /* AK4671_E5_COEFFICIENT2 (0x4c) */ - 0x00, /* AK4671_E5_COEFFICIENT3 (0x4d) */ - 0x00, /* AK4671_E5_COEFFICIENT4 (0x4e) */ - 0x00, /* AK4671_E5_COEFFICIENT5 (0x4f) */ - 0x88, /* AK4671_EQ_CONTROL_250HZ_100HZ (0x50) */ - 0x88, /* AK4671_EQ_CONTROL_3500HZ_1KHZ (0x51) */ - 0x08, /* AK4671_EQ_CONTRO_10KHZ (0x52) */ - 0x00, /* AK4671_PCM_IF_CONTROL0 (0x53) */ - 0x00, /* AK4671_PCM_IF_CONTROL1 (0x54) */ - 0x00, /* AK4671_PCM_IF_CONTROL2 (0x55) */ - 0x18, /* AK4671_DIGITAL_VOLUME_B_CONTROL (0x56) */ - 0x18, /* AK4671_DIGITAL_VOLUME_C_CONTROL (0x57) */ - 0x00, /* AK4671_SIDETONE_VOLUME_CONTROL (0x58) */ - 0x00, /* AK4671_DIGITAL_MIXING_CONTROL2 (0x59) */ - 0x00, /* AK4671_SAR_ADC_CONTROL (0x5a) */ +static const struct reg_default ak4671_reg_defaults[] = { + { 0x00, 0x00 }, /* AK4671_AD_DA_POWER_MANAGEMENT (0x00) */ + { 0x01, 0xf6 }, /* AK4671_PLL_MODE_SELECT0 (0x01) */ + { 0x02, 0x00 }, /* AK4671_PLL_MODE_SELECT1 (0x02) */ + { 0x03, 0x02 }, /* AK4671_FORMAT_SELECT (0x03) */ + { 0x04, 0x00 }, /* AK4671_MIC_SIGNAL_SELECT (0x04) */ + { 0x05, 0x55 }, /* AK4671_MIC_AMP_GAIN (0x05) */ + { 0x06, 0x00 }, /* AK4671_MIXING_POWER_MANAGEMENT0 (0x06) */ + { 0x07, 0x00 }, /* AK4671_MIXING_POWER_MANAGEMENT1 (0x07) */ + { 0x08, 0xb5 }, /* AK4671_OUTPUT_VOLUME_CONTROL (0x08) */ + { 0x09, 0x00 }, /* AK4671_LOUT1_SIGNAL_SELECT (0x09) */ + { 0x0a, 0x00 }, /* AK4671_ROUT1_SIGNAL_SELECT (0x0a) */ + { 0x0b, 0x00 }, /* AK4671_LOUT2_SIGNAL_SELECT (0x0b) */ + { 0x0c, 0x00 }, /* AK4671_ROUT2_SIGNAL_SELECT (0x0c) */ + { 0x0d, 0x00 }, /* AK4671_LOUT3_SIGNAL_SELECT (0x0d) */ + { 0x0e, 0x00 }, /* AK4671_ROUT3_SIGNAL_SELECT (0x0e) */ + { 0x0f, 0x00 }, /* AK4671_LOUT1_POWER_MANAGERMENT (0x0f) */ + { 0x10, 0x00 }, /* AK4671_LOUT2_POWER_MANAGERMENT (0x10) */ + { 0x11, 0x80 }, /* AK4671_LOUT3_POWER_MANAGERMENT (0x11) */ + { 0x12, 0x91 }, /* AK4671_LCH_INPUT_VOLUME_CONTROL (0x12) */ + { 0x13, 0x91 }, /* AK4671_RCH_INPUT_VOLUME_CONTROL (0x13) */ + { 0x14, 0xe1 }, /* AK4671_ALC_REFERENCE_SELECT (0x14) */ + { 0x15, 0x00 }, /* AK4671_DIGITAL_MIXING_CONTROL (0x15) */ + { 0x16, 0x00 }, /* AK4671_ALC_TIMER_SELECT (0x16) */ + { 0x17, 0x00 }, /* AK4671_ALC_MODE_CONTROL (0x17) */ + { 0x18, 0x02 }, /* AK4671_MODE_CONTROL1 (0x18) */ + { 0x19, 0x01 }, /* AK4671_MODE_CONTROL2 (0x19) */ + { 0x1a, 0x18 }, /* AK4671_LCH_OUTPUT_VOLUME_CONTROL (0x1a) */ + { 0x1b, 0x18 }, /* AK4671_RCH_OUTPUT_VOLUME_CONTROL (0x1b) */ + { 0x1c, 0x00 }, /* AK4671_SIDETONE_A_CONTROL (0x1c) */ + { 0x1d, 0x02 }, /* AK4671_DIGITAL_FILTER_SELECT (0x1d) */ + { 0x1e, 0x00 }, /* AK4671_FIL3_COEFFICIENT0 (0x1e) */ + { 0x1f, 0x00 }, /* AK4671_FIL3_COEFFICIENT1 (0x1f) */ + { 0x20, 0x00 }, /* AK4671_FIL3_COEFFICIENT2 (0x20) */ + { 0x21, 0x00 }, /* AK4671_FIL3_COEFFICIENT3 (0x21) */ + { 0x22, 0x00 }, /* AK4671_EQ_COEFFICIENT0 (0x22) */ + { 0x23, 0x00 }, /* AK4671_EQ_COEFFICIENT1 (0x23) */ + { 0x24, 0x00 }, /* AK4671_EQ_COEFFICIENT2 (0x24) */ + { 0x25, 0x00 }, /* AK4671_EQ_COEFFICIENT3 (0x25) */ + { 0x26, 0x00 }, /* AK4671_EQ_COEFFICIENT4 (0x26) */ + { 0x27, 0x00 }, /* AK4671_EQ_COEFFICIENT5 (0x27) */ + { 0x28, 0xa9 }, /* AK4671_FIL1_COEFFICIENT0 (0x28) */ + { 0x29, 0x1f }, /* AK4671_FIL1_COEFFICIENT1 (0x29) */ + { 0x2a, 0xad }, /* AK4671_FIL1_COEFFICIENT2 (0x2a) */ + { 0x2b, 0x20 }, /* AK4671_FIL1_COEFFICIENT3 (0x2b) */ + { 0x2c, 0x00 }, /* AK4671_FIL2_COEFFICIENT0 (0x2c) */ + { 0x2d, 0x00 }, /* AK4671_FIL2_COEFFICIENT1 (0x2d) */ + { 0x2e, 0x00 }, /* AK4671_FIL2_COEFFICIENT2 (0x2e) */ + { 0x2f, 0x00 }, /* AK4671_FIL2_COEFFICIENT3 (0x2f) */ + { 0x30, 0x00 }, /* AK4671_DIGITAL_FILTER_SELECT2 (0x30) */ + + { 0x32, 0x00 }, /* AK4671_E1_COEFFICIENT0 (0x32) */ + { 0x33, 0x00 }, /* AK4671_E1_COEFFICIENT1 (0x33) */ + { 0x34, 0x00 }, /* AK4671_E1_COEFFICIENT2 (0x34) */ + { 0x35, 0x00 }, /* AK4671_E1_COEFFICIENT3 (0x35) */ + { 0x36, 0x00 }, /* AK4671_E1_COEFFICIENT4 (0x36) */ + { 0x37, 0x00 }, /* AK4671_E1_COEFFICIENT5 (0x37) */ + { 0x38, 0x00 }, /* AK4671_E2_COEFFICIENT0 (0x38) */ + { 0x39, 0x00 }, /* AK4671_E2_COEFFICIENT1 (0x39) */ + { 0x3a, 0x00 }, /* AK4671_E2_COEFFICIENT2 (0x3a) */ + { 0x3b, 0x00 }, /* AK4671_E2_COEFFICIENT3 (0x3b) */ + { 0x3c, 0x00 }, /* AK4671_E2_COEFFICIENT4 (0x3c) */ + { 0x3d, 0x00 }, /* AK4671_E2_COEFFICIENT5 (0x3d) */ + { 0x3e, 0x00 }, /* AK4671_E3_COEFFICIENT0 (0x3e) */ + { 0x3f, 0x00 }, /* AK4671_E3_COEFFICIENT1 (0x3f) */ + { 0x40, 0x00 }, /* AK4671_E3_COEFFICIENT2 (0x40) */ + { 0x41, 0x00 }, /* AK4671_E3_COEFFICIENT3 (0x41) */ + { 0x42, 0x00 }, /* AK4671_E3_COEFFICIENT4 (0x42) */ + { 0x43, 0x00 }, /* AK4671_E3_COEFFICIENT5 (0x43) */ + { 0x44, 0x00 }, /* AK4671_E4_COEFFICIENT0 (0x44) */ + { 0x45, 0x00 }, /* AK4671_E4_COEFFICIENT1 (0x45) */ + { 0x46, 0x00 }, /* AK4671_E4_COEFFICIENT2 (0x46) */ + { 0x47, 0x00 }, /* AK4671_E4_COEFFICIENT3 (0x47) */ + { 0x48, 0x00 }, /* AK4671_E4_COEFFICIENT4 (0x48) */ + { 0x49, 0x00 }, /* AK4671_E4_COEFFICIENT5 (0x49) */ + { 0x4a, 0x00 }, /* AK4671_E5_COEFFICIENT0 (0x4a) */ + { 0x4b, 0x00 }, /* AK4671_E5_COEFFICIENT1 (0x4b) */ + { 0x4c, 0x00 }, /* AK4671_E5_COEFFICIENT2 (0x4c) */ + { 0x4d, 0x00 }, /* AK4671_E5_COEFFICIENT3 (0x4d) */ + { 0x4e, 0x00 }, /* AK4671_E5_COEFFICIENT4 (0x4e) */ + { 0x4f, 0x00 }, /* AK4671_E5_COEFFICIENT5 (0x4f) */ + { 0x50, 0x88 }, /* AK4671_EQ_CONTROL_250HZ_100HZ (0x50) */ + { 0x51, 0x88 }, /* AK4671_EQ_CONTROL_3500HZ_1KHZ (0x51) */ + { 0x52, 0x08 }, /* AK4671_EQ_CONTRO_10KHZ (0x52) */ + { 0x53, 0x00 }, /* AK4671_PCM_IF_CONTROL0 (0x53) */ + { 0x54, 0x00 }, /* AK4671_PCM_IF_CONTROL1 (0x54) */ + { 0x55, 0x00 }, /* AK4671_PCM_IF_CONTROL2 (0x55) */ + { 0x56, 0x18 }, /* AK4671_DIGITAL_VOLUME_B_CONTROL (0x56) */ + { 0x57, 0x18 }, /* AK4671_DIGITAL_VOLUME_C_CONTROL (0x57) */ + { 0x58, 0x00 }, /* AK4671_SIDETONE_VOLUME_CONTROL (0x58) */ + { 0x59, 0x00 }, /* AK4671_DIGITAL_MIXING_CONTROL2 (0x59) */ + { 0x5a, 0x00 }, /* AK4671_SAR_ADC_CONTROL (0x5a) */ }; /* @@ -241,19 +237,17 @@ static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = { /* Input MUXs */ static const char *ak4671_lin_mux_texts[] = {"LIN1", "LIN2", "LIN3", "LIN4"}; -static const struct soc_enum ak4671_lin_mux_enum = - SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 0, - ARRAY_SIZE(ak4671_lin_mux_texts), - ak4671_lin_mux_texts); +static SOC_ENUM_SINGLE_DECL(ak4671_lin_mux_enum, + AK4671_MIC_SIGNAL_SELECT, 0, + ak4671_lin_mux_texts); static const struct snd_kcontrol_new ak4671_lin_mux_control = SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum); static const char *ak4671_rin_mux_texts[] = {"RIN1", "RIN2", "RIN3", "RIN4"}; -static const struct soc_enum ak4671_rin_mux_enum = - SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 2, - ARRAY_SIZE(ak4671_rin_mux_texts), - ak4671_rin_mux_texts); +static SOC_ENUM_SINGLE_DECL(ak4671_rin_mux_enum, + AK4671_MIC_SIGNAL_SELECT, 2, + ak4671_rin_mux_texts); static const struct snd_kcontrol_new ak4671_rin_mux_control = SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum); @@ -619,18 +613,14 @@ static struct snd_soc_dai_driver ak4671_dai = { static int ak4671_probe(struct snd_soc_codec *codec) { - struct ak4671_priv *ak4671 = snd_soc_codec_get_drvdata(codec); int ret; - ret = snd_soc_codec_set_cache_io(codec, 8, 8, ak4671->control_type); + 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, ak4671_snd_controls, - ARRAY_SIZE(ak4671_snd_controls)); - ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return ret; @@ -646,28 +636,36 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4671 = { .probe = ak4671_probe, .remove = ak4671_remove, .set_bias_level = ak4671_set_bias_level, - .reg_cache_size = AK4671_CACHEREGNUM, - .reg_word_size = sizeof(u8), - .reg_cache_default = ak4671_reg, + .controls = ak4671_snd_controls, + .num_controls = ARRAY_SIZE(ak4671_snd_controls), .dapm_widgets = ak4671_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets), .dapm_routes = ak4671_intercon, .num_dapm_routes = ARRAY_SIZE(ak4671_intercon), }; +static const struct regmap_config ak4671_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = AK4671_SAR_ADC_CONTROL, + .reg_defaults = ak4671_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4671_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + static int ak4671_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct ak4671_priv *ak4671; + struct regmap *regmap; int ret; - ak4671 = devm_kzalloc(&client->dev, sizeof(struct ak4671_priv), - GFP_KERNEL); - if (ak4671 == NULL) - return -ENOMEM; - - i2c_set_clientdata(client, ak4671); - ak4671->control_type = SND_SOC_I2C; + regmap = devm_regmap_init_i2c(client, &ak4671_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&client->dev, "Failed to create regmap: %d\n", ret); + return ret; + } ret = snd_soc_register_codec(&client->dev, &soc_codec_dev_ak4671, &ak4671_dai, 1); diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h index 61cb7ab7552c..394a34d3f50a 100644 --- a/sound/soc/codecs/ak4671.h +++ b/sound/soc/codecs/ak4671.h @@ -105,8 +105,6 @@ #define AK4671_DIGITAL_MIXING_CONTROL2 0x59 #define AK4671_SAR_ADC_CONTROL 0x5a -#define AK4671_CACHEREGNUM (AK4671_SAR_ADC_CONTROL + 1) - /* Bitfield Definitions */ /* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */ diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 256c364193a5..ed506253a914 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -21,6 +21,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> @@ -38,26 +39,13 @@ MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)"); /* codec private data */ struct alc5623_priv { - enum snd_soc_control_type control_type; + struct regmap *regmap; u8 id; unsigned int sysclk; - u16 reg_cache[ALC5623_VENDOR_ID2+2]; unsigned int add_ctrl; unsigned int jack_det_ctrl; }; -static void alc5623_fill_cache(struct snd_soc_codec *codec) -{ - int i, step = codec->driver->reg_cache_step; - u16 *cache = codec->reg_cache; - - /* not really efficient ... */ - codec->cache_bypass = 1; - for (i = 0 ; i < codec->driver->reg_cache_size ; i += step) - cache[i] = snd_soc_read(codec, i); - codec->cache_bypass = 0; -} - static inline int alc5623_reset(struct snd_soc_codec *codec) { return snd_soc_write(codec, ALC5623_RESET, 0); @@ -228,32 +216,37 @@ static const char *alc5623_aux_out_input_sel[] = { "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"}; /* auxout output mux */ -static const struct soc_enum alc5623_aux_out_input_enum = -SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 6, 4, alc5623_aux_out_input_sel); +static SOC_ENUM_SINGLE_DECL(alc5623_aux_out_input_enum, + ALC5623_OUTPUT_MIXER_CTRL, 6, + alc5623_aux_out_input_sel); static const struct snd_kcontrol_new alc5623_auxout_mux_controls = SOC_DAPM_ENUM("Route", alc5623_aux_out_input_enum); /* speaker output mux */ -static const struct soc_enum alc5623_spkout_input_enum = -SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 10, 4, alc5623_spkout_input_sel); +static SOC_ENUM_SINGLE_DECL(alc5623_spkout_input_enum, + ALC5623_OUTPUT_MIXER_CTRL, 10, + alc5623_spkout_input_sel); static const struct snd_kcontrol_new alc5623_spkout_mux_controls = SOC_DAPM_ENUM("Route", alc5623_spkout_input_enum); /* headphone left output mux */ -static const struct soc_enum alc5623_hpl_out_input_enum = -SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 9, 2, alc5623_hpl_out_input_sel); +static SOC_ENUM_SINGLE_DECL(alc5623_hpl_out_input_enum, + ALC5623_OUTPUT_MIXER_CTRL, 9, + alc5623_hpl_out_input_sel); static const struct snd_kcontrol_new alc5623_hpl_out_mux_controls = SOC_DAPM_ENUM("Route", alc5623_hpl_out_input_enum); /* headphone right output mux */ -static const struct soc_enum alc5623_hpr_out_input_enum = -SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 8, 2, alc5623_hpr_out_input_sel); +static SOC_ENUM_SINGLE_DECL(alc5623_hpr_out_input_enum, + ALC5623_OUTPUT_MIXER_CTRL, 8, + alc5623_hpr_out_input_sel); static const struct snd_kcontrol_new alc5623_hpr_out_mux_controls = SOC_DAPM_ENUM("Route", alc5623_hpr_out_input_enum); /* speaker output N select */ -static const struct soc_enum alc5623_spk_n_sour_enum = -SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 14, 4, alc5623_spk_n_sour_sel); +static SOC_ENUM_SINGLE_DECL(alc5623_spk_n_sour_enum, + ALC5623_OUTPUT_MIXER_CTRL, 14, + alc5623_spk_n_sour_sel); static const struct snd_kcontrol_new alc5623_spkoutn_mux_controls = SOC_DAPM_ENUM("Route", alc5623_spk_n_sour_enum); @@ -338,8 +331,9 @@ SND_SOC_DAPM_VMID("Vmid"), }; static const char *alc5623_amp_names[] = {"AB Amp", "D Amp"}; -static const struct soc_enum alc5623_amp_enum = - SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 13, 2, alc5623_amp_names); +static SOC_ENUM_SINGLE_DECL(alc5623_amp_enum, + ALC5623_OUTPUT_MIXER_CTRL, 13, + alc5623_amp_names); static const struct snd_kcontrol_new alc5623_amp_mux_controls = SOC_DAPM_ENUM("Route", alc5623_amp_enum); @@ -714,17 +708,17 @@ static int alc5623_pcm_hw_params(struct snd_pcm_substream *substream, iface &= ~ALC5623_DAI_I2S_DL_MASK; /* bit size */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: iface |= ALC5623_DAI_I2S_DL_16; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: iface |= ALC5623_DAI_I2S_DL_20; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: iface |= ALC5623_DAI_I2S_DL_24; break; - case SNDRV_PCM_FORMAT_S32_LE: + case 32: iface |= ALC5623_DAI_I2S_DL_32; break; default: @@ -869,18 +863,28 @@ static struct snd_soc_dai_driver alc5623_dai = { static int alc5623_suspend(struct snd_soc_codec *codec) { + struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); + alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF); + regcache_cache_only(alc5623->regmap, true); + return 0; } static int alc5623_resume(struct snd_soc_codec *codec) { - int i, step = codec->driver->reg_cache_step; - u16 *cache = codec->reg_cache; + struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); + int ret; /* Sync reg_cache with the hardware */ - for (i = 2 ; i < codec->driver->reg_cache_size ; i += step) - snd_soc_write(codec, i, cache[i]); + regcache_cache_only(alc5623->regmap, false); + ret = regcache_sync(alc5623->regmap); + if (ret != 0) { + dev_err(codec->dev, "Failed to sync register cache: %d\n", + ret); + regcache_cache_only(alc5623->regmap, true); + return ret; + } alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -900,14 +904,14 @@ static int alc5623_probe(struct snd_soc_codec *codec) struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; - ret = snd_soc_codec_set_cache_io(codec, 8, 16, alc5623->control_type); + codec->control_data = alc5623->regmap; + 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; } alc5623_reset(codec); - alc5623_fill_cache(codec); /* power on device */ alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -980,9 +984,15 @@ static struct snd_soc_codec_driver soc_codec_device_alc5623 = { .suspend = alc5623_suspend, .resume = alc5623_resume, .set_bias_level = alc5623_set_bias_level, - .reg_cache_size = ALC5623_VENDOR_ID2+2, - .reg_word_size = sizeof(u16), - .reg_cache_step = 2, +}; + +static const struct regmap_config alc5623_regmap = { + .reg_bits = 8, + .val_bits = 16, + .reg_stride = 2, + + .max_register = ALC5623_VENDOR_ID2, + .cache_type = REGCACHE_RBTREE, }; /* @@ -996,19 +1006,32 @@ static int alc5623_i2c_probe(struct i2c_client *client, { struct alc5623_platform_data *pdata; struct alc5623_priv *alc5623; - int ret, vid1, vid2; + unsigned int vid1, vid2; + int ret; - vid1 = i2c_smbus_read_word_data(client, ALC5623_VENDOR_ID1); - if (vid1 < 0) { - dev_err(&client->dev, "failed to read I2C\n"); - return -EIO; + alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv), + GFP_KERNEL); + if (alc5623 == NULL) + return -ENOMEM; + + alc5623->regmap = devm_regmap_init_i2c(client, &alc5623_regmap); + if (IS_ERR(alc5623->regmap)) { + ret = PTR_ERR(alc5623->regmap); + dev_err(&client->dev, "Failed to initialise I/O: %d\n", ret); + return ret; + } + + ret = regmap_read(alc5623->regmap, ALC5623_VENDOR_ID1, &vid1); + if (ret < 0) { + dev_err(&client->dev, "failed to read vendor ID1: %d\n", ret); + return ret; } vid1 = ((vid1 & 0xff) << 8) | (vid1 >> 8); - vid2 = i2c_smbus_read_byte_data(client, ALC5623_VENDOR_ID2); - if (vid2 < 0) { - dev_err(&client->dev, "failed to read I2C\n"); - return -EIO; + ret = regmap_read(alc5623->regmap, ALC5623_VENDOR_ID2, &vid2); + if (ret < 0) { + dev_err(&client->dev, "failed to read vendor ID2: %d\n", ret); + return ret; } if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) { @@ -1021,11 +1044,6 @@ static int alc5623_i2c_probe(struct i2c_client *client, dev_dbg(&client->dev, "Found codec id : alc56%02x\n", vid2); - alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv), - GFP_KERNEL); - if (alc5623 == NULL) - return -ENOMEM; - pdata = client->dev.platform_data; if (pdata) { alc5623->add_ctrl = pdata->add_ctrl; @@ -1048,7 +1066,6 @@ static int alc5623_i2c_probe(struct i2c_client *client, } i2c_set_clientdata(client, alc5623); - alc5623->control_type = SND_SOC_I2C; ret = snd_soc_register_codec(&client->dev, &soc_codec_device_alc5623, &alc5623_dai, 1); diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index 19e9f222d09c..d885056ad8f2 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -293,51 +293,59 @@ static const char * const alc5632_i2s_out_sel[] = { "ADC LR", "Voice Stereo Digital"}; /* auxout output mux */ -static const struct soc_enum alc5632_aux_out_input_enum = -SOC_ENUM_SINGLE(ALC5632_OUTPUT_MIXER_CTRL, 6, 4, alc5632_aux_out_input_sel); +static SOC_ENUM_SINGLE_DECL(alc5632_aux_out_input_enum, + ALC5632_OUTPUT_MIXER_CTRL, 6, + alc5632_aux_out_input_sel); static const struct snd_kcontrol_new alc5632_auxout_mux_controls = SOC_DAPM_ENUM("AuxOut Mux", alc5632_aux_out_input_enum); /* speaker output mux */ -static const struct soc_enum alc5632_spkout_input_enum = -SOC_ENUM_SINGLE(ALC5632_OUTPUT_MIXER_CTRL, 10, 4, alc5632_spkout_input_sel); +static SOC_ENUM_SINGLE_DECL(alc5632_spkout_input_enum, + ALC5632_OUTPUT_MIXER_CTRL, 10, + alc5632_spkout_input_sel); static const struct snd_kcontrol_new alc5632_spkout_mux_controls = SOC_DAPM_ENUM("SpeakerOut Mux", alc5632_spkout_input_enum); /* headphone left output mux */ -static const struct soc_enum alc5632_hpl_out_input_enum = -SOC_ENUM_SINGLE(ALC5632_OUTPUT_MIXER_CTRL, 9, 2, alc5632_hpl_out_input_sel); +static SOC_ENUM_SINGLE_DECL(alc5632_hpl_out_input_enum, + ALC5632_OUTPUT_MIXER_CTRL, 9, + alc5632_hpl_out_input_sel); static const struct snd_kcontrol_new alc5632_hpl_out_mux_controls = SOC_DAPM_ENUM("Left Headphone Mux", alc5632_hpl_out_input_enum); /* headphone right output mux */ -static const struct soc_enum alc5632_hpr_out_input_enum = -SOC_ENUM_SINGLE(ALC5632_OUTPUT_MIXER_CTRL, 8, 2, alc5632_hpr_out_input_sel); +static SOC_ENUM_SINGLE_DECL(alc5632_hpr_out_input_enum, + ALC5632_OUTPUT_MIXER_CTRL, 8, + alc5632_hpr_out_input_sel); static const struct snd_kcontrol_new alc5632_hpr_out_mux_controls = SOC_DAPM_ENUM("Right Headphone Mux", alc5632_hpr_out_input_enum); /* speaker output N select */ -static const struct soc_enum alc5632_spk_n_sour_enum = -SOC_ENUM_SINGLE(ALC5632_OUTPUT_MIXER_CTRL, 14, 4, alc5632_spk_n_sour_sel); +static SOC_ENUM_SINGLE_DECL(alc5632_spk_n_sour_enum, + ALC5632_OUTPUT_MIXER_CTRL, 14, + alc5632_spk_n_sour_sel); static const struct snd_kcontrol_new alc5632_spkoutn_mux_controls = SOC_DAPM_ENUM("SpeakerOut N Mux", alc5632_spk_n_sour_enum); /* speaker amplifier */ static const char *alc5632_amp_names[] = {"AB Amp", "D Amp"}; -static const struct soc_enum alc5632_amp_enum = - SOC_ENUM_SINGLE(ALC5632_OUTPUT_MIXER_CTRL, 13, 2, alc5632_amp_names); +static SOC_ENUM_SINGLE_DECL(alc5632_amp_enum, + ALC5632_OUTPUT_MIXER_CTRL, 13, + alc5632_amp_names); static const struct snd_kcontrol_new alc5632_amp_mux_controls = SOC_DAPM_ENUM("AB-D Amp Mux", alc5632_amp_enum); /* ADC output select */ -static const struct soc_enum alc5632_adcr_func_enum = - SOC_ENUM_SINGLE(ALC5632_DAC_FUNC_SELECT, 5, 2, alc5632_adcr_func_sel); +static SOC_ENUM_SINGLE_DECL(alc5632_adcr_func_enum, + ALC5632_DAC_FUNC_SELECT, 5, + alc5632_adcr_func_sel); static const struct snd_kcontrol_new alc5632_adcr_func_controls = SOC_DAPM_ENUM("ADCR Mux", alc5632_adcr_func_enum); /* I2S out select */ -static const struct soc_enum alc5632_i2s_out_enum = - SOC_ENUM_SINGLE(ALC5632_I2S_OUT_CTL, 5, 2, alc5632_i2s_out_sel); +static SOC_ENUM_SINGLE_DECL(alc5632_i2s_out_enum, + ALC5632_I2S_OUT_CTL, 5, + alc5632_i2s_out_sel); static const struct snd_kcontrol_new alc5632_i2s_out_controls = SOC_DAPM_ENUM("I2SOut Mux", alc5632_i2s_out_enum); @@ -869,14 +877,14 @@ static int alc5632_pcm_hw_params(struct snd_pcm_substream *substream, iface &= ~ALC5632_DAI_I2S_DL_MASK; /* bit size */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: iface |= ALC5632_DAI_I2S_DL_16; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: iface |= ALC5632_DAI_I2S_DL_20; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: iface |= ALC5632_DAI_I2S_DL_24; break; default: diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index fea991031be1..29e198f57d4c 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -53,6 +53,14 @@ #define ARIZONA_AIF_RX_ENABLES 0x1A #define ARIZONA_AIF_FORCE_WRITE 0x1B +#define ARIZONA_FLL_VCO_CORNER 141900000 +#define ARIZONA_FLL_MAX_FREF 13500000 +#define ARIZONA_FLL_MIN_FVCO 90000000 +#define ARIZONA_FLL_MAX_FRATIO 16 +#define ARIZONA_FLL_MAX_REFDIV 8 +#define ARIZONA_FLL_MIN_OUTDIV 2 +#define ARIZONA_FLL_MAX_OUTDIV 7 + #define arizona_fll_err(_fll, fmt, ...) \ dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_fll_warn(_fll, fmt, ...) \ @@ -93,7 +101,7 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: if (!priv->spk_ena && manual_ena) { - snd_soc_write(codec, 0x4f5, 0x25a); + regmap_write_async(arizona->regmap, 0x4f5, 0x25a); priv->spk_ena_pending = true; } break; @@ -105,12 +113,13 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w, return -EBUSY; } - snd_soc_update_bits(codec, ARIZONA_OUTPUT_ENABLES_1, - 1 << w->shift, 1 << w->shift); + regmap_update_bits_async(arizona->regmap, + ARIZONA_OUTPUT_ENABLES_1, + 1 << w->shift, 1 << w->shift); if (priv->spk_ena_pending) { msleep(75); - snd_soc_write(codec, 0x4f5, 0xda); + regmap_write_async(arizona->regmap, 0x4f5, 0xda); priv->spk_ena_pending = false; priv->spk_ena++; } @@ -119,16 +128,19 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w, if (manual_ena) { priv->spk_ena--; if (!priv->spk_ena) - snd_soc_write(codec, 0x4f5, 0x25a); + regmap_write_async(arizona->regmap, + 0x4f5, 0x25a); } - snd_soc_update_bits(codec, ARIZONA_OUTPUT_ENABLES_1, - 1 << w->shift, 0); + regmap_update_bits_async(arizona->regmap, + ARIZONA_OUTPUT_ENABLES_1, + 1 << w->shift, 0); break; case SND_SOC_DAPM_POST_PMD: if (manual_ena) { if (!priv->spk_ena) - snd_soc_write(codec, 0x4f5, 0x0da); + regmap_write_async(arizona->regmap, + 0x4f5, 0x0da); } break; } @@ -292,6 +304,10 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "AIF1RX8", "AIF2RX1", "AIF2RX2", + "AIF2RX3", + "AIF2RX4", + "AIF2RX5", + "AIF2RX6", "AIF3RX1", "AIF3RX2", "SLIMRX1", @@ -395,6 +411,10 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { 0x27, 0x28, /* AIF2RX1 */ 0x29, + 0x2a, + 0x2b, + 0x2c, + 0x2d, 0x30, /* AIF3RX1 */ 0x31, 0x38, /* SLIMRX1 */ @@ -486,6 +506,22 @@ const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = { EXPORT_SYMBOL_GPL(arizona_rate_val); +const struct soc_enum arizona_isrc_fsh[] = { + SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_1_CTRL_1, + ARIZONA_ISRC1_FSH_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_2_CTRL_1, + ARIZONA_ISRC2_FSH_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_3_CTRL_1, + ARIZONA_ISRC3_FSH_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), +}; +EXPORT_SYMBOL_GPL(arizona_isrc_fsh); + const struct soc_enum arizona_isrc_fsl[] = { SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_1_CTRL_2, ARIZONA_ISRC1_FSL_SHIFT, 0xf, @@ -502,64 +538,90 @@ const struct soc_enum arizona_isrc_fsl[] = { }; EXPORT_SYMBOL_GPL(arizona_isrc_fsl); +const struct soc_enum arizona_asrc_rate1 = + SOC_VALUE_ENUM_SINGLE(ARIZONA_ASRC_RATE1, + ARIZONA_ASRC_RATE1_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE - 1, + arizona_rate_text, arizona_rate_val); +EXPORT_SYMBOL_GPL(arizona_asrc_rate1); + static const char *arizona_vol_ramp_text[] = { "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", "15ms/6dB", "30ms/6dB", }; -const struct soc_enum arizona_in_vd_ramp = - SOC_ENUM_SINGLE(ARIZONA_INPUT_VOLUME_RAMP, - ARIZONA_IN_VD_RAMP_SHIFT, 7, arizona_vol_ramp_text); +SOC_ENUM_SINGLE_DECL(arizona_in_vd_ramp, + ARIZONA_INPUT_VOLUME_RAMP, + ARIZONA_IN_VD_RAMP_SHIFT, + arizona_vol_ramp_text); EXPORT_SYMBOL_GPL(arizona_in_vd_ramp); -const struct soc_enum arizona_in_vi_ramp = - SOC_ENUM_SINGLE(ARIZONA_INPUT_VOLUME_RAMP, - ARIZONA_IN_VI_RAMP_SHIFT, 7, arizona_vol_ramp_text); +SOC_ENUM_SINGLE_DECL(arizona_in_vi_ramp, + ARIZONA_INPUT_VOLUME_RAMP, + ARIZONA_IN_VI_RAMP_SHIFT, + arizona_vol_ramp_text); EXPORT_SYMBOL_GPL(arizona_in_vi_ramp); -const struct soc_enum arizona_out_vd_ramp = - SOC_ENUM_SINGLE(ARIZONA_OUTPUT_VOLUME_RAMP, - ARIZONA_OUT_VD_RAMP_SHIFT, 7, arizona_vol_ramp_text); +SOC_ENUM_SINGLE_DECL(arizona_out_vd_ramp, + ARIZONA_OUTPUT_VOLUME_RAMP, + ARIZONA_OUT_VD_RAMP_SHIFT, + arizona_vol_ramp_text); EXPORT_SYMBOL_GPL(arizona_out_vd_ramp); -const struct soc_enum arizona_out_vi_ramp = - SOC_ENUM_SINGLE(ARIZONA_OUTPUT_VOLUME_RAMP, - ARIZONA_OUT_VI_RAMP_SHIFT, 7, arizona_vol_ramp_text); +SOC_ENUM_SINGLE_DECL(arizona_out_vi_ramp, + ARIZONA_OUTPUT_VOLUME_RAMP, + ARIZONA_OUT_VI_RAMP_SHIFT, + arizona_vol_ramp_text); EXPORT_SYMBOL_GPL(arizona_out_vi_ramp); static const char *arizona_lhpf_mode_text[] = { "Low-pass", "High-pass" }; -const struct soc_enum arizona_lhpf1_mode = - SOC_ENUM_SINGLE(ARIZONA_HPLPF1_1, ARIZONA_LHPF1_MODE_SHIFT, 2, - arizona_lhpf_mode_text); +SOC_ENUM_SINGLE_DECL(arizona_lhpf1_mode, + ARIZONA_HPLPF1_1, + ARIZONA_LHPF1_MODE_SHIFT, + arizona_lhpf_mode_text); EXPORT_SYMBOL_GPL(arizona_lhpf1_mode); -const struct soc_enum arizona_lhpf2_mode = - SOC_ENUM_SINGLE(ARIZONA_HPLPF2_1, ARIZONA_LHPF2_MODE_SHIFT, 2, - arizona_lhpf_mode_text); +SOC_ENUM_SINGLE_DECL(arizona_lhpf2_mode, + ARIZONA_HPLPF2_1, + ARIZONA_LHPF2_MODE_SHIFT, + arizona_lhpf_mode_text); EXPORT_SYMBOL_GPL(arizona_lhpf2_mode); -const struct soc_enum arizona_lhpf3_mode = - SOC_ENUM_SINGLE(ARIZONA_HPLPF3_1, ARIZONA_LHPF3_MODE_SHIFT, 2, - arizona_lhpf_mode_text); +SOC_ENUM_SINGLE_DECL(arizona_lhpf3_mode, + ARIZONA_HPLPF3_1, + ARIZONA_LHPF3_MODE_SHIFT, + arizona_lhpf_mode_text); EXPORT_SYMBOL_GPL(arizona_lhpf3_mode); -const struct soc_enum arizona_lhpf4_mode = - SOC_ENUM_SINGLE(ARIZONA_HPLPF4_1, ARIZONA_LHPF4_MODE_SHIFT, 2, - arizona_lhpf_mode_text); +SOC_ENUM_SINGLE_DECL(arizona_lhpf4_mode, + ARIZONA_HPLPF4_1, + ARIZONA_LHPF4_MODE_SHIFT, + arizona_lhpf_mode_text); EXPORT_SYMBOL_GPL(arizona_lhpf4_mode); static const char *arizona_ng_hold_text[] = { "30ms", "120ms", "250ms", "500ms", }; -const struct soc_enum arizona_ng_hold = - SOC_ENUM_SINGLE(ARIZONA_NOISE_GATE_CONTROL, ARIZONA_NGATE_HOLD_SHIFT, - 4, arizona_ng_hold_text); +SOC_ENUM_SINGLE_DECL(arizona_ng_hold, + ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_HOLD_SHIFT, + arizona_ng_hold_text); EXPORT_SYMBOL_GPL(arizona_ng_hold); +static const char * const arizona_in_hpf_cut_text[] = { + "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz" +}; + +SOC_ENUM_SINGLE_DECL(arizona_in_hpf_cut_enum, + ARIZONA_HPF_CONTROL, + ARIZONA_IN_HPF_CUT_SHIFT, + arizona_in_hpf_cut_text); +EXPORT_SYMBOL_GPL(arizona_in_hpf_cut_enum); + static const char * const arizona_in_dmic_osr_text[] = { "1.536MHz", "3.072MHz", "6.144MHz", }; @@ -669,6 +731,7 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w, int event) { struct arizona_priv *priv = snd_soc_codec_get_drvdata(w->codec); + struct arizona *arizona = priv->arizona; unsigned int mask = 1 << w->shift; unsigned int val; @@ -691,7 +754,8 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w, if (priv->arizona->hpdet_magic) val = 0; - snd_soc_update_bits(w->codec, ARIZONA_OUTPUT_ENABLES_1, mask, val); + regmap_update_bits_async(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, + mask, val); return arizona_out_ev(w, kcontrol, event); } @@ -846,6 +910,8 @@ EXPORT_SYMBOL_GPL(arizona_set_sysclk); static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_codec *codec = dai->codec; + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; int lrclk, bclk, mode, base; base = dai->driver->base; @@ -902,17 +968,19 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, - ARIZONA_AIF1_BCLK_INV | ARIZONA_AIF1_BCLK_MSTR, - bclk); - snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_PIN_CTRL, - ARIZONA_AIF1TX_LRCLK_INV | - ARIZONA_AIF1TX_LRCLK_MSTR, lrclk); - snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_PIN_CTRL, - ARIZONA_AIF1RX_LRCLK_INV | - ARIZONA_AIF1RX_LRCLK_MSTR, lrclk); - snd_soc_update_bits(codec, base + ARIZONA_AIF_FORMAT, - ARIZONA_AIF1_FMT_MASK, mode); + regmap_update_bits_async(arizona->regmap, base + ARIZONA_AIF_BCLK_CTRL, + ARIZONA_AIF1_BCLK_INV | + ARIZONA_AIF1_BCLK_MSTR, + bclk); + regmap_update_bits_async(arizona->regmap, base + ARIZONA_AIF_TX_PIN_CTRL, + ARIZONA_AIF1TX_LRCLK_INV | + ARIZONA_AIF1TX_LRCLK_MSTR, lrclk); + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_RX_PIN_CTRL, + ARIZONA_AIF1RX_LRCLK_INV | + ARIZONA_AIF1RX_LRCLK_MSTR, lrclk); + regmap_update_bits(arizona->regmap, base + ARIZONA_AIF_FORMAT, + ARIZONA_AIF1_FMT_MASK, mode); return 0; } @@ -1164,18 +1232,22 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, if (ret != 0) return ret; - snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, - ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); - snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_BCLK_RATE, - ARIZONA_AIF1TX_BCPF_MASK, lrclk); - snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_BCLK_RATE, - ARIZONA_AIF1RX_BCPF_MASK, lrclk); - snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_1, - ARIZONA_AIF1TX_WL_MASK | - ARIZONA_AIF1TX_SLOT_LEN_MASK, frame); - snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_2, - ARIZONA_AIF1RX_WL_MASK | - ARIZONA_AIF1RX_SLOT_LEN_MASK, frame); + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_BCLK_CTRL, + ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_TX_BCLK_RATE, + ARIZONA_AIF1TX_BCPF_MASK, lrclk); + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_RX_BCLK_RATE, + ARIZONA_AIF1RX_BCPF_MASK, lrclk); + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_FRAME_CTRL_1, + ARIZONA_AIF1TX_WL_MASK | + ARIZONA_AIF1TX_SLOT_LEN_MASK, frame); + regmap_update_bits(arizona->regmap, base + ARIZONA_AIF_FRAME_CTRL_2, + ARIZONA_AIF1RX_WL_MASK | + ARIZONA_AIF1RX_SLOT_LEN_MASK, frame); return 0; } @@ -1322,74 +1394,147 @@ struct arizona_fll_cfg { int gain; }; -static int arizona_calc_fll(struct arizona_fll *fll, - struct arizona_fll_cfg *cfg, - unsigned int Fref, - unsigned int Fout) +static int arizona_validate_fll(struct arizona_fll *fll, + unsigned int Fref, + unsigned int Fout) { - unsigned int target, div, gcd_fll; - int i, ratio; + unsigned int Fvco_min; + + if (Fref / ARIZONA_FLL_MAX_REFDIV > ARIZONA_FLL_MAX_FREF) { + arizona_fll_err(fll, + "Can't scale %dMHz in to <=13.5MHz\n", + Fref); + return -EINVAL; + } + + Fvco_min = ARIZONA_FLL_MIN_FVCO * fll->vco_mult; + if (Fout * ARIZONA_FLL_MAX_OUTDIV < Fvco_min) { + arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n", + Fout); + return -EINVAL; + } - arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, Fout); + return 0; +} - /* Fref must be <=13.5MHz */ +static int arizona_find_fratio(unsigned int Fref, int *fratio) +{ + int i; + + /* Find an appropriate FLL_FRATIO */ + for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { + if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { + if (fratio) + *fratio = fll_fratios[i].fratio; + return fll_fratios[i].ratio; + } + } + + return -EINVAL; +} + +static int arizona_calc_fratio(struct arizona_fll *fll, + struct arizona_fll_cfg *cfg, + unsigned int target, + unsigned int Fref, bool sync) +{ + int init_ratio, ratio; + int refdiv, div; + + /* Fref must be <=13.5MHz, find initial refdiv */ div = 1; cfg->refdiv = 0; - while ((Fref / div) > 13500000) { + while (Fref > ARIZONA_FLL_MAX_FREF) { div *= 2; + Fref /= 2; cfg->refdiv++; - if (div > 8) { - arizona_fll_err(fll, - "Can't scale %dMHz in to <=13.5MHz\n", - Fref); + if (div > ARIZONA_FLL_MAX_REFDIV) return -EINVAL; + } + + /* Find an appropriate FLL_FRATIO */ + init_ratio = arizona_find_fratio(Fref, &cfg->fratio); + if (init_ratio < 0) { + arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n", + Fref); + return init_ratio; + } + + switch (fll->arizona->type) { + case WM5110: + if (fll->arizona->rev < 3 || sync) + return init_ratio; + break; + default: + return init_ratio; + } + + cfg->fratio = init_ratio - 1; + + /* Adjust FRATIO/refdiv to avoid integer mode if possible */ + refdiv = cfg->refdiv; + + while (div <= ARIZONA_FLL_MAX_REFDIV) { + for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO; + ratio++) { + if (target % (ratio * Fref)) { + cfg->refdiv = refdiv; + cfg->fratio = ratio - 1; + return ratio; + } + } + + for (ratio = init_ratio - 1; ratio >= 0; ratio--) { + if (ARIZONA_FLL_VCO_CORNER / (fll->vco_mult * ratio) < + Fref) + break; + + if (target % (ratio * Fref)) { + cfg->refdiv = refdiv; + cfg->fratio = ratio - 1; + return ratio; + } } + + div *= 2; + Fref /= 2; + refdiv++; + init_ratio = arizona_find_fratio(Fref, NULL); } - /* Apply the division for our remaining calculations */ - Fref /= div; + arizona_fll_warn(fll, "Falling back to integer mode operation\n"); + return cfg->fratio + 1; +} + +static int arizona_calc_fll(struct arizona_fll *fll, + struct arizona_fll_cfg *cfg, + unsigned int Fref, bool sync) +{ + unsigned int target, div, gcd_fll; + int i, ratio; + + arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, fll->fout); /* Fvco should be over the targt; don't check the upper bound */ - div = 1; - while (Fout * div < 90000000 * fll->vco_mult) { + div = ARIZONA_FLL_MIN_OUTDIV; + while (fll->fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) { div++; - if (div > 7) { - arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n", - Fout); + if (div > ARIZONA_FLL_MAX_OUTDIV) return -EINVAL; - } } - target = Fout * div / fll->vco_mult; + target = fll->fout * div / fll->vco_mult; cfg->outdiv = div; arizona_fll_dbg(fll, "Fvco=%dHz\n", target); - /* Find an appropraite FLL_FRATIO and factor it out of the target */ - for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { - if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { - cfg->fratio = fll_fratios[i].fratio; - ratio = fll_fratios[i].ratio; - break; - } - } - if (i == ARRAY_SIZE(fll_fratios)) { - arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n", - Fref); - return -EINVAL; - } + /* Find an appropriate FLL_FRATIO and refdiv */ + ratio = arizona_calc_fratio(fll, cfg, target, Fref, sync); + if (ratio < 0) + return ratio; - for (i = 0; i < ARRAY_SIZE(fll_gains); i++) { - if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) { - cfg->gain = fll_gains[i].gain; - break; - } - } - if (i == ARRAY_SIZE(fll_gains)) { - arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n", - Fref); - return -EINVAL; - } + /* Apply the division for our remaining calculations */ + Fref = Fref / (1 << cfg->refdiv); cfg->n = target / (ratio * Fref); @@ -1414,6 +1559,18 @@ static int arizona_calc_fll(struct arizona_fll *fll, cfg->lambda >>= 1; } + for (i = 0; i < ARRAY_SIZE(fll_gains); i++) { + if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) { + cfg->gain = fll_gains[i].gain; + break; + } + } + if (i == ARRAY_SIZE(fll_gains)) { + arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n", + Fref); + return -EINVAL; + } + arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", cfg->n, cfg->theta, cfg->lambda); arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", @@ -1428,31 +1585,35 @@ static void arizona_apply_fll(struct arizona *arizona, unsigned int base, struct arizona_fll_cfg *cfg, int source, bool sync) { - regmap_update_bits(arizona->regmap, base + 3, - ARIZONA_FLL1_THETA_MASK, cfg->theta); - regmap_update_bits(arizona->regmap, base + 4, - ARIZONA_FLL1_LAMBDA_MASK, cfg->lambda); - regmap_update_bits(arizona->regmap, base + 5, - ARIZONA_FLL1_FRATIO_MASK, - cfg->fratio << ARIZONA_FLL1_FRATIO_SHIFT); - regmap_update_bits(arizona->regmap, base + 6, - ARIZONA_FLL1_CLK_REF_DIV_MASK | - ARIZONA_FLL1_CLK_REF_SRC_MASK, - cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT | - source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT); - - if (sync) + regmap_update_bits_async(arizona->regmap, base + 3, + ARIZONA_FLL1_THETA_MASK, cfg->theta); + regmap_update_bits_async(arizona->regmap, base + 4, + ARIZONA_FLL1_LAMBDA_MASK, cfg->lambda); + regmap_update_bits_async(arizona->regmap, base + 5, + ARIZONA_FLL1_FRATIO_MASK, + cfg->fratio << ARIZONA_FLL1_FRATIO_SHIFT); + regmap_update_bits_async(arizona->regmap, base + 6, + ARIZONA_FLL1_CLK_REF_DIV_MASK | + ARIZONA_FLL1_CLK_REF_SRC_MASK, + cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT | + source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT); + + if (sync) { regmap_update_bits(arizona->regmap, base + 0x7, ARIZONA_FLL1_GAIN_MASK, cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); - else + } else { + regmap_update_bits(arizona->regmap, base + 0x5, + ARIZONA_FLL1_OUTDIV_MASK, + cfg->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); regmap_update_bits(arizona->regmap, base + 0x9, ARIZONA_FLL1_GAIN_MASK, cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); + } - regmap_update_bits(arizona->regmap, base + 2, - ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK, - ARIZONA_FLL1_CTRL_UPD | cfg->n); + regmap_update_bits_async(arizona->regmap, base + 2, + ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK, + ARIZONA_FLL1_CTRL_UPD | cfg->n); } static bool arizona_is_enabled_fll(struct arizona_fll *fll) @@ -1471,13 +1632,12 @@ static bool arizona_is_enabled_fll(struct arizona_fll *fll) return reg & ARIZONA_FLL1_ENA; } -static void arizona_enable_fll(struct arizona_fll *fll, - struct arizona_fll_cfg *ref, - struct arizona_fll_cfg *sync) +static void arizona_enable_fll(struct arizona_fll *fll) { struct arizona *arizona = fll->arizona; int ret; bool use_sync = false; + struct arizona_fll_cfg cfg; /* * If we have both REFCLK and SYNCCLK then enable both, @@ -1485,27 +1645,25 @@ static void arizona_enable_fll(struct arizona_fll *fll, */ if (fll->ref_src >= 0 && fll->ref_freq && fll->ref_src != fll->sync_src) { - regmap_update_bits(arizona->regmap, fll->base + 5, - ARIZONA_FLL1_OUTDIV_MASK, - ref->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); + arizona_calc_fll(fll, &cfg, fll->ref_freq, false); - arizona_apply_fll(arizona, fll->base, ref, fll->ref_src, + arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src, false); if (fll->sync_src >= 0) { - arizona_apply_fll(arizona, fll->base + 0x10, sync, + arizona_calc_fll(fll, &cfg, fll->sync_freq, true); + + arizona_apply_fll(arizona, fll->base + 0x10, &cfg, fll->sync_src, true); use_sync = true; } } else if (fll->sync_src >= 0) { - regmap_update_bits(arizona->regmap, fll->base + 5, - ARIZONA_FLL1_OUTDIV_MASK, - sync->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); + arizona_calc_fll(fll, &cfg, fll->sync_freq, false); - arizona_apply_fll(arizona, fll->base, sync, + arizona_apply_fll(arizona, fll->base, &cfg, fll->sync_src, false); - regmap_update_bits(arizona->regmap, fll->base + 0x11, - ARIZONA_FLL1_SYNC_ENA, 0); + regmap_update_bits_async(arizona->regmap, fll->base + 0x11, + ARIZONA_FLL1_SYNC_ENA, 0); } else { arizona_fll_err(fll, "No clocks provided\n"); return; @@ -1516,11 +1674,12 @@ static void arizona_enable_fll(struct arizona_fll *fll, * sync source. */ if (use_sync && fll->sync_freq > 100000) - regmap_update_bits(arizona->regmap, fll->base + 0x17, - ARIZONA_FLL1_SYNC_BW, 0); + regmap_update_bits_async(arizona->regmap, fll->base + 0x17, + ARIZONA_FLL1_SYNC_BW, 0); else - regmap_update_bits(arizona->regmap, fll->base + 0x17, - ARIZONA_FLL1_SYNC_BW, ARIZONA_FLL1_SYNC_BW); + regmap_update_bits_async(arizona->regmap, fll->base + 0x17, + ARIZONA_FLL1_SYNC_BW, + ARIZONA_FLL1_SYNC_BW); if (!arizona_is_enabled_fll(fll)) pm_runtime_get(arizona->dev); @@ -1528,14 +1687,14 @@ static void arizona_enable_fll(struct arizona_fll *fll, /* Clear any pending completions */ try_wait_for_completion(&fll->ok); - regmap_update_bits(arizona->regmap, fll->base + 1, - ARIZONA_FLL1_FREERUN, 0); - regmap_update_bits(arizona->regmap, fll->base + 1, - ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA); + regmap_update_bits_async(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, 0); + regmap_update_bits_async(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA); if (use_sync) - regmap_update_bits(arizona->regmap, fll->base + 0x11, - ARIZONA_FLL1_SYNC_ENA, - ARIZONA_FLL1_SYNC_ENA); + regmap_update_bits_async(arizona->regmap, fll->base + 0x11, + ARIZONA_FLL1_SYNC_ENA, + ARIZONA_FLL1_SYNC_ENA); ret = wait_for_completion_timeout(&fll->ok, msecs_to_jiffies(250)); @@ -1548,8 +1707,8 @@ static void arizona_disable_fll(struct arizona_fll *fll) struct arizona *arizona = fll->arizona; bool change; - regmap_update_bits(arizona->regmap, fll->base + 1, - ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN); + regmap_update_bits_async(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN); regmap_update_bits_check(arizona->regmap, fll->base + 1, ARIZONA_FLL1_ENA, 0, &change); regmap_update_bits(arizona->regmap, fll->base + 0x11, @@ -1562,32 +1721,22 @@ static void arizona_disable_fll(struct arizona_fll *fll) int arizona_set_fll_refclk(struct arizona_fll *fll, int source, unsigned int Fref, unsigned int Fout) { - struct arizona_fll_cfg ref, sync; int ret; if (fll->ref_src == source && fll->ref_freq == Fref) return 0; - if (fll->fout) { - if (Fref > 0) { - ret = arizona_calc_fll(fll, &ref, Fref, fll->fout); - if (ret != 0) - return ret; - } - - if (fll->sync_src >= 0) { - ret = arizona_calc_fll(fll, &sync, fll->sync_freq, - fll->fout); - if (ret != 0) - return ret; - } + if (fll->fout && Fref > 0) { + ret = arizona_validate_fll(fll, Fref, fll->fout); + if (ret != 0) + return ret; } fll->ref_src = source; fll->ref_freq = Fref; if (fll->fout && Fref > 0) { - arizona_enable_fll(fll, &ref, &sync); + arizona_enable_fll(fll); } return 0; @@ -1597,7 +1746,6 @@ EXPORT_SYMBOL_GPL(arizona_set_fll_refclk); int arizona_set_fll(struct arizona_fll *fll, int source, unsigned int Fref, unsigned int Fout) { - struct arizona_fll_cfg ref, sync; int ret; if (fll->sync_src == source && @@ -1606,13 +1754,12 @@ int arizona_set_fll(struct arizona_fll *fll, int source, if (Fout) { if (fll->ref_src >= 0) { - ret = arizona_calc_fll(fll, &ref, fll->ref_freq, - Fout); + ret = arizona_validate_fll(fll, fll->ref_freq, Fout); if (ret != 0) return ret; } - ret = arizona_calc_fll(fll, &sync, Fref, Fout); + ret = arizona_validate_fll(fll, Fref, Fout); if (ret != 0) return ret; } @@ -1622,7 +1769,7 @@ int arizona_set_fll(struct arizona_fll *fll, int source, fll->fout = Fout; if (Fout) { - arizona_enable_fll(fll, &ref, &sync); + arizona_enable_fll(fll); } else { arizona_disable_fll(fll); } diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 9e81b6392692..16df0f913353 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -81,7 +81,7 @@ struct arizona_priv { unsigned int spk_ena_pending:1; }; -#define ARIZONA_NUM_MIXER_INPUTS 99 +#define ARIZONA_NUM_MIXER_INPUTS 103 extern const unsigned int arizona_mixer_tlv[]; extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; @@ -166,26 +166,29 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; ARIZONA_MIXER_INPUT_ROUTES(name " Input 4") #define ARIZONA_DSP_ROUTES(name) \ - { name, NULL, name " Aux 1" }, \ - { name, NULL, name " Aux 2" }, \ - { name, NULL, name " Aux 3" }, \ - { name, NULL, name " Aux 4" }, \ - { name, NULL, name " Aux 5" }, \ - { name, NULL, name " Aux 6" }, \ + { name, NULL, name " Preloader"}, \ + { name " Preloader", NULL, name " Aux 1" }, \ + { name " Preloader", NULL, name " Aux 2" }, \ + { name " Preloader", NULL, name " Aux 3" }, \ + { name " Preloader", NULL, name " Aux 4" }, \ + { name " Preloader", NULL, name " Aux 5" }, \ + { name " Preloader", NULL, name " Aux 6" }, \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \ - ARIZONA_MIXER_ROUTES(name, name "L"), \ - ARIZONA_MIXER_ROUTES(name, name "R") + ARIZONA_MIXER_ROUTES(name " Preloader", name "L"), \ + ARIZONA_MIXER_ROUTES(name " Preloader", name "R") #define ARIZONA_RATE_ENUM_SIZE 4 extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE]; extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE]; extern const struct soc_enum arizona_isrc_fsl[]; +extern const struct soc_enum arizona_isrc_fsh[]; +extern const struct soc_enum arizona_asrc_rate1; extern const struct soc_enum arizona_in_vi_ramp; extern const struct soc_enum arizona_in_vd_ramp; @@ -199,6 +202,7 @@ extern const struct soc_enum arizona_lhpf3_mode; extern const struct soc_enum arizona_lhpf4_mode; extern const struct soc_enum arizona_ng_hold; +extern const struct soc_enum arizona_in_hpf_cut_enum; extern const struct soc_enum arizona_in_dmic_osr[]; extern int arizona_in_ev(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index f6e953454bc0..aef4965750c7 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -159,7 +159,6 @@ static bool cs4271_volatile_reg(struct device *dev, unsigned int reg) } struct cs4271_private { - /* SND_SOC_I2C or SND_SOC_SPI */ unsigned int mclk; bool master; bool deemph; @@ -540,14 +539,10 @@ static int cs4271_probe(struct snd_soc_codec *codec) struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); struct cs4271_platform_data *cs4271plat = codec->dev->platform_data; int ret; - int gpio_nreset = -EINVAL; bool amutec_eq_bmutec = false; #ifdef CONFIG_OF if (of_match_device(cs4271_dt_ids, codec->dev)) { - gpio_nreset = of_get_named_gpio(codec->dev->of_node, - "reset-gpio", 0); - if (of_get_property(codec->dev->of_node, "cirrus,amutec-eq-bmutec", NULL)) amutec_eq_bmutec = true; @@ -559,27 +554,19 @@ static int cs4271_probe(struct snd_soc_codec *codec) #endif if (cs4271plat) { - if (gpio_is_valid(cs4271plat->gpio_nreset)) - gpio_nreset = cs4271plat->gpio_nreset; - amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec; cs4271->enable_soft_reset = cs4271plat->enable_soft_reset; } - if (gpio_nreset >= 0) - if (devm_gpio_request(codec->dev, gpio_nreset, "CS4271 Reset")) - gpio_nreset = -EINVAL; - if (gpio_nreset >= 0) { + if (gpio_is_valid(cs4271->gpio_nreset)) { /* Reset codec */ - gpio_direction_output(gpio_nreset, 0); + gpio_direction_output(cs4271->gpio_nreset, 0); udelay(1); - gpio_set_value(gpio_nreset, 1); + gpio_set_value(cs4271->gpio_nreset, 1); /* Give the codec time to wake up */ udelay(1); } - cs4271->gpio_nreset = gpio_nreset; - ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, CS4271_MODE2_PDN | CS4271_MODE2_CPEN, CS4271_MODE2_PDN | CS4271_MODE2_CPEN); @@ -625,6 +612,36 @@ static struct snd_soc_codec_driver soc_codec_dev_cs4271 = { .num_dapm_routes = ARRAY_SIZE(cs4271_dapm_routes), }; +static int cs4271_common_probe(struct device *dev, + struct cs4271_private **c) +{ + struct cs4271_platform_data *cs4271plat = dev->platform_data; + struct cs4271_private *cs4271; + + cs4271 = devm_kzalloc(dev, sizeof(*cs4271), GFP_KERNEL); + if (!cs4271) + return -ENOMEM; + + if (of_match_device(cs4271_dt_ids, dev)) + cs4271->gpio_nreset = + of_get_named_gpio(dev->of_node, "reset-gpio", 0); + + if (cs4271plat) + cs4271->gpio_nreset = cs4271plat->gpio_nreset; + + if (gpio_is_valid(cs4271->gpio_nreset)) { + int ret; + + ret = devm_gpio_request(dev, cs4271->gpio_nreset, + "CS4271 Reset"); + if (ret < 0) + return ret; + } + + *c = cs4271; + return 0; +} + #if defined(CONFIG_SPI_MASTER) static const struct regmap_config cs4271_spi_regmap = { @@ -644,10 +661,11 @@ static const struct regmap_config cs4271_spi_regmap = { static int cs4271_spi_probe(struct spi_device *spi) { struct cs4271_private *cs4271; + int ret; - cs4271 = devm_kzalloc(&spi->dev, sizeof(*cs4271), GFP_KERNEL); - if (!cs4271) - return -ENOMEM; + ret = cs4271_common_probe(&spi->dev, &cs4271); + if (ret < 0) + return ret; spi_set_drvdata(spi, cs4271); cs4271->regmap = devm_regmap_init_spi(spi, &cs4271_spi_regmap); @@ -675,7 +693,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}, {} @@ -698,10 +716,11 @@ static int cs4271_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct cs4271_private *cs4271; + int ret; - cs4271 = devm_kzalloc(&client->dev, sizeof(*cs4271), GFP_KERNEL); - if (!cs4271) - return -ENOMEM; + ret = cs4271_common_probe(&client->dev, &cs4271); + if (ret < 0) + return ret; i2c_set_clientdata(client, cs4271); cs4271->regmap = devm_regmap_init_i2c(client, &cs4271_i2c_regmap); @@ -728,7 +747,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 +760,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 +786,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/cs42l51.c b/sound/soc/codecs/cs42l51.c index 1e0fa3b5f79a..5caf75bc6bca 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -30,6 +30,7 @@ #include <sound/pcm_params.h> #include <sound/pcm.h> #include <linux/i2c.h> +#include <linux/regmap.h> #include "cs42l51.h" @@ -40,7 +41,6 @@ enum master_slave_mode { }; struct cs42l51_private { - enum snd_soc_control_type control_type; unsigned int mclk; unsigned int audio_mode; /* The mode (I2S or left-justified) */ enum master_slave_mode func; @@ -52,24 +52,6 @@ struct cs42l51_private { SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) -static int cs42l51_fill_cache(struct snd_soc_codec *codec) -{ - u8 *cache = codec->reg_cache + 1; - struct i2c_client *i2c_client = to_i2c_client(codec->dev); - s32 length; - - length = i2c_smbus_read_i2c_block_data(i2c_client, - CS42L51_FIRSTREG | 0x80, CS42L51_NUMREGS, cache); - if (length != CS42L51_NUMREGS) { - dev_err(&i2c_client->dev, - "I2C read failure, addr=0x%x (ret=%d vs %d)\n", - i2c_client->addr, length, CS42L51_NUMREGS); - return -EIO; - } - - return 0; -} - static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -135,8 +117,7 @@ static const char *chan_mix[] = { "R L", }; -static const struct soc_enum cs42l51_chan_mix = - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(chan_mix), chan_mix); +static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix); static const struct snd_kcontrol_new cs42l51_snd_controls[] = { SOC_DOUBLE_R_SX_TLV("PCM Playback Volume", @@ -192,22 +173,22 @@ static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w, static const char *cs42l51_dac_names[] = {"Direct PCM", "DSP PCM", "ADC"}; -static const struct soc_enum cs42l51_dac_mux_enum = - SOC_ENUM_SINGLE(CS42L51_DAC_CTL, 6, 3, cs42l51_dac_names); +static SOC_ENUM_SINGLE_DECL(cs42l51_dac_mux_enum, + CS42L51_DAC_CTL, 6, cs42l51_dac_names); static const struct snd_kcontrol_new cs42l51_dac_mux_controls = SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum); static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left", "MIC Left", "MIC+preamp Left"}; -static const struct soc_enum cs42l51_adcl_mux_enum = - SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 4, 4, cs42l51_adcl_names); +static SOC_ENUM_SINGLE_DECL(cs42l51_adcl_mux_enum, + CS42L51_ADC_INPUT, 4, cs42l51_adcl_names); static const struct snd_kcontrol_new cs42l51_adcl_mux_controls = SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum); static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right", "MIC Right", "MIC+preamp Right"}; -static const struct soc_enum cs42l51_adcr_mux_enum = - SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 6, 4, cs42l51_adcr_names); +static SOC_ENUM_SINGLE_DECL(cs42l51_adcr_mux_enum, + CS42L51_ADC_INPUT, 6, cs42l51_adcr_names); static const struct snd_kcontrol_new cs42l51_adcr_mux_controls = SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum); @@ -423,21 +404,17 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream, intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24); break; case SND_SOC_DAIFMT_RIGHT_J: - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_S16_BE: + switch (params_width(params)) { + case 16: fmt = CS42L51_DAC_DIF_RJ16; break; - case SNDRV_PCM_FORMAT_S18_3LE: - case SNDRV_PCM_FORMAT_S18_3BE: + case 18: fmt = CS42L51_DAC_DIF_RJ18; break; - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S20_3BE: + case 20: fmt = CS42L51_DAC_DIF_RJ20; break; - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S24_BE: + case 24: fmt = CS42L51_DAC_DIF_RJ24; break; default: @@ -509,16 +486,9 @@ static struct snd_soc_dai_driver cs42l51_dai = { static int cs42l51_probe(struct snd_soc_codec *codec) { - struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec); int ret, reg; - ret = cs42l51_fill_cache(codec); - if (ret < 0) { - dev_err(codec->dev, "failed to fill register cache\n"); - return ret; - } - - ret = snd_soc_codec_set_cache_io(codec, 8, 8, cs42l51->control_type); + 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; @@ -542,8 +512,6 @@ static int cs42l51_probe(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_device_cs42l51 = { .probe = cs42l51_probe, - .reg_cache_size = CS42L51_NUMREGS + 1, - .reg_word_size = sizeof(u8), .controls = cs42l51_snd_controls, .num_controls = ARRAY_SIZE(cs42l51_snd_controls), @@ -553,38 +521,53 @@ static struct snd_soc_codec_driver soc_codec_device_cs42l51 = { .num_dapm_routes = ARRAY_SIZE(cs42l51_routes), }; +static const struct regmap_config cs42l51_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS42L51_CHARGE_FREQ, + .cache_type = REGCACHE_RBTREE, +}; + static int cs42l51_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { struct cs42l51_private *cs42l51; + struct regmap *regmap; + unsigned int val; int ret; + regmap = devm_regmap_init_i2c(i2c_client, &cs42l51_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&i2c_client->dev, "Failed to create regmap: %d\n", + ret); + return ret; + } + /* Verify that we have a CS42L51 */ - ret = i2c_smbus_read_byte_data(i2c_client, CS42L51_CHIP_REV_ID); + ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val); if (ret < 0) { dev_err(&i2c_client->dev, "failed to read I2C\n"); goto error; } - if ((ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) && - (ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) { - dev_err(&i2c_client->dev, "Invalid chip id\n"); + if ((val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) && + (val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) { + dev_err(&i2c_client->dev, "Invalid chip id: %x\n", val); ret = -ENODEV; goto error; } dev_info(&i2c_client->dev, "found device cs42l51 rev %d\n", - ret & 7); + val & 7); cs42l51 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l51_private), GFP_KERNEL); - if (!cs42l51) { - dev_err(&i2c_client->dev, "could not allocate codec\n"); + if (!cs42l51) return -ENOMEM; - } i2c_set_clientdata(i2c_client, cs42l51); - cs42l51->control_type = SND_SOC_I2C; ret = snd_soc_register_codec(&i2c_client->dev, &soc_codec_device_cs42l51, &cs42l51_dai, 1); @@ -604,10 +587,17 @@ static const struct i2c_device_id cs42l51_id[] = { }; MODULE_DEVICE_TABLE(i2c, cs42l51_id); +static const struct of_device_id cs42l51_of_match[] = { + { .compatible = "cirrus,cs42l51", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs42l51_of_match); + static struct i2c_driver cs42l51_i2c_driver = { .driver = { .name = "cs42l51-codec", .owner = THIS_MODULE, + .of_match_table = cs42l51_of_match, }, .id_table = cs42l51_id, .probe = cs42l51_i2c_probe, diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 8b427c977083..be455ea5f2fe 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; @@ -210,13 +210,11 @@ static const char * const cs42l52_adca_text[] = { static const char * const cs42l52_adcb_text[] = { "Input1B", "Input2B", "Input3B", "Input4B", "PGA Input Right"}; -static const struct soc_enum adca_enum = - SOC_ENUM_SINGLE(CS42L52_ADC_PGA_A, 5, - ARRAY_SIZE(cs42l52_adca_text), cs42l52_adca_text); +static SOC_ENUM_SINGLE_DECL(adca_enum, + CS42L52_ADC_PGA_A, 5, cs42l52_adca_text); -static const struct soc_enum adcb_enum = - SOC_ENUM_SINGLE(CS42L52_ADC_PGA_B, 5, - ARRAY_SIZE(cs42l52_adcb_text), cs42l52_adcb_text); +static SOC_ENUM_SINGLE_DECL(adcb_enum, + CS42L52_ADC_PGA_B, 5, cs42l52_adcb_text); static const struct snd_kcontrol_new adca_mux = SOC_DAPM_ENUM("Left ADC Input Capture Mux", adca_enum); @@ -229,32 +227,22 @@ static const char * const mic_bias_level_text[] = { "0.8 +VA", "0.83 +VA", "0.91 +VA" }; -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 SOC_ENUM_SINGLE_DECL(mic_bias_level_enum, + CS42L52_IFACE_CTL2, 0, 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, - ARRAY_SIZE(cs42l52_mic_text), cs42l52_mic_text); +static SOC_ENUM_SINGLE_DECL(mica_enum, + CS42L52_MICA_CTL, 5, cs42l52_mic_text); -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 SOC_ENUM_SINGLE_DECL(micb_enum, + CS42L52_MICB_CTL, 5, cs42l52_mic_text); static const char * const digital_output_mux_text[] = {"ADC", "DSP"}; -static const struct soc_enum digital_output_mux_enum = - SOC_ENUM_SINGLE(CS42L52_ADC_MISC_CTL, 6, - ARRAY_SIZE(digital_output_mux_text), - digital_output_mux_text); +static SOC_ENUM_SINGLE_DECL(digital_output_mux_enum, + CS42L52_ADC_MISC_CTL, 6, + digital_output_mux_text); static const struct snd_kcontrol_new digital_output_mux = SOC_DAPM_ENUM("Digital Output Mux", digital_output_mux_enum); @@ -264,18 +252,18 @@ static const char * const hp_gain_num_text[] = { "0.7099", "0.8399", "1.000", "1.1430" }; -static const struct soc_enum hp_gain_enum = - SOC_ENUM_SINGLE(CS42L52_PB_CTL1, 5, - ARRAY_SIZE(hp_gain_num_text), hp_gain_num_text); +static SOC_ENUM_SINGLE_DECL(hp_gain_enum, + CS42L52_PB_CTL1, 5, + hp_gain_num_text); static const char * const beep_pitch_text[] = { "C4", "C5", "D5", "E5", "F5", "G5", "A5", "B5", "C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7" }; -static const struct soc_enum beep_pitch_enum = - SOC_ENUM_SINGLE(CS42L52_BEEP_FREQ, 4, - ARRAY_SIZE(beep_pitch_text), beep_pitch_text); +static SOC_ENUM_SINGLE_DECL(beep_pitch_enum, + CS42L52_BEEP_FREQ, 4, + beep_pitch_text); static const char * const beep_ontime_text[] = { "86 ms", "430 ms", "780 ms", "1.20 s", "1.50 s", @@ -283,66 +271,66 @@ static const char * const beep_ontime_text[] = { "3.50 s", "3.80 s", "4.20 s", "4.50 s", "4.80 s", "5.20 s" }; -static const struct soc_enum beep_ontime_enum = - SOC_ENUM_SINGLE(CS42L52_BEEP_FREQ, 0, - ARRAY_SIZE(beep_ontime_text), beep_ontime_text); +static SOC_ENUM_SINGLE_DECL(beep_ontime_enum, + CS42L52_BEEP_FREQ, 0, + beep_ontime_text); static const char * const beep_offtime_text[] = { "1.23 s", "2.58 s", "3.90 s", "5.20 s", "6.60 s", "8.05 s", "9.35 s", "10.80 s" }; -static const struct soc_enum beep_offtime_enum = - SOC_ENUM_SINGLE(CS42L52_BEEP_VOL, 5, - ARRAY_SIZE(beep_offtime_text), beep_offtime_text); +static SOC_ENUM_SINGLE_DECL(beep_offtime_enum, + CS42L52_BEEP_VOL, 5, + beep_offtime_text); static const char * const beep_config_text[] = { "Off", "Single", "Multiple", "Continuous" }; -static const struct soc_enum beep_config_enum = - SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 6, - ARRAY_SIZE(beep_config_text), beep_config_text); +static SOC_ENUM_SINGLE_DECL(beep_config_enum, + CS42L52_BEEP_TONE_CTL, 6, + beep_config_text); static const char * const beep_bass_text[] = { "50 Hz", "100 Hz", "200 Hz", "250 Hz" }; -static const struct soc_enum beep_bass_enum = - SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 1, - ARRAY_SIZE(beep_bass_text), beep_bass_text); +static SOC_ENUM_SINGLE_DECL(beep_bass_enum, + CS42L52_BEEP_TONE_CTL, 1, + beep_bass_text); static const char * const beep_treble_text[] = { "5 kHz", "7 kHz", "10 kHz", " 15 kHz" }; -static const struct soc_enum beep_treble_enum = - SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 3, - ARRAY_SIZE(beep_treble_text), beep_treble_text); +static SOC_ENUM_SINGLE_DECL(beep_treble_enum, + CS42L52_BEEP_TONE_CTL, 3, + beep_treble_text); static const char * const ng_threshold_text[] = { "-34dB", "-37dB", "-40dB", "-43dB", "-46dB", "-52dB", "-58dB", "-64dB" }; -static const struct soc_enum ng_threshold_enum = - SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 2, - ARRAY_SIZE(ng_threshold_text), ng_threshold_text); +static SOC_ENUM_SINGLE_DECL(ng_threshold_enum, + CS42L52_NOISE_GATE_CTL, 2, + ng_threshold_text); static const char * const cs42l52_ng_delay_text[] = { "50ms", "100ms", "150ms", "200ms"}; -static const struct soc_enum ng_delay_enum = - SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 0, - ARRAY_SIZE(cs42l52_ng_delay_text), cs42l52_ng_delay_text); +static SOC_ENUM_SINGLE_DECL(ng_delay_enum, + CS42L52_NOISE_GATE_CTL, 0, + cs42l52_ng_delay_text); static const char * const cs42l52_ng_type_text[] = { "Apply Specific", "Apply All" }; -static const struct soc_enum ng_type_enum = - SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 6, - ARRAY_SIZE(cs42l52_ng_type_text), cs42l52_ng_type_text); +static SOC_ENUM_SINGLE_DECL(ng_type_enum, + CS42L52_NOISE_GATE_CTL, 6, + cs42l52_ng_type_text); static const char * const left_swap_text[] = { "Left", "LR 2", "Right"}; @@ -531,6 +519,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 +562,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 +962,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 +1119,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 +1187,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 +1201,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 +1269,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 +1305,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 +1322,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/cs42l73.c b/sound/soc/codecs/cs42l73.c index 549d5d6a3fef..06f429184821 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -278,13 +278,13 @@ static const DECLARE_TLV_DB_SCALE(attn_tlv, -6300, 100, 1); static const char * const cs42l73_pgaa_text[] = { "Line A", "Mic 1" }; static const char * const cs42l73_pgab_text[] = { "Line B", "Mic 2" }; -static const struct soc_enum pgaa_enum = - SOC_ENUM_SINGLE(CS42L73_ADCIPC, 3, - ARRAY_SIZE(cs42l73_pgaa_text), cs42l73_pgaa_text); +static SOC_ENUM_SINGLE_DECL(pgaa_enum, + CS42L73_ADCIPC, 3, + cs42l73_pgaa_text); -static const struct soc_enum pgab_enum = - SOC_ENUM_SINGLE(CS42L73_ADCIPC, 7, - ARRAY_SIZE(cs42l73_pgab_text), cs42l73_pgab_text); +static SOC_ENUM_SINGLE_DECL(pgab_enum, + CS42L73_ADCIPC, 7, + cs42l73_pgab_text); static const struct snd_kcontrol_new pgaa_mux = SOC_DAPM_ENUM("Left Analog Input Capture Mux", pgaa_enum); @@ -309,9 +309,9 @@ static const struct snd_kcontrol_new input_right_mixer[] = { static const char * const cs42l73_ng_delay_text[] = { "50ms", "100ms", "150ms", "200ms" }; -static const struct soc_enum ng_delay_enum = - SOC_ENUM_SINGLE(CS42L73_NGCAB, 0, - ARRAY_SIZE(cs42l73_ng_delay_text), cs42l73_ng_delay_text); +static SOC_ENUM_SINGLE_DECL(ng_delay_enum, + CS42L73_NGCAB, 0, + cs42l73_ng_delay_text); static const char * const cs42l73_mono_mix_texts[] = { "Left", "Right", "Mono Mix"}; @@ -357,19 +357,19 @@ static const struct snd_kcontrol_new esl_xsp_mixer = static const char * const cs42l73_ip_swap_text[] = { "Stereo", "Mono A", "Mono B", "Swap A-B"}; -static const struct soc_enum ip_swap_enum = - SOC_ENUM_SINGLE(CS42L73_MIOPC, 6, - ARRAY_SIZE(cs42l73_ip_swap_text), cs42l73_ip_swap_text); +static SOC_ENUM_SINGLE_DECL(ip_swap_enum, + CS42L73_MIOPC, 6, + cs42l73_ip_swap_text); static const char * const cs42l73_spo_mixer_text[] = {"Mono", "Stereo"}; -static const struct soc_enum vsp_output_mux_enum = - SOC_ENUM_SINGLE(CS42L73_MIXERCTL, 5, - ARRAY_SIZE(cs42l73_spo_mixer_text), cs42l73_spo_mixer_text); +static SOC_ENUM_SINGLE_DECL(vsp_output_mux_enum, + CS42L73_MIXERCTL, 5, + cs42l73_spo_mixer_text); -static const struct soc_enum xsp_output_mux_enum = - SOC_ENUM_SINGLE(CS42L73_MIXERCTL, 4, - ARRAY_SIZE(cs42l73_spo_mixer_text), cs42l73_spo_mixer_text); +static SOC_ENUM_SINGLE_DECL(xsp_output_mux_enum, + CS42L73_MIXERCTL, 4, + cs42l73_spo_mixer_text); static const struct snd_kcontrol_new vsp_output_mux = SOC_DAPM_ENUM("Route", vsp_output_mux_enum); @@ -1108,7 +1108,7 @@ static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return 0; } -static u32 cs42l73_asrc_rates[] = { +static const unsigned int cs42l73_asrc_rates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }; @@ -1241,7 +1241,7 @@ static int cs42l73_set_tristate(struct snd_soc_dai *dai, int tristate) 0x7F, tristate << 7); } -static struct snd_pcm_hw_constraint_list constraints_12_24 = { +static const struct snd_pcm_hw_constraint_list constraints_12_24 = { .count = ARRAY_SIZE(cs42l73_asrc_rates), .list = cs42l73_asrc_rates, }; @@ -1255,9 +1255,6 @@ static int cs42l73_pcm_startup(struct snd_pcm_substream *substream, return 0; } -/* SNDRV_PCM_RATE_KNOT -> 12000, 24000 Hz, limit with constraint list */ -#define CS42L73_RATES (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT) - #define CS42L73_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) @@ -1278,14 +1275,14 @@ static struct snd_soc_dai_driver cs42l73_dai[] = { .stream_name = "XSP Playback", .channels_min = 1, .channels_max = 2, - .rates = CS42L73_RATES, + .rates = SNDRV_PCM_RATE_KNOT, .formats = CS42L73_FORMATS, }, .capture = { .stream_name = "XSP Capture", .channels_min = 1, .channels_max = 2, - .rates = CS42L73_RATES, + .rates = SNDRV_PCM_RATE_KNOT, .formats = CS42L73_FORMATS, }, .ops = &cs42l73_ops, @@ -1298,14 +1295,14 @@ static struct snd_soc_dai_driver cs42l73_dai[] = { .stream_name = "ASP Playback", .channels_min = 2, .channels_max = 2, - .rates = CS42L73_RATES, + .rates = SNDRV_PCM_RATE_KNOT, .formats = CS42L73_FORMATS, }, .capture = { .stream_name = "ASP Capture", .channels_min = 2, .channels_max = 2, - .rates = CS42L73_RATES, + .rates = SNDRV_PCM_RATE_KNOT, .formats = CS42L73_FORMATS, }, .ops = &cs42l73_ops, @@ -1318,14 +1315,14 @@ static struct snd_soc_dai_driver cs42l73_dai[] = { .stream_name = "VSP Playback", .channels_min = 1, .channels_max = 2, - .rates = CS42L73_RATES, + .rates = SNDRV_PCM_RATE_KNOT, .formats = CS42L73_FORMATS, }, .capture = { .stream_name = "VSP Capture", .channels_min = 1, .channels_max = 2, - .rates = CS42L73_RATES, + .rates = SNDRV_PCM_RATE_KNOT, .formats = CS42L73_FORMATS, }, .ops = &cs42l73_ops, diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index 9c1231456502..01e55fc72307 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -307,29 +307,29 @@ static const char * const da7210_hpf_cutoff_txt[] = { "Fs/8192*pi", "Fs/4096*pi", "Fs/2048*pi", "Fs/1024*pi" }; -static const struct soc_enum da7210_dac_hpf_cutoff = - SOC_ENUM_SINGLE(DA7210_DAC_HPF, 0, 4, da7210_hpf_cutoff_txt); +static SOC_ENUM_SINGLE_DECL(da7210_dac_hpf_cutoff, + DA7210_DAC_HPF, 0, da7210_hpf_cutoff_txt); -static const struct soc_enum da7210_adc_hpf_cutoff = - SOC_ENUM_SINGLE(DA7210_ADC_HPF, 0, 4, da7210_hpf_cutoff_txt); +static SOC_ENUM_SINGLE_DECL(da7210_adc_hpf_cutoff, + DA7210_ADC_HPF, 0, da7210_hpf_cutoff_txt); /* ADC and DAC voice (8kHz) high pass cutoff value */ static const char * const da7210_vf_cutoff_txt[] = { "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" }; -static const struct soc_enum da7210_dac_vf_cutoff = - SOC_ENUM_SINGLE(DA7210_DAC_HPF, 4, 8, da7210_vf_cutoff_txt); +static SOC_ENUM_SINGLE_DECL(da7210_dac_vf_cutoff, + DA7210_DAC_HPF, 4, da7210_vf_cutoff_txt); -static const struct soc_enum da7210_adc_vf_cutoff = - SOC_ENUM_SINGLE(DA7210_ADC_HPF, 4, 8, da7210_vf_cutoff_txt); +static SOC_ENUM_SINGLE_DECL(da7210_adc_vf_cutoff, + DA7210_ADC_HPF, 4, da7210_vf_cutoff_txt); static const char *da7210_hp_mode_txt[] = { "Class H", "Class G" }; -static const struct soc_enum da7210_hp_mode_sel = - SOC_ENUM_SINGLE(DA7210_HP_CFG, 0, 2, da7210_hp_mode_txt); +static SOC_ENUM_SINGLE_DECL(da7210_hp_mode_sel, + DA7210_HP_CFG, 0, da7210_hp_mode_txt); /* ALC can be enabled only if noise suppression is disabled */ static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol, @@ -778,17 +778,17 @@ static int da7210_hw_params(struct snd_pcm_substream *substream, dai_cfg1 = 0xFC & snd_soc_read(codec, DA7210_DAI_CFG1); - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: dai_cfg1 |= DA7210_DAI_WORD_S16_LE; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: dai_cfg1 |= DA7210_DAI_WORD_S20_3LE; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: dai_cfg1 |= DA7210_DAI_WORD_S24_LE; break; - case SNDRV_PCM_FORMAT_S32_LE: + case 32: dai_cfg1 |= DA7210_DAI_WORD_S32_LE; break; default: @@ -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/da7213.c b/sound/soc/codecs/da7213.c index 4a6f1daf911f..439d10387f10 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -63,30 +63,30 @@ static const char * const da7213_voice_hpf_corner_txt[] = { "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" }; -static const struct soc_enum da7213_dac_voice_hpf_corner = - SOC_ENUM_SINGLE(DA7213_DAC_FILTERS1, DA7213_VOICE_HPF_CORNER_SHIFT, - DA7213_VOICE_HPF_CORNER_MAX, - da7213_voice_hpf_corner_txt); +static SOC_ENUM_SINGLE_DECL(da7213_dac_voice_hpf_corner, + DA7213_DAC_FILTERS1, + DA7213_VOICE_HPF_CORNER_SHIFT, + da7213_voice_hpf_corner_txt); -static const struct soc_enum da7213_adc_voice_hpf_corner = - SOC_ENUM_SINGLE(DA7213_ADC_FILTERS1, DA7213_VOICE_HPF_CORNER_SHIFT, - DA7213_VOICE_HPF_CORNER_MAX, - da7213_voice_hpf_corner_txt); +static SOC_ENUM_SINGLE_DECL(da7213_adc_voice_hpf_corner, + DA7213_ADC_FILTERS1, + DA7213_VOICE_HPF_CORNER_SHIFT, + da7213_voice_hpf_corner_txt); /* ADC and DAC high pass filter cutoff value */ static const char * const da7213_audio_hpf_corner_txt[] = { "Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000" }; -static const struct soc_enum da7213_dac_audio_hpf_corner = - SOC_ENUM_SINGLE(DA7213_DAC_FILTERS1, DA7213_AUDIO_HPF_CORNER_SHIFT, - DA7213_AUDIO_HPF_CORNER_MAX, - da7213_audio_hpf_corner_txt); +static SOC_ENUM_SINGLE_DECL(da7213_dac_audio_hpf_corner, + DA7213_DAC_FILTERS1 + , DA7213_AUDIO_HPF_CORNER_SHIFT, + da7213_audio_hpf_corner_txt); -static const struct soc_enum da7213_adc_audio_hpf_corner = - SOC_ENUM_SINGLE(DA7213_ADC_FILTERS1, DA7213_AUDIO_HPF_CORNER_SHIFT, - DA7213_AUDIO_HPF_CORNER_MAX, - da7213_audio_hpf_corner_txt); +static SOC_ENUM_SINGLE_DECL(da7213_adc_audio_hpf_corner, + DA7213_ADC_FILTERS1, + DA7213_AUDIO_HPF_CORNER_SHIFT, + da7213_audio_hpf_corner_txt); /* Gain ramping rate value */ static const char * const da7213_gain_ramp_rate_txt[] = { @@ -94,52 +94,50 @@ static const char * const da7213_gain_ramp_rate_txt[] = { "nominal rate / 32" }; -static const struct soc_enum da7213_gain_ramp_rate = - SOC_ENUM_SINGLE(DA7213_GAIN_RAMP_CTRL, DA7213_GAIN_RAMP_RATE_SHIFT, - DA7213_GAIN_RAMP_RATE_MAX, da7213_gain_ramp_rate_txt); +static SOC_ENUM_SINGLE_DECL(da7213_gain_ramp_rate, + DA7213_GAIN_RAMP_CTRL, + DA7213_GAIN_RAMP_RATE_SHIFT, + da7213_gain_ramp_rate_txt); /* DAC noise gate setup time value */ static const char * const da7213_dac_ng_setup_time_txt[] = { "256 samples", "512 samples", "1024 samples", "2048 samples" }; -static const struct soc_enum da7213_dac_ng_setup_time = - SOC_ENUM_SINGLE(DA7213_DAC_NG_SETUP_TIME, - DA7213_DAC_NG_SETUP_TIME_SHIFT, - DA7213_DAC_NG_SETUP_TIME_MAX, - da7213_dac_ng_setup_time_txt); +static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_setup_time, + DA7213_DAC_NG_SETUP_TIME, + DA7213_DAC_NG_SETUP_TIME_SHIFT, + da7213_dac_ng_setup_time_txt); /* DAC noise gate rampup rate value */ static const char * const da7213_dac_ng_rampup_txt[] = { "0.02 ms/dB", "0.16 ms/dB" }; -static const struct soc_enum da7213_dac_ng_rampup_rate = - SOC_ENUM_SINGLE(DA7213_DAC_NG_SETUP_TIME, - DA7213_DAC_NG_RAMPUP_RATE_SHIFT, - DA7213_DAC_NG_RAMP_RATE_MAX, - da7213_dac_ng_rampup_txt); +static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_rampup_rate, + DA7213_DAC_NG_SETUP_TIME, + DA7213_DAC_NG_RAMPUP_RATE_SHIFT, + da7213_dac_ng_rampup_txt); /* DAC noise gate rampdown rate value */ static const char * const da7213_dac_ng_rampdown_txt[] = { "0.64 ms/dB", "20.48 ms/dB" }; -static const struct soc_enum da7213_dac_ng_rampdown_rate = - SOC_ENUM_SINGLE(DA7213_DAC_NG_SETUP_TIME, - DA7213_DAC_NG_RAMPDN_RATE_SHIFT, - DA7213_DAC_NG_RAMP_RATE_MAX, - da7213_dac_ng_rampdown_txt); +static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_rampdown_rate, + DA7213_DAC_NG_SETUP_TIME, + DA7213_DAC_NG_RAMPDN_RATE_SHIFT, + da7213_dac_ng_rampdown_txt); /* DAC soft mute rate value */ static const char * const da7213_dac_soft_mute_rate_txt[] = { "1", "2", "4", "8", "16", "32", "64" }; -static const struct soc_enum da7213_dac_soft_mute_rate = - SOC_ENUM_SINGLE(DA7213_DAC_FILTERS5, DA7213_DAC_SOFTMUTE_RATE_SHIFT, - DA7213_DAC_SOFTMUTE_RATE_MAX, - da7213_dac_soft_mute_rate_txt); +static SOC_ENUM_SINGLE_DECL(da7213_dac_soft_mute_rate, + DA7213_DAC_FILTERS5, + DA7213_DAC_SOFTMUTE_RATE_SHIFT, + da7213_dac_soft_mute_rate_txt); /* ALC Attack Rate select */ static const char * const da7213_alc_attack_rate_txt[] = { @@ -147,9 +145,10 @@ static const char * const da7213_alc_attack_rate_txt[] = { "5632/fs", "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs" }; -static const struct soc_enum da7213_alc_attack_rate = - SOC_ENUM_SINGLE(DA7213_ALC_CTRL2, DA7213_ALC_ATTACK_SHIFT, - DA7213_ALC_ATTACK_MAX, da7213_alc_attack_rate_txt); +static SOC_ENUM_SINGLE_DECL(da7213_alc_attack_rate, + DA7213_ALC_CTRL2, + DA7213_ALC_ATTACK_SHIFT, + da7213_alc_attack_rate_txt); /* ALC Release Rate select */ static const char * const da7213_alc_release_rate_txt[] = { @@ -157,9 +156,10 @@ static const char * const da7213_alc_release_rate_txt[] = { "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs" }; -static const struct soc_enum da7213_alc_release_rate = - SOC_ENUM_SINGLE(DA7213_ALC_CTRL2, DA7213_ALC_RELEASE_SHIFT, - DA7213_ALC_RELEASE_MAX, da7213_alc_release_rate_txt); +static SOC_ENUM_SINGLE_DECL(da7213_alc_release_rate, + DA7213_ALC_CTRL2, + DA7213_ALC_RELEASE_SHIFT, + da7213_alc_release_rate_txt); /* ALC Hold Time select */ static const char * const da7213_alc_hold_time_txt[] = { @@ -168,22 +168,25 @@ static const char * const da7213_alc_hold_time_txt[] = { "253952/fs", "507904/fs", "1015808/fs", "2031616/fs" }; -static const struct soc_enum da7213_alc_hold_time = - SOC_ENUM_SINGLE(DA7213_ALC_CTRL3, DA7213_ALC_HOLD_SHIFT, - DA7213_ALC_HOLD_MAX, da7213_alc_hold_time_txt); +static SOC_ENUM_SINGLE_DECL(da7213_alc_hold_time, + DA7213_ALC_CTRL3, + DA7213_ALC_HOLD_SHIFT, + da7213_alc_hold_time_txt); /* ALC Input Signal Tracking rate select */ static const char * const da7213_alc_integ_rate_txt[] = { "1/4", "1/16", "1/256", "1/65536" }; -static const struct soc_enum da7213_alc_integ_attack_rate = - SOC_ENUM_SINGLE(DA7213_ALC_CTRL3, DA7213_ALC_INTEG_ATTACK_SHIFT, - DA7213_ALC_INTEG_MAX, da7213_alc_integ_rate_txt); +static SOC_ENUM_SINGLE_DECL(da7213_alc_integ_attack_rate, + DA7213_ALC_CTRL3, + DA7213_ALC_INTEG_ATTACK_SHIFT, + da7213_alc_integ_rate_txt); -static const struct soc_enum da7213_alc_integ_release_rate = - SOC_ENUM_SINGLE(DA7213_ALC_CTRL3, DA7213_ALC_INTEG_RELEASE_SHIFT, - DA7213_ALC_INTEG_MAX, da7213_alc_integ_rate_txt); +static SOC_ENUM_SINGLE_DECL(da7213_alc_integ_release_rate, + DA7213_ALC_CTRL3, + DA7213_ALC_INTEG_RELEASE_SHIFT, + da7213_alc_integ_rate_txt); /* @@ -584,15 +587,17 @@ static const char * const da7213_mic_amp_in_sel_txt[] = { "Differential", "MIC_P", "MIC_N" }; -static const struct soc_enum da7213_mic_1_amp_in_sel = - SOC_ENUM_SINGLE(DA7213_MIC_1_CTRL, DA7213_MIC_AMP_IN_SEL_SHIFT, - DA7213_MIC_AMP_IN_SEL_MAX, da7213_mic_amp_in_sel_txt); +static SOC_ENUM_SINGLE_DECL(da7213_mic_1_amp_in_sel, + DA7213_MIC_1_CTRL, + DA7213_MIC_AMP_IN_SEL_SHIFT, + da7213_mic_amp_in_sel_txt); static const struct snd_kcontrol_new da7213_mic_1_amp_in_sel_mux = SOC_DAPM_ENUM("Mic 1 Amp Source MUX", da7213_mic_1_amp_in_sel); -static const struct soc_enum da7213_mic_2_amp_in_sel = - SOC_ENUM_SINGLE(DA7213_MIC_2_CTRL, DA7213_MIC_AMP_IN_SEL_SHIFT, - DA7213_MIC_AMP_IN_SEL_MAX, da7213_mic_amp_in_sel_txt); +static SOC_ENUM_SINGLE_DECL(da7213_mic_2_amp_in_sel, + DA7213_MIC_2_CTRL, + DA7213_MIC_AMP_IN_SEL_SHIFT, + da7213_mic_amp_in_sel_txt); static const struct snd_kcontrol_new da7213_mic_2_amp_in_sel_mux = SOC_DAPM_ENUM("Mic 2 Amp Source MUX", da7213_mic_2_amp_in_sel); @@ -601,15 +606,17 @@ static const char * const da7213_dai_src_txt[] = { "ADC Left", "ADC Right", "DAI Input Left", "DAI Input Right" }; -static const struct soc_enum da7213_dai_l_src = - SOC_ENUM_SINGLE(DA7213_DIG_ROUTING_DAI, DA7213_DAI_L_SRC_SHIFT, - DA7213_DAI_SRC_MAX, da7213_dai_src_txt); +static SOC_ENUM_SINGLE_DECL(da7213_dai_l_src, + DA7213_DIG_ROUTING_DAI, + DA7213_DAI_L_SRC_SHIFT, + da7213_dai_src_txt); static const struct snd_kcontrol_new da7213_dai_l_src_mux = SOC_DAPM_ENUM("DAI Left Source MUX", da7213_dai_l_src); -static const struct soc_enum da7213_dai_r_src = - SOC_ENUM_SINGLE(DA7213_DIG_ROUTING_DAI, DA7213_DAI_R_SRC_SHIFT, - DA7213_DAI_SRC_MAX, da7213_dai_src_txt); +static SOC_ENUM_SINGLE_DECL(da7213_dai_r_src, + DA7213_DIG_ROUTING_DAI, + DA7213_DAI_R_SRC_SHIFT, + da7213_dai_src_txt); static const struct snd_kcontrol_new da7213_dai_r_src_mux = SOC_DAPM_ENUM("DAI Right Source MUX", da7213_dai_r_src); @@ -619,15 +626,17 @@ static const char * const da7213_dac_src_txt[] = { "DAI Input Right" }; -static const struct soc_enum da7213_dac_l_src = - SOC_ENUM_SINGLE(DA7213_DIG_ROUTING_DAC, DA7213_DAC_L_SRC_SHIFT, - DA7213_DAC_SRC_MAX, da7213_dac_src_txt); +static SOC_ENUM_SINGLE_DECL(da7213_dac_l_src, + DA7213_DIG_ROUTING_DAC, + DA7213_DAC_L_SRC_SHIFT, + da7213_dac_src_txt); static const struct snd_kcontrol_new da7213_dac_l_src_mux = SOC_DAPM_ENUM("DAC Left Source MUX", da7213_dac_l_src); -static const struct soc_enum da7213_dac_r_src = - SOC_ENUM_SINGLE(DA7213_DIG_ROUTING_DAC, DA7213_DAC_R_SRC_SHIFT, - DA7213_DAC_SRC_MAX, da7213_dac_src_txt); +static SOC_ENUM_SINGLE_DECL(da7213_dac_r_src, + DA7213_DIG_ROUTING_DAC, + DA7213_DAC_R_SRC_SHIFT, + da7213_dac_src_txt); static const struct snd_kcontrol_new da7213_dac_r_src_mux = SOC_DAPM_ENUM("DAC Right Source MUX", da7213_dac_r_src); @@ -1067,17 +1076,17 @@ static int da7213_hw_params(struct snd_pcm_substream *substream, u8 fs; /* Set DAI format */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: dai_ctrl |= DA7213_DAI_WORD_LENGTH_S16_LE; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: dai_ctrl |= DA7213_DAI_WORD_LENGTH_S20_LE; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: dai_ctrl |= DA7213_DAI_WORD_LENGTH_S24_LE; break; - case SNDRV_PCM_FORMAT_S32_LE: + case 32: dai_ctrl |= DA7213_DAI_WORD_LENGTH_S32_LE; break; default: diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index dc0284dc9e6f..4d1c302f5a76 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -269,81 +269,65 @@ static const char *da732x_hpf_voice[] = { "150Hz", "200Hz", "300Hz", "400Hz" }; -static const struct soc_enum da732x_dac1_hpf_mode_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_DAC1_HPF, DA732X_HPF_MODE_SHIFT, - DA732X_HPF_MODE_MAX, da732x_hpf_mode) -}; +static SOC_ENUM_SINGLE_DECL(da732x_dac1_hpf_mode_enum, + DA732X_REG_DAC1_HPF, DA732X_HPF_MODE_SHIFT, + da732x_hpf_mode); -static const struct soc_enum da732x_dac2_hpf_mode_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_DAC2_HPF, DA732X_HPF_MODE_SHIFT, - DA732X_HPF_MODE_MAX, da732x_hpf_mode) -}; +static SOC_ENUM_SINGLE_DECL(da732x_dac2_hpf_mode_enum, + DA732X_REG_DAC2_HPF, DA732X_HPF_MODE_SHIFT, + da732x_hpf_mode); -static const struct soc_enum da732x_dac3_hpf_mode_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_DAC3_HPF, DA732X_HPF_MODE_SHIFT, - DA732X_HPF_MODE_MAX, da732x_hpf_mode) -}; +static SOC_ENUM_SINGLE_DECL(da732x_dac3_hpf_mode_enum, + DA732X_REG_DAC3_HPF, DA732X_HPF_MODE_SHIFT, + da732x_hpf_mode); -static const struct soc_enum da732x_adc1_hpf_mode_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_ADC1_HPF, DA732X_HPF_MODE_SHIFT, - DA732X_HPF_MODE_MAX, da732x_hpf_mode) -}; +static SOC_ENUM_SINGLE_DECL(da732x_adc1_hpf_mode_enum, + DA732X_REG_ADC1_HPF, DA732X_HPF_MODE_SHIFT, + da732x_hpf_mode); -static const struct soc_enum da732x_adc2_hpf_mode_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_ADC2_HPF, DA732X_HPF_MODE_SHIFT, - DA732X_HPF_MODE_MAX, da732x_hpf_mode) -}; +static SOC_ENUM_SINGLE_DECL(da732x_adc2_hpf_mode_enum, + DA732X_REG_ADC2_HPF, DA732X_HPF_MODE_SHIFT, + da732x_hpf_mode); -static const struct soc_enum da732x_dac1_hp_filter_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_DAC1_HPF, DA732X_HPF_MUSIC_SHIFT, - DA732X_HPF_MUSIC_MAX, da732x_hpf_music) -}; +static SOC_ENUM_SINGLE_DECL(da732x_dac1_hp_filter_enum, + DA732X_REG_DAC1_HPF, DA732X_HPF_MUSIC_SHIFT, + da732x_hpf_music); -static const struct soc_enum da732x_dac2_hp_filter_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_DAC2_HPF, DA732X_HPF_MUSIC_SHIFT, - DA732X_HPF_MUSIC_MAX, da732x_hpf_music) -}; +static SOC_ENUM_SINGLE_DECL(da732x_dac2_hp_filter_enum, + DA732X_REG_DAC2_HPF, DA732X_HPF_MUSIC_SHIFT, + da732x_hpf_music); -static const struct soc_enum da732x_dac3_hp_filter_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_DAC3_HPF, DA732X_HPF_MUSIC_SHIFT, - DA732X_HPF_MUSIC_MAX, da732x_hpf_music) -}; +static SOC_ENUM_SINGLE_DECL(da732x_dac3_hp_filter_enum, + DA732X_REG_DAC3_HPF, DA732X_HPF_MUSIC_SHIFT, + da732x_hpf_music); -static const struct soc_enum da732x_adc1_hp_filter_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_ADC1_HPF, DA732X_HPF_MUSIC_SHIFT, - DA732X_HPF_MUSIC_MAX, da732x_hpf_music) -}; +static SOC_ENUM_SINGLE_DECL(da732x_adc1_hp_filter_enum, + DA732X_REG_ADC1_HPF, DA732X_HPF_MUSIC_SHIFT, + da732x_hpf_music); -static const struct soc_enum da732x_adc2_hp_filter_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_ADC2_HPF, DA732X_HPF_MUSIC_SHIFT, - DA732X_HPF_MUSIC_MAX, da732x_hpf_music) -}; +static SOC_ENUM_SINGLE_DECL(da732x_adc2_hp_filter_enum, + DA732X_REG_ADC2_HPF, DA732X_HPF_MUSIC_SHIFT, + da732x_hpf_music); -static const struct soc_enum da732x_dac1_voice_filter_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_DAC1_HPF, DA732X_HPF_VOICE_SHIFT, - DA732X_HPF_VOICE_MAX, da732x_hpf_voice) -}; +static SOC_ENUM_SINGLE_DECL(da732x_dac1_voice_filter_enum, + DA732X_REG_DAC1_HPF, DA732X_HPF_VOICE_SHIFT, + da732x_hpf_voice); -static const struct soc_enum da732x_dac2_voice_filter_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_DAC2_HPF, DA732X_HPF_VOICE_SHIFT, - DA732X_HPF_VOICE_MAX, da732x_hpf_voice) -}; +static SOC_ENUM_SINGLE_DECL(da732x_dac2_voice_filter_enum, + DA732X_REG_DAC2_HPF, DA732X_HPF_VOICE_SHIFT, + da732x_hpf_voice); -static const struct soc_enum da732x_dac3_voice_filter_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_DAC3_HPF, DA732X_HPF_VOICE_SHIFT, - DA732X_HPF_VOICE_MAX, da732x_hpf_voice) -}; +static SOC_ENUM_SINGLE_DECL(da732x_dac3_voice_filter_enum, + DA732X_REG_DAC3_HPF, DA732X_HPF_VOICE_SHIFT, + da732x_hpf_voice); -static const struct soc_enum da732x_adc1_voice_filter_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_ADC1_HPF, DA732X_HPF_VOICE_SHIFT, - DA732X_HPF_VOICE_MAX, da732x_hpf_voice) -}; - -static const struct soc_enum da732x_adc2_voice_filter_enum[] = { - SOC_ENUM_SINGLE(DA732X_REG_ADC2_HPF, DA732X_HPF_VOICE_SHIFT, - DA732X_HPF_VOICE_MAX, da732x_hpf_voice) -}; +static SOC_ENUM_SINGLE_DECL(da732x_adc1_voice_filter_enum, + DA732X_REG_ADC1_HPF, DA732X_HPF_VOICE_SHIFT, + da732x_hpf_voice); +static SOC_ENUM_SINGLE_DECL(da732x_adc2_voice_filter_enum, + DA732X_REG_ADC2_HPF, DA732X_HPF_VOICE_SHIFT, + da732x_hpf_voice); static int da732x_hpf_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -714,65 +698,65 @@ static const char *enable_text[] = { }; /* ADC1LMUX */ -static const struct soc_enum adc1l_enum = - SOC_ENUM_SINGLE(DA732X_REG_INP_MUX, DA732X_ADC1L_MUX_SEL_SHIFT, - DA732X_ADCL_MUX_MAX, adcl_text); +static SOC_ENUM_SINGLE_DECL(adc1l_enum, + DA732X_REG_INP_MUX, DA732X_ADC1L_MUX_SEL_SHIFT, + adcl_text); static const struct snd_kcontrol_new adc1l_mux = SOC_DAPM_ENUM("ADC Route", adc1l_enum); /* ADC1RMUX */ -static const struct soc_enum adc1r_enum = - SOC_ENUM_SINGLE(DA732X_REG_INP_MUX, DA732X_ADC1R_MUX_SEL_SHIFT, - DA732X_ADCR_MUX_MAX, adcr_text); +static SOC_ENUM_SINGLE_DECL(adc1r_enum, + DA732X_REG_INP_MUX, DA732X_ADC1R_MUX_SEL_SHIFT, + adcr_text); static const struct snd_kcontrol_new adc1r_mux = SOC_DAPM_ENUM("ADC Route", adc1r_enum); /* ADC2LMUX */ -static const struct soc_enum adc2l_enum = - SOC_ENUM_SINGLE(DA732X_REG_INP_MUX, DA732X_ADC2L_MUX_SEL_SHIFT, - DA732X_ADCL_MUX_MAX, adcl_text); +static SOC_ENUM_SINGLE_DECL(adc2l_enum, + DA732X_REG_INP_MUX, DA732X_ADC2L_MUX_SEL_SHIFT, + adcl_text); static const struct snd_kcontrol_new adc2l_mux = SOC_DAPM_ENUM("ADC Route", adc2l_enum); /* ADC2RMUX */ -static const struct soc_enum adc2r_enum = - SOC_ENUM_SINGLE(DA732X_REG_INP_MUX, DA732X_ADC2R_MUX_SEL_SHIFT, - DA732X_ADCR_MUX_MAX, adcr_text); +static SOC_ENUM_SINGLE_DECL(adc2r_enum, + DA732X_REG_INP_MUX, DA732X_ADC2R_MUX_SEL_SHIFT, + adcr_text); static const struct snd_kcontrol_new adc2r_mux = SOC_DAPM_ENUM("ADC Route", adc2r_enum); -static const struct soc_enum da732x_hp_left_output = - SOC_ENUM_SINGLE(DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN_SHIFT, - DA732X_DAC_EN_MAX, enable_text); +static SOC_ENUM_SINGLE_DECL(da732x_hp_left_output, + DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN_SHIFT, + enable_text); static const struct snd_kcontrol_new hpl_mux = SOC_DAPM_ENUM("HPL Switch", da732x_hp_left_output); -static const struct soc_enum da732x_hp_right_output = - SOC_ENUM_SINGLE(DA732X_REG_HPR, DA732X_HP_OUT_DAC_EN_SHIFT, - DA732X_DAC_EN_MAX, enable_text); +static SOC_ENUM_SINGLE_DECL(da732x_hp_right_output, + DA732X_REG_HPR, DA732X_HP_OUT_DAC_EN_SHIFT, + enable_text); static const struct snd_kcontrol_new hpr_mux = SOC_DAPM_ENUM("HPR Switch", da732x_hp_right_output); -static const struct soc_enum da732x_speaker_output = - SOC_ENUM_SINGLE(DA732X_REG_LIN3, DA732X_LOUT_DAC_EN_SHIFT, - DA732X_DAC_EN_MAX, enable_text); +static SOC_ENUM_SINGLE_DECL(da732x_speaker_output, + DA732X_REG_LIN3, DA732X_LOUT_DAC_EN_SHIFT, + enable_text); static const struct snd_kcontrol_new spk_mux = SOC_DAPM_ENUM("SPK Switch", da732x_speaker_output); -static const struct soc_enum da732x_lout4_output = - SOC_ENUM_SINGLE(DA732X_REG_LIN4, DA732X_LOUT_DAC_EN_SHIFT, - DA732X_DAC_EN_MAX, enable_text); +static SOC_ENUM_SINGLE_DECL(da732x_lout4_output, + DA732X_REG_LIN4, DA732X_LOUT_DAC_EN_SHIFT, + enable_text); static const struct snd_kcontrol_new lout4_mux = SOC_DAPM_ENUM("LOUT4 Switch", da732x_lout4_output); -static const struct soc_enum da732x_lout2_output = - SOC_ENUM_SINGLE(DA732X_REG_LIN2, DA732X_LOUT_DAC_EN_SHIFT, - DA732X_DAC_EN_MAX, enable_text); +static SOC_ENUM_SINGLE_DECL(da732x_lout2_output, + DA732X_REG_LIN2, DA732X_LOUT_DAC_EN_SHIFT, + enable_text); static const struct snd_kcontrol_new lout2_mux = SOC_DAPM_ENUM("LOUT2 Switch", da732x_lout2_output); @@ -973,17 +957,17 @@ static int da732x_hw_params(struct snd_pcm_substream *substream, reg_aif = dai->driver->base; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: aif |= DA732X_AIF_WORD_16; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: aif |= DA732X_AIF_WORD_20; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: aif |= DA732X_AIF_WORD_24; break; - case SNDRV_PCM_FORMAT_S32_LE: + case 32: aif |= DA732X_AIF_WORD_32; break; default: @@ -1268,11 +1252,23 @@ static struct snd_soc_dai_driver da732x_dai[] = { }, }; +static bool da732x_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA732X_REG_HPL_DAC_OFF_CNTL: + case DA732X_REG_HPR_DAC_OFF_CNTL: + return true; + default: + return false; + } +} + static const struct regmap_config da732x_regmap = { .reg_bits = 8, .val_bits = 8, .max_register = DA732X_MAX_REG, + .volatile_reg = da732x_volatile, .reg_defaults = da732x_reg_cache, .num_reg_defaults = ARRAY_SIZE(da732x_reg_cache), .cache_type = REGCACHE_RBTREE, @@ -1487,8 +1483,8 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec, da732x_hp_dc_offset_cancellation(codec); - regcache_cache_only(codec->control_data, false); - regcache_sync(codec->control_data); + regcache_cache_only(da732x->regmap, false); + regcache_sync(da732x->regmap); } else { snd_soc_update_bits(codec, DA732X_REG_BIAS_EN, DA732X_BIAS_BOOST_MASK, @@ -1499,7 +1495,7 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec, } break; case SND_SOC_BIAS_OFF: - regcache_cache_only(codec->control_data, true); + regcache_cache_only(da732x->regmap, true); da732x_set_charge_pump(codec, DA732X_DISABLE_CP); snd_soc_update_bits(codec, DA732X_REG_BIAS_EN, DA732X_BIAS_EN, DA732X_BIAS_DIS); @@ -1554,7 +1550,6 @@ static struct snd_soc_codec_driver soc_codec_dev_da732x = { .dapm_routes = da732x_dapm_routes, .num_dapm_routes = ARRAY_SIZE(da732x_dapm_routes), .set_pll = da732x_set_dai_pll, - .reg_cache_size = ARRAY_SIZE(da732x_reg_cache), }; static int da732x_i2c_probe(struct i2c_client *i2c, diff --git a/sound/soc/codecs/da732x.h b/sound/soc/codecs/da732x.h index c8ce5475de22..1dceafeec415 100644 --- a/sound/soc/codecs/da732x.h +++ b/sound/soc/codecs/da732x.h @@ -113,9 +113,6 @@ #define DA732X_EQ_OVERALL_VOL_DB_MIN -1800 #define DA732X_EQ_OVERALL_VOL_DB_INC 600 -#define DA732X_SOC_ENUM_DOUBLE_R(xreg, xrreg, xmax, xtext) \ - {.reg = xreg, .reg2 = xrreg, .max = xmax, .texts = xtext} - enum da732x_sysctl { DA732X_SR_8KHZ = 0x1, DA732X_SR_11_025KHZ = 0x2, diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index fc9802d1281d..f118daa91234 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -18,6 +18,8 @@ #include <linux/regmap.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -321,22 +323,22 @@ static const char * const da9055_hpf_cutoff_txt[] = { "Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000" }; -static const struct soc_enum da9055_dac_hpf_cutoff = - SOC_ENUM_SINGLE(DA9055_DAC_FILTERS1, 4, 4, da9055_hpf_cutoff_txt); +static SOC_ENUM_SINGLE_DECL(da9055_dac_hpf_cutoff, + DA9055_DAC_FILTERS1, 4, da9055_hpf_cutoff_txt); -static const struct soc_enum da9055_adc_hpf_cutoff = - SOC_ENUM_SINGLE(DA9055_ADC_FILTERS1, 4, 4, da9055_hpf_cutoff_txt); +static SOC_ENUM_SINGLE_DECL(da9055_adc_hpf_cutoff, + DA9055_ADC_FILTERS1, 4, da9055_hpf_cutoff_txt); /* ADC and DAC voice mode (8kHz) high pass cutoff value */ static const char * const da9055_vf_cutoff_txt[] = { "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" }; -static const struct soc_enum da9055_dac_vf_cutoff = - SOC_ENUM_SINGLE(DA9055_DAC_FILTERS1, 0, 8, da9055_vf_cutoff_txt); +static SOC_ENUM_SINGLE_DECL(da9055_dac_vf_cutoff, + DA9055_DAC_FILTERS1, 0, da9055_vf_cutoff_txt); -static const struct soc_enum da9055_adc_vf_cutoff = - SOC_ENUM_SINGLE(DA9055_ADC_FILTERS1, 0, 8, da9055_vf_cutoff_txt); +static SOC_ENUM_SINGLE_DECL(da9055_adc_vf_cutoff, + DA9055_ADC_FILTERS1, 0, da9055_vf_cutoff_txt); /* Gain ramping rate value */ static const char * const da9055_gain_ramping_txt[] = { @@ -344,44 +346,44 @@ static const char * const da9055_gain_ramping_txt[] = { "nominal rate / 8" }; -static const struct soc_enum da9055_gain_ramping_rate = - SOC_ENUM_SINGLE(DA9055_GAIN_RAMP_CTRL, 0, 4, da9055_gain_ramping_txt); +static SOC_ENUM_SINGLE_DECL(da9055_gain_ramping_rate, + DA9055_GAIN_RAMP_CTRL, 0, da9055_gain_ramping_txt); /* DAC noise gate setup time value */ static const char * const da9055_dac_ng_setup_time_txt[] = { "256 samples", "512 samples", "1024 samples", "2048 samples" }; -static const struct soc_enum da9055_dac_ng_setup_time = - SOC_ENUM_SINGLE(DA9055_DAC_NG_SETUP_TIME, 0, 4, - da9055_dac_ng_setup_time_txt); +static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_setup_time, + DA9055_DAC_NG_SETUP_TIME, 0, + da9055_dac_ng_setup_time_txt); /* DAC noise gate rampup rate value */ static const char * const da9055_dac_ng_rampup_txt[] = { "0.02 ms/dB", "0.16 ms/dB" }; -static const struct soc_enum da9055_dac_ng_rampup_rate = - SOC_ENUM_SINGLE(DA9055_DAC_NG_SETUP_TIME, 2, 2, - da9055_dac_ng_rampup_txt); +static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_rampup_rate, + DA9055_DAC_NG_SETUP_TIME, 2, + da9055_dac_ng_rampup_txt); /* DAC noise gate rampdown rate value */ static const char * const da9055_dac_ng_rampdown_txt[] = { "0.64 ms/dB", "20.48 ms/dB" }; -static const struct soc_enum da9055_dac_ng_rampdown_rate = - SOC_ENUM_SINGLE(DA9055_DAC_NG_SETUP_TIME, 3, 2, - da9055_dac_ng_rampdown_txt); +static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_rampdown_rate, + DA9055_DAC_NG_SETUP_TIME, 3, + da9055_dac_ng_rampdown_txt); /* DAC soft mute rate value */ static const char * const da9055_dac_soft_mute_rate_txt[] = { "1", "2", "4", "8", "16", "32", "64" }; -static const struct soc_enum da9055_dac_soft_mute_rate = - SOC_ENUM_SINGLE(DA9055_DAC_FILTERS5, 4, 7, - da9055_dac_soft_mute_rate_txt); +static SOC_ENUM_SINGLE_DECL(da9055_dac_soft_mute_rate, + DA9055_DAC_FILTERS5, 4, + da9055_dac_soft_mute_rate_txt); /* DAC routing select */ static const char * const da9055_dac_src_txt[] = { @@ -389,40 +391,40 @@ static const char * const da9055_dac_src_txt[] = { "AIF input right" }; -static const struct soc_enum da9055_dac_l_src = - SOC_ENUM_SINGLE(DA9055_DIG_ROUTING_DAC, 0, 4, da9055_dac_src_txt); +static SOC_ENUM_SINGLE_DECL(da9055_dac_l_src, + DA9055_DIG_ROUTING_DAC, 0, da9055_dac_src_txt); -static const struct soc_enum da9055_dac_r_src = - SOC_ENUM_SINGLE(DA9055_DIG_ROUTING_DAC, 4, 4, da9055_dac_src_txt); +static SOC_ENUM_SINGLE_DECL(da9055_dac_r_src, + DA9055_DIG_ROUTING_DAC, 4, da9055_dac_src_txt); /* MIC PGA Left source select */ static const char * const da9055_mic_l_src_txt[] = { "MIC1_P_N", "MIC1_P", "MIC1_N", "MIC2_L" }; -static const struct soc_enum da9055_mic_l_src = - SOC_ENUM_SINGLE(DA9055_MIXIN_L_SELECT, 4, 4, da9055_mic_l_src_txt); +static SOC_ENUM_SINGLE_DECL(da9055_mic_l_src, + DA9055_MIXIN_L_SELECT, 4, da9055_mic_l_src_txt); /* MIC PGA Right source select */ static const char * const da9055_mic_r_src_txt[] = { "MIC2_R_L", "MIC2_R", "MIC2_L" }; -static const struct soc_enum da9055_mic_r_src = - SOC_ENUM_SINGLE(DA9055_MIXIN_R_SELECT, 4, 3, da9055_mic_r_src_txt); +static SOC_ENUM_SINGLE_DECL(da9055_mic_r_src, + DA9055_MIXIN_R_SELECT, 4, da9055_mic_r_src_txt); /* ALC Input Signal Tracking rate select */ static const char * const da9055_signal_tracking_rate_txt[] = { "1/4", "1/16", "1/256", "1/65536" }; -static const struct soc_enum da9055_integ_attack_rate = - SOC_ENUM_SINGLE(DA9055_ALC_CTRL3, 4, 4, - da9055_signal_tracking_rate_txt); +static SOC_ENUM_SINGLE_DECL(da9055_integ_attack_rate, + DA9055_ALC_CTRL3, 4, + da9055_signal_tracking_rate_txt); -static const struct soc_enum da9055_integ_release_rate = - SOC_ENUM_SINGLE(DA9055_ALC_CTRL3, 6, 4, - da9055_signal_tracking_rate_txt); +static SOC_ENUM_SINGLE_DECL(da9055_integ_release_rate, + DA9055_ALC_CTRL3, 6, + da9055_signal_tracking_rate_txt); /* ALC Attack Rate select */ static const char * const da9055_attack_rate_txt[] = { @@ -430,8 +432,8 @@ static const char * const da9055_attack_rate_txt[] = { "5632/fs", "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs" }; -static const struct soc_enum da9055_attack_rate = - SOC_ENUM_SINGLE(DA9055_ALC_CTRL2, 0, 13, da9055_attack_rate_txt); +static SOC_ENUM_SINGLE_DECL(da9055_attack_rate, + DA9055_ALC_CTRL2, 0, da9055_attack_rate_txt); /* ALC Release Rate select */ static const char * const da9055_release_rate_txt[] = { @@ -439,8 +441,8 @@ static const char * const da9055_release_rate_txt[] = { "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs" }; -static const struct soc_enum da9055_release_rate = - SOC_ENUM_SINGLE(DA9055_ALC_CTRL2, 4, 11, da9055_release_rate_txt); +static SOC_ENUM_SINGLE_DECL(da9055_release_rate, + DA9055_ALC_CTRL2, 4, da9055_release_rate_txt); /* ALC Hold Time select */ static const char * const da9055_hold_time_txt[] = { @@ -449,8 +451,8 @@ static const char * const da9055_hold_time_txt[] = { "253952/fs", "507904/fs", "1015808/fs", "2031616/fs" }; -static const struct soc_enum da9055_hold_time = - SOC_ENUM_SINGLE(DA9055_ALC_CTRL3, 0, 16, da9055_hold_time_txt); +static SOC_ENUM_SINGLE_DECL(da9055_hold_time, + DA9055_ALC_CTRL3, 0, da9055_hold_time_txt); static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val) { @@ -1058,17 +1060,17 @@ static int da9055_hw_params(struct snd_pcm_substream *substream, u8 aif_ctrl, fs; u32 sysclk; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: aif_ctrl = DA9055_AIF_WORD_S16_LE; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: aif_ctrl = DA9055_AIF_WORD_S20_3LE; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: aif_ctrl = DA9055_AIF_WORD_S24_LE; break; - case SNDRV_PCM_FORMAT_S32_LE: + case 32: aif_ctrl = DA9055_AIF_WORD_S32_LE; break; default: @@ -1523,17 +1525,30 @@ static int da9055_remove(struct i2c_client *client) return 0; } +/* + * DO NOT change the device Ids. The naming is intentionally specific as both + * the CODEC and PMIC parts of this chip are instantiated separately as I2C + * devices (both have configurable I2C addresses, and are to all intents and + * purposes separate). As a result there are specific DA9055 Ids for CODEC + * and PMIC, which must be different to operate together. + */ static const struct i2c_device_id da9055_i2c_id[] = { - { "da9055", 0 }, + { "da9055-codec", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, da9055_i2c_id); +static const struct of_device_id da9055_of_match[] = { + { .compatible = "dlg,da9055-codec", }, + { } +}; + /* I2C codec control layer */ static struct i2c_driver da9055_i2c_driver = { .driver = { - .name = "da9055", + .name = "da9055-codec", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(da9055_of_match), }, .probe = da9055_i2c_probe, .remove = da9055_remove, 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/isabelle.c b/sound/soc/codecs/isabelle.c index 53b455b8c07a..cb736ddc446d 100644 --- a/sound/soc/codecs/isabelle.c +++ b/sound/soc/codecs/isabelle.c @@ -140,13 +140,17 @@ static const char *isabelle_rx1_texts[] = {"VRX1", "ARX1"}; static const char *isabelle_rx2_texts[] = {"VRX2", "ARX2"}; static const struct soc_enum isabelle_rx1_enum[] = { - SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 3, 1, isabelle_rx1_texts), - SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 5, 1, isabelle_rx1_texts), + SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 3, + ARRAY_SIZE(isabelle_rx1_texts), isabelle_rx1_texts), + SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 5, + ARRAY_SIZE(isabelle_rx1_texts), isabelle_rx1_texts), }; static const struct soc_enum isabelle_rx2_enum[] = { - SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 2, 1, isabelle_rx2_texts), - SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 4, 1, isabelle_rx2_texts), + SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 2, + ARRAY_SIZE(isabelle_rx2_texts), isabelle_rx2_texts), + SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 4, + ARRAY_SIZE(isabelle_rx2_texts), isabelle_rx2_texts), }; /* Headset DAC playback switches */ @@ -161,13 +165,17 @@ static const char *isabelle_atx_texts[] = {"AMIC1", "DMIC"}; static const char *isabelle_vtx_texts[] = {"AMIC2", "DMIC"}; static const struct soc_enum isabelle_atx_enum[] = { - SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 7, 1, isabelle_atx_texts), - SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0, 1, isabelle_atx_texts), + SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 7, + ARRAY_SIZE(isabelle_atx_texts), isabelle_atx_texts), + SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0, + ARRAY_SIZE(isabelle_atx_texts), isabelle_atx_texts), }; static const struct soc_enum isabelle_vtx_enum[] = { - SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 6, 1, isabelle_vtx_texts), - SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0, 1, isabelle_vtx_texts), + SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 6, + ARRAY_SIZE(isabelle_vtx_texts), isabelle_vtx_texts), + SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0, + ARRAY_SIZE(isabelle_vtx_texts), isabelle_vtx_texts), }; static const struct snd_kcontrol_new atx_mux_controls = @@ -183,17 +191,13 @@ static const char *isabelle_amic1_texts[] = { /* Left analog microphone selection */ static const char *isabelle_amic2_texts[] = {"Sub Mic", "Aux/FM Right"}; -static const struct soc_enum isabelle_amic1_enum[] = { - SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 5, - ARRAY_SIZE(isabelle_amic1_texts), - isabelle_amic1_texts), -}; +static SOC_ENUM_SINGLE_DECL(isabelle_amic1_enum, + ISABELLE_AMIC_CFG_REG, 5, + isabelle_amic1_texts); -static const struct soc_enum isabelle_amic2_enum[] = { - SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 4, - ARRAY_SIZE(isabelle_amic2_texts), - isabelle_amic2_texts), -}; +static SOC_ENUM_SINGLE_DECL(isabelle_amic2_enum, + ISABELLE_AMIC_CFG_REG, 4, + isabelle_amic2_texts); static const struct snd_kcontrol_new amic1_control = SOC_DAPM_ENUM("Route", isabelle_amic1_enum); @@ -206,16 +210,20 @@ static const char *isabelle_st_audio_texts[] = {"ATX1", "ATX2"}; static const char *isabelle_st_voice_texts[] = {"VTX1", "VTX2"}; static const struct soc_enum isabelle_st_audio_enum[] = { - SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA1_CFG_REG, 7, 1, + SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA1_CFG_REG, 7, + ARRAY_SIZE(isabelle_st_audio_texts), isabelle_st_audio_texts), - SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA2_CFG_REG, 7, 1, + SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA2_CFG_REG, 7, + ARRAY_SIZE(isabelle_st_audio_texts), isabelle_st_audio_texts), }; static const struct soc_enum isabelle_st_voice_enum[] = { - SOC_ENUM_SINGLE(ISABELLE_VTX_STPGA1_CFG_REG, 7, 1, + SOC_ENUM_SINGLE(ISABELLE_VTX_STPGA1_CFG_REG, 7, + ARRAY_SIZE(isabelle_st_voice_texts), isabelle_st_voice_texts), - SOC_ENUM_SINGLE(ISABELLE_VTX2_STPGA2_CFG_REG, 7, 1, + SOC_ENUM_SINGLE(ISABELLE_VTX2_STPGA2_CFG_REG, 7, + ARRAY_SIZE(isabelle_st_voice_texts), isabelle_st_voice_texts), }; @@ -951,11 +959,11 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream, ISABELLE_FS_RATE_MASK, fs_val); /* bit size */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S20_3LE: + switch (params_width(params)) { + case 20: aif |= ISABELLE_AIF_LENGTH_20; break; - case SNDRV_PCM_FORMAT_S32_LE: + case 32: aif |= ISABELLE_AIF_LENGTH_32; break; default: diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index e19490cfb3a8..6b7fe5e54881 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -195,18 +195,18 @@ struct lm49453_priv { static const char *lm49453_mic2mode_text[] = {"Single Ended", "Differential"}; -static const SOC_ENUM_SINGLE_DECL(lm49453_mic2mode_enum, LM49453_P0_MICR_REG, 5, - lm49453_mic2mode_text); +static SOC_ENUM_SINGLE_DECL(lm49453_mic2mode_enum, LM49453_P0_MICR_REG, 5, + lm49453_mic2mode_text); static const char *lm49453_dmic_cfg_text[] = {"DMICDAT1", "DMICDAT2"}; -static const SOC_ENUM_SINGLE_DECL(lm49453_dmic12_cfg_enum, - LM49453_P0_DIGITAL_MIC1_CONFIG_REG, - 7, lm49453_dmic_cfg_text); +static SOC_ENUM_SINGLE_DECL(lm49453_dmic12_cfg_enum, + LM49453_P0_DIGITAL_MIC1_CONFIG_REG, 7, + lm49453_dmic_cfg_text); -static const SOC_ENUM_SINGLE_DECL(lm49453_dmic34_cfg_enum, - LM49453_P0_DIGITAL_MIC2_CONFIG_REG, - 7, lm49453_dmic_cfg_text); +static SOC_ENUM_SINGLE_DECL(lm49453_dmic34_cfg_enum, + LM49453_P0_DIGITAL_MIC2_CONFIG_REG, 7, + lm49453_dmic_cfg_text); /* MUX Controls */ static const char *lm49453_adcl_mux_text[] = { "MIC1", "Aux_L" }; diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 53d7dab4e054..bb1ecfc4459b 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1233,12 +1233,12 @@ static int max98088_dai1_hw_params(struct snd_pcm_substream *substream, rate = params_rate(params); - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT, M98088_DAI_WS, 0); break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT, M98088_DAI_WS, M98088_DAI_WS); break; @@ -1849,7 +1849,7 @@ static void max98088_handle_eq_pdata(struct snd_soc_codec *codec) /* Now point the soc_enum to .texts array items */ max98088->eq_enum.texts = max98088->eq_texts; - max98088->eq_enum.max = max98088->eq_textcnt; + max98088->eq_enum.items = max98088->eq_textcnt; ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls)); if (ret != 0) diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 0569a4c3ae00..f363de19be07 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -336,6 +336,7 @@ static bool max98090_readable_register(struct device *dev, unsigned int reg) case M98090_REG_RECORD_TDM_SLOT: case M98090_REG_SAMPLE_RATE: case M98090_REG_DMIC34_BIQUAD_BASE ... M98090_REG_DMIC34_BIQUAD_BASE + 0x0E: + case M98090_REG_REVISION_ID: return true; default: return false; @@ -512,65 +513,75 @@ static const char *max98090_perf_pwr_text[] = static const char *max98090_pwr_perf_text[] = { "Low Power", "High Performance" }; -static const struct soc_enum max98090_vcmbandgap_enum = - SOC_ENUM_SINGLE(M98090_REG_BIAS_CONTROL, M98090_VCM_MODE_SHIFT, - ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text); +static SOC_ENUM_SINGLE_DECL(max98090_vcmbandgap_enum, + M98090_REG_BIAS_CONTROL, + M98090_VCM_MODE_SHIFT, + max98090_pwr_perf_text); static const char *max98090_osr128_text[] = { "64*fs", "128*fs" }; -static const struct soc_enum max98090_osr128_enum = - SOC_ENUM_SINGLE(M98090_REG_ADC_CONTROL, M98090_OSR128_SHIFT, - ARRAY_SIZE(max98090_osr128_text), max98090_osr128_text); +static SOC_ENUM_SINGLE_DECL(max98090_osr128_enum, + M98090_REG_ADC_CONTROL, + M98090_OSR128_SHIFT, + max98090_osr128_text); static const char *max98090_mode_text[] = { "Voice", "Music" }; -static const struct soc_enum max98090_mode_enum = - SOC_ENUM_SINGLE(M98090_REG_FILTER_CONFIG, M98090_MODE_SHIFT, - ARRAY_SIZE(max98090_mode_text), max98090_mode_text); +static SOC_ENUM_SINGLE_DECL(max98090_mode_enum, + M98090_REG_FILTER_CONFIG, + M98090_MODE_SHIFT, + max98090_mode_text); -static const struct soc_enum max98090_filter_dmic34mode_enum = - SOC_ENUM_SINGLE(M98090_REG_FILTER_CONFIG, - M98090_FLT_DMIC34MODE_SHIFT, - ARRAY_SIZE(max98090_mode_text), max98090_mode_text); +static SOC_ENUM_SINGLE_DECL(max98090_filter_dmic34mode_enum, + M98090_REG_FILTER_CONFIG, + M98090_FLT_DMIC34MODE_SHIFT, + max98090_mode_text); static const char *max98090_drcatk_text[] = { "0.5ms", "1ms", "5ms", "10ms", "25ms", "50ms", "100ms", "200ms" }; -static const struct soc_enum max98090_drcatk_enum = - SOC_ENUM_SINGLE(M98090_REG_DRC_TIMING, M98090_DRCATK_SHIFT, - ARRAY_SIZE(max98090_drcatk_text), max98090_drcatk_text); +static SOC_ENUM_SINGLE_DECL(max98090_drcatk_enum, + M98090_REG_DRC_TIMING, + M98090_DRCATK_SHIFT, + max98090_drcatk_text); static const char *max98090_drcrls_text[] = { "8s", "4s", "2s", "1s", "0.5s", "0.25s", "0.125s", "0.0625s" }; -static const struct soc_enum max98090_drcrls_enum = - SOC_ENUM_SINGLE(M98090_REG_DRC_TIMING, M98090_DRCRLS_SHIFT, - ARRAY_SIZE(max98090_drcrls_text), max98090_drcrls_text); +static SOC_ENUM_SINGLE_DECL(max98090_drcrls_enum, + M98090_REG_DRC_TIMING, + M98090_DRCRLS_SHIFT, + max98090_drcrls_text); static const char *max98090_alccmp_text[] = { "1:1", "1:1.5", "1:2", "1:4", "1:INF" }; -static const struct soc_enum max98090_alccmp_enum = - SOC_ENUM_SINGLE(M98090_REG_DRC_COMPRESSOR, M98090_DRCCMP_SHIFT, - ARRAY_SIZE(max98090_alccmp_text), max98090_alccmp_text); +static SOC_ENUM_SINGLE_DECL(max98090_alccmp_enum, + M98090_REG_DRC_COMPRESSOR, + M98090_DRCCMP_SHIFT, + max98090_alccmp_text); static const char *max98090_drcexp_text[] = { "1:1", "2:1", "3:1" }; -static const struct soc_enum max98090_drcexp_enum = - SOC_ENUM_SINGLE(M98090_REG_DRC_EXPANDER, M98090_DRCEXP_SHIFT, - ARRAY_SIZE(max98090_drcexp_text), max98090_drcexp_text); +static SOC_ENUM_SINGLE_DECL(max98090_drcexp_enum, + M98090_REG_DRC_EXPANDER, + M98090_DRCEXP_SHIFT, + max98090_drcexp_text); -static const struct soc_enum max98090_dac_perfmode_enum = - SOC_ENUM_SINGLE(M98090_REG_DAC_CONTROL, M98090_PERFMODE_SHIFT, - ARRAY_SIZE(max98090_perf_pwr_text), max98090_perf_pwr_text); +static SOC_ENUM_SINGLE_DECL(max98090_dac_perfmode_enum, + M98090_REG_DAC_CONTROL, + M98090_PERFMODE_SHIFT, + max98090_perf_pwr_text); -static const struct soc_enum max98090_dachp_enum = - SOC_ENUM_SINGLE(M98090_REG_DAC_CONTROL, M98090_DACHP_SHIFT, - ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text); +static SOC_ENUM_SINGLE_DECL(max98090_dachp_enum, + M98090_REG_DAC_CONTROL, + M98090_DACHP_SHIFT, + max98090_pwr_perf_text); -static const struct soc_enum max98090_adchp_enum = - SOC_ENUM_SINGLE(M98090_REG_ADC_CONTROL, M98090_ADCHP_SHIFT, - ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text); +static SOC_ENUM_SINGLE_DECL(max98090_adchp_enum, + M98090_REG_ADC_CONTROL, + M98090_ADCHP_SHIFT, + max98090_pwr_perf_text); static const struct snd_kcontrol_new max98090_snd_controls[] = { SOC_ENUM("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum), @@ -841,39 +852,42 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w, static const char *mic1_mux_text[] = { "IN12", "IN56" }; -static const struct soc_enum mic1_mux_enum = - SOC_ENUM_SINGLE(M98090_REG_INPUT_MODE, M98090_EXTMIC1_SHIFT, - ARRAY_SIZE(mic1_mux_text), mic1_mux_text); +static SOC_ENUM_SINGLE_DECL(mic1_mux_enum, + M98090_REG_INPUT_MODE, + M98090_EXTMIC1_SHIFT, + mic1_mux_text); static const struct snd_kcontrol_new max98090_mic1_mux = SOC_DAPM_ENUM("MIC1 Mux", mic1_mux_enum); static const char *mic2_mux_text[] = { "IN34", "IN56" }; -static const struct soc_enum mic2_mux_enum = - SOC_ENUM_SINGLE(M98090_REG_INPUT_MODE, M98090_EXTMIC2_SHIFT, - ARRAY_SIZE(mic2_mux_text), mic2_mux_text); +static SOC_ENUM_SINGLE_DECL(mic2_mux_enum, + M98090_REG_INPUT_MODE, + M98090_EXTMIC2_SHIFT, + mic2_mux_text); static const struct snd_kcontrol_new max98090_mic2_mux = SOC_DAPM_ENUM("MIC2 Mux", mic2_mux_enum); static const char *dmic_mux_text[] = { "ADC", "DMIC" }; -static const struct soc_enum dmic_mux_enum = - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dmic_mux_text), dmic_mux_text); +static SOC_ENUM_SINGLE_VIRT_DECL(dmic_mux_enum, dmic_mux_text); static const struct snd_kcontrol_new max98090_dmic_mux = SOC_DAPM_ENUM_VIRT("DMIC Mux", dmic_mux_enum); static const char *max98090_micpre_text[] = { "Off", "On" }; -static const struct soc_enum max98090_pa1en_enum = - SOC_ENUM_SINGLE(M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT, - ARRAY_SIZE(max98090_micpre_text), max98090_micpre_text); +static SOC_ENUM_SINGLE_DECL(max98090_pa1en_enum, + M98090_REG_MIC1_INPUT_LEVEL, + M98090_MIC_PA1EN_SHIFT, + max98090_micpre_text); -static const struct soc_enum max98090_pa2en_enum = - SOC_ENUM_SINGLE(M98090_REG_MIC2_INPUT_LEVEL, M98090_MIC_PA2EN_SHIFT, - ARRAY_SIZE(max98090_micpre_text), max98090_micpre_text); +static SOC_ENUM_SINGLE_DECL(max98090_pa2en_enum, + M98090_REG_MIC2_INPUT_LEVEL, + M98090_MIC_PA2EN_SHIFT, + max98090_micpre_text); /* LINEA mixer switch */ static const struct snd_kcontrol_new max98090_linea_mixer_controls[] = { @@ -937,13 +951,15 @@ static const struct snd_kcontrol_new max98090_right_adc_mixer_controls[] = { static const char *lten_mux_text[] = { "Normal", "Loopthrough" }; -static const struct soc_enum ltenl_mux_enum = - SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LTEN_SHIFT, - ARRAY_SIZE(lten_mux_text), lten_mux_text); +static SOC_ENUM_SINGLE_DECL(ltenl_mux_enum, + M98090_REG_IO_CONFIGURATION, + M98090_LTEN_SHIFT, + lten_mux_text); -static const struct soc_enum ltenr_mux_enum = - SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LTEN_SHIFT, - ARRAY_SIZE(lten_mux_text), lten_mux_text); +static SOC_ENUM_SINGLE_DECL(ltenr_mux_enum, + M98090_REG_IO_CONFIGURATION, + M98090_LTEN_SHIFT, + lten_mux_text); static const struct snd_kcontrol_new max98090_ltenl_mux = SOC_DAPM_ENUM("LTENL Mux", ltenl_mux_enum); @@ -953,13 +969,15 @@ static const struct snd_kcontrol_new max98090_ltenr_mux = static const char *lben_mux_text[] = { "Normal", "Loopback" }; -static const struct soc_enum lbenl_mux_enum = - SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LBEN_SHIFT, - ARRAY_SIZE(lben_mux_text), lben_mux_text); +static SOC_ENUM_SINGLE_DECL(lbenl_mux_enum, + M98090_REG_IO_CONFIGURATION, + M98090_LBEN_SHIFT, + lben_mux_text); -static const struct soc_enum lbenr_mux_enum = - SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LBEN_SHIFT, - ARRAY_SIZE(lben_mux_text), lben_mux_text); +static SOC_ENUM_SINGLE_DECL(lbenr_mux_enum, + M98090_REG_IO_CONFIGURATION, + M98090_LBEN_SHIFT, + lben_mux_text); static const struct snd_kcontrol_new max98090_lbenl_mux = SOC_DAPM_ENUM("LBENL Mux", lbenl_mux_enum); @@ -971,13 +989,15 @@ static const char *stenl_mux_text[] = { "Normal", "Sidetone Left" }; static const char *stenr_mux_text[] = { "Normal", "Sidetone Right" }; -static const struct soc_enum stenl_mux_enum = - SOC_ENUM_SINGLE(M98090_REG_ADC_SIDETONE, M98090_DSTSL_SHIFT, - ARRAY_SIZE(stenl_mux_text), stenl_mux_text); +static SOC_ENUM_SINGLE_DECL(stenl_mux_enum, + M98090_REG_ADC_SIDETONE, + M98090_DSTSL_SHIFT, + stenl_mux_text); -static const struct soc_enum stenr_mux_enum = - SOC_ENUM_SINGLE(M98090_REG_ADC_SIDETONE, M98090_DSTSR_SHIFT, - ARRAY_SIZE(stenr_mux_text), stenr_mux_text); +static SOC_ENUM_SINGLE_DECL(stenr_mux_enum, + M98090_REG_ADC_SIDETONE, + M98090_DSTSR_SHIFT, + stenr_mux_text); static const struct snd_kcontrol_new max98090_stenl_mux = SOC_DAPM_ENUM("STENL Mux", stenl_mux_enum); @@ -1085,9 +1105,10 @@ static const struct snd_kcontrol_new max98090_right_rcv_mixer_controls[] = { static const char *linmod_mux_text[] = { "Left Only", "Left and Right" }; -static const struct soc_enum linmod_mux_enum = - SOC_ENUM_SINGLE(M98090_REG_LOUTR_MIXER, M98090_LINMOD_SHIFT, - ARRAY_SIZE(linmod_mux_text), linmod_mux_text); +static SOC_ENUM_SINGLE_DECL(linmod_mux_enum, + M98090_REG_LOUTR_MIXER, + M98090_LINMOD_SHIFT, + linmod_mux_text); static const struct snd_kcontrol_new max98090_linmod_mux = SOC_DAPM_ENUM("LINMOD Mux", linmod_mux_enum); @@ -1097,16 +1118,18 @@ static const char *mixhpsel_mux_text[] = { "DAC Only", "HP Mixer" }; /* * This is a mux as it selects the HP output, but to DAPM it is a Mixer enable */ -static const struct soc_enum mixhplsel_mux_enum = - SOC_ENUM_SINGLE(M98090_REG_HP_CONTROL, M98090_MIXHPLSEL_SHIFT, - ARRAY_SIZE(mixhpsel_mux_text), mixhpsel_mux_text); +static SOC_ENUM_SINGLE_DECL(mixhplsel_mux_enum, + M98090_REG_HP_CONTROL, + M98090_MIXHPLSEL_SHIFT, + mixhpsel_mux_text); static const struct snd_kcontrol_new max98090_mixhplsel_mux = SOC_DAPM_ENUM("MIXHPLSEL Mux", mixhplsel_mux_enum); -static const struct soc_enum mixhprsel_mux_enum = - SOC_ENUM_SINGLE(M98090_REG_HP_CONTROL, M98090_MIXHPRSEL_SHIFT, - ARRAY_SIZE(mixhpsel_mux_text), mixhpsel_mux_text); +static SOC_ENUM_SINGLE_DECL(mixhprsel_mux_enum, + M98090_REG_HP_CONTROL, + M98090_MIXHPRSEL_SHIFT, + mixhpsel_mux_text); static const struct snd_kcontrol_new max98090_mixhprsel_mux = SOC_DAPM_ENUM("MIXHPRSEL Mux", mixhprsel_mux_enum); @@ -1769,16 +1792,6 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { - ret = regcache_sync(max98090->regmap); - - if (ret != 0) { - dev_err(codec->dev, - "Failed to sync cache: %d\n", ret); - return ret; - } - } - if (max98090->jack_state == M98090_JACK_STATE_HEADSET) { /* * Set to normal bias level. @@ -1792,6 +1805,16 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = regcache_sync(max98090->regmap); + if (ret != 0) { + dev_err(codec->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + } + break; + case SND_SOC_BIAS_OFF: /* Set internal pull-up to lowest power mode */ snd_soc_update_bits(codec, M98090_REG_JACK_DETECT, @@ -1840,8 +1863,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream, max98090->lrclk = params_rate(params); - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: snd_soc_update_bits(codec, M98090_REG_INTERFACE_FORMAT, M98090_WS_MASK, 0); break; diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 67244315c721..5bce9cde4a6d 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1213,12 +1213,12 @@ static int max98095_dai1_hw_params(struct snd_pcm_substream *substream, rate = params_rate(params); - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT, M98095_DAI_WS, 0); break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT, M98095_DAI_WS, M98095_DAI_WS); break; @@ -1861,7 +1861,7 @@ static void max98095_handle_eq_pdata(struct snd_soc_codec *codec) /* Now point the soc_enum to .texts array items */ max98095->eq_enum.texts = max98095->eq_texts; - max98095->eq_enum.max = max98095->eq_textcnt; + max98095->eq_enum.items = max98095->eq_textcnt; ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls)); if (ret != 0) @@ -2016,7 +2016,7 @@ static void max98095_handle_bq_pdata(struct snd_soc_codec *codec) /* Now point the soc_enum to .texts array items */ max98095->bq_enum.texts = max98095->bq_texts; - max98095->bq_enum.max = max98095->bq_textcnt; + max98095->bq_enum.items = max98095->bq_textcnt; ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls)); if (ret != 0) diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index c5dd61785f8d..82757ebf0301 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -149,14 +149,14 @@ static int max9850_hw_params(struct snd_pcm_substream *substream, snd_soc_write(codec, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f); snd_soc_write(codec, MAX9850_LRCLK_LSB, lrclk_div & 0xff); - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: da = 0; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: da = 0x2; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: da = 0x3; break; default: diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index bae60164c7b7..ec89b8f90a64 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -408,8 +408,7 @@ static const char * const adcl_enum_text[] = { "MC1L", "RXINL", }; -static const struct soc_enum adcl_enum = - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adcl_enum_text), adcl_enum_text); +static SOC_ENUM_SINGLE_VIRT_DECL(adcl_enum, adcl_enum_text); static const struct snd_kcontrol_new left_input_mux = SOC_DAPM_ENUM_VIRT("Route", adcl_enum); @@ -418,8 +417,7 @@ static const char * const adcr_enum_text[] = { "MC1R", "MC2", "RXINR", "TXIN", }; -static const struct soc_enum adcr_enum = - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adcr_enum_text), adcr_enum_text); +static SOC_ENUM_SINGLE_VIRT_DECL(adcr_enum, adcr_enum_text); static const struct snd_kcontrol_new right_input_mux = SOC_DAPM_ENUM_VIRT("Route", adcr_enum); @@ -430,8 +428,8 @@ static const struct snd_kcontrol_new samp_ctl = static const char * const speaker_amp_source_text[] = { "CODEC", "Right" }; -static const SOC_ENUM_SINGLE_DECL(speaker_amp_source, MC13783_AUDIO_RX0, 4, - speaker_amp_source_text); +static SOC_ENUM_SINGLE_DECL(speaker_amp_source, MC13783_AUDIO_RX0, 4, + speaker_amp_source_text); static const struct snd_kcontrol_new speaker_amp_source_mux = SOC_DAPM_ENUM("Speaker Amp Source MUX", speaker_amp_source); @@ -439,8 +437,8 @@ static const char * const headset_amp_source_text[] = { "CODEC", "Mixer" }; -static const SOC_ENUM_SINGLE_DECL(headset_amp_source, MC13783_AUDIO_RX0, 11, - headset_amp_source_text); +static SOC_ENUM_SINGLE_DECL(headset_amp_source, MC13783_AUDIO_RX0, 11, + headset_amp_source_text); static const struct snd_kcontrol_new headset_amp_source_mux = SOC_DAPM_ENUM("Headset Amp Source MUX", headset_amp_source); @@ -580,9 +578,9 @@ static struct snd_soc_dapm_route mc13783_routes[] = { static const char * const mc13783_3d_mixer[] = {"Stereo", "Phase Mix", "Mono", "Mono Mix"}; -static const struct soc_enum mc13783_enum_3d_mixer = - SOC_ENUM_SINGLE(MC13783_AUDIO_RX1, 16, ARRAY_SIZE(mc13783_3d_mixer), - mc13783_3d_mixer); +static SOC_ENUM_SINGLE_DECL(mc13783_enum_3d_mixer, + MC13783_AUDIO_RX1, 16, + mc13783_3d_mixer); static struct snd_kcontrol_new mc13783_control_list[] = { SOC_SINGLE("Loudspeaker enable", MC13783_AUDIO_RX0, 5, 1, 0), @@ -750,30 +748,26 @@ static struct snd_soc_codec_driver soc_codec_dev_mc13783 = { .num_dapm_routes = ARRAY_SIZE(mc13783_routes), }; -static int mc13783_codec_probe(struct platform_device *pdev) +static int __init mc13783_codec_probe(struct platform_device *pdev) { - struct mc13xxx *mc13xxx; struct mc13783_priv *priv; struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data; int ret; - mc13xxx = dev_get_drvdata(pdev->dev.parent); - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (priv == NULL) + if (!priv) return -ENOMEM; - dev_set_drvdata(&pdev->dev, priv); - priv->mc13xxx = mc13xxx; if (pdata) { priv->adc_ssi_port = pdata->adc_ssi_port; priv->dac_ssi_port = pdata->dac_ssi_port; } else { - priv->adc_ssi_port = MC13783_SSI1_PORT; - priv->dac_ssi_port = MC13783_SSI2_PORT; + return -ENOSYS; } + dev_set_drvdata(&pdev->dev, priv); + priv->mc13xxx = dev_get_drvdata(pdev->dev.parent); + if (priv->adc_ssi_port == priv->dac_ssi_port) ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783, mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync)); @@ -781,14 +775,6 @@ static int mc13783_codec_probe(struct platform_device *pdev) ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783, mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async)); - if (ret) - goto err_register_codec; - - return 0; - -err_register_codec: - dev_err(&pdev->dev, "register codec failed with %d\n", ret); - return ret; } @@ -801,14 +787,12 @@ static int mc13783_codec_remove(struct platform_device *pdev) static struct platform_driver mc13783_codec_driver = { .driver = { - .name = "mc13783-codec", - .owner = THIS_MODULE, - }, - .probe = mc13783_codec_probe, + .name = "mc13783-codec", + .owner = THIS_MODULE, + }, .remove = mc13783_codec_remove, }; - -module_platform_driver(mc13783_codec_driver); +module_platform_driver_probe(mc13783_codec_driver, mc13783_codec_probe); MODULE_DESCRIPTION("ASoC MC13783 driver"); MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>"); diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 185fa3bc3052..577fb8776ce7 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -73,11 +73,11 @@ static const DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0); static const char * const ml26124_companding[] = {"16bit PCM", "u-law", "A-law"}; -static const struct soc_enum ml26124_adc_companding_enum - = SOC_ENUM_SINGLE(ML26124_SAI_TRANS_CTL, 6, 3, ml26124_companding); +static SOC_ENUM_SINGLE_DECL(ml26124_adc_companding_enum, + ML26124_SAI_TRANS_CTL, 6, ml26124_companding); -static const struct soc_enum ml26124_dac_companding_enum - = SOC_ENUM_SINGLE(ML26124_SAI_RCV_CTL, 6, 3, ml26124_companding); +static SOC_ENUM_SINGLE_DECL(ml26124_dac_companding_enum, + ML26124_SAI_RCV_CTL, 6, ml26124_companding); static const struct snd_kcontrol_new ml26124_snd_controls[] = { SOC_SINGLE_TLV("Capture Digital Volume", ML26124_RECORD_DIG_VOL, 0, @@ -136,8 +136,8 @@ static const struct snd_kcontrol_new ml26124_output_mixer_controls[] = { static const char * const ml26124_input_select[] = {"Analog MIC SingleEnded in", "Digital MIC in", "Analog MIC Differential in"}; -static const struct soc_enum ml26124_insel_enum = - SOC_ENUM_SINGLE(ML26124_MIC_IF_CTL, 0, 3, ml26124_input_select); +static SOC_ENUM_SINGLE_DECL(ml26124_insel_enum, + ML26124_MIC_IF_CTL, 0, ml26124_input_select); static const struct snd_kcontrol_new ml26124_input_mux_controls = SOC_DAPM_ENUM("Input Select", ml26124_insel_enum); diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index 73f9c3630e2c..e427544183d7 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -172,16 +172,21 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); int val = 0, ret; - int pcm_format = params_format(params); priv->rate = params_rate(params); switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_RIGHT_J: - if (pcm_format == SNDRV_PCM_FORMAT_S24_LE) - val = 0x00; - else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE) - val = 0x03; + switch (params_width(params)) { + case 24: + val = 0; + break; + case 16: + val = 3; + break; + default: + return -EINVAL; + } break; case SND_SOC_DAIFMT_I2S: val = 0x04; diff --git a/sound/soc/codecs/pcm1792a.c b/sound/soc/codecs/pcm1792a.c index 7146653a8e16..3a80ba4452df 100644 --- a/sound/soc/codecs/pcm1792a.c +++ b/sound/soc/codecs/pcm1792a.c @@ -107,24 +107,35 @@ static int pcm1792a_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec); int val = 0, ret; - int pcm_format = params_format(params); priv->rate = params_rate(params); switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_RIGHT_J: - if (pcm_format == SNDRV_PCM_FORMAT_S24_LE || - pcm_format == SNDRV_PCM_FORMAT_S32_LE) - val = 0x02; - else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE) - val = 0x00; + switch (params_width(params)) { + case 24: + case 32: + val = 2; + break; + case 16: + val = 0; + break; + default: + return -EINVAL; + } break; case SND_SOC_DAIFMT_I2S: - if (pcm_format == SNDRV_PCM_FORMAT_S24_LE || - pcm_format == SNDRV_PCM_FORMAT_S32_LE) - val = 0x05; - else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE) - val = 0x04; + switch (params_width(params)) { + case 24: + case 32: + val = 5; + break; + case 16: + val = 4; + break; + default: + return -EINVAL; + } break; default: dev_err(codec->dev, "Invalid DAI format\n"); diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c new file mode 100644 index 000000000000..4d62230bd378 --- /dev/null +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -0,0 +1,71 @@ +/* + * Driver for the PCM512x CODECs + * + * Author: Mark Brown <broonie@linaro.org> + * Copyright 2014 Linaro Ltd + * + * 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/i2c.h> + +#include "pcm512x.h" + +static int pcm512x_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &pcm512x_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return pcm512x_probe(&i2c->dev, regmap); +} + +static int pcm512x_i2c_remove(struct i2c_client *i2c) +{ + pcm512x_remove(&i2c->dev); + return 0; +} + +static const struct i2c_device_id pcm512x_i2c_id[] = { + { "pcm5121", }, + { "pcm5122", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id); + +static const struct of_device_id pcm512x_of_match[] = { + { .compatible = "ti,pcm5121", }, + { .compatible = "ti,pcm5122", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm512x_of_match); + +static struct i2c_driver pcm512x_i2c_driver = { + .probe = pcm512x_i2c_probe, + .remove = pcm512x_i2c_remove, + .id_table = pcm512x_i2c_id, + .driver = { + .name = "pcm512x", + .owner = THIS_MODULE, + .of_match_table = pcm512x_of_match, + .pm = &pcm512x_pm_ops, + }, +}; + +module_i2c_driver(pcm512x_i2c_driver); + +MODULE_DESCRIPTION("ASoC PCM512x codec driver - I2C"); +MODULE_AUTHOR("Mark Brown <broonie@linaro.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c new file mode 100644 index 000000000000..f297058c0038 --- /dev/null +++ b/sound/soc/codecs/pcm512x-spi.c @@ -0,0 +1,69 @@ +/* + * Driver for the PCM512x CODECs + * + * Author: Mark Brown <broonie@linaro.org> + * Copyright 2014 Linaro Ltd + * + * 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/spi/spi.h> + +#include "pcm512x.h" + +static int pcm512x_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_spi(spi, &pcm512x_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + return ret; + } + + return pcm512x_probe(&spi->dev, regmap); +} + +static int pcm512x_spi_remove(struct spi_device *spi) +{ + pcm512x_remove(&spi->dev); + return 0; +} + +static const struct spi_device_id pcm512x_spi_id[] = { + { "pcm5121", }, + { "pcm5122", }, + { }, +}; +MODULE_DEVICE_TABLE(spi, pcm512x_spi_id); + +static const struct of_device_id pcm512x_of_match[] = { + { .compatible = "ti,pcm5121", }, + { .compatible = "ti,pcm5122", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm512x_of_match); + +static struct spi_driver pcm512x_spi_driver = { + .probe = pcm512x_spi_probe, + .remove = pcm512x_spi_remove, + .id_table = pcm512x_spi_id, + .driver = { + .name = "pcm512x", + .owner = THIS_MODULE, + .of_match_table = pcm512x_of_match, + .pm = &pcm512x_pm_ops, + }, +}; + +module_spi_driver(pcm512x_spi_driver); diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c new file mode 100644 index 000000000000..4b4c0c7bb918 --- /dev/null +++ b/sound/soc/codecs/pcm512x.c @@ -0,0 +1,589 @@ +/* + * Driver for the PCM512x CODECs + * + * Author: Mark Brown <broonie@linaro.org> + * Copyright 2014 Linaro Ltd + * + * 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/clk.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +#include "pcm512x.h" + +#define PCM512x_NUM_SUPPLIES 3 +static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = { + "AVDD", + "DVDD", + "CPVDD", +}; + +struct pcm512x_priv { + struct regmap *regmap; + struct clk *sclk; + struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES]; + struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES]; +}; + +/* + * We can't use the same notifier block for more than one supply and + * there's no way I can see to get from a callback to the caller + * except container_of(). + */ +#define PCM512x_REGULATOR_EVENT(n) \ +static int pcm512x_regulator_event_##n(struct notifier_block *nb, \ + unsigned long event, void *data) \ +{ \ + struct pcm512x_priv *pcm512x = container_of(nb, struct pcm512x_priv, \ + supply_nb[n]); \ + if (event & REGULATOR_EVENT_DISABLE) { \ + regcache_mark_dirty(pcm512x->regmap); \ + regcache_cache_only(pcm512x->regmap, true); \ + } \ + return 0; \ +} + +PCM512x_REGULATOR_EVENT(0) +PCM512x_REGULATOR_EVENT(1) +PCM512x_REGULATOR_EVENT(2) + +static const struct reg_default pcm512x_reg_defaults[] = { + { PCM512x_RESET, 0x00 }, + { PCM512x_POWER, 0x00 }, + { PCM512x_MUTE, 0x00 }, + { PCM512x_DSP, 0x00 }, + { PCM512x_PLL_REF, 0x00 }, + { PCM512x_DAC_ROUTING, 0x11 }, + { PCM512x_DSP_PROGRAM, 0x01 }, + { PCM512x_CLKDET, 0x00 }, + { PCM512x_AUTO_MUTE, 0x00 }, + { PCM512x_ERROR_DETECT, 0x00 }, + { PCM512x_DIGITAL_VOLUME_1, 0x00 }, + { PCM512x_DIGITAL_VOLUME_2, 0x30 }, + { PCM512x_DIGITAL_VOLUME_3, 0x30 }, + { PCM512x_DIGITAL_MUTE_1, 0x22 }, + { PCM512x_DIGITAL_MUTE_2, 0x00 }, + { PCM512x_DIGITAL_MUTE_3, 0x07 }, + { PCM512x_OUTPUT_AMPLITUDE, 0x00 }, + { PCM512x_ANALOG_GAIN_CTRL, 0x00 }, + { PCM512x_UNDERVOLTAGE_PROT, 0x00 }, + { PCM512x_ANALOG_MUTE_CTRL, 0x00 }, + { PCM512x_ANALOG_GAIN_BOOST, 0x00 }, + { PCM512x_VCOM_CTRL_1, 0x00 }, + { PCM512x_VCOM_CTRL_2, 0x01 }, +}; + +static bool pcm512x_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PCM512x_RESET: + case PCM512x_POWER: + case PCM512x_MUTE: + case PCM512x_PLL_EN: + case PCM512x_SPI_MISO_FUNCTION: + case PCM512x_DSP: + case PCM512x_GPIO_EN: + case PCM512x_BCLK_LRCLK_CFG: + case PCM512x_DSP_GPIO_INPUT: + case PCM512x_MASTER_MODE: + case PCM512x_PLL_REF: + case PCM512x_PLL_COEFF_0: + case PCM512x_PLL_COEFF_1: + case PCM512x_PLL_COEFF_2: + case PCM512x_PLL_COEFF_3: + case PCM512x_PLL_COEFF_4: + case PCM512x_DSP_CLKDIV: + case PCM512x_DAC_CLKDIV: + case PCM512x_NCP_CLKDIV: + case PCM512x_OSR_CLKDIV: + case PCM512x_MASTER_CLKDIV_1: + case PCM512x_MASTER_CLKDIV_2: + case PCM512x_FS_SPEED_MODE: + case PCM512x_IDAC_1: + case PCM512x_IDAC_2: + case PCM512x_ERROR_DETECT: + case PCM512x_I2S_1: + case PCM512x_I2S_2: + case PCM512x_DAC_ROUTING: + case PCM512x_DSP_PROGRAM: + case PCM512x_CLKDET: + case PCM512x_AUTO_MUTE: + case PCM512x_DIGITAL_VOLUME_1: + case PCM512x_DIGITAL_VOLUME_2: + case PCM512x_DIGITAL_VOLUME_3: + case PCM512x_DIGITAL_MUTE_1: + case PCM512x_DIGITAL_MUTE_2: + case PCM512x_DIGITAL_MUTE_3: + case PCM512x_GPIO_OUTPUT_1: + case PCM512x_GPIO_OUTPUT_2: + case PCM512x_GPIO_OUTPUT_3: + case PCM512x_GPIO_OUTPUT_4: + case PCM512x_GPIO_OUTPUT_5: + case PCM512x_GPIO_OUTPUT_6: + case PCM512x_GPIO_CONTROL_1: + case PCM512x_GPIO_CONTROL_2: + case PCM512x_OVERFLOW: + case PCM512x_RATE_DET_1: + case PCM512x_RATE_DET_2: + case PCM512x_RATE_DET_3: + case PCM512x_RATE_DET_4: + case PCM512x_ANALOG_MUTE_DET: + case PCM512x_GPIN: + case PCM512x_DIGITAL_MUTE_DET: + case PCM512x_OUTPUT_AMPLITUDE: + case PCM512x_ANALOG_GAIN_CTRL: + case PCM512x_UNDERVOLTAGE_PROT: + case PCM512x_ANALOG_MUTE_CTRL: + case PCM512x_ANALOG_GAIN_BOOST: + case PCM512x_VCOM_CTRL_1: + case PCM512x_VCOM_CTRL_2: + case PCM512x_CRAM_CTRL: + return true; + default: + /* There are 256 raw register addresses */ + return reg < 0xff; + } +} + +static bool pcm512x_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PCM512x_PLL_EN: + case PCM512x_OVERFLOW: + case PCM512x_RATE_DET_1: + case PCM512x_RATE_DET_2: + case PCM512x_RATE_DET_3: + case PCM512x_RATE_DET_4: + case PCM512x_ANALOG_MUTE_DET: + case PCM512x_GPIN: + case PCM512x_DIGITAL_MUTE_DET: + case PCM512x_CRAM_CTRL: + return true; + default: + /* There are 256 raw register addresses */ + return reg < 0xff; + } +} + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1); +static const DECLARE_TLV_DB_SCALE(analog_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0); + +static const char * const pcm512x_dsp_program_texts[] = { + "FIR interpolation with de-emphasis", + "Low latency IIR with de-emphasis", + "Fixed process flow", + "High attenuation with de-emphasis", + "Ringing-less low latency FIR", +}; + +static const unsigned int pcm512x_dsp_program_values[] = { + 1, + 2, + 3, + 5, + 7, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pcm512x_dsp_program, + PCM512x_DSP_PROGRAM, 0, 0x1f, + pcm512x_dsp_program_texts, + pcm512x_dsp_program_values); + +static const char * const pcm512x_clk_missing_text[] = { + "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s" +}; + +static const struct soc_enum pcm512x_clk_missing = + SOC_ENUM_SINGLE(PCM512x_CLKDET, 0, 8, pcm512x_clk_missing_text); + +static const char * const pcm512x_autom_text[] = { + "21ms", "106ms", "213ms", "533ms", "1.07s", "2.13s", "5.33s", "10.66s" +}; + +static const struct soc_enum pcm512x_autom_l = + SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATML_SHIFT, 8, + pcm512x_autom_text); + +static const struct soc_enum pcm512x_autom_r = + SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATMR_SHIFT, 8, + pcm512x_autom_text); + +static const char * const pcm512x_ramp_rate_text[] = { + "1 sample/update", "2 samples/update", "4 samples/update", + "Immediate" +}; + +static const struct soc_enum pcm512x_vndf = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDF_SHIFT, 4, + pcm512x_ramp_rate_text); + +static const struct soc_enum pcm512x_vnuf = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUF_SHIFT, 4, + pcm512x_ramp_rate_text); + +static const struct soc_enum pcm512x_vedf = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDF_SHIFT, 4, + pcm512x_ramp_rate_text); + +static const char * const pcm512x_ramp_step_text[] = { + "4dB/step", "2dB/step", "1dB/step", "0.5dB/step" +}; + +static const struct soc_enum pcm512x_vnds = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDS_SHIFT, 4, + pcm512x_ramp_step_text); + +static const struct soc_enum pcm512x_vnus = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUS_SHIFT, 4, + pcm512x_ramp_step_text); + +static const struct soc_enum pcm512x_veds = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4, + pcm512x_ramp_step_text); + +static const struct snd_kcontrol_new pcm512x_controls[] = { +SOC_DOUBLE_R_TLV("Playback Digital Volume", PCM512x_DIGITAL_VOLUME_2, + PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv), +SOC_DOUBLE_TLV("Playback Volume", PCM512x_ANALOG_GAIN_CTRL, + PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv), +SOC_DOUBLE_TLV("Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST, + PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv), +SOC_DOUBLE("Playback Digital Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT, + PCM512x_RQMR_SHIFT, 1, 1), + +SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1), +SOC_VALUE_ENUM("DSP Program", pcm512x_dsp_program), + +SOC_ENUM("Clock Missing Period", pcm512x_clk_missing), +SOC_ENUM("Auto Mute Time Left", pcm512x_autom_l), +SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r), +SOC_SINGLE("Auto Mute Mono Switch", PCM512x_DIGITAL_MUTE_3, + PCM512x_ACTL_SHIFT, 1, 0), +SOC_DOUBLE("Auto Mute Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT, + PCM512x_AMLR_SHIFT, 1, 0), + +SOC_ENUM("Volume Ramp Down Rate", pcm512x_vndf), +SOC_ENUM("Volume Ramp Down Step", pcm512x_vnds), +SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf), +SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus), +SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf), +SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds), +}; + +static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_OUTPUT("OUTL"), +SND_SOC_DAPM_OUTPUT("OUTR"), +}; + +static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = { + { "DACL", NULL, "Playback" }, + { "DACR", NULL, "Playback" }, + + { "OUTL", NULL, "DACL" }, + { "OUTR", NULL, "DACR" }, +}; + +static int pcm512x_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(codec->dev); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQST, 0); + if (ret != 0) { + dev_err(codec->dev, "Failed to remove standby: %d\n", + ret); + return ret; + } + break; + + case SND_SOC_BIAS_OFF: + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQST, PCM512x_RQST); + if (ret != 0) { + dev_err(codec->dev, "Failed to request standby: %d\n", + ret); + return ret; + } + break; + } + + codec->dapm.bias_level = level; + + return 0; +} + +static struct snd_soc_dai_driver pcm512x_dai = { + .name = "pcm512x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE + }, +}; + +static struct snd_soc_codec_driver pcm512x_codec_driver = { + .set_bias_level = pcm512x_set_bias_level, + .idle_bias_off = true, + + .controls = pcm512x_controls, + .num_controls = ARRAY_SIZE(pcm512x_controls), + .dapm_widgets = pcm512x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets), + .dapm_routes = pcm512x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes), +}; + +static const struct regmap_range_cfg pcm512x_range = { + .name = "Pages", .range_min = PCM512x_VIRT_BASE, + .range_max = PCM512x_MAX_REGISTER, + .selector_reg = PCM512x_PAGE, + .selector_mask = 0xff, + .window_start = 0, .window_len = 0x100, +}; + +const struct regmap_config pcm512x_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = pcm512x_readable, + .volatile_reg = pcm512x_volatile, + + .ranges = &pcm512x_range, + .num_ranges = 1, + + .max_register = PCM512x_MAX_REGISTER, + .reg_defaults = pcm512x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(pcm512x_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(pcm512x_regmap); + +int pcm512x_probe(struct device *dev, struct regmap *regmap) +{ + struct pcm512x_priv *pcm512x; + int i, ret; + + pcm512x = devm_kzalloc(dev, sizeof(struct pcm512x_priv), GFP_KERNEL); + if (!pcm512x) + return -ENOMEM; + + dev_set_drvdata(dev, pcm512x); + pcm512x->regmap = regmap; + + for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) + pcm512x->supplies[i].supply = pcm512x_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + if (ret != 0) { + dev_err(dev, "Failed to get supplies: %d\n", ret); + return ret; + } + + pcm512x->supply_nb[0].notifier_call = pcm512x_regulator_event_0; + pcm512x->supply_nb[1].notifier_call = pcm512x_regulator_event_1; + pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2; + + for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) { + ret = regulator_register_notifier(pcm512x->supplies[i].consumer, + &pcm512x->supply_nb[i]); + if (ret != 0) { + dev_err(dev, + "Failed to register regulator notifier: %d\n", + ret); + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + /* Reset the device, verifying I/O in the process for I2C */ + ret = regmap_write(regmap, PCM512x_RESET, + PCM512x_RSTM | PCM512x_RSTR); + if (ret != 0) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err; + } + + ret = regmap_write(regmap, PCM512x_RESET, 0); + if (ret != 0) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err; + } + + pcm512x->sclk = devm_clk_get(dev, NULL); + if (IS_ERR(pcm512x->sclk)) { + if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_info(dev, "No SCLK, using BCLK: %ld\n", + PTR_ERR(pcm512x->sclk)); + + /* Disable reporting of missing SCLK as an error */ + regmap_update_bits(regmap, PCM512x_ERROR_DETECT, + PCM512x_IDCH, PCM512x_IDCH); + + /* Switch PLL input to BCLK */ + regmap_update_bits(regmap, PCM512x_PLL_REF, + PCM512x_SREF, PCM512x_SREF); + } else { + ret = clk_prepare_enable(pcm512x->sclk); + if (ret != 0) { + dev_err(dev, "Failed to enable SCLK: %d\n", ret); + return ret; + } + } + + /* Default to standby mode */ + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQST, PCM512x_RQST); + if (ret != 0) { + dev_err(dev, "Failed to request standby: %d\n", + ret); + goto err_clk; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + ret = snd_soc_register_codec(dev, &pcm512x_codec_driver, + &pcm512x_dai, 1); + if (ret != 0) { + dev_err(dev, "Failed to register CODEC: %d\n", ret); + goto err_pm; + } + + return 0; + +err_pm: + pm_runtime_disable(dev); +err_clk: + if (!IS_ERR(pcm512x->sclk)) + clk_disable_unprepare(pcm512x->sclk); +err: + regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + return ret; +} +EXPORT_SYMBOL_GPL(pcm512x_probe); + +void pcm512x_remove(struct device *dev) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); + + snd_soc_unregister_codec(dev); + pm_runtime_disable(dev); + if (!IS_ERR(pcm512x->sclk)) + clk_disable_unprepare(pcm512x->sclk); + regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); +} +EXPORT_SYMBOL_GPL(pcm512x_remove); + +static int pcm512x_suspend(struct device *dev) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQPD, PCM512x_RQPD); + if (ret != 0) { + dev_err(dev, "Failed to request power down: %d\n", ret); + return ret; + } + + ret = regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + if (ret != 0) { + dev_err(dev, "Failed to disable supplies: %d\n", ret); + return ret; + } + + if (!IS_ERR(pcm512x->sclk)) + clk_disable_unprepare(pcm512x->sclk); + + return 0; +} + +static int pcm512x_resume(struct device *dev) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); + int ret; + + if (!IS_ERR(pcm512x->sclk)) { + ret = clk_prepare_enable(pcm512x->sclk); + if (ret != 0) { + dev_err(dev, "Failed to enable SCLK: %d\n", ret); + return ret; + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + regcache_cache_only(pcm512x->regmap, false); + ret = regcache_sync(pcm512x->regmap); + if (ret != 0) { + dev_err(dev, "Failed to sync cache: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQPD, 0); + if (ret != 0) { + dev_err(dev, "Failed to remove power down: %d\n", ret); + return ret; + } + + return 0; +} + +const struct dev_pm_ops pcm512x_pm_ops = { + SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL) +}; +EXPORT_SYMBOL_GPL(pcm512x_pm_ops); + +MODULE_DESCRIPTION("ASoC PCM512x codec driver"); +MODULE_AUTHOR("Mark Brown <broonie@linaro.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h new file mode 100644 index 000000000000..6ee76aaca09a --- /dev/null +++ b/sound/soc/codecs/pcm512x.h @@ -0,0 +1,171 @@ +/* + * Driver for the PCM512x CODECs + * + * Author: Mark Brown <broonie@linaro.org> + * Copyright 2014 Linaro Ltd + * + * 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. + */ + +#ifndef _SND_SOC_PCM512X +#define _SND_SOC_PCM512X + +#include <linux/pm.h> +#include <linux/regmap.h> + +#define PCM512x_VIRT_BASE 0x100 +#define PCM512x_PAGE_LEN 0x100 +#define PCM512x_PAGE_BASE(n) (PCM512x_VIRT_BASE + (PCM512x_PAGE_LEN * n)) + +#define PCM512x_PAGE 0 + +#define PCM512x_RESET (PCM512x_PAGE_BASE(0) + 1) +#define PCM512x_POWER (PCM512x_PAGE_BASE(0) + 2) +#define PCM512x_MUTE (PCM512x_PAGE_BASE(0) + 3) +#define PCM512x_PLL_EN (PCM512x_PAGE_BASE(0) + 4) +#define PCM512x_SPI_MISO_FUNCTION (PCM512x_PAGE_BASE(0) + 6) +#define PCM512x_DSP (PCM512x_PAGE_BASE(0) + 7) +#define PCM512x_GPIO_EN (PCM512x_PAGE_BASE(0) + 8) +#define PCM512x_BCLK_LRCLK_CFG (PCM512x_PAGE_BASE(0) + 9) +#define PCM512x_DSP_GPIO_INPUT (PCM512x_PAGE_BASE(0) + 10) +#define PCM512x_MASTER_MODE (PCM512x_PAGE_BASE(0) + 12) +#define PCM512x_PLL_REF (PCM512x_PAGE_BASE(0) + 13) +#define PCM512x_PLL_COEFF_0 (PCM512x_PAGE_BASE(0) + 20) +#define PCM512x_PLL_COEFF_1 (PCM512x_PAGE_BASE(0) + 21) +#define PCM512x_PLL_COEFF_2 (PCM512x_PAGE_BASE(0) + 22) +#define PCM512x_PLL_COEFF_3 (PCM512x_PAGE_BASE(0) + 23) +#define PCM512x_PLL_COEFF_4 (PCM512x_PAGE_BASE(0) + 24) +#define PCM512x_DSP_CLKDIV (PCM512x_PAGE_BASE(0) + 27) +#define PCM512x_DAC_CLKDIV (PCM512x_PAGE_BASE(0) + 28) +#define PCM512x_NCP_CLKDIV (PCM512x_PAGE_BASE(0) + 29) +#define PCM512x_OSR_CLKDIV (PCM512x_PAGE_BASE(0) + 30) +#define PCM512x_MASTER_CLKDIV_1 (PCM512x_PAGE_BASE(0) + 32) +#define PCM512x_MASTER_CLKDIV_2 (PCM512x_PAGE_BASE(0) + 33) +#define PCM512x_FS_SPEED_MODE (PCM512x_PAGE_BASE(0) + 34) +#define PCM512x_IDAC_1 (PCM512x_PAGE_BASE(0) + 35) +#define PCM512x_IDAC_2 (PCM512x_PAGE_BASE(0) + 36) +#define PCM512x_ERROR_DETECT (PCM512x_PAGE_BASE(0) + 37) +#define PCM512x_I2S_1 (PCM512x_PAGE_BASE(0) + 40) +#define PCM512x_I2S_2 (PCM512x_PAGE_BASE(0) + 41) +#define PCM512x_DAC_ROUTING (PCM512x_PAGE_BASE(0) + 42) +#define PCM512x_DSP_PROGRAM (PCM512x_PAGE_BASE(0) + 43) +#define PCM512x_CLKDET (PCM512x_PAGE_BASE(0) + 44) +#define PCM512x_AUTO_MUTE (PCM512x_PAGE_BASE(0) + 59) +#define PCM512x_DIGITAL_VOLUME_1 (PCM512x_PAGE_BASE(0) + 60) +#define PCM512x_DIGITAL_VOLUME_2 (PCM512x_PAGE_BASE(0) + 61) +#define PCM512x_DIGITAL_VOLUME_3 (PCM512x_PAGE_BASE(0) + 62) +#define PCM512x_DIGITAL_MUTE_1 (PCM512x_PAGE_BASE(0) + 63) +#define PCM512x_DIGITAL_MUTE_2 (PCM512x_PAGE_BASE(0) + 64) +#define PCM512x_DIGITAL_MUTE_3 (PCM512x_PAGE_BASE(0) + 65) +#define PCM512x_GPIO_OUTPUT_1 (PCM512x_PAGE_BASE(0) + 80) +#define PCM512x_GPIO_OUTPUT_2 (PCM512x_PAGE_BASE(0) + 81) +#define PCM512x_GPIO_OUTPUT_3 (PCM512x_PAGE_BASE(0) + 82) +#define PCM512x_GPIO_OUTPUT_4 (PCM512x_PAGE_BASE(0) + 83) +#define PCM512x_GPIO_OUTPUT_5 (PCM512x_PAGE_BASE(0) + 84) +#define PCM512x_GPIO_OUTPUT_6 (PCM512x_PAGE_BASE(0) + 85) +#define PCM512x_GPIO_CONTROL_1 (PCM512x_PAGE_BASE(0) + 86) +#define PCM512x_GPIO_CONTROL_2 (PCM512x_PAGE_BASE(0) + 87) +#define PCM512x_OVERFLOW (PCM512x_PAGE_BASE(0) + 90) +#define PCM512x_RATE_DET_1 (PCM512x_PAGE_BASE(0) + 91) +#define PCM512x_RATE_DET_2 (PCM512x_PAGE_BASE(0) + 92) +#define PCM512x_RATE_DET_3 (PCM512x_PAGE_BASE(0) + 93) +#define PCM512x_RATE_DET_4 (PCM512x_PAGE_BASE(0) + 94) +#define PCM512x_ANALOG_MUTE_DET (PCM512x_PAGE_BASE(0) + 108) +#define PCM512x_GPIN (PCM512x_PAGE_BASE(0) + 119) +#define PCM512x_DIGITAL_MUTE_DET (PCM512x_PAGE_BASE(0) + 120) + +#define PCM512x_OUTPUT_AMPLITUDE (PCM512x_PAGE_BASE(1) + 1) +#define PCM512x_ANALOG_GAIN_CTRL (PCM512x_PAGE_BASE(1) + 2) +#define PCM512x_UNDERVOLTAGE_PROT (PCM512x_PAGE_BASE(1) + 5) +#define PCM512x_ANALOG_MUTE_CTRL (PCM512x_PAGE_BASE(1) + 6) +#define PCM512x_ANALOG_GAIN_BOOST (PCM512x_PAGE_BASE(1) + 7) +#define PCM512x_VCOM_CTRL_1 (PCM512x_PAGE_BASE(1) + 8) +#define PCM512x_VCOM_CTRL_2 (PCM512x_PAGE_BASE(1) + 9) + +#define PCM512x_CRAM_CTRL (PCM512x_PAGE_BASE(44) + 1) + +#define PCM512x_MAX_REGISTER (PCM512x_PAGE_BASE(44) + 1) + +/* Page 0, Register 1 - reset */ +#define PCM512x_RSTR (1 << 0) +#define PCM512x_RSTM (1 << 4) + +/* Page 0, Register 2 - power */ +#define PCM512x_RQPD (1 << 0) +#define PCM512x_RQPD_SHIFT 0 +#define PCM512x_RQST (1 << 4) +#define PCM512x_RQST_SHIFT 4 + +/* Page 0, Register 3 - mute */ +#define PCM512x_RQMR_SHIFT 0 +#define PCM512x_RQML_SHIFT 4 + +/* Page 0, Register 4 - PLL */ +#define PCM512x_PLCE (1 << 0) +#define PCM512x_RLCE_SHIFT 0 +#define PCM512x_PLCK (1 << 4) +#define PCM512x_PLCK_SHIFT 4 + +/* Page 0, Register 7 - DSP */ +#define PCM512x_SDSL (1 << 0) +#define PCM512x_SDSL_SHIFT 0 +#define PCM512x_DEMP (1 << 4) +#define PCM512x_DEMP_SHIFT 4 + +/* Page 0, Register 13 - PLL reference */ +#define PCM512x_SREF (1 << 4) + +/* Page 0, Register 37 - Error detection */ +#define PCM512x_IPLK (1 << 0) +#define PCM512x_DCAS (1 << 1) +#define PCM512x_IDCM (1 << 2) +#define PCM512x_IDCH (1 << 3) +#define PCM512x_IDSK (1 << 4) +#define PCM512x_IDBK (1 << 5) +#define PCM512x_IDFS (1 << 6) + +/* Page 0, Register 42 - DAC routing */ +#define PCM512x_AUPR_SHIFT 0 +#define PCM512x_AUPL_SHIFT 4 + +/* Page 0, Register 59 - auto mute */ +#define PCM512x_ATMR_SHIFT 0 +#define PCM512x_ATML_SHIFT 4 + +/* Page 0, Register 63 - ramp rates */ +#define PCM512x_VNDF_SHIFT 6 +#define PCM512x_VNDS_SHIFT 4 +#define PCM512x_VNUF_SHIFT 2 +#define PCM512x_VNUS_SHIFT 0 + +/* Page 0, Register 64 - emergency ramp rates */ +#define PCM512x_VEDF_SHIFT 6 +#define PCM512x_VEDS_SHIFT 4 + +/* Page 0, Register 65 - Digital mute enables */ +#define PCM512x_ACTL_SHIFT 2 +#define PCM512x_AMLE_SHIFT 1 +#define PCM512x_AMLR_SHIFT 0 + +/* Page 1, Register 2 - analog volume control */ +#define PCM512x_RAGN_SHIFT 0 +#define PCM512x_LAGN_SHIFT 4 + +/* Page 1, Register 7 - analog boost control */ +#define PCM512x_AGBR_SHIFT 0 +#define PCM512x_AGBL_SHIFT 4 + +extern const struct dev_pm_ops pcm512x_pm_ops; +extern const struct regmap_config pcm512x_regmap; + +int pcm512x_probe(struct device *dev, struct regmap *regmap); +void pcm512x_remove(struct device *dev); + +#endif diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index 912c9cbc2724..ce199d375209 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -210,26 +210,22 @@ static int rt5631_dmic_put(struct snd_kcontrol *kcontrol, static const char *rt5631_input_mode[] = { "Single ended", "Differential"}; -static const SOC_ENUM_SINGLE_DECL( - rt5631_mic1_mode_enum, RT5631_MIC_CTRL_1, - RT5631_MIC1_DIFF_INPUT_SHIFT, rt5631_input_mode); +static SOC_ENUM_SINGLE_DECL(rt5631_mic1_mode_enum, RT5631_MIC_CTRL_1, + RT5631_MIC1_DIFF_INPUT_SHIFT, rt5631_input_mode); -static const SOC_ENUM_SINGLE_DECL( - rt5631_mic2_mode_enum, RT5631_MIC_CTRL_1, - RT5631_MIC2_DIFF_INPUT_SHIFT, rt5631_input_mode); +static SOC_ENUM_SINGLE_DECL(rt5631_mic2_mode_enum, RT5631_MIC_CTRL_1, + RT5631_MIC2_DIFF_INPUT_SHIFT, rt5631_input_mode); /* MONO Input Type */ -static const SOC_ENUM_SINGLE_DECL( - rt5631_monoin_mode_enum, RT5631_MONO_INPUT_VOL, - RT5631_MONO_DIFF_INPUT_SHIFT, rt5631_input_mode); +static SOC_ENUM_SINGLE_DECL(rt5631_monoin_mode_enum, RT5631_MONO_INPUT_VOL, + RT5631_MONO_DIFF_INPUT_SHIFT, rt5631_input_mode); /* SPK Ratio Gain Control */ static const char *rt5631_spk_ratio[] = {"1.00x", "1.09x", "1.27x", "1.44x", "1.56x", "1.68x", "1.99x", "2.34x"}; -static const SOC_ENUM_SINGLE_DECL( - rt5631_spk_ratio_enum, RT5631_GEN_PUR_CTRL_REG, - RT5631_SPK_AMP_RATIO_CTRL_SHIFT, rt5631_spk_ratio); +static SOC_ENUM_SINGLE_DECL(rt5631_spk_ratio_enum, RT5631_GEN_PUR_CTRL_REG, + RT5631_SPK_AMP_RATIO_CTRL_SHIFT, rt5631_spk_ratio); static const struct snd_kcontrol_new rt5631_snd_controls[] = { /* MIC */ @@ -759,9 +755,8 @@ static const struct snd_kcontrol_new rt5631_monomix_mixer_controls[] = { /* Left SPK Volume Input */ static const char *rt5631_spkvoll_sel[] = {"Vmid", "SPKMIXL"}; -static const SOC_ENUM_SINGLE_DECL( - rt5631_spkvoll_enum, RT5631_SPK_OUT_VOL, - RT5631_L_EN_SHIFT, rt5631_spkvoll_sel); +static SOC_ENUM_SINGLE_DECL(rt5631_spkvoll_enum, RT5631_SPK_OUT_VOL, + RT5631_L_EN_SHIFT, rt5631_spkvoll_sel); static const struct snd_kcontrol_new rt5631_spkvoll_mux_control = SOC_DAPM_ENUM("Left SPKVOL SRC", rt5631_spkvoll_enum); @@ -769,9 +764,8 @@ static const struct snd_kcontrol_new rt5631_spkvoll_mux_control = /* Left HP Volume Input */ static const char *rt5631_hpvoll_sel[] = {"Vmid", "OUTMIXL"}; -static const SOC_ENUM_SINGLE_DECL( - rt5631_hpvoll_enum, RT5631_HP_OUT_VOL, - RT5631_L_EN_SHIFT, rt5631_hpvoll_sel); +static SOC_ENUM_SINGLE_DECL(rt5631_hpvoll_enum, RT5631_HP_OUT_VOL, + RT5631_L_EN_SHIFT, rt5631_hpvoll_sel); static const struct snd_kcontrol_new rt5631_hpvoll_mux_control = SOC_DAPM_ENUM("Left HPVOL SRC", rt5631_hpvoll_enum); @@ -779,9 +773,8 @@ static const struct snd_kcontrol_new rt5631_hpvoll_mux_control = /* Left Out Volume Input */ static const char *rt5631_outvoll_sel[] = {"Vmid", "OUTMIXL"}; -static const SOC_ENUM_SINGLE_DECL( - rt5631_outvoll_enum, RT5631_MONO_AXO_1_2_VOL, - RT5631_L_EN_SHIFT, rt5631_outvoll_sel); +static SOC_ENUM_SINGLE_DECL(rt5631_outvoll_enum, RT5631_MONO_AXO_1_2_VOL, + RT5631_L_EN_SHIFT, rt5631_outvoll_sel); static const struct snd_kcontrol_new rt5631_outvoll_mux_control = SOC_DAPM_ENUM("Left OUTVOL SRC", rt5631_outvoll_enum); @@ -789,9 +782,8 @@ static const struct snd_kcontrol_new rt5631_outvoll_mux_control = /* Right Out Volume Input */ static const char *rt5631_outvolr_sel[] = {"Vmid", "OUTMIXR"}; -static const SOC_ENUM_SINGLE_DECL( - rt5631_outvolr_enum, RT5631_MONO_AXO_1_2_VOL, - RT5631_R_EN_SHIFT, rt5631_outvolr_sel); +static SOC_ENUM_SINGLE_DECL(rt5631_outvolr_enum, RT5631_MONO_AXO_1_2_VOL, + RT5631_R_EN_SHIFT, rt5631_outvolr_sel); static const struct snd_kcontrol_new rt5631_outvolr_mux_control = SOC_DAPM_ENUM("Right OUTVOL SRC", rt5631_outvolr_enum); @@ -799,9 +791,8 @@ static const struct snd_kcontrol_new rt5631_outvolr_mux_control = /* Right HP Volume Input */ static const char *rt5631_hpvolr_sel[] = {"Vmid", "OUTMIXR"}; -static const SOC_ENUM_SINGLE_DECL( - rt5631_hpvolr_enum, RT5631_HP_OUT_VOL, - RT5631_R_EN_SHIFT, rt5631_hpvolr_sel); +static SOC_ENUM_SINGLE_DECL(rt5631_hpvolr_enum, RT5631_HP_OUT_VOL, + RT5631_R_EN_SHIFT, rt5631_hpvolr_sel); static const struct snd_kcontrol_new rt5631_hpvolr_mux_control = SOC_DAPM_ENUM("Right HPVOL SRC", rt5631_hpvolr_enum); @@ -809,9 +800,8 @@ static const struct snd_kcontrol_new rt5631_hpvolr_mux_control = /* Right SPK Volume Input */ static const char *rt5631_spkvolr_sel[] = {"Vmid", "SPKMIXR"}; -static const SOC_ENUM_SINGLE_DECL( - rt5631_spkvolr_enum, RT5631_SPK_OUT_VOL, - RT5631_R_EN_SHIFT, rt5631_spkvolr_sel); +static SOC_ENUM_SINGLE_DECL(rt5631_spkvolr_enum, RT5631_SPK_OUT_VOL, + RT5631_R_EN_SHIFT, rt5631_spkvolr_sel); static const struct snd_kcontrol_new rt5631_spkvolr_mux_control = SOC_DAPM_ENUM("Right SPKVOL SRC", rt5631_spkvolr_enum); @@ -820,9 +810,8 @@ static const struct snd_kcontrol_new rt5631_spkvolr_mux_control = static const char *rt5631_spol_src_sel[] = { "SPOLMIX", "MONOIN_RX", "VDAC", "DACL"}; -static const SOC_ENUM_SINGLE_DECL( - rt5631_spol_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, - RT5631_SPK_L_MUX_SEL_SHIFT, rt5631_spol_src_sel); +static SOC_ENUM_SINGLE_DECL(rt5631_spol_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, + RT5631_SPK_L_MUX_SEL_SHIFT, rt5631_spol_src_sel); static const struct snd_kcontrol_new rt5631_spol_mux_control = SOC_DAPM_ENUM("SPOL SRC", rt5631_spol_src_enum); @@ -831,9 +820,8 @@ static const struct snd_kcontrol_new rt5631_spol_mux_control = static const char *rt5631_spor_src_sel[] = { "SPORMIX", "MONOIN_RX", "VDAC", "DACR"}; -static const SOC_ENUM_SINGLE_DECL( - rt5631_spor_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, - RT5631_SPK_R_MUX_SEL_SHIFT, rt5631_spor_src_sel); +static SOC_ENUM_SINGLE_DECL(rt5631_spor_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, + RT5631_SPK_R_MUX_SEL_SHIFT, rt5631_spor_src_sel); static const struct snd_kcontrol_new rt5631_spor_mux_control = SOC_DAPM_ENUM("SPOR SRC", rt5631_spor_src_enum); @@ -841,9 +829,8 @@ static const struct snd_kcontrol_new rt5631_spor_mux_control = /* MONO Input */ static const char *rt5631_mono_src_sel[] = {"MONOMIX", "MONOIN_RX", "VDAC"}; -static const SOC_ENUM_SINGLE_DECL( - rt5631_mono_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, - RT5631_MONO_MUX_SEL_SHIFT, rt5631_mono_src_sel); +static SOC_ENUM_SINGLE_DECL(rt5631_mono_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, + RT5631_MONO_MUX_SEL_SHIFT, rt5631_mono_src_sel); static const struct snd_kcontrol_new rt5631_mono_mux_control = SOC_DAPM_ENUM("MONO SRC", rt5631_mono_src_enum); @@ -851,9 +838,8 @@ static const struct snd_kcontrol_new rt5631_mono_mux_control = /* Left HPO Input */ static const char *rt5631_hpl_src_sel[] = {"Left HPVOL", "Left DAC"}; -static const SOC_ENUM_SINGLE_DECL( - rt5631_hpl_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, - RT5631_HP_L_MUX_SEL_SHIFT, rt5631_hpl_src_sel); +static SOC_ENUM_SINGLE_DECL(rt5631_hpl_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, + RT5631_HP_L_MUX_SEL_SHIFT, rt5631_hpl_src_sel); static const struct snd_kcontrol_new rt5631_hpl_mux_control = SOC_DAPM_ENUM("HPL SRC", rt5631_hpl_src_enum); @@ -861,9 +847,8 @@ static const struct snd_kcontrol_new rt5631_hpl_mux_control = /* Right HPO Input */ static const char *rt5631_hpr_src_sel[] = {"Right HPVOL", "Right DAC"}; -static const SOC_ENUM_SINGLE_DECL( - rt5631_hpr_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, - RT5631_HP_R_MUX_SEL_SHIFT, rt5631_hpr_src_sel); +static SOC_ENUM_SINGLE_DECL(rt5631_hpr_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, + RT5631_HP_R_MUX_SEL_SHIFT, rt5631_hpr_src_sel); static const struct snd_kcontrol_new rt5631_hpr_mux_control = SOC_DAPM_ENUM("HPR SRC", rt5631_hpr_src_enum); diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index a3fb41179636..1a1e1150237d 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -361,25 +361,24 @@ static unsigned int bst_tlv[] = { static const char * const rt5640_data_select[] = { "Normal", "left copy to right", "right copy to left", "Swap"}; -static const SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA, - RT5640_IF1_DAC_SEL_SFT, rt5640_data_select); +static SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA, + RT5640_IF1_DAC_SEL_SFT, rt5640_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5640_if1_adc_enum, RT5640_DIG_INF_DATA, - RT5640_IF1_ADC_SEL_SFT, rt5640_data_select); +static SOC_ENUM_SINGLE_DECL(rt5640_if1_adc_enum, RT5640_DIG_INF_DATA, + RT5640_IF1_ADC_SEL_SFT, rt5640_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5640_if2_dac_enum, RT5640_DIG_INF_DATA, - RT5640_IF2_DAC_SEL_SFT, rt5640_data_select); +static SOC_ENUM_SINGLE_DECL(rt5640_if2_dac_enum, RT5640_DIG_INF_DATA, + RT5640_IF2_DAC_SEL_SFT, rt5640_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5640_if2_adc_enum, RT5640_DIG_INF_DATA, - RT5640_IF2_ADC_SEL_SFT, rt5640_data_select); +static SOC_ENUM_SINGLE_DECL(rt5640_if2_adc_enum, RT5640_DIG_INF_DATA, + RT5640_IF2_ADC_SEL_SFT, rt5640_data_select); /* Class D speaker gain ratio */ static const char * const rt5640_clsd_spk_ratio[] = {"1.66x", "1.83x", "1.94x", "2x", "2.11x", "2.22x", "2.33x", "2.44x", "2.55x", "2.66x", "2.77x"}; -static const SOC_ENUM_SINGLE_DECL( - rt5640_clsd_spk_ratio_enum, RT5640_CLS_D_OUT, - RT5640_CLSD_RATIO_SFT, rt5640_clsd_spk_ratio); +static SOC_ENUM_SINGLE_DECL(rt5640_clsd_spk_ratio_enum, RT5640_CLS_D_OUT, + RT5640_CLSD_RATIO_SFT, rt5640_clsd_spk_ratio); static const struct snd_kcontrol_new rt5640_snd_controls[] = { /* Speaker Output Volume */ @@ -753,9 +752,8 @@ static const char * const rt5640_stereo_adc1_src[] = { "DIG MIX", "ADC" }; -static const SOC_ENUM_SINGLE_DECL( - rt5640_stereo_adc1_enum, RT5640_STO_ADC_MIXER, - RT5640_ADC_1_SRC_SFT, rt5640_stereo_adc1_src); +static SOC_ENUM_SINGLE_DECL(rt5640_stereo_adc1_enum, RT5640_STO_ADC_MIXER, + RT5640_ADC_1_SRC_SFT, rt5640_stereo_adc1_src); static const struct snd_kcontrol_new rt5640_sto_adc_1_mux = SOC_DAPM_ENUM("Stereo ADC1 Mux", rt5640_stereo_adc1_enum); @@ -764,9 +762,8 @@ static const char * const rt5640_stereo_adc2_src[] = { "DMIC1", "DMIC2", "DIG MIX" }; -static const SOC_ENUM_SINGLE_DECL( - rt5640_stereo_adc2_enum, RT5640_STO_ADC_MIXER, - RT5640_ADC_2_SRC_SFT, rt5640_stereo_adc2_src); +static SOC_ENUM_SINGLE_DECL(rt5640_stereo_adc2_enum, RT5640_STO_ADC_MIXER, + RT5640_ADC_2_SRC_SFT, rt5640_stereo_adc2_src); static const struct snd_kcontrol_new rt5640_sto_adc_2_mux = SOC_DAPM_ENUM("Stereo ADC2 Mux", rt5640_stereo_adc2_enum); @@ -776,9 +773,8 @@ static const char * const rt5640_mono_adc_l1_src[] = { "Mono DAC MIXL", "ADCL" }; -static const SOC_ENUM_SINGLE_DECL( - rt5640_mono_adc_l1_enum, RT5640_MONO_ADC_MIXER, - RT5640_MONO_ADC_L1_SRC_SFT, rt5640_mono_adc_l1_src); +static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_l1_enum, RT5640_MONO_ADC_MIXER, + RT5640_MONO_ADC_L1_SRC_SFT, rt5640_mono_adc_l1_src); static const struct snd_kcontrol_new rt5640_mono_adc_l1_mux = SOC_DAPM_ENUM("Mono ADC1 left source", rt5640_mono_adc_l1_enum); @@ -787,9 +783,8 @@ static const char * const rt5640_mono_adc_l2_src[] = { "DMIC L1", "DMIC L2", "Mono DAC MIXL" }; -static const SOC_ENUM_SINGLE_DECL( - rt5640_mono_adc_l2_enum, RT5640_MONO_ADC_MIXER, - RT5640_MONO_ADC_L2_SRC_SFT, rt5640_mono_adc_l2_src); +static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_l2_enum, RT5640_MONO_ADC_MIXER, + RT5640_MONO_ADC_L2_SRC_SFT, rt5640_mono_adc_l2_src); static const struct snd_kcontrol_new rt5640_mono_adc_l2_mux = SOC_DAPM_ENUM("Mono ADC2 left source", rt5640_mono_adc_l2_enum); @@ -798,9 +793,8 @@ static const char * const rt5640_mono_adc_r1_src[] = { "Mono DAC MIXR", "ADCR" }; -static const SOC_ENUM_SINGLE_DECL( - rt5640_mono_adc_r1_enum, RT5640_MONO_ADC_MIXER, - RT5640_MONO_ADC_R1_SRC_SFT, rt5640_mono_adc_r1_src); +static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_r1_enum, RT5640_MONO_ADC_MIXER, + RT5640_MONO_ADC_R1_SRC_SFT, rt5640_mono_adc_r1_src); static const struct snd_kcontrol_new rt5640_mono_adc_r1_mux = SOC_DAPM_ENUM("Mono ADC1 right source", rt5640_mono_adc_r1_enum); @@ -809,9 +803,8 @@ static const char * const rt5640_mono_adc_r2_src[] = { "DMIC R1", "DMIC R2", "Mono DAC MIXR" }; -static const SOC_ENUM_SINGLE_DECL( - rt5640_mono_adc_r2_enum, RT5640_MONO_ADC_MIXER, - RT5640_MONO_ADC_R2_SRC_SFT, rt5640_mono_adc_r2_src); +static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_r2_enum, RT5640_MONO_ADC_MIXER, + RT5640_MONO_ADC_R2_SRC_SFT, rt5640_mono_adc_r2_src); static const struct snd_kcontrol_new rt5640_mono_adc_r2_mux = SOC_DAPM_ENUM("Mono ADC2 right source", rt5640_mono_adc_r2_enum); @@ -826,9 +819,9 @@ static int rt5640_dac_l2_values[] = { 3, }; -static const SOC_VALUE_ENUM_SINGLE_DECL( - rt5640_dac_l2_enum, RT5640_DSP_PATH2, RT5640_DAC_L2_SEL_SFT, - 0x3, rt5640_dac_l2_src, rt5640_dac_l2_values); +static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dac_l2_enum, + RT5640_DSP_PATH2, RT5640_DAC_L2_SEL_SFT, + 0x3, rt5640_dac_l2_src, rt5640_dac_l2_values); static const struct snd_kcontrol_new rt5640_dac_l2_mux = SOC_DAPM_VALUE_ENUM("DAC2 left channel source", rt5640_dac_l2_enum); @@ -841,9 +834,9 @@ static int rt5640_dac_r2_values[] = { 0, }; -static const SOC_VALUE_ENUM_SINGLE_DECL( - rt5640_dac_r2_enum, RT5640_DSP_PATH2, RT5640_DAC_R2_SEL_SFT, - 0x3, rt5640_dac_r2_src, rt5640_dac_r2_values); +static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dac_r2_enum, + RT5640_DSP_PATH2, RT5640_DAC_R2_SEL_SFT, + 0x3, rt5640_dac_r2_src, rt5640_dac_r2_values); static const struct snd_kcontrol_new rt5640_dac_r2_mux = SOC_DAPM_ENUM("DAC2 right channel source", rt5640_dac_r2_enum); @@ -860,9 +853,10 @@ static int rt5640_dai_iis_map_values[] = { 7, }; -static const SOC_VALUE_ENUM_SINGLE_DECL( - rt5640_dai_iis_map_enum, RT5640_I2S1_SDP, RT5640_I2S_IF_SFT, - 0x7, rt5640_dai_iis_map, rt5640_dai_iis_map_values); +static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dai_iis_map_enum, + RT5640_I2S1_SDP, RT5640_I2S_IF_SFT, + 0x7, rt5640_dai_iis_map, + rt5640_dai_iis_map_values); static const struct snd_kcontrol_new rt5640_dai_mux = SOC_DAPM_VALUE_ENUM("DAI select", rt5640_dai_iis_map_enum); @@ -872,9 +866,8 @@ static const char * const rt5640_sdi_sel[] = { "IF1", "IF2" }; -static const SOC_ENUM_SINGLE_DECL( - rt5640_sdi_sel_enum, RT5640_I2S2_SDP, - RT5640_I2S2_SDI_SFT, rt5640_sdi_sel); +static SOC_ENUM_SINGLE_DECL(rt5640_sdi_sel_enum, RT5640_I2S2_SDP, + RT5640_I2S2_SDI_SFT, rt5640_sdi_sel); static const struct snd_kcontrol_new rt5640_sdi_mux = SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum); @@ -2093,6 +2086,7 @@ MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id); #ifdef CONFIG_ACPI static struct acpi_device_id rt5640_acpi_match[] = { { "INT33CA", 0 }, + { "10EC5640", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match); diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 1f4093f3f3a1..ab4754a7a88c 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; }; /* @@ -186,8 +187,9 @@ static const char *adc_mux_text[] = { "MIC_IN", "LINE_IN" }; -static const struct soc_enum adc_enum = -SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 2, 2, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(adc_enum, + SGTL5000_CHIP_ANA_CTRL, 2, + adc_mux_text); static const struct snd_kcontrol_new adc_mux = SOC_DAPM_ENUM("Capture Mux", adc_enum); @@ -197,8 +199,9 @@ static const char *dac_mux_text[] = { "DAC", "LINE_IN" }; -static const struct soc_enum dac_enum = -SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 6, 2, dac_mux_text); +static SOC_ENUM_SINGLE_DECL(dac_enum, + SGTL5000_CHIP_ANA_CTRL, 6, + dac_mux_text); static const struct snd_kcontrol_new dac_mux = SOC_DAPM_ENUM("Headphone Mux", dac_enum); @@ -1285,41 +1288,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 +1335,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, ®); - 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 +1539,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/si476x.c b/sound/soc/codecs/si476x.c index 52e7cb08434b..fa2b8e07f420 100644 --- a/sound/soc/codecs/si476x.c +++ b/sound/soc/codecs/si476x.c @@ -210,7 +210,7 @@ out: static int si476x_codec_probe(struct snd_soc_codec *codec) { codec->control_data = dev_get_regmap(codec->dev->parent, NULL); - return 0; + return snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP); } static struct snd_soc_dai_ops si476x_dai_ops = { diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c new file mode 100644 index 000000000000..90e3a228bae4 --- /dev/null +++ b/sound/soc/codecs/sirf-audio-codec.c @@ -0,0 +1,533 @@ +/* + * SiRF audio codec driver + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/soc.h> +#include <sound/dmaengine_pcm.h> + +#include "sirf-audio-codec.h" + +struct sirf_audio_codec { + struct clk *clk; + struct regmap *regmap; + u32 reg_ctrl0, reg_ctrl1; +}; + +static const char * const input_mode_mux[] = {"Single-ended", + "Differential"}; + +static const struct soc_enum input_mode_mux_enum = + SOC_ENUM_SINGLE(AUDIO_IC_CODEC_CTRL1, 4, 2, input_mode_mux); + +static const struct snd_kcontrol_new sirf_audio_codec_input_mode_control = + SOC_DAPM_ENUM("Route", input_mode_mux_enum); + +static const DECLARE_TLV_DB_SCALE(playback_vol_tlv, -12400, 100, 0); +static const DECLARE_TLV_DB_SCALE(capture_vol_tlv_prima2, 500, 100, 0); +static const DECLARE_TLV_DB_RANGE(capture_vol_tlv_atlas6, + 0, 7, TLV_DB_SCALE_ITEM(-100, 100, 0), + 0x22, 0x3F, TLV_DB_SCALE_ITEM(700, 100, 0), +); + +static struct snd_kcontrol_new volume_controls_atlas6[] = { + SOC_DOUBLE_TLV("Playback Volume", AUDIO_IC_CODEC_CTRL0, 21, 14, + 0x7F, 0, playback_vol_tlv), + SOC_DOUBLE_TLV("Capture Volume", AUDIO_IC_CODEC_CTRL1, 16, 10, + 0x3F, 0, capture_vol_tlv_atlas6), +}; + +static struct snd_kcontrol_new volume_controls_prima2[] = { + SOC_DOUBLE_TLV("Speaker Volume", AUDIO_IC_CODEC_CTRL0, 21, 14, + 0x7F, 0, playback_vol_tlv), + SOC_DOUBLE_TLV("Capture Volume", AUDIO_IC_CODEC_CTRL1, 15, 10, + 0x1F, 0, capture_vol_tlv_prima2), +}; + +static struct snd_kcontrol_new left_input_path_controls[] = { + SOC_DAPM_SINGLE("Line Left Switch", AUDIO_IC_CODEC_CTRL1, 6, 1, 0), + SOC_DAPM_SINGLE("Mic Left Switch", AUDIO_IC_CODEC_CTRL1, 3, 1, 0), +}; + +static struct snd_kcontrol_new right_input_path_controls[] = { + SOC_DAPM_SINGLE("Line Right Switch", AUDIO_IC_CODEC_CTRL1, 5, 1, 0), + SOC_DAPM_SINGLE("Mic Right Switch", AUDIO_IC_CODEC_CTRL1, 2, 1, 0), +}; + +static struct snd_kcontrol_new left_dac_to_hp_left_amp_switch_control = + SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 9, 1, 0); + +static struct snd_kcontrol_new left_dac_to_hp_right_amp_switch_control = + SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 8, 1, 0); + +static struct snd_kcontrol_new right_dac_to_hp_left_amp_switch_control = + SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 7, 1, 0); + +static struct snd_kcontrol_new right_dac_to_hp_right_amp_switch_control = + SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 6, 1, 0); + +static struct snd_kcontrol_new left_dac_to_speaker_lineout_switch_control = + SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 11, 1, 0); + +static struct snd_kcontrol_new right_dac_to_speaker_lineout_switch_control = + SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 10, 1, 0); + +/* After enable adc, Delay 200ms to avoid pop noise */ +static int adc_enable_delay_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(200); + break; + default: + break; + } + + return 0; +} + +static void enable_and_reset_codec(struct regmap *regmap, + u32 codec_enable_bits, u32 codec_reset_bits) +{ + regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1, + codec_enable_bits | codec_reset_bits, + codec_enable_bits | ~codec_reset_bits); + msleep(20); + regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1, + codec_reset_bits, codec_reset_bits); +} + +static int atlas6_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ +#define ATLAS6_CODEC_ENABLE_BITS (1 << 29) +#define ATLAS6_CODEC_RESET_BITS (1 << 28) + struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + enable_and_reset_codec(sirf_audio_codec->regmap, + ATLAS6_CODEC_ENABLE_BITS, ATLAS6_CODEC_RESET_BITS); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(sirf_audio_codec->regmap, + AUDIO_IC_CODEC_CTRL1, ATLAS6_CODEC_ENABLE_BITS, + ~ATLAS6_CODEC_ENABLE_BITS); + break; + default: + break; + } + + return 0; +} + +static int prima2_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ +#define PRIMA2_CODEC_ENABLE_BITS (1 << 27) +#define PRIMA2_CODEC_RESET_BITS (1 << 26) + struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev); + switch (event) { + case SND_SOC_DAPM_POST_PMU: + enable_and_reset_codec(sirf_audio_codec->regmap, + PRIMA2_CODEC_ENABLE_BITS, PRIMA2_CODEC_RESET_BITS); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(sirf_audio_codec->regmap, + AUDIO_IC_CODEC_CTRL1, PRIMA2_CODEC_ENABLE_BITS, + ~PRIMA2_CODEC_ENABLE_BITS); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget atlas6_output_driver_dapm_widgets[] = { + SND_SOC_DAPM_OUT_DRV("HP Left Driver", AUDIO_IC_CODEC_CTRL1, + 25, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("HP Right Driver", AUDIO_IC_CODEC_CTRL1, + 26, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Speaker Driver", AUDIO_IC_CODEC_CTRL1, + 27, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_widget prima2_output_driver_dapm_widgets[] = { + SND_SOC_DAPM_OUT_DRV("HP Left Driver", AUDIO_IC_CODEC_CTRL1, + 23, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("HP Right Driver", AUDIO_IC_CODEC_CTRL1, + 24, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Speaker Driver", AUDIO_IC_CODEC_CTRL1, + 25, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_widget atlas6_codec_clock_dapm_widget = + SND_SOC_DAPM_SUPPLY("codecclk", SND_SOC_NOPM, 0, 0, + atlas6_codec_enable_and_reset_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD); + +static const struct snd_soc_dapm_widget prima2_codec_clock_dapm_widget = + SND_SOC_DAPM_SUPPLY("codecclk", SND_SOC_NOPM, 0, 0, + prima2_codec_enable_and_reset_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD); + +static const struct snd_soc_dapm_widget sirf_audio_codec_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC left", NULL, AUDIO_IC_CODEC_CTRL0, 1, 0), + SND_SOC_DAPM_DAC("DAC right", NULL, AUDIO_IC_CODEC_CTRL0, 0, 0), + SND_SOC_DAPM_SWITCH("Left dac to hp left amp", SND_SOC_NOPM, 0, 0, + &left_dac_to_hp_left_amp_switch_control), + SND_SOC_DAPM_SWITCH("Left dac to hp right amp", SND_SOC_NOPM, 0, 0, + &left_dac_to_hp_right_amp_switch_control), + SND_SOC_DAPM_SWITCH("Right dac to hp left amp", SND_SOC_NOPM, 0, 0, + &right_dac_to_hp_left_amp_switch_control), + SND_SOC_DAPM_SWITCH("Right dac to hp right amp", SND_SOC_NOPM, 0, 0, + &right_dac_to_hp_right_amp_switch_control), + SND_SOC_DAPM_OUT_DRV("HP amp left driver", AUDIO_IC_CODEC_CTRL0, 3, 0, + NULL, 0), + SND_SOC_DAPM_OUT_DRV("HP amp right driver", AUDIO_IC_CODEC_CTRL0, 3, 0, + NULL, 0), + + SND_SOC_DAPM_SWITCH("Left dac to speaker lineout", SND_SOC_NOPM, 0, 0, + &left_dac_to_speaker_lineout_switch_control), + SND_SOC_DAPM_SWITCH("Right dac to speaker lineout", SND_SOC_NOPM, 0, 0, + &right_dac_to_speaker_lineout_switch_control), + SND_SOC_DAPM_OUT_DRV("Speaker amp driver", AUDIO_IC_CODEC_CTRL0, 4, 0, + NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPOUTL"), + SND_SOC_DAPM_OUTPUT("HPOUTR"), + SND_SOC_DAPM_OUTPUT("SPKOUT"), + + SND_SOC_DAPM_ADC_E("ADC left", NULL, AUDIO_IC_CODEC_CTRL1, 8, 0, + adc_enable_delay_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_ADC_E("ADC right", NULL, AUDIO_IC_CODEC_CTRL1, 7, 0, + adc_enable_delay_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MIXER("Left PGA mixer", AUDIO_IC_CODEC_CTRL1, 1, 0, + &left_input_path_controls[0], + ARRAY_SIZE(left_input_path_controls)), + SND_SOC_DAPM_MIXER("Right PGA mixer", AUDIO_IC_CODEC_CTRL1, 0, 0, + &right_input_path_controls[0], + ARRAY_SIZE(right_input_path_controls)), + + SND_SOC_DAPM_MUX("Mic input mode mux", SND_SOC_NOPM, 0, 0, + &sirf_audio_codec_input_mode_control), + SND_SOC_DAPM_MICBIAS("Mic Bias", AUDIO_IC_CODEC_PWR, 3, 0), + SND_SOC_DAPM_INPUT("MICIN1"), + SND_SOC_DAPM_INPUT("MICIN2"), + SND_SOC_DAPM_INPUT("LINEIN1"), + SND_SOC_DAPM_INPUT("LINEIN2"), + + SND_SOC_DAPM_SUPPLY("HSL Phase Opposite", AUDIO_IC_CODEC_CTRL0, + 30, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route sirf_audio_codec_map[] = { + {"SPKOUT", NULL, "Speaker Driver"}, + {"Speaker Driver", NULL, "Speaker amp driver"}, + {"Speaker amp driver", NULL, "Left dac to speaker lineout"}, + {"Speaker amp driver", NULL, "Right dac to speaker lineout"}, + {"Left dac to speaker lineout", "Switch", "DAC left"}, + {"Right dac to speaker lineout", "Switch", "DAC right"}, + {"HPOUTL", NULL, "HP Left Driver"}, + {"HPOUTR", NULL, "HP Right Driver"}, + {"HP Left Driver", NULL, "HP amp left driver"}, + {"HP Right Driver", NULL, "HP amp right driver"}, + {"HP amp left driver", NULL, "Right dac to hp left amp"}, + {"HP amp right driver", NULL , "Right dac to hp right amp"}, + {"HP amp left driver", NULL, "Left dac to hp left amp"}, + {"HP amp right driver", NULL , "Right dac to hp right amp"}, + {"Right dac to hp left amp", "Switch", "DAC left"}, + {"Right dac to hp right amp", "Switch", "DAC right"}, + {"Left dac to hp left amp", "Switch", "DAC left"}, + {"Left dac to hp right amp", "Switch", "DAC right"}, + {"DAC left", NULL, "codecclk"}, + {"DAC right", NULL, "codecclk"}, + {"DAC left", NULL, "Playback"}, + {"DAC right", NULL, "Playback"}, + {"DAC left", NULL, "HSL Phase Opposite"}, + {"DAC right", NULL, "HSL Phase Opposite"}, + + {"Capture", NULL, "ADC left"}, + {"Capture", NULL, "ADC right"}, + {"ADC left", NULL, "codecclk"}, + {"ADC right", NULL, "codecclk"}, + {"ADC left", NULL, "Left PGA mixer"}, + {"ADC right", NULL, "Right PGA mixer"}, + {"Left PGA mixer", "Line Left Switch", "LINEIN2"}, + {"Right PGA mixer", "Line Right Switch", "LINEIN1"}, + {"Left PGA mixer", "Mic Left Switch", "MICIN2"}, + {"Right PGA mixer", "Mic Right Switch", "Mic input mode mux"}, + {"Mic input mode mux", "Single-ended", "MICIN1"}, + {"Mic input mode mux", "Differential", "MICIN1"}, +}; + +static int sirf_audio_codec_trigger(struct snd_pcm_substream *substream, + int cmd, + struct snd_soc_dai *dai) +{ + int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct snd_soc_codec *codec = dai->codec; + u32 val = 0; + + /* + * This is a workaround, When stop playback, + * need disable HP amp, avoid the current noise. + */ + switch (cmd) { + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (playback) + val = IC_HSLEN | IC_HSREN; + break; + default: + return -EINVAL; + } + + if (playback) + snd_soc_update_bits(codec, AUDIO_IC_CODEC_CTRL0, + IC_HSLEN | IC_HSREN, val); + return 0; +} + +struct snd_soc_dai_ops sirf_audio_codec_dai_ops = { + .trigger = sirf_audio_codec_trigger, +}; + +struct snd_soc_dai_driver sirf_audio_codec_dai = { + .name = "sirf-audio-codec", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &sirf_audio_codec_dai_ops, +}; + +static int sirf_audio_codec_probe(struct snd_soc_codec *codec) +{ + int ret; + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec); + + pm_runtime_enable(codec->dev); + codec->control_data = sirf_audio_codec->regmap; + + ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP); + if (ret != 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + if (of_device_is_compatible(codec->dev->of_node, "sirf,prima2-audio-codec")) { + snd_soc_dapm_new_controls(dapm, + prima2_output_driver_dapm_widgets, + ARRAY_SIZE(prima2_output_driver_dapm_widgets)); + snd_soc_dapm_new_controls(dapm, + &prima2_codec_clock_dapm_widget, 1); + return snd_soc_add_codec_controls(codec, + volume_controls_prima2, + ARRAY_SIZE(volume_controls_prima2)); + } + if (of_device_is_compatible(codec->dev->of_node, "sirf,atlas6-audio-codec")) { + snd_soc_dapm_new_controls(dapm, + atlas6_output_driver_dapm_widgets, + ARRAY_SIZE(atlas6_output_driver_dapm_widgets)); + snd_soc_dapm_new_controls(dapm, + &atlas6_codec_clock_dapm_widget, 1); + return snd_soc_add_codec_controls(codec, + volume_controls_atlas6, + ARRAY_SIZE(volume_controls_atlas6)); + } + + return -EINVAL; +} + +static int sirf_audio_codec_remove(struct snd_soc_codec *codec) +{ + pm_runtime_disable(codec->dev); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_device_sirf_audio_codec = { + .probe = sirf_audio_codec_probe, + .remove = sirf_audio_codec_remove, + .dapm_widgets = sirf_audio_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sirf_audio_codec_dapm_widgets), + .dapm_routes = sirf_audio_codec_map, + .num_dapm_routes = ARRAY_SIZE(sirf_audio_codec_map), + .idle_bias_off = true, +}; + +static const struct of_device_id sirf_audio_codec_of_match[] = { + { .compatible = "sirf,prima2-audio-codec" }, + { .compatible = "sirf,atlas6-audio-codec" }, + {} +}; +MODULE_DEVICE_TABLE(of, sirf_audio_codec_of_match); + +static const struct regmap_config sirf_audio_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = AUDIO_IC_CODEC_CTRL3, + .cache_type = REGCACHE_NONE, +}; + +static int sirf_audio_codec_driver_probe(struct platform_device *pdev) +{ + int ret; + struct sirf_audio_codec *sirf_audio_codec; + void __iomem *base; + struct resource *mem_res; + const struct of_device_id *match; + + match = of_match_node(sirf_audio_codec_of_match, pdev->dev.of_node); + + sirf_audio_codec = devm_kzalloc(&pdev->dev, + sizeof(struct sirf_audio_codec), GFP_KERNEL); + if (!sirf_audio_codec) + return -ENOMEM; + + platform_set_drvdata(pdev, sirf_audio_codec); + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, mem_res); + if (base == NULL) + return -ENOMEM; + + sirf_audio_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &sirf_audio_codec_regmap_config); + if (IS_ERR(sirf_audio_codec->regmap)) + return PTR_ERR(sirf_audio_codec->regmap); + + sirf_audio_codec->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(sirf_audio_codec->clk)) { + dev_err(&pdev->dev, "Get clock failed.\n"); + return PTR_ERR(sirf_audio_codec->clk); + } + + ret = clk_prepare_enable(sirf_audio_codec->clk); + if (ret) { + dev_err(&pdev->dev, "Enable clock failed.\n"); + return ret; + } + + ret = snd_soc_register_codec(&(pdev->dev), + &soc_codec_device_sirf_audio_codec, + &sirf_audio_codec_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Register Audio Codec dai failed.\n"); + goto err_clk_put; + } + + /* + * Always open charge pump, if not, when the charge pump closed the + * adc will not stable + */ + regmap_update_bits(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0, + IC_CPFREQ, IC_CPFREQ); + + if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas6-audio-codec")) + regmap_update_bits(sirf_audio_codec->regmap, + AUDIO_IC_CODEC_CTRL0, IC_CPEN, IC_CPEN); + return 0; + +err_clk_put: + clk_disable_unprepare(sirf_audio_codec->clk); + return ret; +} + +static int sirf_audio_codec_driver_remove(struct platform_device *pdev) +{ + struct sirf_audio_codec *sirf_audio_codec = platform_get_drvdata(pdev); + + clk_disable_unprepare(sirf_audio_codec->clk); + snd_soc_unregister_codec(&(pdev->dev)); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int sirf_audio_codec_suspend(struct device *dev) +{ + struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(dev); + + regmap_read(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0, + &sirf_audio_codec->reg_ctrl0); + regmap_read(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL1, + &sirf_audio_codec->reg_ctrl1); + clk_disable_unprepare(sirf_audio_codec->clk); + + return 0; +} + +static int sirf_audio_codec_resume(struct device *dev) +{ + struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(sirf_audio_codec->clk); + if (ret) + return ret; + + regmap_write(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0, + sirf_audio_codec->reg_ctrl0); + regmap_write(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL1, + sirf_audio_codec->reg_ctrl1); + + return 0; +} +#endif + +static const struct dev_pm_ops sirf_audio_codec_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sirf_audio_codec_suspend, sirf_audio_codec_resume) +}; + +static struct platform_driver sirf_audio_codec_driver = { + .driver = { + .name = "sirf-audio-codec", + .owner = THIS_MODULE, + .of_match_table = sirf_audio_codec_of_match, + .pm = &sirf_audio_codec_pm_ops, + }, + .probe = sirf_audio_codec_driver_probe, + .remove = sirf_audio_codec_driver_remove, +}; + +module_platform_driver(sirf_audio_codec_driver); + +MODULE_DESCRIPTION("SiRF audio codec driver"); +MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/sirf-audio-codec.h b/sound/soc/codecs/sirf-audio-codec.h new file mode 100644 index 000000000000..d4c187b8e54a --- /dev/null +++ b/sound/soc/codecs/sirf-audio-codec.h @@ -0,0 +1,75 @@ +/* + * SiRF inner codec controllers define + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef _SIRF_AUDIO_CODEC_H +#define _SIRF_AUDIO_CODEC_H + + +#define AUDIO_IC_CODEC_PWR (0x00E0) +#define AUDIO_IC_CODEC_CTRL0 (0x00E4) +#define AUDIO_IC_CODEC_CTRL1 (0x00E8) +#define AUDIO_IC_CODEC_CTRL2 (0x00EC) +#define AUDIO_IC_CODEC_CTRL3 (0x00F0) + +#define MICBIASEN (1 << 3) + +#define IC_RDACEN (1 << 0) +#define IC_LDACEN (1 << 1) +#define IC_HSREN (1 << 2) +#define IC_HSLEN (1 << 3) +#define IC_SPEN (1 << 4) +#define IC_CPEN (1 << 5) + +#define IC_HPRSELR (1 << 6) +#define IC_HPLSELR (1 << 7) +#define IC_HPRSELL (1 << 8) +#define IC_HPLSELL (1 << 9) +#define IC_SPSELR (1 << 10) +#define IC_SPSELL (1 << 11) + +#define IC_MONOR (1 << 12) +#define IC_MONOL (1 << 13) + +#define IC_RXOSRSEL (1 << 28) +#define IC_CPFREQ (1 << 29) +#define IC_HSINVEN (1 << 30) + +#define IC_MICINREN (1 << 0) +#define IC_MICINLEN (1 << 1) +#define IC_MICIN1SEL (1 << 2) +#define IC_MICIN2SEL (1 << 3) +#define IC_MICDIFSEL (1 << 4) +#define IC_LINEIN1SEL (1 << 5) +#define IC_LINEIN2SEL (1 << 6) +#define IC_RADCEN (1 << 7) +#define IC_LADCEN (1 << 8) +#define IC_ALM (1 << 9) + +#define IC_DIGMICEN (1 << 22) +#define IC_DIGMICFREQ (1 << 23) +#define IC_ADC14B_12 (1 << 24) +#define IC_FIRDAC_HSL_EN (1 << 25) +#define IC_FIRDAC_HSR_EN (1 << 26) +#define IC_FIRDAC_LOUT_EN (1 << 27) +#define IC_POR (1 << 28) +#define IC_CODEC_CLK_EN (1 << 29) +#define IC_HP_3DB_BOOST (1 << 30) + +#define IC_ADC_LEFT_GAIN_SHIFT 16 +#define IC_ADC_RIGHT_GAIN_SHIFT 10 +#define IC_ADC_GAIN_MASK 0x3F +#define IC_MIC_MAX_GAIN 0x39 + +#define IC_RXPGAR_MASK 0x3F +#define IC_RXPGAR_SHIFT 14 +#define IC_RXPGAL_MASK 0x3F +#define IC_RXPGAL_SHIFT 21 +#define IC_RXPGAR 0x7B +#define IC_RXPGAL 0x7B + +#endif /*__SIRF_AUDIO_CODEC_H*/ diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index 13045f2af4d3..bca7d02b362a 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -312,14 +312,14 @@ static int sn95031_dmic56_event(struct snd_soc_dapm_widget *w, /* mux controls */ static const char *sn95031_mic_texts[] = { "AMIC", "LineIn" }; -static const struct soc_enum sn95031_micl_enum = - SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 1, 2, sn95031_mic_texts); +static SOC_ENUM_SINGLE_DECL(sn95031_micl_enum, + SN95031_ADCCONFIG, 1, sn95031_mic_texts); static const struct snd_kcontrol_new sn95031_micl_mux_control = SOC_DAPM_ENUM("Route", sn95031_micl_enum); -static const struct soc_enum sn95031_micr_enum = - SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 3, 2, sn95031_mic_texts); +static SOC_ENUM_SINGLE_DECL(sn95031_micr_enum, + SN95031_ADCCONFIG, 3, sn95031_mic_texts); static const struct snd_kcontrol_new sn95031_micr_mux_control = SOC_DAPM_ENUM("Route", sn95031_micr_enum); @@ -328,26 +328,26 @@ static const char *sn95031_input_texts[] = { "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", "DMIC6", "ADC Left", "ADC Right" }; -static const struct soc_enum sn95031_input1_enum = - SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 0, 8, sn95031_input_texts); +static SOC_ENUM_SINGLE_DECL(sn95031_input1_enum, + SN95031_AUDIOMUX12, 0, sn95031_input_texts); static const struct snd_kcontrol_new sn95031_input1_mux_control = SOC_DAPM_ENUM("Route", sn95031_input1_enum); -static const struct soc_enum sn95031_input2_enum = - SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 4, 8, sn95031_input_texts); +static SOC_ENUM_SINGLE_DECL(sn95031_input2_enum, + SN95031_AUDIOMUX12, 4, sn95031_input_texts); static const struct snd_kcontrol_new sn95031_input2_mux_control = SOC_DAPM_ENUM("Route", sn95031_input2_enum); -static const struct soc_enum sn95031_input3_enum = - SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 0, 8, sn95031_input_texts); +static SOC_ENUM_SINGLE_DECL(sn95031_input3_enum, + SN95031_AUDIOMUX34, 0, sn95031_input_texts); static const struct snd_kcontrol_new sn95031_input3_mux_control = SOC_DAPM_ENUM("Route", sn95031_input3_enum); -static const struct soc_enum sn95031_input4_enum = - SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 4, 8, sn95031_input_texts); +static SOC_ENUM_SINGLE_DECL(sn95031_input4_enum, + SN95031_AUDIOMUX34, 4, sn95031_input_texts); static const struct snd_kcontrol_new sn95031_input4_mux_control = SOC_DAPM_ENUM("Route", sn95031_input4_enum); @@ -359,19 +359,19 @@ static const char *sn95031_micmode_text[] = {"Single Ended", "Differential"}; /* 0dB to 30dB in 10dB steps */ static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 10, 0); -static const struct soc_enum sn95031_micmode1_enum = - SOC_ENUM_SINGLE(SN95031_MICAMP1, 1, 2, sn95031_micmode_text); -static const struct soc_enum sn95031_micmode2_enum = - SOC_ENUM_SINGLE(SN95031_MICAMP2, 1, 2, sn95031_micmode_text); +static SOC_ENUM_SINGLE_DECL(sn95031_micmode1_enum, + SN95031_MICAMP1, 1, sn95031_micmode_text); +static SOC_ENUM_SINGLE_DECL(sn95031_micmode2_enum, + SN95031_MICAMP2, 1, sn95031_micmode_text); static const char *sn95031_dmic_cfg_text[] = {"GPO", "DMIC"}; -static const struct soc_enum sn95031_dmic12_cfg_enum = - SOC_ENUM_SINGLE(SN95031_DMICMUX, 0, 2, sn95031_dmic_cfg_text); -static const struct soc_enum sn95031_dmic34_cfg_enum = - SOC_ENUM_SINGLE(SN95031_DMICMUX, 1, 2, sn95031_dmic_cfg_text); -static const struct soc_enum sn95031_dmic56_cfg_enum = - SOC_ENUM_SINGLE(SN95031_DMICMUX, 2, 2, sn95031_dmic_cfg_text); +static SOC_ENUM_SINGLE_DECL(sn95031_dmic12_cfg_enum, + SN95031_DMICMUX, 0, sn95031_dmic_cfg_text); +static SOC_ENUM_SINGLE_DECL(sn95031_dmic34_cfg_enum, + SN95031_DMICMUX, 1, sn95031_dmic_cfg_text); +static SOC_ENUM_SINGLE_DECL(sn95031_dmic56_cfg_enum, + SN95031_DMICMUX, 2, sn95031_dmic_cfg_text); static const struct snd_kcontrol_new sn95031_snd_controls[] = { SOC_ENUM("Mic1Mode Capture Route", sn95031_micmode1_enum), diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index 95aed552139a..806f3d826ffb 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -169,19 +169,19 @@ static const char * const ssm2518_drc_hold_time_text[] = { "682.24 ms", "1364 ms", }; -static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_attack_time_enum, +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_attack_time_enum, SSM2518_REG_DRC_2, 4, ssm2518_drc_peak_detector_attack_time_text); -static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_release_time_enum, +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_release_time_enum, SSM2518_REG_DRC_2, 0, ssm2518_drc_peak_detector_release_time_text); -static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_attack_time_enum, +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_attack_time_enum, SSM2518_REG_DRC_6, 4, ssm2518_drc_peak_detector_attack_time_text); -static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_decay_time_enum, +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_decay_time_enum, SSM2518_REG_DRC_6, 0, ssm2518_drc_peak_detector_release_time_text); -static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_hold_time_enum, +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_hold_time_enum, SSM2518_REG_DRC_7, 4, ssm2518_drc_hold_time_text); -static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_noise_gate_hold_time_enum, +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_noise_gate_hold_time_enum, SSM2518_REG_DRC_7, 0, ssm2518_drc_hold_time_text); -static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_rms_averaging_time_enum, +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_rms_averaging_time_enum, SSM2518_REG_DRC_9, 0, ssm2518_drc_peak_detector_release_time_text); static const struct snd_kcontrol_new ssm2518_snd_controls[] = { @@ -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-i2c.c b/sound/soc/codecs/ssm2602-i2c.c new file mode 100644 index 000000000000..abd63d537173 --- /dev/null +++ b/sound/soc/codecs/ssm2602-i2c.c @@ -0,0 +1,57 @@ +/* + * SSM2602/SSM2603/SSM2604 I2C audio driver + * + * Copyright 2014 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> + +#include <sound/soc.h> + +#include "ssm2602.h" + +/* + * ssm2602 2 wire address is determined by GPIO5 + * state during powerup. + * low = 0x1a + * high = 0x1b + */ +static int ssm2602_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return ssm2602_probe(&client->dev, id->driver_data, + devm_regmap_init_i2c(client, &ssm2602_regmap_config)); +} + +static int ssm2602_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id ssm2602_i2c_id[] = { + { "ssm2602", SSM2602 }, + { "ssm2603", SSM2602 }, + { "ssm2604", SSM2604 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); + +static struct i2c_driver ssm2602_i2c_driver = { + .driver = { + .name = "ssm2602", + .owner = THIS_MODULE, + }, + .probe = ssm2602_i2c_probe, + .remove = ssm2602_i2c_remove, + .id_table = ssm2602_i2c_id, +}; +module_i2c_driver(ssm2602_i2c_driver); + +MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 I2C driver"); +MODULE_AUTHOR("Cliff Cai"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ssm2602-spi.c b/sound/soc/codecs/ssm2602-spi.c new file mode 100644 index 000000000000..2bf55e24a7bb --- /dev/null +++ b/sound/soc/codecs/ssm2602-spi.c @@ -0,0 +1,41 @@ +/* + * SSM2602 SPI audio driver + * + * Copyright 2014 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/regmap.h> + +#include <sound/soc.h> + +#include "ssm2602.h" + +static int ssm2602_spi_probe(struct spi_device *spi) +{ + return ssm2602_probe(&spi->dev, SSM2602, + devm_regmap_init_spi(spi, &ssm2602_regmap_config)); +} + +static int ssm2602_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static struct spi_driver ssm2602_spi_driver = { + .driver = { + .name = "ssm2602", + .owner = THIS_MODULE, + }, + .probe = ssm2602_spi_probe, + .remove = ssm2602_spi_remove, +}; +module_spi_driver(ssm2602_spi_driver); + +MODULE_DESCRIPTION("ASoC SSM2602 SPI driver"); +MODULE_AUTHOR("Cliff Cai"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 492644e67ace..12947096897c 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -27,34 +27,20 @@ */ #include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/init.h> -#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> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/initval.h> #include <sound/tlv.h> #include "ssm2602.h" -enum ssm2602_type { - SSM2602, - SSM2604, -}; - /* codec private data */ 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; + const struct snd_pcm_hw_constraint_list *sysclk_constraints; struct regmap *regmap; @@ -77,15 +63,16 @@ static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = { /*Appending several "None"s just for OSS mixer use*/ static const char *ssm2602_input_select[] = { - "Line", "Mic", "None", "None", "None", - "None", "None", "None", + "Line", "Mic", }; static const char *ssm2602_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; static const struct soc_enum ssm2602_enum[] = { - SOC_ENUM_SINGLE(SSM2602_APANA, 2, 2, ssm2602_input_select), - SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph), + SOC_ENUM_SINGLE(SSM2602_APANA, 2, ARRAY_SIZE(ssm2602_input_select), + ssm2602_input_select), + SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, ARRAY_SIZE(ssm2602_deemph), + ssm2602_deemph), }; static const unsigned int ssm260x_outmix_tlv[] = { @@ -196,10 +183,10 @@ static const struct snd_soc_dapm_route ssm2604_routes[] = { }; static const unsigned int ssm2602_rates_12288000[] = { - 8000, 32000, 48000, 96000, + 8000, 16000, 32000, 48000, 96000, }; -static struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = { +static const struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = { .list = ssm2602_rates_12288000, .count = ARRAY_SIZE(ssm2602_rates_12288000), }; @@ -208,7 +195,7 @@ static const unsigned int ssm2602_rates_11289600[] = { 8000, 44100, 88200, }; -static struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = { +static const struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = { .list = ssm2602_rates_11289600, .count = ARRAY_SIZE(ssm2602_rates_11289600), }; @@ -233,6 +220,11 @@ static const struct ssm2602_coeff ssm2602_coeff_table[] = { {18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)}, {12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)}, + /* 16k */ + {12288000, 16000, SSM2602_COEFF_SRATE(0x5, 0x0, 0x0)}, + {18432000, 16000, SSM2602_COEFF_SRATE(0x5, 0x1, 0x0)}, + {12000000, 16000, SSM2602_COEFF_SRATE(0xa, 0x0, 0x1)}, + /* 8k */ {12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)}, {18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)}, @@ -277,11 +269,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 +301,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 +311,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); @@ -520,9 +467,10 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec, return 0; } -#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_32000 |\ - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) +#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000) #define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) @@ -530,7 +478,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 +498,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) @@ -569,7 +518,7 @@ static int ssm2602_resume(struct snd_soc_codec *codec) return 0; } -static int ssm2602_probe(struct snd_soc_codec *codec) +static int ssm2602_codec_probe(struct snd_soc_codec *codec) { struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); struct snd_soc_dapm_context *dapm = &codec->dapm; @@ -594,7 +543,7 @@ static int ssm2602_probe(struct snd_soc_codec *codec) ARRAY_SIZE(ssm2602_routes)); } -static int ssm2604_probe(struct snd_soc_codec *codec) +static int ssm2604_codec_probe(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; @@ -608,7 +557,7 @@ static int ssm2604_probe(struct snd_soc_codec *codec) ARRAY_SIZE(ssm2604_routes)); } -static int ssm260x_probe(struct snd_soc_codec *codec) +static int ssm260x_codec_probe(struct snd_soc_codec *codec) { struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); int ret; @@ -637,10 +586,10 @@ static int ssm260x_probe(struct snd_soc_codec *codec) switch (ssm2602->type) { case SSM2602: - ret = ssm2602_probe(codec); + ret = ssm2602_codec_probe(codec); break; case SSM2604: - ret = ssm2604_probe(codec); + ret = ssm2604_codec_probe(codec); break; } @@ -660,7 +609,7 @@ static int ssm2602_remove(struct snd_soc_codec *codec) } static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { - .probe = ssm260x_probe, + .probe = ssm260x_codec_probe, .remove = ssm2602_remove, .suspend = ssm2602_suspend, .resume = ssm2602_resume, @@ -679,7 +628,7 @@ static bool ssm2602_register_volatile(struct device *dev, unsigned int reg) return reg == SSM2602_RESET; } -static const struct regmap_config ssm2602_regmap_config = { +const struct regmap_config ssm2602_regmap_config = { .val_bits = 9, .reg_bits = 7, @@ -690,134 +639,28 @@ static const struct regmap_config ssm2602_regmap_config = { .reg_defaults_raw = ssm2602_reg, .num_reg_defaults_raw = ARRAY_SIZE(ssm2602_reg), }; +EXPORT_SYMBOL_GPL(ssm2602_regmap_config); -#if defined(CONFIG_SPI_MASTER) -static int ssm2602_spi_probe(struct spi_device *spi) +int ssm2602_probe(struct device *dev, enum ssm2602_type type, + struct regmap *regmap) { struct ssm2602_priv *ssm2602; - int ret; - - ssm2602 = devm_kzalloc(&spi->dev, sizeof(struct ssm2602_priv), - GFP_KERNEL); - if (ssm2602 == NULL) - return -ENOMEM; - - spi_set_drvdata(spi, ssm2602); - ssm2602->type = SSM2602; - - ssm2602->regmap = devm_regmap_init_spi(spi, &ssm2602_regmap_config); - if (IS_ERR(ssm2602->regmap)) - return PTR_ERR(ssm2602->regmap); - - ret = snd_soc_register_codec(&spi->dev, - &soc_codec_dev_ssm2602, &ssm2602_dai, 1); - return ret; -} - -static int ssm2602_spi_remove(struct spi_device *spi) -{ - snd_soc_unregister_codec(&spi->dev); - return 0; -} - -static struct spi_driver ssm2602_spi_driver = { - .driver = { - .name = "ssm2602", - .owner = THIS_MODULE, - }, - .probe = ssm2602_spi_probe, - .remove = ssm2602_spi_remove, -}; -#endif -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -/* - * ssm2602 2 wire address is determined by GPIO5 - * state during powerup. - * low = 0x1a - * high = 0x1b - */ -static int ssm2602_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct ssm2602_priv *ssm2602; - int ret; + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - ssm2602 = devm_kzalloc(&i2c->dev, sizeof(struct ssm2602_priv), - GFP_KERNEL); + ssm2602 = devm_kzalloc(dev, sizeof(*ssm2602), GFP_KERNEL); if (ssm2602 == NULL) return -ENOMEM; - i2c_set_clientdata(i2c, ssm2602); - ssm2602->type = id->driver_data; - - ssm2602->regmap = devm_regmap_init_i2c(i2c, &ssm2602_regmap_config); - if (IS_ERR(ssm2602->regmap)) - return PTR_ERR(ssm2602->regmap); - - ret = snd_soc_register_codec(&i2c->dev, - &soc_codec_dev_ssm2602, &ssm2602_dai, 1); - return ret; -} - -static int ssm2602_i2c_remove(struct i2c_client *client) -{ - snd_soc_unregister_codec(&client->dev); - return 0; -} - -static const struct i2c_device_id ssm2602_i2c_id[] = { - { "ssm2602", SSM2602 }, - { "ssm2603", SSM2602 }, - { "ssm2604", SSM2604 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); - -/* corgi i2c codec control layer */ -static struct i2c_driver ssm2602_i2c_driver = { - .driver = { - .name = "ssm2602", - .owner = THIS_MODULE, - }, - .probe = ssm2602_i2c_probe, - .remove = ssm2602_i2c_remove, - .id_table = ssm2602_i2c_id, -}; -#endif - - -static int __init ssm2602_modinit(void) -{ - int ret = 0; - -#if defined(CONFIG_SPI_MASTER) - ret = spi_register_driver(&ssm2602_spi_driver); - if (ret) - return ret; -#endif - -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - ret = i2c_add_driver(&ssm2602_i2c_driver); - if (ret) - return ret; -#endif - - return ret; -} -module_init(ssm2602_modinit); - -static void __exit ssm2602_exit(void) -{ -#if defined(CONFIG_SPI_MASTER) - spi_unregister_driver(&ssm2602_spi_driver); -#endif + dev_set_drvdata(dev, ssm2602); + ssm2602->type = SSM2602; + ssm2602->regmap = regmap; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_del_driver(&ssm2602_i2c_driver); -#endif + return snd_soc_register_codec(dev, &soc_codec_dev_ssm2602, + &ssm2602_dai, 1); } -module_exit(ssm2602_exit); +EXPORT_SYMBOL_GPL(ssm2602_probe); MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver"); MODULE_AUTHOR("Cliff Cai"); diff --git a/sound/soc/codecs/ssm2602.h b/sound/soc/codecs/ssm2602.h index fbd07d7b73ca..747538847689 100644 --- a/sound/soc/codecs/ssm2602.h +++ b/sound/soc/codecs/ssm2602.h @@ -28,6 +28,20 @@ #ifndef _SSM2602_H #define _SSM2602_H +#include <linux/regmap.h> + +struct device; + +enum ssm2602_type { + SSM2602, + SSM2604, +}; + +extern const struct regmap_config ssm2602_regmap_config; + +int ssm2602_probe(struct device *dev, enum ssm2602_type type, + struct regmap *regmap); + /* SSM2602 Codec Register definitions */ #define SSM2602_LINVOL 0x00 diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 06edb396e733..2735361a4c3c 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -187,42 +187,42 @@ static const unsigned int sta32x_limiter_drc_release_tlv[] = { 13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0), }; -static const struct soc_enum sta32x_drc_ac_enum = - SOC_ENUM_SINGLE(STA32X_CONFD, STA32X_CONFD_DRC_SHIFT, - 2, sta32x_drc_ac); -static const struct soc_enum sta32x_auto_eq_enum = - SOC_ENUM_SINGLE(STA32X_AUTO1, STA32X_AUTO1_AMEQ_SHIFT, - 3, sta32x_auto_eq_mode); -static const struct soc_enum sta32x_auto_gc_enum = - SOC_ENUM_SINGLE(STA32X_AUTO1, STA32X_AUTO1_AMGC_SHIFT, - 4, sta32x_auto_gc_mode); -static const struct soc_enum sta32x_auto_xo_enum = - SOC_ENUM_SINGLE(STA32X_AUTO2, STA32X_AUTO2_XO_SHIFT, - 16, sta32x_auto_xo_mode); -static const struct soc_enum sta32x_preset_eq_enum = - SOC_ENUM_SINGLE(STA32X_AUTO3, STA32X_AUTO3_PEQ_SHIFT, - 32, sta32x_preset_eq_mode); -static const struct soc_enum sta32x_limiter_ch1_enum = - SOC_ENUM_SINGLE(STA32X_C1CFG, STA32X_CxCFG_LS_SHIFT, - 3, sta32x_limiter_select); -static const struct soc_enum sta32x_limiter_ch2_enum = - SOC_ENUM_SINGLE(STA32X_C2CFG, STA32X_CxCFG_LS_SHIFT, - 3, sta32x_limiter_select); -static const struct soc_enum sta32x_limiter_ch3_enum = - SOC_ENUM_SINGLE(STA32X_C3CFG, STA32X_CxCFG_LS_SHIFT, - 3, sta32x_limiter_select); -static const struct soc_enum sta32x_limiter1_attack_rate_enum = - SOC_ENUM_SINGLE(STA32X_L1AR, STA32X_LxA_SHIFT, - 16, sta32x_limiter_attack_rate); -static const struct soc_enum sta32x_limiter2_attack_rate_enum = - SOC_ENUM_SINGLE(STA32X_L2AR, STA32X_LxA_SHIFT, - 16, sta32x_limiter_attack_rate); -static const struct soc_enum sta32x_limiter1_release_rate_enum = - SOC_ENUM_SINGLE(STA32X_L1AR, STA32X_LxR_SHIFT, - 16, sta32x_limiter_release_rate); -static const struct soc_enum sta32x_limiter2_release_rate_enum = - SOC_ENUM_SINGLE(STA32X_L2AR, STA32X_LxR_SHIFT, - 16, sta32x_limiter_release_rate); +static SOC_ENUM_SINGLE_DECL(sta32x_drc_ac_enum, + STA32X_CONFD, STA32X_CONFD_DRC_SHIFT, + sta32x_drc_ac); +static SOC_ENUM_SINGLE_DECL(sta32x_auto_eq_enum, + STA32X_AUTO1, STA32X_AUTO1_AMEQ_SHIFT, + sta32x_auto_eq_mode); +static SOC_ENUM_SINGLE_DECL(sta32x_auto_gc_enum, + STA32X_AUTO1, STA32X_AUTO1_AMGC_SHIFT, + sta32x_auto_gc_mode); +static SOC_ENUM_SINGLE_DECL(sta32x_auto_xo_enum, + STA32X_AUTO2, STA32X_AUTO2_XO_SHIFT, + sta32x_auto_xo_mode); +static SOC_ENUM_SINGLE_DECL(sta32x_preset_eq_enum, + STA32X_AUTO3, STA32X_AUTO3_PEQ_SHIFT, + sta32x_preset_eq_mode); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter_ch1_enum, + STA32X_C1CFG, STA32X_CxCFG_LS_SHIFT, + sta32x_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter_ch2_enum, + STA32X_C2CFG, STA32X_CxCFG_LS_SHIFT, + sta32x_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter_ch3_enum, + STA32X_C3CFG, STA32X_CxCFG_LS_SHIFT, + sta32x_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter1_attack_rate_enum, + STA32X_L1AR, STA32X_LxA_SHIFT, + sta32x_limiter_attack_rate); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter2_attack_rate_enum, + STA32X_L2AR, STA32X_LxA_SHIFT, + sta32x_limiter_attack_rate); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter1_release_rate_enum, + STA32X_L1AR, STA32X_LxR_SHIFT, + sta32x_limiter_release_rate); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter2_release_rate_enum, + STA32X_L2AR, STA32X_LxR_SHIFT, + sta32x_limiter_release_rate); /* byte array controls for setting biquad, mixer, scaling coefficients; * for biquads all five coefficients need to be set in one go, @@ -331,7 +331,7 @@ static int sta32x_sync_coef_shadow(struct snd_soc_codec *codec) static int sta32x_cache_sync(struct snd_soc_codec *codec) { - struct sta32x_priv *sta32x = codec->control_data; + struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); unsigned int mute; int rc; @@ -434,7 +434,7 @@ SOC_SINGLE_TLV("Treble Tone Control", STA32X_TONE, STA32X_TONE_TTC_SHIFT, 15, 0, SOC_ENUM("Limiter1 Attack Rate (dB/ms)", sta32x_limiter1_attack_rate_enum), SOC_ENUM("Limiter2 Attack Rate (dB/ms)", sta32x_limiter2_attack_rate_enum), SOC_ENUM("Limiter1 Release Rate (dB/ms)", sta32x_limiter1_release_rate_enum), -SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta32x_limiter1_release_rate_enum), +SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta32x_limiter2_release_rate_enum), /* depending on mode, the attack/release thresholds have * two different enum definitions; provide both diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index 40c07be9b581..f15b0e37274c 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -141,7 +141,7 @@ static const char *pwm_mode_text[] = { "Binary", "Headphone", "Ternary", static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -9150, 50, 0); static const DECLARE_TLV_DB_SCALE(master_vol_tlv, -12750, 50, 0); -static const SOC_ENUM_SINGLE_DECL(pwm_src, STA529_FFXCFG1, 4, pwm_mode_text); +static SOC_ENUM_SINGLE_DECL(pwm_src, STA529_FFXCFG1, 4, pwm_mode_text); static const struct snd_kcontrol_new sta529_snd_controls[] = { SOC_DOUBLE_R_TLV("Digital Playback Volume", STA529_LVOL, STA529_RVOL, 0, diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index a5455c1aea42..53b810d23fea 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -62,25 +62,25 @@ static const char *stac9766_boost1[] = {"0dB", "10dB"}; static const char *stac9766_boost2[] = {"0dB", "20dB"}; static const char *stac9766_stereo_mic[] = {"Off", "On"}; -static const struct soc_enum stac9766_record_enum = - SOC_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, stac9766_record_mux); -static const struct soc_enum stac9766_mono_enum = - SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 9, 2, stac9766_mono_mux); -static const struct soc_enum stac9766_mic_enum = - SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, stac9766_mic_mux); -static const struct soc_enum stac9766_SPDIF_enum = - SOC_ENUM_SINGLE(AC97_STAC_DA_CONTROL, 1, 2, stac9766_SPDIF_mux); -static const struct soc_enum stac9766_popbypass_enum = - SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, stac9766_popbypass_mux); -static const struct soc_enum stac9766_record_all_enum = - SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 12, 2, - stac9766_record_all_mux); -static const struct soc_enum stac9766_boost1_enum = - SOC_ENUM_SINGLE(AC97_MIC, 6, 2, stac9766_boost1); /* 0/10dB */ -static const struct soc_enum stac9766_boost2_enum = - SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 2, 2, stac9766_boost2); /* 0/20dB */ -static const struct soc_enum stac9766_stereo_mic_enum = - SOC_ENUM_SINGLE(AC97_STAC_STEREO_MIC, 2, 1, stac9766_stereo_mic); +static SOC_ENUM_DOUBLE_DECL(stac9766_record_enum, + AC97_REC_SEL, 8, 0, stac9766_record_mux); +static SOC_ENUM_SINGLE_DECL(stac9766_mono_enum, + AC97_GENERAL_PURPOSE, 9, stac9766_mono_mux); +static SOC_ENUM_SINGLE_DECL(stac9766_mic_enum, + AC97_GENERAL_PURPOSE, 8, stac9766_mic_mux); +static SOC_ENUM_SINGLE_DECL(stac9766_SPDIF_enum, + AC97_STAC_DA_CONTROL, 1, stac9766_SPDIF_mux); +static SOC_ENUM_SINGLE_DECL(stac9766_popbypass_enum, + AC97_GENERAL_PURPOSE, 15, stac9766_popbypass_mux); +static SOC_ENUM_SINGLE_DECL(stac9766_record_all_enum, + AC97_STAC_ANALOG_SPECIAL, 12, + stac9766_record_all_mux); +static SOC_ENUM_SINGLE_DECL(stac9766_boost1_enum, + AC97_MIC, 6, stac9766_boost1); /* 0/10dB */ +static SOC_ENUM_SINGLE_DECL(stac9766_boost2_enum, + AC97_STAC_ANALOG_SPECIAL, 2, stac9766_boost2); /* 0/20dB */ +static SOC_ENUM_SINGLE_DECL(stac9766_stereo_mic_enum, + AC97_STAC_STEREO_MIC, 2, stac9766_stereo_mic); static const DECLARE_TLV_DB_LINEAR(master_tlv, -4600, 0); static const DECLARE_TLV_DB_LINEAR(record_tlv, 0, 2250); diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c new file mode 100644 index 000000000000..20fc46092c2c --- /dev/null +++ b/sound/soc/codecs/tlv320aic23-i2c.c @@ -0,0 +1,59 @@ +/* + * ALSA SoC TLV320AIC23 codec driver I2C interface + * + * Author: Arun KS, <arunks@mistralsolutions.com> + * Copyright: (C) 2008 Mistral Solutions Pvt Ltd., + * + * Based on sound/soc/codecs/wm8731.c by Richard Purdie + * + * 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. + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/soc.h> + +#include "tlv320aic23.h" + +static int tlv320aic23_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *i2c_id) +{ + struct regmap *regmap; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EINVAL; + + regmap = devm_regmap_init_i2c(i2c, &tlv320aic23_regmap); + return tlv320aic23_probe(&i2c->dev, regmap); +} + +static int __exit tlv320aic23_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + return 0; +} + +static const struct i2c_device_id tlv320aic23_id[] = { + {"tlv320aic23", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tlv320aic23_id); + +static struct i2c_driver tlv320aic23_i2c_driver = { + .driver = { + .name = "tlv320aic23-codec", + }, + .probe = tlv320aic23_i2c_probe, + .remove = __exit_p(tlv320aic23_i2c_remove), + .id_table = tlv320aic23_id, +}; + +module_i2c_driver(tlv320aic23_i2c_driver); + +MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver I2C"); +MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic23-spi.c b/sound/soc/codecs/tlv320aic23-spi.c new file mode 100644 index 000000000000..3b387e41d75d --- /dev/null +++ b/sound/soc/codecs/tlv320aic23-spi.c @@ -0,0 +1,56 @@ +/* + * ALSA SoC TLV320AIC23 codec driver SPI interface + * + * Author: Arun KS, <arunks@mistralsolutions.com> + * Copyright: (C) 2008 Mistral Solutions Pvt Ltd., + * + * Based on sound/soc/codecs/wm8731.c by Richard Purdie + * + * 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. + */ + +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <sound/soc.h> + +#include "tlv320aic23.h" + +static int aic23_spi_probe(struct spi_device *spi) +{ + int ret; + struct regmap *regmap; + + dev_dbg(&spi->dev, "probing tlv320aic23 spi device\n"); + + spi->mode = SPI_MODE_0; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + regmap = devm_regmap_init_spi(spi, &tlv320aic23_regmap); + return tlv320aic23_probe(&spi->dev, regmap); +} + +static int aic23_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static struct spi_driver aic23_spi = { + .driver = { + .name = "tlv320aic23", + .owner = THIS_MODULE, + }, + .probe = aic23_spi_probe, + .remove = aic23_spi_remove, +}; + +module_spi_driver(aic23_spi); + +MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver SPI"); +MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index 5d430cc56f51..dc9a52fcb39a 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -23,7 +23,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/i2c.h> #include <linux/regmap.h> #include <linux/slab.h> #include <sound/core.h> @@ -51,7 +50,7 @@ static const struct reg_default tlv320aic23_reg[] = { { 9, 0x0000 }, }; -static const struct regmap_config tlv320aic23_regmap = { +const struct regmap_config tlv320aic23_regmap = { .reg_bits = 7, .val_bits = 9, @@ -60,20 +59,21 @@ static const struct regmap_config tlv320aic23_regmap = { .num_reg_defaults = ARRAY_SIZE(tlv320aic23_reg), .cache_type = REGCACHE_RBTREE, }; +EXPORT_SYMBOL(tlv320aic23_regmap); static const char *rec_src_text[] = { "Line", "Mic" }; static const char *deemph_text[] = {"None", "32Khz", "44.1Khz", "48Khz"}; -static const struct soc_enum rec_src_enum = - SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text); +static SOC_ENUM_SINGLE_DECL(rec_src_enum, + TLV320AIC23_ANLG, 2, rec_src_text); static const struct snd_kcontrol_new tlv320aic23_rec_src_mux_controls = SOC_DAPM_ENUM("Input Select", rec_src_enum); -static const struct soc_enum tlv320aic23_rec_src = - SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text); -static const struct soc_enum tlv320aic23_deemph = - SOC_ENUM_SINGLE(TLV320AIC23_DIGT, 1, 4, deemph_text); +static SOC_ENUM_SINGLE_DECL(tlv320aic23_rec_src, + TLV320AIC23_ANLG, 2, rec_src_text); +static SOC_ENUM_SINGLE_DECL(tlv320aic23_deemph, + TLV320AIC23_DIGT, 1, deemph_text); static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -12100, 100, 0); static const DECLARE_TLV_DB_SCALE(input_gain_tlv, -1725, 75, 0); @@ -400,7 +400,7 @@ static void tlv320aic23_shutdown(struct snd_pcm_substream *substream, struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec); /* deactivate */ - if (!codec->active) { + if (!snd_soc_codec_is_active(codec)) { udelay(50); snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0); } @@ -557,7 +557,7 @@ static int tlv320aic23_resume(struct snd_soc_codec *codec) return 0; } -static int tlv320aic23_probe(struct snd_soc_codec *codec) +static int tlv320aic23_codec_probe(struct snd_soc_codec *codec) { int ret; @@ -604,7 +604,7 @@ static int tlv320aic23_remove(struct snd_soc_codec *codec) } static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = { - .probe = tlv320aic23_probe, + .probe = tlv320aic23_codec_probe, .remove = tlv320aic23_remove, .suspend = tlv320aic23_suspend, .resume = tlv320aic23_resume, @@ -617,56 +617,25 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = { .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon), }; -/* - * If the i2c layer weren't so broken, we could pass this kind of data - * around - */ -static int tlv320aic23_codec_probe(struct i2c_client *i2c, - const struct i2c_device_id *i2c_id) +int tlv320aic23_probe(struct device *dev, struct regmap *regmap) { struct aic23 *aic23; - int ret; - if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EINVAL; + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - aic23 = devm_kzalloc(&i2c->dev, sizeof(struct aic23), GFP_KERNEL); + aic23 = devm_kzalloc(dev, sizeof(struct aic23), GFP_KERNEL); if (aic23 == NULL) return -ENOMEM; - aic23->regmap = devm_regmap_init_i2c(i2c, &tlv320aic23_regmap); - if (IS_ERR(aic23->regmap)) - return PTR_ERR(aic23->regmap); + aic23->regmap = regmap; - i2c_set_clientdata(i2c, aic23); + dev_set_drvdata(dev, aic23); - ret = snd_soc_register_codec(&i2c->dev, - &soc_codec_dev_tlv320aic23, &tlv320aic23_dai, 1); - return ret; -} -static int __exit tlv320aic23_i2c_remove(struct i2c_client *i2c) -{ - snd_soc_unregister_codec(&i2c->dev); - return 0; + return snd_soc_register_codec(dev, &soc_codec_dev_tlv320aic23, + &tlv320aic23_dai, 1); } - -static const struct i2c_device_id tlv320aic23_id[] = { - {"tlv320aic23", 0}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, tlv320aic23_id); - -static struct i2c_driver tlv320aic23_i2c_driver = { - .driver = { - .name = "tlv320aic23-codec", - }, - .probe = tlv320aic23_codec_probe, - .remove = __exit_p(tlv320aic23_i2c_remove), - .id_table = tlv320aic23_id, -}; - -module_i2c_driver(tlv320aic23_i2c_driver); +EXPORT_SYMBOL(tlv320aic23_probe); MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver"); MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>"); diff --git a/sound/soc/codecs/tlv320aic23.h b/sound/soc/codecs/tlv320aic23.h index e804120bd3da..3a7235a04a89 100644 --- a/sound/soc/codecs/tlv320aic23.h +++ b/sound/soc/codecs/tlv320aic23.h @@ -12,6 +12,12 @@ #ifndef _TLV320AIC23_H #define _TLV320AIC23_H +struct device; +struct regmap_config; + +extern const struct regmap_config tlv320aic23_regmap; +int tlv320aic23_probe(struct device *dev, struct regmap *regmap); + /* Codec TLV320AIC23 */ #define TLV320AIC23_LINVOL 0x00 #define TLV320AIC23_RINVOL 0x01 diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index 94a658fa6d97..ff5f23d482b7 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -238,8 +238,9 @@ static struct snd_soc_dai_driver aic26_dai = { * ALSA controls */ static const char *aic26_capture_src_text[] = {"Mic", "Aux"}; -static const struct soc_enum aic26_capture_src_enum = - SOC_ENUM_SINGLE(AIC26_REG_AUDIO_CTRL1, 12, 2, aic26_capture_src_text); +static SOC_ENUM_SINGLE_DECL(aic26_capture_src_enum, + AIC26_REG_AUDIO_CTRL1, 12, + aic26_capture_src_text); static const struct snd_kcontrol_new aic26_snd_controls[] = { /* Output */ diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 18cdcca9014c..c6bd7e75352d 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -29,9 +29,12 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/gpio.h> +#include <linux/of_gpio.h> #include <linux/i2c.h> #include <linux/cdev.h> #include <linux/slab.h> +#include <linux/clk.h> +#include <linux/regulator/consumer.h> #include <sound/tlv320aic32x4.h> #include <sound/core.h> @@ -66,20 +69,32 @@ struct aic32x4_priv { u32 micpga_routing; bool swapdacs; int rstn_gpio; + struct clk *mclk; + + struct regulator *supply_ldo; + struct regulator *supply_iov; + struct regulator *supply_dv; + struct regulator *supply_av; }; -/* 0dB min, 1dB steps */ -static DECLARE_TLV_DB_SCALE(tlv_step_1, 0, 100, 0); /* 0dB min, 0.5dB steps */ static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0); +/* -63.5dB min, 0.5dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_pcm, -6350, 50, 0); +/* -6dB min, 1dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_driver_gain, -600, 100, 0); +/* -12dB min, 0.5dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_adc_vol, -1200, 50, 0); static const struct snd_kcontrol_new aic32x4_snd_controls[] = { - SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL, - AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5), - SOC_DOUBLE_R_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, - AIC32X4_HPRGAIN, 0, 0x1D, 0, tlv_step_1), - SOC_DOUBLE_R_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, - AIC32X4_LORGAIN, 0, 0x1D, 0, tlv_step_1), + SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL, + AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), + SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, + AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, + tlv_driver_gain), + SOC_DOUBLE_R_S_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, + AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, + tlv_driver_gain), SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN, AIC32X4_HPRGAIN, 6, 0x01, 1), SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN, @@ -90,8 +105,8 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = { SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0), SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0), - SOC_DOUBLE_R_TLV("ADC Level Volume", AIC32X4_LADCVOL, - AIC32X4_RADCVOL, 0, 0x28, 0, tlv_step_0_5), + SOC_DOUBLE_R_S_TLV("ADC Level Volume", AIC32X4_LADCVOL, + AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL, AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), @@ -267,8 +282,8 @@ static const struct regmap_range_cfg aic32x4_regmap_pages[] = { .selector_mask = 0xff, .window_start = 0, .window_len = 128, - .range_min = AIC32X4_PAGE1, - .range_max = AIC32X4_PAGE1 + 127, + .range_min = 0, + .range_max = AIC32X4_RMICPGAVOL, }, }; @@ -450,6 +465,17 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, } snd_soc_write(codec, AIC32X4_IFACE1, data); + if (params_channels(params) == 1) { + data = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN; + } else { + if (aic32x4->swapdacs) + data = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2RCHN; + else + data = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN; + } + snd_soc_update_bits(codec, AIC32X4_DACSETUP, AIC32X4_DAC_CHAN_MASK, + data); + return 0; } @@ -469,8 +495,18 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute) static int aic32x4_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + int ret; + switch (level) { case SND_SOC_BIAS_ON: + /* Switch on master clock */ + ret = clk_prepare_enable(aic32x4->mclk); + if (ret) { + dev_err(codec->dev, "Failed to enable master clock\n"); + return ret; + } + /* Switch on PLL */ snd_soc_update_bits(codec, AIC32X4_PLLPR, AIC32X4_PLLEN, AIC32X4_PLLEN); @@ -498,29 +534,32 @@ static int aic32x4_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - /* Switch off PLL */ - snd_soc_update_bits(codec, AIC32X4_PLLPR, - AIC32X4_PLLEN, 0); + /* Switch off BCLK_N Divider */ + snd_soc_update_bits(codec, AIC32X4_BCLKN, + AIC32X4_BCLKEN, 0); - /* Switch off NDAC Divider */ - snd_soc_update_bits(codec, AIC32X4_NDAC, - AIC32X4_NDACEN, 0); + /* Switch off MADC Divider */ + snd_soc_update_bits(codec, AIC32X4_MADC, + AIC32X4_MADCEN, 0); + + /* Switch off NADC Divider */ + snd_soc_update_bits(codec, AIC32X4_NADC, + AIC32X4_NADCEN, 0); /* Switch off MDAC Divider */ snd_soc_update_bits(codec, AIC32X4_MDAC, AIC32X4_MDACEN, 0); - /* Switch off NADC Divider */ - snd_soc_update_bits(codec, AIC32X4_NADC, - AIC32X4_NADCEN, 0); + /* Switch off NDAC Divider */ + snd_soc_update_bits(codec, AIC32X4_NDAC, + AIC32X4_NDACEN, 0); - /* Switch off MADC Divider */ - snd_soc_update_bits(codec, AIC32X4_MADC, - AIC32X4_MADCEN, 0); + /* Switch off PLL */ + snd_soc_update_bits(codec, AIC32X4_PLLPR, + AIC32X4_PLLEN, 0); - /* Switch off BCLK_N Divider */ - snd_soc_update_bits(codec, AIC32X4_BCLKN, - AIC32X4_BCLKEN, 0); + /* Switch off master clock */ + clk_disable_unprepare(aic32x4->mclk); break; case SND_SOC_BIAS_OFF: break; @@ -577,7 +616,7 @@ static int aic32x4_probe(struct snd_soc_codec *codec) snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); - if (aic32x4->rstn_gpio >= 0) { + if (gpio_is_valid(aic32x4->rstn_gpio)) { ndelay(10); gpio_set_value(aic32x4->rstn_gpio, 1); } @@ -606,20 +645,15 @@ static int aic32x4_probe(struct snd_soc_codec *codec) } snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg); - /* Do DACs need to be swapped? */ - if (aic32x4->swapdacs) { - snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2RCHN | AIC32X4_RDAC2LCHN); - } else { - snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN); - } - /* Mic PGA routing */ - if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) { + if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_IN2R_10K); - } - if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) { + else + snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_CM1L_10K); + if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_IN1L_10K); - } + else + snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_CM1R_10K); aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -657,11 +691,122 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = { .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), }; +static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, + struct device_node *np) +{ + aic32x4->swapdacs = false; + aic32x4->micpga_routing = 0; + aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0); + + return 0; +} + +static void aic32x4_disable_regulators(struct aic32x4_priv *aic32x4) +{ + regulator_disable(aic32x4->supply_iov); + + if (!IS_ERR(aic32x4->supply_ldo)) + regulator_disable(aic32x4->supply_ldo); + + if (!IS_ERR(aic32x4->supply_dv)) + regulator_disable(aic32x4->supply_dv); + + if (!IS_ERR(aic32x4->supply_av)) + regulator_disable(aic32x4->supply_av); +} + +static int aic32x4_setup_regulators(struct device *dev, + struct aic32x4_priv *aic32x4) +{ + int ret = 0; + + aic32x4->supply_ldo = devm_regulator_get_optional(dev, "ldoin"); + aic32x4->supply_iov = devm_regulator_get(dev, "iov"); + aic32x4->supply_dv = devm_regulator_get_optional(dev, "dv"); + aic32x4->supply_av = devm_regulator_get_optional(dev, "av"); + + /* Check if the regulator requirements are fulfilled */ + + if (IS_ERR(aic32x4->supply_iov)) { + dev_err(dev, "Missing supply 'iov'\n"); + return PTR_ERR(aic32x4->supply_iov); + } + + if (IS_ERR(aic32x4->supply_ldo)) { + if (PTR_ERR(aic32x4->supply_ldo) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (IS_ERR(aic32x4->supply_dv)) { + dev_err(dev, "Missing supply 'dv' or 'ldoin'\n"); + return PTR_ERR(aic32x4->supply_dv); + } + if (IS_ERR(aic32x4->supply_av)) { + dev_err(dev, "Missing supply 'av' or 'ldoin'\n"); + return PTR_ERR(aic32x4->supply_av); + } + } else { + if (IS_ERR(aic32x4->supply_dv) && + PTR_ERR(aic32x4->supply_dv) == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (IS_ERR(aic32x4->supply_av) && + PTR_ERR(aic32x4->supply_av) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } + + ret = regulator_enable(aic32x4->supply_iov); + if (ret) { + dev_err(dev, "Failed to enable regulator iov\n"); + return ret; + } + + if (!IS_ERR(aic32x4->supply_ldo)) { + ret = regulator_enable(aic32x4->supply_ldo); + if (ret) { + dev_err(dev, "Failed to enable regulator ldo\n"); + goto error_ldo; + } + } + + if (!IS_ERR(aic32x4->supply_dv)) { + ret = regulator_enable(aic32x4->supply_dv); + if (ret) { + dev_err(dev, "Failed to enable regulator dv\n"); + goto error_dv; + } + } + + if (!IS_ERR(aic32x4->supply_av)) { + ret = regulator_enable(aic32x4->supply_av); + if (ret) { + dev_err(dev, "Failed to enable regulator av\n"); + goto error_av; + } + } + + if (!IS_ERR(aic32x4->supply_ldo) && IS_ERR(aic32x4->supply_av)) + aic32x4->power_cfg |= AIC32X4_PWR_AIC32X4_LDO_ENABLE; + + return 0; + +error_av: + if (!IS_ERR(aic32x4->supply_dv)) + regulator_disable(aic32x4->supply_dv); + +error_dv: + if (!IS_ERR(aic32x4->supply_ldo)) + regulator_disable(aic32x4->supply_ldo); + +error_ldo: + regulator_disable(aic32x4->supply_iov); + return ret; +} + static int aic32x4_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct aic32x4_pdata *pdata = i2c->dev.platform_data; struct aic32x4_priv *aic32x4; + struct device_node *np = i2c->dev.of_node; int ret; aic32x4 = devm_kzalloc(&i2c->dev, sizeof(struct aic32x4_priv), @@ -680,6 +825,12 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c, aic32x4->swapdacs = pdata->swapdacs; aic32x4->micpga_routing = pdata->micpga_routing; aic32x4->rstn_gpio = pdata->rstn_gpio; + } else if (np) { + ret = aic32x4_parse_dt(aic32x4, np); + if (ret) { + dev_err(&i2c->dev, "Failed to parse DT node\n"); + return ret; + } } else { aic32x4->power_cfg = 0; aic32x4->swapdacs = false; @@ -687,20 +838,44 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c, aic32x4->rstn_gpio = -1; } - if (aic32x4->rstn_gpio >= 0) { + aic32x4->mclk = devm_clk_get(&i2c->dev, "mclk"); + if (IS_ERR(aic32x4->mclk)) { + dev_err(&i2c->dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n"); + return PTR_ERR(aic32x4->mclk); + } + + if (gpio_is_valid(aic32x4->rstn_gpio)) { ret = devm_gpio_request_one(&i2c->dev, aic32x4->rstn_gpio, GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn"); if (ret != 0) return ret; } + ret = aic32x4_setup_regulators(&i2c->dev, aic32x4); + if (ret) { + dev_err(&i2c->dev, "Failed to setup regulators\n"); + return ret; + } + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_aic32x4, &aic32x4_dai, 1); - return ret; + if (ret) { + dev_err(&i2c->dev, "Failed to register codec\n"); + aic32x4_disable_regulators(aic32x4); + return ret; + } + + i2c_set_clientdata(i2c, aic32x4); + + return 0; } static int aic32x4_i2c_remove(struct i2c_client *client) { + struct aic32x4_priv *aic32x4 = i2c_get_clientdata(client); + + aic32x4_disable_regulators(aic32x4); + snd_soc_unregister_codec(&client->dev); return 0; } @@ -711,10 +886,17 @@ static const struct i2c_device_id aic32x4_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id); +static const struct of_device_id aic32x4_of_id[] = { + { .compatible = "ti,tlv320aic32x4", }, + { /* senitel */ } +}; +MODULE_DEVICE_TABLE(of, aic32x4_of_id); + static struct i2c_driver aic32x4_i2c_driver = { .driver = { .name = "tlv320aic32x4", .owner = THIS_MODULE, + .of_match_table = aic32x4_of_id, }, .probe = aic32x4_i2c_probe, .remove = aic32x4_i2c_remove, diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index 35774223fd91..995f033a855d 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -120,7 +120,9 @@ #define AIC32X4_MICBIAS_2075V 0x60 #define AIC32X4_LMICPGANIN_IN2R_10K 0x10 +#define AIC32X4_LMICPGANIN_CM1L_10K 0x40 #define AIC32X4_RMICPGANIN_IN1L_10K 0x10 +#define AIC32X4_RMICPGANIN_CM1R_10K 0x40 #define AIC32X4_LMICPGAVOL_NOGAIN 0x80 #define AIC32X4_RMICPGAVOL_NOGAIN 0x80 @@ -138,6 +140,7 @@ #define AIC32X4_LDAC2RCHN (0x02 << 4) #define AIC32X4_LDAC2LCHN (0x01 << 4) #define AIC32X4_RDAC2RCHN (0x01 << 2) +#define AIC32X4_DAC_CHAN_MASK 0x3c #define AIC32X4_SSTEP2WCLK 0x01 #define AIC32X4_MUTEON 0x0C 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/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 4f358393d6d6..793516146670 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -461,7 +461,7 @@ static int dac33_set_fifo_mode(struct snd_kcontrol *kcontrol, if (dac33->fifo_mode == ucontrol->value.integer.value[0]) return 0; /* Do not allow changes while stream is running*/ - if (codec->active) + if (snd_soc_codec_is_active(codec)) return -EPERM; if (ucontrol->value.integer.value[0] < 0 || @@ -478,9 +478,7 @@ static const char *dac33_fifo_mode_texts[] = { "Bypass", "Mode 1", "Mode 7" }; -static const struct soc_enum dac33_fifo_mode_enum = - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dac33_fifo_mode_texts), - dac33_fifo_mode_texts); +static SOC_ENUM_SINGLE_EXT_DECL(dac33_fifo_mode_enum, dac33_fifo_mode_texts); /* L/R Line Output Gain */ static const char *lr_lineout_gain_texts[] = { @@ -488,15 +486,13 @@ static const char *lr_lineout_gain_texts[] = { "Line 0dB DAC 12dB", "Line 6dB DAC 18dB", }; -static const struct soc_enum l_lineout_gain_enum = - SOC_ENUM_SINGLE(DAC33_LDAC_PWR_CTRL, 0, - ARRAY_SIZE(lr_lineout_gain_texts), - lr_lineout_gain_texts); +static SOC_ENUM_SINGLE_DECL(l_lineout_gain_enum, + DAC33_LDAC_PWR_CTRL, 0, + lr_lineout_gain_texts); -static const struct soc_enum r_lineout_gain_enum = - SOC_ENUM_SINGLE(DAC33_RDAC_PWR_CTRL, 0, - ARRAY_SIZE(lr_lineout_gain_texts), - lr_lineout_gain_texts); +static SOC_ENUM_SINGLE_DECL(r_lineout_gain_enum, + DAC33_RDAC_PWR_CTRL, 0, + lr_lineout_gain_texts); /* * DACL/R digital volume control: @@ -534,18 +530,16 @@ static const struct snd_kcontrol_new dac33_dapm_abypassr_control = /* LOP L/R invert selection */ static const char *dac33_lr_lom_texts[] = {"DAC", "LOP"}; -static const struct soc_enum dac33_left_lom_enum = - SOC_ENUM_SINGLE(DAC33_OUT_AMP_CTRL, 3, - ARRAY_SIZE(dac33_lr_lom_texts), - dac33_lr_lom_texts); +static SOC_ENUM_SINGLE_DECL(dac33_left_lom_enum, + DAC33_OUT_AMP_CTRL, 3, + dac33_lr_lom_texts); static const struct snd_kcontrol_new dac33_dapm_left_lom_control = SOC_DAPM_ENUM("Route", dac33_left_lom_enum); -static const struct soc_enum dac33_right_lom_enum = - SOC_ENUM_SINGLE(DAC33_OUT_AMP_CTRL, 2, - ARRAY_SIZE(dac33_lr_lom_texts), - dac33_lr_lom_texts); +static SOC_ENUM_SINGLE_DECL(dac33_right_lom_enum, + DAC33_OUT_AMP_CTRL, 2, + dac33_lr_lom_texts); static const struct snd_kcontrol_new dac33_dapm_right_lom_control = SOC_DAPM_ENUM("Route", dac33_right_lom_enum); diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index dfc51bb425da..975e0f760ac1 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -48,86 +48,6 @@ #define TWL4030_CACHEREGNUM (TWL4030_REG_MISC_SET_2 + 1) -/* - * twl4030 register cache & default register settings - */ -static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { - 0x00, /* this register not used */ - 0x00, /* REG_CODEC_MODE (0x1) */ - 0x00, /* REG_OPTION (0x2) */ - 0x00, /* REG_UNKNOWN (0x3) */ - 0x00, /* REG_MICBIAS_CTL (0x4) */ - 0x00, /* REG_ANAMICL (0x5) */ - 0x00, /* REG_ANAMICR (0x6) */ - 0x00, /* REG_AVADC_CTL (0x7) */ - 0x00, /* REG_ADCMICSEL (0x8) */ - 0x00, /* REG_DIGMIXING (0x9) */ - 0x0f, /* REG_ATXL1PGA (0xA) */ - 0x0f, /* REG_ATXR1PGA (0xB) */ - 0x0f, /* REG_AVTXL2PGA (0xC) */ - 0x0f, /* REG_AVTXR2PGA (0xD) */ - 0x00, /* REG_AUDIO_IF (0xE) */ - 0x00, /* REG_VOICE_IF (0xF) */ - 0x3f, /* REG_ARXR1PGA (0x10) */ - 0x3f, /* REG_ARXL1PGA (0x11) */ - 0x3f, /* REG_ARXR2PGA (0x12) */ - 0x3f, /* REG_ARXL2PGA (0x13) */ - 0x25, /* REG_VRXPGA (0x14) */ - 0x00, /* REG_VSTPGA (0x15) */ - 0x00, /* REG_VRX2ARXPGA (0x16) */ - 0x00, /* REG_AVDAC_CTL (0x17) */ - 0x00, /* REG_ARX2VTXPGA (0x18) */ - 0x32, /* REG_ARXL1_APGA_CTL (0x19) */ - 0x32, /* REG_ARXR1_APGA_CTL (0x1A) */ - 0x32, /* REG_ARXL2_APGA_CTL (0x1B) */ - 0x32, /* REG_ARXR2_APGA_CTL (0x1C) */ - 0x00, /* REG_ATX2ARXPGA (0x1D) */ - 0x00, /* REG_BT_IF (0x1E) */ - 0x55, /* REG_BTPGA (0x1F) */ - 0x00, /* REG_BTSTPGA (0x20) */ - 0x00, /* REG_EAR_CTL (0x21) */ - 0x00, /* REG_HS_SEL (0x22) */ - 0x00, /* REG_HS_GAIN_SET (0x23) */ - 0x00, /* REG_HS_POPN_SET (0x24) */ - 0x00, /* REG_PREDL_CTL (0x25) */ - 0x00, /* REG_PREDR_CTL (0x26) */ - 0x00, /* REG_PRECKL_CTL (0x27) */ - 0x00, /* REG_PRECKR_CTL (0x28) */ - 0x00, /* REG_HFL_CTL (0x29) */ - 0x00, /* REG_HFR_CTL (0x2A) */ - 0x05, /* REG_ALC_CTL (0x2B) */ - 0x00, /* REG_ALC_SET1 (0x2C) */ - 0x00, /* REG_ALC_SET2 (0x2D) */ - 0x00, /* REG_BOOST_CTL (0x2E) */ - 0x00, /* REG_SOFTVOL_CTL (0x2F) */ - 0x13, /* REG_DTMF_FREQSEL (0x30) */ - 0x00, /* REG_DTMF_TONEXT1H (0x31) */ - 0x00, /* REG_DTMF_TONEXT1L (0x32) */ - 0x00, /* REG_DTMF_TONEXT2H (0x33) */ - 0x00, /* REG_DTMF_TONEXT2L (0x34) */ - 0x79, /* REG_DTMF_TONOFF (0x35) */ - 0x11, /* REG_DTMF_WANONOFF (0x36) */ - 0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */ - 0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */ - 0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */ - 0x06, /* REG_APLL_CTL (0x3A) */ - 0x00, /* REG_DTMF_CTL (0x3B) */ - 0x44, /* REG_DTMF_PGA_CTL2 (0x3C) */ - 0x69, /* REG_DTMF_PGA_CTL1 (0x3D) */ - 0x00, /* REG_MISC_SET_1 (0x3E) */ - 0x00, /* REG_PCMBTMUX (0x3F) */ - 0x00, /* not used (0x40) */ - 0x00, /* not used (0x41) */ - 0x00, /* not used (0x42) */ - 0x00, /* REG_RX_PATH_SEL (0x43) */ - 0x32, /* REG_VDL_APGA_CTL (0x44) */ - 0x00, /* REG_VIBRA_CTL (0x45) */ - 0x00, /* REG_VIBRA_SET (0x46) */ - 0x00, /* REG_VIBRA_PWM_SET (0x47) */ - 0x00, /* REG_ANAMIC_GAIN (0x48) */ - 0x00, /* REG_MISC_SET_2 (0x49) */ -}; - /* codec private data */ struct twl4030_priv { unsigned int codec_powered; @@ -150,81 +70,108 @@ struct twl4030_priv { u8 earpiece_enabled; u8 predrivel_enabled, predriver_enabled; u8 carkitl_enabled, carkitr_enabled; + u8 ctl_cache[TWL4030_REG_PRECKR_CTL - TWL4030_REG_EAR_CTL + 1]; struct twl4030_codec_data *pdata; }; -/* - * read twl4030 register cache - */ -static inline unsigned int twl4030_read_reg_cache(struct snd_soc_codec *codec, - unsigned int reg) +static void tw4030_init_ctl_cache(struct twl4030_priv *twl4030) { - u8 *cache = codec->reg_cache; - - if (reg >= TWL4030_CACHEREGNUM) - return -EIO; + int i; + u8 byte; - return cache[reg]; + for (i = TWL4030_REG_EAR_CTL; i <= TWL4030_REG_PRECKR_CTL; i++) { + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, i); + twl4030->ctl_cache[i - TWL4030_REG_EAR_CTL] = byte; + } } -/* - * write twl4030 register cache - */ -static inline void twl4030_write_reg_cache(struct snd_soc_codec *codec, - u8 reg, u8 value) +static unsigned int twl4030_read(struct snd_soc_codec *codec, unsigned int reg) { - u8 *cache = codec->reg_cache; + struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); + u8 value = 0; if (reg >= TWL4030_CACHEREGNUM) - return; - cache[reg] = value; + return -EIO; + + switch (reg) { + case TWL4030_REG_EAR_CTL: + case TWL4030_REG_PREDL_CTL: + case TWL4030_REG_PREDR_CTL: + case TWL4030_REG_PRECKL_CTL: + case TWL4030_REG_PRECKR_CTL: + case TWL4030_REG_HS_GAIN_SET: + value = twl4030->ctl_cache[reg - TWL4030_REG_EAR_CTL]; + break; + default: + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &value, reg); + break; + } + + return value; } -/* - * write to the twl4030 register space - */ -static int twl4030_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int value) +static bool twl4030_can_write_to_chip(struct twl4030_priv *twl4030, + unsigned int reg) { - struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); - int write_to_reg = 0; + bool write_to_reg = false; - twl4030_write_reg_cache(codec, reg, value); /* Decide if the given register can be written */ switch (reg) { case TWL4030_REG_EAR_CTL: if (twl4030->earpiece_enabled) - write_to_reg = 1; + write_to_reg = true; break; case TWL4030_REG_PREDL_CTL: if (twl4030->predrivel_enabled) - write_to_reg = 1; + write_to_reg = true; break; case TWL4030_REG_PREDR_CTL: if (twl4030->predriver_enabled) - write_to_reg = 1; + write_to_reg = true; break; case TWL4030_REG_PRECKL_CTL: if (twl4030->carkitl_enabled) - write_to_reg = 1; + write_to_reg = true; break; case TWL4030_REG_PRECKR_CTL: if (twl4030->carkitr_enabled) - write_to_reg = 1; + write_to_reg = true; break; case TWL4030_REG_HS_GAIN_SET: if (twl4030->hsl_enabled || twl4030->hsr_enabled) - write_to_reg = 1; + write_to_reg = true; break; default: /* All other register can be written */ - write_to_reg = 1; + write_to_reg = true; break; } - if (write_to_reg) - return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, - value, reg); + + return write_to_reg; +} + +static int twl4030_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); + + /* Update the ctl cache */ + switch (reg) { + case TWL4030_REG_EAR_CTL: + case TWL4030_REG_PREDL_CTL: + case TWL4030_REG_PREDR_CTL: + case TWL4030_REG_PRECKL_CTL: + case TWL4030_REG_PRECKR_CTL: + case TWL4030_REG_HS_GAIN_SET: + twl4030->ctl_cache[reg - TWL4030_REG_EAR_CTL] = value; + break; + default: + break; + } + + if (twl4030_can_write_to_chip(twl4030, reg)) + return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg); return 0; } @@ -252,46 +199,14 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) else mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER); - if (mode >= 0) { - twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode); + if (mode >= 0) twl4030->codec_powered = enable; - } /* REVISIT: this delay is present in TI sample drivers */ /* but there seems to be no TRM requirement for it */ udelay(10); } -static inline void twl4030_check_defaults(struct snd_soc_codec *codec) -{ - int i, difference = 0; - u8 val; - - dev_dbg(codec->dev, "Checking TWL audio default configuration\n"); - for (i = 1; i <= TWL4030_REG_MISC_SET_2; i++) { - twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, i); - if (val != twl4030_reg[i]) { - difference++; - dev_dbg(codec->dev, - "Reg 0x%02x: chip: 0x%02x driver: 0x%02x\n", - i, val, twl4030_reg[i]); - } - } - dev_dbg(codec->dev, "Found %d non-matching registers. %s\n", - difference, difference ? "Not OK" : "OK"); -} - -static inline void twl4030_reset_registers(struct snd_soc_codec *codec) -{ - int i; - - /* set all audio section registers to reasonable defaults */ - for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) - if (i != TWL4030_REG_APLL_CTL) - twl4030_write(codec, i, twl4030_reg[i]); - -} - static void twl4030_setup_pdata_of(struct twl4030_codec_data *pdata, struct device_node *node) { @@ -372,27 +287,17 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) } } - /* Check defaults, if instructed before anything else */ - if (pdata && pdata->check_defaults) - twl4030_check_defaults(codec); - - /* Reset registers, if no setup data or if instructed to do so */ - if (!pdata || (pdata && pdata->reset_registers)) - twl4030_reset_registers(codec); - - /* Refresh APLL_CTL register from HW */ - twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, - TWL4030_REG_APLL_CTL); - twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, byte); + /* Initialize the local ctl register cache */ + tw4030_init_ctl_cache(twl4030); /* anti-pop when changing analog gain */ - reg = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1); + reg = twl4030_read(codec, TWL4030_REG_MISC_SET_1); twl4030_write(codec, TWL4030_REG_MISC_SET_1, - reg | TWL4030_SMOOTH_ANAVOL_EN); + reg | TWL4030_SMOOTH_ANAVOL_EN); twl4030_write(codec, TWL4030_REG_OPTION, - TWL4030_ATXL1_EN | TWL4030_ATXR1_EN | - TWL4030_ARXL2_EN | TWL4030_ARXR2_EN); + TWL4030_ATXL1_EN | TWL4030_ATXR1_EN | + TWL4030_ARXL2_EN | TWL4030_ARXR2_EN); /* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */ twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32); @@ -403,19 +308,19 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) twl4030->pdata = pdata; - reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); + reg = twl4030_read(codec, TWL4030_REG_HS_POPN_SET); reg &= ~TWL4030_RAMP_DELAY; reg |= (pdata->ramp_delay_value << 2); - twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, reg); + twl4030_write(codec, TWL4030_REG_HS_POPN_SET, reg); /* initiate offset cancellation */ twl4030_codec_enable(codec, 1); - reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); + reg = twl4030_read(codec, TWL4030_REG_ANAMICL); reg &= ~TWL4030_OFFSET_CNCL_SEL; reg |= pdata->offset_cncl_path; twl4030_write(codec, TWL4030_REG_ANAMICL, - reg | TWL4030_CNCL_OFFSET_START); + reg | TWL4030_CNCL_OFFSET_START); /* * Wait for offset cancellation to complete. @@ -425,15 +330,14 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) msleep(20); do { usleep_range(1000, 2000); + twl_set_regcache_bypass(TWL4030_MODULE_AUDIO_VOICE, true); twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, - TWL4030_REG_ANAMICL); + TWL4030_REG_ANAMICL); + twl_set_regcache_bypass(TWL4030_MODULE_AUDIO_VOICE, false); } while ((i++ < 100) && ((byte & TWL4030_CNCL_OFFSET_START) == TWL4030_CNCL_OFFSET_START)); - /* Make sure that the reg_cache has the same value as the HW */ - twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); - twl4030_codec_enable(codec, 0); } @@ -453,9 +357,6 @@ static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) status = twl4030_audio_disable_resource( TWL4030_AUDIO_RES_APLL); } - - if (status >= 0) - twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); } /* Earpiece */ @@ -514,10 +415,9 @@ static const struct snd_kcontrol_new twl4030_dapm_carkitr_controls[] = { static const char *twl4030_handsfreel_texts[] = {"Voice", "AudioL1", "AudioL2", "AudioR2"}; -static const struct soc_enum twl4030_handsfreel_enum = - SOC_ENUM_SINGLE(TWL4030_REG_HFL_CTL, 0, - ARRAY_SIZE(twl4030_handsfreel_texts), - twl4030_handsfreel_texts); +static SOC_ENUM_SINGLE_DECL(twl4030_handsfreel_enum, + TWL4030_REG_HFL_CTL, 0, + twl4030_handsfreel_texts); static const struct snd_kcontrol_new twl4030_dapm_handsfreel_control = SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum); @@ -530,10 +430,9 @@ static const struct snd_kcontrol_new twl4030_dapm_handsfreelmute_control = static const char *twl4030_handsfreer_texts[] = {"Voice", "AudioR1", "AudioR2", "AudioL2"}; -static const struct soc_enum twl4030_handsfreer_enum = - SOC_ENUM_SINGLE(TWL4030_REG_HFR_CTL, 0, - ARRAY_SIZE(twl4030_handsfreer_texts), - twl4030_handsfreer_texts); +static SOC_ENUM_SINGLE_DECL(twl4030_handsfreer_enum, + TWL4030_REG_HFR_CTL, 0, + twl4030_handsfreer_texts); static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control = SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum); @@ -547,10 +446,9 @@ static const struct snd_kcontrol_new twl4030_dapm_handsfreermute_control = static const char *twl4030_vibra_texts[] = {"AudioL1", "AudioR1", "AudioL2", "AudioR2"}; -static const struct soc_enum twl4030_vibra_enum = - SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 2, - ARRAY_SIZE(twl4030_vibra_texts), - twl4030_vibra_texts); +static SOC_ENUM_SINGLE_DECL(twl4030_vibra_enum, + TWL4030_REG_VIBRA_CTL, 2, + twl4030_vibra_texts); static const struct snd_kcontrol_new twl4030_dapm_vibra_control = SOC_DAPM_ENUM("Route", twl4030_vibra_enum); @@ -559,10 +457,9 @@ SOC_DAPM_ENUM("Route", twl4030_vibra_enum); static const char *twl4030_vibrapath_texts[] = {"Local vibrator", "Audio"}; -static const struct soc_enum twl4030_vibrapath_enum = - SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 4, - ARRAY_SIZE(twl4030_vibrapath_texts), - twl4030_vibrapath_texts); +static SOC_ENUM_SINGLE_DECL(twl4030_vibrapath_enum, + TWL4030_REG_VIBRA_CTL, 4, + twl4030_vibrapath_texts); static const struct snd_kcontrol_new twl4030_dapm_vibrapath_control = SOC_DAPM_ENUM("Route", twl4030_vibrapath_enum); @@ -589,10 +486,9 @@ static const struct snd_kcontrol_new twl4030_dapm_analogrmic_controls[] = { static const char *twl4030_micpathtx1_texts[] = {"Analog", "Digimic0"}; -static const struct soc_enum twl4030_micpathtx1_enum = - SOC_ENUM_SINGLE(TWL4030_REG_ADCMICSEL, 0, - ARRAY_SIZE(twl4030_micpathtx1_texts), - twl4030_micpathtx1_texts); +static SOC_ENUM_SINGLE_DECL(twl4030_micpathtx1_enum, + TWL4030_REG_ADCMICSEL, 0, + twl4030_micpathtx1_texts); static const struct snd_kcontrol_new twl4030_dapm_micpathtx1_control = SOC_DAPM_ENUM("Route", twl4030_micpathtx1_enum); @@ -601,10 +497,9 @@ SOC_DAPM_ENUM("Route", twl4030_micpathtx1_enum); static const char *twl4030_micpathtx2_texts[] = {"Analog", "Digimic1"}; -static const struct soc_enum twl4030_micpathtx2_enum = - SOC_ENUM_SINGLE(TWL4030_REG_ADCMICSEL, 2, - ARRAY_SIZE(twl4030_micpathtx2_texts), - twl4030_micpathtx2_texts); +static SOC_ENUM_SINGLE_DECL(twl4030_micpathtx2_enum, + TWL4030_REG_ADCMICSEL, 2, + twl4030_micpathtx2_texts); static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control = SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum); @@ -671,20 +566,18 @@ static const struct snd_kcontrol_new twl4030_dapm_dbypassv_control = */ #define TWL4030_OUTPUT_PGA(pin_name, reg, mask) \ static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \ - struct snd_kcontrol *kcontrol, int event) \ + struct snd_kcontrol *kcontrol, int event) \ { \ struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); \ \ switch (event) { \ case SND_SOC_DAPM_POST_PMU: \ twl4030->pin_name##_enabled = 1; \ - twl4030_write(w->codec, reg, \ - twl4030_read_reg_cache(w->codec, reg)); \ + twl4030_write(w->codec, reg, twl4030_read(w->codec, reg)); \ break; \ case SND_SOC_DAPM_POST_PMD: \ twl4030->pin_name##_enabled = 0; \ - twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \ - 0, reg); \ + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 0, reg); \ break; \ } \ return 0; \ @@ -700,7 +593,7 @@ static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp) { unsigned char hs_ctl; - hs_ctl = twl4030_read_reg_cache(codec, reg); + hs_ctl = twl4030_read(codec, reg); if (ramp) { /* HF ramp-up */ @@ -727,7 +620,7 @@ static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp) } static int handsfreelpga_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { switch (event) { case SND_SOC_DAPM_POST_PMU: @@ -741,7 +634,7 @@ static int handsfreelpga_event(struct snd_soc_dapm_widget *w, } static int handsfreerpga_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { switch (event) { case SND_SOC_DAPM_POST_PMU: @@ -755,14 +648,14 @@ static int handsfreerpga_event(struct snd_soc_dapm_widget *w, } static int vibramux_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { twl4030_write(w->codec, TWL4030_REG_VIBRA_SET, 0xff); return 0; } static int apll_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -776,11 +669,11 @@ static int apll_event(struct snd_soc_dapm_widget *w, } static int aif_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { u8 audio_if; - audio_if = twl4030_read_reg_cache(w->codec, TWL4030_REG_AUDIO_IF); + audio_if = twl4030_read(w->codec, TWL4030_REG_AUDIO_IF); switch (event) { case SND_SOC_DAPM_PRE_PMU: /* Enable AIF */ @@ -788,12 +681,12 @@ static int aif_event(struct snd_soc_dapm_widget *w, twl4030_apll_enable(w->codec, 1); twl4030_write(w->codec, TWL4030_REG_AUDIO_IF, - audio_if | TWL4030_AIF_EN); + audio_if | TWL4030_AIF_EN); break; case SND_SOC_DAPM_POST_PMD: /* disable the DAI before we stop it's source PLL */ twl4030_write(w->codec, TWL4030_REG_AUDIO_IF, - audio_if & ~TWL4030_AIF_EN); + audio_if & ~TWL4030_AIF_EN); twl4030_apll_enable(w->codec, 0); break; } @@ -810,8 +703,8 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) 8388608, 16777216, 33554432, 67108864}; unsigned int delay; - hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET); - hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); + hs_gain = twl4030_read(codec, TWL4030_REG_HS_GAIN_SET); + hs_pop = twl4030_read(codec, TWL4030_REG_HS_POPN_SET); delay = (ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] / twl4030->sysclk) + 1; @@ -831,9 +724,8 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) hs_pop |= TWL4030_VMID_EN; twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); /* Actually write to the register */ - twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, - hs_gain, - TWL4030_REG_HS_GAIN_SET); + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain, + TWL4030_REG_HS_GAIN_SET); hs_pop |= TWL4030_RAMP_EN; twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); /* Wait ramp delay time + 1, so the VMID can settle */ @@ -846,9 +738,8 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) /* Wait ramp delay time + 1, so the VMID can settle */ twl4030_wait_ms(delay); /* Bypass the reg_cache to mute the headset */ - twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, - hs_gain & (~0x0f), - TWL4030_REG_HS_GAIN_SET); + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain & (~0x0f), + TWL4030_REG_HS_GAIN_SET); hs_pop &= ~TWL4030_VMID_EN; twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); @@ -866,7 +757,7 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) } static int headsetlpga_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); @@ -890,7 +781,7 @@ static int headsetlpga_event(struct snd_soc_dapm_widget *w, } static int headsetrpga_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); @@ -914,7 +805,7 @@ static int headsetrpga_event(struct snd_soc_dapm_widget *w, } static int digimic_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event) { struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); struct twl4030_codec_data *pdata = twl4030->pdata; @@ -935,7 +826,7 @@ static int digimic_event(struct snd_soc_dapm_widget *w, * Custom volsw and volsw_2r get/put functions to handle these gain bits. */ static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; @@ -964,7 +855,7 @@ static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol, } static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; @@ -993,7 +884,7 @@ static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol, } static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; @@ -1020,7 +911,7 @@ static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, } static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; @@ -1058,19 +949,15 @@ static const char *twl4030_op_modes_texts[] = { "Option 2 (voice/audio)", "Option 1 (audio)" }; -static const struct soc_enum twl4030_op_modes_enum = - SOC_ENUM_SINGLE(TWL4030_REG_CODEC_MODE, 0, - ARRAY_SIZE(twl4030_op_modes_texts), - twl4030_op_modes_texts); +static SOC_ENUM_SINGLE_DECL(twl4030_op_modes_enum, + TWL4030_REG_CODEC_MODE, 0, + twl4030_op_modes_texts); static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned short val; - unsigned short mask; if (twl4030->configured) { dev_err(codec->dev, @@ -1078,19 +965,7 @@ static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol, return -EBUSY; } - if (ucontrol->value.enumerated.item[0] > e->max - 1) - return -EINVAL; - - val = ucontrol->value.enumerated.item[0] << e->shift_l; - mask = e->mask << e->shift_l; - if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] > e->max - 1) - return -EINVAL; - val |= ucontrol->value.enumerated.item[1] << e->shift_r; - mask |= e->mask << e->shift_r; - } - - return snd_soc_update_bits(codec, e->reg, mask, val); + return snd_soc_put_enum_double(kcontrol, ucontrol); } /* @@ -1147,10 +1022,9 @@ static const char *twl4030_avadc_clk_priority_texts[] = { "Voice high priority", "HiFi high priority" }; -static const struct soc_enum twl4030_avadc_clk_priority_enum = - SOC_ENUM_SINGLE(TWL4030_REG_AVADC_CTL, 2, - ARRAY_SIZE(twl4030_avadc_clk_priority_texts), - twl4030_avadc_clk_priority_texts); +static SOC_ENUM_SINGLE_DECL(twl4030_avadc_clk_priority_enum, + TWL4030_REG_AVADC_CTL, 2, + twl4030_avadc_clk_priority_texts); static const char *twl4030_rampdelay_texts[] = { "27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms", @@ -1158,40 +1032,36 @@ static const char *twl4030_rampdelay_texts[] = { "3495/2581/1748 ms" }; -static const struct soc_enum twl4030_rampdelay_enum = - SOC_ENUM_SINGLE(TWL4030_REG_HS_POPN_SET, 2, - ARRAY_SIZE(twl4030_rampdelay_texts), - twl4030_rampdelay_texts); +static SOC_ENUM_SINGLE_DECL(twl4030_rampdelay_enum, + TWL4030_REG_HS_POPN_SET, 2, + twl4030_rampdelay_texts); /* Vibra H-bridge direction mode */ static const char *twl4030_vibradirmode_texts[] = { "Vibra H-bridge direction", "Audio data MSB", }; -static const struct soc_enum twl4030_vibradirmode_enum = - SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 5, - ARRAY_SIZE(twl4030_vibradirmode_texts), - twl4030_vibradirmode_texts); +static SOC_ENUM_SINGLE_DECL(twl4030_vibradirmode_enum, + TWL4030_REG_VIBRA_CTL, 5, + twl4030_vibradirmode_texts); /* Vibra H-bridge direction */ static const char *twl4030_vibradir_texts[] = { "Positive polarity", "Negative polarity", }; -static const struct soc_enum twl4030_vibradir_enum = - SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 1, - ARRAY_SIZE(twl4030_vibradir_texts), - twl4030_vibradir_texts); +static SOC_ENUM_SINGLE_DECL(twl4030_vibradir_enum, + TWL4030_REG_VIBRA_CTL, 1, + twl4030_vibradir_texts); /* Digimic Left and right swapping */ static const char *twl4030_digimicswap_texts[] = { "Not swapped", "Swapped", }; -static const struct soc_enum twl4030_digimicswap_enum = - SOC_ENUM_SINGLE(TWL4030_REG_MISC_SET_1, 0, - ARRAY_SIZE(twl4030_digimicswap_texts), - twl4030_digimicswap_texts); +static SOC_ENUM_SINGLE_DECL(twl4030_digimicswap_enum, + TWL4030_REG_MISC_SET_1, 0, + twl4030_digimicswap_texts); static const struct snd_kcontrol_new twl4030_snd_controls[] = { /* Codec operation mode control */ @@ -1751,11 +1621,11 @@ static void twl4030_constraints(struct twl4030_priv *twl4030, /* In case of 4 channel mode, the RX1 L/R for playback and the TX2 L/R for * capture has to be enabled/disabled. */ static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction, - int enable) + int enable) { u8 reg, mask; - reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION); + reg = twl4030_read(codec, TWL4030_REG_OPTION); if (direction == SNDRV_PCM_STREAM_PLAYBACK) mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN; @@ -1784,14 +1654,14 @@ static int twl4030_startup(struct snd_pcm_substream *substream, if (twl4030->configured) twl4030_constraints(twl4030, twl4030->master_substream); } else { - if (!(twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) & + if (!(twl4030_read(codec, TWL4030_REG_CODEC_MODE) & TWL4030_OPTION_1)) { /* In option2 4 channel is not supported, set the * constraint for the first stream for channels, the * second stream will 'inherit' this cosntraint */ snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_CHANNELS, - 2, 2); + SNDRV_PCM_HW_PARAM_CHANNELS, + 2, 2); } twl4030->master_substream = substream; } @@ -1823,8 +1693,8 @@ static void twl4030_shutdown(struct snd_pcm_substream *substream, } static int twl4030_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); @@ -1832,8 +1702,8 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, /* If the substream has 4 channel, do the necessary setup */ if (params_channels(params) == 4) { - format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); - mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); + format = twl4030_read(codec, TWL4030_REG_AUDIO_IF); + mode = twl4030_read(codec, TWL4030_REG_CODEC_MODE); /* Safety check: are we in the correct operating mode and * the interface is in TDM mode? */ @@ -1849,8 +1719,8 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, return 0; /* bit rate */ - old_mode = twl4030_read_reg_cache(codec, - TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ; + old_mode = twl4030_read(codec, + TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ; mode = old_mode & ~TWL4030_APLL_RATE; switch (params_rate(params)) { @@ -1891,7 +1761,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, } /* sample size */ - old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); + old_format = twl4030_read(codec, TWL4030_REG_AUDIO_IF); format = old_format; format &= ~TWL4030_DATA_WIDTH; switch (params_format(params)) { @@ -1940,8 +1810,8 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, return 0; } -static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, - int clk_id, unsigned int freq, int dir) +static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, + unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); @@ -1966,15 +1836,14 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, return 0; } -static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) +static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); u8 old_format, format; /* get format */ - old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); + old_format = twl4030_read(codec, TWL4030_REG_AUDIO_IF); format = old_format; /* set master/slave audio interface */ @@ -2024,7 +1893,7 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate) { struct snd_soc_codec *codec = dai->codec; - u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); + u8 reg = twl4030_read(codec, TWL4030_REG_AUDIO_IF); if (tristate) reg |= TWL4030_AIF_TRI_EN; @@ -2037,11 +1906,11 @@ static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate) /* In case of voice mode, the RX1 L(VRX) for downlink and the TX2 L/R * (VTXL, VTXR) for uplink has to be enabled/disabled. */ static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction, - int enable) + int enable) { u8 reg, mask; - reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION); + reg = twl4030_read(codec, TWL4030_REG_OPTION); if (direction == SNDRV_PCM_STREAM_PLAYBACK) mask = TWL4030_ARXL1_VRX_EN; @@ -2057,7 +1926,7 @@ static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction, } static int twl4030_voice_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); @@ -2076,7 +1945,7 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream, /* If the codec mode is not option2, the voice PCM interface is not * available. */ - mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) + mode = twl4030_read(codec, TWL4030_REG_CODEC_MODE) & TWL4030_OPT_MODE; if (mode != TWL4030_OPTION_2) { @@ -2089,7 +1958,7 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream, } static void twl4030_voice_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; @@ -2098,7 +1967,8 @@ static void twl4030_voice_shutdown(struct snd_pcm_substream *substream, } static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); @@ -2108,8 +1978,8 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, twl4030_voice_enable(codec, substream->stream, 1); /* bit rate */ - old_mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) - & ~(TWL4030_CODECPDZ); + old_mode = twl4030_read(codec, + TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ; mode = old_mode; switch (params_rate(params)) { @@ -2143,7 +2013,7 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, } static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, - int clk_id, unsigned int freq, int dir) + int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); @@ -2164,14 +2034,14 @@ static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, } static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) + unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); u8 old_format, format; /* get format */ - old_format = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF); + old_format = twl4030_read(codec, TWL4030_REG_VOICE_IF); format = old_format; /* set master/slave audio interface */ @@ -2218,7 +2088,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate) { struct snd_soc_codec *codec = dai->codec; - u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF); + u8 reg = twl4030_read(codec, TWL4030_REG_VOICE_IF); if (tristate) reg |= TWL4030_VIF_TRI_EN; @@ -2310,8 +2180,6 @@ static int twl4030_soc_remove(struct snd_soc_codec *codec) struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); struct twl4030_codec_data *pdata = twl4030->pdata; - /* Reset registers to their chip default before leaving */ - twl4030_reset_registers(codec); twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); if (pdata && pdata->hs_extmute && gpio_is_valid(pdata->hs_extmute_gpio)) @@ -2323,13 +2191,10 @@ static int twl4030_soc_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { .probe = twl4030_soc_probe, .remove = twl4030_soc_remove, - .read = twl4030_read_reg_cache, + .read = twl4030_read, .write = twl4030_write, .set_bias_level = twl4030_set_bias_level, .idle_bias_off = true, - .reg_cache_size = sizeof(twl4030_reg), - .reg_word_size = sizeof(u8), - .reg_cache_default = twl4030_reg, .controls = twl4030_snd_controls, .num_controls = ARRAY_SIZE(twl4030_snd_controls), @@ -2342,7 +2207,7 @@ static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { static int twl4030_codec_probe(struct platform_device *pdev) { return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl4030, - twl4030_dai, ARRAY_SIZE(twl4030_dai)); + twl4030_dai, ARRAY_SIZE(twl4030_dai)); } static int twl4030_codec_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index f2f4bcb2ff71..bd3a20647fdf 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,77 +80,8 @@ 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[] = { +static const unsigned int lp_rates[] = { 8000, 11250, 16000, @@ -161,7 +93,7 @@ static unsigned int lp_rates[] = { 96000, }; -static unsigned int hp_rates[] = { +static const unsigned int hp_rates[] = { 8000, 16000, 32000, @@ -169,58 +101,38 @@ static unsigned int hp_rates[] = { 96000, }; -static struct snd_pcm_hw_constraint_list sysclk_constraints[] = { +static const struct snd_pcm_hw_constraint_list sysclk_constraints[] = { { .count = ARRAY_SIZE(lp_rates), .list = lp_rates, }, { .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; @@ -483,8 +392,10 @@ static const char *twl6040_amicr_texts[] = {"Headset Mic", "Sub Mic", "Aux/FM Right", "Off"}; static const struct soc_enum twl6040_enum[] = { - SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3, 4, twl6040_amicl_texts), - SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3, 4, twl6040_amicr_texts), + SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3, + ARRAY_SIZE(twl6040_amicl_texts), twl6040_amicl_texts), + SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3, + ARRAY_SIZE(twl6040_amicr_texts), twl6040_amicr_texts), }; static const char *twl6040_hs_texts[] = { @@ -567,9 +478,8 @@ static const char *twl6040_power_mode_texts[] = { "Low-Power", "High-Performance", }; -static const struct soc_enum twl6040_power_mode_enum = - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl6040_power_mode_texts), - twl6040_power_mode_texts); +static SOC_ENUM_SINGLE_EXT_DECL(twl6040_power_mode_enum, + twl6040_power_mode_texts); static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -656,7 +566,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 +841,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 +961,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 +979,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 +1117,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 +1129,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 +1149,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 +1170,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..4dadaa8ad46c 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -108,7 +108,7 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg, /* the interpolator & decimator regs must only be written when the * codec DAI is active. */ - if (!codec->active && (reg >= UDA1380_MVOL)) + if (!snd_soc_codec_is_active(codec) && (reg >= UDA1380_MVOL)) return 0; pr_debug("uda1380: hw write %x val %x\n", reg, value); if (codec->hw_write(codec->control_data, data, 3) == 3) { @@ -237,25 +237,27 @@ static const char *uda1380_os_setting[] = { }; static const struct soc_enum uda1380_deemp_enum[] = { - SOC_ENUM_SINGLE(UDA1380_DEEMP, 8, 5, uda1380_deemp), - SOC_ENUM_SINGLE(UDA1380_DEEMP, 0, 5, uda1380_deemp), + SOC_ENUM_SINGLE(UDA1380_DEEMP, 8, ARRAY_SIZE(uda1380_deemp), + uda1380_deemp), + SOC_ENUM_SINGLE(UDA1380_DEEMP, 0, ARRAY_SIZE(uda1380_deemp), + uda1380_deemp), }; -static const struct soc_enum uda1380_input_sel_enum = - SOC_ENUM_SINGLE(UDA1380_ADC, 2, 4, uda1380_input_sel); /* SEL_MIC, SEL_LNA */ -static const struct soc_enum uda1380_output_sel_enum = - SOC_ENUM_SINGLE(UDA1380_PM, 7, 2, uda1380_output_sel); /* R02_EN_AVC */ -static const struct soc_enum uda1380_spf_enum = - SOC_ENUM_SINGLE(UDA1380_MODE, 14, 4, uda1380_spf_mode); /* M */ -static const struct soc_enum uda1380_capture_sel_enum = - SOC_ENUM_SINGLE(UDA1380_IFACE, 6, 2, uda1380_capture_sel); /* SEL_SOURCE */ -static const struct soc_enum uda1380_sel_ns_enum = - SOC_ENUM_SINGLE(UDA1380_MIXER, 14, 2, uda1380_sel_ns); /* SEL_NS */ -static const struct soc_enum uda1380_mix_enum = - SOC_ENUM_SINGLE(UDA1380_MIXER, 12, 4, uda1380_mix_control); /* MIX, MIX_POS */ -static const struct soc_enum uda1380_sdet_enum = - SOC_ENUM_SINGLE(UDA1380_MIXER, 4, 4, uda1380_sdet_setting); /* SD_VALUE */ -static const struct soc_enum uda1380_os_enum = - SOC_ENUM_SINGLE(UDA1380_MIXER, 0, 3, uda1380_os_setting); /* OS */ +static SOC_ENUM_SINGLE_DECL(uda1380_input_sel_enum, + UDA1380_ADC, 2, uda1380_input_sel); /* SEL_MIC, SEL_LNA */ +static SOC_ENUM_SINGLE_DECL(uda1380_output_sel_enum, + UDA1380_PM, 7, uda1380_output_sel); /* R02_EN_AVC */ +static SOC_ENUM_SINGLE_DECL(uda1380_spf_enum, + UDA1380_MODE, 14, uda1380_spf_mode); /* M */ +static SOC_ENUM_SINGLE_DECL(uda1380_capture_sel_enum, + UDA1380_IFACE, 6, uda1380_capture_sel); /* SEL_SOURCE */ +static SOC_ENUM_SINGLE_DECL(uda1380_sel_ns_enum, + UDA1380_MIXER, 14, uda1380_sel_ns); /* SEL_NS */ +static SOC_ENUM_SINGLE_DECL(uda1380_mix_enum, + UDA1380_MIXER, 12, uda1380_mix_control); /* MIX, MIX_POS */ +static SOC_ENUM_SINGLE_DECL(uda1380_sdet_enum, + UDA1380_MIXER, 4, uda1380_sdet_setting); /* SD_VALUE */ +static SOC_ENUM_SINGLE_DECL(uda1380_os_enum, + UDA1380_MIXER, 0, uda1380_os_setting); /* OS */ /* * from -48 dB in 1.5 dB steps (mute instead of -49.5 dB) @@ -794,7 +796,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 +842,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 +853,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/wl1273.c b/sound/soc/codecs/wl1273.c index b7ab2ef567c8..6be5f80b65f1 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -197,7 +197,7 @@ static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, return 0; /* Do not allow changes while stream is running */ - if (codec->active) + if (snd_soc_codec_is_active(codec)) return -EPERM; if (ucontrol->value.integer.value[0] < 0 || @@ -209,8 +209,7 @@ static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, return 1; } -static const struct soc_enum wl1273_enum = - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wl1273_audio_route), wl1273_audio_route); +static SOC_ENUM_SINGLE_EXT_DECL(wl1273_enum, wl1273_audio_route); static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -247,9 +246,7 @@ static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol, static const char * const wl1273_audio_strings[] = { "Digital", "Analog" }; -static const struct soc_enum wl1273_audio_enum = - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wl1273_audio_strings), - wl1273_audio_strings); +static SOC_ENUM_SINGLE_EXT_DECL(wl1273_audio_enum, wl1273_audio_strings); static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 57ba315d0c84..1e0a083d8345 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1113,11 +1113,10 @@ static const char *wm2200_rxanc_input_sel_texts[] = { "None", "IN1", "IN2", "IN3", }; -static const struct soc_enum wm2200_rxanc_input_sel = - SOC_ENUM_SINGLE(WM2200_RXANC_SRC, - WM2200_IN_RXANC_SEL_SHIFT, - ARRAY_SIZE(wm2200_rxanc_input_sel_texts), - wm2200_rxanc_input_sel_texts); +static SOC_ENUM_SINGLE_DECL(wm2200_rxanc_input_sel, + WM2200_RXANC_SRC, + WM2200_IN_RXANC_SEL_SHIFT, + wm2200_rxanc_input_sel_texts); static const struct snd_kcontrol_new wm2200_snd_controls[] = { SOC_SINGLE("IN1 High Performance Switch", WM2200_IN1L_CONTROL, @@ -1288,11 +1287,10 @@ static const char *wm2200_aec_loopback_texts[] = { "OUT1L", "OUT1R", "OUT2L", "OUT2R", }; -static const struct soc_enum wm2200_aec_loopback = - SOC_ENUM_SINGLE(WM2200_DAC_AEC_CONTROL_1, - WM2200_AEC_LOOPBACK_SRC_SHIFT, - ARRAY_SIZE(wm2200_aec_loopback_texts), - wm2200_aec_loopback_texts); +static SOC_ENUM_SINGLE_DECL(wm2200_aec_loopback, + WM2200_DAC_AEC_CONTROL_1, + WM2200_AEC_LOOPBACK_SRC_SHIFT, + wm2200_aec_loopback_texts); static const struct snd_kcontrol_new wm2200_aec_loopback_mux = SOC_DAPM_ENUM("AEC Loopback", wm2200_aec_loopback); diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 4cf91deabc02..d3fa65fd9e85 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -14,6 +14,7 @@ #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/export.h> #include <linux/pm.h> #include <linux/gcd.h> #include <linux/gpio.h> @@ -505,21 +506,21 @@ static const char *wm5100_lhpf_mode_text[] = { "Low-pass", "High-pass" }; -static const struct soc_enum wm5100_lhpf1_mode = - SOC_ENUM_SINGLE(WM5100_HPLPF1_1, WM5100_LHPF1_MODE_SHIFT, 2, - wm5100_lhpf_mode_text); +static SOC_ENUM_SINGLE_DECL(wm5100_lhpf1_mode, + WM5100_HPLPF1_1, WM5100_LHPF1_MODE_SHIFT, + wm5100_lhpf_mode_text); -static const struct soc_enum wm5100_lhpf2_mode = - SOC_ENUM_SINGLE(WM5100_HPLPF2_1, WM5100_LHPF2_MODE_SHIFT, 2, - wm5100_lhpf_mode_text); +static SOC_ENUM_SINGLE_DECL(wm5100_lhpf2_mode, + WM5100_HPLPF2_1, WM5100_LHPF2_MODE_SHIFT, + wm5100_lhpf_mode_text); -static const struct soc_enum wm5100_lhpf3_mode = - SOC_ENUM_SINGLE(WM5100_HPLPF3_1, WM5100_LHPF3_MODE_SHIFT, 2, - wm5100_lhpf_mode_text); +static SOC_ENUM_SINGLE_DECL(wm5100_lhpf3_mode, + WM5100_HPLPF3_1, WM5100_LHPF3_MODE_SHIFT, + wm5100_lhpf_mode_text); -static const struct soc_enum wm5100_lhpf4_mode = - SOC_ENUM_SINGLE(WM5100_HPLPF4_1, WM5100_LHPF4_MODE_SHIFT, 2, - wm5100_lhpf_mode_text); +static SOC_ENUM_SINGLE_DECL(wm5100_lhpf4_mode, + WM5100_HPLPF4_1, WM5100_LHPF4_MODE_SHIFT, + wm5100_lhpf_mode_text); static const struct snd_kcontrol_new wm5100_snd_controls[] = { SOC_SINGLE("IN1 High Performance Switch", WM5100_IN1L_CONTROL, @@ -2099,6 +2100,7 @@ static void wm5100_micd_irq(struct wm5100_priv *wm5100) int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) { struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; if (jack) { wm5100->jack = jack; @@ -2116,9 +2118,14 @@ int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) WM5100_ACCDET_RATE_MASK); /* We need the charge pump to power MICBIAS */ - snd_soc_dapm_force_enable_pin(&codec->dapm, "CP2"); - snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_mutex_lock(dapm); + + snd_soc_dapm_force_enable_pin_unlocked(dapm, "CP2"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "SYSCLK"); + + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); /* We start off just enabling microphone detection - even a * plain headphone will trigger detection. @@ -2141,6 +2148,7 @@ int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) return 0; } +EXPORT_SYMBOL_GPL(wm5100_detect); static irqreturn_t wm5100_irq(int irq, void *data) { diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index a08e8bf6d07c..34109050ceed 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -582,7 +582,7 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = w->codec; struct arizona *arizona = dev_get_drvdata(codec->dev->parent); - struct regmap *regmap = codec->control_data; + struct regmap *regmap = arizona->regmap; const struct reg_default *patch = NULL; int i, patch_size; @@ -601,8 +601,8 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: if (patch) for (i = 0; i < patch_size; i++) - regmap_write(regmap, patch[i].reg, - patch[i].def); + regmap_write_async(regmap, patch[i].reg, + patch[i].def); break; default: @@ -622,13 +622,16 @@ static const unsigned int wm5102_osr_val[] = { static const struct soc_enum wm5102_hpout_osr[] = { SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L, - ARIZONA_OUT1_OSR_SHIFT, 0x7, 3, + ARIZONA_OUT1_OSR_SHIFT, 0x7, + ARRAY_SIZE(wm5102_osr_text), wm5102_osr_text, wm5102_osr_val), SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2L, - ARIZONA_OUT2_OSR_SHIFT, 0x7, 3, + ARIZONA_OUT2_OSR_SHIFT, 0x7, + ARRAY_SIZE(wm5102_osr_text), wm5102_osr_text, wm5102_osr_val), SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L, - ARIZONA_OUT3_OSR_SHIFT, 0x7, 3, + ARIZONA_OUT3_OSR_SHIFT, 0x7, + ARRAY_SIZE(wm5102_osr_text), wm5102_osr_text, wm5102_osr_val), }; @@ -685,15 +688,8 @@ ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), -SND_SOC_BYTES_MASK("EQ1 Coefficients", ARIZONA_EQ1_1, 21, - ARIZONA_EQ1_ENA_MASK), -SND_SOC_BYTES_MASK("EQ2 Coefficients", ARIZONA_EQ2_1, 21, - ARIZONA_EQ2_ENA_MASK), -SND_SOC_BYTES_MASK("EQ3 Coefficients", ARIZONA_EQ3_1, 21, - ARIZONA_EQ3_ENA_MASK), -SND_SOC_BYTES_MASK("EQ4 Coefficients", ARIZONA_EQ4_1, 21, - ARIZONA_EQ4_ENA_MASK), - +SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19), +SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0), SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, @@ -705,6 +701,8 @@ SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, 24, 0, eq_tlv), +SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19), +SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0), SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, @@ -716,6 +714,8 @@ SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, 24, 0, eq_tlv), +SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19), +SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0), SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, @@ -727,6 +727,8 @@ SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, 24, 0, eq_tlv), +SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19), +SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0), SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index c3c7396a6181..d7bf8848174a 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -30,19 +30,105 @@ #include <linux/mfd/arizona/registers.h> #include "arizona.h" +#include "wm_adsp.h" #include "wm5110.h" +#define WM5110_NUM_ADSP 4 + struct wm5110_priv { struct arizona_priv core; struct arizona_fll fll[2]; }; +static const struct wm_adsp_region wm5110_dsp1_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x100000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x180000 }, + { .type = WMFW_ADSP2_XM, .base = 0x190000 }, + { .type = WMFW_ADSP2_YM, .base = 0x1a8000 }, +}; + +static const struct wm_adsp_region wm5110_dsp2_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x200000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x280000 }, + { .type = WMFW_ADSP2_XM, .base = 0x290000 }, + { .type = WMFW_ADSP2_YM, .base = 0x2a8000 }, +}; + +static const struct wm_adsp_region wm5110_dsp3_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x300000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x380000 }, + { .type = WMFW_ADSP2_XM, .base = 0x390000 }, + { .type = WMFW_ADSP2_YM, .base = 0x3a8000 }, +}; + +static const struct wm_adsp_region wm5110_dsp4_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x400000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x480000 }, + { .type = WMFW_ADSP2_XM, .base = 0x490000 }, + { .type = WMFW_ADSP2_YM, .base = 0x4a8000 }, +}; + +static const struct wm_adsp_region *wm5110_dsp_regions[] = { + wm5110_dsp1_regions, + wm5110_dsp2_regions, + wm5110_dsp3_regions, + wm5110_dsp4_regions, +}; + static const struct reg_default wm5110_sysclk_revd_patch[] = { { 0x3093, 0x1001 }, { 0x30E3, 0x1301 }, { 0x3133, 0x1201 }, { 0x3183, 0x1501 }, { 0x31D3, 0x1401 }, + { 0x0049, 0x01ea }, + { 0x004a, 0x01f2 }, + { 0x0057, 0x01e7 }, + { 0x0058, 0x01fb }, + { 0x33ce, 0xc4f5 }, + { 0x33cf, 0x1361 }, + { 0x33d0, 0x0402 }, + { 0x33d1, 0x4700 }, + { 0x33d2, 0x026d }, + { 0x33d3, 0xff00 }, + { 0x33d4, 0x026d }, + { 0x33d5, 0x0101 }, + { 0x33d6, 0xc4f5 }, + { 0x33d7, 0x0361 }, + { 0x33d8, 0x0402 }, + { 0x33d9, 0x6701 }, + { 0x33da, 0xc4f5 }, + { 0x33db, 0x136f }, + { 0x33dc, 0xc4f5 }, + { 0x33dd, 0x134f }, + { 0x33de, 0xc4f5 }, + { 0x33df, 0x131f }, + { 0x33e0, 0x026d }, + { 0x33e1, 0x4f01 }, + { 0x33e2, 0x026d }, + { 0x33e3, 0xf100 }, + { 0x33e4, 0x026d }, + { 0x33e5, 0x0001 }, + { 0x33e6, 0xc4f5 }, + { 0x33e7, 0x0361 }, + { 0x33e8, 0x0402 }, + { 0x33e9, 0x6601 }, + { 0x33ea, 0xc4f5 }, + { 0x33eb, 0x136f }, + { 0x33ec, 0xc4f5 }, + { 0x33ed, 0x134f }, + { 0x33ee, 0xc4f5 }, + { 0x33ef, 0x131f }, + { 0x33f0, 0x026d }, + { 0x33f1, 0x4e01 }, + { 0x33f2, 0x026d }, + { 0x33f3, 0xf000 }, + { 0x33f6, 0xc4f5 }, + { 0x33f7, 0x1361 }, + { 0x33f8, 0x0402 }, + { 0x33f9, 0x4600 }, + { 0x33fa, 0x026d }, + { 0x33fb, 0xfe00 }, }; static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, @@ -50,7 +136,7 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = w->codec; struct arizona *arizona = dev_get_drvdata(codec->dev->parent); - struct regmap *regmap = codec->control_data; + struct regmap *regmap = arizona->regmap; const struct reg_default *patch = NULL; int i, patch_size; @@ -67,8 +153,8 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: if (patch) for (i = 0; i < patch_size; i++) - regmap_write(regmap, patch[i].reg, - patch[i].def); + regmap_write_async(regmap, patch[i].reg, + patch[i].def); break; default: @@ -117,6 +203,25 @@ SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum), + +SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2L HPF Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2R HPF Switch", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN3L HPF Switch", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN3R HPF Switch", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN4L HPF Switch", ARIZONA_IN4L_CONTROL, + ARIZONA_IN4L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN4R HPF Switch", ARIZONA_IN4R_CONTROL, + ARIZONA_IN4R_HPF_SHIFT, 1, 0), + SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, @@ -142,15 +247,8 @@ ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), -SND_SOC_BYTES_MASK("EQ1 Coefficients", ARIZONA_EQ1_1, 21, - ARIZONA_EQ1_ENA_MASK), -SND_SOC_BYTES_MASK("EQ2 Coefficients", ARIZONA_EQ2_1, 21, - ARIZONA_EQ2_ENA_MASK), -SND_SOC_BYTES_MASK("EQ3 Coefficients", ARIZONA_EQ3_1, 21, - ARIZONA_EQ3_ENA_MASK), -SND_SOC_BYTES_MASK("EQ4 Coefficients", ARIZONA_EQ4_1, 21, - ARIZONA_EQ4_ENA_MASK), - +SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19), +SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0), SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, @@ -162,6 +260,8 @@ SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, 24, 0, eq_tlv), +SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19), +SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0), SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, @@ -173,6 +273,8 @@ SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, 24, 0, eq_tlv), +SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19), +SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0), SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, @@ -184,6 +286,8 @@ SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, 24, 0, eq_tlv), +SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19), +SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0), SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, @@ -220,6 +324,14 @@ SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), +SOC_VALUE_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), +SOC_VALUE_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), +SOC_VALUE_ENUM("ISRC3 FSL", arizona_isrc_fsl[2]), +SOC_VALUE_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]), +SOC_VALUE_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]), +SOC_VALUE_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]), +SOC_VALUE_ENUM("ASRC RATE 1", arizona_asrc_rate1), + ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE), @@ -248,18 +360,12 @@ 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_SINGLE("HPOUT1 SC Protect Switch", ARIZONA_HP1_SHORT_CIRCUIT_CTRL, + ARIZONA_HP1_SC_ENA_SHIFT, 1, 0), +SOC_SINGLE("HPOUT2 SC Protect Switch", ARIZONA_HP2_SHORT_CIRCUIT_CTRL, + ARIZONA_HP2_SC_ENA_SHIFT, 1, 0), +SOC_SINGLE("HPOUT3 SC Protect Switch", ARIZONA_HP3_SHORT_CIRCUIT_CTRL, + ARIZONA_HP3_SC_ENA_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), @@ -293,23 +399,18 @@ 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, ARIZONA_SPK2R_MUTE_SHIFT, 1, 1), +SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0), +SOC_DOUBLE("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0), +SOC_DOUBLE("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0), + SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), @@ -343,6 +444,10 @@ ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), @@ -372,6 +477,22 @@ ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP1, ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP2L, ARIZONA_DSP2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP2R, ARIZONA_DSP2RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP2, ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP3L, ARIZONA_DSP3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP3R, ARIZONA_DSP3RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP3, ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP4L, ARIZONA_DSP4LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP4R, ARIZONA_DSP4RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP4, ARIZONA_DSP4AUX1MIX_INPUT_1_SOURCE); + ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE); @@ -402,6 +523,10 @@ ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); @@ -420,6 +545,36 @@ ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT3, ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT4, ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC3, ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC4, ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC3INT1, ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT2, ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT3, ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT4, ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC3DEC1, ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC2, ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC3, ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC4, ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE); + static const char *wm5110_aec_loopback_texts[] = { "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R", "SPKOUTL", "SPKOUTR", "SPKDAT1L", "SPKDAT1R", "SPKDAT2L", "SPKDAT2R", @@ -560,6 +715,65 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, NULL, 0), +WM_ADSP2("DSP1", 0), +WM_ADSP2("DSP2", 1), +WM_ADSP2("DSP3", 2), +WM_ADSP2("DSP4", 3), + +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT3", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT4", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC3", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC4", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3INT1", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT2", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT3", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT4", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3DEC1", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC2", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC3", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC4", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC3_ENA_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, &wm5110_aec_loopback_mux), @@ -602,11 +816,27 @@ SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, @@ -744,6 +974,10 @@ ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), +ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"), +ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"), +ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"), +ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"), ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), @@ -762,6 +996,41 @@ ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), +ARIZONA_DSP_WIDGETS(DSP1, "DSP1"), +ARIZONA_DSP_WIDGETS(DSP2, "DSP2"), +ARIZONA_DSP_WIDGETS(DSP3, "DSP3"), +ARIZONA_DSP_WIDGETS(DSP4, "DSP4"), + +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), +ARIZONA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"), +ARIZONA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), +ARIZONA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"), +ARIZONA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"), + +ARIZONA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"), +ARIZONA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"), +ARIZONA_MUX_WIDGETS(ISRC3DEC3, "ISRC3DEC3"), +ARIZONA_MUX_WIDGETS(ISRC3DEC4, "ISRC3DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"), +ARIZONA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"), +ARIZONA_MUX_WIDGETS(ISRC3INT3, "ISRC3INT3"), +ARIZONA_MUX_WIDGETS(ISRC3INT4, "ISRC3INT4"), + SND_SOC_DAPM_OUTPUT("HPOUT1L"), SND_SOC_DAPM_OUTPUT("HPOUT1R"), SND_SOC_DAPM_OUTPUT("HPOUT2L"), @@ -805,6 +1074,10 @@ SND_SOC_DAPM_OUTPUT("MICSUPP"), { name, "AIF1RX8", "AIF1RX8" }, \ { name, "AIF2RX1", "AIF2RX1" }, \ { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF2RX3", "AIF2RX3" }, \ + { name, "AIF2RX4", "AIF2RX4" }, \ + { name, "AIF2RX5", "AIF2RX5" }, \ + { name, "AIF2RX6", "AIF2RX6" }, \ { name, "AIF3RX1", "AIF3RX1" }, \ { name, "AIF3RX2", "AIF3RX2" }, \ { name, "SLIMRX1", "SLIMRX1" }, \ @@ -830,7 +1103,55 @@ SND_SOC_DAPM_OUTPUT("MICSUPP"), { name, "ASRC1L", "ASRC1L" }, \ { name, "ASRC1R", "ASRC1R" }, \ { name, "ASRC2L", "ASRC2L" }, \ - { name, "ASRC2R", "ASRC2R" } + { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2DEC3", "ISRC2DEC3" }, \ + { name, "ISRC2DEC4", "ISRC2DEC4" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ + { name, "ISRC2INT3", "ISRC2INT3" }, \ + { name, "ISRC2INT4", "ISRC2INT4" }, \ + { name, "ISRC3DEC1", "ISRC3DEC1" }, \ + { name, "ISRC3DEC2", "ISRC3DEC2" }, \ + { name, "ISRC3DEC3", "ISRC3DEC3" }, \ + { name, "ISRC3DEC4", "ISRC3DEC4" }, \ + { name, "ISRC3INT1", "ISRC3INT1" }, \ + { name, "ISRC3INT2", "ISRC3INT2" }, \ + { name, "ISRC3INT3", "ISRC3INT3" }, \ + { name, "ISRC3INT4", "ISRC3INT4" }, \ + { name, "DSP1.1", "DSP1" }, \ + { name, "DSP1.2", "DSP1" }, \ + { name, "DSP1.3", "DSP1" }, \ + { name, "DSP1.4", "DSP1" }, \ + { name, "DSP1.5", "DSP1" }, \ + { name, "DSP1.6", "DSP1" }, \ + { name, "DSP2.1", "DSP2" }, \ + { name, "DSP2.2", "DSP2" }, \ + { name, "DSP2.3", "DSP2" }, \ + { name, "DSP2.4", "DSP2" }, \ + { name, "DSP2.5", "DSP2" }, \ + { name, "DSP2.6", "DSP2" }, \ + { name, "DSP3.1", "DSP3" }, \ + { name, "DSP3.2", "DSP3" }, \ + { name, "DSP3.3", "DSP3" }, \ + { name, "DSP3.4", "DSP3" }, \ + { name, "DSP3.5", "DSP3" }, \ + { name, "DSP3.6", "DSP3" }, \ + { name, "DSP4.1", "DSP4" }, \ + { name, "DSP4.2", "DSP4" }, \ + { name, "DSP4.3", "DSP4" }, \ + { name, "DSP4.4", "DSP4" }, \ + { name, "DSP4.5", "DSP4" }, \ + { name, "DSP4.6", "DSP4" } static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "AIF2 Capture", NULL, "DBVDD2" }, @@ -902,9 +1223,17 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "AIF2 Capture", NULL, "AIF2TX1" }, { "AIF2 Capture", NULL, "AIF2TX2" }, + { "AIF2 Capture", NULL, "AIF2TX3" }, + { "AIF2 Capture", NULL, "AIF2TX4" }, + { "AIF2 Capture", NULL, "AIF2TX5" }, + { "AIF2 Capture", NULL, "AIF2TX6" }, { "AIF2RX1", NULL, "AIF2 Playback" }, { "AIF2RX2", NULL, "AIF2 Playback" }, + { "AIF2RX3", NULL, "AIF2 Playback" }, + { "AIF2RX4", NULL, "AIF2 Playback" }, + { "AIF2RX5", NULL, "AIF2 Playback" }, + { "AIF2RX6", NULL, "AIF2 Playback" }, { "AIF3 Capture", NULL, "AIF3TX1" }, { "AIF3 Capture", NULL, "AIF3TX2" }, @@ -988,6 +1317,10 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"), + ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"), + ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"), + ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"), ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), @@ -1024,6 +1357,41 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), + ARIZONA_DSP_ROUTES("DSP1"), + ARIZONA_DSP_ROUTES("DSP2"), + ARIZONA_DSP_ROUTES("DSP3"), + ARIZONA_DSP_ROUTES("DSP4"), + + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + ARIZONA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"), + ARIZONA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + ARIZONA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"), + ARIZONA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"), + + ARIZONA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"), + ARIZONA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"), + ARIZONA_MUX_ROUTES("ISRC3INT3", "ISRC3INT3"), + ARIZONA_MUX_ROUTES("ISRC3INT4", "ISRC3INT4"), + + ARIZONA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"), + ARIZONA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"), + ARIZONA_MUX_ROUTES("ISRC3DEC3", "ISRC3DEC3"), + ARIZONA_MUX_ROUTES("ISRC3DEC4", "ISRC3DEC4"), + { "AEC Loopback", "HPOUT1L", "OUT1L" }, { "AEC Loopback", "HPOUT1R", "OUT1R" }, { "HPOUT1L", NULL, "OUT1L" }, @@ -1037,7 +1405,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" }, @@ -1120,14 +1488,14 @@ static struct snd_soc_dai_driver wm5110_dai[] = { .playback = { .stream_name = "AIF2 Playback", .channels_min = 1, - .channels_max = 2, + .channels_max = 6, .rates = WM5110_RATES, .formats = WM5110_FORMATS, }, .capture = { .stream_name = "AIF2 Capture", .channels_min = 1, - .channels_max = 2, + .channels_max = 6, .rates = WM5110_RATES, .formats = WM5110_FORMATS, }, @@ -1229,6 +1597,10 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) arizona_init_spk(codec); arizona_init_gpio(codec); + ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 8); + if (ret != 0) + return ret; + snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); priv->core.arizona->dapm = &codec->dapm; @@ -1283,7 +1655,7 @@ static int wm5110_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); struct wm5110_priv *wm5110; - int i; + int i, ret; wm5110 = devm_kzalloc(&pdev->dev, sizeof(struct wm5110_priv), GFP_KERNEL); @@ -1294,6 +1666,24 @@ static int wm5110_probe(struct platform_device *pdev) wm5110->core.arizona = arizona; wm5110->core.num_inputs = 8; + for (i = 0; i < WM5110_NUM_ADSP; i++) { + wm5110->core.adsp[i].part = "wm5110"; + wm5110->core.adsp[i].num = i + 1; + wm5110->core.adsp[i].type = WMFW_ADSP2; + wm5110->core.adsp[i].dev = arizona->dev; + wm5110->core.adsp[i].regmap = arizona->regmap; + + wm5110->core.adsp[i].base = ARIZONA_DSP1_CONTROL_1 + + (0x100 * i); + wm5110->core.adsp[i].mem = wm5110_dsp_regions[i]; + wm5110->core.adsp[i].num_mems + = ARRAY_SIZE(wm5110_dsp1_regions); + + ret = wm_adsp2_init(&wm5110->core.adsp[i], false); + if (ret != 0) + return ret; + } + for (i = 0; i < ARRAY_SIZE(wm5110->fll); i++) wm5110->fll[i].vco_mult = 3; @@ -1304,6 +1694,12 @@ static int wm5110_probe(struct platform_device *pdev) ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, &wm5110->fll[1]); + /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */ + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2, + ARIZONA_SAMPLE_RATE_2_MASK, 0x11); + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3, + ARIZONA_SAMPLE_RATE_3_MASK, 0x12); + for (i = 0; i < ARRAY_SIZE(wm5110_dai); i++) arizona_init_dai(&wm5110->core, i); diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 48dc7d2fee36..6d684d934f4d 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -117,19 +117,23 @@ static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, static const char *wm8400_digital_sidetone[] = {"None", "Left ADC", "Right ADC", "Reserved"}; -static const struct soc_enum wm8400_left_digital_sidetone_enum = -SOC_ENUM_SINGLE(WM8400_DIGITAL_SIDE_TONE, - WM8400_ADC_TO_DACL_SHIFT, 2, wm8400_digital_sidetone); +static SOC_ENUM_SINGLE_DECL(wm8400_left_digital_sidetone_enum, + WM8400_DIGITAL_SIDE_TONE, + WM8400_ADC_TO_DACL_SHIFT, + wm8400_digital_sidetone); -static const struct soc_enum wm8400_right_digital_sidetone_enum = -SOC_ENUM_SINGLE(WM8400_DIGITAL_SIDE_TONE, - WM8400_ADC_TO_DACR_SHIFT, 2, wm8400_digital_sidetone); +static SOC_ENUM_SINGLE_DECL(wm8400_right_digital_sidetone_enum, + WM8400_DIGITAL_SIDE_TONE, + WM8400_ADC_TO_DACR_SHIFT, + wm8400_digital_sidetone); static const char *wm8400_adcmode[] = {"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"}; -static const struct soc_enum wm8400_right_adcmode_enum = -SOC_ENUM_SINGLE(WM8400_ADC_CTRL, WM8400_ADC_HPF_CUT_SHIFT, 3, wm8400_adcmode); +static SOC_ENUM_SINGLE_DECL(wm8400_right_adcmode_enum, + WM8400_ADC_CTRL, + WM8400_ADC_HPF_CUT_SHIFT, + wm8400_adcmode); static const struct snd_kcontrol_new wm8400_snd_controls[] = { /* INMIXL */ @@ -422,9 +426,10 @@ SOC_DAPM_SINGLE("RINPGA34 Switch", WM8400_INPUT_MIXER3, WM8400_L34MNB_SHIFT, static const char *wm8400_ainlmux[] = {"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"}; -static const struct soc_enum wm8400_ainlmux_enum = -SOC_ENUM_SINGLE( WM8400_INPUT_MIXER1, WM8400_AINLMODE_SHIFT, - ARRAY_SIZE(wm8400_ainlmux), wm8400_ainlmux); +static SOC_ENUM_SINGLE_DECL(wm8400_ainlmux_enum, + WM8400_INPUT_MIXER1, + WM8400_AINLMODE_SHIFT, + wm8400_ainlmux); static const struct snd_kcontrol_new wm8400_dapm_ainlmux_controls = SOC_DAPM_ENUM("Route", wm8400_ainlmux_enum); @@ -435,9 +440,10 @@ SOC_DAPM_ENUM("Route", wm8400_ainlmux_enum); static const char *wm8400_ainrmux[] = {"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"}; -static const struct soc_enum wm8400_ainrmux_enum = -SOC_ENUM_SINGLE( WM8400_INPUT_MIXER1, WM8400_AINRMODE_SHIFT, - ARRAY_SIZE(wm8400_ainrmux), wm8400_ainrmux); +static SOC_ENUM_SINGLE_DECL(wm8400_ainrmux_enum, + WM8400_INPUT_MIXER1, + WM8400_AINRMODE_SHIFT, + wm8400_ainrmux); static const struct snd_kcontrol_new wm8400_dapm_ainrmux_controls = SOC_DAPM_ENUM("Route", wm8400_ainrmux_enum); 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..5dfd571b1a03 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -75,8 +75,8 @@ static const char *wm8523_zd_count_text[] = { "2048", }; -static const struct soc_enum wm8523_zc_count = - SOC_ENUM_SINGLE(WM8523_ZERO_DETECT, 0, 2, wm8523_zd_count_text); +static SOC_ENUM_SINGLE_DECL(wm8523_zc_count, WM8523_ZERO_DETECT, 0, + wm8523_zd_count_text); static const struct snd_kcontrol_new wm8523_controls[] = { SOC_DOUBLE_R_TLV("Playback Volume", WM8523_DAC_GAINL, WM8523_DAC_GAINR, @@ -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..6efcc40a7cb3 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -201,7 +201,7 @@ static void wm8711_shutdown(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; /* deactivate */ - if (!codec->active) { + if (!snd_soc_codec_is_active(codec)) { udelay(50); snd_soc_write(codec, WM8711_ACTIVE, 0x0); } @@ -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..d9655f981df1 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -83,8 +83,8 @@ static bool wm8731_writeable(struct device *dev, unsigned int reg) static const char *wm8731_input_select[] = {"Line In", "Mic"}; -static const struct soc_enum wm8731_insel_enum = - SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select); +static SOC_ENUM_SINGLE_DECL(wm8731_insel_enum, + WM8731_APANA, 2, wm8731_input_select); static int wm8731_deemph[] = { 0, 32000, 44100, 48000 }; @@ -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..dd02ebf88015 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -44,7 +44,7 @@ struct wm8741_priv { struct regmap *regmap; struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES]; unsigned int sysclk; - struct snd_pcm_hw_constraint_list *sysclk_constraints; + const struct snd_pcm_hw_constraint_list *sysclk_constraints; }; static const struct reg_default wm8741_reg_defaults[] = { @@ -122,74 +122,74 @@ static struct { { 6, 768 }, }; -static unsigned int rates_11289[] = { +static const unsigned int rates_11289[] = { 44100, 88235, }; -static struct snd_pcm_hw_constraint_list constraints_11289 = { +static const struct snd_pcm_hw_constraint_list constraints_11289 = { .count = ARRAY_SIZE(rates_11289), .list = rates_11289, }; -static unsigned int rates_12288[] = { +static const unsigned int rates_12288[] = { 32000, 48000, 96000, }; -static struct snd_pcm_hw_constraint_list constraints_12288 = { +static const struct snd_pcm_hw_constraint_list constraints_12288 = { .count = ARRAY_SIZE(rates_12288), .list = rates_12288, }; -static unsigned int rates_16384[] = { +static const unsigned int rates_16384[] = { 32000, }; -static struct snd_pcm_hw_constraint_list constraints_16384 = { +static const struct snd_pcm_hw_constraint_list constraints_16384 = { .count = ARRAY_SIZE(rates_16384), .list = rates_16384, }; -static unsigned int rates_16934[] = { +static const unsigned int rates_16934[] = { 44100, 88235, }; -static struct snd_pcm_hw_constraint_list constraints_16934 = { +static const struct snd_pcm_hw_constraint_list constraints_16934 = { .count = ARRAY_SIZE(rates_16934), .list = rates_16934, }; -static unsigned int rates_18432[] = { +static const unsigned int rates_18432[] = { 48000, 96000, }; -static struct snd_pcm_hw_constraint_list constraints_18432 = { +static const struct snd_pcm_hw_constraint_list constraints_18432 = { .count = ARRAY_SIZE(rates_18432), .list = rates_18432, }; -static unsigned int rates_22579[] = { +static const unsigned int rates_22579[] = { 44100, 88235, 1764000 }; -static struct snd_pcm_hw_constraint_list constraints_22579 = { +static const struct snd_pcm_hw_constraint_list constraints_22579 = { .count = ARRAY_SIZE(rates_22579), .list = rates_22579, }; -static unsigned int rates_24576[] = { +static const unsigned int rates_24576[] = { 32000, 48000, 96000, 192000 }; -static struct snd_pcm_hw_constraint_list constraints_24576 = { +static const struct snd_pcm_hw_constraint_list constraints_24576 = { .count = ARRAY_SIZE(rates_24576), .list = rates_24576, }; -static unsigned int rates_36864[] = { +static const unsigned int rates_36864[] = { 48000, 96000, 19200 }; -static struct snd_pcm_hw_constraint_list constraints_36864 = { +static const struct snd_pcm_hw_constraint_list constraints_36864 = { .count = ARRAY_SIZE(rates_36864), .list = rates_36864, }; @@ -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..6a6855d8b8ea 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -251,7 +251,7 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol, if (wm8753->dai_func == ucontrol->value.integer.value[0]) return 0; - if (codec->active) + if (snd_soc_codec_is_active(codec)) return -EBUSY; ioctl = snd_soc_read(codec, WM8753_IOCTL); @@ -1314,7 +1314,7 @@ static int wm8753_mute(struct snd_soc_dai *dai, int mute) /* the digital mute covers the HiFi and Voice DAC's on the WM8753. * make sure we check if they are not both active when we mute */ if (mute && wm8753->dai_func == 1) { - if (!codec->active) + if (!snd_soc_codec_is_active(codec)) snd_soc_write(codec, WM8753_DAC, mute_reg | 0x8); } else { if (mute) @@ -1440,7 +1440,6 @@ static void wm8753_work(struct work_struct *work) static int wm8753_suspend(struct snd_soc_codec *codec) { wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF); - codec->cache_sync = 1; return 0; } @@ -1596,7 +1595,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 +1652,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 +1672,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/wm8770.c b/sound/soc/codecs/wm8770.c index 89a18d82f303..5bce21013485 100644 --- a/sound/soc/codecs/wm8770.c +++ b/sound/soc/codecs/wm8770.c @@ -196,8 +196,8 @@ static const char *ain_text[] = { "AIN5", "AIN6", "AIN7", "AIN8" }; -static const struct soc_enum ain_enum = - SOC_ENUM_DOUBLE(WM8770_ADCMUX, 0, 4, 8, ain_text); +static SOC_ENUM_DOUBLE_DECL(ain_enum, + WM8770_ADCMUX, 0, 4, ain_text); static const struct snd_kcontrol_new ain_mux = SOC_DAPM_ENUM("Capture Mux", ain_enum); 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..72d12bbe1a56 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -92,7 +92,7 @@ WM8804_REGULATOR_EVENT(0) WM8804_REGULATOR_EVENT(1) static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; -static const SOC_ENUM_SINGLE_EXT_DECL(txsrc, txsrc_text); +static SOC_ENUM_SINGLE_EXT_DECL(txsrc, txsrc_text); static const struct snd_kcontrol_new wm8804_snd_controls[] = { SOC_ENUM_EXT("Input Source", txsrc, txsrc_get, txsrc_put), @@ -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..43c2201cb901 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -304,53 +304,53 @@ static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1); static const char *mic_bias_level_txt[] = { "0.9*AVDD", "0.65*AVDD" }; -static const struct soc_enum mic_bias_level = -SOC_ENUM_SINGLE(WM8900_REG_INCTL, 8, 2, mic_bias_level_txt); +static SOC_ENUM_SINGLE_DECL(mic_bias_level, + WM8900_REG_INCTL, 8, mic_bias_level_txt); static const char *dac_mute_rate_txt[] = { "Fast", "Slow" }; -static const struct soc_enum dac_mute_rate = -SOC_ENUM_SINGLE(WM8900_REG_DACCTRL, 7, 2, dac_mute_rate_txt); +static SOC_ENUM_SINGLE_DECL(dac_mute_rate, + WM8900_REG_DACCTRL, 7, dac_mute_rate_txt); static const char *dac_deemphasis_txt[] = { "Disabled", "32kHz", "44.1kHz", "48kHz" }; -static const struct soc_enum dac_deemphasis = -SOC_ENUM_SINGLE(WM8900_REG_DACCTRL, 4, 4, dac_deemphasis_txt); +static SOC_ENUM_SINGLE_DECL(dac_deemphasis, + WM8900_REG_DACCTRL, 4, dac_deemphasis_txt); static const char *adc_hpf_cut_txt[] = { "Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3" }; -static const struct soc_enum adc_hpf_cut = -SOC_ENUM_SINGLE(WM8900_REG_ADCCTRL, 5, 4, adc_hpf_cut_txt); +static SOC_ENUM_SINGLE_DECL(adc_hpf_cut, + WM8900_REG_ADCCTRL, 5, adc_hpf_cut_txt); static const char *lr_txt[] = { "Left", "Right" }; -static const struct soc_enum aifl_src = -SOC_ENUM_SINGLE(WM8900_REG_AUDIO1, 15, 2, lr_txt); +static SOC_ENUM_SINGLE_DECL(aifl_src, + WM8900_REG_AUDIO1, 15, lr_txt); -static const struct soc_enum aifr_src = -SOC_ENUM_SINGLE(WM8900_REG_AUDIO1, 14, 2, lr_txt); +static SOC_ENUM_SINGLE_DECL(aifr_src, + WM8900_REG_AUDIO1, 14, lr_txt); -static const struct soc_enum dacl_src = -SOC_ENUM_SINGLE(WM8900_REG_AUDIO2, 15, 2, lr_txt); +static SOC_ENUM_SINGLE_DECL(dacl_src, + WM8900_REG_AUDIO2, 15, lr_txt); -static const struct soc_enum dacr_src = -SOC_ENUM_SINGLE(WM8900_REG_AUDIO2, 14, 2, lr_txt); +static SOC_ENUM_SINGLE_DECL(dacr_src, + WM8900_REG_AUDIO2, 14, lr_txt); static const char *sidetone_txt[] = { "Disabled", "Left ADC", "Right ADC" }; -static const struct soc_enum dacl_sidetone = -SOC_ENUM_SINGLE(WM8900_REG_SIDETONE, 2, 3, sidetone_txt); +static SOC_ENUM_SINGLE_DECL(dacl_sidetone, + WM8900_REG_SIDETONE, 2, sidetone_txt); -static const struct soc_enum dacr_sidetone = -SOC_ENUM_SINGLE(WM8900_REG_SIDETONE, 0, 3, sidetone_txt); +static SOC_ENUM_SINGLE_DECL(dacr_sidetone, + WM8900_REG_SIDETONE, 0, sidetone_txt); static const struct snd_kcontrol_new wm8900_snd_controls[] = { SOC_ENUM("Mic Bias Level", mic_bias_level), @@ -496,8 +496,8 @@ SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INCTL, 0, 1, 0), static const char *wm8900_lp_mux[] = { "Disabled", "Enabled" }; -static const struct soc_enum wm8900_lineout2_lp_mux = -SOC_ENUM_SINGLE(WM8900_REG_LOUTMIXCTL1, 1, 2, wm8900_lp_mux); +static SOC_ENUM_SINGLE_DECL(wm8900_lineout2_lp_mux, + WM8900_REG_LOUTMIXCTL1, 1, wm8900_lp_mux); static const struct snd_kcontrol_new wm8900_lineout2_lp = SOC_DAPM_ENUM("Route", wm8900_lineout2_lp_mux); @@ -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/wm8903.c b/sound/soc/codecs/wm8903.c index eebcb1da3b7b..b82b70a3b3d3 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -489,28 +489,28 @@ static const char *hpf_mode_text[] = { "Hi-fi", "Voice 1", "Voice 2", "Voice 3" }; -static const struct soc_enum hpf_mode = - SOC_ENUM_SINGLE(WM8903_ADC_DIGITAL_0, 5, 4, hpf_mode_text); +static SOC_ENUM_SINGLE_DECL(hpf_mode, + WM8903_ADC_DIGITAL_0, 5, hpf_mode_text); static const char *osr_text[] = { "Low power", "High performance" }; -static const struct soc_enum adc_osr = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_ADC_0, 0, 2, osr_text); +static SOC_ENUM_SINGLE_DECL(adc_osr, + WM8903_ANALOGUE_ADC_0, 0, osr_text); -static const struct soc_enum dac_osr = - SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 0, 2, osr_text); +static SOC_ENUM_SINGLE_DECL(dac_osr, + WM8903_DAC_DIGITAL_1, 0, osr_text); static const char *drc_slope_text[] = { "1", "1/2", "1/4", "1/8", "1/16", "0" }; -static const struct soc_enum drc_slope_r0 = - SOC_ENUM_SINGLE(WM8903_DRC_2, 3, 6, drc_slope_text); +static SOC_ENUM_SINGLE_DECL(drc_slope_r0, + WM8903_DRC_2, 3, drc_slope_text); -static const struct soc_enum drc_slope_r1 = - SOC_ENUM_SINGLE(WM8903_DRC_2, 0, 6, drc_slope_text); +static SOC_ENUM_SINGLE_DECL(drc_slope_r1, + WM8903_DRC_2, 0, drc_slope_text); static const char *drc_attack_text[] = { "instantaneous", @@ -518,125 +518,125 @@ static const char *drc_attack_text[] = { "46.4ms", "92.8ms", "185.6ms" }; -static const struct soc_enum drc_attack = - SOC_ENUM_SINGLE(WM8903_DRC_1, 12, 11, drc_attack_text); +static SOC_ENUM_SINGLE_DECL(drc_attack, + WM8903_DRC_1, 12, drc_attack_text); static const char *drc_decay_text[] = { "186ms", "372ms", "743ms", "1.49s", "2.97s", "5.94s", "11.89s", "23.87s", "47.56s" }; -static const struct soc_enum drc_decay = - SOC_ENUM_SINGLE(WM8903_DRC_1, 8, 9, drc_decay_text); +static SOC_ENUM_SINGLE_DECL(drc_decay, + WM8903_DRC_1, 8, drc_decay_text); static const char *drc_ff_delay_text[] = { "5 samples", "9 samples" }; -static const struct soc_enum drc_ff_delay = - SOC_ENUM_SINGLE(WM8903_DRC_0, 5, 2, drc_ff_delay_text); +static SOC_ENUM_SINGLE_DECL(drc_ff_delay, + WM8903_DRC_0, 5, drc_ff_delay_text); static const char *drc_qr_decay_text[] = { "0.725ms", "1.45ms", "5.8ms" }; -static const struct soc_enum drc_qr_decay = - SOC_ENUM_SINGLE(WM8903_DRC_1, 4, 3, drc_qr_decay_text); +static SOC_ENUM_SINGLE_DECL(drc_qr_decay, + WM8903_DRC_1, 4, drc_qr_decay_text); static const char *drc_smoothing_text[] = { "Low", "Medium", "High" }; -static const struct soc_enum drc_smoothing = - SOC_ENUM_SINGLE(WM8903_DRC_0, 11, 3, drc_smoothing_text); +static SOC_ENUM_SINGLE_DECL(drc_smoothing, + WM8903_DRC_0, 11, drc_smoothing_text); static const char *soft_mute_text[] = { "Fast (fs/2)", "Slow (fs/32)" }; -static const struct soc_enum soft_mute = - SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 10, 2, soft_mute_text); +static SOC_ENUM_SINGLE_DECL(soft_mute, + WM8903_DAC_DIGITAL_1, 10, soft_mute_text); static const char *mute_mode_text[] = { "Hard", "Soft" }; -static const struct soc_enum mute_mode = - SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 9, 2, mute_mode_text); +static SOC_ENUM_SINGLE_DECL(mute_mode, + WM8903_DAC_DIGITAL_1, 9, mute_mode_text); static const char *companding_text[] = { "ulaw", "alaw" }; -static const struct soc_enum dac_companding = - SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 0, 2, companding_text); +static SOC_ENUM_SINGLE_DECL(dac_companding, + WM8903_AUDIO_INTERFACE_0, 0, companding_text); -static const struct soc_enum adc_companding = - SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 2, 2, companding_text); +static SOC_ENUM_SINGLE_DECL(adc_companding, + WM8903_AUDIO_INTERFACE_0, 2, companding_text); static const char *input_mode_text[] = { "Single-Ended", "Differential Line", "Differential Mic" }; -static const struct soc_enum linput_mode_enum = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 0, 3, input_mode_text); +static SOC_ENUM_SINGLE_DECL(linput_mode_enum, + WM8903_ANALOGUE_LEFT_INPUT_1, 0, input_mode_text); -static const struct soc_enum rinput_mode_enum = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 0, 3, input_mode_text); +static SOC_ENUM_SINGLE_DECL(rinput_mode_enum, + WM8903_ANALOGUE_RIGHT_INPUT_1, 0, input_mode_text); static const char *linput_mux_text[] = { "IN1L", "IN2L", "IN3L" }; -static const struct soc_enum linput_enum = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 2, 3, linput_mux_text); +static SOC_ENUM_SINGLE_DECL(linput_enum, + WM8903_ANALOGUE_LEFT_INPUT_1, 2, linput_mux_text); -static const struct soc_enum linput_inv_enum = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 4, 3, linput_mux_text); +static SOC_ENUM_SINGLE_DECL(linput_inv_enum, + WM8903_ANALOGUE_LEFT_INPUT_1, 4, linput_mux_text); static const char *rinput_mux_text[] = { "IN1R", "IN2R", "IN3R" }; -static const struct soc_enum rinput_enum = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 2, 3, rinput_mux_text); +static SOC_ENUM_SINGLE_DECL(rinput_enum, + WM8903_ANALOGUE_RIGHT_INPUT_1, 2, rinput_mux_text); -static const struct soc_enum rinput_inv_enum = - SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 4, 3, rinput_mux_text); +static SOC_ENUM_SINGLE_DECL(rinput_inv_enum, + WM8903_ANALOGUE_RIGHT_INPUT_1, 4, rinput_mux_text); static const char *sidetone_text[] = { "None", "Left", "Right" }; -static const struct soc_enum lsidetone_enum = - SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 2, 3, sidetone_text); +static SOC_ENUM_SINGLE_DECL(lsidetone_enum, + WM8903_DAC_DIGITAL_0, 2, sidetone_text); -static const struct soc_enum rsidetone_enum = - SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text); +static SOC_ENUM_SINGLE_DECL(rsidetone_enum, + WM8903_DAC_DIGITAL_0, 0, sidetone_text); static const char *adcinput_text[] = { "ADC", "DMIC" }; -static const struct soc_enum adcinput_enum = - SOC_ENUM_SINGLE(WM8903_CLOCK_RATE_TEST_4, 9, 2, adcinput_text); +static SOC_ENUM_SINGLE_DECL(adcinput_enum, + WM8903_CLOCK_RATE_TEST_4, 9, adcinput_text); static const char *aif_text[] = { "Left", "Right" }; -static const struct soc_enum lcapture_enum = - SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 7, 2, aif_text); +static SOC_ENUM_SINGLE_DECL(lcapture_enum, + WM8903_AUDIO_INTERFACE_0, 7, aif_text); -static const struct soc_enum rcapture_enum = - SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 6, 2, aif_text); +static SOC_ENUM_SINGLE_DECL(rcapture_enum, + WM8903_AUDIO_INTERFACE_0, 6, aif_text); -static const struct soc_enum lplay_enum = - SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 5, 2, aif_text); +static SOC_ENUM_SINGLE_DECL(lplay_enum, + WM8903_AUDIO_INTERFACE_0, 5, aif_text); -static const struct soc_enum rplay_enum = - SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 4, 2, aif_text); +static SOC_ENUM_SINGLE_DECL(rplay_enum, + WM8903_AUDIO_INTERFACE_0, 4, aif_text); static const struct snd_kcontrol_new wm8903_snd_controls[] = { diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 3938fb1c203e..27299cda0e99 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -552,18 +552,20 @@ static const char *input_mode_text[] = { "Single-Ended", "Differential Line", "Differential Mic" }; -static const struct soc_enum lin_mode = - SOC_ENUM_SINGLE(WM8904_ANALOGUE_LEFT_INPUT_1, 0, 3, input_mode_text); +static SOC_ENUM_SINGLE_DECL(lin_mode, + WM8904_ANALOGUE_LEFT_INPUT_1, 0, + input_mode_text); -static const struct soc_enum rin_mode = - SOC_ENUM_SINGLE(WM8904_ANALOGUE_RIGHT_INPUT_1, 0, 3, input_mode_text); +static SOC_ENUM_SINGLE_DECL(rin_mode, + WM8904_ANALOGUE_RIGHT_INPUT_1, 0, + input_mode_text); static const char *hpf_mode_text[] = { "Hi-fi", "Voice 1", "Voice 2", "Voice 3" }; -static const struct soc_enum hpf_mode = - SOC_ENUM_SINGLE(WM8904_ADC_DIGITAL_0, 5, 4, hpf_mode_text); +static SOC_ENUM_SINGLE_DECL(hpf_mode, WM8904_ADC_DIGITAL_0, 5, + hpf_mode_text); static int wm8904_adc_osr_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -611,8 +613,7 @@ static const char *drc_path_text[] = { "ADC", "DAC" }; -static const struct soc_enum drc_path = - SOC_ENUM_SINGLE(WM8904_DRC_0, 14, 2, drc_path_text); +static SOC_ENUM_SINGLE_DECL(drc_path, WM8904_DRC_0, 14, drc_path_text); static const struct snd_kcontrol_new wm8904_dac_snd_controls[] = { SOC_SINGLE_TLV("Digital Playback Boost Volume", @@ -858,14 +859,14 @@ static const char *lin_text[] = { "IN1L", "IN2L", "IN3L" }; -static const struct soc_enum lin_enum = - SOC_ENUM_SINGLE(WM8904_ANALOGUE_LEFT_INPUT_1, 2, 3, lin_text); +static SOC_ENUM_SINGLE_DECL(lin_enum, WM8904_ANALOGUE_LEFT_INPUT_1, 2, + lin_text); static const struct snd_kcontrol_new lin_mux = SOC_DAPM_ENUM("Left Capture Mux", lin_enum); -static const struct soc_enum lin_inv_enum = - SOC_ENUM_SINGLE(WM8904_ANALOGUE_LEFT_INPUT_1, 4, 3, lin_text); +static SOC_ENUM_SINGLE_DECL(lin_inv_enum, WM8904_ANALOGUE_LEFT_INPUT_1, 4, + lin_text); static const struct snd_kcontrol_new lin_inv_mux = SOC_DAPM_ENUM("Left Capture Inveting Mux", lin_inv_enum); @@ -874,14 +875,14 @@ static const char *rin_text[] = { "IN1R", "IN2R", "IN3R" }; -static const struct soc_enum rin_enum = - SOC_ENUM_SINGLE(WM8904_ANALOGUE_RIGHT_INPUT_1, 2, 3, rin_text); +static SOC_ENUM_SINGLE_DECL(rin_enum, WM8904_ANALOGUE_RIGHT_INPUT_1, 2, + rin_text); static const struct snd_kcontrol_new rin_mux = SOC_DAPM_ENUM("Right Capture Mux", rin_enum); -static const struct soc_enum rin_inv_enum = - SOC_ENUM_SINGLE(WM8904_ANALOGUE_RIGHT_INPUT_1, 4, 3, rin_text); +static SOC_ENUM_SINGLE_DECL(rin_inv_enum, WM8904_ANALOGUE_RIGHT_INPUT_1, 4, + rin_text); static const struct snd_kcontrol_new rin_inv_mux = SOC_DAPM_ENUM("Right Capture Inveting Mux", rin_inv_enum); @@ -890,26 +891,26 @@ static const char *aif_text[] = { "Left", "Right" }; -static const struct soc_enum aifoutl_enum = - SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 7, 2, aif_text); +static SOC_ENUM_SINGLE_DECL(aifoutl_enum, WM8904_AUDIO_INTERFACE_0, 7, + aif_text); static const struct snd_kcontrol_new aifoutl_mux = SOC_DAPM_ENUM("AIFOUTL Mux", aifoutl_enum); -static const struct soc_enum aifoutr_enum = - SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 6, 2, aif_text); +static SOC_ENUM_SINGLE_DECL(aifoutr_enum, WM8904_AUDIO_INTERFACE_0, 6, + aif_text); static const struct snd_kcontrol_new aifoutr_mux = SOC_DAPM_ENUM("AIFOUTR Mux", aifoutr_enum); -static const struct soc_enum aifinl_enum = - SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 5, 2, aif_text); +static SOC_ENUM_SINGLE_DECL(aifinl_enum, WM8904_AUDIO_INTERFACE_0, 5, + aif_text); static const struct snd_kcontrol_new aifinl_mux = SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum); -static const struct soc_enum aifinr_enum = - SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 4, 2, aif_text); +static SOC_ENUM_SINGLE_DECL(aifinr_enum, WM8904_AUDIO_INTERFACE_0, 4, + aif_text); static const struct snd_kcontrol_new aifinr_mux = SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum); @@ -991,26 +992,26 @@ static const char *out_mux_text[] = { "DAC", "Bypass" }; -static const struct soc_enum hpl_enum = - SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 3, 2, out_mux_text); +static SOC_ENUM_SINGLE_DECL(hpl_enum, WM8904_ANALOGUE_OUT12_ZC, 3, + out_mux_text); static const struct snd_kcontrol_new hpl_mux = SOC_DAPM_ENUM("HPL Mux", hpl_enum); -static const struct soc_enum hpr_enum = - SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 2, 2, out_mux_text); +static SOC_ENUM_SINGLE_DECL(hpr_enum, WM8904_ANALOGUE_OUT12_ZC, 2, + out_mux_text); static const struct snd_kcontrol_new hpr_mux = SOC_DAPM_ENUM("HPR Mux", hpr_enum); -static const struct soc_enum linel_enum = - SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 1, 2, out_mux_text); +static SOC_ENUM_SINGLE_DECL(linel_enum, WM8904_ANALOGUE_OUT12_ZC, 1, + out_mux_text); static const struct snd_kcontrol_new linel_mux = SOC_DAPM_ENUM("LINEL Mux", linel_enum); -static const struct soc_enum liner_enum = - SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 0, 2, out_mux_text); +static SOC_ENUM_SINGLE_DECL(liner_enum, WM8904_ANALOGUE_OUT12_ZC, 0, + out_mux_text); static const struct snd_kcontrol_new liner_mux = SOC_DAPM_ENUM("LINER Mux", liner_enum); @@ -1019,14 +1020,14 @@ static const char *sidetone_text[] = { "None", "Left", "Right" }; -static const struct soc_enum dacl_sidetone_enum = - SOC_ENUM_SINGLE(WM8904_DAC_DIGITAL_0, 2, 3, sidetone_text); +static SOC_ENUM_SINGLE_DECL(dacl_sidetone_enum, WM8904_DAC_DIGITAL_0, 2, + sidetone_text); static const struct snd_kcontrol_new dacl_sidetone_mux = SOC_DAPM_ENUM("Left Sidetone Mux", dacl_sidetone_enum); -static const struct soc_enum dacr_sidetone_enum = - SOC_ENUM_SINGLE(WM8904_DAC_DIGITAL_0, 0, 3, sidetone_text); +static SOC_ENUM_SINGLE_DECL(dacr_sidetone_enum, WM8904_DAC_DIGITAL_0, 0, + sidetone_text); static const struct snd_kcontrol_new dacr_sidetone_mux = SOC_DAPM_ENUM("Right Sidetone Mux", dacr_sidetone_enum); @@ -1444,7 +1445,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; @@ -1981,7 +1982,7 @@ static void wm8904_handle_retune_mobile_pdata(struct snd_soc_codec *codec) dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n", wm8904->num_retune_mobile_texts); - wm8904->retune_mobile_enum.max = wm8904->num_retune_mobile_texts; + wm8904->retune_mobile_enum.items = wm8904->num_retune_mobile_texts; wm8904->retune_mobile_enum.texts = wm8904->retune_mobile_texts; ret = snd_soc_add_codec_controls(codec, &control, 1); @@ -2022,7 +2023,7 @@ static void wm8904_handle_pdata(struct snd_soc_codec *codec) for (i = 0; i < pdata->num_drc_cfgs; i++) wm8904->drc_texts[i] = pdata->drc_cfgs[i].name; - wm8904->drc_enum.max = pdata->num_drc_cfgs; + wm8904->drc_enum.items = pdata->num_drc_cfgs; wm8904->drc_enum.texts = wm8904->drc_texts; ret = snd_soc_add_codec_controls(codec, &control, 1); diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index b1591c61c254..87f032d0d19f 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,97 +41,135 @@ 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" }; -static const struct soc_enum wm8940_adc_companding_enum -= SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 1, 4, wm8940_companding); -static const struct soc_enum wm8940_dac_companding_enum -= SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 3, 4, wm8940_companding); +static SOC_ENUM_SINGLE_DECL(wm8940_adc_companding_enum, + WM8940_COMPANDINGCTL, 1, wm8940_companding); +static SOC_ENUM_SINGLE_DECL(wm8940_dac_companding_enum, + WM8940_COMPANDINGCTL, 3, wm8940_companding); static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"}; -static const struct soc_enum wm8940_alc_mode_enum -= SOC_ENUM_SINGLE(WM8940_ALC3, 8, 2, wm8940_alc_mode_text); +static SOC_ENUM_SINGLE_DECL(wm8940_alc_mode_enum, + WM8940_ALC3, 8, wm8940_alc_mode_text); static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"}; -static const struct soc_enum wm8940_mic_bias_level_enum -= SOC_ENUM_SINGLE(WM8940_INPUTCTL, 8, 2, wm8940_mic_bias_level_text); +static SOC_ENUM_SINGLE_DECL(wm8940_mic_bias_level_enum, + WM8940_INPUTCTL, 8, wm8940_mic_bias_level_text); static const char *wm8940_filter_mode_text[] = {"Audio", "Application"}; -static const struct soc_enum wm8940_filter_mode_enum -= SOC_ENUM_SINGLE(WM8940_ADC, 7, 2, wm8940_filter_mode_text); +static SOC_ENUM_SINGLE_DECL(wm8940_filter_mode_enum, + WM8940_ADC, 7, wm8940_filter_mode_text); static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1); static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0); @@ -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/wm8955.c b/sound/soc/codecs/wm8955.c index 82c8ba975720..d4dcaecc8a5f 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -416,22 +416,21 @@ static const char *bass_mode_text[] = { "Linear", "Adaptive", }; -static const struct soc_enum bass_mode = - SOC_ENUM_SINGLE(WM8955_BASS_CONTROL, 7, 2, bass_mode_text); +static SOC_ENUM_SINGLE_DECL(bass_mode, WM8955_BASS_CONTROL, 7, bass_mode_text); static const char *bass_cutoff_text[] = { "Low", "High" }; -static const struct soc_enum bass_cutoff = - SOC_ENUM_SINGLE(WM8955_BASS_CONTROL, 6, 2, bass_cutoff_text); +static SOC_ENUM_SINGLE_DECL(bass_cutoff, WM8955_BASS_CONTROL, 6, + bass_cutoff_text); static const char *treble_cutoff_text[] = { "High", "Low" }; -static const struct soc_enum treble_cutoff = - SOC_ENUM_SINGLE(WM8955_TREBLE_CONTROL, 6, 2, treble_cutoff_text); +static SOC_ENUM_SINGLE_DECL(treble_cutoff, WM8955_TREBLE_CONTROL, 2, + treble_cutoff_text); static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); static const DECLARE_TLV_DB_SCALE(atten_tlv, -600, 600, 0); diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c index b7488f190d2b..7ac2e511403c 100644 --- a/sound/soc/codecs/wm8958-dsp2.c +++ b/sound/soc/codecs/wm8958-dsp2.c @@ -153,7 +153,7 @@ static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name, data32 &= 0xffffff; - wm8994_bulk_write(codec->control_data, + wm8994_bulk_write(wm8994->wm8994, data32 & 0xffffff, block_len / 2, (void *)(data + 8)); @@ -944,7 +944,7 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec) for (i = 0; i < pdata->num_mbc_cfgs; i++) wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name; - wm8994->mbc_enum.max = pdata->num_mbc_cfgs; + wm8994->mbc_enum.items = pdata->num_mbc_cfgs; wm8994->mbc_enum.texts = wm8994->mbc_texts; ret = snd_soc_add_codec_controls(wm8994->hubs.codec, @@ -973,7 +973,7 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec) for (i = 0; i < pdata->num_vss_cfgs; i++) wm8994->vss_texts[i] = pdata->vss_cfgs[i].name; - wm8994->vss_enum.max = pdata->num_vss_cfgs; + wm8994->vss_enum.items = pdata->num_vss_cfgs; wm8994->vss_enum.texts = wm8994->vss_texts; ret = snd_soc_add_codec_controls(wm8994->hubs.codec, @@ -1003,7 +1003,7 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec) for (i = 0; i < pdata->num_vss_hpf_cfgs; i++) wm8994->vss_hpf_texts[i] = pdata->vss_hpf_cfgs[i].name; - wm8994->vss_hpf_enum.max = pdata->num_vss_hpf_cfgs; + wm8994->vss_hpf_enum.items = pdata->num_vss_hpf_cfgs; wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts; ret = snd_soc_add_codec_controls(wm8994->hubs.codec, @@ -1034,7 +1034,7 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec) for (i = 0; i < pdata->num_enh_eq_cfgs; i++) wm8994->enh_eq_texts[i] = pdata->enh_eq_cfgs[i].name; - wm8994->enh_eq_enum.max = pdata->num_enh_eq_cfgs; + wm8994->enh_eq_enum.items = pdata->num_enh_eq_cfgs; wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts; ret = snd_soc_add_codec_controls(wm8994->hubs.codec, diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 543c5c2631b6..9e6233633c44 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; @@ -3076,6 +3089,7 @@ static irqreturn_t wm8962_irq(int irq, void *data) int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) { struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; int irq_mask, enable; wm8962->jack = jack; @@ -3096,19 +3110,23 @@ int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) snd_soc_jack_report(wm8962->jack, 0, SND_JACK_MICROPHONE | SND_JACK_BTN_0); + snd_soc_dapm_mutex_lock(dapm); + if (jack) { - snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK"); - snd_soc_dapm_force_enable_pin(&codec->dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "SYSCLK"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS"); } else { - snd_soc_dapm_disable_pin(&codec->dapm, "SYSCLK"); - snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS"); + snd_soc_dapm_disable_pin_unlocked(dapm, "SYSCLK"); + snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS"); } + snd_soc_dapm_mutex_unlock(dapm); + return 0; } 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/wm8978.c b/sound/soc/codecs/wm8978.c index d8fc531c0e59..a9e2f465c331 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -117,21 +117,21 @@ static const char *wm8978_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz"}; static const char *wm8978_alc3[] = {"ALC", "Limiter"}; static const char *wm8978_alc1[] = {"Off", "Right", "Left", "Both"}; -static const SOC_ENUM_SINGLE_DECL(adc_compand, WM8978_COMPANDING_CONTROL, 1, - wm8978_companding); -static const SOC_ENUM_SINGLE_DECL(dac_compand, WM8978_COMPANDING_CONTROL, 3, - wm8978_companding); -static const SOC_ENUM_SINGLE_DECL(eqmode, WM8978_EQ1, 8, wm8978_eqmode); -static const SOC_ENUM_SINGLE_DECL(eq1, WM8978_EQ1, 5, wm8978_eq1); -static const SOC_ENUM_SINGLE_DECL(eq2bw, WM8978_EQ2, 8, wm8978_bw); -static const SOC_ENUM_SINGLE_DECL(eq2, WM8978_EQ2, 5, wm8978_eq2); -static const SOC_ENUM_SINGLE_DECL(eq3bw, WM8978_EQ3, 8, wm8978_bw); -static const SOC_ENUM_SINGLE_DECL(eq3, WM8978_EQ3, 5, wm8978_eq3); -static const SOC_ENUM_SINGLE_DECL(eq4bw, WM8978_EQ4, 8, wm8978_bw); -static const SOC_ENUM_SINGLE_DECL(eq4, WM8978_EQ4, 5, wm8978_eq4); -static const SOC_ENUM_SINGLE_DECL(eq5, WM8978_EQ5, 5, wm8978_eq5); -static const SOC_ENUM_SINGLE_DECL(alc3, WM8978_ALC_CONTROL_3, 8, wm8978_alc3); -static const SOC_ENUM_SINGLE_DECL(alc1, WM8978_ALC_CONTROL_1, 7, wm8978_alc1); +static SOC_ENUM_SINGLE_DECL(adc_compand, WM8978_COMPANDING_CONTROL, 1, + wm8978_companding); +static SOC_ENUM_SINGLE_DECL(dac_compand, WM8978_COMPANDING_CONTROL, 3, + wm8978_companding); +static SOC_ENUM_SINGLE_DECL(eqmode, WM8978_EQ1, 8, wm8978_eqmode); +static SOC_ENUM_SINGLE_DECL(eq1, WM8978_EQ1, 5, wm8978_eq1); +static SOC_ENUM_SINGLE_DECL(eq2bw, WM8978_EQ2, 8, wm8978_bw); +static SOC_ENUM_SINGLE_DECL(eq2, WM8978_EQ2, 5, wm8978_eq2); +static SOC_ENUM_SINGLE_DECL(eq3bw, WM8978_EQ3, 8, wm8978_bw); +static SOC_ENUM_SINGLE_DECL(eq3, WM8978_EQ3, 5, wm8978_eq3); +static SOC_ENUM_SINGLE_DECL(eq4bw, WM8978_EQ4, 8, wm8978_bw); +static SOC_ENUM_SINGLE_DECL(eq4, WM8978_EQ4, 5, wm8978_eq4); +static SOC_ENUM_SINGLE_DECL(eq5, WM8978_EQ5, 5, wm8978_eq5); +static SOC_ENUM_SINGLE_DECL(alc3, WM8978_ALC_CONTROL_3, 8, wm8978_alc3); +static SOC_ENUM_SINGLE_DECL(alc1, WM8978_ALC_CONTROL_1, 7, wm8978_alc1); static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index aa41ba0dfff4..770e5a705851 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -205,49 +205,44 @@ static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0); static const char *alc_sel_text[] = { "Off", "Right", "Left", "Stereo" }; -static const SOC_ENUM_SINGLE_DECL(alc_sel, WM8983_ALC_CONTROL_1, 7, - alc_sel_text); +static SOC_ENUM_SINGLE_DECL(alc_sel, WM8983_ALC_CONTROL_1, 7, alc_sel_text); static const char *alc_mode_text[] = { "ALC", "Limiter" }; -static const SOC_ENUM_SINGLE_DECL(alc_mode, WM8983_ALC_CONTROL_3, 8, - alc_mode_text); +static SOC_ENUM_SINGLE_DECL(alc_mode, WM8983_ALC_CONTROL_3, 8, alc_mode_text); static const char *filter_mode_text[] = { "Audio", "Application" }; -static const SOC_ENUM_SINGLE_DECL(filter_mode, WM8983_ADC_CONTROL, 7, - filter_mode_text); +static SOC_ENUM_SINGLE_DECL(filter_mode, WM8983_ADC_CONTROL, 7, + filter_mode_text); static const char *eq_bw_text[] = { "Narrow", "Wide" }; static const char *eqmode_text[] = { "Capture", "Playback" }; -static const SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text); +static SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text); static const char *eq1_cutoff_text[] = { "80Hz", "105Hz", "135Hz", "175Hz" }; -static const SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8983_EQ1_LOW_SHELF, 5, - eq1_cutoff_text); +static SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8983_EQ1_LOW_SHELF, 5, + eq1_cutoff_text); static const char *eq2_cutoff_text[] = { "230Hz", "300Hz", "385Hz", "500Hz" }; -static const SOC_ENUM_SINGLE_DECL(eq2_bw, WM8983_EQ2_PEAK_1, 8, eq_bw_text); -static const SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8983_EQ2_PEAK_1, 5, - eq2_cutoff_text); +static SOC_ENUM_SINGLE_DECL(eq2_bw, WM8983_EQ2_PEAK_1, 8, eq_bw_text); +static SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8983_EQ2_PEAK_1, 5, eq2_cutoff_text); static const char *eq3_cutoff_text[] = { "650Hz", "850Hz", "1.1kHz", "1.4kHz" }; -static const SOC_ENUM_SINGLE_DECL(eq3_bw, WM8983_EQ3_PEAK_2, 8, eq_bw_text); -static const SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8983_EQ3_PEAK_2, 5, - eq3_cutoff_text); +static SOC_ENUM_SINGLE_DECL(eq3_bw, WM8983_EQ3_PEAK_2, 8, eq_bw_text); +static SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8983_EQ3_PEAK_2, 5, eq3_cutoff_text); static const char *eq4_cutoff_text[] = { "1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" }; -static const SOC_ENUM_SINGLE_DECL(eq4_bw, WM8983_EQ4_PEAK_3, 8, eq_bw_text); -static const SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8983_EQ4_PEAK_3, 5, - eq4_cutoff_text); +static SOC_ENUM_SINGLE_DECL(eq4_bw, WM8983_EQ4_PEAK_3, 8, eq_bw_text); +static SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8983_EQ4_PEAK_3, 5, eq4_cutoff_text); static const char *eq5_cutoff_text[] = { "5.3kHz", "6.9kHz", "9kHz", "11.7kHz" }; -static const SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8983_EQ5_HIGH_SHELF, 5, - eq5_cutoff_text); +static SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8983_EQ5_HIGH_SHELF, 5, + eq5_cutoff_text); static const char *depth_3d_text[] = { "Off", @@ -267,8 +262,8 @@ static const char *depth_3d_text[] = { "93.3%", "100%" }; -static const SOC_ENUM_SINGLE_DECL(depth_3d, WM8983_3D_CONTROL, 0, - depth_3d_text); +static SOC_ENUM_SINGLE_DECL(depth_3d, WM8983_3D_CONTROL, 0, + depth_3d_text); static const struct snd_kcontrol_new wm8983_snd_controls[] = { SOC_SINGLE("Digital Loopback Switch", WM8983_COMPANDING_CONTROL, diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index 18f2babe1090..d786f2b39764 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -226,52 +226,48 @@ static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0); static const char *alc_sel_text[] = { "Off", "Right", "Left", "Stereo" }; -static const SOC_ENUM_SINGLE_DECL(alc_sel, WM8985_ALC_CONTROL_1, 7, - alc_sel_text); +static SOC_ENUM_SINGLE_DECL(alc_sel, WM8985_ALC_CONTROL_1, 7, alc_sel_text); static const char *alc_mode_text[] = { "ALC", "Limiter" }; -static const SOC_ENUM_SINGLE_DECL(alc_mode, WM8985_ALC_CONTROL_3, 8, - alc_mode_text); +static SOC_ENUM_SINGLE_DECL(alc_mode, WM8985_ALC_CONTROL_3, 8, alc_mode_text); static const char *filter_mode_text[] = { "Audio", "Application" }; -static const SOC_ENUM_SINGLE_DECL(filter_mode, WM8985_ADC_CONTROL, 7, - filter_mode_text); +static SOC_ENUM_SINGLE_DECL(filter_mode, WM8985_ADC_CONTROL, 7, + filter_mode_text); static const char *eq_bw_text[] = { "Narrow", "Wide" }; static const char *eqmode_text[] = { "Capture", "Playback" }; -static const SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text); +static SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text); static const char *eq1_cutoff_text[] = { "80Hz", "105Hz", "135Hz", "175Hz" }; -static const SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8985_EQ1_LOW_SHELF, 5, - eq1_cutoff_text); +static SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8985_EQ1_LOW_SHELF, 5, + eq1_cutoff_text); static const char *eq2_cutoff_text[] = { "230Hz", "300Hz", "385Hz", "500Hz" }; -static const SOC_ENUM_SINGLE_DECL(eq2_bw, WM8985_EQ2_PEAK_1, 8, eq_bw_text); -static const SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8985_EQ2_PEAK_1, 5, - eq2_cutoff_text); +static SOC_ENUM_SINGLE_DECL(eq2_bw, WM8985_EQ2_PEAK_1, 8, eq_bw_text); +static SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8985_EQ2_PEAK_1, 5, eq2_cutoff_text); static const char *eq3_cutoff_text[] = { "650Hz", "850Hz", "1.1kHz", "1.4kHz" }; -static const SOC_ENUM_SINGLE_DECL(eq3_bw, WM8985_EQ3_PEAK_2, 8, eq_bw_text); -static const SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8985_EQ3_PEAK_2, 5, - eq3_cutoff_text); +static SOC_ENUM_SINGLE_DECL(eq3_bw, WM8985_EQ3_PEAK_2, 8, eq_bw_text); +static SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8985_EQ3_PEAK_2, 5, + eq3_cutoff_text); static const char *eq4_cutoff_text[] = { "1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" }; -static const SOC_ENUM_SINGLE_DECL(eq4_bw, WM8985_EQ4_PEAK_3, 8, eq_bw_text); -static const SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8985_EQ4_PEAK_3, 5, - eq4_cutoff_text); +static SOC_ENUM_SINGLE_DECL(eq4_bw, WM8985_EQ4_PEAK_3, 8, eq_bw_text); +static SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8985_EQ4_PEAK_3, 5, eq4_cutoff_text); static const char *eq5_cutoff_text[] = { "5.3kHz", "6.9kHz", "9kHz", "11.7kHz" }; -static const SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8985_EQ5_HIGH_SHELF, 5, +static SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8985_EQ5_HIGH_SHELF, 5, eq5_cutoff_text); static const char *speaker_mode_text[] = { "Class A/B", "Class D" }; -static const SOC_ENUM_SINGLE_DECL(speaker_mode, 0x17, 8, speaker_mode_text); +static SOC_ENUM_SINGLE_DECL(speaker_mode, 0x17, 8, speaker_mode_text); static const char *depth_3d_text[] = { "Off", @@ -291,8 +287,7 @@ static const char *depth_3d_text[] = { "93.3%", "100%" }; -static const SOC_ENUM_SINGLE_DECL(depth_3d, WM8985_3D_CONTROL, 0, - depth_3d_text); +static SOC_ENUM_SINGLE_DECL(depth_3d, WM8985_3D_CONTROL, 0, depth_3d_text); static const struct snd_kcontrol_new wm8985_snd_controls[] = { SOC_SINGLE("Digital Loopback Switch", WM8985_COMPANDING_CONTROL, @@ -1148,7 +1143,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 +1196,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 +1216,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/wm8993.c b/sound/soc/codecs/wm8993.c index 433d59a0f3ef..2ee23a39622c 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1562,7 +1562,6 @@ static int wm8993_remove(struct snd_soc_codec *codec) struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec); wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF); - regulator_bulk_free(ARRAY_SIZE(wm8993->supplies), wm8993->supplies); return 0; } diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 86426a117b07..79854cb7feb6 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -265,21 +265,21 @@ static const char *sidetone_hpf_text[] = { "2.7kHz", "1.35kHz", "675Hz", "370Hz", "180Hz", "90Hz", "45Hz" }; -static const struct soc_enum sidetone_hpf = - SOC_ENUM_SINGLE(WM8994_SIDETONE, 7, 7, sidetone_hpf_text); +static SOC_ENUM_SINGLE_DECL(sidetone_hpf, + WM8994_SIDETONE, 7, sidetone_hpf_text); static const char *adc_hpf_text[] = { "HiFi", "Voice 1", "Voice 2", "Voice 3" }; -static const struct soc_enum aif1adc1_hpf = - SOC_ENUM_SINGLE(WM8994_AIF1_ADC1_FILTERS, 13, 4, adc_hpf_text); +static SOC_ENUM_SINGLE_DECL(aif1adc1_hpf, + WM8994_AIF1_ADC1_FILTERS, 13, adc_hpf_text); -static const struct soc_enum aif1adc2_hpf = - SOC_ENUM_SINGLE(WM8994_AIF1_ADC2_FILTERS, 13, 4, adc_hpf_text); +static SOC_ENUM_SINGLE_DECL(aif1adc2_hpf, + WM8994_AIF1_ADC2_FILTERS, 13, adc_hpf_text); -static const struct soc_enum aif2adc_hpf = - SOC_ENUM_SINGLE(WM8994_AIF2_ADC_FILTERS, 13, 4, adc_hpf_text); +static SOC_ENUM_SINGLE_DECL(aif2adc_hpf, + WM8994_AIF2_ADC_FILTERS, 13, adc_hpf_text); static const DECLARE_TLV_DB_SCALE(aif_tlv, 0, 600, 0); static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); @@ -501,39 +501,39 @@ static const char *aif_chan_src_text[] = { "Left", "Right" }; -static const struct soc_enum aif1adcl_src = - SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_1, 15, 2, aif_chan_src_text); +static SOC_ENUM_SINGLE_DECL(aif1adcl_src, + WM8994_AIF1_CONTROL_1, 15, aif_chan_src_text); -static const struct soc_enum aif1adcr_src = - SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_1, 14, 2, aif_chan_src_text); +static SOC_ENUM_SINGLE_DECL(aif1adcr_src, + WM8994_AIF1_CONTROL_1, 14, aif_chan_src_text); -static const struct soc_enum aif2adcl_src = - SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_1, 15, 2, aif_chan_src_text); +static SOC_ENUM_SINGLE_DECL(aif2adcl_src, + WM8994_AIF2_CONTROL_1, 15, aif_chan_src_text); -static const struct soc_enum aif2adcr_src = - SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_1, 14, 2, aif_chan_src_text); +static SOC_ENUM_SINGLE_DECL(aif2adcr_src, + WM8994_AIF2_CONTROL_1, 14, aif_chan_src_text); -static const struct soc_enum aif1dacl_src = - SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, 15, 2, aif_chan_src_text); +static SOC_ENUM_SINGLE_DECL(aif1dacl_src, + WM8994_AIF1_CONTROL_2, 15, aif_chan_src_text); -static const struct soc_enum aif1dacr_src = - SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, 14, 2, aif_chan_src_text); +static SOC_ENUM_SINGLE_DECL(aif1dacr_src, + WM8994_AIF1_CONTROL_2, 14, aif_chan_src_text); -static const struct soc_enum aif2dacl_src = - SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 15, 2, aif_chan_src_text); +static SOC_ENUM_SINGLE_DECL(aif2dacl_src, + WM8994_AIF2_CONTROL_2, 15, aif_chan_src_text); -static const struct soc_enum aif2dacr_src = - SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 14, 2, aif_chan_src_text); +static SOC_ENUM_SINGLE_DECL(aif2dacr_src, + WM8994_AIF2_CONTROL_2, 14, aif_chan_src_text); static const char *osr_text[] = { "Low Power", "High Performance", }; -static const struct soc_enum dac_osr = - SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 0, 2, osr_text); +static SOC_ENUM_SINGLE_DECL(dac_osr, + WM8994_OVERSAMPLING, 0, osr_text); -static const struct soc_enum adc_osr = - SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 1, 2, osr_text); +static SOC_ENUM_SINGLE_DECL(adc_osr, + WM8994_OVERSAMPLING, 1, osr_text); static const struct snd_kcontrol_new wm8994_snd_controls[] = { SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME, @@ -690,17 +690,20 @@ static const char *wm8958_ng_text[] = { "30ms", "125ms", "250ms", "500ms", }; -static const struct soc_enum wm8958_aif1dac1_ng_hold = - SOC_ENUM_SINGLE(WM8958_AIF1_DAC1_NOISE_GATE, - WM8958_AIF1DAC1_NG_THR_SHIFT, 4, wm8958_ng_text); +static SOC_ENUM_SINGLE_DECL(wm8958_aif1dac1_ng_hold, + WM8958_AIF1_DAC1_NOISE_GATE, + WM8958_AIF1DAC1_NG_THR_SHIFT, + wm8958_ng_text); -static const struct soc_enum wm8958_aif1dac2_ng_hold = - SOC_ENUM_SINGLE(WM8958_AIF1_DAC2_NOISE_GATE, - WM8958_AIF1DAC2_NG_THR_SHIFT, 4, wm8958_ng_text); +static SOC_ENUM_SINGLE_DECL(wm8958_aif1dac2_ng_hold, + WM8958_AIF1_DAC2_NOISE_GATE, + WM8958_AIF1DAC2_NG_THR_SHIFT, + wm8958_ng_text); -static const struct soc_enum wm8958_aif2dac_ng_hold = - SOC_ENUM_SINGLE(WM8958_AIF2_DAC_NOISE_GATE, - WM8958_AIF2DAC_NG_THR_SHIFT, 4, wm8958_ng_text); +static SOC_ENUM_SINGLE_DECL(wm8958_aif2dac_ng_hold, + WM8958_AIF2_DAC_NOISE_GATE, + WM8958_AIF2DAC_NG_THR_SHIFT, + wm8958_ng_text); static const struct snd_kcontrol_new wm8958_snd_controls[] = { SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv), @@ -1341,8 +1344,7 @@ static const char *adc_mux_text[] = { "DMIC", }; -static const struct soc_enum adc_enum = - SOC_ENUM_SINGLE(0, 0, 2, adc_mux_text); +static SOC_ENUM_SINGLE_VIRT_DECL(adc_enum, adc_mux_text); static const struct snd_kcontrol_new adcl_mux = SOC_DAPM_ENUM_VIRT("ADCL Mux", adc_enum); @@ -1478,14 +1480,14 @@ static const char *sidetone_text[] = { "ADC/DMIC1", "DMIC2", }; -static const struct soc_enum sidetone1_enum = - SOC_ENUM_SINGLE(WM8994_SIDETONE, 0, 2, sidetone_text); +static SOC_ENUM_SINGLE_DECL(sidetone1_enum, + WM8994_SIDETONE, 0, sidetone_text); static const struct snd_kcontrol_new sidetone1_mux = SOC_DAPM_ENUM("Left Sidetone Mux", sidetone1_enum); -static const struct soc_enum sidetone2_enum = - SOC_ENUM_SINGLE(WM8994_SIDETONE, 1, 2, sidetone_text); +static SOC_ENUM_SINGLE_DECL(sidetone2_enum, + WM8994_SIDETONE, 1, sidetone_text); static const struct snd_kcontrol_new sidetone2_mux = SOC_DAPM_ENUM("Right Sidetone Mux", sidetone2_enum); @@ -1498,22 +1500,24 @@ static const char *loopback_text[] = { "None", "ADCDAT", }; -static const struct soc_enum aif1_loopback_enum = - SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, WM8994_AIF1_LOOPBACK_SHIFT, 2, - loopback_text); +static SOC_ENUM_SINGLE_DECL(aif1_loopback_enum, + WM8994_AIF1_CONTROL_2, + WM8994_AIF1_LOOPBACK_SHIFT, + loopback_text); static const struct snd_kcontrol_new aif1_loopback = SOC_DAPM_ENUM("AIF1 Loopback", aif1_loopback_enum); -static const struct soc_enum aif2_loopback_enum = - SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, WM8994_AIF2_LOOPBACK_SHIFT, 2, - loopback_text); +static SOC_ENUM_SINGLE_DECL(aif2_loopback_enum, + WM8994_AIF2_CONTROL_2, + WM8994_AIF2_LOOPBACK_SHIFT, + loopback_text); static const struct snd_kcontrol_new aif2_loopback = SOC_DAPM_ENUM("AIF2 Loopback", aif2_loopback_enum); -static const struct soc_enum aif1dac_enum = - SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 0, 2, aif1dac_text); +static SOC_ENUM_SINGLE_DECL(aif1dac_enum, + WM8994_POWER_MANAGEMENT_6, 0, aif1dac_text); static const struct snd_kcontrol_new aif1dac_mux = SOC_DAPM_ENUM("AIF1DAC Mux", aif1dac_enum); @@ -1522,8 +1526,8 @@ static const char *aif2dac_text[] = { "AIF2DACDAT", "AIF3DACDAT", }; -static const struct soc_enum aif2dac_enum = - SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 1, 2, aif2dac_text); +static SOC_ENUM_SINGLE_DECL(aif2dac_enum, + WM8994_POWER_MANAGEMENT_6, 1, aif2dac_text); static const struct snd_kcontrol_new aif2dac_mux = SOC_DAPM_ENUM("AIF2DAC Mux", aif2dac_enum); @@ -1532,8 +1536,8 @@ static const char *aif2adc_text[] = { "AIF2ADCDAT", "AIF3DACDAT", }; -static const struct soc_enum aif2adc_enum = - SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 2, 2, aif2adc_text); +static SOC_ENUM_SINGLE_DECL(aif2adc_enum, + WM8994_POWER_MANAGEMENT_6, 2, aif2adc_text); static const struct snd_kcontrol_new aif2adc_mux = SOC_DAPM_ENUM("AIF2ADC Mux", aif2adc_enum); @@ -1542,14 +1546,14 @@ static const char *aif3adc_text[] = { "AIF1ADCDAT", "AIF2ADCDAT", "AIF2DACDAT", "Mono PCM", }; -static const struct soc_enum wm8994_aif3adc_enum = - SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 3, 3, aif3adc_text); +static SOC_ENUM_SINGLE_DECL(wm8994_aif3adc_enum, + WM8994_POWER_MANAGEMENT_6, 3, aif3adc_text); static const struct snd_kcontrol_new wm8994_aif3adc_mux = SOC_DAPM_ENUM("AIF3ADC Mux", wm8994_aif3adc_enum); -static const struct soc_enum wm8958_aif3adc_enum = - SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 3, 4, aif3adc_text); +static SOC_ENUM_SINGLE_DECL(wm8958_aif3adc_enum, + WM8994_POWER_MANAGEMENT_6, 3, aif3adc_text); static const struct snd_kcontrol_new wm8958_aif3adc_mux = SOC_DAPM_ENUM("AIF3ADC Mux", wm8958_aif3adc_enum); @@ -1558,8 +1562,8 @@ static const char *mono_pcm_out_text[] = { "None", "AIF2ADCL", "AIF2ADCR", }; -static const struct soc_enum mono_pcm_out_enum = - SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 9, 3, mono_pcm_out_text); +static SOC_ENUM_SINGLE_DECL(mono_pcm_out_enum, + WM8994_POWER_MANAGEMENT_6, 9, mono_pcm_out_text); static const struct snd_kcontrol_new mono_pcm_out_mux = SOC_DAPM_ENUM("Mono PCM Out Mux", mono_pcm_out_enum); @@ -1569,14 +1573,14 @@ static const char *aif2dac_src_text[] = { }; /* Note that these two control shouldn't be simultaneously switched to AIF3 */ -static const struct soc_enum aif2dacl_src_enum = - SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 7, 2, aif2dac_src_text); +static SOC_ENUM_SINGLE_DECL(aif2dacl_src_enum, + WM8994_POWER_MANAGEMENT_6, 7, aif2dac_src_text); static const struct snd_kcontrol_new aif2dacl_src_mux = SOC_DAPM_ENUM("AIF2DACL Mux", aif2dacl_src_enum); -static const struct soc_enum aif2dacr_src_enum = - SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 8, 2, aif2dac_src_text); +static SOC_ENUM_SINGLE_DECL(aif2dacr_src_enum, + WM8994_POWER_MANAGEMENT_6, 8, aif2dac_src_text); static const struct snd_kcontrol_new aif2dacr_src_mux = SOC_DAPM_ENUM("AIF2DACR Mux", aif2dacr_src_enum); @@ -2549,43 +2553,52 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; switch (mode) { case WM8994_VMID_NORMAL: + snd_soc_dapm_mutex_lock(dapm); + if (wm8994->hubs.lineout1_se) { - snd_soc_dapm_disable_pin(&codec->dapm, - "LINEOUT1N Driver"); - snd_soc_dapm_disable_pin(&codec->dapm, - "LINEOUT1P Driver"); + snd_soc_dapm_disable_pin_unlocked(dapm, + "LINEOUT1N Driver"); + snd_soc_dapm_disable_pin_unlocked(dapm, + "LINEOUT1P Driver"); } if (wm8994->hubs.lineout2_se) { - snd_soc_dapm_disable_pin(&codec->dapm, - "LINEOUT2N Driver"); - snd_soc_dapm_disable_pin(&codec->dapm, - "LINEOUT2P Driver"); + snd_soc_dapm_disable_pin_unlocked(dapm, + "LINEOUT2N Driver"); + snd_soc_dapm_disable_pin_unlocked(dapm, + "LINEOUT2P Driver"); } /* Do the sync with the old mode to allow it to clean up */ - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync_unlocked(dapm); wm8994->vmid_mode = mode; + + snd_soc_dapm_mutex_unlock(dapm); break; case WM8994_VMID_FORCE: + snd_soc_dapm_mutex_lock(dapm); + if (wm8994->hubs.lineout1_se) { - snd_soc_dapm_force_enable_pin(&codec->dapm, - "LINEOUT1N Driver"); - snd_soc_dapm_force_enable_pin(&codec->dapm, - "LINEOUT1P Driver"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, + "LINEOUT1N Driver"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, + "LINEOUT1P Driver"); } if (wm8994->hubs.lineout2_se) { - snd_soc_dapm_force_enable_pin(&codec->dapm, - "LINEOUT2N Driver"); - snd_soc_dapm_force_enable_pin(&codec->dapm, - "LINEOUT2P Driver"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, + "LINEOUT2N Driver"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, + "LINEOUT2P Driver"); } wm8994->vmid_mode = mode; - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); break; default: @@ -3237,7 +3250,7 @@ static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994) dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n", wm8994->num_retune_mobile_texts); - wm8994->retune_mobile_enum.max = wm8994->num_retune_mobile_texts; + wm8994->retune_mobile_enum.items = wm8994->num_retune_mobile_texts; wm8994->retune_mobile_enum.texts = wm8994->retune_mobile_texts; ret = snd_soc_add_codec_controls(wm8994->hubs.codec, controls, @@ -3293,7 +3306,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) for (i = 0; i < pdata->num_drc_cfgs; i++) wm8994->drc_texts[i] = pdata->drc_cfgs[i].name; - wm8994->drc_enum.max = pdata->num_drc_cfgs; + wm8994->drc_enum.items = pdata->num_drc_cfgs; wm8994->drc_enum.texts = wm8994->drc_texts; ret = snd_soc_add_codec_controls(wm8994->hubs.codec, controls, @@ -4077,12 +4090,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 +4320,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..ddb197dc1d53 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -423,24 +423,24 @@ static const char *in1l_text[] = { "Differential", "Single-ended IN1LN", "Single-ended IN1LP" }; -static const SOC_ENUM_SINGLE_DECL(in1l_enum, WM8995_LEFT_LINE_INPUT_CONTROL, - 2, in1l_text); +static SOC_ENUM_SINGLE_DECL(in1l_enum, WM8995_LEFT_LINE_INPUT_CONTROL, + 2, in1l_text); static const char *in1r_text[] = { "Differential", "Single-ended IN1RN", "Single-ended IN1RP" }; -static const SOC_ENUM_SINGLE_DECL(in1r_enum, WM8995_LEFT_LINE_INPUT_CONTROL, - 0, in1r_text); +static SOC_ENUM_SINGLE_DECL(in1r_enum, WM8995_LEFT_LINE_INPUT_CONTROL, + 0, in1r_text); static const char *dmic_src_text[] = { "DMICDAT1", "DMICDAT2", "DMICDAT3" }; -static const SOC_ENUM_SINGLE_DECL(dmic_src1_enum, WM8995_POWER_MANAGEMENT_5, - 8, dmic_src_text); -static const SOC_ENUM_SINGLE_DECL(dmic_src2_enum, WM8995_POWER_MANAGEMENT_5, - 6, dmic_src_text); +static SOC_ENUM_SINGLE_DECL(dmic_src1_enum, WM8995_POWER_MANAGEMENT_5, + 8, dmic_src_text); +static SOC_ENUM_SINGLE_DECL(dmic_src2_enum, WM8995_POWER_MANAGEMENT_5, + 6, dmic_src_text); static const struct snd_kcontrol_new wm8995_snd_controls[] = { SOC_DOUBLE_R_TLV("DAC1 Volume", WM8995_DAC1_LEFT_VOLUME, @@ -561,10 +561,8 @@ static int hp_supply_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec; - struct wm8995_priv *wm8995; codec = w->codec; - wm8995 = snd_soc_codec_get_drvdata(codec); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -783,14 +781,12 @@ static const char *sidetone_text[] = { "ADC/DMIC1", "DMIC2", }; -static const struct soc_enum sidetone1_enum = - SOC_ENUM_SINGLE(WM8995_SIDETONE, 0, 2, sidetone_text); +static SOC_ENUM_SINGLE_DECL(sidetone1_enum, WM8995_SIDETONE, 0, sidetone_text); static const struct snd_kcontrol_new sidetone1_mux = SOC_DAPM_ENUM("Left Sidetone Mux", sidetone1_enum); -static const struct soc_enum sidetone2_enum = - SOC_ENUM_SINGLE(WM8995_SIDETONE, 1, 2, sidetone_text); +static SOC_ENUM_SINGLE_DECL(sidetone2_enum, WM8995_SIDETONE, 1, sidetone_text); static const struct snd_kcontrol_new sidetone2_mux = SOC_DAPM_ENUM("Right Sidetone Mux", sidetone2_enum); @@ -886,8 +882,7 @@ static const char *adc_mux_text[] = { "DMIC", }; -static const struct soc_enum adc_enum = - SOC_ENUM_SINGLE(0, 0, 2, adc_mux_text); +static SOC_ENUM_SINGLE_VIRT_DECL(adc_enum, adc_mux_text); static const struct snd_kcontrol_new adcl_mux = SOC_DAPM_ENUM_VIRT("ADCL Mux", adc_enum); @@ -899,14 +894,14 @@ static const char *spk_src_text[] = { "DAC1L", "DAC1R", "DAC2L", "DAC2R" }; -static const SOC_ENUM_SINGLE_DECL(spk1l_src_enum, WM8995_LEFT_PDM_SPEAKER_1, - 0, spk_src_text); -static const SOC_ENUM_SINGLE_DECL(spk1r_src_enum, WM8995_RIGHT_PDM_SPEAKER_1, - 0, spk_src_text); -static const SOC_ENUM_SINGLE_DECL(spk2l_src_enum, WM8995_LEFT_PDM_SPEAKER_2, - 0, spk_src_text); -static const SOC_ENUM_SINGLE_DECL(spk2r_src_enum, WM8995_RIGHT_PDM_SPEAKER_2, - 0, spk_src_text); +static SOC_ENUM_SINGLE_DECL(spk1l_src_enum, WM8995_LEFT_PDM_SPEAKER_1, + 0, spk_src_text); +static SOC_ENUM_SINGLE_DECL(spk1r_src_enum, WM8995_RIGHT_PDM_SPEAKER_1, + 0, spk_src_text); +static SOC_ENUM_SINGLE_DECL(spk2l_src_enum, WM8995_LEFT_PDM_SPEAKER_2, + 0, spk_src_text); +static SOC_ENUM_SINGLE_DECL(spk2r_src_enum, WM8995_RIGHT_PDM_SPEAKER_2, + 0, spk_src_text); static const struct snd_kcontrol_new spk1l_mux = SOC_DAPM_ENUM("SPK1L SRC", spk1l_src_enum); @@ -2293,7 +2288,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 +2345,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 +2366,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/wm8996.c b/sound/soc/codecs/wm8996.c index 1a7655b0aa22..0330165079a4 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -2251,6 +2251,7 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, wm8996_polarity_fn polarity_cb) { struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; wm8996->jack = jack; wm8996->detecting = true; @@ -2267,8 +2268,12 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, WM8996_MICB2_DISCH, 0); /* LDO2 powers the microphones, SYSCLK clocks detection */ - snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2"); - snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK"); + snd_soc_dapm_mutex_lock(dapm); + + snd_soc_dapm_force_enable_pin_unlocked(dapm, "LDO2"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "SYSCLK"); + + snd_soc_dapm_mutex_unlock(dapm); /* We start off just enabling microphone detection - even a * plain headphone will trigger detection. @@ -2595,7 +2600,7 @@ static void wm8996_retune_mobile_pdata(struct snd_soc_codec *codec) dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n", wm8996->num_retune_mobile_texts); - wm8996->retune_mobile_enum.max = wm8996->num_retune_mobile_texts; + wm8996->retune_mobile_enum.items = wm8996->num_retune_mobile_texts; wm8996->retune_mobile_enum.texts = wm8996->retune_mobile_texts; ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls)); diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index 1392bb3c9254..e10f44d7fdb7 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -86,7 +86,7 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = w->codec; struct arizona *arizona = dev_get_drvdata(codec->dev->parent); - struct regmap *regmap = codec->control_data; + struct regmap *regmap = arizona->regmap; const struct reg_default *patch = NULL; int i, patch_size; @@ -103,8 +103,8 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: if (patch) for (i = 0; i < patch_size; i++) - regmap_write(regmap, patch[i].reg, - patch[i].def); + regmap_write_async(regmap, patch[i].reg, + patch[i].def); break; default: break; @@ -123,10 +123,12 @@ static const unsigned int wm8997_osr_val[] = { static const struct soc_enum wm8997_hpout_osr[] = { SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L, - ARIZONA_OUT1_OSR_SHIFT, 0x7, 3, + ARIZONA_OUT1_OSR_SHIFT, 0x7, + ARRAY_SIZE(wm8997_osr_text), wm8997_osr_text, wm8997_osr_val), SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L, - ARIZONA_OUT3_OSR_SHIFT, 0x7, 3, + ARIZONA_OUT3_OSR_SHIFT, 0x7, + ARRAY_SIZE(wm8997_osr_text), wm8997_osr_text, wm8997_osr_val), }; @@ -170,15 +172,8 @@ ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), -SND_SOC_BYTES_MASK("EQ1 Coefficients", ARIZONA_EQ1_1, 21, - ARIZONA_EQ1_ENA_MASK), -SND_SOC_BYTES_MASK("EQ2 Coefficients", ARIZONA_EQ2_1, 21, - ARIZONA_EQ2_ENA_MASK), -SND_SOC_BYTES_MASK("EQ3 Coefficients", ARIZONA_EQ3_1, 21, - ARIZONA_EQ3_ENA_MASK), -SND_SOC_BYTES_MASK("EQ4 Coefficients", ARIZONA_EQ4_1, 21, - ARIZONA_EQ4_ENA_MASK), - +SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19), +SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE, 1, 0), SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, @@ -190,6 +185,8 @@ SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, 24, 0, eq_tlv), +SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19), +SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE, 1, 0), SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, @@ -201,6 +198,8 @@ SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, 24, 0, eq_tlv), +SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19), +SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE, 1, 0), SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, @@ -212,6 +211,8 @@ SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, 24, 0, eq_tlv), +SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19), +SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE, 1, 0), SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, 24, 0, eq_tlv), SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, 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 46ec0e9744d4..bb5f7b4e3ebb 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -684,24 +684,38 @@ static int wm_adsp_load(struct wm_adsp *dsp) } if (reg) { - buf = wm_adsp_buf_alloc(region->data, - le32_to_cpu(region->len), - &buf_list); - if (!buf) { - adsp_err(dsp, "Out of memory\n"); - ret = -ENOMEM; - goto out_fw; - } + size_t to_write = PAGE_SIZE; + size_t remain = le32_to_cpu(region->len); + const u8 *data = region->data; + + while (remain > 0) { + if (remain < PAGE_SIZE) + to_write = remain; + + buf = wm_adsp_buf_alloc(data, + to_write, + &buf_list); + if (!buf) { + adsp_err(dsp, "Out of memory\n"); + ret = -ENOMEM; + goto out_fw; + } - ret = regmap_raw_write_async(regmap, reg, buf->buf, - le32_to_cpu(region->len)); - if (ret != 0) { - adsp_err(dsp, - "%s.%d: Failed to write %d bytes at %d in %s: %d\n", - file, regions, - le32_to_cpu(region->len), offset, - region_name, ret); - goto out_fw; + ret = regmap_raw_write_async(regmap, reg, + buf->buf, + to_write); + if (ret != 0) { + adsp_err(dsp, + "%s.%d: Failed to write %zd bytes at %d in %s: %d\n", + file, regions, + to_write, offset, + region_name, ret); + goto out_fw; + } + + data += to_write; + reg += to_write / 2; + remain -= to_write; } } @@ -1286,6 +1300,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) reg = wm_adsp_region_to_reg(mem, reg); reg += offset; + break; } } @@ -1468,19 +1483,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"); @@ -1488,112 +1507,153 @@ 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; } -int wm_adsp2_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +static void wm_adsp2_boot_work(struct work_struct *work) { - struct snd_soc_codec *codec = w->codec; - struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); - struct wm_adsp *dsp = &dsps[w->shift]; - struct wm_adsp_alg_region *alg_region; - struct wm_coeff_ctl *ctl; - unsigned int val; + struct wm_adsp *dsp = container_of(work, + struct wm_adsp, + boot_work); int ret; + unsigned int val; - dsp->card = codec->card; + /* + * For simplicity set the DSP clock rate to be the + * SYSCLK rate rather than making it configurable. + */ + ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); + if (ret != 0) { + adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); + return; + } + val = (val & ARIZONA_SYSCLK_FREQ_MASK) + >> ARIZONA_SYSCLK_FREQ_SHIFT; - switch (event) { - case SND_SOC_DAPM_POST_PMU: - /* - * For simplicity set the DSP clock rate to be the - * SYSCLK rate rather than making it configurable. - */ - ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); - if (ret != 0) { - adsp_err(dsp, "Failed to read SYSCLK state: %d\n", - ret); - return ret; - } - val = (val & ARIZONA_SYSCLK_FREQ_MASK) - >> ARIZONA_SYSCLK_FREQ_SHIFT; + 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); + return; + } - ret = regmap_update_bits(dsp->regmap, - dsp->base + ADSP2_CLOCKING, - ADSP2_CLK_SEL_MASK, val); + if (dsp->dvfs) { + ret = regmap_read(dsp->regmap, + dsp->base + ADSP2_CLOCKING, &val); if (ret != 0) { - adsp_err(dsp, "Failed to set clock rate: %d\n", - ret); - return ret; + dev_err(dsp->dev, "Failed to read clocking: %d\n", ret); + return; } - if (dsp->dvfs) { - ret = regmap_read(dsp->regmap, - dsp->base + ADSP2_CLOCKING, &val); + if ((val & ADSP2_CLK_SEL_MASK) >= 3) { + ret = regulator_enable(dsp->dvfs); if (ret != 0) { dev_err(dsp->dev, - "Failed to read clocking: %d\n", ret); - return ret; + "Failed to enable supply: %d\n", + ret); + return; } - if ((val & ADSP2_CLK_SEL_MASK) >= 3) { - ret = regulator_enable(dsp->dvfs); - if (ret != 0) { - dev_err(dsp->dev, - "Failed to enable supply: %d\n", - ret); - return ret; - } - - ret = regulator_set_voltage(dsp->dvfs, - 1800000, - 1800000); - if (ret != 0) { - dev_err(dsp->dev, - "Failed to raise supply: %d\n", - ret); - return ret; - } + ret = regulator_set_voltage(dsp->dvfs, + 1800000, + 1800000); + if (ret != 0) { + dev_err(dsp->dev, + "Failed to raise supply: %d\n", + ret); + return; } } + } - ret = wm_adsp2_ena(dsp); - if (ret != 0) - return ret; + ret = wm_adsp2_ena(dsp); + if (ret != 0) + return; - ret = wm_adsp_load(dsp); - if (ret != 0) - goto err; + ret = wm_adsp_load(dsp); + if (ret != 0) + goto err; - ret = wm_adsp_setup_algs(dsp); - if (ret != 0) - goto err; + ret = wm_adsp_setup_algs(dsp); + if (ret != 0) + goto err; - ret = wm_adsp_load_coeff(dsp); - if (ret != 0) - goto err; + ret = wm_adsp_load_coeff(dsp); + if (ret != 0) + goto err; - /* Initialize caches for enabled and unset controls */ - ret = wm_coeff_init_control_caches(dsp); - if (ret != 0) - goto err; + /* Initialize caches for enabled and unset controls */ + ret = wm_coeff_init_control_caches(dsp); + if (ret != 0) + goto err; - /* Sync set controls */ - ret = wm_coeff_sync_controls(dsp); - if (ret != 0) - goto err; + /* Sync set controls */ + ret = wm_coeff_sync_controls(dsp); + if (ret != 0) + goto err; + + ret = regmap_update_bits_async(dsp->regmap, + dsp->base + ADSP2_CONTROL, + ADSP2_CORE_ENA, + ADSP2_CORE_ENA); + if (ret != 0) + goto err; + + dsp->running = true; + + return; + +err: + regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, + ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); +} + +int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); + struct wm_adsp *dsp = &dsps[w->shift]; + + dsp->card = codec->card; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + queue_work(system_unbound_wq, &dsp->boot_work); + break; + default: + break; + }; + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp2_early_event); + +int wm_adsp2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); + struct wm_adsp *dsp = &dsps[w->shift]; + struct wm_adsp_alg_region *alg_region; + struct wm_coeff_ctl *ctl; + int ret; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + flush_work(&dsp->boot_work); + + if (!dsp->running) + return -EIO; ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_CORE_ENA | ADSP2_START, - ADSP2_CORE_ENA | ADSP2_START); + ADSP2_START, + ADSP2_START); if (ret != 0) goto err; - - dsp->running = true; break; case SND_SOC_DAPM_PRE_PMD: @@ -1633,6 +1693,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, list_del(&alg_region->list); kfree(alg_region); } + + adsp_dbg(dsp, "Shutdown complete\n"); break; default: @@ -1664,6 +1726,7 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) INIT_LIST_HEAD(&adsp->alg_regions); INIT_LIST_HEAD(&adsp->ctl_list); + INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work); if (dvfs) { adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index d018dea6254d..a4f6b64deb61 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -59,6 +59,8 @@ struct wm_adsp { struct regulator *dvfs; struct list_head ctl_list; + + struct work_struct boot_work; }; #define WM_ADSP1(wname, num) \ @@ -66,8 +68,12 @@ struct wm_adsp { wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) #define WM_ADSP2(wname, num) \ - SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \ - wm_adsp2_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) +{ .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \ + .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_early_event, \ + .event_flags = SND_SOC_DAPM_PRE_PMU }, \ +{ .id = snd_soc_dapm_out_drv, .name = wname, \ + .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \ + .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } extern const struct snd_kcontrol_new wm_adsp1_fw_controls[]; extern const struct snd_kcontrol_new wm_adsp2_fw_controls[]; @@ -76,6 +82,8 @@ int wm_adsp1_init(struct wm_adsp *adsp); int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); 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..621e9a997d4c 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -17,6 +17,7 @@ #include <linux/platform_data/edma.h> #include <linux/i2c.h> #include <linux/of_platform.h> +#include <linux/clk.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> @@ -28,14 +29,36 @@ #include "davinci-pcm.h" #include "davinci-i2s.h" -#include "davinci-mcasp.h" struct snd_soc_card_drvdata_davinci { + struct clk *mclk; unsigned sysclk; }; -#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \ - SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF) +static int evm_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *soc_card = rtd->codec->card; + struct snd_soc_card_drvdata_davinci *drvdata = + snd_soc_card_get_drvdata(soc_card); + + if (drvdata->mclk) + return clk_prepare_enable(drvdata->mclk); + + return 0; +} + +static void evm_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *soc_card = rtd->codec->card; + struct snd_soc_card_drvdata_davinci *drvdata = + snd_soc_card_get_drvdata(soc_card); + + if (drvdata->mclk) + clk_disable_unprepare(drvdata->mclk); +} + static int evm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -48,16 +71,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 +84,12 @@ 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 = { + .startup = evm_startup, + .shutdown = evm_shutdown, .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 +166,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 +179,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 +189,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 +215,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 +225,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 +239,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 +252,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 +350,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[] = { @@ -360,6 +376,7 @@ static int davinci_evm_probe(struct platform_device *pdev) of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev); struct snd_soc_dai_link *dai = (struct snd_soc_dai_link *) match->data; struct snd_soc_card_drvdata_davinci *drvdata = NULL; + struct clk *mclk; int ret = 0; evm_soc_card.dai_link = dai; @@ -379,13 +396,38 @@ static int davinci_evm_probe(struct platform_device *pdev) if (ret) return ret; + mclk = devm_clk_get(&pdev->dev, "mclk"); + if (PTR_ERR(mclk) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (IS_ERR(mclk)) { + dev_dbg(&pdev->dev, "mclk not found.\n"); + mclk = NULL; + } + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; + drvdata->mclk = mclk; + ret = of_property_read_u32(np, "ti,codec-clock-rate", &drvdata->sysclk); - if (ret < 0) - return -EINVAL; + + if (ret < 0) { + if (!drvdata->mclk) { + dev_err(&pdev->dev, + "No clock or clock rate defined.\n"); + return -EINVAL; + } + drvdata->sysclk = clk_get_rate(drvdata->mclk); + } else if (drvdata->mclk) { + unsigned int requestd_rate = drvdata->sysclk; + clk_set_rate(drvdata->mclk, drvdata->sysclk); + drvdata->sysclk = clk_get_rate(drvdata->mclk); + if (drvdata->sysclk != requestd_rate) + dev_warn(&pdev->dev, + "Could not get requested rate %u using %u.\n", + requestd_rate, drvdata->sysclk); + } snd_soc_card_set_drvdata(&evm_soc_card, drvdata); ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card); @@ -411,6 +453,7 @@ static struct platform_driver davinci_evm_driver = { .driver = { .name = "davinci_evm", .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, .of_match_table = of_match_ptr(davinci_evm_dt_ids), }, }; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 71e14bb3a8cd..b0ae0677f023 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,152 @@ #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 - -/* - * 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) +struct davinci_mcasp_context { + u32 txfmtctl; + u32 rxfmtctl; + u32 txfmt; + u32 rxfmt; + u32 aclkxctl; + u32 aclkrctl; + u32 pdir; +}; -/* - * 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 */ +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_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) + /* 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_REVTCTL_REG - Receiver DMA Event Control Register bits - */ -#define RXDATADMADIS BIT(0) + int sysclk_freq; + bool bclk_master; -/* - * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits - */ -#define TXDATADMADIS BIT(0) + /* McASP FIFO related */ + u8 txnumevt; + u8 rxnumevt; -/* - * 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) + bool dat_port; -#define DAVINCI_MCASP_NUM_SERIALIZER 16 +#ifdef CONFIG_PM_SLEEP + struct davinci_mcasp_context 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) +{ + u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); + u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG); + + return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE; +} + +static void mcasp_start_rx(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); + 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(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); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); - mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); - mcasp_set_ctl_reg(dev->base + 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,223 +185,215 @@ 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); + int ret = 0; + pm_runtime_get_sync(mcasp->dev); 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); + mcasp->bclk_master = 1; 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); + mcasp->bclk_master = 0; 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); + mcasp->bclk_master = 0; break; default: - return -EINVAL; + ret = -EINVAL; + goto out; } 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: - return -EINVAL; + ret = -EINVAL; + break; } - - return 0; +out: + pm_runtime_put_sync(mcasp->dev); + return ret; } 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 +406,24 @@ 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); } + mcasp->sysclk_freq = freq; + 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 +440,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 mcasp_common_hw_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,146 +511,133 @@ 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 int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream) { int i, active_slots; u32 mask = 0; + u32 busel = 0; + + if ((mcasp->tdm_slots < 2) || (mcasp->tdm_slots > 32)) { + dev_err(mcasp->dev, "tdm slot %d not supported\n", + mcasp->tdm_slots); + return -EINVAL; + } - 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 (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); - - if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32)) - mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, - FSXMOD(dev->tdm_slots), FSXMOD(0x1FF)); - else - printk(KERN_ERR "playback tdm slot %d not supported\n", - dev->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); - - if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32)) - mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, - FSRMOD(dev->tdm_slots), FSRMOD(0x1FF)); - else - printk(KERN_ERR "capture tdm slot %d not supported\n", - dev->tdm_slots); - } + if (!mcasp->dat_port) + busel = TXSEL; + + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, + FSXMOD(mcasp->tdm_slots), FSXMOD(0x1FF)); + + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, + FSRMOD(mcasp->tdm_slots), FSRMOD(0x1FF)); + + return 0; } /* S/PDIF */ -static void davinci_hw_dit_param(struct davinci_audio_dev *dev) +static int mcasp_dit_hw_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); + + return 0; } 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, - SNDRV_PCM_HW_PARAM_CHANNELS); - channels = pcm_channels->min; + int channels = params_channels(params); + int ret; - active_serializers = (channels + slots - 1) / slots; + /* If mcasp is BCLK master we need to set BCLK divider */ + if (mcasp->bclk_master) { + unsigned int bclk_freq = snd_soc_params_to_bclk(params); + if (mcasp->sysclk_freq % bclk_freq != 0) { + dev_err(mcasp->dev, "Can't produce requred BCLK\n"); + return -EINVAL; + } + davinci_mcasp_set_clkdiv( + cpu_dai, 1, mcasp->sysclk_freq / bclk_freq); + } - if (davinci_hw_common_param(dev, substream->stream, channels) == -EINVAL) - return -EINVAL; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - fifo_level = dev->txnumevt * active_serializers; - else - fifo_level = dev->rxnumevt * active_serializers; + ret = mcasp_common_hw_param(mcasp, substream->stream, channels); + if (ret) + return ret; - if (dev->op_mode == DAVINCI_MCASP_DIT_MODE) - davinci_hw_dit_param(dev); + if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) + ret = mcasp_dit_hw_param(mcasp); else - davinci_hw_param(dev, substream->stream); + ret = mcasp_i2s_hw_param(mcasp, substream->stream); + + if (ret) + return ret; switch (params_format(params)) { case SNDRV_PCM_FORMAT_U8: @@ -891,13 +671,22 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - if (dev->version == MCASP_VERSION_2 && !fifo_level) + /* Calculate FIFO level */ + active_serializers = (channels + slots - 1) / slots; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + fifo_level = mcasp->txnumevt * active_serializers; + else + fifo_level = mcasp->rxnumevt * active_serializers; + + 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 +694,19 @@ 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); - if (IS_ERR_VALUE(ret)) - dev_err(dev->dev, "pm_runtime_get_sync() failed\n"); - davinci_mcasp_start(dev, substream->stream); + 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); - if (IS_ERR_VALUE(ret)) - dev_err(dev->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 +719,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 +739,45 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { .set_sysclk = davinci_mcasp_set_sysclk, }; +#ifdef CONFIG_PM_SLEEP +static int davinci_mcasp_suspend(struct snd_soc_dai *dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + struct davinci_mcasp_context *context = &mcasp->context; + + context->txfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG); + context->rxfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); + context->txfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMT_REG); + context->rxfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMT_REG); + context->aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG); + context->aclkrctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG); + context->pdir = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG); + + return 0; +} + +static int davinci_mcasp_resume(struct snd_soc_dai *dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + struct davinci_mcasp_context *context = &mcasp->context; + + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, context->txfmtctl); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG, context->rxfmtctl); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMT_REG, context->txfmt); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMT_REG, context->rxfmt); + mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, context->aclkxctl); + mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, context->aclkrctl); + mcasp_set_reg(mcasp, DAVINCI_MCASP_PDIR_REG, context->pdir); + + return 0; +} +#else +#define davinci_mcasp_suspend NULL +#define davinci_mcasp_resume NULL +#endif + +#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 | \ @@ -969,6 +792,8 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { static struct snd_soc_dai_driver davinci_mcasp_dai[] = { { .name = "davinci-mcasp.0", + .suspend = davinci_mcasp_suspend, + .resume = davinci_mcasp_resume, .playback = { .channels_min = 2, .channels_max = 32 * 16, @@ -985,7 +810,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = { }, { - "davinci-mcasp.1", + .name = "davinci-mcasp.1", .playback = { .channels_min = 1, .channels_max = 384, @@ -1002,27 +827,34 @@ static const struct snd_soc_component_driver davinci_mcasp_component = { }; /* Some HW specific values and defaults. The rest is filled in from DT. */ -static struct snd_platform_data dm646x_mcasp_pdata = { +static struct davinci_mcasp_pdata dm646x_mcasp_pdata = { .tx_dma_offset = 0x400, .rx_dma_offset = 0x400, .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_1, }; -static struct snd_platform_data da830_mcasp_pdata = { +static struct davinci_mcasp_pdata da830_mcasp_pdata = { .tx_dma_offset = 0x2000, .rx_dma_offset = 0x2000, .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_2, }; -static struct snd_platform_data omap2_mcasp_pdata = { +static struct davinci_mcasp_pdata am33xx_mcasp_pdata = { .tx_dma_offset = 0, .rx_dma_offset = 0, .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_3, }; +static struct davinci_mcasp_pdata 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,17 +866,61 @@ 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 struct snd_platform_data *davinci_mcasp_set_pdata_from_of( +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 davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of( struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct snd_platform_data *pdata = NULL; + struct davinci_mcasp_pdata *pdata = NULL; const struct of_device_id *match = of_match_device(mcasp_dt_ids, &pdev->dev); struct of_phandle_args dma_spec; @@ -1057,7 +933,7 @@ static struct snd_platform_data *davinci_mcasp_set_pdata_from_of( pdata = pdev->dev.platform_data; return pdata; } else if (match) { - pdata = (struct snd_platform_data *) match->data; + pdata = (struct davinci_mcasp_pdata*) match->data; } else { /* control shouldn't reach here. something is wrong */ ret = -EINVAL; @@ -1149,10 +1025,10 @@ nodata: static int davinci_mcasp_probe(struct platform_device *pdev) { - struct davinci_pcm_dma_params *dma_data; + struct davinci_pcm_dma_params *dma_params; struct resource *mem, *ioarea, *res, *dat; - struct snd_platform_data *pdata; - struct davinci_audio_dev *dev; + struct davinci_mcasp_pdata *pdata; + struct davinci_mcasp *mcasp; int ret; if (!pdev->dev.platform_data && !pdev->dev.of_node) { @@ -1160,9 +1036,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 +1049,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,63 +1073,93 @@ 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_params = &mcasp->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; + dma_params->asp_chan_q = pdata->asp_chan_q; + dma_params->ram_chan_q = pdata->ram_chan_q; + dma_params->sram_pool = pdata->sram_pool; + dma_params->sram_size = pdata->sram_size_playback; + if (dat) + dma_params->dma_addr = dat->start; + else + dma_params->dma_addr = mem->start + pdata->tx_dma_offset; - dma_data = &dev->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; + /* Unconditional dmaengine stuff */ + mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = dma_params->dma_addr; res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (res) - dma_data->channel = res->start; + dma_params->channel = res->start; else - dma_data->channel = pdata->tx_dma_channel; + dma_params->channel = pdata->tx_dma_channel; + + dma_params = &mcasp->dma_params[SNDRV_PCM_STREAM_CAPTURE]; + dma_params->asp_chan_q = pdata->asp_chan_q; + dma_params->ram_chan_q = pdata->ram_chan_q; + dma_params->sram_pool = pdata->sram_pool; + dma_params->sram_size = pdata->sram_size_capture; + if (dat) + dma_params->dma_addr = dat->start; + else + dma_params->dma_addr = mem->start + pdata->rx_dma_offset; + + /* Unconditional dmaengine stuff */ + mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = dma_params->dma_addr; - dma_data = &dev->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 (mcasp->version < MCASP_VERSION_3) { + mcasp->fifo_base = DAVINCI_MCASP_V2_AFIFO_BASE; + /* dma_params->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) - dma_data->channel = res->start; + dma_params->channel = res->start; else - dma_data->channel = pdata->rx_dma_channel; + dma_params->channel = pdata->rx_dma_channel; + + /* 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); - dev_set_drvdata(&pdev->dev, dev); 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 +1174,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); @@ -1278,51 +1186,12 @@ static int davinci_mcasp_remove(struct platform_device *pdev) return 0; } -#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; - - 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); - - 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); - - return 0; -} -#endif - -SIMPLE_DEV_PM_OPS(davinci_mcasp_pm_ops, - davinci_mcasp_suspend, - davinci_mcasp_resume); - static struct platform_driver davinci_mcasp_driver = { .probe = davinci_mcasp_probe, .remove = davinci_mcasp_remove, .driver = { .name = "davinci-mcasp", .owner = THIS_MODULE, - .pm = &davinci_mcasp_pm_ops, .of_match_table = mcasp_dt_ids, }, }; 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..597962ec28fa 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,8 +1,18 @@ +config SND_SOC_FSL_SAI + tristate + select REGMAP_MMIO + select SND_SOC_GENERIC_DMAENGINE_PCM + config SND_SOC_FSL_SSI tristate config SND_SOC_FSL_SPDIF tristate + select REGMAP_MMIO + +config SND_SOC_FSL_ESAI + tristate + select REGMAP_MMIO config SND_SOC_FSL_UTILS tristate @@ -161,12 +171,14 @@ config SND_SOC_EUKREA_TLV320 depends on MACH_EUKREA_MBIMX27_BASEBOARD \ || MACH_EUKREA_MBIMXSD25_BASEBOARD \ || MACH_EUKREA_MBIMXSD35_BASEBOARD \ - || MACH_EUKREA_MBIMXSD51_BASEBOARD + || MACH_EUKREA_MBIMXSD51_BASEBOARD \ + || (OF && ARM) depends on I2C - select SND_SOC_TLV320AIC23 - select SND_SOC_IMX_PCM_FIQ + select SND_SOC_TLV320AIC23_I2C select SND_SOC_IMX_AUDMUX select SND_SOC_IMX_SSI + select SND_SOC_FSL_SSI + select SND_SOC_IMX_PCM_DMA help Enable I2S based access to the TLV320AIC23B codec attached to the SSI interface @@ -197,8 +209,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 Say Y if you want to add support for SoC audio on an i.MX board with @@ -206,7 +216,7 @@ config SND_SOC_IMX_SPDIF config SND_SOC_IMX_MC13783 tristate "SoC Audio support for I.MX boards with mc13783" - depends on MFD_MC13783 && ARM + depends on MFD_MC13XXX && ARM select SND_SOC_IMX_SSI select SND_SOC_IMX_AUDMUX select SND_SOC_MC13783 diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 8db705b0fdf9..b12ad4b9b4da 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -10,13 +10,17 @@ 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-esai-objs := fsl_esai.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_ESAI) += snd-soc-fsl-esai.o obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index 5983740be123..eb093d5b85c4 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -15,8 +15,11 @@ * */ +#include <linux/errno.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/device.h> #include <linux/i2c.h> #include <sound/core.h> @@ -26,6 +29,7 @@ #include "../codecs/tlv320aic23.h" #include "imx-ssi.h" +#include "fsl_ssi.h" #include "imx-audmux.h" #define CODEC_CLOCK 12000000 @@ -41,7 +45,8 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream, ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret) { + /* fsl_ssi lacks the set_fmt ops. */ + if (ret && ret != -ENOTSUPP) { dev_err(cpu_dai->dev, "Failed to set the cpu dai format.\n"); return ret; @@ -63,11 +68,13 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream, "Failed to set the codec sysclk.\n"); return ret; } + snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0); ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_IN); - if (ret) { + /* fsl_ssi lacks the set_sysclk ops */ + if (ret && ret != -EINVAL) { dev_err(cpu_dai->dev, "Can't set the IMX_SSP_SYS_CLK CPU system clock.\n"); return ret; @@ -84,14 +91,10 @@ static struct snd_soc_dai_link eukrea_tlv320_dai = { .name = "tlv320aic23", .stream_name = "TLV320AIC23", .codec_dai_name = "tlv320aic23-hifi", - .platform_name = "imx-ssi.0", - .codec_name = "tlv320aic23-codec.0-001a", - .cpu_dai_name = "imx-ssi.0", .ops = &eukrea_tlv320_snd_ops, }; static struct snd_soc_card eukrea_tlv320 = { - .name = "cpuimx-audio", .owner = THIS_MODULE, .dai_link = &eukrea_tlv320_dai, .num_links = 1, @@ -101,8 +104,65 @@ static int eukrea_tlv320_probe(struct platform_device *pdev) { int ret; int int_port = 0, ext_port; + struct device_node *np = pdev->dev.of_node; + struct device_node *ssi_np, *codec_np; - if (machine_is_eukrea_cpuimx27()) { + eukrea_tlv320.dev = &pdev->dev; + if (np) { + ret = snd_soc_of_parse_card_name(&eukrea_tlv320, + "eukrea,model"); + if (ret) { + dev_err(&pdev->dev, + "eukrea,model node missing or invalid.\n"); + goto err; + } + + ssi_np = of_parse_phandle(pdev->dev.of_node, + "ssi-controller", 0); + if (!ssi_np) { + dev_err(&pdev->dev, + "ssi-controller missing or invalid.\n"); + ret = -ENODEV; + goto err; + } + + codec_np = of_parse_phandle(ssi_np, "codec-handle", 0); + if (codec_np) + eukrea_tlv320_dai.codec_of_node = codec_np; + else + dev_err(&pdev->dev, "codec-handle node missing or invalid.\n"); + + ret = of_property_read_u32(np, "fsl,mux-int-port", &int_port); + if (ret) { + dev_err(&pdev->dev, + "fsl,mux-int-port node missing or invalid.\n"); + return ret; + } + ret = of_property_read_u32(np, "fsl,mux-ext-port", &ext_port); + if (ret) { + dev_err(&pdev->dev, + "fsl,mux-ext-port node missing or invalid.\n"); + return ret; + } + + /* + * The port numbering in the hardware manual starts at 1, while + * the audmux API expects it starts at 0. + */ + int_port--; + ext_port--; + + eukrea_tlv320_dai.cpu_of_node = ssi_np; + eukrea_tlv320_dai.platform_of_node = ssi_np; + } else { + eukrea_tlv320_dai.cpu_dai_name = "imx-ssi.0"; + eukrea_tlv320_dai.platform_name = "imx-ssi.0"; + eukrea_tlv320_dai.codec_name = "tlv320aic23-codec.0-001a"; + eukrea_tlv320.name = "cpuimx-audio"; + } + + if (machine_is_eukrea_cpuimx27() || + of_find_compatible_node(NULL, NULL, "fsl,imx21-audmux")) { imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, IMX_AUDMUX_V1_PCR_SYN | IMX_AUDMUX_V1_PCR_TFSDIR | @@ -119,8 +179,12 @@ static int eukrea_tlv320_probe(struct platform_device *pdev) ); } else if (machine_is_eukrea_cpuimx25sd() || machine_is_eukrea_cpuimx35sd() || - machine_is_eukrea_cpuimx51sd()) { - ext_port = machine_is_eukrea_cpuimx25sd() ? 4 : 3; + machine_is_eukrea_cpuimx51sd() || + of_find_compatible_node(NULL, NULL, "fsl,imx31-audmux")) { + if (!np) + ext_port = machine_is_eukrea_cpuimx25sd() ? + 4 : 3; + imx_audmux_v2_configure_port(int_port, IMX_AUDMUX_V2_PTCR_SYN | IMX_AUDMUX_V2_PTCR_TFSDIR | @@ -134,14 +198,27 @@ static int eukrea_tlv320_probe(struct platform_device *pdev) IMX_AUDMUX_V2_PDCR_RXDSEL(int_port) ); } else { - /* return happy. We might run on a totally different machine */ - return 0; + if (np) { + /* The eukrea,asoc-tlv320 driver was explicitely + * requested (through the device tree). + */ + dev_err(&pdev->dev, + "Missing or invalid audmux DT node.\n"); + return -ENODEV; + } else { + /* Return happy. + * We might run on a totally different machine. + */ + return 0; + } } - eukrea_tlv320.dev = &pdev->dev; ret = snd_soc_register_card(&eukrea_tlv320); +err: if (ret) dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + if (np) + of_node_put(ssi_np); return ret; } @@ -153,10 +230,17 @@ static int eukrea_tlv320_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id imx_tlv320_dt_ids[] = { + { .compatible = "eukrea,asoc-tlv320"}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_tlv320_dt_ids); + static struct platform_driver eukrea_tlv320_driver = { .driver = { .name = "eukrea_tlv320", .owner = THIS_MODULE, + .of_match_table = imx_tlv320_dt_ids, }, .probe = eukrea_tlv320_probe, .remove = eukrea_tlv320_remove, diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index fb9bb9eb5ca3..6bb0ea59284f 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -55,10 +55,6 @@ SNDRV_PCM_FMTBIT_S32_BE | \ SNDRV_PCM_FMTBIT_U32_LE | \ SNDRV_PCM_FMTBIT_U32_BE) - -#define FSLDMA_PCM_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ - SNDRV_PCM_RATE_CONTINUOUS) - struct dma_object { struct snd_soc_platform_driver dai; dma_addr_t ssi_stx_phys; @@ -140,9 +136,6 @@ static const struct snd_pcm_hardware fsl_dma_hardware = { SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_PAUSE, .formats = FSLDMA_PCM_FORMATS, - .rates = FSLDMA_PCM_RATES, - .rate_min = 5512, - .rate_max = 192000, .period_bytes_min = 512, /* A reasonable limit */ .period_bytes_max = (u32) -1, .periods_min = NUM_DMA_LINKS, @@ -852,7 +845,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_esai.c b/sound/soc/fsl/fsl_esai.c new file mode 100644 index 000000000000..0ba37005ab04 --- /dev/null +++ b/sound/soc/fsl/fsl_esai.c @@ -0,0 +1,835 @@ +/* + * Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/dmaengine.h> +#include <linux/module.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> + +#include "fsl_esai.h" +#include "imx-pcm.h" + +#define FSL_ESAI_RATES SNDRV_PCM_RATE_8000_192000 +#define FSL_ESAI_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +/** + * fsl_esai: ESAI private data + * + * @dma_params_rx: DMA parameters for receive channel + * @dma_params_tx: DMA parameters for transmit channel + * @pdev: platform device pointer + * @regmap: regmap handler + * @coreclk: clock source to access register + * @extalclk: esai clock source to derive HCK, SCK and FS + * @fsysclk: system clock source to derive HCK, SCK and FS + * @fifo_depth: depth of tx/rx FIFO + * @slot_width: width of each DAI slot + * @hck_rate: clock rate of desired HCKx clock + * @sck_div: if using PSR/PM dividers for SCKx clock + * @slave_mode: if fully using DAI slave mode + * @synchronous: if using tx/rx synchronous mode + * @name: driver name + */ +struct fsl_esai { + struct snd_dmaengine_dai_dma_data dma_params_rx; + struct snd_dmaengine_dai_dma_data dma_params_tx; + struct platform_device *pdev; + struct regmap *regmap; + struct clk *coreclk; + struct clk *extalclk; + struct clk *fsysclk; + u32 fifo_depth; + u32 slot_width; + u32 hck_rate[2]; + bool sck_div[2]; + bool slave_mode; + bool synchronous; + char name[32]; +}; + +static irqreturn_t esai_isr(int irq, void *devid) +{ + struct fsl_esai *esai_priv = (struct fsl_esai *)devid; + struct platform_device *pdev = esai_priv->pdev; + u32 esr; + + regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr); + + if (esr & ESAI_ESR_TINIT_MASK) + dev_dbg(&pdev->dev, "isr: Transmition Initialized\n"); + + if (esr & ESAI_ESR_RFF_MASK) + dev_warn(&pdev->dev, "isr: Receiving overrun\n"); + + if (esr & ESAI_ESR_TFE_MASK) + dev_warn(&pdev->dev, "isr: Transmition underrun\n"); + + if (esr & ESAI_ESR_TLS_MASK) + dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n"); + + if (esr & ESAI_ESR_TDE_MASK) + dev_dbg(&pdev->dev, "isr: Transmition data exception\n"); + + if (esr & ESAI_ESR_TED_MASK) + dev_dbg(&pdev->dev, "isr: Transmitting even slots\n"); + + if (esr & ESAI_ESR_TD_MASK) + dev_dbg(&pdev->dev, "isr: Transmitting data\n"); + + if (esr & ESAI_ESR_RLS_MASK) + dev_dbg(&pdev->dev, "isr: Just received the last slot\n"); + + if (esr & ESAI_ESR_RDE_MASK) + dev_dbg(&pdev->dev, "isr: Receiving data exception\n"); + + if (esr & ESAI_ESR_RED_MASK) + dev_dbg(&pdev->dev, "isr: Receiving even slots\n"); + + if (esr & ESAI_ESR_RD_MASK) + dev_dbg(&pdev->dev, "isr: Receiving data\n"); + + return IRQ_HANDLED; +} + +/** + * This function is used to calculate the divisors of psr, pm, fp and it is + * supposed to be called in set_dai_sysclk() and set_bclk(). + * + * @ratio: desired overall ratio for the paticipating dividers + * @usefp: for HCK setting, there is no need to set fp divider + * @fp: bypass other dividers by setting fp directly if fp != 0 + * @tx: current setting is for playback or capture + */ +static int fsl_esai_divisor_cal(struct snd_soc_dai *dai, bool tx, u32 ratio, + bool usefp, u32 fp) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + u32 psr, pm = 999, maxfp, prod, sub, savesub, i, j; + + maxfp = usefp ? 16 : 1; + + if (usefp && fp) + goto out_fp; + + if (ratio > 2 * 8 * 256 * maxfp || ratio < 2) { + dev_err(dai->dev, "the ratio is out of range (2 ~ %d)\n", + 2 * 8 * 256 * maxfp); + return -EINVAL; + } else if (ratio % 2) { + dev_err(dai->dev, "the raio must be even if using upper divider\n"); + return -EINVAL; + } + + ratio /= 2; + + psr = ratio <= 256 * maxfp ? ESAI_xCCR_xPSR_BYPASS : ESAI_xCCR_xPSR_DIV8; + + /* Set the max fluctuation -- 0.1% of the max devisor */ + savesub = (psr ? 1 : 8) * 256 * maxfp / 1000; + + /* Find the best value for PM */ + for (i = 1; i <= 256; i++) { + for (j = 1; j <= maxfp; j++) { + /* PSR (1 or 8) * PM (1 ~ 256) * FP (1 ~ 16) */ + prod = (psr ? 1 : 8) * i * j; + + if (prod == ratio) + sub = 0; + else if (prod / ratio == 1) + sub = prod - ratio; + else if (ratio / prod == 1) + sub = ratio - prod; + else + continue; + + /* Calculate the fraction */ + sub = sub * 1000 / ratio; + if (sub < savesub) { + savesub = sub; + pm = i; + fp = j; + } + + /* We are lucky */ + if (savesub == 0) + goto out; + } + } + + if (pm == 999) { + dev_err(dai->dev, "failed to calculate proper divisors\n"); + return -EINVAL; + } + +out: + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx), + ESAI_xCCR_xPSR_MASK | ESAI_xCCR_xPM_MASK, + psr | ESAI_xCCR_xPM(pm)); + +out_fp: + /* Bypass fp if not being required */ + if (maxfp <= 1) + return 0; + + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx), + ESAI_xCCR_xFP_MASK, ESAI_xCCR_xFP(fp)); + + return 0; +} + +/** + * This function mainly configures the clock frequency of MCLK (HCKT/HCKR) + * + * @Parameters: + * clk_id: The clock source of HCKT/HCKR + * (Input from outside; output from inside, FSYS or EXTAL) + * freq: The required clock rate of HCKT/HCKR + * dir: The clock direction of HCKT/HCKR + * + * Note: If the direction is input, we do not care about clk_id. + */ +static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + struct clk *clksrc = esai_priv->extalclk; + bool tx = clk_id <= ESAI_HCKT_EXTAL; + bool in = dir == SND_SOC_CLOCK_IN; + u32 ret, ratio, ecr = 0; + unsigned long clk_rate; + + /* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */ + esai_priv->sck_div[tx] = true; + + /* Set the direction of HCKT/HCKR pins */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx), + ESAI_xCCR_xHCKD, in ? 0 : ESAI_xCCR_xHCKD); + + if (in) + goto out; + + switch (clk_id) { + case ESAI_HCKT_FSYS: + case ESAI_HCKR_FSYS: + clksrc = esai_priv->fsysclk; + break; + case ESAI_HCKT_EXTAL: + ecr |= ESAI_ECR_ETI; + case ESAI_HCKR_EXTAL: + ecr |= ESAI_ECR_ERI; + break; + default: + return -EINVAL; + } + + if (IS_ERR(clksrc)) { + dev_err(dai->dev, "no assigned %s clock\n", + clk_id % 2 ? "extal" : "fsys"); + return PTR_ERR(clksrc); + } + clk_rate = clk_get_rate(clksrc); + + ratio = clk_rate / freq; + if (ratio * freq > clk_rate) + ret = ratio * freq - clk_rate; + else if (ratio * freq < clk_rate) + ret = clk_rate - ratio * freq; + else + ret = 0; + + /* Block if clock source can not be divided into the required rate */ + if (ret != 0 && clk_rate / ret < 1000) { + dev_err(dai->dev, "failed to derive required HCK%c rate\n", + tx ? 'T' : 'R'); + return -EINVAL; + } + + if (ratio == 1) { + /* Bypass all the dividers if not being needed */ + ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO; + goto out; + } + + ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0); + if (ret) + return ret; + + esai_priv->sck_div[tx] = false; + +out: + esai_priv->hck_rate[tx] = freq; + + regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, + tx ? ESAI_ECR_ETI | ESAI_ECR_ETO : + ESAI_ECR_ERI | ESAI_ECR_ERO, ecr); + + return 0; +} + +/** + * This function configures the related dividers according to the bclk rate + */ +static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + u32 hck_rate = esai_priv->hck_rate[tx]; + u32 sub, ratio = hck_rate / freq; + + /* Don't apply for fully slave mode*/ + if (esai_priv->slave_mode) + return 0; + + if (ratio * freq > hck_rate) + sub = ratio * freq - hck_rate; + else if (ratio * freq < hck_rate) + sub = hck_rate - ratio * freq; + else + sub = 0; + + /* Block if clock source can not be divided into the required rate */ + if (sub != 0 && hck_rate / sub < 1000) { + dev_err(dai->dev, "failed to derive required SCK%c rate\n", + tx ? 'T' : 'R'); + return -EINVAL; + } + + if (esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) { + dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n"); + return -EINVAL; + } + + return fsl_esai_divisor_cal(dai, tx, ratio, true, + esai_priv->sck_div[tx] ? 0 : ratio); +} + +static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, + u32 rx_mask, int slots, int slot_width) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, + ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots)); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMA, + ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(tx_mask)); + regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMB, + ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(tx_mask)); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, + ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots)); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMA, + ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(rx_mask)); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMB, + ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(rx_mask)); + + esai_priv->slot_width = slot_width; + + return 0; +} + +static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + u32 xcr = 0, xccr = 0, mask; + + /* DAI mode */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* Data on rising edge of bclk, frame low, 1clk before data */ + xcr |= ESAI_xCR_xFSR; + xccr |= ESAI_xCCR_xFSP | ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; + break; + case SND_SOC_DAIFMT_LEFT_J: + /* Data on rising edge of bclk, frame high */ + xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; + break; + case SND_SOC_DAIFMT_RIGHT_J: + /* Data on rising edge of bclk, frame high, right aligned */ + xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCR_xWA; + break; + case SND_SOC_DAIFMT_DSP_A: + /* Data on rising edge of bclk, frame high, 1clk before data */ + xcr |= ESAI_xCR_xFSL | ESAI_xCR_xFSR; + xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; + break; + case SND_SOC_DAIFMT_DSP_B: + /* Data on rising edge of bclk, frame high */ + xcr |= ESAI_xCR_xFSL; + xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; + 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 */ + xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP; + break; + case SND_SOC_DAIFMT_NB_IF: + /* Invert frame clock */ + xccr ^= ESAI_xCCR_xFSP; + break; + case SND_SOC_DAIFMT_IB_IF: + /* Invert both clocks */ + xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP; + break; + default: + return -EINVAL; + } + + esai_priv->slave_mode = false; + + /* DAI clock master masks */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + esai_priv->slave_mode = true; + break; + case SND_SOC_DAIFMT_CBS_CFM: + xccr |= ESAI_xCCR_xCKD; + break; + case SND_SOC_DAIFMT_CBM_CFS: + xccr |= ESAI_xCCR_xFSD; + break; + case SND_SOC_DAIFMT_CBS_CFS: + xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD; + break; + default: + return -EINVAL; + } + + mask = ESAI_xCR_xFSL | ESAI_xCR_xFSR; + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, xcr); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, mask, xcr); + + mask = ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP | + ESAI_xCCR_xFSD | ESAI_xCCR_xCKD | ESAI_xCR_xWA; + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, mask, xccr); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, mask, xccr); + + return 0; +} + +static int fsl_esai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret; + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + + /* + * Some platforms might use the same bit to gate all three or two of + * clocks, so keep all clocks open/close at the same time for safety + */ + ret = clk_prepare_enable(esai_priv->coreclk); + if (ret) + return ret; + if (!IS_ERR(esai_priv->extalclk)) { + ret = clk_prepare_enable(esai_priv->extalclk); + if (ret) + goto err_extalck; + } + if (!IS_ERR(esai_priv->fsysclk)) { + ret = clk_prepare_enable(esai_priv->fsysclk); + if (ret) + goto err_fsysclk; + } + + if (!dai->active) { + /* Reset Port C */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, + ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO)); + regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, + ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO)); + + /* Set synchronous mode */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR, + ESAI_SAICR_SYNC, esai_priv->synchronous ? + ESAI_SAICR_SYNC : 0); + + /* Set a default slot number -- 2 */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, + ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2)); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, + ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2)); + } + + return 0; + +err_fsysclk: + if (!IS_ERR(esai_priv->extalclk)) + clk_disable_unprepare(esai_priv->extalclk); +err_extalck: + clk_disable_unprepare(esai_priv->coreclk); + + return ret; +} + +static int fsl_esai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u32 width = snd_pcm_format_width(params_format(params)); + u32 channels = params_channels(params); + u32 bclk, mask, val, ret; + + bclk = params_rate(params) * esai_priv->slot_width * 2; + + ret = fsl_esai_set_bclk(dai, tx, bclk); + if (ret) + return ret; + + /* Use Normal mode to support monaural audio */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), + ESAI_xCR_xMOD_MASK, params_channels(params) > 1 ? + ESAI_xCR_xMOD_NETWORK : 0); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR); + + mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK | + (tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK); + val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) | + (tx ? ESAI_xFCR_TE(channels) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(channels)); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val); + + mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0); + val = ESAI_xCR_xSWS(esai_priv->slot_width, width) | (tx ? ESAI_xCR_PADC : 0); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val); + + return 0; +} + +static void fsl_esai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + + if (!IS_ERR(esai_priv->fsysclk)) + clk_disable_unprepare(esai_priv->fsysclk); + if (!IS_ERR(esai_priv->extalclk)) + clk_disable_unprepare(esai_priv->extalclk); + clk_disable_unprepare(esai_priv->coreclk); +} + +static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u8 i, channels = substream->runtime->channels; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN); + + /* Write initial words reqiured by ESAI as normal procedure */ + for (i = 0; tx && i < channels; i++) + regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0); + + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), + tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, + tx ? ESAI_xCR_TE(channels) : ESAI_xCR_RE(channels)); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), + tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0); + + /* Disable and reset FIFO */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR); + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFR, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct snd_soc_dai_ops fsl_esai_dai_ops = { + .startup = fsl_esai_startup, + .shutdown = fsl_esai_shutdown, + .trigger = fsl_esai_trigger, + .hw_params = fsl_esai_hw_params, + .set_sysclk = fsl_esai_set_dai_sysclk, + .set_fmt = fsl_esai_set_dai_fmt, + .set_tdm_slot = fsl_esai_set_dai_tdm_slot, +}; + +static int fsl_esai_dai_probe(struct snd_soc_dai *dai) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &esai_priv->dma_params_tx, + &esai_priv->dma_params_rx); + + return 0; +} + +static struct snd_soc_dai_driver fsl_esai_dai = { + .probe = fsl_esai_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 12, + .rates = FSL_ESAI_RATES, + .formats = FSL_ESAI_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + .rates = FSL_ESAI_RATES, + .formats = FSL_ESAI_FORMATS, + }, + .ops = &fsl_esai_dai_ops, +}; + +static const struct snd_soc_component_driver fsl_esai_component = { + .name = "fsl-esai", +}; + +static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_ESAI_ERDR: + case REG_ESAI_ECR: + case REG_ESAI_ESR: + case REG_ESAI_TFCR: + case REG_ESAI_TFSR: + case REG_ESAI_RFCR: + case REG_ESAI_RFSR: + case REG_ESAI_RX0: + case REG_ESAI_RX1: + case REG_ESAI_RX2: + case REG_ESAI_RX3: + case REG_ESAI_SAISR: + case REG_ESAI_SAICR: + case REG_ESAI_TCR: + case REG_ESAI_TCCR: + case REG_ESAI_RCR: + case REG_ESAI_RCCR: + case REG_ESAI_TSMA: + case REG_ESAI_TSMB: + case REG_ESAI_RSMA: + case REG_ESAI_RSMB: + case REG_ESAI_PRRC: + case REG_ESAI_PCRC: + return true; + default: + return false; + } +} + +static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_ESAI_ETDR: + case REG_ESAI_ECR: + case REG_ESAI_TFCR: + case REG_ESAI_RFCR: + case REG_ESAI_TX0: + case REG_ESAI_TX1: + case REG_ESAI_TX2: + case REG_ESAI_TX3: + case REG_ESAI_TX4: + case REG_ESAI_TX5: + case REG_ESAI_TSR: + case REG_ESAI_SAICR: + case REG_ESAI_TCR: + case REG_ESAI_TCCR: + case REG_ESAI_RCR: + case REG_ESAI_RCCR: + case REG_ESAI_TSMA: + case REG_ESAI_TSMB: + case REG_ESAI_RSMA: + case REG_ESAI_RSMB: + case REG_ESAI_PRRC: + case REG_ESAI_PCRC: + return true; + default: + return false; + } +} + +static struct regmap_config fsl_esai_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = REG_ESAI_PCRC, + .readable_reg = fsl_esai_readable_reg, + .writeable_reg = fsl_esai_writeable_reg, +}; + +static int fsl_esai_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_esai *esai_priv; + struct resource *res; + const uint32_t *iprop; + void __iomem *regs; + int irq, ret; + + esai_priv = devm_kzalloc(&pdev->dev, sizeof(*esai_priv), GFP_KERNEL); + if (!esai_priv) + return -ENOMEM; + + esai_priv->pdev = pdev; + strcpy(esai_priv->name, np->name); + + if (of_property_read_bool(np, "big-endian")) + fsl_esai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; + + /* Get the addresses and IRQ */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + esai_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, + "core", regs, &fsl_esai_regmap_config); + if (IS_ERR(esai_priv->regmap)) { + dev_err(&pdev->dev, "failed to init regmap: %ld\n", + PTR_ERR(esai_priv->regmap)); + return PTR_ERR(esai_priv->regmap); + } + + esai_priv->coreclk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(esai_priv->coreclk)) { + dev_err(&pdev->dev, "failed to get core clock: %ld\n", + PTR_ERR(esai_priv->coreclk)); + return PTR_ERR(esai_priv->coreclk); + } + + esai_priv->extalclk = devm_clk_get(&pdev->dev, "extal"); + if (IS_ERR(esai_priv->extalclk)) + dev_warn(&pdev->dev, "failed to get extal clock: %ld\n", + PTR_ERR(esai_priv->extalclk)); + + esai_priv->fsysclk = devm_clk_get(&pdev->dev, "fsys"); + if (IS_ERR(esai_priv->fsysclk)) + dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n", + PTR_ERR(esai_priv->fsysclk)); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, esai_isr, 0, + esai_priv->name, esai_priv); + if (ret) { + dev_err(&pdev->dev, "failed to claim irq %u\n", irq); + return ret; + } + + /* Set a default slot size */ + esai_priv->slot_width = 32; + + /* Set a default master/slave state */ + esai_priv->slave_mode = true; + + /* Determine the FIFO depth */ + iprop = of_get_property(np, "fsl,fifo-depth", NULL); + if (iprop) + esai_priv->fifo_depth = be32_to_cpup(iprop); + else + esai_priv->fifo_depth = 64; + + esai_priv->dma_params_tx.maxburst = 16; + esai_priv->dma_params_rx.maxburst = 16; + esai_priv->dma_params_tx.addr = res->start + REG_ESAI_ETDR; + esai_priv->dma_params_rx.addr = res->start + REG_ESAI_ERDR; + + esai_priv->synchronous = + of_property_read_bool(np, "fsl,esai-synchronous"); + + /* Implement full symmetry for synchronous mode */ + if (esai_priv->synchronous) { + fsl_esai_dai.symmetric_rates = 1; + fsl_esai_dai.symmetric_channels = 1; + fsl_esai_dai.symmetric_samplebits = 1; + } + + dev_set_drvdata(&pdev->dev, esai_priv); + + /* Reset ESAI unit */ + ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ERST); + if (ret) { + dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret); + return ret; + } + + /* + * We need to enable ESAI so as to access some of its registers. + * Otherwise, we would fail to dump regmap from user space. + */ + ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ESAIEN); + if (ret) { + dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_esai_component, + &fsl_esai_dai, 1); + if (ret) { + dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); + return ret; + } + + ret = imx_pcm_dma_init(pdev); + if (ret) + dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret); + + return ret; +} + +static const struct of_device_id fsl_esai_dt_ids[] = { + { .compatible = "fsl,imx35-esai", }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); + +static struct platform_driver fsl_esai_driver = { + .probe = fsl_esai_probe, + .driver = { + .name = "fsl-esai-dai", + .owner = THIS_MODULE, + .of_match_table = fsl_esai_dt_ids, + }, +}; + +module_platform_driver(fsl_esai_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Freescale ESAI CPU DAI driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:fsl-esai-dai"); diff --git a/sound/soc/fsl/fsl_esai.h b/sound/soc/fsl/fsl_esai.h new file mode 100644 index 000000000000..75e14033e8d8 --- /dev/null +++ b/sound/soc/fsl/fsl_esai.h @@ -0,0 +1,354 @@ +/* + * fsl_esai.h - ALSA ESAI interface for the Freescale i.MX SoC + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Nicolin Chen <Guangyu.Chen@freescale.com> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef _FSL_ESAI_DAI_H +#define _FSL_ESAI_DAI_H + +/* ESAI Register Map */ +#define REG_ESAI_ETDR 0x00 +#define REG_ESAI_ERDR 0x04 +#define REG_ESAI_ECR 0x08 +#define REG_ESAI_ESR 0x0C +#define REG_ESAI_TFCR 0x10 +#define REG_ESAI_TFSR 0x14 +#define REG_ESAI_RFCR 0x18 +#define REG_ESAI_RFSR 0x1C +#define REG_ESAI_xFCR(tx) (tx ? REG_ESAI_TFCR : REG_ESAI_RFCR) +#define REG_ESAI_xFSR(tx) (tx ? REG_ESAI_TFSR : REG_ESAI_RFSR) +#define REG_ESAI_TX0 0x80 +#define REG_ESAI_TX1 0x84 +#define REG_ESAI_TX2 0x88 +#define REG_ESAI_TX3 0x8C +#define REG_ESAI_TX4 0x90 +#define REG_ESAI_TX5 0x94 +#define REG_ESAI_TSR 0x98 +#define REG_ESAI_RX0 0xA0 +#define REG_ESAI_RX1 0xA4 +#define REG_ESAI_RX2 0xA8 +#define REG_ESAI_RX3 0xAC +#define REG_ESAI_SAISR 0xCC +#define REG_ESAI_SAICR 0xD0 +#define REG_ESAI_TCR 0xD4 +#define REG_ESAI_TCCR 0xD8 +#define REG_ESAI_RCR 0xDC +#define REG_ESAI_RCCR 0xE0 +#define REG_ESAI_xCR(tx) (tx ? REG_ESAI_TCR : REG_ESAI_RCR) +#define REG_ESAI_xCCR(tx) (tx ? REG_ESAI_TCCR : REG_ESAI_RCCR) +#define REG_ESAI_TSMA 0xE4 +#define REG_ESAI_TSMB 0xE8 +#define REG_ESAI_RSMA 0xEC +#define REG_ESAI_RSMB 0xF0 +#define REG_ESAI_xSMA(tx) (tx ? REG_ESAI_TSMA : REG_ESAI_RSMA) +#define REG_ESAI_xSMB(tx) (tx ? REG_ESAI_TSMB : REG_ESAI_RSMB) +#define REG_ESAI_PRRC 0xF8 +#define REG_ESAI_PCRC 0xFC + +/* ESAI Control Register -- REG_ESAI_ECR 0x8 */ +#define ESAI_ECR_ETI_SHIFT 19 +#define ESAI_ECR_ETI_MASK (1 << ESAI_ECR_ETI_SHIFT) +#define ESAI_ECR_ETI (1 << ESAI_ECR_ETI_SHIFT) +#define ESAI_ECR_ETO_SHIFT 18 +#define ESAI_ECR_ETO_MASK (1 << ESAI_ECR_ETO_SHIFT) +#define ESAI_ECR_ETO (1 << ESAI_ECR_ETO_SHIFT) +#define ESAI_ECR_ERI_SHIFT 17 +#define ESAI_ECR_ERI_MASK (1 << ESAI_ECR_ERI_SHIFT) +#define ESAI_ECR_ERI (1 << ESAI_ECR_ERI_SHIFT) +#define ESAI_ECR_ERO_SHIFT 16 +#define ESAI_ECR_ERO_MASK (1 << ESAI_ECR_ERO_SHIFT) +#define ESAI_ECR_ERO (1 << ESAI_ECR_ERO_SHIFT) +#define ESAI_ECR_ERST_SHIFT 1 +#define ESAI_ECR_ERST_MASK (1 << ESAI_ECR_ERST_SHIFT) +#define ESAI_ECR_ERST (1 << ESAI_ECR_ERST_SHIFT) +#define ESAI_ECR_ESAIEN_SHIFT 0 +#define ESAI_ECR_ESAIEN_MASK (1 << ESAI_ECR_ESAIEN_SHIFT) +#define ESAI_ECR_ESAIEN (1 << ESAI_ECR_ESAIEN_SHIFT) + +/* ESAI Status Register -- REG_ESAI_ESR 0xC */ +#define ESAI_ESR_TINIT_SHIFT 10 +#define ESAI_ESR_TINIT_MASK (1 << ESAI_ESR_TINIT_SHIFT) +#define ESAI_ESR_TINIT (1 << ESAI_ESR_TINIT_SHIFT) +#define ESAI_ESR_RFF_SHIFT 9 +#define ESAI_ESR_RFF_MASK (1 << ESAI_ESR_RFF_SHIFT) +#define ESAI_ESR_RFF (1 << ESAI_ESR_RFF_SHIFT) +#define ESAI_ESR_TFE_SHIFT 8 +#define ESAI_ESR_TFE_MASK (1 << ESAI_ESR_TFE_SHIFT) +#define ESAI_ESR_TFE (1 << ESAI_ESR_TFE_SHIFT) +#define ESAI_ESR_TLS_SHIFT 7 +#define ESAI_ESR_TLS_MASK (1 << ESAI_ESR_TLS_SHIFT) +#define ESAI_ESR_TLS (1 << ESAI_ESR_TLS_SHIFT) +#define ESAI_ESR_TDE_SHIFT 6 +#define ESAI_ESR_TDE_MASK (1 << ESAI_ESR_TDE_SHIFT) +#define ESAI_ESR_TDE (1 << ESAI_ESR_TDE_SHIFT) +#define ESAI_ESR_TED_SHIFT 5 +#define ESAI_ESR_TED_MASK (1 << ESAI_ESR_TED_SHIFT) +#define ESAI_ESR_TED (1 << ESAI_ESR_TED_SHIFT) +#define ESAI_ESR_TD_SHIFT 4 +#define ESAI_ESR_TD_MASK (1 << ESAI_ESR_TD_SHIFT) +#define ESAI_ESR_TD (1 << ESAI_ESR_TD_SHIFT) +#define ESAI_ESR_RLS_SHIFT 3 +#define ESAI_ESR_RLS_MASK (1 << ESAI_ESR_RLS_SHIFT) +#define ESAI_ESR_RLS (1 << ESAI_ESR_RLS_SHIFT) +#define ESAI_ESR_RDE_SHIFT 2 +#define ESAI_ESR_RDE_MASK (1 << ESAI_ESR_RDE_SHIFT) +#define ESAI_ESR_RDE (1 << ESAI_ESR_RDE_SHIFT) +#define ESAI_ESR_RED_SHIFT 1 +#define ESAI_ESR_RED_MASK (1 << ESAI_ESR_RED_SHIFT) +#define ESAI_ESR_RED (1 << ESAI_ESR_RED_SHIFT) +#define ESAI_ESR_RD_SHIFT 0 +#define ESAI_ESR_RD_MASK (1 << ESAI_ESR_RD_SHIFT) +#define ESAI_ESR_RD (1 << ESAI_ESR_RD_SHIFT) + +/* + * Transmit FIFO Configuration Register -- REG_ESAI_TFCR 0x10 + * Receive FIFO Configuration Register -- REG_ESAI_RFCR 0x18 + */ +#define ESAI_xFCR_TIEN_SHIFT 19 +#define ESAI_xFCR_TIEN_MASK (1 << ESAI_xFCR_TIEN_SHIFT) +#define ESAI_xFCR_TIEN (1 << ESAI_xFCR_TIEN_SHIFT) +#define ESAI_xFCR_REXT_SHIFT 19 +#define ESAI_xFCR_REXT_MASK (1 << ESAI_xFCR_REXT_SHIFT) +#define ESAI_xFCR_REXT (1 << ESAI_xFCR_REXT_SHIFT) +#define ESAI_xFCR_xWA_SHIFT 16 +#define ESAI_xFCR_xWA_WIDTH 3 +#define ESAI_xFCR_xWA_MASK (((1 << ESAI_xFCR_xWA_WIDTH) - 1) << ESAI_xFCR_xWA_SHIFT) +#define ESAI_xFCR_xWA(v) (((8 - ((v) >> 2)) << ESAI_xFCR_xWA_SHIFT) & ESAI_xFCR_xWA_MASK) +#define ESAI_xFCR_xFWM_SHIFT 8 +#define ESAI_xFCR_xFWM_WIDTH 8 +#define ESAI_xFCR_xFWM_MASK (((1 << ESAI_xFCR_xFWM_WIDTH) - 1) << ESAI_xFCR_xFWM_SHIFT) +#define ESAI_xFCR_xFWM(v) ((((v) - 1) << ESAI_xFCR_xFWM_SHIFT) & ESAI_xFCR_xFWM_MASK) +#define ESAI_xFCR_xE_SHIFT 2 +#define ESAI_xFCR_TE_WIDTH 6 +#define ESAI_xFCR_RE_WIDTH 4 +#define ESAI_xFCR_TE_MASK (((1 << ESAI_xFCR_TE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT) +#define ESAI_xFCR_RE_MASK (((1 << ESAI_xFCR_RE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT) +#define ESAI_xFCR_TE(x) ((ESAI_xFCR_TE_MASK >> (ESAI_xFCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_TE_MASK) +#define ESAI_xFCR_RE(x) ((ESAI_xFCR_RE_MASK >> (ESAI_xFCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_RE_MASK) +#define ESAI_xFCR_xFR_SHIFT 1 +#define ESAI_xFCR_xFR_MASK (1 << ESAI_xFCR_xFR_SHIFT) +#define ESAI_xFCR_xFR (1 << ESAI_xFCR_xFR_SHIFT) +#define ESAI_xFCR_xFEN_SHIFT 0 +#define ESAI_xFCR_xFEN_MASK (1 << ESAI_xFCR_xFEN_SHIFT) +#define ESAI_xFCR_xFEN (1 << ESAI_xFCR_xFEN_SHIFT) + +/* + * Transmit FIFO Status Register -- REG_ESAI_TFSR 0x14 + * Receive FIFO Status Register --REG_ESAI_RFSR 0x1C + */ +#define ESAI_xFSR_NTFO_SHIFT 12 +#define ESAI_xFSR_NRFI_SHIFT 12 +#define ESAI_xFSR_NTFI_SHIFT 8 +#define ESAI_xFSR_NRFO_SHIFT 8 +#define ESAI_xFSR_NTFx_WIDTH 3 +#define ESAI_xFSR_NRFx_WIDTH 2 +#define ESAI_xFSR_NTFO_MASK (((1 << ESAI_xFSR_NTFx_WIDTH) - 1) << ESAI_xFSR_NTFO_SHIFT) +#define ESAI_xFSR_NTFI_MASK (((1 << ESAI_xFSR_NTFx_WIDTH) - 1) << ESAI_xFSR_NTFI_SHIFT) +#define ESAI_xFSR_NRFO_MASK (((1 << ESAI_xFSR_NRFx_WIDTH) - 1) << ESAI_xFSR_NRFO_SHIFT) +#define ESAI_xFSR_NRFI_MASK (((1 << ESAI_xFSR_NRFx_WIDTH) - 1) << ESAI_xFSR_NRFI_SHIFT) +#define ESAI_xFSR_xFCNT_SHIFT 0 +#define ESAI_xFSR_xFCNT_WIDTH 8 +#define ESAI_xFSR_xFCNT_MASK (((1 << ESAI_xFSR_xFCNT_WIDTH) - 1) << ESAI_xFSR_xFCNT_SHIFT) + +/* ESAI Transmit Slot Register -- REG_ESAI_TSR 0x98 */ +#define ESAI_TSR_SHIFT 0 +#define ESAI_TSR_WIDTH 24 +#define ESAI_TSR_MASK (((1 << ESAI_TSR_WIDTH) - 1) << ESAI_TSR_SHIFT) + +/* Serial Audio Interface Status Register -- REG_ESAI_SAISR 0xCC */ +#define ESAI_SAISR_TODFE_SHIFT 17 +#define ESAI_SAISR_TODFE_MASK (1 << ESAI_SAISR_TODFE_SHIFT) +#define ESAI_SAISR_TODFE (1 << ESAI_SAISR_TODFE_SHIFT) +#define ESAI_SAISR_TEDE_SHIFT 16 +#define ESAI_SAISR_TEDE_MASK (1 << ESAI_SAISR_TEDE_SHIFT) +#define ESAI_SAISR_TEDE (1 << ESAI_SAISR_TEDE_SHIFT) +#define ESAI_SAISR_TDE_SHIFT 15 +#define ESAI_SAISR_TDE_MASK (1 << ESAI_SAISR_TDE_SHIFT) +#define ESAI_SAISR_TDE (1 << ESAI_SAISR_TDE_SHIFT) +#define ESAI_SAISR_TUE_SHIFT 14 +#define ESAI_SAISR_TUE_MASK (1 << ESAI_SAISR_TUE_SHIFT) +#define ESAI_SAISR_TUE (1 << ESAI_SAISR_TUE_SHIFT) +#define ESAI_SAISR_TFS_SHIFT 13 +#define ESAI_SAISR_TFS_MASK (1 << ESAI_SAISR_TFS_SHIFT) +#define ESAI_SAISR_TFS (1 << ESAI_SAISR_TFS_SHIFT) +#define ESAI_SAISR_RODF_SHIFT 10 +#define ESAI_SAISR_RODF_MASK (1 << ESAI_SAISR_RODF_SHIFT) +#define ESAI_SAISR_RODF (1 << ESAI_SAISR_RODF_SHIFT) +#define ESAI_SAISR_REDF_SHIFT 9 +#define ESAI_SAISR_REDF_MASK (1 << ESAI_SAISR_REDF_SHIFT) +#define ESAI_SAISR_REDF (1 << ESAI_SAISR_REDF_SHIFT) +#define ESAI_SAISR_RDF_SHIFT 8 +#define ESAI_SAISR_RDF_MASK (1 << ESAI_SAISR_RDF_SHIFT) +#define ESAI_SAISR_RDF (1 << ESAI_SAISR_RDF_SHIFT) +#define ESAI_SAISR_ROE_SHIFT 7 +#define ESAI_SAISR_ROE_MASK (1 << ESAI_SAISR_ROE_SHIFT) +#define ESAI_SAISR_ROE (1 << ESAI_SAISR_ROE_SHIFT) +#define ESAI_SAISR_RFS_SHIFT 6 +#define ESAI_SAISR_RFS_MASK (1 << ESAI_SAISR_RFS_SHIFT) +#define ESAI_SAISR_RFS (1 << ESAI_SAISR_RFS_SHIFT) +#define ESAI_SAISR_IF2_SHIFT 2 +#define ESAI_SAISR_IF2_MASK (1 << ESAI_SAISR_IF2_SHIFT) +#define ESAI_SAISR_IF2 (1 << ESAI_SAISR_IF2_SHIFT) +#define ESAI_SAISR_IF1_SHIFT 1 +#define ESAI_SAISR_IF1_MASK (1 << ESAI_SAISR_IF1_SHIFT) +#define ESAI_SAISR_IF1 (1 << ESAI_SAISR_IF1_SHIFT) +#define ESAI_SAISR_IF0_SHIFT 0 +#define ESAI_SAISR_IF0_MASK (1 << ESAI_SAISR_IF0_SHIFT) +#define ESAI_SAISR_IF0 (1 << ESAI_SAISR_IF0_SHIFT) + +/* Serial Audio Interface Control Register -- REG_ESAI_SAICR 0xD0 */ +#define ESAI_SAICR_ALC_SHIFT 8 +#define ESAI_SAICR_ALC_MASK (1 << ESAI_SAICR_ALC_SHIFT) +#define ESAI_SAICR_ALC (1 << ESAI_SAICR_ALC_SHIFT) +#define ESAI_SAICR_TEBE_SHIFT 7 +#define ESAI_SAICR_TEBE_MASK (1 << ESAI_SAICR_TEBE_SHIFT) +#define ESAI_SAICR_TEBE (1 << ESAI_SAICR_TEBE_SHIFT) +#define ESAI_SAICR_SYNC_SHIFT 6 +#define ESAI_SAICR_SYNC_MASK (1 << ESAI_SAICR_SYNC_SHIFT) +#define ESAI_SAICR_SYNC (1 << ESAI_SAICR_SYNC_SHIFT) +#define ESAI_SAICR_OF2_SHIFT 2 +#define ESAI_SAICR_OF2_MASK (1 << ESAI_SAICR_OF2_SHIFT) +#define ESAI_SAICR_OF2 (1 << ESAI_SAICR_OF2_SHIFT) +#define ESAI_SAICR_OF1_SHIFT 1 +#define ESAI_SAICR_OF1_MASK (1 << ESAI_SAICR_OF1_SHIFT) +#define ESAI_SAICR_OF1 (1 << ESAI_SAICR_OF1_SHIFT) +#define ESAI_SAICR_OF0_SHIFT 0 +#define ESAI_SAICR_OF0_MASK (1 << ESAI_SAICR_OF0_SHIFT) +#define ESAI_SAICR_OF0 (1 << ESAI_SAICR_OF0_SHIFT) + +/* + * Transmit Control Register -- REG_ESAI_TCR 0xD4 + * Receive Control Register -- REG_ESAI_RCR 0xDC + */ +#define ESAI_xCR_xLIE_SHIFT 23 +#define ESAI_xCR_xLIE_MASK (1 << ESAI_xCR_xLIE_SHIFT) +#define ESAI_xCR_xLIE (1 << ESAI_xCR_xLIE_SHIFT) +#define ESAI_xCR_xIE_SHIFT 22 +#define ESAI_xCR_xIE_MASK (1 << ESAI_xCR_xIE_SHIFT) +#define ESAI_xCR_xIE (1 << ESAI_xCR_xIE_SHIFT) +#define ESAI_xCR_xEDIE_SHIFT 21 +#define ESAI_xCR_xEDIE_MASK (1 << ESAI_xCR_xEDIE_SHIFT) +#define ESAI_xCR_xEDIE (1 << ESAI_xCR_xEDIE_SHIFT) +#define ESAI_xCR_xEIE_SHIFT 20 +#define ESAI_xCR_xEIE_MASK (1 << ESAI_xCR_xEIE_SHIFT) +#define ESAI_xCR_xEIE (1 << ESAI_xCR_xEIE_SHIFT) +#define ESAI_xCR_xPR_SHIFT 19 +#define ESAI_xCR_xPR_MASK (1 << ESAI_xCR_xPR_SHIFT) +#define ESAI_xCR_xPR (1 << ESAI_xCR_xPR_SHIFT) +#define ESAI_xCR_PADC_SHIFT 17 +#define ESAI_xCR_PADC_MASK (1 << ESAI_xCR_PADC_SHIFT) +#define ESAI_xCR_PADC (1 << ESAI_xCR_PADC_SHIFT) +#define ESAI_xCR_xFSR_SHIFT 16 +#define ESAI_xCR_xFSR_MASK (1 << ESAI_xCR_xFSR_SHIFT) +#define ESAI_xCR_xFSR (1 << ESAI_xCR_xFSR_SHIFT) +#define ESAI_xCR_xFSL_SHIFT 15 +#define ESAI_xCR_xFSL_MASK (1 << ESAI_xCR_xFSL_SHIFT) +#define ESAI_xCR_xFSL (1 << ESAI_xCR_xFSL_SHIFT) +#define ESAI_xCR_xSWS_SHIFT 10 +#define ESAI_xCR_xSWS_WIDTH 5 +#define ESAI_xCR_xSWS_MASK (((1 << ESAI_xCR_xSWS_WIDTH) - 1) << ESAI_xCR_xSWS_SHIFT) +#define ESAI_xCR_xSWS(s, w) ((w < 24 ? (s - w + ((w - 8) >> 2)) : (s < 32 ? 0x1e : 0x1f)) << ESAI_xCR_xSWS_SHIFT) +#define ESAI_xCR_xMOD_SHIFT 8 +#define ESAI_xCR_xMOD_WIDTH 2 +#define ESAI_xCR_xMOD_MASK (((1 << ESAI_xCR_xMOD_WIDTH) - 1) << ESAI_xCR_xMOD_SHIFT) +#define ESAI_xCR_xMOD_ONDEMAND (0x1 << ESAI_xCR_xMOD_SHIFT) +#define ESAI_xCR_xMOD_NETWORK (0x1 << ESAI_xCR_xMOD_SHIFT) +#define ESAI_xCR_xMOD_AC97 (0x3 << ESAI_xCR_xMOD_SHIFT) +#define ESAI_xCR_xWA_SHIFT 7 +#define ESAI_xCR_xWA_MASK (1 << ESAI_xCR_xWA_SHIFT) +#define ESAI_xCR_xWA (1 << ESAI_xCR_xWA_SHIFT) +#define ESAI_xCR_xSHFD_SHIFT 6 +#define ESAI_xCR_xSHFD_MASK (1 << ESAI_xCR_xSHFD_SHIFT) +#define ESAI_xCR_xSHFD (1 << ESAI_xCR_xSHFD_SHIFT) +#define ESAI_xCR_xE_SHIFT 0 +#define ESAI_xCR_TE_WIDTH 6 +#define ESAI_xCR_RE_WIDTH 4 +#define ESAI_xCR_TE_MASK (((1 << ESAI_xCR_TE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT) +#define ESAI_xCR_RE_MASK (((1 << ESAI_xCR_RE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT) +#define ESAI_xCR_TE(x) ((ESAI_xCR_TE_MASK >> (ESAI_xCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_TE_MASK) +#define ESAI_xCR_RE(x) ((ESAI_xCR_RE_MASK >> (ESAI_xCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_RE_MASK) + +/* + * Transmit Clock Control Register -- REG_ESAI_TCCR 0xD8 + * Receive Clock Control Register -- REG_ESAI_RCCR 0xE0 + */ +#define ESAI_xCCR_xHCKD_SHIFT 23 +#define ESAI_xCCR_xHCKD_MASK (1 << ESAI_xCCR_xHCKD_SHIFT) +#define ESAI_xCCR_xHCKD (1 << ESAI_xCCR_xHCKD_SHIFT) +#define ESAI_xCCR_xFSD_SHIFT 22 +#define ESAI_xCCR_xFSD_MASK (1 << ESAI_xCCR_xFSD_SHIFT) +#define ESAI_xCCR_xFSD (1 << ESAI_xCCR_xFSD_SHIFT) +#define ESAI_xCCR_xCKD_SHIFT 21 +#define ESAI_xCCR_xCKD_MASK (1 << ESAI_xCCR_xCKD_SHIFT) +#define ESAI_xCCR_xCKD (1 << ESAI_xCCR_xCKD_SHIFT) +#define ESAI_xCCR_xHCKP_SHIFT 20 +#define ESAI_xCCR_xHCKP_MASK (1 << ESAI_xCCR_xHCKP_SHIFT) +#define ESAI_xCCR_xHCKP (1 << ESAI_xCCR_xHCKP_SHIFT) +#define ESAI_xCCR_xFSP_SHIFT 19 +#define ESAI_xCCR_xFSP_MASK (1 << ESAI_xCCR_xFSP_SHIFT) +#define ESAI_xCCR_xFSP (1 << ESAI_xCCR_xFSP_SHIFT) +#define ESAI_xCCR_xCKP_SHIFT 18 +#define ESAI_xCCR_xCKP_MASK (1 << ESAI_xCCR_xCKP_SHIFT) +#define ESAI_xCCR_xCKP (1 << ESAI_xCCR_xCKP_SHIFT) +#define ESAI_xCCR_xFP_SHIFT 14 +#define ESAI_xCCR_xFP_WIDTH 4 +#define ESAI_xCCR_xFP_MASK (((1 << ESAI_xCCR_xFP_WIDTH) - 1) << ESAI_xCCR_xFP_SHIFT) +#define ESAI_xCCR_xFP(v) ((((v) - 1) << ESAI_xCCR_xFP_SHIFT) & ESAI_xCCR_xFP_MASK) +#define ESAI_xCCR_xDC_SHIFT 9 +#define ESAI_xCCR_xDC_WIDTH 4 +#define ESAI_xCCR_xDC_MASK (((1 << ESAI_xCCR_xDC_WIDTH) - 1) << ESAI_xCCR_xDC_SHIFT) +#define ESAI_xCCR_xDC(v) ((((v) - 1) << ESAI_xCCR_xDC_SHIFT) & ESAI_xCCR_xDC_MASK) +#define ESAI_xCCR_xPSR_SHIFT 8 +#define ESAI_xCCR_xPSR_MASK (1 << ESAI_xCCR_xPSR_SHIFT) +#define ESAI_xCCR_xPSR_BYPASS (1 << ESAI_xCCR_xPSR_SHIFT) +#define ESAI_xCCR_xPSR_DIV8 (0 << ESAI_xCCR_xPSR_SHIFT) +#define ESAI_xCCR_xPM_SHIFT 0 +#define ESAI_xCCR_xPM_WIDTH 8 +#define ESAI_xCCR_xPM_MASK (((1 << ESAI_xCCR_xPM_WIDTH) - 1) << ESAI_xCCR_xPM_SHIFT) +#define ESAI_xCCR_xPM(v) ((((v) - 1) << ESAI_xCCR_xPM_SHIFT) & ESAI_xCCR_xPM_MASK) + +/* Transmit Slot Mask Register A/B -- REG_ESAI_TSMA/B 0xE4 ~ 0xF0 */ +#define ESAI_xSMA_xS_SHIFT 0 +#define ESAI_xSMA_xS_WIDTH 16 +#define ESAI_xSMA_xS_MASK (((1 << ESAI_xSMA_xS_WIDTH) - 1) << ESAI_xSMA_xS_SHIFT) +#define ESAI_xSMA_xS(v) ((v) & ESAI_xSMA_xS_MASK) +#define ESAI_xSMB_xS_SHIFT 0 +#define ESAI_xSMB_xS_WIDTH 16 +#define ESAI_xSMB_xS_MASK (((1 << ESAI_xSMB_xS_WIDTH) - 1) << ESAI_xSMB_xS_SHIFT) +#define ESAI_xSMB_xS(v) (((v) >> ESAI_xSMA_xS_WIDTH) & ESAI_xSMB_xS_MASK) + +/* Port C Direction Register -- REG_ESAI_PRRC 0xF8 */ +#define ESAI_PRRC_PDC_SHIFT 0 +#define ESAI_PRRC_PDC_WIDTH 12 +#define ESAI_PRRC_PDC_MASK (((1 << ESAI_PRRC_PDC_WIDTH) - 1) << ESAI_PRRC_PDC_SHIFT) +#define ESAI_PRRC_PDC(v) ((v) & ESAI_PRRC_PDC_MASK) + +/* Port C Control Register -- REG_ESAI_PCRC 0xFC */ +#define ESAI_PCRC_PC_SHIFT 0 +#define ESAI_PCRC_PC_WIDTH 12 +#define ESAI_PCRC_PC_MASK (((1 << ESAI_PCRC_PC_WIDTH) - 1) << ESAI_PCRC_PC_SHIFT) +#define ESAI_PCRC_PC(v) ((v) & ESAI_PCRC_PC_MASK) + +#define ESAI_GPIO 0xfff + +/* ESAI clock source */ +#define ESAI_HCKT_FSYS 0 +#define ESAI_HCKT_EXTAL 1 +#define ESAI_HCKR_FSYS 2 +#define ESAI_HCKR_EXTAL 3 + +/* ESAI clock divider */ +#define ESAI_TX_DIV_PSR 0 +#define ESAI_TX_DIV_PM 1 +#define ESAI_TX_DIV_FP 2 +#define ESAI_RX_DIV_PSR 3 +#define ESAI_RX_DIV_PM 4 +#define ESAI_RX_DIV_FP 5 +#endif /* _FSL_ESAI_DAI_H */ diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c new file mode 100644 index 000000000000..c4a423111673 --- /dev/null +++ b/sound/soc/fsl/fsl_sai.c @@ -0,0 +1,551 @@ +/* + * 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/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> + +#include "fsl_sai.h" + +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; + + regmap_read(sai->regmap, reg_cr2, &val_cr2); + + val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; + + switch (clk_id) { + case FSL_SAI_CLK_BUS: + val_cr2 |= FSL_SAI_CR2_MSEL_BUS; + break; + case FSL_SAI_CLK_MAST1: + val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1; + break; + case FSL_SAI_CLK_MAST2: + val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2; + break; + case FSL_SAI_CLK_MAST3: + val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3; + break; + default: + return -EINVAL; + } + + regmap_write(sai->regmap, reg_cr2, val_cr2); + + return 0; +} + +static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + int ret; + + if (dir == SND_SOC_CLOCK_IN) + return 0; + + 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); + return ret; + } + + 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); + + 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; + } + + regmap_read(sai->regmap, reg_cr2, &val_cr2); + regmap_read(sai->regmap, reg_cr4, &val_cr4); + + if (sai->big_endian_data) + val_cr4 &= ~FSL_SAI_CR4_MF; + else + val_cr4 |= FSL_SAI_CR4_MF; + + /* DAI mode */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* + * Frame low, 1clk before data, one word length for frame sync, + * frame sync starts one serial clock cycle earlier, + * that is, together with the last bit of the previous + * data word. + */ + val_cr2 &= ~FSL_SAI_CR2_BCP; + val_cr4 |= FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP; + break; + case SND_SOC_DAIFMT_LEFT_J: + /* + * Frame high, one word length for frame sync, + * frame sync asserts with the first bit of the frame. + */ + val_cr2 &= ~FSL_SAI_CR2_BCP; + val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP); + break; + case SND_SOC_DAIFMT_DSP_A: + /* + * Frame high, 1clk before data, one bit for frame sync, + * frame sync starts one serial clock cycle earlier, + * that is, together with the last bit of the previous + * data word. + */ + val_cr2 &= ~FSL_SAI_CR2_BCP; + val_cr4 &= ~FSL_SAI_CR4_FSP; + val_cr4 |= FSL_SAI_CR4_FSE; + sai->is_dsp_mode = true; + break; + case SND_SOC_DAIFMT_DSP_B: + /* + * Frame high, one bit for frame sync, + * frame sync asserts with the first bit of the frame. + */ + val_cr2 &= ~FSL_SAI_CR2_BCP; + val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP); + sai->is_dsp_mode = true; + break; + case SND_SOC_DAIFMT_RIGHT_J: + /* To be done */ + default: + return -EINVAL; + } + + /* DAI clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + /* Invert both clocks */ + val_cr2 ^= FSL_SAI_CR2_BCP; + val_cr4 ^= FSL_SAI_CR4_FSP; + break; + case SND_SOC_DAIFMT_IB_NF: + /* Invert bit clock */ + val_cr2 ^= FSL_SAI_CR2_BCP; + break; + case SND_SOC_DAIFMT_NB_IF: + /* Invert frame clock */ + val_cr4 ^= FSL_SAI_CR4_FSP; + break; + case SND_SOC_DAIFMT_NB_NF: + /* Nothing to do for both normal cases */ + break; + default: + return -EINVAL; + } + + /* DAI clock master masks */ + 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; + case SND_SOC_DAIFMT_CBS_CFM: + val_cr2 |= FSL_SAI_CR2_BCD_MSTR; + val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; + val_cr4 |= FSL_SAI_CR4_FSD_MSTR; + break; + default: + return -EINVAL; + } + + regmap_write(sai->regmap, reg_cr2, val_cr2); + regmap_write(sai->regmap, reg_cr4, val_cr4); + + return 0; +} + +static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + int 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); + return ret; + } + + 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); + + 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; + } + + regmap_read(sai->regmap, reg_cr4, &val_cr4); + regmap_read(sai->regmap, reg_cr4, &val_cr5); + + val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK; + val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK; + + val_cr5 &= ~FSL_SAI_CR5_WNW_MASK; + val_cr5 &= ~FSL_SAI_CR5_W0W_MASK; + val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; + + if (!sai->is_dsp_mode) + 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); + + regmap_write(sai->regmap, reg_cr4, val_cr4); + regmap_write(sai->regmap, reg_cr5, val_cr5); + regmap_write(sai->regmap, reg_mr, val_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; + + /* + * The transmitter bit clock and frame sync are to be + * used by both the transmitter and receiver. + */ + regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, + ~FSL_SAI_CR2_SYNC); + regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC, + FSL_SAI_CR2_SYNC); + + regmap_read(sai->regmap, FSL_SAI_TCSR, &tcsr); + regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + tcsr |= FSL_SAI_CSR_FRDE; + rcsr &= ~FSL_SAI_CSR_FRDE; + } else { + rcsr |= FSL_SAI_CSR_FRDE; + tcsr &= ~FSL_SAI_CSR_FRDE; + } + + /* + * It is recommended that the transmitter is the last enabled + * and the first disabled. + */ + 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; + + regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr); + regmap_write(sai->regmap, FSL_SAI_TCSR, 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; + } + + regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr); + regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr); + 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); + u32 reg; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = FSL_SAI_TCR3; + else + reg = FSL_SAI_RCR3; + + regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE, + FSL_SAI_CR3_TRCE); + + return 0; +} + +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); + u32 reg; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = FSL_SAI_TCR3; + else + reg = FSL_SAI_RCR3; + + regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE, + ~FSL_SAI_CR3_TRCE); +} + +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); + + regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0); + regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0); + regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK, + FSL_SAI_MAXBURST_TX * 2); + regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK, + FSL_SAI_MAXBURST_RX - 1); + + 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 bool fsl_sai_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSL_SAI_TCSR: + case FSL_SAI_TCR1: + case FSL_SAI_TCR2: + case FSL_SAI_TCR3: + case FSL_SAI_TCR4: + case FSL_SAI_TCR5: + case FSL_SAI_TFR: + case FSL_SAI_TMR: + case FSL_SAI_RCSR: + case FSL_SAI_RCR1: + case FSL_SAI_RCR2: + case FSL_SAI_RCR3: + case FSL_SAI_RCR4: + case FSL_SAI_RCR5: + case FSL_SAI_RDR: + case FSL_SAI_RFR: + case FSL_SAI_RMR: + return true; + default: + return false; + } +} + +static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSL_SAI_TFR: + case FSL_SAI_RFR: + case FSL_SAI_TDR: + case FSL_SAI_RDR: + return true; + default: + return false; + } + +} + +static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSL_SAI_TCSR: + case FSL_SAI_TCR1: + case FSL_SAI_TCR2: + case FSL_SAI_TCR3: + case FSL_SAI_TCR4: + case FSL_SAI_TCR5: + case FSL_SAI_TDR: + case FSL_SAI_TMR: + case FSL_SAI_RCSR: + case FSL_SAI_RCR1: + case FSL_SAI_RCR2: + case FSL_SAI_RCR3: + case FSL_SAI_RCR4: + case FSL_SAI_RCR5: + case FSL_SAI_RMR: + return true; + default: + return false; + } +} + +static struct regmap_config fsl_sai_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = FSL_SAI_RMR, + .readable_reg = fsl_sai_readable_reg, + .volatile_reg = fsl_sai_volatile_reg, + .writeable_reg = fsl_sai_writeable_reg, +}; + +static int fsl_sai_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_sai *sai; + struct resource *res; + void __iomem *base; + int ret; + + sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); + if (!sai) + return -ENOMEM; + + sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); + if (sai->big_endian_regs) + fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; + + sai->big_endian_data = of_property_read_bool(np, "big-endian-data"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, + "sai", base, &fsl_sai_regmap_config); + if (IS_ERR(sai->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(sai->regmap); + } + + 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; + + 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..e432260be598 --- /dev/null +++ b/sound/soc/fsl/fsl_sai.h @@ -0,0 +1,112 @@ +/* + * 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 Register Map Register */ +#define FSL_SAI_TCSR 0x00 /* SAI Transmit Control */ +#define FSL_SAI_TCR1 0x04 /* SAI Transmit Configuration 1 */ +#define FSL_SAI_TCR2 0x08 /* SAI Transmit Configuration 2 */ +#define FSL_SAI_TCR3 0x0c /* SAI Transmit Configuration 3 */ +#define FSL_SAI_TCR4 0x10 /* SAI Transmit Configuration 4 */ +#define FSL_SAI_TCR5 0x14 /* SAI Transmit Configuration 5 */ +#define FSL_SAI_TDR 0x20 /* SAI Transmit Data */ +#define FSL_SAI_TFR 0x40 /* SAI Transmit FIFO */ +#define FSL_SAI_TMR 0x60 /* SAI Transmit Mask */ +#define FSL_SAI_RCSR 0x80 /* SAI Receive Control */ +#define FSL_SAI_RCR1 0x84 /* SAI Receive Configuration 1 */ +#define FSL_SAI_RCR2 0x88 /* SAI Receive Configuration 2 */ +#define FSL_SAI_RCR3 0x8c /* SAI Receive Configuration 3 */ +#define FSL_SAI_RCR4 0x90 /* SAI Receive Configuration 4 */ +#define FSL_SAI_RCR5 0x94 /* SAI Receive Configuration 5 */ +#define FSL_SAI_RDR 0xa0 /* SAI Receive Data */ +#define FSL_SAI_RFR 0xc0 /* SAI Receive FIFO */ +#define FSL_SAI_RMR 0xe0 /* SAI Receive Mask */ + +/* SAI Transmit/Recieve Control Register */ +#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 and Recieve Configuration 1 Register */ +#define FSL_SAI_CR1_RFW_MASK 0x1f + +/* SAI Transmit and Recieve Configuration 2 Register */ +#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_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_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_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 regmap *regmap; + + bool big_endian_regs; + bool big_endian_data; + bool is_dsp_mode; + + 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..6452ca83d889 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -911,8 +911,8 @@ static int fsl_spdif_dai_probe(struct snd_soc_dai *dai) { struct fsl_spdif_priv *spdif_private = snd_soc_dai_get_drvdata(dai); - dai->playback_dma_data = &spdif_private->dma_params_tx; - dai->capture_dma_data = &spdif_private->dma_params_rx; + snd_soc_dai_init_dma_data(dai, &spdif_private->dma_params_tx, + &spdif_private->dma_params_rx); snd_soc_add_dai_controls(dai, fsl_spdif_ctrls, ARRAY_SIZE(fsl_spdif_ctrls)); @@ -985,7 +985,7 @@ static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg) } } -static const struct regmap_config fsl_spdif_regmap_config = { +static struct regmap_config fsl_spdif_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, @@ -1105,6 +1105,9 @@ static int fsl_spdif_probe(struct platform_device *pdev) memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai)); spdif_priv->cpu_dai_drv.name = spdif_priv->name; + if (of_property_read_bool(np, "big-endian")) + fsl_spdif_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; + /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(&pdev->dev, res); @@ -1181,13 +1184,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 +1197,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..5428a1fda260 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -35,9 +35,11 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/clk.h> +#include <linux/debugfs.h> #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> @@ -79,8 +81,7 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set) * ALSA that we support all rates and let the codec driver decide what rates * are really supported. */ -#define FSLSSI_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ - SNDRV_PCM_RATE_CONTINUOUS) +#define FSLSSI_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS /** * FSLSSI_I2S_FORMATS: audio formats supported by the SSI @@ -106,12 +107,33 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set) SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE) #endif -/* SIER bitflag of interrupts to enable */ -#define SIER_FLAGS (CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE | \ - CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN | \ - CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN | \ - CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | \ - CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN) +#define FSLSSI_SIER_DBG_RX_FLAGS (CCSR_SSI_SIER_RFF0_EN | \ + CCSR_SSI_SIER_RLS_EN | CCSR_SSI_SIER_RFS_EN | \ + CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_RFRC_EN) +#define FSLSSI_SIER_DBG_TX_FLAGS (CCSR_SSI_SIER_TFE0_EN | \ + CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_EN | \ + CCSR_SSI_SIER_TUE0_EN | CCSR_SSI_SIER_TFRC_EN) +#define FSLSSI_SISR_MASK (FSLSSI_SIER_DBG_RX_FLAGS | FSLSSI_SIER_DBG_TX_FLAGS) + + +enum fsl_ssi_type { + FSL_SSI_MCP8610, + FSL_SSI_MX21, + FSL_SSI_MX35, + FSL_SSI_MX51, +}; + +struct fsl_ssi_reg_val { + u32 sier; + u32 srcr; + u32 stcr; + u32 scr; +}; + +struct fsl_ssi_rxtx_reg_val { + struct fsl_ssi_reg_val rx; + struct fsl_ssi_reg_val tx; +}; /** * fsl_ssi_private: per-SSI private data @@ -119,8 +141,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,23 +152,30 @@ 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; struct platform_device *pdev; + enum fsl_ssi_type hw_type; bool new_binding; bool ssi_on_imx; bool imx_ac97; bool use_dma; + bool baudclk_locked; + bool irq_stats; + bool offline_config; + bool use_dual_fifo; + 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; struct imx_dma_data filter_data_tx; struct imx_dma_data filter_data_rx; struct imx_pcm_fiq_params fiq_params; + /* Register values for rx/tx configuration */ + struct fsl_ssi_rxtx_reg_val rxtx_reg_val; struct { unsigned int rfrc; @@ -173,10 +200,21 @@ struct fsl_ssi_private { unsigned int tfe1; unsigned int tfe0; } stats; + struct dentry *dbg_dir; + struct dentry *dbg_stats; char name[1]; }; +static const struct of_device_id fsl_ssi_ids[] = { + { .compatible = "fsl,mpc8610-ssi", .data = (void *) FSL_SSI_MCP8610}, + { .compatible = "fsl,imx51-ssi", .data = (void *) FSL_SSI_MX51}, + { .compatible = "fsl,imx35-ssi", .data = (void *) FSL_SSI_MX35}, + { .compatible = "fsl,imx21-ssi", .data = (void *) FSL_SSI_MX21}, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_ssi_ids); + /** * fsl_ssi_isr: SSI interrupt handler * @@ -195,23 +233,40 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) struct ccsr_ssi __iomem *ssi = ssi_private->ssi; irqreturn_t ret = IRQ_NONE; __be32 sisr; - __be32 sisr2 = 0; + __be32 sisr2; + __be32 sisr_write_mask = 0; + + switch (ssi_private->hw_type) { + case FSL_SSI_MX21: + sisr_write_mask = 0; + break; + + case FSL_SSI_MCP8610: + case FSL_SSI_MX35: + sisr_write_mask = CCSR_SSI_SISR_RFRC | CCSR_SSI_SISR_TFRC | + CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 | + CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1; + break; + + case FSL_SSI_MX51: + sisr_write_mask = CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 | + CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1; + break; + } /* We got an interrupt, so read the status register to see what we were interrupted for. We mask it with the Interrupt Enable register so that we only check for events that we're interested in. */ - sisr = read_ssi(&ssi->sisr) & SIER_FLAGS; + sisr = read_ssi(&ssi->sisr) & FSLSSI_SISR_MASK; if (sisr & CCSR_SSI_SISR_RFRC) { ssi_private->stats.rfrc++; - sisr2 |= CCSR_SSI_SISR_RFRC; ret = IRQ_HANDLED; } if (sisr & CCSR_SSI_SISR_TFRC) { ssi_private->stats.tfrc++; - sisr2 |= CCSR_SSI_SISR_TFRC; ret = IRQ_HANDLED; } @@ -252,25 +307,21 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) if (sisr & CCSR_SSI_SISR_ROE1) { ssi_private->stats.roe1++; - sisr2 |= CCSR_SSI_SISR_ROE1; ret = IRQ_HANDLED; } if (sisr & CCSR_SSI_SISR_ROE0) { ssi_private->stats.roe0++; - sisr2 |= CCSR_SSI_SISR_ROE0; ret = IRQ_HANDLED; } if (sisr & CCSR_SSI_SISR_TUE1) { ssi_private->stats.tue1++; - sisr2 |= CCSR_SSI_SISR_TUE1; ret = IRQ_HANDLED; } if (sisr & CCSR_SSI_SISR_TUE0) { ssi_private->stats.tue0++; - sisr2 |= CCSR_SSI_SISR_TUE0; ret = IRQ_HANDLED; } @@ -314,6 +365,7 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) ret = IRQ_HANDLED; } + sisr2 = sisr & sisr_write_mask; /* Clear the bits that we set */ if (sisr2) write_ssi(sisr2, &ssi->sisr); @@ -321,17 +373,287 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) return ret; } +#if IS_ENABLED(CONFIG_DEBUG_FS) +/* Show the statistics of a flag only if its interrupt is enabled. The + * compiler will optimze this code to a no-op if the interrupt is not + * enabled. + */ +#define SIER_SHOW(flag, name) \ + do { \ + if (FSLSSI_SISR_MASK & CCSR_SSI_SIER_##flag) \ + seq_printf(s, #name "=%u\n", ssi_private->stats.name); \ + } while (0) + + +/** + * fsl_sysfs_ssi_show: display SSI statistics + * + * Display the statistics for the current SSI device. To avoid confusion, + * we only show those counts that are enabled. + */ +static int fsl_ssi_stats_show(struct seq_file *s, void *unused) +{ + struct fsl_ssi_private *ssi_private = s->private; + + SIER_SHOW(RFRC_EN, rfrc); + SIER_SHOW(TFRC_EN, tfrc); + SIER_SHOW(CMDAU_EN, cmdau); + SIER_SHOW(CMDDU_EN, cmddu); + SIER_SHOW(RXT_EN, rxt); + SIER_SHOW(RDR1_EN, rdr1); + SIER_SHOW(RDR0_EN, rdr0); + SIER_SHOW(TDE1_EN, tde1); + SIER_SHOW(TDE0_EN, tde0); + SIER_SHOW(ROE1_EN, roe1); + SIER_SHOW(ROE0_EN, roe0); + SIER_SHOW(TUE1_EN, tue1); + SIER_SHOW(TUE0_EN, tue0); + SIER_SHOW(TFS_EN, tfs); + SIER_SHOW(RFS_EN, rfs); + SIER_SHOW(TLS_EN, tls); + SIER_SHOW(RLS_EN, rls); + SIER_SHOW(RFF1_EN, rff1); + SIER_SHOW(RFF0_EN, rff0); + SIER_SHOW(TFE1_EN, tfe1); + SIER_SHOW(TFE0_EN, tfe0); + + return 0; +} + +static int fsl_ssi_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, fsl_ssi_stats_show, inode->i_private); +} + +static const struct file_operations fsl_ssi_stats_ops = { + .open = fsl_ssi_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private, + struct device *dev) +{ + ssi_private->dbg_dir = debugfs_create_dir(dev_name(dev), NULL); + if (!ssi_private->dbg_dir) + return -ENOMEM; + + ssi_private->dbg_stats = debugfs_create_file("stats", S_IRUGO, + ssi_private->dbg_dir, ssi_private, &fsl_ssi_stats_ops); + if (!ssi_private->dbg_stats) { + debugfs_remove(ssi_private->dbg_dir); + return -ENOMEM; + } + + return 0; +} + +static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private) +{ + debugfs_remove(ssi_private->dbg_stats); + debugfs_remove(ssi_private->dbg_dir); +} + +#else + +static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private, + struct device *dev) +{ + return 0; +} + +static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private) +{ +} + +#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */ + +/* + * Enable/Disable all rx/tx config flags at once. + */ +static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private, + bool enable) +{ + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + struct fsl_ssi_rxtx_reg_val *vals = &ssi_private->rxtx_reg_val; + + if (enable) { + write_ssi_mask(&ssi->sier, 0, vals->rx.sier | vals->tx.sier); + write_ssi_mask(&ssi->srcr, 0, vals->rx.srcr | vals->tx.srcr); + write_ssi_mask(&ssi->stcr, 0, vals->rx.stcr | vals->tx.stcr); + } else { + write_ssi_mask(&ssi->srcr, vals->rx.srcr | vals->tx.srcr, 0); + write_ssi_mask(&ssi->stcr, vals->rx.stcr | vals->tx.stcr, 0); + write_ssi_mask(&ssi->sier, vals->rx.sier | vals->tx.sier, 0); + } +} + +/* + * Enable/Disable a ssi configuration. You have to pass either + * ssi_private->rxtx_reg_val.rx or tx as vals parameter. + */ +static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, + struct fsl_ssi_reg_val *vals) +{ + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + struct fsl_ssi_reg_val *avals; + u32 scr_val = read_ssi(&ssi->scr); + int nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) + + !!(scr_val & CCSR_SSI_SCR_RE); + + /* Find the other direction values rx or tx which we do not want to + * modify */ + if (&ssi_private->rxtx_reg_val.rx == vals) + avals = &ssi_private->rxtx_reg_val.tx; + else + avals = &ssi_private->rxtx_reg_val.rx; + + /* If vals should be disabled, start with disabling the unit */ + if (!enable) { + u32 scr = vals->scr & (vals->scr ^ avals->scr); + write_ssi_mask(&ssi->scr, scr, 0); + } + + /* + * We are running on a SoC which does not support online SSI + * reconfiguration, so we have to enable all necessary flags at once + * even if we do not use them later (capture and playback configuration) + */ + if (ssi_private->offline_config) { + if ((enable && !nr_active_streams) || + (!enable && nr_active_streams == 1)) + fsl_ssi_rxtx_config(ssi_private, enable); + + goto config_done; + } + + /* + * Configure single direction units while the SSI unit is running + * (online configuration) + */ + if (enable) { + write_ssi_mask(&ssi->sier, 0, vals->sier); + write_ssi_mask(&ssi->srcr, 0, vals->srcr); + write_ssi_mask(&ssi->stcr, 0, vals->stcr); + } else { + u32 sier; + u32 srcr; + u32 stcr; + + /* + * Disabling the necessary flags for one of rx/tx while the + * other stream is active is a little bit more difficult. We + * have to disable only those flags that differ between both + * streams (rx XOR tx) and that are set in the stream that is + * disabled now. Otherwise we could alter flags of the other + * stream + */ + + /* These assignments are simply vals without bits set in avals*/ + sier = vals->sier & (vals->sier ^ avals->sier); + srcr = vals->srcr & (vals->srcr ^ avals->srcr); + stcr = vals->stcr & (vals->stcr ^ avals->stcr); + + write_ssi_mask(&ssi->srcr, srcr, 0); + write_ssi_mask(&ssi->stcr, stcr, 0); + write_ssi_mask(&ssi->sier, sier, 0); + } + +config_done: + /* Enabling of subunits is done after configuration */ + if (enable) + write_ssi_mask(&ssi->scr, 0, vals->scr); +} + + +static void fsl_ssi_rx_config(struct fsl_ssi_private *ssi_private, bool enable) +{ + fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.rx); +} + +static void fsl_ssi_tx_config(struct fsl_ssi_private *ssi_private, bool enable) +{ + fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.tx); +} + +/* + * Setup rx/tx register values used to enable/disable the streams. These will + * be used later in fsl_ssi_config to setup the streams without the need to + * check for all different SSI modes. + */ +static void fsl_ssi_setup_reg_vals(struct fsl_ssi_private *ssi_private) +{ + struct fsl_ssi_rxtx_reg_val *reg = &ssi_private->rxtx_reg_val; + + reg->rx.sier = CCSR_SSI_SIER_RFF0_EN; + reg->rx.srcr = CCSR_SSI_SRCR_RFEN0; + reg->rx.scr = 0; + reg->tx.sier = CCSR_SSI_SIER_TFE0_EN; + reg->tx.stcr = CCSR_SSI_STCR_TFEN0; + reg->tx.scr = 0; + + if (!ssi_private->imx_ac97) { + reg->rx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE; + reg->rx.sier |= CCSR_SSI_SIER_RFF0_EN; + reg->tx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE; + reg->tx.sier |= CCSR_SSI_SIER_TFE0_EN; + } + + if (ssi_private->use_dma) { + reg->rx.sier |= CCSR_SSI_SIER_RDMAE; + reg->tx.sier |= CCSR_SSI_SIER_TDMAE; + } else { + reg->rx.sier |= CCSR_SSI_SIER_RIE; + reg->tx.sier |= CCSR_SSI_SIER_TIE; + } + + reg->rx.sier |= FSLSSI_SIER_DBG_RX_FLAGS; + reg->tx.sier |= FSLSSI_SIER_DBG_TX_FLAGS; +} + +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; + fsl_ssi_setup_reg_vals(ssi_private); + 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,16 +670,15 @@ 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 | - CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS | - CCSR_SSI_STCR_TSCKP, &ssi->stcr); + write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFSI | + CCSR_SSI_STCR_TEFS | CCSR_SSI_STCR_TSCKP, &ssi->stcr); + + write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFSI | + CCSR_SSI_SRCR_REFS | CCSR_SSI_SRCR_RSCKP, &ssi->srcr); - write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 | - CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS | - CCSR_SSI_SRCR_RSCKP, &ssi->srcr); /* * The DC and PM bits are only used if the SSI is the clock master. */ @@ -387,30 +708,24 @@ 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); + if (ssi_private->imx_ac97) + fsl_ssi_setup_ac97(ssi_private); - /* - * Enable SSI, Transmit and Receive - */ - write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN | - CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE); + /* + * Set a default slot number so that there is no need for those common + * cases like I2S mode to call the extra set_tdm_slot() any more. + */ + if (!ssi_private->imx_ac97) { + write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_DC_MASK, + CCSR_SSI_SxCCR_DC(2)); + write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_DC_MASK, + CCSR_SSI_SxCCR_DC(2)); + } - write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor); + if (ssi_private->use_dual_fifo) { + write_ssi_mask(&ssi->srcr, 0, CCSR_SSI_SRCR_RFEN1); + write_ssi_mask(&ssi->stcr, 0, CCSR_SSI_STCR_TFEN1); + write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_TCH_EN); } return 0; @@ -431,55 +746,28 @@ 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); } + /* When using dual fifo mode, it is safer to ensure an even period + * size. If appearing to an odd number while DMA always starts its + * task from fifo0, fifo1 would be neglected at the end of each + * period. But SSI would still access fifo1 with an invalid data. + */ + if (ssi_private->use_dual_fifo) + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); + return 0; } @@ -501,6 +789,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 +819,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; } @@ -548,77 +1079,46 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_pcm_runtime *rtd = substream->private_data; 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; - - /* - * Enable only the interrupts and DMA requests - * that are needed for the channel. As the fiq - * is polling for this bits, we have to ensure - * that this are aligned with the preallocated - * buffers - */ - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (ssi_private->use_dma) - sier_bits = SIER_FLAGS; - else - sier_bits = CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN; - } else { - if (ssi_private->use_dma) - sier_bits = SIER_FLAGS; - else - sier_bits = CCSR_SSI_SIER_RIE | CCSR_SSI_SIER_RFF0_EN; - } + unsigned long flags; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - write_ssi_mask(&ssi->scr, 0, - CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE); + fsl_ssi_tx_config(ssi_private, true); else - write_ssi_mask(&ssi->scr, 0, - CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE); + fsl_ssi_rx_config(ssi_private, true); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0); + fsl_ssi_tx_config(ssi_private, false); else - write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0); + fsl_ssi_rx_config(ssi_private, false); if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) & - (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) - write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); + (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) { + spin_lock_irqsave(&ssi_private->baudclk_lock, flags); + ssi_private->baudclk_locked = false; + spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags); + } break; default: return -EINVAL; } - write_ssi(sier_bits, &ssi->sier); + if (ssi_private->imx_ac97) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor); + else + write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor); + } 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 +1134,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 +1144,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, @@ -661,59 +1162,6 @@ static const struct snd_soc_component_driver fsl_ssi_component = { .name = "fsl-ssi", }; -/** - * fsl_ssi_ac97_trigger: start and stop the AC97 receive/transmit. - * - * This function is called by ALSA to start, stop, pause, and resume the - * transfer of data. - */ -static int fsl_ssi_ac97_trigger(struct snd_pcm_substream *substream, int cmd, - 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); - struct ccsr_ssi __iomem *ssi = ssi_private->ssi; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_TIE | - CCSR_SSI_SIER_TFE0_EN); - else - write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_RIE | - CCSR_SSI_SIER_RFF0_EN); - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_TIE | - CCSR_SSI_SIER_TFE0_EN, 0); - else - write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_RIE | - CCSR_SSI_SIER_RFF0_EN, 0); - break; - - default: - return -EINVAL; - } - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor); - else - write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor); - - return 0; -} - -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, -}; - static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { .ac97_control = 1, .playback = { @@ -730,7 +1178,7 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { .rates = SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, - .ops = &fsl_ssi_ac97_dai_ops, + .ops = &fsl_ssi_dai_ops, }; @@ -788,56 +1236,6 @@ static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = { .write = fsl_ssi_ac97_write, }; -/* Show the statistics of a flag only if its interrupt is enabled. The - * compiler will optimze this code to a no-op if the interrupt is not - * enabled. - */ -#define SIER_SHOW(flag, name) \ - do { \ - if (SIER_FLAGS & CCSR_SSI_SIER_##flag) \ - length += sprintf(buf + length, #name "=%u\n", \ - ssi_private->stats.name); \ - } while (0) - - -/** - * fsl_sysfs_ssi_show: display SSI statistics - * - * Display the statistics for the current SSI device. To avoid confusion, - * we only show those counts that are enabled. - */ -static ssize_t fsl_sysfs_ssi_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct fsl_ssi_private *ssi_private = - container_of(attr, struct fsl_ssi_private, dev_attr); - ssize_t length = 0; - - SIER_SHOW(RFRC_EN, rfrc); - SIER_SHOW(TFRC_EN, tfrc); - SIER_SHOW(CMDAU_EN, cmdau); - SIER_SHOW(CMDDU_EN, cmddu); - SIER_SHOW(RXT_EN, rxt); - SIER_SHOW(RDR1_EN, rdr1); - SIER_SHOW(RDR0_EN, rdr0); - SIER_SHOW(TDE1_EN, tde1); - SIER_SHOW(TDE0_EN, tde0); - SIER_SHOW(ROE1_EN, roe1); - SIER_SHOW(ROE0_EN, roe0); - SIER_SHOW(TUE1_EN, tue1); - SIER_SHOW(TUE0_EN, tue0); - SIER_SHOW(TFS_EN, tfs); - SIER_SHOW(RFS_EN, rfs); - SIER_SHOW(TLS_EN, tls); - SIER_SHOW(RLS_EN, rls); - SIER_SHOW(RFF1_EN, rff1); - SIER_SHOW(RFF0_EN, rff0); - SIER_SHOW(TFE1_EN, tfe1); - SIER_SHOW(TFE0_EN, tfe0); - - return length; -} - /** * Make every character in a string lower-case */ @@ -859,6 +1257,8 @@ static int fsl_ssi_probe(struct platform_device *pdev) int ret = 0; struct device_attribute *dev_attr = NULL; struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id; + enum fsl_ssi_type hw_type; const char *p, *sprop; const uint32_t *iprop; struct resource res; @@ -873,18 +1273,18 @@ static int fsl_ssi_probe(struct platform_device *pdev) if (!of_device_is_available(np)) return -ENODEV; - /* We only support the SSI in "I2S Slave" mode */ + of_id = of_match_device(fsl_ssi_ids, &pdev->dev); + if (!of_id) + return -EINVAL; + hw_type = (enum fsl_ssi_type) of_id->data; + sprop = of_get_property(np, "fsl,mode", NULL); if (!sprop) { dev_err(&pdev->dev, "fsl,mode property is necessary\n"); return -EINVAL; } - if (!strcmp(sprop, "ac97-slave")) { + if (!strcmp(sprop, "ac97-slave")) ac97 = true; - } else if (strcmp(sprop, "i2s-slave")) { - dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop); - return -ENODEV; - } /* The DAI name is the last part of the full name of the node. */ p = strrchr(np->full_name, '/') + 1; @@ -899,6 +1299,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) ssi_private->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter"); + ssi_private->hw_type = hw_type; if (ac97) { memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai, @@ -935,8 +1336,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,8 +1350,38 @@ static int fsl_ssi_probe(struct platform_device *pdev) /* Older 8610 DTs didn't have the fifo-depth property */ ssi_private->fifo_depth = 8; - if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) { - u32 dma_events[2]; + ssi_private->baudclk_locked = false; + spin_lock_init(&ssi_private->baudclk_lock); + + /* + * imx51 and later SoCs have a slightly different IP that allows the + * SSI configuration while the SSI unit is running. + * + * More important, it is necessary on those SoCs to configure the + * sperate TX/RX DMA bits just before starting the stream + * (fsl_ssi_trigger). The SDMA unit has to be configured before fsl_ssi + * sends any DMA requests to the SDMA unit, otherwise it is not defined + * how the SDMA unit handles the DMA request. + * + * SDMA units are present on devices starting at imx35 but the imx35 + * reference manual states that the DMA bits should not be changed + * while the SSI unit is running (SSIEN). So we support the necessary + * online configuration of fsl-ssi starting at imx51. + */ + switch (hw_type) { + case FSL_SSI_MCP8610: + case FSL_SSI_MX21: + case FSL_SSI_MX35: + ssi_private->offline_config = true; + break; + case FSL_SSI_MX51: + ssi_private->offline_config = false; + break; + } + + if (hw_type == FSL_SSI_MX21 || hw_type == FSL_SSI_MX51 || + hw_type == FSL_SSI_MX35) { + u32 dma_events[2], dmas[4]; ssi_private->ssi_on_imx = true; ssi_private->clk = devm_clk_get(&pdev->dev, NULL); @@ -963,6 +1397,16 @@ 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_dbg(&pdev->dev, "could not get baud clock: %ld\n", + PTR_ERR(ssi_private->baudclk)); + else + clk_prepare_enable(ssi_private->baudclk); + /* * We have burstsize be "fifo_depth - 2" to match the SSI * watermark setting in fsl_ssi_startup(). @@ -993,6 +1437,16 @@ static int fsl_ssi_probe(struct platform_device *pdev) goto error_clk; } } + /* Should this be merge with the above? */ + if (!of_property_read_u32_array(pdev->dev.of_node, "dmas", dmas, 4) + && dmas[2] == IMX_DMATYPE_SSI_DUAL) { + ssi_private->use_dual_fifo = true; + /* When using dual fifo mode, we need to keep watermark + * as even numbers due to dma script limitation. + */ + ssi_private->dma_params_tx.maxburst &= ~0x1; + ssi_private->dma_params_rx.maxburst &= ~0x1; + } shared = of_device_is_compatible(of_get_parent(np), "fsl,spba-bus"); @@ -1001,32 +1455,25 @@ static int fsl_ssi_probe(struct platform_device *pdev) dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI); imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx, dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI); - } else if (ssi_private->use_dma) { + } + + /* + * Enable interrupts only for MCP8610 and MX51. The other MXs have + * different writeable interrupt status registers. + */ + if (ssi_private->use_dma) { /* The 'name' should not have any slashes in it. */ ret = devm_request_irq(&pdev->dev, ssi_private->irq, fsl_ssi_isr, 0, ssi_private->name, ssi_private); + ssi_private->irq_stats = true; if (ret < 0) { dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq); - goto error_irqmap; + goto error_clk; } } - /* Initialize the the device_attribute structure */ - dev_attr = &ssi_private->dev_attr; - sysfs_attr_init(&dev_attr->attr); - dev_attr->attr.name = "statistics"; - dev_attr->attr.mode = S_IRUGO; - dev_attr->show = fsl_sysfs_ssi_show; - - ret = device_create_file(&pdev->dev, dev_attr); - if (ret) { - dev_err(&pdev->dev, "could not create sysfs %s file\n", - ssi_private->dev_attr.attr.name); - goto error_clk; - } - /* Register with ASoC */ dev_set_drvdata(&pdev->dev, ssi_private); @@ -1037,6 +1484,10 @@ static int fsl_ssi_probe(struct platform_device *pdev) goto error_dev; } + ret = fsl_ssi_debugfs_create(ssi_private, &pdev->dev); + if (ret) + goto error_dbgfs; + if (ssi_private->ssi_on_imx) { if (!ssi_private->use_dma) { @@ -1056,11 +1507,11 @@ static int fsl_ssi_probe(struct platform_device *pdev) ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params); if (ret) - goto error_dev; + goto error_pcm; } else { ret = imx_pcm_dma_init(pdev); if (ret) - goto error_dev; + goto error_pcm; } } @@ -1102,19 +1553,28 @@ done: return 0; error_dai: - if (ssi_private->ssi_on_imx) - imx_pcm_dma_exit(pdev); + if (ssi_private->ssi_on_imx && !ssi_private->use_dma) + imx_pcm_fiq_exit(pdev); + +error_pcm: + fsl_ssi_debugfs_remove(ssi_private); + +error_dbgfs: 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); + if (ssi_private->irq_stats) + irq_dispose_mapping(ssi_private->irq); return ret; } @@ -1123,26 +1583,22 @@ static int fsl_ssi_remove(struct platform_device *pdev) { struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev); + fsl_ssi_debugfs_remove(ssi_private); + 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); + } + if (ssi_private->irq_stats) + irq_dispose_mapping(ssi_private->irq); return 0; } -static const struct of_device_id fsl_ssi_ids[] = { - { .compatible = "fsl,mpc8610-ssi", }, - { .compatible = "fsl,imx21-ssi", }, - {} -}; -MODULE_DEVICE_TABLE(of, fsl_ssi_ids); - static struct platform_driver fsl_ssi_driver = { .driver = { .name = "fsl-ssi-dai", 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-mc13783.c b/sound/soc/fsl/imx-mc13783.c index 79cee782dbbf..a2fd7321b5a9 100644 --- a/sound/soc/fsl/imx-mc13783.c +++ b/sound/soc/fsl/imx-mc13783.c @@ -160,7 +160,6 @@ static struct platform_driver imx_mc13783_audio_driver = { .driver = { .name = "imx_mc13783", .owner = THIS_MODULE, - .pm = &snd_soc_pm_ops, }, .probe = imx_mc13783_probe, .remove = imx_mc13783_remove diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index aee23077080a..2585ae44e634 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -41,9 +41,6 @@ static const struct snd_pcm_hardware imx_pcm_hardware = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rate_min = 8000, - .channels_min = 2, - .channels_max = 2, .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, .period_bytes_min = 128, .period_bytes_max = 65535, /* Limited by SDMA engine */ @@ -61,16 +58,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-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index c75d43bb2e92..7abf6a079574 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -162,9 +162,6 @@ static struct snd_pcm_hardware snd_imx_hardware = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rate_min = 8000, - .channels_min = 2, - .channels_max = 2, .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, .period_bytes_min = 128, .period_bytes_max = 16 * 1024, @@ -273,18 +270,17 @@ static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd) ret = imx_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); if (ret) - goto out; + return ret; } if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { ret = imx_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); if (ret) - goto out; + return ret; } -out: - return ret; + return 0; } static int ssi_irq = 0; 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-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index f2beae78969f..1cb22dd034eb 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -33,8 +33,7 @@ struct imx_sgtl5000_data { static int imx_sgtl5000_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct imx_sgtl5000_data *data = container_of(rtd->card, - struct imx_sgtl5000_data, card); + struct imx_sgtl5000_data *data = snd_soc_card_get_drvdata(rtd->card); struct device *dev = rtd->card->dev; int ret; @@ -159,13 +158,15 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) data->card.dapm_widgets = imx_sgtl5000_dapm_widgets; data->card.num_dapm_widgets = ARRAY_SIZE(imx_sgtl5000_dapm_widgets); + platform_set_drvdata(pdev, &data->card); + snd_soc_card_set_drvdata(&data->card, data); + 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 fail; } - platform_set_drvdata(pdev, data); of_node_put(ssi_np); of_node_put(codec_np); @@ -184,7 +185,8 @@ fail: static int imx_sgtl5000_remove(struct platform_device *pdev) { - struct imx_sgtl5000_data *data = platform_get_drvdata(pdev); + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct imx_sgtl5000_data *data = snd_soc_card_get_drvdata(card); clk_put(data->codec_clk); 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..3a3d17ce6ba4 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -71,7 +71,7 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card, { struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; struct imx_priv *priv = &card_priv; - struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev); + struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card); struct device *dev = &priv->pdev->dev; unsigned int pll_out; int ret; @@ -130,8 +130,6 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card, break; } - dapm->bias_level = level; - return 0; } @@ -139,7 +137,7 @@ static int imx_wm8962_late_probe(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; struct imx_priv *priv = &card_priv; - struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev); + struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card); struct device *dev = &priv->pdev->dev; int ret; @@ -266,13 +264,15 @@ static int imx_wm8962_probe(struct platform_device *pdev) data->card.late_probe = imx_wm8962_late_probe; data->card.set_bias_level = imx_wm8962_set_bias_level; + platform_set_drvdata(pdev, &data->card); + snd_soc_card_set_drvdata(&data->card, data); + 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 clk_fail; } - platform_set_drvdata(pdev, data); of_node_put(ssi_np); of_node_put(codec_np); @@ -291,7 +291,8 @@ fail: static int imx_wm8962_remove(struct platform_device *pdev) { - struct imx_wm8962_data *data = platform_get_drvdata(pdev); + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card); if (!IS_ERR(data->codec_clk)) clk_disable_unprepare(data->codec_clk); diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 71bf2f248cd4..f2b5d756b1f3 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -200,10 +200,6 @@ static const struct snd_pcm_hardware psc_dma_hardware = { SNDRV_PCM_INFO_BATCH, .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, - .rate_min = 8000, - .rate_max = 48000, - .channels_min = 1, - .channels_max = 2, .period_bytes_max = 1024 * 1024, .period_bytes_min = 32, .periods_min = 2, diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index f4efaadb80a2..5d07e8a74a21 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -26,8 +26,7 @@ * ALSA that we support all rates and let the codec driver decide what rates * are really supported. */ -#define PSC_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ - SNDRV_PCM_RATE_CONTINUOUS) +#define PSC_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS /** * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode 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/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c index fce63252bdbb..804749a6c61e 100644 --- a/sound/soc/fsl/wm1133-ev1.c +++ b/sound/soc/fsl/wm1133-ev1.c @@ -214,12 +214,6 @@ static int wm1133_ev1_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_context *dapm = &codec->dapm; - snd_soc_dapm_new_controls(dapm, wm1133_ev1_widgets, - ARRAY_SIZE(wm1133_ev1_widgets)); - - snd_soc_dapm_add_routes(dapm, wm1133_ev1_map, - ARRAY_SIZE(wm1133_ev1_map)); - /* Headphone jack detection */ snd_soc_jack_new(codec, "Headphone", SND_JACK_HEADPHONE, &hp_jack); snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins), @@ -257,6 +251,11 @@ static struct snd_soc_card wm1133_ev1 = { .owner = THIS_MODULE, .dai_link = &wm1133_ev1_dai, .num_links = 1, + + .dapm_widgets = wm1133_ev1_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm1133_ev1_widgets), + .dapm_routes = wm1133_ev1_map, + .num_dapm_routes = ARRAY_SIZE(wm1133_ev1_map), }; static struct platform_device *wm1133_ev1_snd_device; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index b2fbb7075a6c..5dd47691ba41 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -8,112 +8,332 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - -#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/device.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/string.h> #include <sound/simple_card.h> +#include <sound/soc-dai.h> +#include <sound/soc.h> -#define asoc_simple_get_card_info(p) \ - container_of(p->dai_link, struct asoc_simple_card_info, snd_link) +struct simple_card_data { + struct snd_soc_card snd_card; + unsigned int daifmt; + struct asoc_simple_dai cpu_dai; + struct asoc_simple_dai codec_dai; + struct snd_soc_dai_link snd_link; +}; static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, - struct asoc_simple_dai *set, - unsigned int daifmt) + struct asoc_simple_dai *set) { - int ret = 0; + int ret; - daifmt |= set->fmt; + if (set->fmt) { + ret = snd_soc_dai_set_fmt(dai, set->fmt); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_fmt error\n"); + goto err; + } + } - if (!ret && daifmt) - ret = snd_soc_dai_set_fmt(dai, daifmt); + if (set->sysclk) { + ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_sysclk error\n"); + goto err; + } + } - if (ret == -ENOTSUPP) { - dev_dbg(dai->dev, "ASoC: set_fmt is not supported\n"); - ret = 0; + if (set->slots) { + ret = snd_soc_dai_set_tdm_slot(dai, 0, 0, + set->slots, + set->slot_width); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); + goto err; + } } - if (!ret && set->sysclk) - ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); + ret = 0; +err: return ret; } static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct asoc_simple_card_info *info = asoc_simple_get_card_info(rtd); + struct simple_card_data *priv = + snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *codec = rtd->codec_dai; struct snd_soc_dai *cpu = rtd->cpu_dai; - unsigned int daifmt = info->daifmt; int ret; - ret = __asoc_simple_card_dai_init(codec, &info->codec_dai, daifmt); + ret = __asoc_simple_card_dai_init(codec, &priv->codec_dai); if (ret < 0) return ret; - ret = __asoc_simple_card_dai_init(cpu, &info->cpu_dai, daifmt); + ret = __asoc_simple_card_dai_init(cpu, &priv->cpu_dai); if (ret < 0) return ret; return 0; } -static int asoc_simple_card_probe(struct platform_device *pdev) +static int +asoc_simple_card_sub_parse_of(struct device_node *np, + unsigned int daifmt, + struct asoc_simple_dai *dai, + const struct device_node **p_node, + const char **name) { - struct asoc_simple_card_info *cinfo = pdev->dev.platform_data; - struct device *dev = &pdev->dev; + struct device_node *node; + struct clk *clk; + int ret; - if (!cinfo) { - dev_err(dev, "no info for asoc-simple-card\n"); - return -EINVAL; + /* + * 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; + *p_node = node; + + /* get dai->name */ + ret = snd_soc_of_get_dai_name(np, name); + if (ret < 0) + goto parse_error; + + /* parse TDM slot */ + ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width); + if (ret) + 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->fmt |= daifmt; + + /* + * 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)) + dai->sysclk = clk_get_rate(clk); } - if (!cinfo->name || - !cinfo->card || - !cinfo->codec || - !cinfo->platform || - !cinfo->cpu_dai.name || - !cinfo->codec_dai.name) { - dev_err(dev, "insufficient asoc_simple_card_info settings\n"); - return -EINVAL; + ret = 0; + +parse_error: + of_node_put(node); + + return ret; +} + +static int asoc_simple_card_parse_of(struct device_node *node, + struct simple_card_data *priv, + struct device *dev) +{ + struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link; + struct device_node *np; + char *name; + int ret; + + /* parsing the card name from DT */ + snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name"); + + /* get CPU/CODEC common format via simple-audio-card,format */ + priv->daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,") & + (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK); + + /* off-codec widgets */ + if (of_property_read_bool(node, "simple-audio-card,widgets")) { + ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card, + "simple-audio-card,widgets"); + if (ret) + return ret; } + /* DAPM routes */ + if (of_property_read_bool(node, "simple-audio-card,routing")) { + ret = snd_soc_of_parse_audio_routing(&priv->snd_card, + "simple-audio-card,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, priv->daifmt, + &priv->cpu_dai, + &dai_link->cpu_of_node, + &dai_link->cpu_dai_name); + 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, priv->daifmt, + &priv->codec_dai, + &dai_link->codec_of_node, + &dai_link->codec_dai_name); + if (ret < 0) + return ret; + + if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) + return -EINVAL; + + /* card name is created from CPU/CODEC dai name */ + name = devm_kzalloc(dev, + strlen(dai_link->cpu_dai_name) + + strlen(dai_link->codec_dai_name) + 2, + GFP_KERNEL); + sprintf(name, "%s-%s", dai_link->cpu_dai_name, + dai_link->codec_dai_name); + if (!priv->snd_card.name) + priv->snd_card.name = name; + dai_link->name = dai_link->stream_name = name; + + /* simple-card assumes platform == cpu */ + dai_link->platform_of_node = dai_link->cpu_of_node; + + dev_dbg(dev, "card-name : %s\n", name); + dev_dbg(dev, "platform : %04x\n", priv->daifmt); + dev_dbg(dev, "cpu : %s / %04x / %d\n", + dai_link->cpu_dai_name, + priv->cpu_dai.fmt, + priv->cpu_dai.sysclk); + dev_dbg(dev, "codec : %s / %04x / %d\n", + dai_link->codec_dai_name, + priv->codec_dai.fmt, + priv->codec_dai.sysclk); + /* - * init snd_soc_dai_link + * soc_bind_dai_link() will check cpu name + * after of_node matching if dai_link has cpu_dai_name. + * but, it will never match if name was created by fmt_single_name() + * remove cpu_dai_name to escape name matching. + * see + * fmt_single_name() + * fmt_multiple_name() */ - cinfo->snd_link.name = cinfo->name; - cinfo->snd_link.stream_name = cinfo->name; - cinfo->snd_link.cpu_dai_name = cinfo->cpu_dai.name; - 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.init = asoc_simple_card_dai_init; + dai_link->cpu_dai_name = NULL; + + return 0; +} + +static int asoc_simple_card_probe(struct platform_device *pdev) +{ + struct simple_card_data *priv; + struct snd_soc_dai_link *dai_link; + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; /* * init snd_soc_card */ - cinfo->snd_card.name = cinfo->card; - 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; + priv->snd_card.owner = THIS_MODULE; + priv->snd_card.dev = dev; + dai_link = &priv->snd_link; + priv->snd_card.dai_link = dai_link; + priv->snd_card.num_links = 1; - return snd_soc_register_card(&cinfo->snd_card); -} + if (np && of_device_is_available(np)) { -static int asoc_simple_card_remove(struct platform_device *pdev) -{ - struct asoc_simple_card_info *cinfo = pdev->dev.platform_data; + ret = asoc_simple_card_parse_of(np, priv, dev); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "parse error %d\n", ret); + return ret; + } + } else { + struct asoc_simple_card_info *cinfo; + + cinfo = dev->platform_data; + if (!cinfo) { + dev_err(dev, "no info for asoc-simple-card\n"); + return -EINVAL; + } + + if (!cinfo->name || + !cinfo->codec_dai.name || + !cinfo->codec || + !cinfo->platform || + !cinfo->cpu_dai.name) { + dev_err(dev, "insufficient asoc_simple_card_info settings\n"); + return -EINVAL; + } + + priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name; + dai_link->name = cinfo->name; + dai_link->stream_name = cinfo->name; + dai_link->platform_name = cinfo->platform; + dai_link->codec_name = cinfo->codec; + dai_link->cpu_dai_name = cinfo->cpu_dai.name; + dai_link->codec_dai_name = cinfo->codec_dai.name; + memcpy(&priv->cpu_dai, &cinfo->cpu_dai, + sizeof(priv->cpu_dai)); + memcpy(&priv->codec_dai, &cinfo->codec_dai, + sizeof(priv->codec_dai)); - return snd_soc_unregister_card(&cinfo->snd_card); + priv->cpu_dai.fmt |= cinfo->daifmt; + priv->codec_dai.fmt |= cinfo->daifmt; + } + + /* + * init snd_soc_dai_link + */ + dai_link->init = asoc_simple_card_dai_init; + + snd_soc_card_set_drvdata(&priv->snd_card, priv); + + return devm_snd_soc_register_card(&pdev->dev, &priv->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/intel/Kconfig b/sound/soc/intel/Kconfig new file mode 100644 index 000000000000..4577b69fcf2c --- /dev/null +++ b/sound/soc/intel/Kconfig @@ -0,0 +1,51 @@ +config SND_MFLD_MACHINE + tristate "SOC Machine Audio driver for Intel Medfield MID platform" + depends on INTEL_SCU_IPC + select SND_SOC_SN95031 + select SND_SST_MFLD_PLATFORM + help + This adds support for ASoC machine driver for Intel(R) MID Medfield platform + used as alsa device in audio substem in Intel(R) MID devices + Say Y if you have such a device + If unsure select "N". + +config SND_SST_MFLD_PLATFORM + tristate + +config SND_SOC_INTEL_SST + tristate "ASoC support for Intel(R) Smart Sound Technology" + select SND_SOC_INTEL_SST_ACPI if ACPI + depends on (X86 || COMPILE_TEST) + help + This adds support for Intel(R) Smart Sound Technology (SST). + Say Y if you have such a device + If unsure select "N". + +config SND_SOC_INTEL_SST_ACPI + tristate + +config SND_SOC_INTEL_HASWELL + tristate + +config SND_SOC_INTEL_BAYTRAIL + tristate + +config SND_SOC_INTEL_HASWELL_MACH + tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint" + depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS + select SND_SOC_INTEL_HASWELL + select SND_SOC_RT5640 + help + This adds support for the Lynxpoint Audio DSP on Intel(R) Haswell + Ultrabook platforms. + Say Y if you have such a device + If unsure select "N". + +config SND_SOC_INTEL_BYT_RT5640_MACH + tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec" + depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS + select SND_SOC_INTEL_BAYTRAIL + select SND_SOC_RT5640 + help + This adds audio driver for Intel Baytrail platform based boards + with the RT5640 audio codec. diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile new file mode 100644 index 000000000000..edeb79ae3dff --- /dev/null +++ b/sound/soc/intel/Makefile @@ -0,0 +1,28 @@ +# Core support +snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o +snd-soc-sst-acpi-objs := sst-acpi.o + +snd-soc-sst-mfld-platform-objs := sst-mfld-platform.o +snd-soc-mfld-machine-objs := mfld_machine.o + +obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o +obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o + +obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o +obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o + +# Platform Support +snd-soc-sst-haswell-pcm-objs := \ + sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o +snd-soc-sst-baytrail-pcm-objs := \ + sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o + +obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o +obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o + +# Machine support +snd-soc-sst-haswell-objs := haswell.o +snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o + +obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o +obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o diff --git a/sound/soc/intel/byt-rt5640.c b/sound/soc/intel/byt-rt5640.c new file mode 100644 index 000000000000..eff97c8e5218 --- /dev/null +++ b/sound/soc/intel/byt-rt5640.c @@ -0,0 +1,187 @@ +/* + * Intel Baytrail SST RT5640 machine driver + * Copyright (c) 2014, Intel Corporation. + * + * 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. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../codecs/rt5640.h" + +#include "sst-dsp.h" + +static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Internal Mic", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = { + {"IN2P", NULL, "Headset Mic"}, + {"IN2N", NULL, "Headset Mic"}, + {"DMIC1", NULL, "Internal Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Speaker", NULL, "SPOLP"}, + {"Speaker", NULL, "SPOLN"}, + {"Speaker", NULL, "SPORP"}, + {"Speaker", NULL, "SPORN"}, +}; + +static const struct snd_kcontrol_new byt_rt5640_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Internal Mic"), + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static int byt_rt5640_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; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, + params_rate(params) * 256, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "can't set codec clock %d\n", ret); + return ret; + } + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1, + params_rate(params) * 64, + params_rate(params) * 256); + if (ret < 0) { + dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret); + return ret; + } + return 0; +} + +static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_card *card = runtime->card; + + card->dapm.idle_bias_off = true; + + ret = snd_soc_add_card_controls(card, byt_rt5640_controls, + ARRAY_SIZE(byt_rt5640_controls)); + if (ret) { + dev_err(card->dev, "unable to add card controls\n"); + return ret; + } + + snd_soc_dapm_ignore_suspend(dapm, "HPOL"); + snd_soc_dapm_ignore_suspend(dapm, "HPOR"); + + snd_soc_dapm_ignore_suspend(dapm, "SPOLP"); + snd_soc_dapm_ignore_suspend(dapm, "SPOLN"); + snd_soc_dapm_ignore_suspend(dapm, "SPORP"); + snd_soc_dapm_ignore_suspend(dapm, "SPORN"); + + snd_soc_dapm_enable_pin(dapm, "Headset Mic"); + snd_soc_dapm_enable_pin(dapm, "Headphone"); + snd_soc_dapm_enable_pin(dapm, "Speaker"); + snd_soc_dapm_enable_pin(dapm, "Internal Mic"); + + snd_soc_dapm_sync(dapm); + return ret; +} + +static struct snd_soc_ops byt_rt5640_ops = { + .hw_params = byt_rt5640_hw_params, +}; + +static struct snd_soc_dai_link byt_rt5640_dais[] = { + { + .name = "Baytrail Audio", + .stream_name = "Audio", + .cpu_dai_name = "Front-cpu-dai", + .codec_dai_name = "rt5640-aif1", + .codec_name = "i2c-10EC5640:00", + .platform_name = "baytrail-pcm-audio", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .init = byt_rt5640_init, + .ignore_suspend = 1, + .ops = &byt_rt5640_ops, + }, + { + .name = "Baytrail Voice", + .stream_name = "Voice", + .cpu_dai_name = "Mic1-cpu-dai", + .codec_dai_name = "rt5640-aif1", + .codec_name = "i2c-10EC5640:00", + .platform_name = "baytrail-pcm-audio", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .init = NULL, + .ignore_suspend = 1, + .ops = &byt_rt5640_ops, + }, +}; + +static struct snd_soc_card byt_rt5640_card = { + .name = "byt-rt5640", + .dai_link = byt_rt5640_dais, + .num_links = ARRAY_SIZE(byt_rt5640_dais), + .dapm_widgets = byt_rt5640_widgets, + .num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets), + .dapm_routes = byt_rt5640_audio_map, + .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map), +}; + +static int byt_rt5640_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &byt_rt5640_card; + struct device *dev = &pdev->dev; + + card->dev = &pdev->dev; + dev_set_drvdata(dev, card); + return snd_soc_register_card(card); +} + +static int byt_rt5640_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static struct platform_driver byt_rt5640_audio = { + .probe = byt_rt5640_probe, + .remove = byt_rt5640_remove, + .driver = { + .name = "byt-rt5640", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(byt_rt5640_audio) + +MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver"); +MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:byt-rt5640"); diff --git a/sound/soc/intel/haswell.c b/sound/soc/intel/haswell.c new file mode 100644 index 000000000000..54345a2a7386 --- /dev/null +++ b/sound/soc/intel/haswell.c @@ -0,0 +1,235 @@ +/* + * Intel Haswell Lynxpoint SST Audio + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License 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/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include "sst-dsp.h" +#include "sst-haswell-ipc.h" + +#include "../codecs/rt5640.h" + +/* Haswell ULT platforms have a Headphone and Mic jack */ +static const struct snd_soc_dapm_widget haswell_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("Mic", NULL), +}; + +static const struct snd_soc_dapm_route haswell_rt5640_map[] = { + + {"Headphones", NULL, "HPOR"}, + {"Headphones", NULL, "HPOL"}, + {"IN2P", NULL, "Mic"}, + + /* CODEC BE connections */ + {"SSP0 CODEC IN", NULL, "AIF1 Capture"}, + {"AIF1 Playback", NULL, "SSP0 CODEC OUT"}, +}; + +static int haswell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The ADSP will covert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP0 to 16 bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S16_LE); + return 0; +} + +static int haswell_rt5640_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; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, 12288000, + SND_SOC_CLOCK_IN); + + if (ret < 0) { + dev_err(rtd->dev, "can't set codec sysclk configuration\n"); + return ret; + } + + /* set correct codec filter for DAI format and clock config */ + snd_soc_update_bits(rtd->codec, 0x83, 0xffff, 0x8000); + + return ret; +} + +static struct snd_soc_ops haswell_rt5640_ops = { + .hw_params = haswell_rt5640_hw_params, +}; + +static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev); + struct sst_hsw *haswell = pdata->dsp; + int ret; + + /* Set ADSP SSP port settings */ + ret = sst_hsw_device_set_config(haswell, SST_HSW_DEVICE_SSP_0, + SST_HSW_DEVICE_MCLK_FREQ_24_MHZ, + SST_HSW_DEVICE_CLOCK_MASTER, 9); + if (ret < 0) { + dev_err(rtd->dev, "failed to set device config\n"); + return ret; + } + + /* always connected */ + snd_soc_dapm_enable_pin(dapm, "Headphones"); + snd_soc_dapm_enable_pin(dapm, "Mic"); + + return 0; +} + +static struct snd_soc_dai_link haswell_rt5640_dais[] = { + /* Front End DAI links */ + { + .name = "System", + .stream_name = "System Playback", + .cpu_dai_name = "System Pin", + .platform_name = "haswell-pcm-audio", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = haswell_rtd_init, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + }, + { + .name = "Offload0", + .stream_name = "Offload0 Playback", + .cpu_dai_name = "Offload0 Pin", + .platform_name = "haswell-pcm-audio", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + }, + { + .name = "Offload1", + .stream_name = "Offload1 Playback", + .cpu_dai_name = "Offload1 Pin", + .platform_name = "haswell-pcm-audio", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + }, + { + .name = "Loopback", + .stream_name = "Loopback", + .cpu_dai_name = "Loopback Pin", + .platform_name = "haswell-pcm-audio", + .dynamic = 0, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + }, + { + .name = "Capture", + .stream_name = "Capture", + .cpu_dai_name = "Capture Pin", + .platform_name = "haswell-pcm-audio", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "Codec", + .be_id = 0, + .cpu_dai_name = "snd-soc-dummy-dai", + .platform_name = "snd-soc-dummy", + .no_pcm = 1, + .codec_name = "i2c-INT33CA:00", + .codec_dai_name = "rt5640-aif1", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = haswell_ssp0_fixup, + .ops = &haswell_rt5640_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +/* audio machine driver for Haswell Lynxpoint DSP + RT5640 */ +static struct snd_soc_card haswell_rt5640 = { + .name = "haswell-rt5640", + .owner = THIS_MODULE, + .dai_link = haswell_rt5640_dais, + .num_links = ARRAY_SIZE(haswell_rt5640_dais), + .dapm_widgets = haswell_widgets, + .num_dapm_widgets = ARRAY_SIZE(haswell_widgets), + .dapm_routes = haswell_rt5640_map, + .num_dapm_routes = ARRAY_SIZE(haswell_rt5640_map), + .fully_routed = true, +}; + +static int haswell_audio_probe(struct platform_device *pdev) +{ + haswell_rt5640.dev = &pdev->dev; + + return snd_soc_register_card(&haswell_rt5640); +} + +static int haswell_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&haswell_rt5640); + return 0; +} + +static struct platform_driver haswell_audio = { + .probe = haswell_audio_probe, + .remove = haswell_audio_remove, + .driver = { + .name = "haswell-audio", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(haswell_audio) + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood, Xingchao Wang"); +MODULE_DESCRIPTION("Intel SST Audio for Haswell Lynxpoint"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:haswell-audio"); diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/intel/mfld_machine.c index d3d4c32434f7..0cef32e9d402 100644 --- a/sound/soc/mid-x86/mfld_machine.c +++ b/sound/soc/intel/mfld_machine.c @@ -101,20 +101,27 @@ static int headset_set_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = &codec->dapm; if (ucontrol->value.integer.value[0] == hs_switch) return 0; + snd_soc_dapm_mutex_lock(dapm); + if (ucontrol->value.integer.value[0]) { pr_debug("hs_set HS path\n"); - snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); - snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones"); + snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT"); } else { pr_debug("hs_set EP path\n"); - snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); - snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones"); + snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT"); } - snd_soc_dapm_sync(&codec->dapm); + + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); + hs_switch = ucontrol->value.integer.value[0]; return 0; @@ -122,18 +129,20 @@ static int headset_set_switch(struct snd_kcontrol *kcontrol, static void lo_enable_out_pins(struct snd_soc_codec *codec) { - snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL"); - snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR"); - snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL"); - snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR"); - snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT"); - snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT"); + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTL"); + snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTR"); + snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTL"); + snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTR"); + snd_soc_dapm_enable_pin_unlocked(dapm, "VIB1OUT"); + snd_soc_dapm_enable_pin_unlocked(dapm, "VIB2OUT"); if (hs_switch) { - snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); - snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones"); + snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT"); } else { - snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); - snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones"); + snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT"); } } @@ -148,44 +157,52 @@ static int lo_set_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = &codec->dapm; if (ucontrol->value.integer.value[0] == lo_dac) return 0; + snd_soc_dapm_mutex_lock(dapm); + /* we dont want to work with last state of lineout so just enable all * pins and then disable pins not required */ lo_enable_out_pins(codec); + switch (ucontrol->value.integer.value[0]) { case 0: pr_debug("set vibra path\n"); - snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT"); - snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT"); + snd_soc_dapm_disable_pin_unlocked(dapm, "VIB1OUT"); + snd_soc_dapm_disable_pin_unlocked(dapm, "VIB2OUT"); snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0); break; case 1: pr_debug("set hs path\n"); - snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); - snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones"); + snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT"); snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22); break; case 2: pr_debug("set spkr path\n"); - snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL"); - snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR"); + snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTL"); + snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTR"); snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44); break; case 3: pr_debug("set null path\n"); - snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL"); - snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR"); + snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTL"); + snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTR"); snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66); break; } - snd_soc_dapm_sync(&codec->dapm); + + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); + lo_dac = ucontrol->value.integer.value[0]; return 0; } diff --git a/sound/soc/intel/sst-acpi.c b/sound/soc/intel/sst-acpi.c new file mode 100644 index 000000000000..5d06eecb6198 --- /dev/null +++ b/sound/soc/intel/sst-acpi.c @@ -0,0 +1,284 @@ +/* + * Intel SST loader on ACPI systems + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License 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/acpi.h> +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "sst-dsp.h" + +#define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000 +#define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000 +#define SST_LPT_DSP_DMA_SIZE (1024 - 1) + +/* Descriptor for SST ASoC machine driver */ +struct sst_acpi_mach { + /* ACPI ID for the matching machine driver. Audio codec for instance */ + const u8 id[ACPI_ID_LEN]; + /* machine driver name */ + const char *drv_name; + /* firmware file name */ + const char *fw_filename; +}; + +/* Descriptor for setting up SST platform data */ +struct sst_acpi_desc { + const char *drv_name; + struct sst_acpi_mach *machines; + /* Platform resource indexes. Must set to -1 if not used */ + int resindex_lpe_base; + int resindex_pcicfg_base; + int resindex_fw_base; + int irqindex_host_ipc; + int resindex_dma_base; + /* Unique number identifying the SST core on platform */ + int sst_id; + /* DMA only valid when resindex_dma_base != -1*/ + int dma_engine; + int dma_size; +}; + +struct sst_acpi_priv { + struct platform_device *pdev_mach; + struct platform_device *pdev_pcm; + struct sst_pdata sst_pdata; + struct sst_acpi_desc *desc; + struct sst_acpi_mach *mach; +}; + +static void sst_acpi_fw_cb(const struct firmware *fw, void *context) +{ + struct platform_device *pdev = context; + struct device *dev = &pdev->dev; + struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev); + struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata; + struct sst_acpi_desc *desc = sst_acpi->desc; + struct sst_acpi_mach *mach = sst_acpi->mach; + + sst_pdata->fw = fw; + if (!fw) { + dev_err(dev, "Cannot load firmware %s\n", mach->fw_filename); + return; + } + + /* register PCM and DAI driver */ + sst_acpi->pdev_pcm = + platform_device_register_data(dev, desc->drv_name, -1, + sst_pdata, sizeof(*sst_pdata)); + if (IS_ERR(sst_acpi->pdev_pcm)) { + dev_err(dev, "Cannot register device %s. Error %d\n", + desc->drv_name, (int)PTR_ERR(sst_acpi->pdev_pcm)); + } + + return; +} + +static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, + void *context, void **ret) +{ + *(bool *)context = true; + return AE_OK; +} + +static struct sst_acpi_mach *sst_acpi_find_machine( + struct sst_acpi_mach *machines) +{ + struct sst_acpi_mach *mach; + bool found = false; + + for (mach = machines; mach->id[0]; mach++) + if (ACPI_SUCCESS(acpi_get_devices(mach->id, + sst_acpi_mach_match, + &found, NULL)) && found) + return mach; + + return NULL; +} + +static int sst_acpi_probe(struct platform_device *pdev) +{ + const struct acpi_device_id *id; + struct device *dev = &pdev->dev; + struct sst_acpi_priv *sst_acpi; + struct sst_pdata *sst_pdata; + struct sst_acpi_mach *mach; + struct sst_acpi_desc *desc; + struct resource *mmio; + int ret = 0; + + sst_acpi = devm_kzalloc(dev, sizeof(*sst_acpi), GFP_KERNEL); + if (sst_acpi == NULL) + return -ENOMEM; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + desc = (struct sst_acpi_desc *)id->driver_data; + mach = sst_acpi_find_machine(desc->machines); + if (mach == NULL) { + dev_err(dev, "No matching ASoC machine driver found\n"); + return -ENODEV; + } + + sst_pdata = &sst_acpi->sst_pdata; + sst_pdata->id = desc->sst_id; + sst_acpi->desc = desc; + sst_acpi->mach = mach; + + if (desc->resindex_dma_base >= 0) { + sst_pdata->dma_engine = desc->dma_engine; + sst_pdata->dma_base = desc->resindex_dma_base; + sst_pdata->dma_size = desc->dma_size; + } + + if (desc->irqindex_host_ipc >= 0) + sst_pdata->irq = platform_get_irq(pdev, desc->irqindex_host_ipc); + + if (desc->resindex_lpe_base >= 0) { + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_lpe_base); + if (mmio) { + sst_pdata->lpe_base = mmio->start; + sst_pdata->lpe_size = resource_size(mmio); + } + } + + if (desc->resindex_pcicfg_base >= 0) { + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_pcicfg_base); + if (mmio) { + sst_pdata->pcicfg_base = mmio->start; + sst_pdata->pcicfg_size = resource_size(mmio); + } + } + + if (desc->resindex_fw_base >= 0) { + mmio = platform_get_resource(pdev, IORESOURCE_MEM, + desc->resindex_fw_base); + if (mmio) { + sst_pdata->fw_base = mmio->start; + sst_pdata->fw_size = resource_size(mmio); + } + } + + platform_set_drvdata(pdev, sst_acpi); + + /* register machine driver */ + sst_acpi->pdev_mach = + platform_device_register_data(dev, mach->drv_name, -1, + sst_pdata, sizeof(*sst_pdata)); + if (IS_ERR(sst_acpi->pdev_mach)) + return PTR_ERR(sst_acpi->pdev_mach); + + /* continue SST probing after firmware is loaded */ + ret = request_firmware_nowait(THIS_MODULE, true, mach->fw_filename, + dev, GFP_KERNEL, pdev, sst_acpi_fw_cb); + if (ret) + platform_device_unregister(sst_acpi->pdev_mach); + + return ret; +} + +static int sst_acpi_remove(struct platform_device *pdev) +{ + struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev); + struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata; + + platform_device_unregister(sst_acpi->pdev_mach); + if (!IS_ERR_OR_NULL(sst_acpi->pdev_pcm)) + platform_device_unregister(sst_acpi->pdev_pcm); + release_firmware(sst_pdata->fw); + + return 0; +} + +static struct sst_acpi_mach haswell_machines[] = { + { "INT33CA", "haswell-audio", "intel/IntcSST1.bin" }, + {} +}; + +static struct sst_acpi_desc sst_acpi_haswell_desc = { + .drv_name = "haswell-pcm-audio", + .machines = haswell_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_fw_base = -1, + .irqindex_host_ipc = 0, + .sst_id = SST_DEV_ID_LYNX_POINT, + .dma_engine = SST_DMA_TYPE_DW, + .resindex_dma_base = SST_LPT_DSP_DMA_ADDR_OFFSET, + .dma_size = SST_LPT_DSP_DMA_SIZE, +}; + +static struct sst_acpi_mach broadwell_machines[] = { + { "INT343A", "broadwell-audio", "intel/IntcSST2.bin" }, + {} +}; + +static struct sst_acpi_desc sst_acpi_broadwell_desc = { + .drv_name = "haswell-pcm-audio", + .machines = broadwell_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_fw_base = -1, + .irqindex_host_ipc = 0, + .sst_id = SST_DEV_ID_WILDCAT_POINT, + .dma_engine = SST_DMA_TYPE_DW, + .resindex_dma_base = SST_WPT_DSP_DMA_ADDR_OFFSET, + .dma_size = SST_LPT_DSP_DMA_SIZE, +}; + +static struct sst_acpi_mach baytrail_machines[] = { + { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-i2s_master" }, + {} +}; + +static struct sst_acpi_desc sst_acpi_baytrail_desc = { + .drv_name = "baytrail-pcm-audio", + .machines = baytrail_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = 1, + .resindex_fw_base = 2, + .irqindex_host_ipc = 5, + .sst_id = SST_DEV_ID_BYT, + .resindex_dma_base = -1, +}; + +static struct acpi_device_id sst_acpi_match[] = { + { "INT33C8", (unsigned long)&sst_acpi_haswell_desc }, + { "INT3438", (unsigned long)&sst_acpi_broadwell_desc }, + { "80860F28", (unsigned long)&sst_acpi_baytrail_desc }, + { } +}; +MODULE_DEVICE_TABLE(acpi, sst_acpi_match); + +static struct platform_driver sst_acpi_driver = { + .probe = sst_acpi_probe, + .remove = sst_acpi_remove, + .driver = { + .name = "sst-acpi", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(sst_acpi_match), + }, +}; +module_platform_driver(sst_acpi_driver); + +MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>"); +MODULE_DESCRIPTION("Intel SST loader on ACPI systems"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/intel/sst-baytrail-dsp.c b/sound/soc/intel/sst-baytrail-dsp.c new file mode 100644 index 000000000000..a50bf7fc0e3a --- /dev/null +++ b/sound/soc/intel/sst-baytrail-dsp.c @@ -0,0 +1,372 @@ +/* + * Intel Baytrail SST DSP driver + * Copyright (c) 2014, Intel Corporation. + * + * 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. + */ + +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/firmware.h> + +#include "sst-dsp.h" +#include "sst-dsp-priv.h" +#include "sst-baytrail-ipc.h" + +#define SST_BYT_FW_SIGNATURE_SIZE 4 +#define SST_BYT_FW_SIGN "$SST" + +#define SST_BYT_IRAM_OFFSET 0xC0000 +#define SST_BYT_DRAM_OFFSET 0x100000 +#define SST_BYT_SHIM_OFFSET 0x140000 + +enum sst_ram_type { + SST_BYT_IRAM = 1, + SST_BYT_DRAM = 2, + SST_BYT_CACHE = 3, +}; + +struct dma_block_info { + enum sst_ram_type type; /* IRAM/DRAM */ + u32 size; /* Bytes */ + u32 ram_offset; /* Offset in I/DRAM */ + u32 rsvd; /* Reserved field */ +}; + +struct fw_header { + unsigned char signature[SST_BYT_FW_SIGNATURE_SIZE]; + u32 file_size; /* size of fw minus this header */ + u32 modules; /* # of modules */ + u32 file_format; /* version of header format */ + u32 reserved[4]; +}; + +struct sst_byt_fw_module_header { + unsigned char signature[SST_BYT_FW_SIGNATURE_SIZE]; + u32 mod_size; /* size of module */ + u32 blocks; /* # of blocks */ + u32 type; /* codec type, pp lib */ + u32 entry_point; +}; + +static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, + struct sst_byt_fw_module_header *module) +{ + struct dma_block_info *block; + struct sst_module *mod; + struct sst_module_data block_data; + struct sst_module_template template; + int count; + + memset(&template, 0, sizeof(template)); + template.id = module->type; + template.entry = module->entry_point; + template.p.type = SST_MEM_DRAM; + template.p.data_type = SST_DATA_P; + template.s.type = SST_MEM_DRAM; + template.s.data_type = SST_DATA_S; + + mod = sst_module_new(fw, &template, NULL); + if (mod == NULL) + return -ENOMEM; + + block = (void *)module + sizeof(*module); + + for (count = 0; count < module->blocks; count++) { + + if (block->size <= 0) { + dev_err(dsp->dev, "block %d size invalid\n", count); + return -EINVAL; + } + + switch (block->type) { + case SST_BYT_IRAM: + block_data.offset = block->ram_offset + + dsp->addr.iram_offset; + block_data.type = SST_MEM_IRAM; + break; + case SST_BYT_DRAM: + block_data.offset = block->ram_offset + + dsp->addr.dram_offset; + block_data.type = SST_MEM_DRAM; + break; + case SST_BYT_CACHE: + block_data.offset = block->ram_offset + + (dsp->addr.fw_ext - dsp->addr.lpe); + block_data.type = SST_MEM_CACHE; + break; + default: + dev_err(dsp->dev, "wrong ram type 0x%x in block0x%x\n", + block->type, count); + return -EINVAL; + } + + block_data.size = block->size; + block_data.data_type = SST_DATA_M; + block_data.data = (void *)block + sizeof(*block); + + sst_module_insert_fixed_block(mod, &block_data); + + block = (void *)block + sizeof(*block) + block->size; + } + return 0; +} + +static int sst_byt_parse_fw_image(struct sst_fw *sst_fw) +{ + struct fw_header *header; + struct sst_byt_fw_module_header *module; + struct sst_dsp *dsp = sst_fw->dsp; + int ret, count; + + /* Read the header information from the data pointer */ + header = (struct fw_header *)sst_fw->dma_buf; + + /* verify FW */ + if ((strncmp(header->signature, SST_BYT_FW_SIGN, 4) != 0) || + (sst_fw->size != header->file_size + sizeof(*header))) { + /* Invalid FW signature */ + dev_err(dsp->dev, "Invalid FW sign/filesize mismatch\n"); + return -EINVAL; + } + + dev_dbg(dsp->dev, + "header sign=%4s size=0x%x modules=0x%x fmt=0x%x size=%zu\n", + header->signature, header->file_size, header->modules, + header->file_format, sizeof(*header)); + + module = (void *)sst_fw->dma_buf + sizeof(*header); + for (count = 0; count < header->modules; count++) { + /* module */ + ret = sst_byt_parse_module(dsp, sst_fw, module); + if (ret < 0) { + dev_err(dsp->dev, "invalid module %d\n", count); + return ret; + } + module = (void *)module + sizeof(*module) + module->mod_size; + } + + return 0; +} + +static void sst_byt_dump_shim(struct sst_dsp *sst) +{ + int i; + u64 reg; + + for (i = 0; i <= 0xF0; i += 8) { + reg = sst_dsp_shim_read64_unlocked(sst, i); + if (reg) + dev_dbg(sst->dev, "shim 0x%2.2x value 0x%16.16llx\n", + i, reg); + } + + for (i = 0x00; i <= 0xff; i += 4) { + reg = readl(sst->addr.pci_cfg + i); + if (reg) + dev_dbg(sst->dev, "pci 0x%2.2x value 0x%8.8x\n", + i, (u32)reg); + } +} + +static irqreturn_t sst_byt_irq(int irq, void *context) +{ + struct sst_dsp *sst = (struct sst_dsp *) context; + u64 isrx; + irqreturn_t ret = IRQ_NONE; + + spin_lock(&sst->spinlock); + + isrx = sst_dsp_shim_read64_unlocked(sst, SST_ISRX); + if (isrx & SST_ISRX_DONE) { + /* ADSP has processed the message request from IA */ + sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCX, + SST_BYT_IPCX_DONE, 0); + ret = IRQ_WAKE_THREAD; + } + if (isrx & SST_BYT_ISRX_REQUEST) { + /* mask message request from ADSP and do processing later */ + sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX, + SST_BYT_IMRX_REQUEST, + SST_BYT_IMRX_REQUEST); + ret = IRQ_WAKE_THREAD; + } + + spin_unlock(&sst->spinlock); + + return ret; +} + +static void sst_byt_boot(struct sst_dsp *sst) +{ + int tries = 10; + + /* release stall and wait to unstall */ + sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_STALL, 0x0); + while (tries--) { + if (!(sst_dsp_shim_read64(sst, SST_CSR) & + SST_BYT_CSR_PWAITMODE)) + break; + msleep(100); + } + if (tries < 0) { + dev_err(sst->dev, "unable to start DSP\n"); + sst_byt_dump_shim(sst); + } +} + +static void sst_byt_reset(struct sst_dsp *sst) +{ + /* put DSP into reset, set reset vector and stall */ + sst_dsp_shim_update_bits64(sst, SST_CSR, + SST_BYT_CSR_RST | SST_BYT_CSR_VECTOR_SEL | SST_BYT_CSR_STALL, + SST_BYT_CSR_RST | SST_BYT_CSR_VECTOR_SEL | SST_BYT_CSR_STALL); + + udelay(10); + + /* take DSP out of reset and keep stalled for FW loading */ + sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_RST, 0); +} + +struct sst_adsp_memregion { + u32 start; + u32 end; + int blocks; + enum sst_mem_type type; +}; + +/* BYT test stuff */ +static const struct sst_adsp_memregion byt_region[] = { + {0xC0000, 0x100000, 8, SST_MEM_IRAM}, /* I-SRAM - 8 * 32kB */ + {0x100000, 0x140000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */ +}; + +static int sst_byt_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata) +{ + sst->addr.lpe_base = pdata->lpe_base; + sst->addr.lpe = ioremap(pdata->lpe_base, pdata->lpe_size); + if (!sst->addr.lpe) + return -ENODEV; + + /* ADSP PCI MMIO config space */ + sst->addr.pci_cfg = ioremap(pdata->pcicfg_base, pdata->pcicfg_size); + if (!sst->addr.pci_cfg) { + iounmap(sst->addr.lpe); + return -ENODEV; + } + + /* SST Extended FW allocation */ + sst->addr.fw_ext = ioremap(pdata->fw_base, pdata->fw_size); + if (!sst->addr.fw_ext) { + iounmap(sst->addr.pci_cfg); + iounmap(sst->addr.lpe); + return -ENODEV; + } + + /* SST Shim */ + sst->addr.shim = sst->addr.lpe + sst->addr.shim_offset; + + sst_dsp_mailbox_init(sst, SST_BYT_MAILBOX_OFFSET + 0x204, + SST_BYT_IPC_MAX_PAYLOAD_SIZE, + SST_BYT_MAILBOX_OFFSET, + SST_BYT_IPC_MAX_PAYLOAD_SIZE); + + sst->irq = pdata->irq; + + return 0; +} + +static int sst_byt_init(struct sst_dsp *sst, struct sst_pdata *pdata) +{ + const struct sst_adsp_memregion *region; + struct device *dev; + int ret = -ENODEV, i, j, region_count; + u32 offset, size; + + dev = sst->dev; + + switch (sst->id) { + case SST_DEV_ID_BYT: + region = byt_region; + region_count = ARRAY_SIZE(byt_region); + sst->addr.iram_offset = SST_BYT_IRAM_OFFSET; + sst->addr.dram_offset = SST_BYT_DRAM_OFFSET; + sst->addr.shim_offset = SST_BYT_SHIM_OFFSET; + break; + default: + dev_err(dev, "failed to get mem resources\n"); + return ret; + } + + ret = sst_byt_resource_map(sst, pdata); + if (ret < 0) { + dev_err(dev, "failed to map resources\n"); + return ret; + } + + /* + * save the physical address of extended firmware block in the first + * 4 bytes of the mailbox + */ + memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET, + &pdata->fw_base, sizeof(u32)); + + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + /* enable Interrupt from both sides */ + sst_dsp_shim_update_bits64(sst, SST_IMRX, 0x3, 0x0); + sst_dsp_shim_update_bits64(sst, SST_IMRD, 0x3, 0x0); + + /* register DSP memory blocks - ideally we should get this from ACPI */ + for (i = 0; i < region_count; i++) { + offset = region[i].start; + size = (region[i].end - region[i].start) / region[i].blocks; + + /* register individual memory blocks */ + for (j = 0; j < region[i].blocks; j++) { + sst_mem_block_register(sst, offset, size, + region[i].type, NULL, j, sst); + offset += size; + } + } + + return 0; +} + +static void sst_byt_free(struct sst_dsp *sst) +{ + sst_mem_block_unregister_all(sst); + iounmap(sst->addr.lpe); + iounmap(sst->addr.pci_cfg); + iounmap(sst->addr.fw_ext); +} + +struct sst_ops sst_byt_ops = { + .reset = sst_byt_reset, + .boot = sst_byt_boot, + .write = sst_shim32_write, + .read = sst_shim32_read, + .write64 = sst_shim32_write64, + .read64 = sst_shim32_read64, + .ram_read = sst_memcpy_fromio_32, + .ram_write = sst_memcpy_toio_32, + .irq_handler = sst_byt_irq, + .init = sst_byt_init, + .free = sst_byt_free, + .parse_fw = sst_byt_parse_fw_image, +}; diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c new file mode 100644 index 000000000000..d0eaeee21be4 --- /dev/null +++ b/sound/soc/intel/sst-baytrail-ipc.c @@ -0,0 +1,867 @@ +/* + * Intel Baytrail SST IPC Support + * Copyright (c) 2014, Intel Corporation. + * + * 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. + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/device.h> +#include <linux/wait.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/list.h> +#include <linux/platform_device.h> +#include <linux/kthread.h> +#include <linux/firmware.h> +#include <linux/io.h> +#include <asm/div64.h> + +#include "sst-baytrail-ipc.h" +#include "sst-dsp.h" +#include "sst-dsp-priv.h" + +/* IPC message timeout */ +#define IPC_TIMEOUT_MSECS 300 +#define IPC_BOOT_MSECS 200 + +#define IPC_EMPTY_LIST_SIZE 8 + +/* IPC header bits */ +#define IPC_HEADER_MSG_ID_MASK 0xff +#define IPC_HEADER_MSG_ID(x) ((x) & IPC_HEADER_MSG_ID_MASK) +#define IPC_HEADER_STR_ID_SHIFT 8 +#define IPC_HEADER_STR_ID_MASK 0x1f +#define IPC_HEADER_STR_ID(x) (((x) & 0x1f) << IPC_HEADER_STR_ID_SHIFT) +#define IPC_HEADER_LARGE_SHIFT 13 +#define IPC_HEADER_LARGE(x) (((x) & 0x1) << IPC_HEADER_LARGE_SHIFT) +#define IPC_HEADER_DATA_SHIFT 16 +#define IPC_HEADER_DATA_MASK 0x3fff +#define IPC_HEADER_DATA(x) (((x) & 0x3fff) << IPC_HEADER_DATA_SHIFT) + +/* mask for differentiating between notification and reply message */ +#define IPC_NOTIFICATION (0x1 << 7) + +/* I2L Stream config/control msgs */ +#define IPC_IA_ALLOC_STREAM 0x20 +#define IPC_IA_FREE_STREAM 0x21 +#define IPC_IA_PAUSE_STREAM 0x24 +#define IPC_IA_RESUME_STREAM 0x25 +#define IPC_IA_DROP_STREAM 0x26 +#define IPC_IA_START_STREAM 0x30 + +/* notification messages */ +#define IPC_IA_FW_INIT_CMPLT 0x81 +#define IPC_SST_PERIOD_ELAPSED 0x97 + +/* IPC messages between host and ADSP */ +struct sst_byt_address_info { + u32 addr; + u32 size; +} __packed; + +struct sst_byt_str_type { + u8 codec_type; + u8 str_type; + u8 operation; + u8 protected_str; + u8 time_slots; + u8 reserved; + u16 result; +} __packed; + +struct sst_byt_pcm_params { + u8 num_chan; + u8 pcm_wd_sz; + u8 use_offload_path; + u8 reserved; + u32 sfreq; + u8 channel_map[8]; +} __packed; + +struct sst_byt_frames_info { + u16 num_entries; + u16 rsrvd; + u32 frag_size; + struct sst_byt_address_info ring_buf_info[8]; +} __packed; + +struct sst_byt_alloc_params { + struct sst_byt_str_type str_type; + struct sst_byt_pcm_params pcm_params; + struct sst_byt_frames_info frame_info; +} __packed; + +struct sst_byt_alloc_response { + struct sst_byt_str_type str_type; + u8 reserved[88]; +} __packed; + +struct sst_byt_start_stream_params { + u32 byte_offset; +} __packed; + +struct sst_byt_tstamp { + u64 ring_buffer_counter; + u64 hardware_counter; + u64 frames_decoded; + u64 bytes_decoded; + u64 bytes_copied; + u32 sampling_frequency; + u32 channel_peak[8]; +} __packed; + +/* driver internal IPC message structure */ +struct ipc_message { + struct list_head list; + u64 header; + + /* direction wrt host CPU */ + char tx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE]; + size_t tx_size; + char rx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE]; + size_t rx_size; + + wait_queue_head_t waitq; + bool complete; + bool wait; + int errno; +}; + +struct sst_byt_stream; +struct sst_byt; + +/* stream infomation */ +struct sst_byt_stream { + struct list_head node; + + /* configuration */ + struct sst_byt_alloc_params request; + struct sst_byt_alloc_response reply; + + /* runtime info */ + struct sst_byt *byt; + int str_id; + bool commited; + bool running; + + /* driver callback */ + u32 (*notify_position)(struct sst_byt_stream *stream, void *data); + void *pdata; +}; + +/* SST Baytrail IPC data */ +struct sst_byt { + struct device *dev; + struct sst_dsp *dsp; + + /* stream */ + struct list_head stream_list; + + /* boot */ + wait_queue_head_t boot_wait; + bool boot_complete; + + /* IPC messaging */ + struct list_head tx_list; + struct list_head rx_list; + struct list_head empty_list; + wait_queue_head_t wait_txq; + struct task_struct *tx_thread; + struct kthread_worker kworker; + struct kthread_work kwork; + struct ipc_message *msg; +}; + +static inline u64 sst_byt_header(int msg_id, int data, bool large, int str_id) +{ + u64 header; + + header = IPC_HEADER_MSG_ID(msg_id) | + IPC_HEADER_STR_ID(str_id) | + IPC_HEADER_LARGE(large) | + IPC_HEADER_DATA(data) | + SST_BYT_IPCX_BUSY; + + return header; +} + +static inline u16 sst_byt_header_msg_id(u64 header) +{ + return header & IPC_HEADER_MSG_ID_MASK; +} + +static inline u8 sst_byt_header_str_id(u64 header) +{ + return (header >> IPC_HEADER_STR_ID_SHIFT) & IPC_HEADER_STR_ID_MASK; +} + +static inline u16 sst_byt_header_data(u64 header) +{ + return (header >> IPC_HEADER_DATA_SHIFT) & IPC_HEADER_DATA_MASK; +} + +static struct sst_byt_stream *sst_byt_get_stream(struct sst_byt *byt, + int stream_id) +{ + struct sst_byt_stream *stream; + + list_for_each_entry(stream, &byt->stream_list, node) { + if (stream->str_id == stream_id) + return stream; + } + + return NULL; +} + +static void sst_byt_ipc_shim_dbg(struct sst_byt *byt, const char *text) +{ + struct sst_dsp *sst = byt->dsp; + u64 isr, ipcd, imrx, ipcx; + + ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX); + isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX); + ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); + imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX); + + dev_err(byt->dev, + "ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n", + text, ipcx, isr, ipcd, imrx); +} + +/* locks held by caller */ +static struct ipc_message *sst_byt_msg_get_empty(struct sst_byt *byt) +{ + struct ipc_message *msg = NULL; + + if (!list_empty(&byt->empty_list)) { + msg = list_first_entry(&byt->empty_list, + struct ipc_message, list); + list_del(&msg->list); + } + + return msg; +} + +static void sst_byt_ipc_tx_msgs(struct kthread_work *work) +{ + struct sst_byt *byt = + container_of(work, struct sst_byt, kwork); + struct ipc_message *msg; + u64 ipcx; + unsigned long flags; + + spin_lock_irqsave(&byt->dsp->spinlock, flags); + if (list_empty(&byt->tx_list)) { + spin_unlock_irqrestore(&byt->dsp->spinlock, flags); + return; + } + + /* if the DSP is busy we will TX messages after IRQ */ + ipcx = sst_dsp_shim_read64_unlocked(byt->dsp, SST_IPCX); + if (ipcx & SST_BYT_IPCX_BUSY) { + spin_unlock_irqrestore(&byt->dsp->spinlock, flags); + return; + } + + msg = list_first_entry(&byt->tx_list, struct ipc_message, list); + + list_move(&msg->list, &byt->rx_list); + + /* send the message */ + if (msg->header & IPC_HEADER_LARGE(true)) + sst_dsp_outbox_write(byt->dsp, msg->tx_data, msg->tx_size); + sst_dsp_shim_write64_unlocked(byt->dsp, SST_IPCX, msg->header); + + spin_unlock_irqrestore(&byt->dsp->spinlock, flags); +} + +static inline void sst_byt_tx_msg_reply_complete(struct sst_byt *byt, + struct ipc_message *msg) +{ + msg->complete = true; + + if (!msg->wait) + list_add_tail(&msg->list, &byt->empty_list); + else + wake_up(&msg->waitq); +} + +static int sst_byt_tx_wait_done(struct sst_byt *byt, struct ipc_message *msg, + void *rx_data) +{ + unsigned long flags; + int ret; + + /* wait for DSP completion */ + ret = wait_event_timeout(msg->waitq, msg->complete, + msecs_to_jiffies(IPC_TIMEOUT_MSECS)); + + spin_lock_irqsave(&byt->dsp->spinlock, flags); + if (ret == 0) { + list_del(&msg->list); + sst_byt_ipc_shim_dbg(byt, "message timeout"); + + ret = -ETIMEDOUT; + } else { + + /* copy the data returned from DSP */ + if (msg->rx_size) + memcpy(rx_data, msg->rx_data, msg->rx_size); + ret = msg->errno; + } + + list_add_tail(&msg->list, &byt->empty_list); + spin_unlock_irqrestore(&byt->dsp->spinlock, flags); + return ret; +} + +static int sst_byt_ipc_tx_message(struct sst_byt *byt, u64 header, + void *tx_data, size_t tx_bytes, + void *rx_data, size_t rx_bytes, int wait) +{ + unsigned long flags; + struct ipc_message *msg; + + spin_lock_irqsave(&byt->dsp->spinlock, flags); + + msg = sst_byt_msg_get_empty(byt); + if (msg == NULL) { + spin_unlock_irqrestore(&byt->dsp->spinlock, flags); + return -EBUSY; + } + + msg->header = header; + msg->tx_size = tx_bytes; + msg->rx_size = rx_bytes; + msg->wait = wait; + msg->errno = 0; + msg->complete = false; + + if (tx_bytes) { + /* msg content = lower 32-bit of the header + data */ + *(u32 *)msg->tx_data = (u32)(header & (u32)-1); + memcpy(msg->tx_data + sizeof(u32), tx_data, tx_bytes); + msg->tx_size += sizeof(u32); + } + + list_add_tail(&msg->list, &byt->tx_list); + spin_unlock_irqrestore(&byt->dsp->spinlock, flags); + + queue_kthread_work(&byt->kworker, &byt->kwork); + + if (wait) + return sst_byt_tx_wait_done(byt, msg, rx_data); + else + return 0; +} + +static inline int sst_byt_ipc_tx_msg_wait(struct sst_byt *byt, u64 header, + void *tx_data, size_t tx_bytes, + void *rx_data, size_t rx_bytes) +{ + return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes, + rx_data, rx_bytes, 1); +} + +static inline int sst_byt_ipc_tx_msg_nowait(struct sst_byt *byt, u64 header, + void *tx_data, size_t tx_bytes) +{ + return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes, + NULL, 0, 0); +} + +static struct ipc_message *sst_byt_reply_find_msg(struct sst_byt *byt, + u64 header) +{ + struct ipc_message *msg = NULL, *_msg; + u64 mask; + + /* match reply to message sent based on msg and stream IDs */ + mask = IPC_HEADER_MSG_ID_MASK | + IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT; + header &= mask; + + if (list_empty(&byt->rx_list)) { + dev_err(byt->dev, + "ipc: rx list is empty but received 0x%llx\n", header); + goto out; + } + + list_for_each_entry(_msg, &byt->rx_list, list) { + if ((_msg->header & mask) == header) { + msg = _msg; + break; + } + } + +out: + return msg; +} + +static void sst_byt_stream_update(struct sst_byt *byt, struct ipc_message *msg) +{ + struct sst_byt_stream *stream; + u64 header = msg->header; + u8 stream_id = sst_byt_header_str_id(header); + u8 stream_msg = sst_byt_header_msg_id(header); + + stream = sst_byt_get_stream(byt, stream_id); + if (stream == NULL) + return; + + switch (stream_msg) { + case IPC_IA_DROP_STREAM: + case IPC_IA_PAUSE_STREAM: + case IPC_IA_FREE_STREAM: + stream->running = false; + break; + case IPC_IA_START_STREAM: + case IPC_IA_RESUME_STREAM: + stream->running = true; + break; + } +} + +static int sst_byt_process_reply(struct sst_byt *byt, u64 header) +{ + struct ipc_message *msg; + + msg = sst_byt_reply_find_msg(byt, header); + if (msg == NULL) + return 1; + + if (header & IPC_HEADER_LARGE(true)) { + msg->rx_size = sst_byt_header_data(header); + sst_dsp_inbox_read(byt->dsp, msg->rx_data, msg->rx_size); + } + + /* update any stream states */ + sst_byt_stream_update(byt, msg); + + list_del(&msg->list); + /* wake up */ + sst_byt_tx_msg_reply_complete(byt, msg); + + return 1; +} + +static void sst_byt_fw_ready(struct sst_byt *byt, u64 header) +{ + dev_dbg(byt->dev, "ipc: DSP is ready 0x%llX\n", header); + + byt->boot_complete = true; + wake_up(&byt->boot_wait); +} + +static int sst_byt_process_notification(struct sst_byt *byt, + unsigned long *flags) +{ + struct sst_dsp *sst = byt->dsp; + struct sst_byt_stream *stream; + u64 header; + u8 msg_id, stream_id; + int handled = 1; + + header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); + msg_id = sst_byt_header_msg_id(header); + + switch (msg_id) { + case IPC_SST_PERIOD_ELAPSED: + stream_id = sst_byt_header_str_id(header); + stream = sst_byt_get_stream(byt, stream_id); + if (stream && stream->running && stream->notify_position) { + spin_unlock_irqrestore(&sst->spinlock, *flags); + stream->notify_position(stream, stream->pdata); + spin_lock_irqsave(&sst->spinlock, *flags); + } + break; + case IPC_IA_FW_INIT_CMPLT: + sst_byt_fw_ready(byt, header); + break; + } + + return handled; +} + +static irqreturn_t sst_byt_irq_thread(int irq, void *context) +{ + struct sst_dsp *sst = (struct sst_dsp *) context; + struct sst_byt *byt = sst_dsp_get_thread_context(sst); + u64 header; + unsigned long flags; + + spin_lock_irqsave(&sst->spinlock, flags); + + header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); + if (header & SST_BYT_IPCD_BUSY) { + if (header & IPC_NOTIFICATION) { + /* message from ADSP */ + sst_byt_process_notification(byt, &flags); + } else { + /* reply from ADSP */ + sst_byt_process_reply(byt, header); + } + /* + * clear IPCD BUSY bit and set DONE bit. Tell DSP we have + * processed the message and can accept new. Clear data part + * of the header + */ + sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCD, + SST_BYT_IPCD_DONE | SST_BYT_IPCD_BUSY | + IPC_HEADER_DATA(IPC_HEADER_DATA_MASK), + SST_BYT_IPCD_DONE); + /* unmask message request interrupts */ + sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX, + SST_BYT_IMRX_REQUEST, 0); + } + + spin_unlock_irqrestore(&sst->spinlock, flags); + + /* continue to send any remaining messages... */ + queue_kthread_work(&byt->kworker, &byt->kwork); + + return IRQ_HANDLED; +} + +/* stream API */ +struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id, + u32 (*notify_position)(struct sst_byt_stream *stream, void *data), + void *data) +{ + struct sst_byt_stream *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (stream == NULL) + return NULL; + + list_add(&stream->node, &byt->stream_list); + stream->notify_position = notify_position; + stream->pdata = data; + stream->byt = byt; + stream->str_id = id; + + return stream; +} + +int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream, + int bits) +{ + stream->request.pcm_params.pcm_wd_sz = bits; + return 0; +} + +int sst_byt_stream_set_channels(struct sst_byt *byt, + struct sst_byt_stream *stream, u8 channels) +{ + stream->request.pcm_params.num_chan = channels; + return 0; +} + +int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream, + unsigned int rate) +{ + stream->request.pcm_params.sfreq = rate; + return 0; +} + +/* stream sonfiguration */ +int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream, + int codec_type, int stream_type, int operation) +{ + stream->request.str_type.codec_type = codec_type; + stream->request.str_type.str_type = stream_type; + stream->request.str_type.operation = operation; + stream->request.str_type.time_slots = 0xc; + + return 0; +} + +int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream, + uint32_t buffer_addr, uint32_t buffer_size) +{ + stream->request.frame_info.num_entries = 1; + stream->request.frame_info.ring_buf_info[0].addr = buffer_addr; + stream->request.frame_info.ring_buf_info[0].size = buffer_size; + /* calculate bytes per 4 ms fragment */ + stream->request.frame_info.frag_size = + stream->request.pcm_params.sfreq * + stream->request.pcm_params.num_chan * + stream->request.pcm_params.pcm_wd_sz / 8 * + 4 / 1000; + return 0; +} + +int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream) +{ + struct sst_byt_alloc_params *str_req = &stream->request; + struct sst_byt_alloc_response *reply = &stream->reply; + u64 header; + int ret; + + header = sst_byt_header(IPC_IA_ALLOC_STREAM, + sizeof(*str_req) + sizeof(u32), + true, stream->str_id); + ret = sst_byt_ipc_tx_msg_wait(byt, header, str_req, sizeof(*str_req), + reply, sizeof(*reply)); + if (ret < 0) { + dev_err(byt->dev, "ipc: error stream commit failed\n"); + return ret; + } + + stream->commited = true; + + return 0; +} + +int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream) +{ + u64 header; + int ret = 0; + + if (!stream->commited) + goto out; + + header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id); + ret = sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0); + if (ret < 0) { + dev_err(byt->dev, "ipc: free stream %d failed\n", + stream->str_id); + return -EAGAIN; + } + + stream->commited = false; +out: + list_del(&stream->node); + kfree(stream); + + return ret; +} + +static int sst_byt_stream_operations(struct sst_byt *byt, int type, + int stream_id, int wait) +{ + struct sst_byt_start_stream_params start_stream; + u64 header; + void *tx_msg = NULL; + size_t size = 0; + + if (type != IPC_IA_START_STREAM) { + header = sst_byt_header(type, 0, false, stream_id); + } else { + start_stream.byte_offset = 0; + header = sst_byt_header(IPC_IA_START_STREAM, + sizeof(start_stream) + sizeof(u32), + true, stream_id); + tx_msg = &start_stream; + size = sizeof(start_stream); + } + + if (wait) + return sst_byt_ipc_tx_msg_wait(byt, header, + tx_msg, size, NULL, 0); + else + return sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size); +} + +/* stream ALSA trigger operations */ +int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream) +{ + int ret; + + ret = sst_byt_stream_operations(byt, IPC_IA_START_STREAM, + stream->str_id, 0); + if (ret < 0) + dev_err(byt->dev, "ipc: error failed to start stream %d\n", + stream->str_id); + + return ret; +} + +int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream) +{ + int ret; + + /* don't stop streams that are not commited */ + if (!stream->commited) + return 0; + + ret = sst_byt_stream_operations(byt, IPC_IA_DROP_STREAM, + stream->str_id, 0); + if (ret < 0) + dev_err(byt->dev, "ipc: error failed to stop stream %d\n", + stream->str_id); + return ret; +} + +int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream) +{ + int ret; + + ret = sst_byt_stream_operations(byt, IPC_IA_PAUSE_STREAM, + stream->str_id, 0); + if (ret < 0) + dev_err(byt->dev, "ipc: error failed to pause stream %d\n", + stream->str_id); + + return ret; +} + +int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream) +{ + int ret; + + ret = sst_byt_stream_operations(byt, IPC_IA_RESUME_STREAM, + stream->str_id, 0); + if (ret < 0) + dev_err(byt->dev, "ipc: error failed to resume stream %d\n", + stream->str_id); + + return ret; +} + +int sst_byt_get_dsp_position(struct sst_byt *byt, + struct sst_byt_stream *stream, int buffer_size) +{ + struct sst_dsp *sst = byt->dsp; + struct sst_byt_tstamp fw_tstamp; + u8 str_id = stream->str_id; + u32 tstamp_offset; + + tstamp_offset = SST_BYT_TIMESTAMP_OFFSET + str_id * sizeof(fw_tstamp); + memcpy_fromio(&fw_tstamp, + sst->addr.lpe + tstamp_offset, sizeof(fw_tstamp)); + + return do_div(fw_tstamp.ring_buffer_counter, buffer_size); +} + +static int msg_empty_list_init(struct sst_byt *byt) +{ + struct ipc_message *msg; + int i; + + byt->msg = kzalloc(sizeof(*msg) * IPC_EMPTY_LIST_SIZE, GFP_KERNEL); + if (byt->msg == NULL) + return -ENOMEM; + + for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + init_waitqueue_head(&byt->msg[i].waitq); + list_add(&byt->msg[i].list, &byt->empty_list); + } + + return 0; +} + +struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt) +{ + return byt->dsp; +} + +static struct sst_dsp_device byt_dev = { + .thread = sst_byt_irq_thread, + .ops = &sst_byt_ops, +}; + +int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) +{ + struct sst_byt *byt; + struct sst_fw *byt_sst_fw; + int err; + + dev_dbg(dev, "initialising Byt DSP IPC\n"); + + byt = devm_kzalloc(dev, sizeof(*byt), GFP_KERNEL); + if (byt == NULL) + return -ENOMEM; + + byt->dev = dev; + INIT_LIST_HEAD(&byt->stream_list); + INIT_LIST_HEAD(&byt->tx_list); + INIT_LIST_HEAD(&byt->rx_list); + INIT_LIST_HEAD(&byt->empty_list); + init_waitqueue_head(&byt->boot_wait); + init_waitqueue_head(&byt->wait_txq); + + err = msg_empty_list_init(byt); + if (err < 0) + return -ENOMEM; + + /* start the IPC message thread */ + init_kthread_worker(&byt->kworker); + byt->tx_thread = kthread_run(kthread_worker_fn, + &byt->kworker, + dev_name(byt->dev)); + if (IS_ERR(byt->tx_thread)) { + err = PTR_ERR(byt->tx_thread); + dev_err(byt->dev, "error failed to create message TX task\n"); + goto err_free_msg; + } + init_kthread_work(&byt->kwork, sst_byt_ipc_tx_msgs); + + byt_dev.thread_context = byt; + + /* init SST shim */ + byt->dsp = sst_dsp_new(dev, &byt_dev, pdata); + if (byt->dsp == NULL) { + err = -ENODEV; + goto err_free_msg; + } + + /* keep the DSP in reset state for base FW loading */ + sst_dsp_reset(byt->dsp); + + byt_sst_fw = sst_fw_new(byt->dsp, pdata->fw, byt); + if (byt_sst_fw == NULL) { + err = -ENODEV; + dev_err(dev, "error: failed to load firmware\n"); + goto fw_err; + } + + /* wait for DSP boot completion */ + sst_dsp_boot(byt->dsp); + err = wait_event_timeout(byt->boot_wait, byt->boot_complete, + msecs_to_jiffies(IPC_BOOT_MSECS)); + if (err == 0) { + err = -EIO; + dev_err(byt->dev, "ipc: error DSP boot timeout\n"); + goto boot_err; + } + + pdata->dsp = byt; + + return 0; + +boot_err: + sst_dsp_reset(byt->dsp); + sst_fw_free(byt_sst_fw); +fw_err: + sst_dsp_free(byt->dsp); +err_free_msg: + kfree(byt->msg); + + return err; +} +EXPORT_SYMBOL_GPL(sst_byt_dsp_init); + +void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata) +{ + struct sst_byt *byt = pdata->dsp; + + sst_dsp_reset(byt->dsp); + sst_fw_free_all(byt->dsp); + sst_dsp_free(byt->dsp); + kfree(byt->msg); +} +EXPORT_SYMBOL_GPL(sst_byt_dsp_free); diff --git a/sound/soc/intel/sst-baytrail-ipc.h b/sound/soc/intel/sst-baytrail-ipc.h new file mode 100644 index 000000000000..f172b6440fa9 --- /dev/null +++ b/sound/soc/intel/sst-baytrail-ipc.h @@ -0,0 +1,69 @@ +/* + * Intel Baytrail SST IPC Support + * Copyright (c) 2014, Intel Corporation. + * + * 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. + */ + +#ifndef __SST_BYT_IPC_H +#define __SST_BYT_IPC_H + +#include <linux/types.h> + +struct sst_byt; +struct sst_byt_stream; +struct sst_pdata; +extern struct sst_ops sst_byt_ops; + + +#define SST_BYT_MAILBOX_OFFSET 0x144000 +#define SST_BYT_TIMESTAMP_OFFSET (SST_BYT_MAILBOX_OFFSET + 0x800) + +/** + * Upfront defined maximum message size that is + * expected by the in/out communication pipes in FW. + */ +#define SST_BYT_IPC_MAX_PAYLOAD_SIZE 200 + +/* stream API */ +struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id, + uint32_t (*get_write_position)(struct sst_byt_stream *stream, + void *data), + void *data); + +/* stream configuration */ +int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream, + int bits); +int sst_byt_stream_set_channels(struct sst_byt *byt, + struct sst_byt_stream *stream, u8 channels); +int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream, + unsigned int rate); +int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream, + int codec_type, int stream_type, int operation); +int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream, + uint32_t buffer_addr, uint32_t buffer_size); +int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream); +int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream); + +/* stream ALSA trigger operations */ +int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream); +int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream); +int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream); +int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream); + +int sst_byt_get_dsp_position(struct sst_byt *byt, + struct sst_byt_stream *stream, int buffer_size); + +/* init */ +int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata); +void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata); +struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt); + +#endif diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c new file mode 100644 index 000000000000..6d101f3813b4 --- /dev/null +++ b/sound/soc/intel/sst-baytrail-pcm.c @@ -0,0 +1,422 @@ +/* + * Intel Baytrail SST PCM Support + * Copyright (c) 2014, Intel Corporation. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "sst-baytrail-ipc.h" +#include "sst-dsp-priv.h" +#include "sst-dsp.h" + +#define BYT_PCM_COUNT 2 + +static const struct snd_pcm_hardware sst_byt_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FORMAT_S24_LE, + .period_bytes_min = 384, + .period_bytes_max = 48000, + .periods_min = 2, + .periods_max = 250, + .buffer_bytes_max = 96000, +}; + +/* private data for each PCM DSP stream */ +struct sst_byt_pcm_data { + struct sst_byt_stream *stream; + struct snd_pcm_substream *substream; + struct mutex mutex; +}; + +/* private data for the driver */ +struct sst_byt_priv_data { + /* runtime DSP */ + struct sst_byt *byt; + + /* DAI data */ + struct sst_byt_pcm_data pcm[BYT_PCM_COUNT]; +}; + +/* this may get called several times by oss emulation */ +static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sst_byt_priv_data *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct sst_byt *byt = pdata->byt; + u32 rate, bits; + u8 channels; + int ret, playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + + dev_dbg(rtd->dev, "PCM: hw_params, pcm_data %p\n", pcm_data); + + ret = sst_byt_stream_type(byt, pcm_data->stream, + 1, 1, !playback); + if (ret < 0) { + dev_err(rtd->dev, "failed to set stream format %d\n", ret); + return ret; + } + + rate = params_rate(params); + ret = sst_byt_stream_set_rate(byt, pcm_data->stream, rate); + if (ret < 0) { + dev_err(rtd->dev, "could not set rate %d\n", rate); + return ret; + } + + bits = snd_pcm_format_width(params_format(params)); + ret = sst_byt_stream_set_bits(byt, pcm_data->stream, bits); + if (ret < 0) { + dev_err(rtd->dev, "could not set formats %d\n", + params_rate(params)); + return ret; + } + + channels = (u8)(params_channels(params) & 0xF); + ret = sst_byt_stream_set_channels(byt, pcm_data->stream, channels); + if (ret < 0) { + dev_err(rtd->dev, "could not set channels %d\n", + params_rate(params)); + return ret; + } + + snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + + ret = sst_byt_stream_buffer(byt, pcm_data->stream, + substream->dma_buffer.addr, + params_buffer_bytes(params)); + if (ret < 0) { + dev_err(rtd->dev, "PCM: failed to set DMA buffer %d\n", ret); + return ret; + } + + ret = sst_byt_stream_commit(byt, pcm_data->stream); + if (ret < 0) { + dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret); + return ret; + } + + return 0; +} + +static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + dev_dbg(rtd->dev, "PCM: hw_free\n"); + snd_pcm_lib_free_pages(substream); + + return 0; +} + +static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sst_byt_priv_data *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct sst_byt *byt = pdata->byt; + + dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + sst_byt_stream_start(byt, pcm_data->stream); + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + sst_byt_stream_resume(byt, pcm_data->stream); + break; + case SNDRV_PCM_TRIGGER_STOP: + sst_byt_stream_stop(byt, pcm_data->stream); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + sst_byt_stream_pause(byt, pcm_data->stream); + break; + default: + break; + } + + return 0; +} + +static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data) +{ + struct sst_byt_pcm_data *pcm_data = data; + struct snd_pcm_substream *substream = pcm_data->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + u32 pos; + + pos = frames_to_bytes(runtime, + (runtime->control->appl_ptr % + runtime->buffer_size)); + + dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos); + + snd_pcm_period_elapsed(substream); + return pos; +} + +static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct sst_byt_priv_data *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct sst_byt *byt = pdata->byt; + snd_pcm_uframes_t offset; + int pos; + + pos = sst_byt_get_dsp_position(byt, pcm_data->stream, + snd_pcm_lib_buffer_bytes(substream)); + offset = bytes_to_frames(runtime, pos); + + dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n", + frames_to_bytes(runtime, (u32)offset)); + return offset; +} + +static int sst_byt_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sst_byt_priv_data *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct sst_byt *byt = pdata->byt; + + dev_dbg(rtd->dev, "PCM: open\n"); + + pcm_data = &pdata->pcm[rtd->cpu_dai->id]; + mutex_lock(&pcm_data->mutex); + + snd_soc_pcm_set_drvdata(rtd, pcm_data); + pcm_data->substream = substream; + + snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware); + + pcm_data->stream = sst_byt_stream_new(byt, rtd->cpu_dai->id + 1, + byt_notify_pointer, pcm_data); + if (pcm_data->stream == NULL) { + dev_err(rtd->dev, "failed to create stream\n"); + mutex_unlock(&pcm_data->mutex); + return -EINVAL; + } + + mutex_unlock(&pcm_data->mutex); + return 0; +} + +static int sst_byt_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sst_byt_priv_data *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct sst_byt *byt = pdata->byt; + int ret; + + dev_dbg(rtd->dev, "PCM: close\n"); + + mutex_lock(&pcm_data->mutex); + ret = sst_byt_stream_free(byt, pcm_data->stream); + if (ret < 0) { + dev_dbg(rtd->dev, "Free stream fail\n"); + goto out; + } + pcm_data->stream = NULL; + +out: + mutex_unlock(&pcm_data->mutex); + return ret; +} + +static int sst_byt_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + dev_dbg(rtd->dev, "PCM: mmap\n"); + return snd_pcm_lib_default_mmap(substream, vma); +} + +static struct snd_pcm_ops sst_byt_pcm_ops = { + .open = sst_byt_pcm_open, + .close = sst_byt_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = sst_byt_pcm_hw_params, + .hw_free = sst_byt_pcm_hw_free, + .trigger = sst_byt_pcm_trigger, + .pointer = sst_byt_pcm_pointer, + .mmap = sst_byt_pcm_mmap, +}; + +static void sst_byt_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + size_t size; + int ret = 0; + + ret = dma_coerce_mask_and_coherent(rtd->card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || + pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + size = sst_byt_pcm_hardware.buffer_bytes_max; + ret = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV, + rtd->card->dev, + size, size); + if (ret) { + dev_err(rtd->dev, "dma buffer allocation failed %d\n", + ret); + return ret; + } + } + + return ret; +} + +static struct snd_soc_dai_driver byt_dais[] = { + { + .name = "Front-cpu-dai", + .playback = { + .stream_name = "System Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S16_LE, + }, + }, + { + .name = "Mic1-cpu-dai", + .capture = { + .stream_name = "Analog Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, +}; + +static int sst_byt_pcm_probe(struct snd_soc_platform *platform) +{ + struct sst_pdata *plat_data = dev_get_platdata(platform->dev); + struct sst_byt_priv_data *priv_data; + int i; + + if (!plat_data) + return -ENODEV; + + priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data), + GFP_KERNEL); + priv_data->byt = plat_data->dsp; + snd_soc_platform_set_drvdata(platform, priv_data); + + for (i = 0; i < ARRAY_SIZE(byt_dais); i++) + mutex_init(&priv_data->pcm[i].mutex); + + return 0; +} + +static int sst_byt_pcm_remove(struct snd_soc_platform *platform) +{ + return 0; +} + +static struct snd_soc_platform_driver byt_soc_platform = { + .probe = sst_byt_pcm_probe, + .remove = sst_byt_pcm_remove, + .ops = &sst_byt_pcm_ops, + .pcm_new = sst_byt_pcm_new, + .pcm_free = sst_byt_pcm_free, +}; + +static const struct snd_soc_component_driver byt_dai_component = { + .name = "byt-dai", +}; + +static int sst_byt_pcm_dev_probe(struct platform_device *pdev) +{ + struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); + int ret; + + ret = sst_byt_dsp_init(&pdev->dev, sst_pdata); + if (ret < 0) + return -ENODEV; + + ret = snd_soc_register_platform(&pdev->dev, &byt_soc_platform); + if (ret < 0) + goto err_plat; + + ret = snd_soc_register_component(&pdev->dev, &byt_dai_component, + byt_dais, ARRAY_SIZE(byt_dais)); + if (ret < 0) + goto err_comp; + + return 0; + +err_comp: + snd_soc_unregister_platform(&pdev->dev); +err_plat: + sst_byt_dsp_free(&pdev->dev, sst_pdata); + return ret; +} + +static int sst_byt_pcm_dev_remove(struct platform_device *pdev) +{ + struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); + + snd_soc_unregister_platform(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + sst_byt_dsp_free(&pdev->dev, sst_pdata); + + return 0; +} + +static struct platform_driver sst_byt_pcm_driver = { + .driver = { + .name = "baytrail-pcm-audio", + .owner = THIS_MODULE, + }, + + .probe = sst_byt_pcm_dev_probe, + .remove = sst_byt_pcm_dev_remove, +}; +module_platform_driver(sst_byt_pcm_driver); + +MODULE_AUTHOR("Jarkko Nikula"); +MODULE_DESCRIPTION("Baytrail PCM"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:baytrail-pcm-audio"); diff --git a/sound/soc/intel/sst-dsp-priv.h b/sound/soc/intel/sst-dsp-priv.h new file mode 100644 index 000000000000..fe8e81aad646 --- /dev/null +++ b/sound/soc/intel/sst-dsp-priv.h @@ -0,0 +1,309 @@ +/* + * Intel Smart Sound Technology + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License 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. + * + */ + +#ifndef __SOUND_SOC_SST_DSP_PRIV_H +#define __SOUND_SOC_SST_DSP_PRIV_H + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/firmware.h> + +struct sst_mem_block; +struct sst_module; +struct sst_fw; + +/* + * DSP Operations exported by platform Audio DSP driver. + */ +struct sst_ops { + /* DSP core boot / reset */ + void (*boot)(struct sst_dsp *); + void (*reset)(struct sst_dsp *); + + /* Shim IO */ + void (*write)(void __iomem *addr, u32 offset, u32 value); + u32 (*read)(void __iomem *addr, u32 offset); + void (*write64)(void __iomem *addr, u32 offset, u64 value); + u64 (*read64)(void __iomem *addr, u32 offset); + + /* DSP I/DRAM IO */ + void (*ram_read)(struct sst_dsp *sst, void *dest, void __iomem *src, + size_t bytes); + void (*ram_write)(struct sst_dsp *sst, void __iomem *dest, void *src, + size_t bytes); + + void (*dump)(struct sst_dsp *); + + /* IRQ handlers */ + irqreturn_t (*irq_handler)(int irq, void *context); + + /* SST init and free */ + int (*init)(struct sst_dsp *sst, struct sst_pdata *pdata); + void (*free)(struct sst_dsp *sst); + + /* FW module parser/loader */ + int (*parse_fw)(struct sst_fw *sst_fw); +}; + +/* + * Audio DSP memory offsets and addresses. + */ +struct sst_addr { + u32 lpe_base; + u32 shim_offset; + u32 iram_offset; + u32 dram_offset; + void __iomem *lpe; + void __iomem *shim; + void __iomem *pci_cfg; + void __iomem *fw_ext; +}; + +/* + * Audio DSP Mailbox configuration. + */ +struct sst_mailbox { + void __iomem *in_base; + void __iomem *out_base; + size_t in_size; + size_t out_size; +}; + +/* + * Audio DSP Firmware data types. + */ +enum sst_data_type { + SST_DATA_M = 0, /* module block data */ + SST_DATA_P = 1, /* peristant data (text, data) */ + SST_DATA_S = 2, /* scratch data (usually buffers) */ +}; + +/* + * Audio DSP memory block types. + */ +enum sst_mem_type { + SST_MEM_IRAM = 0, + SST_MEM_DRAM = 1, + SST_MEM_ANY = 2, + SST_MEM_CACHE= 3, +}; + +/* + * Audio DSP Generic Firmware File. + * + * SST Firmware files can consist of 1..N modules. This generic structure is + * used to manage each firmware file and it's modules regardless of SST firmware + * type. A SST driver may load multiple FW files. + */ +struct sst_fw { + struct sst_dsp *dsp; + + /* base addresses of FW file data */ + dma_addr_t dmable_fw_paddr; /* physical address of fw data */ + void *dma_buf; /* virtual address of fw data */ + u32 size; /* size of fw data */ + + /* lists */ + struct list_head list; /* DSP list of FW */ + struct list_head module_list; /* FW list of modules */ + + void *private; /* core doesn't touch this */ +}; + +/* + * Audio DSP Generic Module data. + * + * This is used to dsecribe any sections of persistent (text and data) and + * scratch (buffers) of module data in ADSP memory space. + */ +struct sst_module_data { + + enum sst_mem_type type; /* destination memory type */ + enum sst_data_type data_type; /* type of module data */ + + u32 size; /* size in bytes */ + u32 offset; /* offset in FW file */ + u32 data_offset; /* offset in ADSP memory space */ + void *data; /* module data */ +}; + +/* + * Audio DSP Generic Module Template. + * + * Used to define and register a new FW module. This data is extracted from + * FW module header information. + */ +struct sst_module_template { + u32 id; + u32 entry; /* entry point */ + struct sst_module_data s; /* scratch data */ + struct sst_module_data p; /* peristant data */ +}; + +/* + * Audio DSP Generic Module. + * + * Each Firmware file can consist of 1..N modules. A module can span multiple + * ADSP memory blocks. The simplest FW will be a file with 1 module. + */ +struct sst_module { + struct sst_dsp *dsp; + struct sst_fw *sst_fw; /* parent FW we belong too */ + + /* module configuration */ + u32 id; + u32 entry; /* module entry point */ + u32 offset; /* module offset in firmware file */ + u32 size; /* module size */ + struct sst_module_data s; /* scratch data */ + struct sst_module_data p; /* peristant data */ + + /* runtime */ + u32 usage_count; /* can be unloaded if count == 0 */ + void *private; /* core doesn't touch this */ + + /* lists */ + struct list_head block_list; /* Module list of blocks in use */ + struct list_head list; /* DSP list of modules */ + struct list_head list_fw; /* FW list of modules */ +}; + +/* + * SST Memory Block operations. + */ +struct sst_block_ops { + int (*enable)(struct sst_mem_block *block); + int (*disable)(struct sst_mem_block *block); +}; + +/* + * SST Generic Memory Block. + * + * SST ADP memory has multiple IRAM and DRAM blocks. Some ADSP blocks can be + * power gated. + */ +struct sst_mem_block { + struct sst_dsp *dsp; + struct sst_module *module; /* module that uses this block */ + + /* block config */ + u32 offset; /* offset from base */ + u32 size; /* block size */ + u32 index; /* block index 0..N */ + enum sst_mem_type type; /* block memory type IRAM/DRAM */ + struct sst_block_ops *ops; /* block operations, if any */ + + /* block status */ + enum sst_data_type data_type; /* data type held in this block */ + u32 bytes_used; /* bytes in use by modules */ + void *private; /* generic core does not touch this */ + int users; /* number of modules using this block */ + + /* block lists */ + struct list_head module_list; /* Module list of blocks */ + struct list_head list; /* Map list of free/used blocks */ +}; + +/* + * Generic SST Shim Interface. + */ +struct sst_dsp { + + /* runtime */ + struct sst_dsp_device *sst_dev; + spinlock_t spinlock; /* IPC locking */ + struct mutex mutex; /* DSP FW lock */ + struct device *dev; + void *thread_context; + int irq; + u32 id; + + /* list of free and used ADSP memory blocks */ + struct list_head used_block_list; + struct list_head free_block_list; + + /* operations */ + struct sst_ops *ops; + + /* debug FS */ + struct dentry *debugfs_root; + + /* base addresses */ + struct sst_addr addr; + + /* mailbox */ + struct sst_mailbox mailbox; + + /* SST FW files loaded and their modules */ + struct list_head module_list; + struct list_head fw_list; + + /* platform data */ + struct sst_pdata *pdata; + + /* DMA FW loading */ + struct sst_dma *dma; + bool fw_use_dma; +}; + +/* Size optimised DRAM/IRAM memcpy */ +static inline void sst_dsp_write(struct sst_dsp *sst, void *src, + u32 dest_offset, size_t bytes) +{ + sst->ops->ram_write(sst, sst->addr.lpe + dest_offset, src, bytes); +} + +static inline void sst_dsp_read(struct sst_dsp *sst, void *dest, + u32 src_offset, size_t bytes) +{ + sst->ops->ram_read(sst, dest, sst->addr.lpe + src_offset, bytes); +} + +static inline void *sst_dsp_get_thread_context(struct sst_dsp *sst) +{ + return sst->thread_context; +} + +/* Create/Free FW files - can contain multiple modules */ +struct sst_fw *sst_fw_new(struct sst_dsp *dsp, + const struct firmware *fw, void *private); +void sst_fw_free(struct sst_fw *sst_fw); +void sst_fw_free_all(struct sst_dsp *dsp); + +/* Create/Free firmware modules */ +struct sst_module *sst_module_new(struct sst_fw *sst_fw, + struct sst_module_template *template, void *private); +void sst_module_free(struct sst_module *sst_module); +int sst_module_insert(struct sst_module *sst_module); +int sst_module_remove(struct sst_module *sst_module); +int sst_module_insert_fixed_block(struct sst_module *module, + struct sst_module_data *data); +struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id); + +/* allocate/free pesistent/scratch memory regions managed by drv */ +struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp); +void sst_mem_block_free_scratch(struct sst_dsp *dsp, + struct sst_module *scratch); +int sst_block_module_remove(struct sst_module *module); + +/* Register the DSPs memory blocks - would be nice to read from ACPI */ +struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, + u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index, + void *private); +void sst_mem_block_unregister_all(struct sst_dsp *dsp); + +#endif diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/sst-dsp.c new file mode 100644 index 000000000000..0c129fd85ecf --- /dev/null +++ b/sound/soc/intel/sst-dsp.c @@ -0,0 +1,385 @@ +/* + * Intel Smart Sound Technology (SST) DSP Core Driver + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License 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/slab.h> +#include <linux/export.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +#include "sst-dsp.h" +#include "sst-dsp-priv.h" + +#define CREATE_TRACE_POINTS +#include <trace/events/intel-sst.h> + +/* Internal generic low-level SST IO functions - can be overidden */ +void sst_shim32_write(void __iomem *addr, u32 offset, u32 value) +{ + writel(value, addr + offset); +} +EXPORT_SYMBOL_GPL(sst_shim32_write); + +u32 sst_shim32_read(void __iomem *addr, u32 offset) +{ + return readl(addr + offset); +} +EXPORT_SYMBOL_GPL(sst_shim32_read); + +void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value) +{ + memcpy_toio(addr + offset, &value, sizeof(value)); +} +EXPORT_SYMBOL_GPL(sst_shim32_write64); + +u64 sst_shim32_read64(void __iomem *addr, u32 offset) +{ + u64 val; + + memcpy_fromio(&val, addr + offset, sizeof(val)); + return val; +} +EXPORT_SYMBOL_GPL(sst_shim32_read64); + +static inline void _sst_memcpy_toio_32(volatile u32 __iomem *dest, + u32 *src, size_t bytes) +{ + int i, words = bytes >> 2; + + for (i = 0; i < words; i++) + writel(src[i], dest + i); +} + +static inline void _sst_memcpy_fromio_32(u32 *dest, + const volatile __iomem u32 *src, size_t bytes) +{ + int i, words = bytes >> 2; + + for (i = 0; i < words; i++) + dest[i] = readl(src + i); +} + +void sst_memcpy_toio_32(struct sst_dsp *sst, + void __iomem *dest, void *src, size_t bytes) +{ + _sst_memcpy_toio_32(dest, src, bytes); +} +EXPORT_SYMBOL_GPL(sst_memcpy_toio_32); + +void sst_memcpy_fromio_32(struct sst_dsp *sst, void *dest, + void __iomem *src, size_t bytes) +{ + _sst_memcpy_fromio_32(dest, src, bytes); +} +EXPORT_SYMBOL_GPL(sst_memcpy_fromio_32); + +/* Public API */ +void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&sst->spinlock, flags); + sst->ops->write(sst->addr.shim, offset, value); + spin_unlock_irqrestore(&sst->spinlock, flags); +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_write); + +u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&sst->spinlock, flags); + val = sst->ops->read(sst->addr.shim, offset); + spin_unlock_irqrestore(&sst->spinlock, flags); + + return val; +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_read); + +void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value) +{ + unsigned long flags; + + spin_lock_irqsave(&sst->spinlock, flags); + sst->ops->write64(sst->addr.shim, offset, value); + spin_unlock_irqrestore(&sst->spinlock, flags); +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_write64); + +u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset) +{ + unsigned long flags; + u64 val; + + spin_lock_irqsave(&sst->spinlock, flags); + val = sst->ops->read64(sst->addr.shim, offset); + spin_unlock_irqrestore(&sst->spinlock, flags); + + return val; +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_read64); + +void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value) +{ + sst->ops->write(sst->addr.shim, offset, value); +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_write_unlocked); + +u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset) +{ + return sst->ops->read(sst->addr.shim, offset); +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_read_unlocked); + +void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value) +{ + sst->ops->write64(sst->addr.shim, offset, value); +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_write64_unlocked); + +u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset) +{ + return sst->ops->read64(sst->addr.shim, offset); +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_read64_unlocked); + +int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset, + u32 mask, u32 value) +{ + bool change; + unsigned int old, new; + u32 ret; + + ret = sst_dsp_shim_read_unlocked(sst, offset); + + old = ret; + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + sst_dsp_shim_write_unlocked(sst, offset, new); + + return change; +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_unlocked); + +int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset, + u64 mask, u64 value) +{ + bool change; + u64 old, new; + + old = sst_dsp_shim_read64_unlocked(sst, offset); + + new = (old & (~mask)) | (value & mask); + + change = (old != new); + if (change) + sst_dsp_shim_write64_unlocked(sst, offset, new); + + return change; +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked); + +int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset, + u32 mask, u32 value) +{ + unsigned long flags; + bool change; + + spin_lock_irqsave(&sst->spinlock, flags); + change = sst_dsp_shim_update_bits_unlocked(sst, offset, mask, value); + spin_unlock_irqrestore(&sst->spinlock, flags); + return change; +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits); + +int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset, + u64 mask, u64 value) +{ + unsigned long flags; + bool change; + + spin_lock_irqsave(&sst->spinlock, flags); + change = sst_dsp_shim_update_bits64_unlocked(sst, offset, mask, value); + spin_unlock_irqrestore(&sst->spinlock, flags); + return change; +} +EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64); + +void sst_dsp_dump(struct sst_dsp *sst) +{ + sst->ops->dump(sst); +} +EXPORT_SYMBOL_GPL(sst_dsp_dump); + +void sst_dsp_reset(struct sst_dsp *sst) +{ + sst->ops->reset(sst); +} +EXPORT_SYMBOL_GPL(sst_dsp_reset); + +int sst_dsp_boot(struct sst_dsp *sst) +{ + sst->ops->boot(sst); + return 0; +} +EXPORT_SYMBOL_GPL(sst_dsp_boot); + +void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg) +{ + sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY); + trace_sst_ipc_msg_tx(msg); +} +EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx); + +u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp) +{ + u32 msg; + + msg = sst_dsp_shim_read_unlocked(dsp, SST_IPCX); + trace_sst_ipc_msg_rx(msg); + + return msg; +} +EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx); + +int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size, + u32 outbox_offset, size_t outbox_size) +{ + sst->mailbox.in_base = sst->addr.lpe + inbox_offset; + sst->mailbox.out_base = sst->addr.lpe + outbox_offset; + sst->mailbox.in_size = inbox_size; + sst->mailbox.out_size = outbox_size; + return 0; +} +EXPORT_SYMBOL_GPL(sst_dsp_mailbox_init); + +void sst_dsp_outbox_write(struct sst_dsp *sst, void *message, size_t bytes) +{ + u32 i; + + trace_sst_ipc_outbox_write(bytes); + + memcpy_toio(sst->mailbox.out_base, message, bytes); + + for (i = 0; i < bytes; i += 4) + trace_sst_ipc_outbox_wdata(i, *(u32 *)(message + i)); +} +EXPORT_SYMBOL_GPL(sst_dsp_outbox_write); + +void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes) +{ + u32 i; + + trace_sst_ipc_outbox_read(bytes); + + memcpy_fromio(message, sst->mailbox.out_base, bytes); + + for (i = 0; i < bytes; i += 4) + trace_sst_ipc_outbox_rdata(i, *(u32 *)(message + i)); +} +EXPORT_SYMBOL_GPL(sst_dsp_outbox_read); + +void sst_dsp_inbox_write(struct sst_dsp *sst, void *message, size_t bytes) +{ + u32 i; + + trace_sst_ipc_inbox_write(bytes); + + memcpy_toio(sst->mailbox.in_base, message, bytes); + + for (i = 0; i < bytes; i += 4) + trace_sst_ipc_inbox_wdata(i, *(u32 *)(message + i)); +} +EXPORT_SYMBOL_GPL(sst_dsp_inbox_write); + +void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes) +{ + u32 i; + + trace_sst_ipc_inbox_read(bytes); + + memcpy_fromio(message, sst->mailbox.in_base, bytes); + + for (i = 0; i < bytes; i += 4) + trace_sst_ipc_inbox_rdata(i, *(u32 *)(message + i)); +} +EXPORT_SYMBOL_GPL(sst_dsp_inbox_read); + +struct sst_dsp *sst_dsp_new(struct device *dev, + struct sst_dsp_device *sst_dev, struct sst_pdata *pdata) +{ + struct sst_dsp *sst; + int err; + + dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id); + + sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL); + if (sst == NULL) + return NULL; + + spin_lock_init(&sst->spinlock); + mutex_init(&sst->mutex); + sst->dev = dev; + sst->thread_context = sst_dev->thread_context; + sst->sst_dev = sst_dev; + sst->id = pdata->id; + sst->irq = pdata->irq; + sst->ops = sst_dev->ops; + sst->pdata = pdata; + INIT_LIST_HEAD(&sst->used_block_list); + INIT_LIST_HEAD(&sst->free_block_list); + INIT_LIST_HEAD(&sst->module_list); + INIT_LIST_HEAD(&sst->fw_list); + + /* Initialise SST Audio DSP */ + if (sst->ops->init) { + err = sst->ops->init(sst, pdata); + if (err < 0) + return NULL; + } + + /* Register the ISR */ + err = request_threaded_irq(sst->irq, sst->ops->irq_handler, + sst_dev->thread, IRQF_SHARED, "AudioDSP", sst); + if (err) + goto irq_err; + + return sst; + +irq_err: + if (sst->ops->free) + sst->ops->free(sst); + + return NULL; +} +EXPORT_SYMBOL_GPL(sst_dsp_new); + +void sst_dsp_free(struct sst_dsp *sst) +{ + free_irq(sst->irq, sst); + if (sst->ops->free) + sst->ops->free(sst); +} +EXPORT_SYMBOL_GPL(sst_dsp_free); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood"); +MODULE_DESCRIPTION("Intel SST Core"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h new file mode 100644 index 000000000000..74052b59485c --- /dev/null +++ b/sound/soc/intel/sst-dsp.h @@ -0,0 +1,233 @@ +/* + * Intel Smart Sound Technology (SST) Core + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License 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. + * + */ + +#ifndef __SOUND_SOC_SST_DSP_H +#define __SOUND_SOC_SST_DSP_H + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> + +/* SST Device IDs */ +#define SST_DEV_ID_LYNX_POINT 0x33C8 +#define SST_DEV_ID_WILDCAT_POINT 0x3438 +#define SST_DEV_ID_BYT 0x0F28 + +/* Supported SST DMA Devices */ +#define SST_DMA_TYPE_DW 1 +#define SST_DMA_TYPE_MID 2 + +/* SST Shim register map + * The register naming can differ between products. Some products also + * contain extra functionality. + */ +#define SST_CSR 0x00 +#define SST_PISR 0x08 +#define SST_PIMR 0x10 +#define SST_ISRX 0x18 +#define SST_ISRD 0x20 +#define SST_IMRX 0x28 +#define SST_IMRD 0x30 +#define SST_IPCX 0x38 /* IPC IA -> SST */ +#define SST_IPCD 0x40 /* IPC SST -> IA */ +#define SST_ISRSC 0x48 +#define SST_ISRLPESC 0x50 +#define SST_IMRSC 0x58 +#define SST_IMRLPESC 0x60 +#define SST_IPCSC 0x68 +#define SST_IPCLPESC 0x70 +#define SST_CLKCTL 0x78 +#define SST_CSR2 0x80 +#define SST_LTRC 0xE0 +#define SST_HDMC 0xE8 +#define SST_DBGO 0xF0 + +#define SST_SHIM_SIZE 0x100 +#define SST_PWMCTRL 0x1000 + +/* SST Shim Register bits + * The register bit naming can differ between products. Some products also + * contain extra functionality. + */ + +/* CSR / CS */ +#define SST_CSR_RST (0x1 << 1) +#define SST_CSR_SBCS0 (0x1 << 2) +#define SST_CSR_SBCS1 (0x1 << 3) +#define SST_CSR_DCS(x) (x << 4) +#define SST_CSR_DCS_MASK (0x7 << 4) +#define SST_CSR_STALL (0x1 << 10) +#define SST_CSR_S0IOCS (0x1 << 21) +#define SST_CSR_S1IOCS (0x1 << 23) +#define SST_CSR_LPCS (0x1 << 31) +#define SST_BYT_CSR_RST (0x1 << 0) +#define SST_BYT_CSR_VECTOR_SEL (0x1 << 1) +#define SST_BYT_CSR_STALL (0x1 << 2) +#define SST_BYT_CSR_PWAITMODE (0x1 << 3) + +/* ISRX / ISC */ +#define SST_ISRX_BUSY (0x1 << 1) +#define SST_ISRX_DONE (0x1 << 0) +#define SST_BYT_ISRX_REQUEST (0x1 << 1) + +/* ISRD / ISD */ +#define SST_ISRD_BUSY (0x1 << 1) +#define SST_ISRD_DONE (0x1 << 0) + +/* IMRX / IMC */ +#define SST_IMRX_BUSY (0x1 << 1) +#define SST_IMRX_DONE (0x1 << 0) +#define SST_BYT_IMRX_REQUEST (0x1 << 1) + +/* IPCX / IPCC */ +#define SST_IPCX_DONE (0x1 << 30) +#define SST_IPCX_BUSY (0x1 << 31) +#define SST_BYT_IPCX_DONE ((u64)0x1 << 62) +#define SST_BYT_IPCX_BUSY ((u64)0x1 << 63) + +/* IPCD */ +#define SST_IPCD_DONE (0x1 << 30) +#define SST_IPCD_BUSY (0x1 << 31) +#define SST_BYT_IPCD_DONE ((u64)0x1 << 62) +#define SST_BYT_IPCD_BUSY ((u64)0x1 << 63) + +/* CLKCTL */ +#define SST_CLKCTL_SMOS(x) (x << 24) +#define SST_CLKCTL_MASK (3 << 24) +#define SST_CLKCTL_DCPLCG (1 << 18) +#define SST_CLKCTL_SCOE1 (1 << 17) +#define SST_CLKCTL_SCOE0 (1 << 16) + +/* CSR2 / CS2 */ +#define SST_CSR2_SDFD_SSP0 (1 << 1) +#define SST_CSR2_SDFD_SSP1 (1 << 2) + +/* LTRC */ +#define SST_LTRC_VAL(x) (x << 0) + +/* HDMC */ +#define SST_HDMC_HDDA0(x) (x << 0) +#define SST_HDMC_HDDA1(x) (x << 7) + + +/* SST Vendor Defined Registers and bits */ +#define SST_VDRTCTL0 0xa0 +#define SST_VDRTCTL1 0xa4 +#define SST_VDRTCTL2 0xa8 +#define SST_VDRTCTL3 0xaC + +/* VDRTCTL0 */ +#define SST_VDRTCL0_DSRAMPGE_SHIFT 16 +#define SST_VDRTCL0_DSRAMPGE_MASK (0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT) +#define SST_VDRTCL0_ISRAMPGE_SHIFT 6 +#define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT) + +struct sst_dsp; + +/* + * SST Device. + * + * This structure is populated by the SST core driver. + */ +struct sst_dsp_device { + /* Mandatory fields */ + struct sst_ops *ops; + irqreturn_t (*thread)(int irq, void *context); + void *thread_context; +}; + +/* + * SST Platform Data. + */ +struct sst_pdata { + /* ACPI data */ + u32 lpe_base; + u32 lpe_size; + u32 pcicfg_base; + u32 pcicfg_size; + u32 fw_base; + u32 fw_size; + int irq; + + /* Firmware */ + const struct firmware *fw; + + /* DMA */ + u32 dma_base; + u32 dma_size; + int dma_engine; + + /* DSP */ + u32 id; + void *dsp; +}; + +/* Initialization */ +struct sst_dsp *sst_dsp_new(struct device *dev, + struct sst_dsp_device *sst_dev, struct sst_pdata *pdata); +void sst_dsp_free(struct sst_dsp *sst); + +/* SHIM Read / Write */ +void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value); +u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset); +int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset, + u32 mask, u32 value); +void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value); +u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset); +int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset, + u64 mask, u64 value); + +/* SHIM Read / Write Unlocked for callers already holding sst lock */ +void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value); +u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset); +int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset, + u32 mask, u32 value); +void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value); +u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset); +int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset, + u64 mask, u64 value); + +/* Internal generic low-level SST IO functions - can be overidden */ +void sst_shim32_write(void __iomem *addr, u32 offset, u32 value); +u32 sst_shim32_read(void __iomem *addr, u32 offset); +void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value); +u64 sst_shim32_read64(void __iomem *addr, u32 offset); +void sst_memcpy_toio_32(struct sst_dsp *sst, + void __iomem *dest, void *src, size_t bytes); +void sst_memcpy_fromio_32(struct sst_dsp *sst, + void *dest, void __iomem *src, size_t bytes); + +/* DSP reset & boot */ +void sst_dsp_reset(struct sst_dsp *sst); +int sst_dsp_boot(struct sst_dsp *sst); + +/* Msg IO */ +void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg); +u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp); + +/* Mailbox management */ +int sst_dsp_mailbox_init(struct sst_dsp *dsp, u32 inbox_offset, + size_t inbox_size, u32 outbox_offset, size_t outbox_size); +void sst_dsp_inbox_write(struct sst_dsp *dsp, void *message, size_t bytes); +void sst_dsp_inbox_read(struct sst_dsp *dsp, void *message, size_t bytes); +void sst_dsp_outbox_write(struct sst_dsp *dsp, void *message, size_t bytes); +void sst_dsp_outbox_read(struct sst_dsp *dsp, void *message, size_t bytes); +void sst_dsp_mailbox_dump(struct sst_dsp *dsp, size_t bytes); + +/* Debug */ +void sst_dsp_dump(struct sst_dsp *sst); + +#endif diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c new file mode 100644 index 000000000000..f7687107cf7f --- /dev/null +++ b/sound/soc/intel/sst-firmware.c @@ -0,0 +1,587 @@ +/* + * Intel SST Firmware Loader + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License 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/kernel.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/firmware.h> +#include <linux/export.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/pci.h> + +#include <asm/page.h> +#include <asm/pgtable.h> + +#include "sst-dsp.h" +#include "sst-dsp-priv.h" + +static void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes) +{ + u32 i; + + /* copy one 32 bit word at a time as 64 bit access is not supported */ + for (i = 0; i < bytes; i += 4) + memcpy_toio(dest + i, src + i, 4); +} + +/* create new generic firmware object */ +struct sst_fw *sst_fw_new(struct sst_dsp *dsp, + const struct firmware *fw, void *private) +{ + struct sst_fw *sst_fw; + int err; + + if (!dsp->ops->parse_fw) + return NULL; + + sst_fw = kzalloc(sizeof(*sst_fw), GFP_KERNEL); + if (sst_fw == NULL) + return NULL; + + sst_fw->dsp = dsp; + sst_fw->private = private; + sst_fw->size = fw->size; + + err = dma_coerce_mask_and_coherent(dsp->dev, DMA_BIT_MASK(32)); + if (err < 0) { + kfree(sst_fw); + return NULL; + } + + /* allocate DMA buffer to store FW data */ + sst_fw->dma_buf = dma_alloc_coherent(dsp->dev, sst_fw->size, + &sst_fw->dmable_fw_paddr, GFP_DMA | GFP_KERNEL); + if (!sst_fw->dma_buf) { + dev_err(dsp->dev, "error: DMA alloc failed\n"); + kfree(sst_fw); + return NULL; + } + + /* copy FW data to DMA-able memory */ + memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size); + + /* call core specific FW paser to load FW data into DSP */ + err = dsp->ops->parse_fw(sst_fw); + if (err < 0) { + dev_err(dsp->dev, "error: parse fw failed %d\n", err); + goto parse_err; + } + + mutex_lock(&dsp->mutex); + list_add(&sst_fw->list, &dsp->fw_list); + mutex_unlock(&dsp->mutex); + + return sst_fw; + +parse_err: + dma_free_coherent(dsp->dev, sst_fw->size, + sst_fw->dma_buf, + sst_fw->dmable_fw_paddr); + kfree(sst_fw); + return NULL; +} +EXPORT_SYMBOL_GPL(sst_fw_new); + +/* free single firmware object */ +void sst_fw_free(struct sst_fw *sst_fw) +{ + struct sst_dsp *dsp = sst_fw->dsp; + + mutex_lock(&dsp->mutex); + list_del(&sst_fw->list); + mutex_unlock(&dsp->mutex); + + dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf, + sst_fw->dmable_fw_paddr); + kfree(sst_fw); +} +EXPORT_SYMBOL_GPL(sst_fw_free); + +/* free all firmware objects */ +void sst_fw_free_all(struct sst_dsp *dsp) +{ + struct sst_fw *sst_fw, *t; + + mutex_lock(&dsp->mutex); + list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) { + + list_del(&sst_fw->list); + dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf, + sst_fw->dmable_fw_paddr); + kfree(sst_fw); + } + mutex_unlock(&dsp->mutex); +} +EXPORT_SYMBOL_GPL(sst_fw_free_all); + +/* create a new SST generic module from FW template */ +struct sst_module *sst_module_new(struct sst_fw *sst_fw, + struct sst_module_template *template, void *private) +{ + struct sst_dsp *dsp = sst_fw->dsp; + struct sst_module *sst_module; + + sst_module = kzalloc(sizeof(*sst_module), GFP_KERNEL); + if (sst_module == NULL) + return NULL; + + sst_module->id = template->id; + sst_module->dsp = dsp; + sst_module->sst_fw = sst_fw; + + memcpy(&sst_module->s, &template->s, sizeof(struct sst_module_data)); + memcpy(&sst_module->p, &template->p, sizeof(struct sst_module_data)); + + INIT_LIST_HEAD(&sst_module->block_list); + + mutex_lock(&dsp->mutex); + list_add(&sst_module->list, &dsp->module_list); + mutex_unlock(&dsp->mutex); + + return sst_module; +} +EXPORT_SYMBOL_GPL(sst_module_new); + +/* free firmware module and remove from available list */ +void sst_module_free(struct sst_module *sst_module) +{ + struct sst_dsp *dsp = sst_module->dsp; + + mutex_lock(&dsp->mutex); + list_del(&sst_module->list); + mutex_unlock(&dsp->mutex); + + kfree(sst_module); +} +EXPORT_SYMBOL_GPL(sst_module_free); + +static struct sst_mem_block *find_block(struct sst_dsp *dsp, int type, + u32 offset) +{ + struct sst_mem_block *block; + + list_for_each_entry(block, &dsp->free_block_list, list) { + if (block->type == type && block->offset == offset) + return block; + } + + return NULL; +} + +static int block_alloc_contiguous(struct sst_module *module, + struct sst_module_data *data, u32 offset, int size) +{ + struct list_head tmp = LIST_HEAD_INIT(tmp); + struct sst_dsp *dsp = module->dsp; + struct sst_mem_block *block; + + while (size > 0) { + block = find_block(dsp, data->type, offset); + if (!block) { + list_splice(&tmp, &dsp->free_block_list); + return -ENOMEM; + } + + list_move_tail(&block->list, &tmp); + offset += block->size; + size -= block->size; + } + + list_splice(&tmp, &dsp->used_block_list); + return 0; +} + +/* allocate free DSP blocks for module data - callers hold locks */ +static int block_alloc(struct sst_module *module, + struct sst_module_data *data) +{ + struct sst_dsp *dsp = module->dsp; + struct sst_mem_block *block, *tmp; + int ret = 0; + + if (data->size == 0) + return 0; + + /* find first free whole blocks that can hold module */ + list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { + + /* ignore blocks with wrong type */ + if (block->type != data->type) + continue; + + if (data->size > block->size) + continue; + + data->offset = block->offset; + block->data_type = data->data_type; + block->bytes_used = data->size % block->size; + list_add(&block->module_list, &module->block_list); + list_move(&block->list, &dsp->used_block_list); + dev_dbg(dsp->dev, " *module %d added block %d:%d\n", + module->id, block->type, block->index); + return 0; + } + + /* then find free multiple blocks that can hold module */ + list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { + + /* ignore blocks with wrong type */ + if (block->type != data->type) + continue; + + /* do we span > 1 blocks */ + if (data->size > block->size) { + ret = block_alloc_contiguous(module, data, + block->offset + block->size, + data->size - block->size); + if (ret == 0) + return ret; + } + } + + /* not enough free block space */ + return -ENOMEM; +} + +/* remove module from memory - callers hold locks */ +static void block_module_remove(struct sst_module *module) +{ + struct sst_mem_block *block, *tmp; + struct sst_dsp *dsp = module->dsp; + int err; + + /* disable each block */ + list_for_each_entry(block, &module->block_list, module_list) { + + if (block->ops && block->ops->disable) { + err = block->ops->disable(block); + if (err < 0) + dev_err(dsp->dev, + "error: cant disable block %d:%d\n", + block->type, block->index); + } + } + + /* mark each block as free */ + list_for_each_entry_safe(block, tmp, &module->block_list, module_list) { + list_del(&block->module_list); + list_move(&block->list, &dsp->free_block_list); + } +} + +/* prepare the memory block to receive data from host - callers hold locks */ +static int block_module_prepare(struct sst_module *module) +{ + struct sst_mem_block *block; + int ret = 0; + + /* enable each block so that's it'e ready for module P/S data */ + list_for_each_entry(block, &module->block_list, module_list) { + + if (block->ops && block->ops->enable) { + ret = block->ops->enable(block); + if (ret < 0) { + dev_err(module->dsp->dev, + "error: cant disable block %d:%d\n", + block->type, block->index); + goto err; + } + } + } + return ret; + +err: + list_for_each_entry(block, &module->block_list, module_list) { + if (block->ops && block->ops->disable) + block->ops->disable(block); + } + return ret; +} + +/* allocate memory blocks for static module addresses - callers hold locks */ +static int block_alloc_fixed(struct sst_module *module, + struct sst_module_data *data) +{ + struct sst_dsp *dsp = module->dsp; + struct sst_mem_block *block, *tmp; + u32 end = data->offset + data->size, block_end; + int err; + + /* only IRAM/DRAM blocks are managed */ + if (data->type != SST_MEM_IRAM && data->type != SST_MEM_DRAM) + return 0; + + /* are blocks already attached to this module */ + list_for_each_entry_safe(block, tmp, &module->block_list, module_list) { + + /* force compacting mem blocks of the same data_type */ + if (block->data_type != data->data_type) + continue; + + block_end = block->offset + block->size; + + /* find block that holds section */ + if (data->offset >= block->offset && end < block_end) + return 0; + + /* does block span more than 1 section */ + if (data->offset >= block->offset && data->offset < block_end) { + + err = block_alloc_contiguous(module, data, + block->offset + block->size, + data->size - block->size + data->offset - block->offset); + if (err < 0) + return -ENOMEM; + + /* module already owns blocks */ + return 0; + } + } + + /* find first free blocks that can hold section in free list */ + list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { + block_end = block->offset + block->size; + + /* find block that holds section */ + if (data->offset >= block->offset && end < block_end) { + + /* add block */ + block->data_type = data->data_type; + list_move(&block->list, &dsp->used_block_list); + list_add(&block->module_list, &module->block_list); + return 0; + } + + /* does block span more than 1 section */ + if (data->offset >= block->offset && data->offset < block_end) { + + err = block_alloc_contiguous(module, data, + block->offset + block->size, + data->size - block->size); + if (err < 0) + return -ENOMEM; + + /* add block */ + block->data_type = data->data_type; + list_move(&block->list, &dsp->used_block_list); + list_add(&block->module_list, &module->block_list); + return 0; + } + + } + + return -ENOMEM; +} + +/* Load fixed module data into DSP memory blocks */ +int sst_module_insert_fixed_block(struct sst_module *module, + struct sst_module_data *data) +{ + struct sst_dsp *dsp = module->dsp; + int ret; + + mutex_lock(&dsp->mutex); + + /* alloc blocks that includes this section */ + ret = block_alloc_fixed(module, data); + if (ret < 0) { + dev_err(dsp->dev, + "error: no free blocks for section at offset 0x%x size 0x%x\n", + data->offset, data->size); + mutex_unlock(&dsp->mutex); + return -ENOMEM; + } + + /* prepare DSP blocks for module copy */ + ret = block_module_prepare(module); + if (ret < 0) { + dev_err(dsp->dev, "error: fw module prepare failed\n"); + goto err; + } + + /* copy partial module data to blocks */ + sst_memcpy32(dsp->addr.lpe + data->offset, data->data, data->size); + + mutex_unlock(&dsp->mutex); + return ret; + +err: + block_module_remove(module); + mutex_unlock(&dsp->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(sst_module_insert_fixed_block); + +/* Unload entire module from DSP memory */ +int sst_block_module_remove(struct sst_module *module) +{ + struct sst_dsp *dsp = module->dsp; + + mutex_lock(&dsp->mutex); + block_module_remove(module); + mutex_unlock(&dsp->mutex); + return 0; +} +EXPORT_SYMBOL_GPL(sst_block_module_remove); + +/* register a DSP memory block for use with FW based modules */ +struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, + u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index, + void *private) +{ + struct sst_mem_block *block; + + block = kzalloc(sizeof(*block), GFP_KERNEL); + if (block == NULL) + return NULL; + + block->offset = offset; + block->size = size; + block->index = index; + block->type = type; + block->dsp = dsp; + block->private = private; + block->ops = ops; + + mutex_lock(&dsp->mutex); + list_add(&block->list, &dsp->free_block_list); + mutex_unlock(&dsp->mutex); + + return block; +} +EXPORT_SYMBOL_GPL(sst_mem_block_register); + +/* unregister all DSP memory blocks */ +void sst_mem_block_unregister_all(struct sst_dsp *dsp) +{ + struct sst_mem_block *block, *tmp; + + mutex_lock(&dsp->mutex); + + /* unregister used blocks */ + list_for_each_entry_safe(block, tmp, &dsp->used_block_list, list) { + list_del(&block->list); + kfree(block); + } + + /* unregister free blocks */ + list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { + list_del(&block->list); + kfree(block); + } + + mutex_unlock(&dsp->mutex); +} +EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all); + +/* allocate scratch buffer blocks */ +struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp) +{ + struct sst_module *sst_module, *scratch; + struct sst_mem_block *block, *tmp; + u32 block_size; + int ret = 0; + + scratch = kzalloc(sizeof(struct sst_module), GFP_KERNEL); + if (scratch == NULL) + return NULL; + + mutex_lock(&dsp->mutex); + + /* calculate required scratch size */ + list_for_each_entry(sst_module, &dsp->module_list, list) { + if (scratch->s.size > sst_module->s.size) + scratch->s.size = scratch->s.size; + else + scratch->s.size = sst_module->s.size; + } + + dev_dbg(dsp->dev, "scratch buffer required is %d bytes\n", + scratch->s.size); + + /* init scratch module */ + scratch->dsp = dsp; + scratch->s.type = SST_MEM_DRAM; + scratch->s.data_type = SST_DATA_S; + INIT_LIST_HEAD(&scratch->block_list); + + /* check free blocks before looking at used blocks for space */ + if (!list_empty(&dsp->free_block_list)) + block = list_first_entry(&dsp->free_block_list, + struct sst_mem_block, list); + else + block = list_first_entry(&dsp->used_block_list, + struct sst_mem_block, list); + block_size = block->size; + + /* allocate blocks for module scratch buffers */ + dev_dbg(dsp->dev, "allocating scratch blocks\n"); + ret = block_alloc(scratch, &scratch->s); + if (ret < 0) { + dev_err(dsp->dev, "error: can't alloc scratch blocks\n"); + goto err; + } + + /* assign the same offset of scratch to each module */ + list_for_each_entry(sst_module, &dsp->module_list, list) + sst_module->s.offset = scratch->s.offset; + + mutex_unlock(&dsp->mutex); + return scratch; + +err: + list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list) + list_del(&block->module_list); + mutex_unlock(&dsp->mutex); + return NULL; +} +EXPORT_SYMBOL_GPL(sst_mem_block_alloc_scratch); + +/* free all scratch blocks */ +void sst_mem_block_free_scratch(struct sst_dsp *dsp, + struct sst_module *scratch) +{ + struct sst_mem_block *block, *tmp; + + mutex_lock(&dsp->mutex); + + list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list) + list_del(&block->module_list); + + mutex_unlock(&dsp->mutex); +} +EXPORT_SYMBOL_GPL(sst_mem_block_free_scratch); + +/* get a module from it's unique ID */ +struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id) +{ + struct sst_module *module; + + mutex_lock(&dsp->mutex); + + list_for_each_entry(module, &dsp->module_list, list) { + if (module->id == id) { + mutex_unlock(&dsp->mutex); + return module; + } + } + + mutex_unlock(&dsp->mutex); + return NULL; +} +EXPORT_SYMBOL_GPL(sst_module_get_from_id); diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c new file mode 100644 index 000000000000..f5ebf36af889 --- /dev/null +++ b/sound/soc/intel/sst-haswell-dsp.c @@ -0,0 +1,517 @@ +/* + * Intel Haswell SST DSP driver + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License 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/delay.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/sched.h> +#include <linux/export.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/pci.h> +#include <linux/firmware.h> +#include <linux/pm_runtime.h> + +#include <linux/acpi.h> +#include <acpi/acpi_bus.h> + +#include "sst-dsp.h" +#include "sst-dsp-priv.h" +#include "sst-haswell-ipc.h" + +#include <trace/events/hswadsp.h> + +#define SST_HSW_FW_SIGNATURE_SIZE 4 +#define SST_HSW_FW_SIGN "$SST" +#define SST_HSW_FW_LIB_SIGN "$LIB" + +#define SST_WPT_SHIM_OFFSET 0xFB000 +#define SST_LP_SHIM_OFFSET 0xE7000 +#define SST_WPT_IRAM_OFFSET 0xA0000 +#define SST_LP_IRAM_OFFSET 0x80000 + +#define SST_SHIM_PM_REG 0x84 + +#define SST_HSW_IRAM 1 +#define SST_HSW_DRAM 2 +#define SST_HSW_REGS 3 + +struct dma_block_info { + __le32 type; /* IRAM/DRAM */ + __le32 size; /* Bytes */ + __le32 ram_offset; /* Offset in I/DRAM */ + __le32 rsvd; /* Reserved field */ +} __attribute__((packed)); + +struct fw_module_info { + __le32 persistent_size; + __le32 scratch_size; +} __attribute__((packed)); + +struct fw_header { + unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* FW signature */ + __le32 file_size; /* size of fw minus this header */ + __le32 modules; /* # of modules */ + __le32 file_format; /* version of header format */ + __le32 reserved[4]; +} __attribute__((packed)); + +struct fw_module_header { + unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* module signature */ + __le32 mod_size; /* size of module */ + __le32 blocks; /* # of blocks */ + __le16 padding; + __le16 type; /* codec type, pp lib */ + __le32 entry_point; + struct fw_module_info info; +} __attribute__((packed)); + +static void hsw_free(struct sst_dsp *sst); + +static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, + struct fw_module_header *module) +{ + struct dma_block_info *block; + struct sst_module *mod; + struct sst_module_data block_data; + struct sst_module_template template; + int count; + void __iomem *ram; + + /* TODO: allowed module types need to be configurable */ + if (module->type != SST_HSW_MODULE_BASE_FW + && module->type != SST_HSW_MODULE_PCM_SYSTEM + && module->type != SST_HSW_MODULE_PCM + && module->type != SST_HSW_MODULE_PCM_REFERENCE + && module->type != SST_HSW_MODULE_PCM_CAPTURE + && module->type != SST_HSW_MODULE_LPAL) + return 0; + + dev_dbg(dsp->dev, "new module sign 0x%s size 0x%x blocks 0x%x type 0x%x\n", + module->signature, module->mod_size, + module->blocks, module->type); + dev_dbg(dsp->dev, " entrypoint 0x%x\n", module->entry_point); + dev_dbg(dsp->dev, " persistent 0x%x scratch 0x%x\n", + module->info.persistent_size, module->info.scratch_size); + + memset(&template, 0, sizeof(template)); + template.id = module->type; + template.entry = module->entry_point; + template.p.size = module->info.persistent_size; + template.p.type = SST_MEM_DRAM; + template.p.data_type = SST_DATA_P; + template.s.size = module->info.scratch_size; + template.s.type = SST_MEM_DRAM; + template.s.data_type = SST_DATA_S; + + mod = sst_module_new(fw, &template, NULL); + if (mod == NULL) + return -ENOMEM; + + block = (void *)module + sizeof(*module); + + for (count = 0; count < module->blocks; count++) { + + if (block->size <= 0) { + dev_err(dsp->dev, + "error: block %d size invalid\n", count); + sst_module_free(mod); + return -EINVAL; + } + + switch (block->type) { + case SST_HSW_IRAM: + ram = dsp->addr.lpe; + block_data.offset = + block->ram_offset + dsp->addr.iram_offset; + block_data.type = SST_MEM_IRAM; + break; + case SST_HSW_DRAM: + ram = dsp->addr.lpe; + block_data.offset = block->ram_offset; + block_data.type = SST_MEM_DRAM; + break; + default: + dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n", + block->type, count); + sst_module_free(mod); + return -EINVAL; + } + + block_data.size = block->size; + block_data.data_type = SST_DATA_M; + block_data.data = (void *)block + sizeof(*block); + block_data.data_offset = block_data.data - fw->dma_buf; + + dev_dbg(dsp->dev, "copy firmware block %d type 0x%x " + "size 0x%x ==> ram %p offset 0x%x\n", + count, block->type, block->size, ram, + block->ram_offset); + + sst_module_insert_fixed_block(mod, &block_data); + + block = (void *)block + sizeof(*block) + block->size; + } + return 0; +} + +static int hsw_parse_fw_image(struct sst_fw *sst_fw) +{ + struct fw_header *header; + struct sst_module *scratch; + struct fw_module_header *module; + struct sst_dsp *dsp = sst_fw->dsp; + struct sst_hsw *hsw = sst_fw->private; + int ret, count; + + /* Read the header information from the data pointer */ + header = (struct fw_header *)sst_fw->dma_buf; + + /* verify FW */ + if ((strncmp(header->signature, SST_HSW_FW_SIGN, 4) != 0) || + (sst_fw->size != header->file_size + sizeof(*header))) { + dev_err(dsp->dev, "error: invalid fw sign/filesize mismatch\n"); + return -EINVAL; + } + + dev_dbg(dsp->dev, "header size=0x%x modules=0x%x fmt=0x%x size=%zu\n", + header->file_size, header->modules, + header->file_format, sizeof(*header)); + + /* parse each module */ + module = (void *)sst_fw->dma_buf + sizeof(*header); + for (count = 0; count < header->modules; count++) { + + /* module */ + ret = hsw_parse_module(dsp, sst_fw, module); + if (ret < 0) { + dev_err(dsp->dev, "error: invalid module %d\n", count); + return ret; + } + module = (void *)module + sizeof(*module) + module->mod_size; + } + + /* allocate persistent/scratch mem regions */ + scratch = sst_mem_block_alloc_scratch(dsp); + if (scratch == NULL) + return -ENOMEM; + + sst_hsw_set_scratch_module(hsw, scratch); + + return 0; +} + +static irqreturn_t hsw_irq(int irq, void *context) +{ + struct sst_dsp *sst = (struct sst_dsp *) context; + u32 isr; + int ret = IRQ_NONE; + + spin_lock(&sst->spinlock); + + /* Interrupt arrived, check src */ + isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX); + if (isr & SST_ISRX_DONE) { + trace_sst_irq_done(isr, + sst_dsp_shim_read_unlocked(sst, SST_IMRX)); + + /* Mask Done interrupt before return */ + sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, + SST_IMRX_DONE, SST_IMRX_DONE); + ret = IRQ_WAKE_THREAD; + } + + if (isr & SST_ISRX_BUSY) { + trace_sst_irq_busy(isr, + sst_dsp_shim_read_unlocked(sst, SST_IMRX)); + + /* Mask Busy interrupt before return */ + sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, + SST_IMRX_BUSY, SST_IMRX_BUSY); + ret = IRQ_WAKE_THREAD; + } + + spin_unlock(&sst->spinlock); + return ret; +} + +static void hsw_boot(struct sst_dsp *sst) +{ + /* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */ + sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, + SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0); + + /* stall DSP core, set clk to 192/96Mhz */ + sst_dsp_shim_update_bits_unlocked(sst, + SST_CSR, SST_CSR_STALL | SST_CSR_DCS_MASK, + SST_CSR_STALL | SST_CSR_DCS(4)); + + /* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */ + sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL, + SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0, + SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0); + + /* disable DMA finish function for SSP0 & SSP1 */ + sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1, + SST_CSR2_SDFD_SSP1); + + /* enable DMA engine 0,1 all channels to access host memory */ + sst_dsp_shim_update_bits_unlocked(sst, SST_HDMC, + SST_HDMC_HDDA1(0xff) | SST_HDMC_HDDA0(0xff), + SST_HDMC_HDDA1(0xff) | SST_HDMC_HDDA0(0xff)); + + /* disable all clock gating */ + writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2); + + /* set DSP to RUN */ + sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0); +} + +static void hsw_reset(struct sst_dsp *sst) +{ + /* put DSP into reset and stall */ + sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, + SST_CSR_RST | SST_CSR_STALL, SST_CSR_RST | SST_CSR_STALL); + + /* keep in reset for 10ms */ + mdelay(10); + + /* take DSP out of reset and keep stalled for FW loading */ + sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, + SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL); +} + +struct sst_adsp_memregion { + u32 start; + u32 end; + int blocks; + enum sst_mem_type type; +}; + +/* lynx point ADSP mem regions */ +static const struct sst_adsp_memregion lp_region[] = { + {0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */ + {0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */ + {0x80000, 0xE0000, 12, SST_MEM_IRAM}, /* I-SRAM - 12 * 32kB */ +}; + +/* wild cat point ADSP mem regions */ +static const struct sst_adsp_memregion wpt_region[] = { + {0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */ + {0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */ + {0x80000, 0xA0000, 4, SST_MEM_DRAM}, /* D-SRAM2 - 4 * 32kB */ + {0xA0000, 0xF0000, 10, SST_MEM_IRAM}, /* I-SRAM - 10 * 32kB */ +}; + +static int hsw_acpi_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata) +{ + /* ADSP DRAM & IRAM */ + sst->addr.lpe_base = pdata->lpe_base; + sst->addr.lpe = ioremap(pdata->lpe_base, pdata->lpe_size); + if (!sst->addr.lpe) + return -ENODEV; + + /* ADSP PCI MMIO config space */ + sst->addr.pci_cfg = ioremap(pdata->pcicfg_base, pdata->pcicfg_size); + if (!sst->addr.pci_cfg) { + iounmap(sst->addr.lpe); + return -ENODEV; + } + + /* SST Shim */ + sst->addr.shim = sst->addr.lpe + sst->addr.shim_offset; + return 0; +} + +static u32 hsw_block_get_bit(struct sst_mem_block *block) +{ + u32 bit = 0, shift = 0; + + switch (block->type) { + case SST_MEM_DRAM: + shift = 16; + break; + case SST_MEM_IRAM: + shift = 6; + break; + default: + return 0; + } + + bit = 1 << (block->index + shift); + + return bit; +} + +/* enable 32kB memory block - locks held by caller */ +static int hsw_block_enable(struct sst_mem_block *block) +{ + struct sst_dsp *sst = block->dsp; + u32 bit, val; + + if (block->users++ > 0) + return 0; + + dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n", + block->type, block->index, block->offset); + + val = readl(sst->addr.pci_cfg + SST_VDRTCTL0); + bit = hsw_block_get_bit(block); + writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0); + + /* wait 18 DSP clock ticks */ + udelay(10); + + return 0; +} + +/* disable 32kB memory block - locks held by caller */ +static int hsw_block_disable(struct sst_mem_block *block) +{ + struct sst_dsp *sst = block->dsp; + u32 bit, val; + + if (--block->users > 0) + return 0; + + dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n", + block->type, block->index, block->offset); + + val = readl(sst->addr.pci_cfg + SST_VDRTCTL0); + bit = hsw_block_get_bit(block); + writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0); + + return 0; +} + +static struct sst_block_ops sst_hsw_ops = { + .enable = hsw_block_enable, + .disable = hsw_block_disable, +}; + +static int hsw_enable_shim(struct sst_dsp *sst) +{ + int tries = 10; + u32 reg; + + /* enable shim */ + reg = readl(sst->addr.pci_cfg + SST_SHIM_PM_REG); + writel(reg & ~0x3, sst->addr.pci_cfg + SST_SHIM_PM_REG); + + /* check that ADSP shim is enabled */ + while (tries--) { + reg = sst_dsp_shim_read_unlocked(sst, SST_CSR); + if (reg != 0xffffffff) + return 0; + + msleep(1); + } + + return -ENODEV; +} + +static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata) +{ + const struct sst_adsp_memregion *region; + struct device *dev; + int ret = -ENODEV, i, j, region_count; + u32 offset, size; + + dev = sst->dev; + + switch (sst->id) { + case SST_DEV_ID_LYNX_POINT: + region = lp_region; + region_count = ARRAY_SIZE(lp_region); + sst->addr.iram_offset = SST_LP_IRAM_OFFSET; + sst->addr.shim_offset = SST_LP_SHIM_OFFSET; + break; + case SST_DEV_ID_WILDCAT_POINT: + region = wpt_region; + region_count = ARRAY_SIZE(wpt_region); + sst->addr.iram_offset = SST_WPT_IRAM_OFFSET; + sst->addr.shim_offset = SST_WPT_SHIM_OFFSET; + break; + default: + dev_err(dev, "error: failed to get mem resources\n"); + return ret; + } + + ret = hsw_acpi_resource_map(sst, pdata); + if (ret < 0) { + dev_err(dev, "error: failed to map resources\n"); + return ret; + } + + /* enable the DSP SHIM */ + ret = hsw_enable_shim(sst); + if (ret < 0) { + dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n"); + return ret; + } + + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + /* Enable Interrupt from both sides */ + sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, 0x3, 0x0); + sst_dsp_shim_update_bits_unlocked(sst, SST_IMRD, + (0x3 | 0x1 << 16 | 0x3 << 21), 0x0); + + /* register DSP memory blocks - ideally we should get this from ACPI */ + for (i = 0; i < region_count; i++) { + offset = region[i].start; + size = (region[i].end - region[i].start) / region[i].blocks; + + /* register individual memory blocks */ + for (j = 0; j < region[i].blocks; j++) { + sst_mem_block_register(sst, offset, size, + region[i].type, &sst_hsw_ops, j, sst); + offset += size; + } + } + + /* set default power gating mask */ + writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL0); + + return 0; +} + +static void hsw_free(struct sst_dsp *sst) +{ + sst_mem_block_unregister_all(sst); + iounmap(sst->addr.lpe); + iounmap(sst->addr.pci_cfg); +} + +struct sst_ops haswell_ops = { + .reset = hsw_reset, + .boot = hsw_boot, + .write = sst_shim32_write, + .read = sst_shim32_read, + .write64 = sst_shim32_write64, + .read64 = sst_shim32_read64, + .ram_read = sst_memcpy_fromio_32, + .ram_write = sst_memcpy_toio_32, + .irq_handler = hsw_irq, + .init = hsw_init, + .free = hsw_free, + .parse_fw = hsw_parse_fw_image, +}; diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c new file mode 100644 index 000000000000..f46bb4ddde6f --- /dev/null +++ b/sound/soc/intel/sst-haswell-ipc.c @@ -0,0 +1,1785 @@ +/* + * Intel SST Haswell/Broadwell IPC Support + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License 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/types.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/device.h> +#include <linux/wait.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/list.h> +#include <linux/platform_device.h> +#include <linux/kthread.h> +#include <linux/firmware.h> +#include <linux/dma-mapping.h> +#include <linux/debugfs.h> + +#include "sst-haswell-ipc.h" +#include "sst-dsp.h" +#include "sst-dsp-priv.h" + +/* Global Message - Generic */ +#define IPC_GLB_TYPE_SHIFT 24 +#define IPC_GLB_TYPE_MASK (0x1f << IPC_GLB_TYPE_SHIFT) +#define IPC_GLB_TYPE(x) (x << IPC_GLB_TYPE_SHIFT) + +/* Global Message - Reply */ +#define IPC_GLB_REPLY_SHIFT 0 +#define IPC_GLB_REPLY_MASK (0x1f << IPC_GLB_REPLY_SHIFT) +#define IPC_GLB_REPLY_TYPE(x) (x << IPC_GLB_REPLY_TYPE_SHIFT) + +/* Stream Message - Generic */ +#define IPC_STR_TYPE_SHIFT 20 +#define IPC_STR_TYPE_MASK (0xf << IPC_STR_TYPE_SHIFT) +#define IPC_STR_TYPE(x) (x << IPC_STR_TYPE_SHIFT) +#define IPC_STR_ID_SHIFT 16 +#define IPC_STR_ID_MASK (0xf << IPC_STR_ID_SHIFT) +#define IPC_STR_ID(x) (x << IPC_STR_ID_SHIFT) + +/* Stream Message - Reply */ +#define IPC_STR_REPLY_SHIFT 0 +#define IPC_STR_REPLY_MASK (0x1f << IPC_STR_REPLY_SHIFT) + +/* Stream Stage Message - Generic */ +#define IPC_STG_TYPE_SHIFT 12 +#define IPC_STG_TYPE_MASK (0xf << IPC_STG_TYPE_SHIFT) +#define IPC_STG_TYPE(x) (x << IPC_STG_TYPE_SHIFT) +#define IPC_STG_ID_SHIFT 10 +#define IPC_STG_ID_MASK (0x3 << IPC_STG_ID_SHIFT) +#define IPC_STG_ID(x) (x << IPC_STG_ID_SHIFT) + +/* Stream Stage Message - Reply */ +#define IPC_STG_REPLY_SHIFT 0 +#define IPC_STG_REPLY_MASK (0x1f << IPC_STG_REPLY_SHIFT) + +/* Debug Log Message - Generic */ +#define IPC_LOG_OP_SHIFT 20 +#define IPC_LOG_OP_MASK (0xf << IPC_LOG_OP_SHIFT) +#define IPC_LOG_OP_TYPE(x) (x << IPC_LOG_OP_SHIFT) +#define IPC_LOG_ID_SHIFT 16 +#define IPC_LOG_ID_MASK (0xf << IPC_LOG_ID_SHIFT) +#define IPC_LOG_ID(x) (x << IPC_LOG_ID_SHIFT) + +/* IPC message timeout (msecs) */ +#define IPC_TIMEOUT_MSECS 300 +#define IPC_BOOT_MSECS 200 +#define IPC_MSG_WAIT 0 +#define IPC_MSG_NOWAIT 1 + +/* Firmware Ready Message */ +#define IPC_FW_READY (0x1 << 29) +#define IPC_STATUS_MASK (0x3 << 30) + +#define IPC_EMPTY_LIST_SIZE 8 +#define IPC_MAX_STREAMS 4 + +/* Mailbox */ +#define IPC_MAX_MAILBOX_BYTES 256 + +/* Global Message - Types and Replies */ +enum ipc_glb_type { + IPC_GLB_GET_FW_VERSION = 0, /* Retrieves firmware version */ + IPC_GLB_PERFORMANCE_MONITOR = 1, /* Performance monitoring actions */ + IPC_GLB_ALLOCATE_STREAM = 3, /* Request to allocate new stream */ + IPC_GLB_FREE_STREAM = 4, /* Request to free stream */ + IPC_GLB_GET_FW_CAPABILITIES = 5, /* Retrieves firmware capabilities */ + IPC_GLB_STREAM_MESSAGE = 6, /* Message directed to stream or its stages */ + /* Request to store firmware context during D0->D3 transition */ + IPC_GLB_REQUEST_DUMP = 7, + /* Request to restore firmware context during D3->D0 transition */ + IPC_GLB_RESTORE_CONTEXT = 8, + IPC_GLB_GET_DEVICE_FORMATS = 9, /* Set device format */ + IPC_GLB_SET_DEVICE_FORMATS = 10, /* Get device format */ + IPC_GLB_SHORT_REPLY = 11, + IPC_GLB_ENTER_DX_STATE = 12, + IPC_GLB_GET_MIXER_STREAM_INFO = 13, /* Request mixer stream params */ + IPC_GLB_DEBUG_LOG_MESSAGE = 14, /* Message to or from the debug logger. */ + IPC_GLB_REQUEST_TRANSFER = 16, /* < Request Transfer for host */ + IPC_GLB_MAX_IPC_MESSAGE_TYPE = 17, /* Maximum message number */ +}; + +enum ipc_glb_reply { + IPC_GLB_REPLY_SUCCESS = 0, /* The operation was successful. */ + IPC_GLB_REPLY_ERROR_INVALID_PARAM = 1, /* Invalid parameter was passed. */ + IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE = 2, /* Uknown message type was resceived. */ + IPC_GLB_REPLY_OUT_OF_RESOURCES = 3, /* No resources to satisfy the request. */ + IPC_GLB_REPLY_BUSY = 4, /* The system or resource is busy. */ + IPC_GLB_REPLY_PENDING = 5, /* The action was scheduled for processing. */ + IPC_GLB_REPLY_FAILURE = 6, /* Critical error happened. */ + IPC_GLB_REPLY_INVALID_REQUEST = 7, /* Request can not be completed. */ + IPC_GLB_REPLY_STAGE_UNINITIALIZED = 8, /* Processing stage was uninitialized. */ + IPC_GLB_REPLY_NOT_FOUND = 9, /* Required resource can not be found. */ + IPC_GLB_REPLY_SOURCE_NOT_STARTED = 10, /* Source was not started. */ +}; + +/* Stream Message - Types */ +enum ipc_str_operation { + IPC_STR_RESET = 0, + IPC_STR_PAUSE = 1, + IPC_STR_RESUME = 2, + IPC_STR_STAGE_MESSAGE = 3, + IPC_STR_NOTIFICATION = 4, + IPC_STR_MAX_MESSAGE +}; + +/* Stream Stage Message Types */ +enum ipc_stg_operation { + IPC_STG_GET_VOLUME = 0, + IPC_STG_SET_VOLUME, + IPC_STG_SET_WRITE_POSITION, + IPC_STG_SET_FX_ENABLE, + IPC_STG_SET_FX_DISABLE, + IPC_STG_SET_FX_GET_PARAM, + IPC_STG_SET_FX_SET_PARAM, + IPC_STG_SET_FX_GET_INFO, + IPC_STG_MUTE_LOOPBACK, + IPC_STG_MAX_MESSAGE +}; + +/* Stream Stage Message Types For Notification*/ +enum ipc_stg_operation_notify { + IPC_POSITION_CHANGED = 0, + IPC_STG_GLITCH, + IPC_STG_MAX_NOTIFY +}; + +enum ipc_glitch_type { + IPC_GLITCH_UNDERRUN = 1, + IPC_GLITCH_DECODER_ERROR, + IPC_GLITCH_DOUBLED_WRITE_POS, + IPC_GLITCH_MAX +}; + +/* Debug Control */ +enum ipc_debug_operation { + IPC_DEBUG_ENABLE_LOG = 0, + IPC_DEBUG_DISABLE_LOG = 1, + IPC_DEBUG_REQUEST_LOG_DUMP = 2, + IPC_DEBUG_NOTIFY_LOG_DUMP = 3, + IPC_DEBUG_MAX_DEBUG_LOG +}; + +/* Firmware Ready */ +struct sst_hsw_ipc_fw_ready { + u32 inbox_offset; + u32 outbox_offset; + u32 inbox_size; + u32 outbox_size; + u32 fw_info_size; + u8 fw_info[1]; +} __attribute__((packed)); + +struct ipc_message { + struct list_head list; + u32 header; + + /* direction wrt host CPU */ + char tx_data[IPC_MAX_MAILBOX_BYTES]; + size_t tx_size; + char rx_data[IPC_MAX_MAILBOX_BYTES]; + size_t rx_size; + + wait_queue_head_t waitq; + bool pending; + bool complete; + bool wait; + int errno; +}; + +struct sst_hsw_stream; +struct sst_hsw; + +/* Stream infomation */ +struct sst_hsw_stream { + /* configuration */ + struct sst_hsw_ipc_stream_alloc_req request; + struct sst_hsw_ipc_stream_alloc_reply reply; + struct sst_hsw_ipc_stream_free_req free_req; + + /* Mixer info */ + u32 mute_volume[SST_HSW_NO_CHANNELS]; + u32 mute[SST_HSW_NO_CHANNELS]; + + /* runtime info */ + struct sst_hsw *hsw; + int host_id; + bool commited; + bool running; + + /* Notification work */ + struct work_struct notify_work; + u32 header; + + /* Position info from DSP */ + struct sst_hsw_ipc_stream_set_position wpos; + struct sst_hsw_ipc_stream_get_position rpos; + struct sst_hsw_ipc_stream_glitch_position glitch; + + /* Volume info */ + struct sst_hsw_ipc_volume_req vol_req; + + /* driver callback */ + u32 (*notify_position)(struct sst_hsw_stream *stream, void *data); + void *pdata; + + struct list_head node; +}; + +/* FW log ring information */ +struct sst_hsw_log_stream { + dma_addr_t dma_addr; + unsigned char *dma_area; + unsigned char *ring_descr; + int pages; + int size; + + /* Notification work */ + struct work_struct notify_work; + wait_queue_head_t readers_wait_q; + struct mutex rw_mutex; + + u32 last_pos; + u32 curr_pos; + u32 reader_pos; + + /* fw log config */ + u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS]; + + struct sst_hsw *hsw; +}; + +/* SST Haswell IPC data */ +struct sst_hsw { + struct device *dev; + struct sst_dsp *dsp; + struct platform_device *pdev_pcm; + + /* FW config */ + struct sst_hsw_ipc_fw_ready fw_ready; + struct sst_hsw_ipc_fw_version version; + struct sst_module *scratch; + bool fw_done; + + /* stream */ + struct list_head stream_list; + + /* global mixer */ + struct sst_hsw_ipc_stream_info_reply mixer_info; + enum sst_hsw_volume_curve curve_type; + u32 curve_duration; + u32 mute[SST_HSW_NO_CHANNELS]; + u32 mute_volume[SST_HSW_NO_CHANNELS]; + + /* DX */ + struct sst_hsw_ipc_dx_reply dx; + + /* boot */ + wait_queue_head_t boot_wait; + bool boot_complete; + bool shutdown; + + /* IPC messaging */ + struct list_head tx_list; + struct list_head rx_list; + struct list_head empty_list; + wait_queue_head_t wait_txq; + struct task_struct *tx_thread; + struct kthread_worker kworker; + struct kthread_work kwork; + bool pending; + struct ipc_message *msg; + + /* FW log stream */ + struct sst_hsw_log_stream log_stream; +}; + +#define CREATE_TRACE_POINTS +#include <trace/events/hswadsp.h> + +static inline u32 msg_get_global_type(u32 msg) +{ + return (msg & IPC_GLB_TYPE_MASK) >> IPC_GLB_TYPE_SHIFT; +} + +static inline u32 msg_get_global_reply(u32 msg) +{ + return (msg & IPC_GLB_REPLY_MASK) >> IPC_GLB_REPLY_SHIFT; +} + +static inline u32 msg_get_stream_type(u32 msg) +{ + return (msg & IPC_STR_TYPE_MASK) >> IPC_STR_TYPE_SHIFT; +} + +static inline u32 msg_get_stage_type(u32 msg) +{ + return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT; +} + +static inline u32 msg_set_stage_type(u32 msg, u32 type) +{ + return (msg & ~IPC_STG_TYPE_MASK) + + (type << IPC_STG_TYPE_SHIFT); +} + +static inline u32 msg_get_stream_id(u32 msg) +{ + return (msg & IPC_STR_ID_MASK) >> IPC_STR_ID_SHIFT; +} + +static inline u32 msg_get_notify_reason(u32 msg) +{ + return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT; +} + +u32 create_channel_map(enum sst_hsw_channel_config config) +{ + switch (config) { + case SST_HSW_CHANNEL_CONFIG_MONO: + return (0xFFFFFFF0 | SST_HSW_CHANNEL_CENTER); + case SST_HSW_CHANNEL_CONFIG_STEREO: + return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT + | (SST_HSW_CHANNEL_RIGHT << 4)); + case SST_HSW_CHANNEL_CONFIG_2_POINT_1: + return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT + | (SST_HSW_CHANNEL_RIGHT << 4) + | (SST_HSW_CHANNEL_LFE << 8 )); + case SST_HSW_CHANNEL_CONFIG_3_POINT_0: + return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT + | (SST_HSW_CHANNEL_CENTER << 4) + | (SST_HSW_CHANNEL_RIGHT << 8)); + case SST_HSW_CHANNEL_CONFIG_3_POINT_1: + return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT + | (SST_HSW_CHANNEL_CENTER << 4) + | (SST_HSW_CHANNEL_RIGHT << 8) + | (SST_HSW_CHANNEL_LFE << 12)); + case SST_HSW_CHANNEL_CONFIG_QUATRO: + return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT + | (SST_HSW_CHANNEL_RIGHT << 4) + | (SST_HSW_CHANNEL_LEFT_SURROUND << 8) + | (SST_HSW_CHANNEL_RIGHT_SURROUND << 12)); + case SST_HSW_CHANNEL_CONFIG_4_POINT_0: + return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT + | (SST_HSW_CHANNEL_CENTER << 4) + | (SST_HSW_CHANNEL_RIGHT << 8) + | (SST_HSW_CHANNEL_CENTER_SURROUND << 12)); + case SST_HSW_CHANNEL_CONFIG_5_POINT_0: + return (0xFFF00000 | SST_HSW_CHANNEL_LEFT + | (SST_HSW_CHANNEL_CENTER << 4) + | (SST_HSW_CHANNEL_RIGHT << 8) + | (SST_HSW_CHANNEL_LEFT_SURROUND << 12) + | (SST_HSW_CHANNEL_RIGHT_SURROUND << 16)); + case SST_HSW_CHANNEL_CONFIG_5_POINT_1: + return (0xFF000000 | SST_HSW_CHANNEL_CENTER + | (SST_HSW_CHANNEL_LEFT << 4) + | (SST_HSW_CHANNEL_RIGHT << 8) + | (SST_HSW_CHANNEL_LEFT_SURROUND << 12) + | (SST_HSW_CHANNEL_RIGHT_SURROUND << 16) + | (SST_HSW_CHANNEL_LFE << 20)); + case SST_HSW_CHANNEL_CONFIG_DUAL_MONO: + return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT + | (SST_HSW_CHANNEL_LEFT << 4)); + default: + return 0xFFFFFFFF; + } +} + +static struct sst_hsw_stream *get_stream_by_id(struct sst_hsw *hsw, + int stream_id) +{ + struct sst_hsw_stream *stream; + + list_for_each_entry(stream, &hsw->stream_list, node) { + if (stream->reply.stream_hw_id == stream_id) + return stream; + } + + return NULL; +} + +static void ipc_shim_dbg(struct sst_hsw *hsw, const char *text) +{ + struct sst_dsp *sst = hsw->dsp; + u32 isr, ipcd, imrx, ipcx; + + ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX); + isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX); + ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD); + imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX); + + dev_err(hsw->dev, "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n", + text, ipcx, isr, ipcd, imrx); +} + +/* locks held by caller */ +static struct ipc_message *msg_get_empty(struct sst_hsw *hsw) +{ + struct ipc_message *msg = NULL; + + if (!list_empty(&hsw->empty_list)) { + msg = list_first_entry(&hsw->empty_list, struct ipc_message, + list); + list_del(&msg->list); + } + + return msg; +} + +static void ipc_tx_msgs(struct kthread_work *work) +{ + struct sst_hsw *hsw = + container_of(work, struct sst_hsw, kwork); + struct ipc_message *msg; + unsigned long flags; + u32 ipcx; + + spin_lock_irqsave(&hsw->dsp->spinlock, flags); + + if (list_empty(&hsw->tx_list) || hsw->pending) { + spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); + return; + } + + /* if the DSP is busy we will TX messages after IRQ */ + ipcx = sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX); + if (ipcx & SST_IPCX_BUSY) { + spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); + return; + } + + msg = list_first_entry(&hsw->tx_list, struct ipc_message, list); + + list_move(&msg->list, &hsw->rx_list); + + /* send the message */ + sst_dsp_outbox_write(hsw->dsp, msg->tx_data, msg->tx_size); + sst_dsp_ipc_msg_tx(hsw->dsp, msg->header | SST_IPCX_BUSY); + + spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); +} + +/* locks held by caller */ +static void tx_msg_reply_complete(struct sst_hsw *hsw, struct ipc_message *msg) +{ + msg->complete = true; + trace_ipc_reply("completed", msg->header); + + if (!msg->wait) + list_add_tail(&msg->list, &hsw->empty_list); + else + wake_up(&msg->waitq); +} + +static int tx_wait_done(struct sst_hsw *hsw, struct ipc_message *msg, + void *rx_data) +{ + unsigned long flags; + int ret; + + /* wait for DSP completion (in all cases atm inc pending) */ + ret = wait_event_timeout(msg->waitq, msg->complete, + msecs_to_jiffies(IPC_TIMEOUT_MSECS)); + + spin_lock_irqsave(&hsw->dsp->spinlock, flags); + if (ret == 0) { + ipc_shim_dbg(hsw, "message timeout"); + + trace_ipc_error("error message timeout for", msg->header); + ret = -ETIMEDOUT; + } else { + + /* copy the data returned from DSP */ + if (msg->rx_size) + memcpy(rx_data, msg->rx_data, msg->rx_size); + ret = msg->errno; + } + + list_add_tail(&msg->list, &hsw->empty_list); + spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); + return ret; +} + +static int ipc_tx_message(struct sst_hsw *hsw, u32 header, void *tx_data, + size_t tx_bytes, void *rx_data, size_t rx_bytes, int wait) +{ + struct ipc_message *msg; + unsigned long flags; + + spin_lock_irqsave(&hsw->dsp->spinlock, flags); + + msg = msg_get_empty(hsw); + if (msg == NULL) { + spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); + return -EBUSY; + } + + if (tx_bytes) + memcpy(msg->tx_data, tx_data, tx_bytes); + + msg->header = header; + msg->tx_size = tx_bytes; + msg->rx_size = rx_bytes; + msg->wait = wait; + msg->errno = 0; + msg->pending = false; + msg->complete = false; + + list_add_tail(&msg->list, &hsw->tx_list); + spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); + + queue_kthread_work(&hsw->kworker, &hsw->kwork); + + if (wait) + return tx_wait_done(hsw, msg, rx_data); + else + return 0; +} + +static inline int ipc_tx_message_wait(struct sst_hsw *hsw, u32 header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) +{ + return ipc_tx_message(hsw, header, tx_data, tx_bytes, rx_data, + rx_bytes, 1); +} + +static inline int ipc_tx_message_nowait(struct sst_hsw *hsw, u32 header, + void *tx_data, size_t tx_bytes) +{ + return ipc_tx_message(hsw, header, tx_data, tx_bytes, NULL, 0, 0); +} + +static void hsw_fw_ready(struct sst_hsw *hsw, u32 header) +{ + struct sst_hsw_ipc_fw_ready fw_ready; + u32 offset; + + offset = (header & 0x1FFFFFFF) << 3; + + dev_dbg(hsw->dev, "ipc: DSP is ready 0x%8.8x offset %d\n", + header, offset); + + /* copy data from the DSP FW ready offset */ + sst_dsp_read(hsw->dsp, &fw_ready, offset, sizeof(fw_ready)); + + sst_dsp_mailbox_init(hsw->dsp, fw_ready.inbox_offset, + fw_ready.inbox_size, fw_ready.outbox_offset, + fw_ready.outbox_size); + + hsw->boot_complete = true; + wake_up(&hsw->boot_wait); + + dev_dbg(hsw->dev, " mailbox upstream 0x%x - size 0x%x\n", + fw_ready.inbox_offset, fw_ready.inbox_size); + dev_dbg(hsw->dev, " mailbox downstream 0x%x - size 0x%x\n", + fw_ready.outbox_offset, fw_ready.outbox_size); +} + +static void hsw_notification_work(struct work_struct *work) +{ + struct sst_hsw_stream *stream = container_of(work, + struct sst_hsw_stream, notify_work); + struct sst_hsw_ipc_stream_glitch_position *glitch = &stream->glitch; + struct sst_hsw_ipc_stream_get_position *pos = &stream->rpos; + struct sst_hsw *hsw = stream->hsw; + u32 reason; + + reason = msg_get_notify_reason(stream->header); + + switch (reason) { + case IPC_STG_GLITCH: + trace_ipc_notification("DSP stream under/overrun", + stream->reply.stream_hw_id); + sst_dsp_inbox_read(hsw->dsp, glitch, sizeof(*glitch)); + + dev_err(hsw->dev, "glitch %d pos 0x%x write pos 0x%x\n", + glitch->glitch_type, glitch->present_pos, + glitch->write_pos); + break; + + case IPC_POSITION_CHANGED: + trace_ipc_notification("DSP stream position changed for", + stream->reply.stream_hw_id); + sst_dsp_inbox_read(hsw->dsp, pos, sizeof(pos)); + + if (stream->notify_position) + stream->notify_position(stream, stream->pdata); + + break; + default: + dev_err(hsw->dev, "error: unknown notification 0x%x\n", + stream->header); + break; + } + + /* tell DSP that notification has been handled */ + sst_dsp_shim_update_bits_unlocked(hsw->dsp, SST_IPCD, + SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE); + + /* unmask busy interrupt */ + sst_dsp_shim_update_bits_unlocked(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0); +} + +static struct ipc_message *reply_find_msg(struct sst_hsw *hsw, u32 header) +{ + struct ipc_message *msg; + + /* clear reply bits & status bits */ + header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK); + + if (list_empty(&hsw->rx_list)) { + dev_err(hsw->dev, "error: rx list empty but received 0x%x\n", + header); + return NULL; + } + + list_for_each_entry(msg, &hsw->rx_list, list) { + if (msg->header == header) + return msg; + } + + return NULL; +} + +static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg) +{ + struct sst_hsw_stream *stream; + u32 header = msg->header & ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK); + u32 stream_id = msg_get_stream_id(header); + u32 stream_msg = msg_get_stream_type(header); + + stream = get_stream_by_id(hsw, stream_id); + if (stream == NULL) + return; + + switch (stream_msg) { + case IPC_STR_STAGE_MESSAGE: + case IPC_STR_NOTIFICATION: + case IPC_STR_RESET: + break; + case IPC_STR_PAUSE: + stream->running = false; + trace_ipc_notification("stream paused", + stream->reply.stream_hw_id); + break; + case IPC_STR_RESUME: + stream->running = true; + trace_ipc_notification("stream running", + stream->reply.stream_hw_id); + break; + } +} + +static int hsw_process_reply(struct sst_hsw *hsw, u32 header) +{ + struct ipc_message *msg; + u32 reply = msg_get_global_reply(header); + + trace_ipc_reply("processing -->", header); + + msg = reply_find_msg(hsw, header); + if (msg == NULL) { + trace_ipc_error("error: can't find message header", header); + return -EIO; + } + + /* first process the header */ + switch (reply) { + case IPC_GLB_REPLY_PENDING: + trace_ipc_pending_reply("received", header); + msg->pending = true; + hsw->pending = true; + return 1; + case IPC_GLB_REPLY_SUCCESS: + if (msg->pending) { + trace_ipc_pending_reply("completed", header); + sst_dsp_inbox_read(hsw->dsp, msg->rx_data, + msg->rx_size); + hsw->pending = false; + } else { + /* copy data from the DSP */ + sst_dsp_outbox_read(hsw->dsp, msg->rx_data, + msg->rx_size); + } + break; + /* these will be rare - but useful for debug */ + case IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE: + trace_ipc_error("error: unknown message type", header); + msg->errno = -EBADMSG; + break; + case IPC_GLB_REPLY_OUT_OF_RESOURCES: + trace_ipc_error("error: out of resources", header); + msg->errno = -ENOMEM; + break; + case IPC_GLB_REPLY_BUSY: + trace_ipc_error("error: reply busy", header); + msg->errno = -EBUSY; + break; + case IPC_GLB_REPLY_FAILURE: + trace_ipc_error("error: reply failure", header); + msg->errno = -EINVAL; + break; + case IPC_GLB_REPLY_STAGE_UNINITIALIZED: + trace_ipc_error("error: stage uninitialized", header); + msg->errno = -EINVAL; + break; + case IPC_GLB_REPLY_NOT_FOUND: + trace_ipc_error("error: reply not found", header); + msg->errno = -EINVAL; + break; + case IPC_GLB_REPLY_SOURCE_NOT_STARTED: + trace_ipc_error("error: source not started", header); + msg->errno = -EINVAL; + break; + case IPC_GLB_REPLY_INVALID_REQUEST: + trace_ipc_error("error: invalid request", header); + msg->errno = -EINVAL; + break; + case IPC_GLB_REPLY_ERROR_INVALID_PARAM: + trace_ipc_error("error: invalid parameter", header); + msg->errno = -EINVAL; + break; + default: + trace_ipc_error("error: unknown reply", header); + msg->errno = -EINVAL; + break; + } + + /* update any stream states */ + hsw_stream_update(hsw, msg); + + /* wake up and return the error if we have waiters on this message ? */ + list_del(&msg->list); + tx_msg_reply_complete(hsw, msg); + + return 1; +} + +static int hsw_stream_message(struct sst_hsw *hsw, u32 header) +{ + u32 stream_msg, stream_id, stage_type; + struct sst_hsw_stream *stream; + int handled = 0; + + stream_msg = msg_get_stream_type(header); + stream_id = msg_get_stream_id(header); + stage_type = msg_get_stage_type(header); + + stream = get_stream_by_id(hsw, stream_id); + if (stream == NULL) + return handled; + + stream->header = header; + + switch (stream_msg) { + case IPC_STR_STAGE_MESSAGE: + dev_err(hsw->dev, "error: stage msg not implemented 0x%8.8x\n", + header); + break; + case IPC_STR_NOTIFICATION: + schedule_work(&stream->notify_work); + break; + default: + /* handle pending message complete request */ + handled = hsw_process_reply(hsw, header); + break; + } + + return handled; +} + +static int hsw_log_message(struct sst_hsw *hsw, u32 header) +{ + u32 operation = (header & IPC_LOG_OP_MASK) >> IPC_LOG_OP_SHIFT; + struct sst_hsw_log_stream *stream = &hsw->log_stream; + int ret = 1; + + if (operation != IPC_DEBUG_REQUEST_LOG_DUMP) { + dev_err(hsw->dev, + "error: log msg not implemented 0x%8.8x\n", header); + return 0; + } + + mutex_lock(&stream->rw_mutex); + stream->last_pos = stream->curr_pos; + sst_dsp_inbox_read( + hsw->dsp, &stream->curr_pos, sizeof(stream->curr_pos)); + mutex_unlock(&stream->rw_mutex); + + schedule_work(&stream->notify_work); + + return ret; +} + +static int hsw_process_notification(struct sst_hsw *hsw) +{ + struct sst_dsp *sst = hsw->dsp; + u32 type, header; + int handled = 1; + + header = sst_dsp_shim_read_unlocked(sst, SST_IPCD); + type = msg_get_global_type(header); + + trace_ipc_request("processing -->", header); + + /* FW Ready is a special case */ + if (!hsw->boot_complete && header & IPC_FW_READY) { + hsw_fw_ready(hsw, header); + return handled; + } + + switch (type) { + case IPC_GLB_GET_FW_VERSION: + case IPC_GLB_ALLOCATE_STREAM: + case IPC_GLB_FREE_STREAM: + case IPC_GLB_GET_FW_CAPABILITIES: + case IPC_GLB_REQUEST_DUMP: + case IPC_GLB_GET_DEVICE_FORMATS: + case IPC_GLB_SET_DEVICE_FORMATS: + case IPC_GLB_ENTER_DX_STATE: + case IPC_GLB_GET_MIXER_STREAM_INFO: + case IPC_GLB_MAX_IPC_MESSAGE_TYPE: + case IPC_GLB_RESTORE_CONTEXT: + case IPC_GLB_SHORT_REPLY: + dev_err(hsw->dev, "error: message type %d header 0x%x\n", + type, header); + break; + case IPC_GLB_STREAM_MESSAGE: + handled = hsw_stream_message(hsw, header); + break; + case IPC_GLB_DEBUG_LOG_MESSAGE: + handled = hsw_log_message(hsw, header); + break; + default: + dev_err(hsw->dev, "error: unexpected type %d hdr 0x%8.8x\n", + type, header); + break; + } + + return handled; +} + +static irqreturn_t hsw_irq_thread(int irq, void *context) +{ + struct sst_dsp *sst = (struct sst_dsp *) context; + struct sst_hsw *hsw = sst_dsp_get_thread_context(sst); + u32 ipcx, ipcd; + int handled; + unsigned long flags; + + spin_lock_irqsave(&sst->spinlock, flags); + + ipcx = sst_dsp_ipc_msg_rx(hsw->dsp); + ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD); + + /* reply message from DSP */ + if (ipcx & SST_IPCX_DONE) { + + /* Handle Immediate reply from DSP Core */ + handled = hsw_process_reply(hsw, ipcx); + + if (handled > 0) { + /* clear DONE bit - tell DSP we have completed */ + sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX, + SST_IPCX_DONE, 0); + + /* unmask Done interrupt */ + sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, + SST_IMRX_DONE, 0); + } + } + + /* new message from DSP */ + if (ipcd & SST_IPCD_BUSY) { + + /* Handle Notification and Delayed reply from DSP Core */ + handled = hsw_process_notification(hsw); + + /* clear BUSY bit and set DONE bit - accept new messages */ + if (handled > 0) { + sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD, + SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE); + + /* unmask busy interrupt */ + sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, + SST_IMRX_BUSY, 0); + } + } + + spin_unlock_irqrestore(&sst->spinlock, flags); + + /* continue to send any remaining messages... */ + queue_kthread_work(&hsw->kworker, &hsw->kwork); + + return IRQ_HANDLED; +} + +int sst_hsw_fw_get_version(struct sst_hsw *hsw, + struct sst_hsw_ipc_fw_version *version) +{ + int ret; + + ret = ipc_tx_message_wait(hsw, IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION), + NULL, 0, version, sizeof(*version)); + if (ret < 0) + dev_err(hsw->dev, "error: get version failed\n"); + + return ret; +} + +/* Mixer Controls */ +int sst_hsw_stream_mute(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + u32 stage_id, u32 channel) +{ + int ret; + + ret = sst_hsw_stream_get_volume(hsw, stream, stage_id, channel, + &stream->mute_volume[channel]); + if (ret < 0) + return ret; + + ret = sst_hsw_stream_set_volume(hsw, stream, stage_id, channel, 0); + if (ret < 0) { + dev_err(hsw->dev, "error: can't unmute stream %d channel %d\n", + stream->reply.stream_hw_id, channel); + return ret; + } + + stream->mute[channel] = 1; + return 0; +} + +int sst_hsw_stream_unmute(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + u32 stage_id, u32 channel) + +{ + int ret; + + stream->mute[channel] = 0; + ret = sst_hsw_stream_set_volume(hsw, stream, stage_id, channel, + stream->mute_volume[channel]); + if (ret < 0) { + dev_err(hsw->dev, "error: can't unmute stream %d channel %d\n", + stream->reply.stream_hw_id, channel); + return ret; + } + + return 0; +} + +int sst_hsw_stream_get_volume(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + u32 stage_id, u32 channel, u32 *volume) +{ + if (channel > 1) + return -EINVAL; + + sst_dsp_read(hsw->dsp, volume, + stream->reply.volume_register_address[channel], sizeof(volume)); + + return 0; +} + +int sst_hsw_stream_set_volume_curve(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u64 curve_duration, + enum sst_hsw_volume_curve curve) +{ + /* curve duration in steps of 100ns */ + stream->vol_req.curve_duration = curve_duration; + stream->vol_req.curve_type = curve; + + return 0; +} + +/* stream volume */ +int sst_hsw_stream_set_volume(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume) +{ + struct sst_hsw_ipc_volume_req *req; + u32 header; + int ret; + + trace_ipc_request("set stream volume", stream->reply.stream_hw_id); + + if (channel > 1) + return -EINVAL; + + if (stream->mute[channel]) { + stream->mute_volume[channel] = volume; + return 0; + } + + header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | + IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE); + header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT); + header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT); + header |= (stage_id << IPC_STG_ID_SHIFT); + + req = &stream->vol_req; + req->channel = channel; + req->target_volume = volume; + + ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0); + if (ret < 0) { + dev_err(hsw->dev, "error: set stream volume failed\n"); + return ret; + } + + return 0; +} + +int sst_hsw_mixer_mute(struct sst_hsw *hsw, u32 stage_id, u32 channel) +{ + int ret; + + ret = sst_hsw_mixer_get_volume(hsw, stage_id, channel, + &hsw->mute_volume[channel]); + if (ret < 0) + return ret; + + ret = sst_hsw_mixer_set_volume(hsw, stage_id, channel, 0); + if (ret < 0) { + dev_err(hsw->dev, "error: failed to unmute mixer channel %d\n", + channel); + return ret; + } + + hsw->mute[channel] = 1; + return 0; +} + +int sst_hsw_mixer_unmute(struct sst_hsw *hsw, u32 stage_id, u32 channel) +{ + int ret; + + ret = sst_hsw_mixer_set_volume(hsw, stage_id, channel, + hsw->mixer_info.volume_register_address[channel]); + if (ret < 0) { + dev_err(hsw->dev, "error: failed to unmute mixer channel %d\n", + channel); + return ret; + } + + hsw->mute[channel] = 0; + return 0; +} + +int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, + u32 *volume) +{ + if (channel > 1) + return -EINVAL; + + sst_dsp_read(hsw->dsp, volume, + hsw->mixer_info.volume_register_address[channel], + sizeof(*volume)); + + return 0; +} + +int sst_hsw_mixer_set_volume_curve(struct sst_hsw *hsw, + u64 curve_duration, enum sst_hsw_volume_curve curve) +{ + /* curve duration in steps of 100ns */ + hsw->curve_duration = curve_duration; + hsw->curve_type = curve; + + return 0; +} + +/* global mixer volume */ +int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, + u32 volume) +{ + struct sst_hsw_ipc_volume_req req; + u32 header; + int ret; + + trace_ipc_request("set mixer volume", volume); + + /* set both at same time ? */ + if (channel == 2) { + if (hsw->mute[0] && hsw->mute[1]) { + hsw->mute_volume[0] = hsw->mute_volume[1] = volume; + return 0; + } else if (hsw->mute[0]) + req.channel = 1; + else if (hsw->mute[1]) + req.channel = 0; + else + req.channel = 0xffffffff; + } else { + /* set only 1 channel */ + if (hsw->mute[channel]) { + hsw->mute_volume[channel] = volume; + return 0; + } + req.channel = channel; + } + + header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | + IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE); + header |= (hsw->mixer_info.mixer_hw_id << IPC_STR_ID_SHIFT); + header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT); + header |= (stage_id << IPC_STG_ID_SHIFT); + + req.curve_duration = hsw->curve_duration; + req.curve_type = hsw->curve_type; + req.target_volume = volume; + + ret = ipc_tx_message_wait(hsw, header, &req, sizeof(req), NULL, 0); + if (ret < 0) { + dev_err(hsw->dev, "error: set mixer volume failed\n"); + return ret; + } + + return 0; +} + +/* Stream API */ +struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id, + u32 (*notify_position)(struct sst_hsw_stream *stream, void *data), + void *data) +{ + struct sst_hsw_stream *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (stream == NULL) + return NULL; + + list_add(&stream->node, &hsw->stream_list); + stream->notify_position = notify_position; + stream->pdata = data; + stream->hsw = hsw; + stream->host_id = id; + + /* work to process notification messages */ + INIT_WORK(&stream->notify_work, hsw_notification_work); + + return stream; +} + +int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream) +{ + u32 header; + int ret = 0; + + /* dont free DSP streams that are not commited */ + if (!stream->commited) + goto out; + + trace_ipc_request("stream free", stream->host_id); + + stream->free_req.stream_id = stream->reply.stream_hw_id; + header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM); + + ret = ipc_tx_message_wait(hsw, header, &stream->free_req, + sizeof(stream->free_req), NULL, 0); + if (ret < 0) { + dev_err(hsw->dev, "error: free stream %d failed\n", + stream->free_req.stream_id); + return -EAGAIN; + } + + trace_hsw_stream_free_req(stream, &stream->free_req); + +out: + list_del(&stream->node); + kfree(stream); + + return ret; +} + +int sst_hsw_stream_set_bits(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, enum sst_hsw_bitdepth bits) +{ + if (stream->commited) { + dev_err(hsw->dev, "error: stream committed for set bits\n"); + return -EINVAL; + } + + stream->request.format.bitdepth = bits; + return 0; +} + +int sst_hsw_stream_set_channels(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, int channels) +{ + if (stream->commited) { + dev_err(hsw->dev, "error: stream committed for set channels\n"); + return -EINVAL; + } + + /* stereo is only supported atm */ + if (channels != 2) + return -EINVAL; + + stream->request.format.ch_num = channels; + return 0; +} + +int sst_hsw_stream_set_rate(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, int rate) +{ + if (stream->commited) { + dev_err(hsw->dev, "error: stream committed for set rate\n"); + return -EINVAL; + } + + stream->request.format.frequency = rate; + return 0; +} + +int sst_hsw_stream_set_map_config(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 map, + enum sst_hsw_channel_config config) +{ + if (stream->commited) { + dev_err(hsw->dev, "error: stream committed for set map\n"); + return -EINVAL; + } + + stream->request.format.map = map; + stream->request.format.config = config; + return 0; +} + +int sst_hsw_stream_set_style(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, enum sst_hsw_interleaving style) +{ + if (stream->commited) { + dev_err(hsw->dev, "error: stream committed for set style\n"); + return -EINVAL; + } + + stream->request.format.style = style; + return 0; +} + +int sst_hsw_stream_set_valid(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 bits) +{ + if (stream->commited) { + dev_err(hsw->dev, "error: stream committed for set valid bits\n"); + return -EINVAL; + } + + stream->request.format.valid_bit = bits; + return 0; +} + +/* Stream Configuration */ +int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + enum sst_hsw_stream_path_id path_id, + enum sst_hsw_stream_type stream_type, + enum sst_hsw_stream_format format_id) +{ + if (stream->commited) { + dev_err(hsw->dev, "error: stream committed for set format\n"); + return -EINVAL; + } + + stream->request.path_id = path_id; + stream->request.stream_type = stream_type; + stream->request.format_id = format_id; + + trace_hsw_stream_alloc_request(stream, &stream->request); + + return 0; +} + +int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + u32 ring_pt_address, u32 num_pages, + u32 ring_size, u32 ring_offset, u32 ring_first_pfn) +{ + if (stream->commited) { + dev_err(hsw->dev, "error: stream committed for buffer\n"); + return -EINVAL; + } + + stream->request.ringinfo.ring_pt_address = ring_pt_address; + stream->request.ringinfo.num_pages = num_pages; + stream->request.ringinfo.ring_size = ring_size; + stream->request.ringinfo.ring_offset = ring_offset; + stream->request.ringinfo.ring_first_pfn = ring_first_pfn; + + trace_hsw_stream_buffer(stream); + + return 0; +} + +int sst_hsw_stream_set_module_info(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id, + u32 entry_point) +{ + struct sst_hsw_module_map *map = &stream->request.map; + + if (stream->commited) { + dev_err(hsw->dev, "error: stream committed for set module\n"); + return -EINVAL; + } + + /* only support initial module atm */ + map->module_entries_count = 1; + map->module_entries[0].module_id = module_id; + map->module_entries[0].entry_point = entry_point; + + return 0; +} + +int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 offset, u32 size) +{ + if (stream->commited) { + dev_err(hsw->dev, "error: stream committed for set pmem\n"); + return -EINVAL; + } + + stream->request.persistent_mem.offset = offset; + stream->request.persistent_mem.size = size; + + return 0; +} + +int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 offset, u32 size) +{ + if (stream->commited) { + dev_err(hsw->dev, "error: stream committed for set smem\n"); + return -EINVAL; + } + + stream->request.scratch_mem.offset = offset; + stream->request.scratch_mem.size = size; + + return 0; +} + +int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) +{ + struct sst_hsw_ipc_stream_alloc_req *str_req = &stream->request; + struct sst_hsw_ipc_stream_alloc_reply *reply = &stream->reply; + u32 header; + int ret; + + trace_ipc_request("stream alloc", stream->host_id); + + header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM); + + ret = ipc_tx_message_wait(hsw, header, str_req, sizeof(*str_req), + reply, sizeof(*reply)); + if (ret < 0) { + dev_err(hsw->dev, "error: stream commit failed\n"); + return ret; + } + + stream->commited = 1; + trace_hsw_stream_alloc_reply(stream); + + return 0; +} + +/* Stream Information - these calls could be inline but we want the IPC + ABI to be opaque to client PCM drivers to cope with any future ABI changes */ +int sst_hsw_stream_get_hw_id(struct sst_hsw *hsw, + struct sst_hsw_stream *stream) +{ + return stream->reply.stream_hw_id; +} + +int sst_hsw_stream_get_mixer_id(struct sst_hsw *hsw, + struct sst_hsw_stream *stream) +{ + return stream->reply.mixer_hw_id; +} + +u32 sst_hsw_stream_get_read_reg(struct sst_hsw *hsw, + struct sst_hsw_stream *stream) +{ + return stream->reply.read_position_register_address; +} + +u32 sst_hsw_stream_get_pointer_reg(struct sst_hsw *hsw, + struct sst_hsw_stream *stream) +{ + return stream->reply.presentation_position_register_address; +} + +u32 sst_hsw_stream_get_peak_reg(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 channel) +{ + if (channel >= 2) + return 0; + + return stream->reply.peak_meter_register_address[channel]; +} + +u32 sst_hsw_stream_get_vol_reg(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 channel) +{ + if (channel >= 2) + return 0; + + return stream->reply.volume_register_address[channel]; +} + +int sst_hsw_mixer_get_info(struct sst_hsw *hsw) +{ + struct sst_hsw_ipc_stream_info_reply *reply; + u32 header; + int ret; + + reply = &hsw->mixer_info; + header = IPC_GLB_TYPE(IPC_GLB_GET_MIXER_STREAM_INFO); + + trace_ipc_request("get global mixer info", 0); + + ret = ipc_tx_message_wait(hsw, header, NULL, 0, reply, sizeof(*reply)); + if (ret < 0) { + dev_err(hsw->dev, "error: get stream info failed\n"); + return ret; + } + + trace_hsw_mixer_info_reply(reply); + + return 0; +} + +/* Send stream command */ +static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type, + int stream_id, int wait) +{ + u32 header; + + header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | IPC_STR_TYPE(type); + header |= (stream_id << IPC_STR_ID_SHIFT); + + if (wait) + return ipc_tx_message_wait(hsw, header, NULL, 0, NULL, 0); + else + return ipc_tx_message_nowait(hsw, header, NULL, 0); +} + +/* Stream ALSA trigger operations */ +int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + int wait) +{ + int ret; + + trace_ipc_request("stream pause", stream->reply.stream_hw_id); + + ret = sst_hsw_stream_operations(hsw, IPC_STR_PAUSE, + stream->reply.stream_hw_id, wait); + if (ret < 0) + dev_err(hsw->dev, "error: failed to pause stream %d\n", + stream->reply.stream_hw_id); + + return ret; +} + +int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + int wait) +{ + int ret; + + trace_ipc_request("stream resume", stream->reply.stream_hw_id); + + ret = sst_hsw_stream_operations(hsw, IPC_STR_RESUME, + stream->reply.stream_hw_id, wait); + if (ret < 0) + dev_err(hsw->dev, "error: failed to resume stream %d\n", + stream->reply.stream_hw_id); + + return ret; +} + +int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream) +{ + int ret, tries = 10; + + /* dont reset streams that are not commited */ + if (!stream->commited) + return 0; + + /* wait for pause to complete before we reset the stream */ + while (stream->running && tries--) + msleep(1); + if (!tries) { + dev_err(hsw->dev, "error: reset stream %d still running\n", + stream->reply.stream_hw_id); + return -EINVAL; + } + + trace_ipc_request("stream reset", stream->reply.stream_hw_id); + + ret = sst_hsw_stream_operations(hsw, IPC_STR_RESET, + stream->reply.stream_hw_id, 1); + if (ret < 0) + dev_err(hsw->dev, "error: failed to reset stream %d\n", + stream->reply.stream_hw_id); + return ret; +} + +/* Stream pointer positions */ +int sst_hsw_get_dsp_position(struct sst_hsw *hsw, + struct sst_hsw_stream *stream) +{ + return stream->rpos.position; +} + +int sst_hsw_stream_set_write_position(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 stage_id, u32 position) +{ + u32 header; + int ret; + + trace_stream_write_position(stream->reply.stream_hw_id, position); + + header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | + IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE); + header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT); + header |= (IPC_STG_SET_WRITE_POSITION << IPC_STG_TYPE_SHIFT); + header |= (stage_id << IPC_STG_ID_SHIFT); + stream->wpos.position = position; + + ret = ipc_tx_message_nowait(hsw, header, &stream->wpos, + sizeof(stream->wpos)); + if (ret < 0) + dev_err(hsw->dev, "error: stream %d set position %d failed\n", + stream->reply.stream_hw_id, position); + + return ret; +} + +/* physical BE config */ +int sst_hsw_device_set_config(struct sst_hsw *hsw, + enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk, + enum sst_hsw_device_mode mode, u32 clock_divider) +{ + struct sst_hsw_ipc_device_config_req config; + u32 header; + int ret; + + trace_ipc_request("set device config", dev); + + config.ssp_interface = dev; + config.clock_frequency = mclk; + config.mode = mode; + config.clock_divider = clock_divider; + + trace_hsw_device_config_req(&config); + + header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS); + + ret = ipc_tx_message_wait(hsw, header, &config, sizeof(config), + NULL, 0); + if (ret < 0) + dev_err(hsw->dev, "error: set device formats failed\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(sst_hsw_device_set_config); + +/* DX Config */ +int sst_hsw_dx_set_state(struct sst_hsw *hsw, + enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx) +{ + u32 header, state_; + int ret; + + header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE); + state_ = state; + + trace_ipc_request("PM enter Dx state", state); + + ret = ipc_tx_message_wait(hsw, header, &state_, sizeof(state_), + dx, sizeof(dx)); + if (ret < 0) { + dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state); + return ret; + } + + dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n", + dx->entries_no, state); + + memcpy(&hsw->dx, dx, sizeof(*dx)); + return 0; +} + +/* Used to save state into hsw->dx_reply */ +int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item, + u32 *offset, u32 *size, u32 *source) +{ + struct sst_hsw_ipc_dx_memory_item *dx_mem; + struct sst_hsw_ipc_dx_reply *dx_reply; + int entry_no; + + dx_reply = &hsw->dx; + entry_no = dx_reply->entries_no; + + trace_ipc_request("PM get Dx state", entry_no); + + if (item >= entry_no) + return -EINVAL; + + dx_mem = &dx_reply->mem_info[item]; + *offset = dx_mem->offset; + *size = dx_mem->size; + *source = dx_mem->source; + + return 0; +} + +static int msg_empty_list_init(struct sst_hsw *hsw) +{ + int i; + + hsw->msg = kzalloc(sizeof(struct ipc_message) * + IPC_EMPTY_LIST_SIZE, GFP_KERNEL); + if (hsw->msg == NULL) + return -ENOMEM; + + for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + init_waitqueue_head(&hsw->msg[i].waitq); + list_add(&hsw->msg[i].list, &hsw->empty_list); + } + + return 0; +} + +void sst_hsw_set_scratch_module(struct sst_hsw *hsw, + struct sst_module *scratch) +{ + hsw->scratch = scratch; +} + +struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw) +{ + return hsw->dsp; +} + +static struct sst_dsp_device hsw_dev = { + .thread = hsw_irq_thread, + .ops = &haswell_ops, +}; + +int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) +{ + struct sst_hsw_ipc_fw_version version; + struct sst_hsw *hsw; + struct sst_fw *hsw_sst_fw; + int ret; + + dev_dbg(dev, "initialising Audio DSP IPC\n"); + + hsw = devm_kzalloc(dev, sizeof(*hsw), GFP_KERNEL); + if (hsw == NULL) + return -ENOMEM; + + hsw->dev = dev; + INIT_LIST_HEAD(&hsw->stream_list); + INIT_LIST_HEAD(&hsw->tx_list); + INIT_LIST_HEAD(&hsw->rx_list); + INIT_LIST_HEAD(&hsw->empty_list); + init_waitqueue_head(&hsw->boot_wait); + init_waitqueue_head(&hsw->wait_txq); + + ret = msg_empty_list_init(hsw); + if (ret < 0) + goto list_err; + + /* start the IPC message thread */ + init_kthread_worker(&hsw->kworker); + hsw->tx_thread = kthread_run(kthread_worker_fn, + &hsw->kworker, + dev_name(hsw->dev)); + if (IS_ERR(hsw->tx_thread)) { + ret = PTR_ERR(hsw->tx_thread); + dev_err(hsw->dev, "error: failed to create message TX task\n"); + goto list_err; + } + init_kthread_work(&hsw->kwork, ipc_tx_msgs); + + hsw_dev.thread_context = hsw; + + /* init SST shim */ + hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata); + if (hsw->dsp == NULL) { + ret = -ENODEV; + goto list_err; + } + + /* keep the DSP in reset state for base FW loading */ + sst_dsp_reset(hsw->dsp); + + hsw_sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw); + + if (hsw_sst_fw == NULL) { + ret = -ENODEV; + dev_err(dev, "error: failed to load firmware\n"); + goto fw_err; + } + + /* wait for DSP boot completion */ + sst_dsp_boot(hsw->dsp); + ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, + msecs_to_jiffies(IPC_BOOT_MSECS)); + if (ret == 0) { + ret = -EIO; + dev_err(hsw->dev, "error: ADSP boot timeout\n"); + goto boot_err; + } + + /* get the FW version */ + sst_hsw_fw_get_version(hsw, &version); + dev_info(hsw->dev, "FW loaded: type %d - version: %d.%d build %d\n", + version.type, version.major, version.minor, version.build); + + /* get the globalmixer */ + ret = sst_hsw_mixer_get_info(hsw); + if (ret < 0) { + dev_err(hsw->dev, "error: failed to get stream info\n"); + goto boot_err; + } + + pdata->dsp = hsw; + return 0; + +boot_err: + sst_dsp_reset(hsw->dsp); + sst_fw_free(hsw_sst_fw); +fw_err: + sst_dsp_free(hsw->dsp); + kfree(hsw->msg); +list_err: + return ret; +} +EXPORT_SYMBOL_GPL(sst_hsw_dsp_init); + +void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata) +{ + struct sst_hsw *hsw = pdata->dsp; + + sst_dsp_reset(hsw->dsp); + sst_fw_free_all(hsw->dsp); + sst_dsp_free(hsw->dsp); + kfree(hsw->scratch); + kfree(hsw->msg); +} +EXPORT_SYMBOL_GPL(sst_hsw_dsp_free); diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h new file mode 100644 index 000000000000..d517929ccc38 --- /dev/null +++ b/sound/soc/intel/sst-haswell-ipc.h @@ -0,0 +1,488 @@ +/* + * Intel SST Haswell/Broadwell IPC Support + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License 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. + * + */ + +#ifndef __SST_HASWELL_IPC_H +#define __SST_HASWELL_IPC_H + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> + +#define SST_HSW_NO_CHANNELS 2 +#define SST_HSW_MAX_DX_REGIONS 14 + +#define SST_HSW_FW_LOG_CONFIG_DWORDS 12 +#define SST_HSW_GLOBAL_LOG 15 + +/** + * Upfront defined maximum message size that is + * expected by the in/out communication pipes in FW. + */ +#define SST_HSW_IPC_MAX_PAYLOAD_SIZE 400 +#define SST_HSW_MAX_INFO_SIZE 64 +#define SST_HSW_BUILD_HASH_LENGTH 40 + +struct sst_hsw; +struct sst_hsw_stream; +struct sst_hsw_log_stream; +struct sst_pdata; +struct sst_module; +extern struct sst_ops haswell_ops; + +/* Stream Allocate Path ID */ +enum sst_hsw_stream_path_id { + SST_HSW_STREAM_PATH_SSP0_OUT = 0, + SST_HSW_STREAM_PATH_SSP0_IN = 1, + SST_HSW_STREAM_PATH_MAX_PATH_ID = 2, +}; + +/* Stream Allocate Stream Type */ +enum sst_hsw_stream_type { + SST_HSW_STREAM_TYPE_RENDER = 0, + SST_HSW_STREAM_TYPE_SYSTEM = 1, + SST_HSW_STREAM_TYPE_CAPTURE = 2, + SST_HSW_STREAM_TYPE_LOOPBACK = 3, + SST_HSW_STREAM_TYPE_MAX_STREAM_TYPE = 4, +}; + +/* Stream Allocate Stream Format */ +enum sst_hsw_stream_format { + SST_HSW_STREAM_FORMAT_PCM_FORMAT = 0, + SST_HSW_STREAM_FORMAT_MP3_FORMAT = 1, + SST_HSW_STREAM_FORMAT_AAC_FORMAT = 2, + SST_HSW_STREAM_FORMAT_MAX_FORMAT_ID = 3, +}; + +/* Device ID */ +enum sst_hsw_device_id { + SST_HSW_DEVICE_SSP_0 = 0, + SST_HSW_DEVICE_SSP_1 = 1, +}; + +/* Device Master Clock Frequency */ +enum sst_hsw_device_mclk { + SST_HSW_DEVICE_MCLK_OFF = 0, + SST_HSW_DEVICE_MCLK_FREQ_6_MHZ = 1, + SST_HSW_DEVICE_MCLK_FREQ_12_MHZ = 2, + SST_HSW_DEVICE_MCLK_FREQ_24_MHZ = 3, +}; + +/* Device Clock Master */ +enum sst_hsw_device_mode { + SST_HSW_DEVICE_CLOCK_SLAVE = 0, + SST_HSW_DEVICE_CLOCK_MASTER = 1, +}; + +/* DX Power State */ +enum sst_hsw_dx_state { + SST_HSW_DX_STATE_D0 = 0, + SST_HSW_DX_STATE_D1 = 1, + SST_HSW_DX_STATE_D3 = 3, + SST_HSW_DX_STATE_MAX = 3, +}; + +/* Audio stream stage IDs */ +enum sst_hsw_fx_stage_id { + SST_HSW_STAGE_ID_WAVES = 0, + SST_HSW_STAGE_ID_DTS = 1, + SST_HSW_STAGE_ID_DOLBY = 2, + SST_HSW_STAGE_ID_BOOST = 3, + SST_HSW_STAGE_ID_MAX_FX_ID +}; + +/* DX State Type */ +enum sst_hsw_dx_type { + SST_HSW_DX_TYPE_FW_IMAGE = 0, + SST_HSW_DX_TYPE_MEMORY_DUMP = 1 +}; + +/* Volume Curve Type*/ +enum sst_hsw_volume_curve { + SST_HSW_VOLUME_CURVE_NONE = 0, + SST_HSW_VOLUME_CURVE_FADE = 1 +}; + +/* Sample ordering */ +enum sst_hsw_interleaving { + SST_HSW_INTERLEAVING_PER_CHANNEL = 0, + SST_HSW_INTERLEAVING_PER_SAMPLE = 1, +}; + +/* Channel indices */ +enum sst_hsw_channel_index { + SST_HSW_CHANNEL_LEFT = 0, + SST_HSW_CHANNEL_CENTER = 1, + SST_HSW_CHANNEL_RIGHT = 2, + SST_HSW_CHANNEL_LEFT_SURROUND = 3, + SST_HSW_CHANNEL_CENTER_SURROUND = 3, + SST_HSW_CHANNEL_RIGHT_SURROUND = 4, + SST_HSW_CHANNEL_LFE = 7, + SST_HSW_CHANNEL_INVALID = 0xF, +}; + +/* List of supported channel maps. */ +enum sst_hsw_channel_config { + SST_HSW_CHANNEL_CONFIG_MONO = 0, /* mono only. */ + SST_HSW_CHANNEL_CONFIG_STEREO = 1, /* L & R. */ + SST_HSW_CHANNEL_CONFIG_2_POINT_1 = 2, /* L, R & LFE; PCM only. */ + SST_HSW_CHANNEL_CONFIG_3_POINT_0 = 3, /* L, C & R; MP3 & AAC only. */ + SST_HSW_CHANNEL_CONFIG_3_POINT_1 = 4, /* L, C, R & LFE; PCM only. */ + SST_HSW_CHANNEL_CONFIG_QUATRO = 5, /* L, R, Ls & Rs; PCM only. */ + SST_HSW_CHANNEL_CONFIG_4_POINT_0 = 6, /* L, C, R & Cs; MP3 & AAC only. */ + SST_HSW_CHANNEL_CONFIG_5_POINT_0 = 7, /* L, C, R, Ls & Rs. */ + SST_HSW_CHANNEL_CONFIG_5_POINT_1 = 8, /* L, C, R, Ls, Rs & LFE. */ + SST_HSW_CHANNEL_CONFIG_DUAL_MONO = 9, /* One channel replicated in two. */ + SST_HSW_CHANNEL_CONFIG_INVALID, +}; + +/* List of supported bit depths. */ +enum sst_hsw_bitdepth { + SST_HSW_DEPTH_8BIT = 8, + SST_HSW_DEPTH_16BIT = 16, + SST_HSW_DEPTH_24BIT = 24, /* Default. */ + SST_HSW_DEPTH_32BIT = 32, + SST_HSW_DEPTH_INVALID = 33, +}; + +enum sst_hsw_module_id { + SST_HSW_MODULE_BASE_FW = 0x0, + SST_HSW_MODULE_MP3 = 0x1, + SST_HSW_MODULE_AAC_5_1 = 0x2, + SST_HSW_MODULE_AAC_2_0 = 0x3, + SST_HSW_MODULE_SRC = 0x4, + SST_HSW_MODULE_WAVES = 0x5, + SST_HSW_MODULE_DOLBY = 0x6, + SST_HSW_MODULE_BOOST = 0x7, + SST_HSW_MODULE_LPAL = 0x8, + SST_HSW_MODULE_DTS = 0x9, + SST_HSW_MODULE_PCM_CAPTURE = 0xA, + SST_HSW_MODULE_PCM_SYSTEM = 0xB, + SST_HSW_MODULE_PCM_REFERENCE = 0xC, + SST_HSW_MODULE_PCM = 0xD, + SST_HSW_MODULE_BLUETOOTH_RENDER_MODULE = 0xE, + SST_HSW_MODULE_BLUETOOTH_CAPTURE_MODULE = 0xF, + SST_HSW_MAX_MODULE_ID, +}; + +enum sst_hsw_performance_action { + SST_HSW_PERF_START = 0, + SST_HSW_PERF_STOP = 1, +}; + +/* SST firmware module info */ +struct sst_hsw_module_info { + u8 name[SST_HSW_MAX_INFO_SIZE]; + u8 version[SST_HSW_MAX_INFO_SIZE]; +} __attribute__((packed)); + +/* Module entry point */ +struct sst_hsw_module_entry { + enum sst_hsw_module_id module_id; + u32 entry_point; +} __attribute__((packed)); + +/* Module map - alignement matches DSP */ +struct sst_hsw_module_map { + u8 module_entries_count; + struct sst_hsw_module_entry module_entries[1]; +} __attribute__((packed)); + +struct sst_hsw_memory_info { + u32 offset; + u32 size; +} __attribute__((packed)); + +struct sst_hsw_fx_enable { + struct sst_hsw_module_map module_map; + struct sst_hsw_memory_info persistent_mem; +} __attribute__((packed)); + +struct sst_hsw_get_fx_param { + u32 parameter_id; + u32 param_size; +} __attribute__((packed)); + +struct sst_hsw_perf_action { + u32 action; +} __attribute__((packed)); + +struct sst_hsw_perf_data { + u64 timestamp; + u64 cycles; + u64 datatime; +} __attribute__((packed)); + +/* FW version */ +struct sst_hsw_ipc_fw_version { + u8 build; + u8 minor; + u8 major; + u8 type; + u8 fw_build_hash[SST_HSW_BUILD_HASH_LENGTH]; + u32 fw_log_providers_hash; +} __attribute__((packed)); + +/* Stream ring info */ +struct sst_hsw_ipc_stream_ring { + u32 ring_pt_address; + u32 num_pages; + u32 ring_size; + u32 ring_offset; + u32 ring_first_pfn; +} __attribute__((packed)); + +/* Debug Dump Log Enable Request */ +struct sst_hsw_ipc_debug_log_enable_req { + struct sst_hsw_ipc_stream_ring ringinfo; + u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS]; +} __attribute__((packed)); + +/* Debug Dump Log Reply */ +struct sst_hsw_ipc_debug_log_reply { + u32 log_buffer_begining; + u32 log_buffer_size; +} __attribute__((packed)); + +/* Stream glitch position */ +struct sst_hsw_ipc_stream_glitch_position { + u32 glitch_type; + u32 present_pos; + u32 write_pos; +} __attribute__((packed)); + +/* Stream get position */ +struct sst_hsw_ipc_stream_get_position { + u32 position; + u32 fw_cycle_count; +} __attribute__((packed)); + +/* Stream set position */ +struct sst_hsw_ipc_stream_set_position { + u32 position; + u32 end_of_buffer; +} __attribute__((packed)); + +/* Stream Free Request */ +struct sst_hsw_ipc_stream_free_req { + u8 stream_id; + u8 reserved[3]; +} __attribute__((packed)); + +/* Set Volume Request */ +struct sst_hsw_ipc_volume_req { + u32 channel; + u32 target_volume; + u64 curve_duration; + u32 curve_type; +} __attribute__((packed)); + +/* Device Configuration Request */ +struct sst_hsw_ipc_device_config_req { + u32 ssp_interface; + u32 clock_frequency; + u32 mode; + u16 clock_divider; + u16 reserved; +} __attribute__((packed)); + +/* Audio Data formats */ +struct sst_hsw_audio_data_format_ipc { + u32 frequency; + u32 bitdepth; + u32 map; + u32 config; + u32 style; + u8 ch_num; + u8 valid_bit; + u8 reserved[2]; +} __attribute__((packed)); + +/* Stream Allocate Request */ +struct sst_hsw_ipc_stream_alloc_req { + u8 path_id; + u8 stream_type; + u8 format_id; + u8 reserved; + struct sst_hsw_audio_data_format_ipc format; + struct sst_hsw_ipc_stream_ring ringinfo; + struct sst_hsw_module_map map; + struct sst_hsw_memory_info persistent_mem; + struct sst_hsw_memory_info scratch_mem; + u32 number_of_notifications; +} __attribute__((packed)); + +/* Stream Allocate Reply */ +struct sst_hsw_ipc_stream_alloc_reply { + u32 stream_hw_id; + u32 mixer_hw_id; // returns rate ???? + u32 read_position_register_address; + u32 presentation_position_register_address; + u32 peak_meter_register_address[SST_HSW_NO_CHANNELS]; + u32 volume_register_address[SST_HSW_NO_CHANNELS]; +} __attribute__((packed)); + +/* Get Mixer Stream Info */ +struct sst_hsw_ipc_stream_info_reply { + u32 mixer_hw_id; + u32 peak_meter_register_address[SST_HSW_NO_CHANNELS]; + u32 volume_register_address[SST_HSW_NO_CHANNELS]; +} __attribute__((packed)); + +/* DX State Request */ +struct sst_hsw_ipc_dx_req { + u8 state; + u8 reserved[3]; +} __attribute__((packed)); + +/* DX State Reply Memory Info Item */ +struct sst_hsw_ipc_dx_memory_item { + u32 offset; + u32 size; + u32 source; +} __attribute__((packed)); + +/* DX State Reply */ +struct sst_hsw_ipc_dx_reply { + u32 entries_no; + struct sst_hsw_ipc_dx_memory_item mem_info[SST_HSW_MAX_DX_REGIONS]; +} __attribute__((packed)); + +struct sst_hsw_ipc_fw_version; + +/* SST Init & Free */ +struct sst_hsw *sst_hsw_new(struct device *dev, const u8 *fw, size_t fw_length, + u32 fw_offset); +void sst_hsw_free(struct sst_hsw *hsw); +int sst_hsw_fw_get_version(struct sst_hsw *hsw, + struct sst_hsw_ipc_fw_version *version); +u32 create_channel_map(enum sst_hsw_channel_config config); + +/* Stream Mixer Controls - */ +int sst_hsw_stream_mute(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + u32 stage_id, u32 channel); +int sst_hsw_stream_unmute(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + u32 stage_id, u32 channel); + +int sst_hsw_stream_set_volume(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume); +int sst_hsw_stream_get_volume(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 *volume); + +int sst_hsw_stream_set_volume_curve(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u64 curve_duration, + enum sst_hsw_volume_curve curve); + +/* Global Mixer Controls - */ +int sst_hsw_mixer_mute(struct sst_hsw *hsw, u32 stage_id, u32 channel); +int sst_hsw_mixer_unmute(struct sst_hsw *hsw, u32 stage_id, u32 channel); + +int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, + u32 volume); +int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, + u32 *volume); + +int sst_hsw_mixer_set_volume_curve(struct sst_hsw *hsw, + u64 curve_duration, enum sst_hsw_volume_curve curve); + +/* Stream API */ +struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id, + u32 (*get_write_position)(struct sst_hsw_stream *stream, void *data), + void *data); + +int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream); + +/* Stream Configuration */ +int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + enum sst_hsw_stream_path_id path_id, + enum sst_hsw_stream_type stream_type, + enum sst_hsw_stream_format format_id); + +int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + u32 ring_pt_address, u32 num_pages, + u32 ring_size, u32 ring_offset, u32 ring_first_pfn); + +int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream); + +int sst_hsw_stream_set_valid(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + u32 bits); +int sst_hsw_stream_set_rate(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + int rate); +int sst_hsw_stream_set_bits(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + enum sst_hsw_bitdepth bits); +int sst_hsw_stream_set_channels(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, int channels); +int sst_hsw_stream_set_map_config(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 map, + enum sst_hsw_channel_config config); +int sst_hsw_stream_set_style(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + enum sst_hsw_interleaving style); +int sst_hsw_stream_set_module_info(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id, + u32 entry_point); +int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 offset, u32 size); +int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 offset, u32 size); +int sst_hsw_stream_get_hw_id(struct sst_hsw *hsw, + struct sst_hsw_stream *stream); +int sst_hsw_stream_get_mixer_id(struct sst_hsw *hsw, + struct sst_hsw_stream *stream); +u32 sst_hsw_stream_get_read_reg(struct sst_hsw *hsw, + struct sst_hsw_stream *stream); +u32 sst_hsw_stream_get_pointer_reg(struct sst_hsw *hsw, + struct sst_hsw_stream *stream); +u32 sst_hsw_stream_get_peak_reg(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 channel); +u32 sst_hsw_stream_get_vol_reg(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 channel); +int sst_hsw_mixer_get_info(struct sst_hsw *hsw); + +/* Stream ALSA trigger operations */ +int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + int wait); +int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream, + int wait); +int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream); + +/* Stream pointer positions */ +int sst_hsw_stream_get_read_pos(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 *position); +int sst_hsw_stream_get_write_pos(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 *position); +int sst_hsw_stream_set_write_position(struct sst_hsw *hsw, + struct sst_hsw_stream *stream, u32 stage_id, u32 position); +int sst_hsw_get_dsp_position(struct sst_hsw *hsw, + struct sst_hsw_stream *stream); + +/* HW port config */ +int sst_hsw_device_set_config(struct sst_hsw *hsw, + enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk, + enum sst_hsw_device_mode mode, u32 clock_divider); + +/* DX Config */ +int sst_hsw_dx_set_state(struct sst_hsw *hsw, + enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx); +int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item, + u32 *offset, u32 *size, u32 *source); + +/* init */ +int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata); +void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata); +struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw); +void sst_hsw_set_scratch_module(struct sst_hsw *hsw, + struct sst_module *scratch); + +#endif diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c new file mode 100644 index 000000000000..0a32dd13a23d --- /dev/null +++ b/sound/soc/intel/sst-haswell-pcm.c @@ -0,0 +1,872 @@ +/* + * Intel SST Haswell/Broadwell PCM Support + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License 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/dma-mapping.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <asm/page.h> +#include <asm/pgtable.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/tlv.h> +#include <sound/compress_driver.h> + +#include "sst-haswell-ipc.h" +#include "sst-dsp-priv.h" +#include "sst-dsp.h" + +#define HSW_PCM_COUNT 6 +#define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */ + +/* simple volume table */ +static const u32 volume_map[] = { + HSW_VOLUME_MAX >> 30, + HSW_VOLUME_MAX >> 29, + HSW_VOLUME_MAX >> 28, + HSW_VOLUME_MAX >> 27, + HSW_VOLUME_MAX >> 26, + HSW_VOLUME_MAX >> 25, + HSW_VOLUME_MAX >> 24, + HSW_VOLUME_MAX >> 23, + HSW_VOLUME_MAX >> 22, + HSW_VOLUME_MAX >> 21, + HSW_VOLUME_MAX >> 20, + HSW_VOLUME_MAX >> 19, + HSW_VOLUME_MAX >> 18, + HSW_VOLUME_MAX >> 17, + HSW_VOLUME_MAX >> 16, + HSW_VOLUME_MAX >> 15, + HSW_VOLUME_MAX >> 14, + HSW_VOLUME_MAX >> 13, + HSW_VOLUME_MAX >> 12, + HSW_VOLUME_MAX >> 11, + HSW_VOLUME_MAX >> 10, + HSW_VOLUME_MAX >> 9, + HSW_VOLUME_MAX >> 8, + HSW_VOLUME_MAX >> 7, + HSW_VOLUME_MAX >> 6, + HSW_VOLUME_MAX >> 5, + HSW_VOLUME_MAX >> 4, + HSW_VOLUME_MAX >> 3, + HSW_VOLUME_MAX >> 2, + HSW_VOLUME_MAX >> 1, + HSW_VOLUME_MAX >> 0, +}; + +#define HSW_PCM_PERIODS_MAX 64 +#define HSW_PCM_PERIODS_MIN 2 + +static const struct snd_pcm_hardware hsw_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = PAGE_SIZE, + .period_bytes_max = (HSW_PCM_PERIODS_MAX / HSW_PCM_PERIODS_MIN) * PAGE_SIZE, + .periods_min = HSW_PCM_PERIODS_MIN, + .periods_max = HSW_PCM_PERIODS_MAX, + .buffer_bytes_max = HSW_PCM_PERIODS_MAX * PAGE_SIZE, +}; + +/* private data for each PCM DSP stream */ +struct hsw_pcm_data { + int dai_id; + struct sst_hsw_stream *stream; + u32 volume[2]; + struct snd_pcm_substream *substream; + struct snd_compr_stream *cstream; + unsigned int wpos; + struct mutex mutex; +}; + +/* private data for the driver */ +struct hsw_priv_data { + /* runtime DSP */ + struct sst_hsw *hsw; + + /* page tables */ + unsigned char *pcm_pg[HSW_PCM_COUNT][2]; + + /* DAI data */ + struct hsw_pcm_data pcm[HSW_PCM_COUNT]; +}; + +static inline u32 hsw_mixer_to_ipc(unsigned int value) +{ + if (value >= ARRAY_SIZE(volume_map)) + return volume_map[0]; + else + return volume_map[value]; +} + +static inline unsigned int hsw_ipc_to_mixer(u32 value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(volume_map); i++) { + if (volume_map[i] >= value) + return i; + } + + return i - 1; +} + +static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct hsw_priv_data *pdata = + snd_soc_platform_get_drvdata(platform); + struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg]; + struct sst_hsw *hsw = pdata->hsw; + u32 volume; + + mutex_lock(&pcm_data->mutex); + + if (!pcm_data->stream) { + pcm_data->volume[0] = + hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); + pcm_data->volume[1] = + hsw_mixer_to_ipc(ucontrol->value.integer.value[1]); + mutex_unlock(&pcm_data->mutex); + return 0; + } + + if (ucontrol->value.integer.value[0] == + ucontrol->value.integer.value[1]) { + volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); + sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 2, volume); + } else { + volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); + sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume); + volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]); + sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume); + } + + mutex_unlock(&pcm_data->mutex); + return 0; +} + +static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct hsw_priv_data *pdata = + snd_soc_platform_get_drvdata(platform); + struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg]; + struct sst_hsw *hsw = pdata->hsw; + u32 volume; + + mutex_lock(&pcm_data->mutex); + + if (!pcm_data->stream) { + ucontrol->value.integer.value[0] = + hsw_ipc_to_mixer(pcm_data->volume[0]); + ucontrol->value.integer.value[1] = + hsw_ipc_to_mixer(pcm_data->volume[1]); + mutex_unlock(&pcm_data->mutex); + return 0; + } + + sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 0, &volume); + ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume); + sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume); + ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume); + mutex_unlock(&pcm_data->mutex); + + return 0; +} + +static int hsw_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct sst_hsw *hsw = pdata->hsw; + u32 volume; + + if (ucontrol->value.integer.value[0] == + ucontrol->value.integer.value[1]) { + + volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); + sst_hsw_mixer_set_volume(hsw, 0, 2, volume); + + } else { + volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); + sst_hsw_mixer_set_volume(hsw, 0, 0, volume); + + volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]); + sst_hsw_mixer_set_volume(hsw, 0, 1, volume); + } + + return 0; +} + +static int hsw_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct sst_hsw *hsw = pdata->hsw; + unsigned int volume = 0; + + sst_hsw_mixer_get_volume(hsw, 0, 0, &volume); + ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume); + + sst_hsw_mixer_get_volume(hsw, 0, 1, &volume); + ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume); + + return 0; +} + +/* TLV used by both global and stream volumes */ +static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1); + +/* System Pin has no volume control */ +static const struct snd_kcontrol_new hsw_volume_controls[] = { + /* Global DSP volume */ + SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8, + ARRAY_SIZE(volume_map) -1, 0, + hsw_volume_get, hsw_volume_put, hsw_vol_tlv), + /* Offload 0 volume */ + SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8, + ARRAY_SIZE(volume_map), 0, + hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), + /* Offload 1 volume */ + SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8, + ARRAY_SIZE(volume_map), 0, + hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), + /* Loopback volume */ + SOC_DOUBLE_EXT_TLV("Loopback Capture Volume", 3, 0, 8, + ARRAY_SIZE(volume_map), 0, + hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), + /* Mic Capture volume */ + SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8, + ARRAY_SIZE(volume_map), 0, + hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), +}; + +/* Create DMA buffer page table for DSP */ +static int create_adsp_page_table(struct hsw_priv_data *pdata, + struct snd_soc_pcm_runtime *rtd, + unsigned char *dma_area, size_t size, int pcm, int stream) +{ + int i, pages; + + if (size % PAGE_SIZE) + pages = (size / PAGE_SIZE) + 1; + else + pages = size / PAGE_SIZE; + + dev_dbg(rtd->dev, "generating page table for %p size 0x%zu pages %d\n", + dma_area, size, pages); + + for (i = 0; i < pages; i++) { + u32 idx = (((i << 2) + i)) >> 1; + u32 pfn = (virt_to_phys(dma_area + i * PAGE_SIZE)) >> PAGE_SHIFT; + u32 *pg_table; + + dev_dbg(rtd->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); + + pg_table = (u32*)(pdata->pcm_pg[pcm][stream] + idx); + + if (i & 1) + *pg_table |= (pfn << 4); + else + *pg_table |= pfn; + } + + return 0; +} + +/* this may get called several times by oss emulation */ +static int hsw_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hsw_priv_data *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct sst_hsw *hsw = pdata->hsw; + struct sst_module *module_data; + struct sst_dsp *dsp; + enum sst_hsw_stream_type stream_type; + enum sst_hsw_stream_path_id path_id; + u32 rate, bits, map, pages, module_id; + u8 channels; + int ret; + + /* stream direction */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + path_id = SST_HSW_STREAM_PATH_SSP0_OUT; + else + path_id = SST_HSW_STREAM_PATH_SSP0_IN; + + /* DSP stream type depends on DAI ID */ + switch (rtd->cpu_dai->id) { + case 0: + stream_type = SST_HSW_STREAM_TYPE_SYSTEM; + module_id = SST_HSW_MODULE_PCM_SYSTEM; + break; + case 1: + case 2: + stream_type = SST_HSW_STREAM_TYPE_RENDER; + module_id = SST_HSW_MODULE_PCM; + break; + case 3: + /* path ID needs to be OUT for loopback */ + stream_type = SST_HSW_STREAM_TYPE_LOOPBACK; + path_id = SST_HSW_STREAM_PATH_SSP0_OUT; + module_id = SST_HSW_MODULE_PCM_REFERENCE; + break; + case 4: + stream_type = SST_HSW_STREAM_TYPE_CAPTURE; + module_id = SST_HSW_MODULE_PCM_CAPTURE; + break; + default: + dev_err(rtd->dev, "error: invalid DAI ID %d\n", + rtd->cpu_dai->id); + return -EINVAL; + }; + + ret = sst_hsw_stream_format(hsw, pcm_data->stream, + path_id, stream_type, SST_HSW_STREAM_FORMAT_PCM_FORMAT); + if (ret < 0) { + dev_err(rtd->dev, "error: failed to set format %d\n", ret); + return ret; + } + + rate = params_rate(params); + ret = sst_hsw_stream_set_rate(hsw, pcm_data->stream, rate); + if (ret < 0) { + dev_err(rtd->dev, "error: could not set rate %d\n", rate); + return ret; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + bits = SST_HSW_DEPTH_16BIT; + sst_hsw_stream_set_valid(hsw, pcm_data->stream, 16); + break; + case SNDRV_PCM_FORMAT_S24_LE: + bits = SST_HSW_DEPTH_24BIT; + sst_hsw_stream_set_valid(hsw, pcm_data->stream, 32); + break; + default: + dev_err(rtd->dev, "error: invalid format %d\n", + params_format(params)); + return -EINVAL; + } + + ret = sst_hsw_stream_set_bits(hsw, pcm_data->stream, bits); + if (ret < 0) { + dev_err(rtd->dev, "error: could not set bits %d\n", bits); + return ret; + } + + /* we only support stereo atm */ + channels = params_channels(params); + if (channels != 2) { + dev_err(rtd->dev, "error: invalid channels %d\n", channels); + return -EINVAL; + } + + map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO); + sst_hsw_stream_set_map_config(hsw, pcm_data->stream, + map, SST_HSW_CHANNEL_CONFIG_STEREO); + + ret = sst_hsw_stream_set_channels(hsw, pcm_data->stream, channels); + if (ret < 0) { + dev_err(rtd->dev, "error: could not set channels %d\n", + channels); + return ret; + } + + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (ret < 0) { + dev_err(rtd->dev, "error: could not allocate %d bytes for PCM %d\n", + params_buffer_bytes(params), ret); + return ret; + } + + ret = create_adsp_page_table(pdata, rtd, runtime->dma_area, + runtime->dma_bytes, rtd->cpu_dai->id, substream->stream); + if (ret < 0) + return ret; + + sst_hsw_stream_set_style(hsw, pcm_data->stream, + SST_HSW_INTERLEAVING_PER_CHANNEL); + + if (runtime->dma_bytes % PAGE_SIZE) + pages = (runtime->dma_bytes / PAGE_SIZE) + 1; + else + pages = runtime->dma_bytes / PAGE_SIZE; + + ret = sst_hsw_stream_buffer(hsw, pcm_data->stream, + virt_to_phys(pdata->pcm_pg[rtd->cpu_dai->id][substream->stream]), + pages, runtime->dma_bytes, 0, + (u32)(virt_to_phys(runtime->dma_area) >> PAGE_SHIFT)); + if (ret < 0) { + dev_err(rtd->dev, "error: failed to set DMA buffer %d\n", ret); + return ret; + } + + dsp = sst_hsw_get_dsp(hsw); + + module_data = sst_module_get_from_id(dsp, module_id); + if (module_data == NULL) { + dev_err(rtd->dev, "error: failed to get module config\n"); + return -EINVAL; + } + + /* we use hardcoded memory offsets atm, will be updated for new FW */ + if (stream_type == SST_HSW_STREAM_TYPE_CAPTURE) { + sst_hsw_stream_set_module_info(hsw, pcm_data->stream, + SST_HSW_MODULE_PCM_CAPTURE, module_data->entry); + sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream, + 0x449400, 0x4000); + sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream, + 0x400000, 0); + } else { /* stream_type == SST_HSW_STREAM_TYPE_SYSTEM */ + sst_hsw_stream_set_module_info(hsw, pcm_data->stream, + SST_HSW_MODULE_PCM_SYSTEM, module_data->entry); + + sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream, + module_data->offset, module_data->size); + sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream, + 0x44d400, 0x3800); + + sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream, + module_data->offset, module_data->size); + sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream, + 0x400000, 0); + } + + ret = sst_hsw_stream_commit(hsw, pcm_data->stream); + if (ret < 0) { + dev_err(rtd->dev, "error: failed to commit stream %d\n", ret); + return ret; + } + + ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1); + if (ret < 0) + dev_err(rtd->dev, "error: failed to pause %d\n", ret); + + return 0; +} + +static int hsw_pcm_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int hsw_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct hsw_priv_data *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct sst_hsw *hsw = pdata->hsw; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + sst_hsw_stream_resume(hsw, pcm_data->stream, 0); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + sst_hsw_stream_pause(hsw, pcm_data->stream, 0); + break; + default: + break; + } + + return 0; +} + +static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data) +{ + struct hsw_pcm_data *pcm_data = data; + struct snd_pcm_substream *substream = pcm_data->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + u32 pos; + + pos = frames_to_bytes(runtime, + (runtime->control->appl_ptr % runtime->buffer_size)); + + dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos); + + /* let alsa know we have play a period */ + snd_pcm_period_elapsed(substream); + return pos; +} + +static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hsw_priv_data *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct sst_hsw *hsw = pdata->hsw; + snd_pcm_uframes_t offset; + + offset = bytes_to_frames(runtime, + sst_hsw_get_dsp_position(hsw, pcm_data->stream)); + + dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n", + frames_to_bytes(runtime, (u32)offset)); + return offset; +} + +static int hsw_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct hsw_priv_data *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct hsw_pcm_data *pcm_data; + struct sst_hsw *hsw = pdata->hsw; + + pcm_data = &pdata->pcm[rtd->cpu_dai->id]; + + mutex_lock(&pcm_data->mutex); + + snd_soc_pcm_set_drvdata(rtd, pcm_data); + pcm_data->substream = substream; + + snd_soc_set_runtime_hwparams(substream, &hsw_pcm_hardware); + + pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id, + hsw_notify_pointer, pcm_data); + if (pcm_data->stream == NULL) { + dev_err(rtd->dev, "error: failed to create stream\n"); + mutex_unlock(&pcm_data->mutex); + return -EINVAL; + } + + /* Set previous saved volume */ + sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, + 0, pcm_data->volume[0]); + sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, + 1, pcm_data->volume[1]); + + mutex_unlock(&pcm_data->mutex); + return 0; +} + +static int hsw_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct hsw_priv_data *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct sst_hsw *hsw = pdata->hsw; + int ret; + + mutex_lock(&pcm_data->mutex); + ret = sst_hsw_stream_reset(hsw, pcm_data->stream); + if (ret < 0) { + dev_dbg(rtd->dev, "error: reset stream failed %d\n", ret); + goto out; + } + + ret = sst_hsw_stream_free(hsw, pcm_data->stream); + if (ret < 0) { + dev_dbg(rtd->dev, "error: free stream failed %d\n", ret); + goto out; + } + pcm_data->stream = NULL; + +out: + mutex_unlock(&pcm_data->mutex); + return ret; +} + +static struct snd_pcm_ops hsw_pcm_ops = { + .open = hsw_pcm_open, + .close = hsw_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = hsw_pcm_hw_params, + .hw_free = hsw_pcm_hw_free, + .trigger = hsw_pcm_trigger, + .pointer = hsw_pcm_pointer, + .mmap = snd_pcm_lib_default_mmap, +}; + +static void hsw_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + int ret = 0; + + ret = dma_coerce_mask_and_coherent(rtd->card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || + pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + ret = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV, + rtd->card->dev, + hsw_pcm_hardware.buffer_bytes_max, + hsw_pcm_hardware.buffer_bytes_max); + if (ret) { + dev_err(rtd->dev, "dma buffer allocation failed %d\n", + ret); + return ret; + } + } + + return ret; +} + +#define HSW_FORMATS \ + (SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver hsw_dais[] = { + { + .name = "System Pin", + .playback = { + .stream_name = "System Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, + { + /* PCM */ + .name = "Offload0 Pin", + .playback = { + .stream_name = "Offload0 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = HSW_FORMATS, + }, + }, + { + /* PCM */ + .name = "Offload1 Pin", + .playback = { + .stream_name = "Offload1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = HSW_FORMATS, + }, + }, + { + .name = "Loopback Pin", + .capture = { + .stream_name = "Loopback Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = HSW_FORMATS, + }, + }, + { + .name = "Capture Pin", + .capture = { + .stream_name = "Analog Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = HSW_FORMATS, + }, + }, +}; + +static const struct snd_soc_dapm_widget widgets[] = { + + /* Backend DAIs */ + SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0), + + /* Global Playback Mixer */ + SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route graph[] = { + + /* Playback Mixer */ + {"Playback VMixer", NULL, "System Playback"}, + {"Playback VMixer", NULL, "Offload0 Playback"}, + {"Playback VMixer", NULL, "Offload1 Playback"}, + + {"SSP0 CODEC OUT", NULL, "Playback VMixer"}, + + {"Analog Capture", NULL, "SSP0 CODEC IN"}, +}; + +static int hsw_pcm_probe(struct snd_soc_platform *platform) +{ + struct sst_pdata *pdata = dev_get_platdata(platform->dev); + struct hsw_priv_data *priv_data; + int i; + + if (!pdata) + return -ENODEV; + + priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data), GFP_KERNEL); + priv_data->hsw = pdata->dsp; + snd_soc_platform_set_drvdata(platform, priv_data); + + /* allocate DSP buffer page tables */ + for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) { + + mutex_init(&priv_data->pcm[i].mutex); + + /* playback */ + if (hsw_dais[i].playback.channels_min) { + priv_data->pcm_pg[i][0] = kzalloc(PAGE_SIZE, GFP_DMA); + if (priv_data->pcm_pg[i][0] == NULL) + goto err; + } + + /* capture */ + if (hsw_dais[i].capture.channels_min) { + priv_data->pcm_pg[i][1] = kzalloc(PAGE_SIZE, GFP_DMA); + if (priv_data->pcm_pg[i][1] == NULL) + goto err; + } + } + + return 0; + +err: + for (;i >= 0; i--) { + if (hsw_dais[i].playback.channels_min) + kfree(priv_data->pcm_pg[i][0]); + if (hsw_dais[i].capture.channels_min) + kfree(priv_data->pcm_pg[i][1]); + } + return -ENOMEM; +} + +static int hsw_pcm_remove(struct snd_soc_platform *platform) +{ + struct hsw_priv_data *priv_data = + snd_soc_platform_get_drvdata(platform); + int i; + + for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) { + if (hsw_dais[i].playback.channels_min) + kfree(priv_data->pcm_pg[i][0]); + if (hsw_dais[i].capture.channels_min) + kfree(priv_data->pcm_pg[i][1]); + } + + return 0; +} + +static struct snd_soc_platform_driver hsw_soc_platform = { + .probe = hsw_pcm_probe, + .remove = hsw_pcm_remove, + .ops = &hsw_pcm_ops, + .pcm_new = hsw_pcm_new, + .pcm_free = hsw_pcm_free, + .controls = hsw_volume_controls, + .num_controls = ARRAY_SIZE(hsw_volume_controls), + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = graph, + .num_dapm_routes = ARRAY_SIZE(graph), +}; + +static const struct snd_soc_component_driver hsw_dai_component = { + .name = "haswell-dai", +}; + +static int hsw_pcm_dev_probe(struct platform_device *pdev) +{ + struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); + int ret; + + ret = sst_hsw_dsp_init(&pdev->dev, sst_pdata); + if (ret < 0) + return -ENODEV; + + ret = snd_soc_register_platform(&pdev->dev, &hsw_soc_platform); + if (ret < 0) + goto err_plat; + + ret = snd_soc_register_component(&pdev->dev, &hsw_dai_component, + hsw_dais, ARRAY_SIZE(hsw_dais)); + if (ret < 0) + goto err_comp; + + return 0; + +err_comp: + snd_soc_unregister_platform(&pdev->dev); +err_plat: + sst_hsw_dsp_free(&pdev->dev, sst_pdata); + return 0; +} + +static int hsw_pcm_dev_remove(struct platform_device *pdev) +{ + struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); + + snd_soc_unregister_platform(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + sst_hsw_dsp_free(&pdev->dev, sst_pdata); + + return 0; +} + +static struct platform_driver hsw_pcm_driver = { + .driver = { + .name = "haswell-pcm-audio", + .owner = THIS_MODULE, + }, + + .probe = hsw_pcm_dev_probe, + .remove = hsw_pcm_dev_remove, +}; +module_platform_driver(hsw_pcm_driver); + +MODULE_AUTHOR("Liam Girdwood, Xingchao Wang"); +MODULE_DESCRIPTION("Haswell/Lynxpoint + Broadwell/Wildcatpoint PCM"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:haswell-pcm-audio"); diff --git a/sound/soc/mid-x86/sst_dsp.h b/sound/soc/intel/sst-mfld-dsp.h index 0fce1de284ff..3b63edc04b7f 100644 --- a/sound/soc/mid-x86/sst_dsp.h +++ b/sound/soc/intel/sst-mfld-dsp.h @@ -1,7 +1,7 @@ -#ifndef __SST_DSP_H__ -#define __SST_DSP_H__ +#ifndef __SST_MFLD_DSP_H__ +#define __SST_MFLD_DSP_H__ /* - * sst_dsp.h - Intel SST Driver for audio engine + * sst_mfld_dsp.h - Intel SST Driver for audio engine * * Copyright (C) 2008-12 Intel Corporation * Authors: Vinod Koul <vinod.koul@linux.intel.com> @@ -131,4 +131,4 @@ struct snd_sst_params { struct snd_sst_alloc_params_ext aparams; }; -#endif /* __SST_DSP_H__ */ +#endif /* __SST_MFLD_DSP_H__ */ diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/intel/sst-mfld-platform.c index b6b5eb698d33..840306c2ef14 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/intel/sst-mfld-platform.c @@ -1,5 +1,5 @@ /* - * sst_platform.c - Intel MID Platform driver + * sst_mfld_platform.c - Intel MID Platform driver * * Copyright (C) 2010-2013 Intel Corp * Author: Vinod Koul <vinod.koul@intel.com> @@ -33,7 +33,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/compress_driver.h> -#include "sst_platform.h" +#include "sst-mfld-platform.h" static struct sst_device *sst; static DEFINE_MUTEX(sst_lock); @@ -89,16 +89,6 @@ static struct snd_pcm_hardware sst_platform_pcm_hw = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_SYNC_START), - .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 | - SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 | - SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), - .rates = (SNDRV_PCM_RATE_8000| - SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000), - .rate_min = SST_MIN_RATE, - .rate_max = SST_MAX_RATE, - .channels_min = SST_MIN_CHANNEL, - .channels_max = SST_MAX_CHANNEL, .buffer_bytes_max = SST_MAX_BUFFER, .period_bytes_min = SST_MIN_PERIOD_BYTES, .period_bytes_max = SST_MAX_PERIOD_BYTES, @@ -719,7 +709,7 @@ static int sst_platform_remove(struct platform_device *pdev) static struct platform_driver sst_platform_driver = { .driver = { - .name = "sst-platform", + .name = "sst-mfld-platform", .owner = THIS_MODULE, }, .probe = sst_platform_probe, @@ -732,4 +722,4 @@ MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver"); MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:sst-platform"); +MODULE_ALIAS("platform:sst-mfld-platform"); diff --git a/sound/soc/mid-x86/sst_platform.h b/sound/soc/intel/sst-mfld-platform.h index cacc9066ec52..0c4e2ddcecb1 100644 --- a/sound/soc/mid-x86/sst_platform.h +++ b/sound/soc/intel/sst-mfld-platform.h @@ -1,5 +1,5 @@ /* - * sst_platform.h - Intel MID Platform driver header file + * sst_mfld_platform.h - Intel MID Platform driver header file * * Copyright (C) 2010 Intel Corp * Author: Vinod Koul <vinod.koul@intel.com> @@ -27,16 +27,12 @@ #ifndef __SST_PLATFORMDRV_H__ #define __SST_PLATFORMDRV_H__ -#include "sst_dsp.h" +#include "sst-mfld-dsp.h" #define SST_MONO 1 #define SST_STEREO 2 #define SST_MAX_CAP 5 -#define SST_MIN_RATE 8000 -#define SST_MAX_RATE 48000 -#define SST_MIN_CHANNEL 1 -#define SST_MAX_CHANNEL 5 #define SST_MAX_BUFFER (800*1024) #define SST_MIN_BUFFER (800*1024) #define SST_MIN_PERIOD_BYTES 32 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-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index 4af1936cf0f4..aac22fccdcdc 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -21,16 +21,6 @@ #include <sound/soc.h> #include "kirkwood.h" -#define KIRKWOOD_RATES \ - (SNDRV_PCM_RATE_8000_192000 | \ - SNDRV_PCM_RATE_CONTINUOUS | \ - SNDRV_PCM_RATE_KNOT) - -#define KIRKWOOD_FORMATS \ - (SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE) - static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs) { struct snd_soc_pcm_runtime *soc_runtime = subs->private_data; @@ -43,12 +33,6 @@ static struct snd_pcm_hardware kirkwood_dma_snd_hw = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE), - .formats = KIRKWOOD_FORMATS, - .rates = KIRKWOOD_RATES, - .rate_min = 8000, - .rate_max = 384000, - .channels_min = 1, - .channels_max = 8, .buffer_bytes_max = KIRKWOOD_SND_MAX_BUFFER_BYTES, .period_bytes_min = KIRKWOOD_SND_MIN_PERIOD_BYTES, .period_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES, 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/mid-x86/Kconfig b/sound/soc/mid-x86/Kconfig deleted file mode 100644 index 61c10bf503d2..000000000000 --- a/sound/soc/mid-x86/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -config SND_MFLD_MACHINE - tristate "SOC Machine Audio driver for Intel Medfield MID platform" - depends on INTEL_SCU_IPC - select SND_SOC_SN95031 - select SND_SST_PLATFORM - help - This adds support for ASoC machine driver for Intel(R) MID Medfield platform - used as alsa device in audio substem in Intel(R) MID devices - Say Y if you have such a device - If unsure select "N". - -config SND_SST_PLATFORM - tristate diff --git a/sound/soc/mid-x86/Makefile b/sound/soc/mid-x86/Makefile deleted file mode 100644 index 639883339465..000000000000 --- a/sound/soc/mid-x86/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -snd-soc-sst-platform-objs := sst_platform.o -snd-soc-mfld-machine-objs := mfld_machine.o - -obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o -obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c index b16abbbf7764..a371b4f91c53 100644 --- a/sound/soc/mxs/mxs-pcm.c +++ b/sound/soc/mxs/mxs-pcm.c @@ -36,11 +36,6 @@ static const struct snd_pcm_hardware snd_mxs_hardware = { SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_HALF_DUPLEX, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S20_3LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 2, - .channels_max = 2, .period_bytes_min = 32, .period_bytes_max = 8192, .periods_min = 1, @@ -56,16 +51,9 @@ 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, - SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | + return devm_snd_dmaengine_pcm_register(dev, &mxs_dmaengine_pcm_config, 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/nuc900/nuc900-pcm.c b/sound/soc/nuc900/nuc900-pcm.c index f588ee45b4fd..f434ed79d1b6 100644 --- a/sound/soc/nuc900/nuc900-pcm.c +++ b/sound/soc/nuc900/nuc900-pcm.c @@ -32,9 +32,6 @@ static const struct snd_pcm_hardware nuc900_pcm_hardware = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .channels_min = 1, - .channels_max = 2, .buffer_bytes_max = 4*1024, .period_bytes_min = 1*1024, .period_bytes_max = 4*1024, diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 4a07f7179690..e00659351a4e 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -30,6 +30,7 @@ config SND_OMAP_SOC_RX51 select SND_OMAP_SOC_MCBSP select SND_SOC_TLV320AIC3X select SND_SOC_TPA6130A2 + depends on GPIOLIB help Say Y if you want to add support for SoC audio on Nokia RX-51 hardware. This is also known as Nokia N900 product. @@ -57,7 +58,7 @@ config SND_OMAP_SOC_OSK5912 tristate "SoC Audio support for omap osk5912" depends on SND_OMAP_SOC && MACH_OMAP_OSK && I2C select SND_OMAP_SOC_MCBSP - select SND_SOC_TLV320AIC23 + select SND_SOC_TLV320AIC23_I2C help Say Y if you want to add support for SoC audio on osk5912. @@ -65,7 +66,7 @@ config SND_OMAP_SOC_AM3517EVM tristate "SoC Audio support for OMAP3517 / AM3517 EVM" depends on SND_OMAP_SOC && MACH_OMAP3517EVM && I2C select SND_OMAP_SOC_MCBSP - select SND_SOC_TLV320AIC23 + select SND_SOC_TLV320AIC23_I2C help Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517 EVM. diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 629446482a91..f141435b0b4a 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -103,60 +103,62 @@ static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol, if (!codec->hw_write) return -EUNATCH; - if (ucontrol->value.enumerated.item[0] >= control->max) + if (ucontrol->value.enumerated.item[0] >= control->items) return -EINVAL; - mutex_lock(&codec->mutex); + snd_soc_dapm_mutex_lock(dapm); /* Translate selection to bitmap */ pins = ams_delta_audio_mode_pins[ucontrol->value.enumerated.item[0]]; /* Setup pins after corresponding bits if changed */ pin = !!(pins & (1 << AMS_DELTA_MOUTHPIECE)); + if (pin != snd_soc_dapm_get_pin_status(dapm, "Mouthpiece")) { changed = 1; if (pin) - snd_soc_dapm_enable_pin(dapm, "Mouthpiece"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Mouthpiece"); else - snd_soc_dapm_disable_pin(dapm, "Mouthpiece"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece"); } pin = !!(pins & (1 << AMS_DELTA_EARPIECE)); if (pin != snd_soc_dapm_get_pin_status(dapm, "Earpiece")) { changed = 1; if (pin) - snd_soc_dapm_enable_pin(dapm, "Earpiece"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece"); else - snd_soc_dapm_disable_pin(dapm, "Earpiece"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Earpiece"); } pin = !!(pins & (1 << AMS_DELTA_MICROPHONE)); if (pin != snd_soc_dapm_get_pin_status(dapm, "Microphone")) { changed = 1; if (pin) - snd_soc_dapm_enable_pin(dapm, "Microphone"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone"); else - snd_soc_dapm_disable_pin(dapm, "Microphone"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Microphone"); } pin = !!(pins & (1 << AMS_DELTA_SPEAKER)); if (pin != snd_soc_dapm_get_pin_status(dapm, "Speaker")) { changed = 1; if (pin) - snd_soc_dapm_enable_pin(dapm, "Speaker"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker"); else - snd_soc_dapm_disable_pin(dapm, "Speaker"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker"); } pin = !!(pins & (1 << AMS_DELTA_AGC)); if (pin != ams_delta_audio_agc) { ams_delta_audio_agc = pin; changed = 1; if (pin) - snd_soc_dapm_enable_pin(dapm, "AGCIN"); + snd_soc_dapm_enable_pin_unlocked(dapm, "AGCIN"); else - snd_soc_dapm_disable_pin(dapm, "AGCIN"); + snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN"); } + if (changed) - snd_soc_dapm_sync(dapm); + snd_soc_dapm_sync_unlocked(dapm); - mutex_unlock(&codec->mutex); + snd_soc_dapm_mutex_unlock(dapm); return changed; } @@ -194,13 +196,11 @@ static int ams_delta_get_audio_mode(struct snd_kcontrol *kcontrol, return 0; } -static const struct soc_enum ams_delta_audio_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ams_delta_audio_mode), - ams_delta_audio_mode), -}; +static const SOC_ENUM_SINGLE_EXT_DECL(ams_delta_audio_enum, + ams_delta_audio_mode); static const struct snd_kcontrol_new ams_delta_audio_controls[] = { - SOC_ENUM_EXT("Audio Mode", ams_delta_audio_enum[0], + SOC_ENUM_EXT("Audio Mode", ams_delta_audio_enum, ams_delta_get_audio_mode, ams_delta_set_audio_mode), }; @@ -315,12 +315,17 @@ static void cx81801_close(struct tty_struct *tty) v253_ops.close(tty); /* Revert back to default audio input/output constellation */ - snd_soc_dapm_disable_pin(dapm, "Mouthpiece"); - snd_soc_dapm_enable_pin(dapm, "Earpiece"); - snd_soc_dapm_enable_pin(dapm, "Microphone"); - snd_soc_dapm_disable_pin(dapm, "Speaker"); - snd_soc_dapm_disable_pin(dapm, "AGCIN"); - snd_soc_dapm_sync(dapm); + snd_soc_dapm_mutex_lock(dapm); + + snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker"); + snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN"); + + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(codec); } /* Line discipline .hangup() */ 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..fd4d9c809e50 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -68,26 +68,30 @@ static void n810_ext_control(struct snd_soc_dapm_context *dapm) break; } + snd_soc_dapm_mutex_lock(dapm); + if (n810_spk_func) - snd_soc_dapm_enable_pin(dapm, "Ext Spk"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk"); else - snd_soc_dapm_disable_pin(dapm, "Ext Spk"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk"); if (hp) - snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); else - snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); if (line1l) - snd_soc_dapm_enable_pin(dapm, "LINE1L"); + snd_soc_dapm_enable_pin_unlocked(dapm, "LINE1L"); else - snd_soc_dapm_disable_pin(dapm, "LINE1L"); + snd_soc_dapm_disable_pin_unlocked(dapm, "LINE1L"); if (n810_dmic_func) - snd_soc_dapm_enable_pin(dapm, "DMic"); + snd_soc_dapm_enable_pin_unlocked(dapm, "DMic"); else - snd_soc_dapm_disable_pin(dapm, "DMic"); + snd_soc_dapm_disable_pin_unlocked(dapm, "DMic"); + + snd_soc_dapm_sync_unlocked(dapm); - snd_soc_dapm_sync(dapm); + snd_soc_dapm_mutex_unlock(dapm); } static int n810_startup(struct snd_pcm_substream *substream) @@ -100,12 +104,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, @@ -305,7 +309,9 @@ static int __init n810_soc_init(void) int err; struct device *dev; - if (!(machine_is_nokia_n810() || machine_is_nokia_n810_wimax())) + if (!of_have_populated_dt() || + (!of_machine_is_compatible("nokia,n810") && + !of_machine_is_compatible("nokia,n810-wimax"))) return -ENODEV; n810_snd_device = platform_device_alloc("soc-audio", -1); 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/omap/rx51.c b/sound/soc/omap/rx51.c index 611179c3bca4..7fb3d4b10370 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -74,26 +74,30 @@ static void rx51_ext_control(struct snd_soc_dapm_context *dapm) break; } + snd_soc_dapm_mutex_lock(dapm); + if (rx51_spk_func) - snd_soc_dapm_enable_pin(dapm, "Ext Spk"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk"); else - snd_soc_dapm_disable_pin(dapm, "Ext Spk"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk"); if (rx51_dmic_func) - snd_soc_dapm_enable_pin(dapm, "DMic"); + snd_soc_dapm_enable_pin_unlocked(dapm, "DMic"); else - snd_soc_dapm_disable_pin(dapm, "DMic"); + snd_soc_dapm_disable_pin_unlocked(dapm, "DMic"); if (hp) - snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); else - snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); if (hs) - snd_soc_dapm_enable_pin(dapm, "HS Mic"); + snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic"); else - snd_soc_dapm_disable_pin(dapm, "HS Mic"); + snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic"); gpio_set_value(RX51_TVOUT_SEL_GPIO, tvout); - snd_soc_dapm_sync(dapm); + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); } static int rx51_startup(struct snd_pcm_substream *substream) 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/corgi.c b/sound/soc/pxa/corgi.c index 1853d41034bf..5a88136aa800 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -47,64 +47,63 @@ static int corgi_spk_func; static void corgi_ext_control(struct snd_soc_dapm_context *dapm) { + snd_soc_dapm_mutex_lock(dapm); + /* set up jack connection */ switch (corgi_jack_func) { case CORGI_HP: /* set = unmute headphone */ gpio_set_value(CORGI_GPIO_MUTE_L, 1); gpio_set_value(CORGI_GPIO_MUTE_R, 1); - snd_soc_dapm_disable_pin(dapm, "Mic Jack"); - snd_soc_dapm_disable_pin(dapm, "Line Jack"); - snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); - snd_soc_dapm_disable_pin(dapm, "Headset Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); break; case CORGI_MIC: /* reset = mute headphone */ gpio_set_value(CORGI_GPIO_MUTE_L, 0); gpio_set_value(CORGI_GPIO_MUTE_R, 0); - snd_soc_dapm_enable_pin(dapm, "Mic Jack"); - snd_soc_dapm_disable_pin(dapm, "Line Jack"); - snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); - snd_soc_dapm_disable_pin(dapm, "Headset Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); break; case CORGI_LINE: gpio_set_value(CORGI_GPIO_MUTE_L, 0); gpio_set_value(CORGI_GPIO_MUTE_R, 0); - snd_soc_dapm_disable_pin(dapm, "Mic Jack"); - snd_soc_dapm_enable_pin(dapm, "Line Jack"); - snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); - snd_soc_dapm_disable_pin(dapm, "Headset Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); break; case CORGI_HEADSET: gpio_set_value(CORGI_GPIO_MUTE_L, 0); gpio_set_value(CORGI_GPIO_MUTE_R, 1); - snd_soc_dapm_enable_pin(dapm, "Mic Jack"); - snd_soc_dapm_disable_pin(dapm, "Line Jack"); - snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); - snd_soc_dapm_enable_pin(dapm, "Headset Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack"); break; } if (corgi_spk_func == CORGI_SPK_ON) - snd_soc_dapm_enable_pin(dapm, "Ext Spk"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk"); else - snd_soc_dapm_disable_pin(dapm, "Ext Spk"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk"); /* signal a DAPM event */ - snd_soc_dapm_sync(dapm); + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); } static int corgi_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - - mutex_lock(&codec->mutex); /* check the jack status at stream startup */ - corgi_ext_control(&codec->dapm); - - mutex_unlock(&codec->mutex); + corgi_ext_control(&rtd->card->dapm); return 0; } diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c index 44b5c09d296b..c29fedab2f49 100644 --- a/sound/soc/pxa/e740_wm9705.c +++ b/sound/soc/pxa/e740_wm9705.c @@ -103,11 +103,6 @@ static int e740_ac97_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_nc_pin(dapm, "PCBEEP"); snd_soc_dapm_nc_pin(dapm, "MIC2"); - snd_soc_dapm_new_controls(dapm, e740_dapm_widgets, - ARRAY_SIZE(e740_dapm_widgets)); - - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - return 0; } @@ -136,6 +131,11 @@ static struct snd_soc_card e740 = { .owner = THIS_MODULE, .dai_link = e740_dai, .num_links = ARRAY_SIZE(e740_dai), + + .dapm_widgets = e740_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(e740_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), }; static struct gpio e740_audio_gpios[] = { diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c index c34e447eb991..ee36aba88063 100644 --- a/sound/soc/pxa/e750_wm9705.c +++ b/sound/soc/pxa/e750_wm9705.c @@ -85,11 +85,6 @@ static int e750_ac97_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_nc_pin(dapm, "PCBEEP"); snd_soc_dapm_nc_pin(dapm, "MIC2"); - snd_soc_dapm_new_controls(dapm, e750_dapm_widgets, - ARRAY_SIZE(e750_dapm_widgets)); - - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - return 0; } @@ -119,6 +114,11 @@ static struct snd_soc_card e750 = { .owner = THIS_MODULE, .dai_link = e750_dai, .num_links = ARRAY_SIZE(e750_dai), + + .dapm_widgets = e750_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(e750_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), }; static struct gpio e750_audio_gpios[] = { diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c index 3137f800b43f..24c2078ce70b 100644 --- a/sound/soc/pxa/e800_wm9712.c +++ b/sound/soc/pxa/e800_wm9712.c @@ -71,19 +71,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"MIC2", NULL, "Mic (Internal2)"}, }; -static int e800_ac97_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, e800_dapm_widgets, - ARRAY_SIZE(e800_dapm_widgets)); - - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - - return 0; -} - static struct snd_soc_dai_link e800_dai[] = { { .name = "AC97", @@ -92,7 +79,6 @@ static struct snd_soc_dai_link e800_dai[] = { .codec_dai_name = "wm9712-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", - .init = e800_ac97_init, }, { .name = "AC97 Aux", @@ -109,6 +95,11 @@ static struct snd_soc_card e800 = { .owner = THIS_MODULE, .dai_link = e800_dai, .num_links = ARRAY_SIZE(e800_dai), + + .dapm_widgets = e800_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(e800_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), }; static struct gpio e800_audio_gpios[] = { diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index aace19e0fe2c..41ab6678b65d 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -45,27 +45,31 @@ static void magician_ext_control(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = &codec->dapm; + snd_soc_dapm_mutex_lock(dapm); + if (magician_spk_switch) - snd_soc_dapm_enable_pin(dapm, "Speaker"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker"); else - snd_soc_dapm_disable_pin(dapm, "Speaker"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker"); if (magician_hp_switch) - snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); else - snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); switch (magician_in_sel) { case MAGICIAN_MIC: - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_enable_pin(dapm, "Call Mic"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Mic"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Call Mic"); break; case MAGICIAN_MIC_EXT: - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - snd_soc_dapm_enable_pin(dapm, "Headset Mic"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Call Mic"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Mic"); break; } - snd_soc_dapm_sync(dapm); + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); } static int magician_startup(struct snd_pcm_substream *substream) @@ -73,13 +77,9 @@ static int magician_startup(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; - mutex_lock(&codec->mutex); - /* check the jack status at stream startup */ magician_ext_control(codec); - mutex_unlock(&codec->mutex); - return 0; } diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index 160c5245448f..595eee341e90 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -127,16 +127,8 @@ static const struct snd_soc_dapm_route audio_map[] = { static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; unsigned short reg; - /* Add mioa701 specific widgets */ - snd_soc_dapm_new_controls(dapm, mioa701_dapm_widgets, - ARRAY_SIZE(mioa701_dapm_widgets)); - - /* Set up mioa701 specific audio path audio_mapnects */ - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - /* Prepare GPIO8 for rear speaker amplifier */ reg = codec->driver->read(codec, AC97_GPIO_CFG); codec->driver->write(codec, AC97_GPIO_CFG, reg | 0x0100); @@ -145,12 +137,6 @@ static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd) reg = codec->driver->read(codec, AC97_3D_CONTROL); codec->driver->write(codec, AC97_3D_CONTROL, reg | 0xc000); - snd_soc_dapm_enable_pin(dapm, "Front Speaker"); - snd_soc_dapm_enable_pin(dapm, "Rear Speaker"); - snd_soc_dapm_enable_pin(dapm, "Front Mic"); - snd_soc_dapm_enable_pin(dapm, "GSM Line In"); - snd_soc_dapm_enable_pin(dapm, "GSM Line Out"); - return 0; } @@ -183,6 +169,11 @@ static struct snd_soc_card mioa701 = { .owner = THIS_MODULE, .dai_link = mioa701_dai, .num_links = ARRAY_SIZE(mioa701_dai), + + .dapm_widgets = mioa701_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mioa701_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), }; static int mioa701_wm9713_probe(struct platform_device *pdev) 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/pxa/poodle.c b/sound/soc/pxa/poodle.c index c93e138d8dc3..c6bdc6c0eff6 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -74,14 +74,9 @@ static void poodle_ext_control(struct snd_soc_dapm_context *dapm) static int poodle_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - - mutex_lock(&codec->mutex); /* check the jack status at stream startup */ - poodle_ext_control(&codec->dapm); - - mutex_unlock(&codec->mutex); + poodle_ext_control(&rtd->card->dapm); return 0; } diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index fc052d8247ff..1373b017a951 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -46,74 +46,74 @@ static int spitz_mic_gpio; static void spitz_ext_control(struct snd_soc_dapm_context *dapm) { + snd_soc_dapm_mutex_lock(dapm); + if (spitz_spk_func == SPITZ_SPK_ON) - snd_soc_dapm_enable_pin(dapm, "Ext Spk"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk"); else - snd_soc_dapm_disable_pin(dapm, "Ext Spk"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk"); /* set up jack connection */ switch (spitz_jack_func) { case SPITZ_HP: /* enable and unmute hp jack, disable mic bias */ - snd_soc_dapm_disable_pin(dapm, "Headset Jack"); - snd_soc_dapm_disable_pin(dapm, "Mic Jack"); - snd_soc_dapm_disable_pin(dapm, "Line Jack"); - snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); gpio_set_value(SPITZ_GPIO_MUTE_L, 1); gpio_set_value(SPITZ_GPIO_MUTE_R, 1); break; case SPITZ_MIC: /* enable mic jack and bias, mute hp */ - snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); - snd_soc_dapm_disable_pin(dapm, "Headset Jack"); - snd_soc_dapm_disable_pin(dapm, "Line Jack"); - snd_soc_dapm_enable_pin(dapm, "Mic Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack"); gpio_set_value(SPITZ_GPIO_MUTE_L, 0); gpio_set_value(SPITZ_GPIO_MUTE_R, 0); break; case SPITZ_LINE: /* enable line jack, disable mic bias and mute hp */ - snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); - snd_soc_dapm_disable_pin(dapm, "Headset Jack"); - snd_soc_dapm_disable_pin(dapm, "Mic Jack"); - snd_soc_dapm_enable_pin(dapm, "Line Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack"); gpio_set_value(SPITZ_GPIO_MUTE_L, 0); gpio_set_value(SPITZ_GPIO_MUTE_R, 0); break; case SPITZ_HEADSET: /* enable and unmute headset jack enable mic bias, mute L hp */ - snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); - snd_soc_dapm_enable_pin(dapm, "Mic Jack"); - snd_soc_dapm_disable_pin(dapm, "Line Jack"); - snd_soc_dapm_enable_pin(dapm, "Headset Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack"); gpio_set_value(SPITZ_GPIO_MUTE_L, 0); gpio_set_value(SPITZ_GPIO_MUTE_R, 1); break; case SPITZ_HP_OFF: /* jack removed, everything off */ - snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); - snd_soc_dapm_disable_pin(dapm, "Headset Jack"); - snd_soc_dapm_disable_pin(dapm, "Mic Jack"); - snd_soc_dapm_disable_pin(dapm, "Line Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); gpio_set_value(SPITZ_GPIO_MUTE_L, 0); gpio_set_value(SPITZ_GPIO_MUTE_R, 0); break; } - snd_soc_dapm_sync(dapm); + + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); } static int spitz_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - - mutex_lock(&codec->mutex); /* check the jack status at stream startup */ - spitz_ext_control(&codec->dapm); - - mutex_unlock(&codec->mutex); + spitz_ext_control(&rtd->card->dapm); return 0; } diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index 1d9c2ed223bc..cead1658d10a 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -48,31 +48,35 @@ static void tosa_ext_control(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = &codec->dapm; + snd_soc_dapm_mutex_lock(dapm); + /* set up jack connection */ switch (tosa_jack_func) { case TOSA_HP: - snd_soc_dapm_disable_pin(dapm, "Mic (Internal)"); - snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); - snd_soc_dapm_disable_pin(dapm, "Headset Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); break; case TOSA_MIC_INT: - snd_soc_dapm_enable_pin(dapm, "Mic (Internal)"); - snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); - snd_soc_dapm_disable_pin(dapm, "Headset Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Mic (Internal)"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); break; case TOSA_HEADSET: - snd_soc_dapm_disable_pin(dapm, "Mic (Internal)"); - snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); - snd_soc_dapm_enable_pin(dapm, "Headset Jack"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack"); break; } if (tosa_spk_func == TOSA_SPK_ON) - snd_soc_dapm_enable_pin(dapm, "Speaker"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker"); else - snd_soc_dapm_disable_pin(dapm, "Speaker"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker"); + + snd_soc_dapm_sync_unlocked(dapm); - snd_soc_dapm_sync(dapm); + snd_soc_dapm_mutex_unlock(dapm); } static int tosa_startup(struct snd_pcm_substream *substream) @@ -80,13 +84,9 @@ static int tosa_startup(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; - mutex_lock(&codec->mutex); - /* check the jack status at stream startup */ tosa_ext_control(codec); - mutex_unlock(&codec->mutex); - return 0; } diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index db8aadf8932d..23bf991e95d5 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -71,22 +71,10 @@ static const struct snd_soc_dapm_route audio_map[] = { static int zylonite_wm9713_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - if (clk_pout) snd_soc_dai_set_pll(rtd->codec_dai, 0, 0, clk_get_rate(pout), 0); - snd_soc_dapm_new_controls(dapm, zylonite_dapm_widgets, - ARRAY_SIZE(zylonite_dapm_widgets)); - - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - - /* Static setup for now */ - snd_soc_dapm_enable_pin(dapm, "Headphone"); - snd_soc_dapm_enable_pin(dapm, "Headset Earpiece"); - return 0; } @@ -256,6 +244,11 @@ static struct snd_soc_card zylonite = { .resume_pre = &zylonite_resume_pre, .dai_link = zylonite_dai, .num_links = ARRAY_SIZE(zylonite_dai), + + .dapm_widgets = zylonite_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(zylonite_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), }; static struct platform_device *zylonite_snd_ac97_device; diff --git a/sound/soc/s6000/s6000-i2s.c b/sound/soc/s6000/s6000-i2s.c index 73bb99f0109a..7eba7979b9af 100644 --- a/sound/soc/s6000/s6000-i2s.c +++ b/sound/soc/s6000/s6000-i2s.c @@ -405,8 +405,7 @@ static int s6000_i2s_dai_probe(struct snd_soc_dai *dai) return 0; } -#define S6000_I2S_RATES (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \ - SNDRV_PCM_RATE_8000_192000) +#define S6000_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS #define S6000_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops s6000_i2s_dai_ops = { 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..f2e289180e46 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -1,16 +1,25 @@ 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 + select S3C24XX_DMA config SND_S3C_I2SV2_SOC tristate @@ -50,7 +59,7 @@ config SND_SOC_SAMSUNG_JIVE_WM8750 select SND_SOC_WM8750 select SND_S3C2412_SOC_I2S help - Sat Y if you want to add support for SoC audio on the Jive. + Say Y if you want to add support for SoC audio on the Jive. config SND_SOC_SAMSUNG_SMDK_WM8580 tristate "SoC I2S Audio support for WM8580 on SMDK" @@ -108,7 +117,7 @@ config SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23 tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards" depends on SND_SOC_SAMSUNG && ARCH_S3C24XX select SND_S3C24XX_I2S - select SND_SOC_TLV320AIC23 + select SND_SOC_TLV320AIC23_I2C select SND_SOC_SAMSUNG_SIMTEC config SND_SOC_SAMSUNG_SIMTEC_HERMES @@ -136,11 +145,11 @@ config SND_SOC_SAMSUNG_RX1950_UDA1380 config SND_SOC_SAMSUNG_SMDK_WM9713 tristate "SoC AC97 Audio support for SMDK with WM9713" - depends on SND_SOC_SAMSUNG && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110 || MACH_SMDKV310 || MACH_SMDKC210) + depends on SND_SOC_SAMSUNG && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110) select SND_SOC_WM9713 select SND_SAMSUNG_AC97 help - Sat Y if you want to add support for SoC audio on the SMDK. + Say Y if you want to add support for SoC audio on the SMDK. config SND_SOC_SMARTQ tristate "SoC I2S Audio support for SmartQ board" @@ -201,7 +210,7 @@ config SND_SOC_TOBERMORY config SND_SOC_BELLS tristate "Audio support for Wolfson Bells" - depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 + depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && MFD_ARIZONA select SND_SAMSUNG_I2S select SND_SOC_WM5102 select SND_SOC_WM5110 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..750ce5808d9f --- /dev/null +++ b/sound/soc/samsung/dmaengine.c @@ -0,0 +1,83 @@ +/* + * 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_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/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c index fa91376e323d..88b09e022503 100644 --- a/sound/soc/samsung/h1940_uda1380.c +++ b/sound/soc/samsung/h1940_uda1380.c @@ -23,6 +23,7 @@ #include "regs-iis.h" #include <asm/mach-types.h> +#include <mach/gpio-samsung.h> #include "s3c24xx-i2s.h" static unsigned int rates[] = { @@ -65,10 +66,6 @@ static int h1940_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - runtime->hw.rate_min = hw_rates.list[0]; - runtime->hw.rate_max = hw_rates.list[hw_rates.count - 1]; - runtime->hw.rates = SNDRV_PCM_RATE_KNOT; - return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_rates); @@ -93,7 +90,7 @@ static int h1940_hw_params(struct snd_pcm_substream *substream, div++; break; default: - dev_err(&rtd->dev, "%s: rate %d is not supported\n", + dev_err(rtd->dev, "%s: rate %d is not supported\n", __func__, rate); return -EINVAL; } @@ -180,7 +177,6 @@ static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_context *dapm = &codec->dapm; - int err; snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); snd_soc_dapm_enable_pin(dapm, "Speaker"); diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index a5cbdb4f1655..0a9b44c940ce 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -22,8 +22,6 @@ #include <sound/soc.h> #include <sound/pcm_params.h> -#include <mach/dma.h> - #include <linux/platform_data/asoc-s3c.h> #include "dma.h" @@ -702,6 +700,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 +946,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 +966,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; @@ -1263,7 +1266,8 @@ static int samsung_i2s_probe(struct platform_device *pdev) return 0; err: - release_mem_region(regs_base, resource_size(res)); + if (res) + release_mem_region(regs_base, resource_size(res)); return ret; } 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/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c index 807db417d234..b0800337b79e 100644 --- a/sound/soc/samsung/neo1973_wm8753.c +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -20,6 +20,7 @@ #include <sound/soc.h> +#include <mach/gpio-samsung.h> #include <asm/mach-types.h> #include "regs-iis.h" @@ -191,44 +192,6 @@ static struct snd_soc_ops neo1973_voice_ops = { .hw_free = neo1973_voice_hw_free, }; -/* Shared routes and controls */ - -static const struct snd_soc_dapm_widget neo1973_wm8753_dapm_widgets[] = { - SND_SOC_DAPM_LINE("GSM Line Out", NULL), - SND_SOC_DAPM_LINE("GSM Line In", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Handset Mic", NULL), -}; - -static const struct snd_soc_dapm_route neo1973_wm8753_routes[] = { - /* Connections to the GSM Module */ - {"GSM Line Out", NULL, "MONO1"}, - {"GSM Line Out", NULL, "MONO2"}, - {"RXP", NULL, "GSM Line In"}, - {"RXN", NULL, "GSM Line In"}, - - /* Connections to Headset */ - {"MIC1", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Headset Mic"}, - - /* Call Mic */ - {"MIC2", NULL, "Mic Bias"}, - {"MIC2N", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Handset Mic"}, - - /* Connect the ALC pins */ - {"ACIN", NULL, "ACOP"}, -}; - -static const struct snd_kcontrol_new neo1973_wm8753_controls[] = { - SOC_DAPM_PIN_SWITCH("GSM Line Out"), - SOC_DAPM_PIN_SWITCH("GSM Line In"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), - SOC_DAPM_PIN_SWITCH("Handset Mic"), -}; - -/* GTA02 specific routes and controls */ - static int gta02_speaker_enabled; static int lm4853_set_spk(struct snd_kcontrol *kcontrol, @@ -256,7 +219,34 @@ static int lm4853_event(struct snd_soc_dapm_widget *w, return 0; } -static const struct snd_soc_dapm_route neo1973_gta02_routes[] = { +static const struct snd_soc_dapm_widget neo1973_wm8753_dapm_widgets[] = { + SND_SOC_DAPM_LINE("GSM Line Out", NULL), + SND_SOC_DAPM_LINE("GSM Line In", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_SPK("Handset Spk", NULL), + SND_SOC_DAPM_SPK("Stereo Out", lm4853_event), +}; + +static const struct snd_soc_dapm_route neo1973_wm8753_routes[] = { + /* Connections to the GSM Module */ + {"GSM Line Out", NULL, "MONO1"}, + {"GSM Line Out", NULL, "MONO2"}, + {"RXP", NULL, "GSM Line In"}, + {"RXN", NULL, "GSM Line In"}, + + /* Connections to Headset */ + {"MIC1", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Headset Mic"}, + + /* Call Mic */ + {"MIC2", NULL, "Mic Bias"}, + {"MIC2N", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Handset Mic"}, + + /* Connect the ALC pins */ + {"ACIN", NULL, "ACOP"}, + /* Connections to the amp */ {"Stereo Out", NULL, "LOUT1"}, {"Stereo Out", NULL, "ROUT1"}, @@ -266,7 +256,11 @@ static const struct snd_soc_dapm_route neo1973_gta02_routes[] = { {"Handset Spk", NULL, "ROUT2"}, }; -static const struct snd_kcontrol_new neo1973_gta02_wm8753_controls[] = { +static const struct snd_kcontrol_new neo1973_wm8753_controls[] = { + SOC_DAPM_PIN_SWITCH("GSM Line Out"), + SOC_DAPM_PIN_SWITCH("GSM Line In"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Handset Mic"), SOC_DAPM_PIN_SWITCH("Handset Spk"), SOC_DAPM_PIN_SWITCH("Stereo Out"), @@ -275,86 +269,32 @@ static const struct snd_kcontrol_new neo1973_gta02_wm8753_controls[] = { lm4853_set_spk), }; -static const struct snd_soc_dapm_widget neo1973_gta02_wm8753_dapm_widgets[] = { - SND_SOC_DAPM_SPK("Handset Spk", NULL), - SND_SOC_DAPM_SPK("Stereo Out", lm4853_event), -}; - -static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - int ret; - - ret = snd_soc_dapm_new_controls(dapm, neo1973_gta02_wm8753_dapm_widgets, - ARRAY_SIZE(neo1973_gta02_wm8753_dapm_widgets)); - if (ret) - return ret; - - ret = snd_soc_dapm_add_routes(dapm, neo1973_gta02_routes, - ARRAY_SIZE(neo1973_gta02_routes)); - if (ret) - return ret; - - ret = snd_soc_add_card_controls(codec->card, neo1973_gta02_wm8753_controls, - ARRAY_SIZE(neo1973_gta02_wm8753_controls)); - if (ret) - return ret; - - snd_soc_dapm_disable_pin(dapm, "Stereo Out"); - snd_soc_dapm_disable_pin(dapm, "Handset Spk"); - snd_soc_dapm_ignore_suspend(dapm, "Stereo Out"); - snd_soc_dapm_ignore_suspend(dapm, "Handset Spk"); - - return 0; -} - static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - int ret; + struct snd_soc_card *card = rtd->card; /* set up NC codec pins */ - snd_soc_dapm_nc_pin(dapm, "OUT3"); - snd_soc_dapm_nc_pin(dapm, "OUT4"); - snd_soc_dapm_nc_pin(dapm, "LINE1"); - snd_soc_dapm_nc_pin(dapm, "LINE2"); - - /* Add neo1973 specific widgets */ - ret = snd_soc_dapm_new_controls(dapm, neo1973_wm8753_dapm_widgets, - ARRAY_SIZE(neo1973_wm8753_dapm_widgets)); - if (ret) - return ret; - - /* add neo1973 specific controls */ - ret = snd_soc_add_card_controls(rtd->card, neo1973_wm8753_controls, - ARRAY_SIZE(neo1973_wm8753_controls)); - if (ret) - return ret; - - /* set up neo1973 specific audio routes */ - ret = snd_soc_dapm_add_routes(dapm, neo1973_wm8753_routes, - ARRAY_SIZE(neo1973_wm8753_routes)); - if (ret) - return ret; + snd_soc_dapm_nc_pin(&codec->dapm, "OUT3"); + snd_soc_dapm_nc_pin(&codec->dapm, "OUT4"); + snd_soc_dapm_nc_pin(&codec->dapm, "LINE1"); + snd_soc_dapm_nc_pin(&codec->dapm, "LINE2"); /* set endpoints to default off mode */ - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Handset Mic"); + snd_soc_dapm_disable_pin(&card->dapm, "GSM Line Out"); + snd_soc_dapm_disable_pin(&card->dapm, "GSM Line In"); + snd_soc_dapm_disable_pin(&card->dapm, "Headset Mic"); + snd_soc_dapm_disable_pin(&card->dapm, "Handset Mic"); + snd_soc_dapm_disable_pin(&card->dapm, "Stereo Out"); + snd_soc_dapm_disable_pin(&card->dapm, "Handset Spk"); /* allow audio paths from the GSM modem to run during suspend */ - snd_soc_dapm_ignore_suspend(dapm, "GSM Line Out"); - snd_soc_dapm_ignore_suspend(dapm, "GSM Line In"); - snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); - snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); - - if (machine_is_neo1973_gta02()) { - ret = neo1973_gta02_wm8753_init(codec); - if (ret) - return ret; - } + snd_soc_dapm_ignore_suspend(&card->dapm, "GSM Line Out"); + snd_soc_dapm_ignore_suspend(&card->dapm, "GSM Line In"); + snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(&card->dapm, "Handset Mic"); + snd_soc_dapm_ignore_suspend(&card->dapm, "Stereo Out"); + snd_soc_dapm_ignore_suspend(&card->dapm, "Handset Spk"); return 0; } @@ -408,6 +348,13 @@ static struct snd_soc_card neo1973 = { .num_aux_devs = ARRAY_SIZE(neo1973_aux_devs), .codec_conf = neo1973_codec_conf, .num_configs = ARRAY_SIZE(neo1973_codec_conf), + + .controls = neo1973_wm8753_controls, + .num_controls = ARRAY_SIZE(neo1973_wm8753_controls), + .dapm_widgets = neo1973_wm8753_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(neo1973_wm8753_dapm_widgets), + .dapm_routes = neo1973_wm8753_routes, + .num_dapm_routes = ARRAY_SIZE(neo1973_wm8753_routes), }; static struct platform_device *neo1973_snd_device; 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/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index 704460a37005..2982d9e7f268 100644 --- a/sound/soc/samsung/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -24,6 +24,7 @@ #include <sound/soc.h> #include <sound/jack.h> +#include <mach/gpio-samsung.h> #include "regs-iis.h" #include <asm/mach-types.h> @@ -130,10 +131,6 @@ static int rx1950_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - runtime->hw.rate_min = hw_rates.list[0]; - runtime->hw.rate_max = hw_rates.list[hw_rates.count - 1]; - runtime->hw.rates = SNDRV_PCM_RATE_KNOT; - return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_rates); @@ -225,7 +222,6 @@ static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_context *dapm = &codec->dapm; - int err; snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); snd_soc_dapm_enable_pin(dapm, "Speaker"); diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c index fefc56100349..79e7efb9283c 100644 --- a/sound/soc/samsung/s3c-i2s-v2.c +++ b/sound/soc/samsung/s3c-i2s-v2.c @@ -729,7 +729,7 @@ int s3c_i2sv2_register_component(struct device *dev, int id, struct snd_soc_component_driver *cmp_drv, struct snd_soc_dai_driver *dai_drv) { - struct snd_soc_dai_ops *ops = drv->ops; + struct snd_soc_dai_ops *ops = dai_drv->ops; ops->trigger = s3c2412_i2s_trigger; if (!ops->hw_params) @@ -742,8 +742,8 @@ int s3c_i2sv2_register_component(struct device *dev, int id, if (!ops->delay) ops->delay = s3c2412_i2s_delay; - drv->suspend = s3c2412_i2s_suspend; - drv->resume = s3c2412_i2s_resume; + dai_drv->suspend = s3c2412_i2s_suspend; + dai_drv->resume = s3c2412_i2s_resume; return snd_soc_register_component(dev, cmp_drv, dai_drv, 1); } diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c index ea885cb9f76c..d0794458963a 100644 --- a/sound/soc/samsung/s3c2412-i2s.c +++ b/sound/soc/samsung/s3c2412-i2s.c @@ -26,6 +26,8 @@ #include <sound/pcm_params.h> #include <mach/dma.h> +#include <mach/gpio-samsung.h> +#include <plat/gpio-cfg.h> #include "dma.h" #include "regs-i2s-v2.h" diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index 9c8ebd872fac..f31e916dd8c4 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -24,6 +24,8 @@ #include <sound/pcm_params.h> #include <mach/dma.h> +#include <mach/gpio-samsung.h> +#include <plat/gpio-cfg.h> #include "regs-iis.h" #include "dma.h" diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c index 58ae3237ef69..c3b2adafb7b5 100644 --- a/sound/soc/samsung/smartq_wm8987.c +++ b/sound/soc/samsung/smartq_wm8987.c @@ -19,6 +19,7 @@ #include <sound/soc.h> #include <sound/jack.h> +#include <mach/gpio-samsung.h> #include <asm/mach-types.h> #include "i2s.h" diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index b072bd107b31..682eb4f7ba0c 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -152,13 +152,11 @@ static struct snd_soc_card smdk = { .num_links = ARRAY_SIZE(smdk_dai), }; -#ifdef CONFIG_OF static const struct of_device_id samsung_wm8994_of_match[] = { { .compatible = "samsung,smdk-wm8994", .data = &smdk_board_data }, {}, }; MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match); -#endif /* CONFIG_OF */ static int smdk_audio_probe(struct platform_device *pdev) { @@ -188,7 +186,7 @@ static int smdk_audio_probe(struct platform_device *pdev) smdk_dai[0].platform_of_node = smdk_dai[0].cpu_of_node; } - id = of_match_device(samsung_wm8994_of_match, &pdev->dev); + id = of_match_device(of_match_ptr(samsung_wm8994_of_match), &pdev->dev); if (id) *board = *((struct smdk_wm8994_data *)id->data); @@ -204,7 +202,7 @@ static int smdk_audio_probe(struct platform_device *pdev) static struct platform_driver smdk_audio_driver = { .driver = { - .name = "smdk-audio-wm8894", + .name = "smdk-audio-wm8994", .owner = THIS_MODULE, .of_match_table = of_match_ptr(samsung_wm8994_of_match), .pm = &snd_soc_pm_ops, diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c index f21ff608a819..1807b75ccc12 100644 --- a/sound/soc/samsung/tobermory.c +++ b/sound/soc/samsung/tobermory.c @@ -44,6 +44,8 @@ static int tobermory_set_bias_level(struct snd_soc_card *card, SND_SOC_CLOCK_IN); if (ret < 0) { pr_err("Failed to set SYSCLK: %d\n", ret); + snd_soc_dai_set_pll(codec_dai, WM8962_FLL, + 0, 0, 0); return ret; } } 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/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index 1a8b03e4b41b..c85f8eb66c97 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c @@ -89,29 +89,12 @@ struct camelot_pcm { #define DMABRG_PREALLOC_BUFFER 32 * 1024 #define DMABRG_PREALLOC_BUFFER_MAX 32 * 1024 -/* support everything the SSI supports */ -#define DMABRG_RATES \ - SNDRV_PCM_RATE_8000_192000 - -#define DMABRG_FMTS \ - (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \ - SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \ - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \ - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \ - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) - static struct snd_pcm_hardware camelot_pcm_hardware = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH), - .formats = DMABRG_FMTS, - .rates = DMABRG_RATES, - .rate_min = 8000, - .rate_max = 192000, - .channels_min = 2, - .channels_max = 8, /* max of the SSI */ .buffer_bytes_max = DMABRG_PERIOD_MAX, .period_bytes_min = DMABRG_PERIOD_MIN, .period_bytes_max = DMABRG_PERIOD_MAX / 2, diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index b33ca7cd085b..1967f44e7cd4 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; @@ -1777,12 +1787,6 @@ static struct snd_pcm_hardware fsi_pcm_hardware = { SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE, - .formats = FSI_FMTS, - .rates = FSI_RATES, - .rate_min = 8000, - .rate_max = 192000, - .channels_min = 2, - .channels_max = 2, .buffer_bytes_max = 64 * 1024, .period_bytes_min = 32, .period_bytes_max = 8192, @@ -1960,7 +1964,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/migor.c b/sound/soc/sh/migor.c index 5014a884afee..c58c2529f103 100644 --- a/sound/soc/sh/migor.c +++ b/sound/soc/sh/migor.c @@ -136,19 +136,6 @@ static const struct snd_soc_dapm_route audio_map[] = { { "Mic Bias", NULL, "External Microphone" }, }; -static int migor_dai_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, migor_dapm_widgets, - ARRAY_SIZE(migor_dapm_widgets)); - - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - - return 0; -} - /* migor digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link migor_dai = { .name = "wm8978", @@ -158,7 +145,6 @@ static struct snd_soc_dai_link migor_dai = { .platform_name = "siu-pcm-audio", .codec_name = "wm8978.0-001a", .ops = &migor_dai_ops, - .init = migor_dai_init, }; /* migor audio machine driver */ @@ -167,6 +153,11 @@ static struct snd_soc_card snd_soc_migor = { .owner = THIS_MODULE, .dai_link = &migor_dai, .num_links = 1, + + .dapm_widgets = migor_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(migor_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), }; static struct platform_device *migor_snd_device; diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 0ff492df7929..7d0051ced838 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o gen.o scu.o adg.o ssi.o +snd-soc-rcar-objs := core.o gen.o src.o adg.o ssi.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
\ No newline at end of file diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 9430097979a5..953f1cce982d 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -19,52 +19,263 @@ 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; }; #define for_each_rsnd_clk(pos, adg, i) \ - for (i = 0, (pos) = adg->clk[i]; \ - i < CLKMAX; \ - i++, (pos) = adg->clk[i]) + for (i = 0; \ + (i < CLKMAX) && \ + ((pos) = adg->clk[i]); \ + i++) #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) -static enum rsnd_reg rsnd_adg_ssi_reg_get(int id) + +static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) +{ + struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + int id = rsnd_mod_id(mod); + int ws = id; + + if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) { + switch (id) { + case 1: + case 2: + ws = 0; + break; + case 4: + ws = 3; + break; + case 8: + ws = 7; + break; + } + } + + return (0x6 + ws) << 8; +} + +static int rsnd_adg_set_src_timsel_gen2(struct rsnd_dai *rdai, + struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + u32 timsel) +{ + int is_play = rsnd_dai_is_play(rdai, io); + int id = rsnd_mod_id(mod); + int shift = (id % 2) ? 16 : 0; + u32 mask, ws; + u32 in, out; + + ws = rsnd_adg_ssi_ws_timing_gen2(io); + + in = (is_play) ? timsel : ws; + out = (is_play) ? ws : timsel; + + in = in << shift; + out = out << shift; + mask = 0xffff << shift; + + switch (id / 2) { + case 0: + rsnd_mod_bset(mod, SRCIN_TIMSEL0, mask, in); + rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out); + break; + case 1: + rsnd_mod_bset(mod, SRCIN_TIMSEL1, mask, in); + rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out); + break; + case 2: + rsnd_mod_bset(mod, SRCIN_TIMSEL2, mask, in); + rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out); + break; + case 3: + rsnd_mod_bset(mod, SRCIN_TIMSEL3, mask, in); + rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out); + break; + case 4: + rsnd_mod_bset(mod, SRCIN_TIMSEL4, mask, in); + rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out); + break; + } + + return 0; +} + +int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io, + unsigned int src_rate, + unsigned int dst_rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int idx, sel, div, step, ret; + u32 val, en; + unsigned int min, diff; + unsigned int sel_rate [] = { + clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ + clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ + clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ + adg->rbga_rate_for_441khz_div_6,/* 0011: RBGA */ + adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */ + }; + + min = ~0; + val = 0; + en = 0; + for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { + idx = 0; + step = 2; + + if (!sel_rate[sel]) + continue; + + for (div = 2; div <= 98304; div += step) { + diff = abs(src_rate - sel_rate[sel] / div); + if (min > diff) { + val = (sel << 8) | idx; + min = diff; + en = 1 << (sel + 1); /* fixme */ + } + + /* + * step of 0_0000 / 0_0001 / 0_1101 + * are out of order + */ + if ((idx > 2) && (idx % 2)) + step *= 2; + if (idx == 0x1c) { + div += step; + step *= 2; + } + idx++; + } + } + + if (min == ~0) { + dev_err(dev, "no Input clock\n"); + return -EIO; + } + + ret = rsnd_adg_set_src_timsel_gen2(rdai, mod, io, val); + if (ret < 0) { + dev_err(dev, "timsel error\n"); + return ret; + } + + rsnd_mod_bset(mod, DIV_EN, en, en); + + return 0; +} + +int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + u32 val = rsnd_adg_ssi_ws_timing_gen2(io); + + return rsnd_adg_set_src_timsel_gen2(rdai, mod, io, val); +} + +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; +} + +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 +286,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 +312,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 +335,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); + rsnd_adg_set_ssi_clk(mod, data); - dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", id, i, rate); - - /* - * Enable SSIx clock - */ - shift = (id % 4) * 8; - - 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 +367,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 +376,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; } } @@ -191,13 +392,13 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) } int rsnd_adg_probe(struct platform_device *pdev, - struct rcar_snd_info *info, struct rsnd_priv *priv) { struct rsnd_adg *adg; struct device *dev = rsnd_priv_to_dev(priv); - struct clk *clk; + struct clk *clk, *clk_orig; int i; + bool use_old_style = false; adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); if (!adg) { @@ -205,10 +406,39 @@ int rsnd_adg_probe(struct platform_device *pdev, return -ENOMEM; } - adg->clk[CLKA] = clk_get(NULL, "audio_clk_a"); - adg->clk[CLKB] = clk_get(NULL, "audio_clk_b"); - adg->clk[CLKC] = clk_get(NULL, "audio_clk_c"); - adg->clk[CLKI] = clk_get(NULL, "audio_clk_internal"); + clk_orig = devm_clk_get(dev, NULL); + adg->clk[CLKA] = devm_clk_get(dev, "clk_a"); + adg->clk[CLKB] = devm_clk_get(dev, "clk_b"); + adg->clk[CLKC] = devm_clk_get(dev, "clk_c"); + adg->clk[CLKI] = devm_clk_get(dev, "clk_i"); + + /* + * It request device dependent audio clock. + * But above all clks will indicate rsnd module clock + * if platform doesn't it + */ + for_each_rsnd_clk(clk, adg, i) { + if (clk_orig == clk) { + dev_warn(dev, + "doesn't have device dependent clock, use independent clock\n"); + use_old_style = true; + break; + } + } + + /* + * note: + * these exist in order to keep compatible with + * platform which has device independent audio clock, + * but will be removed soon + */ + if (use_old_style) { + adg->clk[CLKA] = devm_clk_get(NULL, "audio_clk_a"); + adg->clk[CLKB] = devm_clk_get(NULL, "audio_clk_b"); + adg->clk[CLKC] = devm_clk_get(NULL, "audio_clk_c"); + adg->clk[CLKI] = devm_clk_get(NULL, "audio_clk_internal"); + } + for_each_rsnd_clk(clk, adg, i) { if (IS_ERR(clk)) { dev_err(dev, "Audio clock failed\n"); @@ -224,14 +454,3 @@ int rsnd_adg_probe(struct platform_device *pdev, return 0; } - -void rsnd_adg_remove(struct platform_device *pdev, - struct rsnd_priv *priv) -{ - struct rsnd_adg *adg = priv->adg; - struct clk *clk; - int i; - - for_each_rsnd_clk(clk, adg, i) - clk_put(clk); -} diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index b3653d37f75f..6a1b45df8101 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -73,13 +73,13 @@ * | +- ssi[2] * | ... * | - * | ** these control scu + * | ** these control src * | - * +- scu + * +- src * | - * +- scu[0] - * +- scu[1] - * +- scu[2] + * +- src[0] + * +- src[1] + * +- src[2] * ... * * @@ -107,6 +107,11 @@ (!(priv->info->func) ? 0 : \ priv->info->func(param)) +#define rsnd_is_enable_path(io, name) \ + ((io)->info ? (io)->info->name : NULL) +#define rsnd_info_id(priv, io, name) \ + ((io)->info->name - priv->info->name##_info) + /* * rsnd_mod functions */ @@ -121,17 +126,19 @@ char *rsnd_mod_name(struct rsnd_mod *mod) void rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, struct rsnd_mod_ops *ops, + enum rsnd_mod_type type, int id) { mod->priv = priv; mod->id = id; mod->ops = ops; - INIT_LIST_HEAD(&mod->list); + mod->type = type; } /* * rsnd_dma functions */ +static void __rsnd_dma_start(struct rsnd_dma *dma); static void rsnd_dma_continue(struct rsnd_dma *dma) { /* push next A or B plane */ @@ -142,8 +149,9 @@ static void rsnd_dma_continue(struct rsnd_dma *dma) void rsnd_dma_start(struct rsnd_dma *dma) { /* push both A and B plane*/ + dma->offset = 0; dma->submit_loop = 2; - schedule_work(&dma->work); + __rsnd_dma_start(dma); } void rsnd_dma_stop(struct rsnd_dma *dma) @@ -156,12 +164,26 @@ void rsnd_dma_stop(struct rsnd_dma *dma) static void rsnd_dma_complete(void *data) { struct rsnd_dma *dma = (struct rsnd_dma *)data; - struct rsnd_priv *priv = dma->priv; + struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_priv *priv = rsnd_mod_to_priv(rsnd_dma_to_mod(dma)); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); unsigned long flags; rsnd_lock(priv, flags); - dma->complete(dma); + /* + * Renesas sound Gen1 needs 1 DMAC, + * Gen2 needs 2 DMAC. + * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. + * But, Audio-DMAC-peri-peri doesn't have interrupt, + * and this driver is assuming that here. + * + * If Audio-DMAC-peri-peri has interrpt, + * rsnd_dai_pointer_update() will be called twice, + * ant it will breaks io->byte_pos + */ + + rsnd_dai_pointer_update(io, io->byte_per_period); if (dma->submit_loop) rsnd_dma_continue(dma); @@ -169,20 +191,23 @@ static void rsnd_dma_complete(void *data) rsnd_unlock(priv, flags); } -static void rsnd_dma_do_work(struct work_struct *work) +static void __rsnd_dma_start(struct rsnd_dma *dma) { - struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work); - struct rsnd_priv *priv = dma->priv; + struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); struct dma_async_tx_descriptor *desc; dma_addr_t buf; - size_t len; + size_t len = io->byte_per_period; int i; for (i = 0; i < dma->submit_loop; i++) { - if (dma->inquiry(dma, &buf, &len) < 0) - return; + buf = runtime->dma_addr + + rsnd_dai_pointer_offset(io, dma->offset + len); + dma->offset = len; desc = dmaengine_prep_slave_single( dma->chan, buf, len, dma->dir, @@ -204,16 +229,20 @@ static void rsnd_dma_do_work(struct work_struct *work) } } +static void rsnd_dma_do_work(struct work_struct *work) +{ + struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work); + + __rsnd_dma_start(dma); +} + int rsnd_dma_available(struct rsnd_dma *dma) { return !!dma->chan; } int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, - int is_play, int id, - int (*inquiry)(struct rsnd_dma *dma, - dma_addr_t *buf, int *len), - int (*complete)(struct rsnd_dma *dma)) + int is_play, int id) { struct device *dev = rsnd_priv_to_dev(priv); struct dma_slave_config cfg; @@ -246,9 +275,6 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, goto rsnd_dma_init_err; dma->dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - dma->priv = priv; - dma->inquiry = inquiry; - dma->complete = complete; INIT_WORK(&dma->work, rsnd_dma_do_work); return 0; @@ -271,26 +297,42 @@ void rsnd_dma_quit(struct rsnd_priv *priv, /* * rsnd_dai functions */ -#define rsnd_dai_call(rdai, io, fn) \ -({ \ - struct rsnd_mod *mod, *n; \ - int ret = 0; \ - for_each_rsnd_mod(mod, n, io) { \ - ret = rsnd_mod_call(mod, fn, rdai, io); \ - if (ret < 0) \ - break; \ - } \ - ret; \ +#define __rsnd_mod_call(mod, func, rdai, io) \ +({ \ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ + struct device *dev = rsnd_priv_to_dev(priv); \ + dev_dbg(dev, "%s [%d] %s\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \ + (mod)->ops->func(mod, rdai, io); \ +}) + +#define rsnd_mod_call(mod, func, rdai, io) \ + (!(mod) ? -ENODEV : \ + !((mod)->ops->func) ? 0 : \ + __rsnd_mod_call(mod, func, (rdai), (io))) + +#define rsnd_dai_call(rdai, io, fn) \ +({ \ + struct rsnd_mod *mod; \ + int ret = 0, i; \ + for (i = 0; i < RSND_MOD_MAX; i++) { \ + mod = (io)->mod[i]; \ + if (!mod) \ + continue; \ + ret = rsnd_mod_call(mod, fn, (rdai), (io)); \ + if (ret < 0) \ + break; \ + } \ + ret; \ }) -int rsnd_dai_connect(struct rsnd_dai *rdai, - struct rsnd_mod *mod, - struct rsnd_dai_stream *io) +static int rsnd_dai_connect(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { if (!mod) return -EIO; - if (!list_empty(&mod->list)) { + if (io->mod[mod->type]) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); @@ -300,14 +342,8 @@ int rsnd_dai_connect(struct rsnd_dai *rdai, return -EIO; } - list_add_tail(&mod->list, &io->head); - - return 0; -} - -int rsnd_dai_disconnect(struct rsnd_mod *mod) -{ - list_del_init(&mod->list); + io->mod[mod->type] = mod; + mod->io = io; return 0; } @@ -316,7 +352,7 @@ int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai) { int id = rdai - priv->rdai; - if ((id < 0) || (id >= rsnd_dai_nr(priv))) + if ((id < 0) || (id >= rsnd_rdai_nr(priv))) return -EINVAL; return id; @@ -324,7 +360,7 @@ int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai) struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id) { - if ((id < 0) || (id >= rsnd_dai_nr(priv))) + if ((id < 0) || (id >= rsnd_rdai_nr(priv))) return NULL; return priv->rdai + id; @@ -382,10 +418,6 @@ static int rsnd_dai_stream_init(struct rsnd_dai_stream *io, { struct snd_pcm_runtime *runtime = substream->runtime; - if (!list_empty(&io->head)) - return -EIO; - - INIT_LIST_HEAD(&io->head); io->substream = substream; io->byte_pos = 0; io->period_pos = 0; @@ -440,10 +472,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) goto dai_trigger_end; - ret = rsnd_gen_path_init(priv, rdai, io); - if (ret < 0) - goto dai_trigger_end; - ret = rsnd_dai_call(rdai, io, init); if (ret < 0) goto dai_trigger_end; @@ -461,10 +489,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) goto dai_trigger_end; - ret = rsnd_gen_path_exit(priv, rdai, io); - if (ret < 0) - goto dai_trigger_end; - ret = rsnd_platform_call(priv, dai, stop, ssi_id); if (ret < 0) goto dai_trigger_end; @@ -540,24 +564,86 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { .set_fmt = rsnd_soc_dai_set_fmt, }; +static int rsnd_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_mod *mod; + struct rsnd_dai_platform_info *dai_info = rdai->info; + int ret; + int ssi_id = -1; + int src_id = -1; + + /* + * Gen1 is created by SRU/SSI, and this SRU is base module of + * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU) + * + * Easy image is.. + * Gen1 SRU = Gen2 SCU + SSIU + etc + * + * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is + * using fixed path. + */ + if (dai_info) { + if (rsnd_is_enable_path(io, ssi)) + ssi_id = rsnd_info_id(priv, io, ssi); + if (rsnd_is_enable_path(io, src)) + src_id = rsnd_info_id(priv, io, src); + } else { + /* get SSI's ID */ + mod = rsnd_ssi_mod_get_frm_dai(priv, + rsnd_dai_id(priv, rdai), + rsnd_dai_is_play(rdai, io)); + if (!mod) + return 0; + ssi_id = src_id = rsnd_mod_id(mod); + } + + ret = 0; + + /* SRC */ + if (src_id >= 0) { + mod = rsnd_src_mod_get(priv, src_id); + ret = rsnd_dai_connect(mod, io); + if (ret < 0) + return ret; + } + + /* SSI */ + if (ssi_id >= 0) { + mod = rsnd_ssi_mod_get(priv, ssi_id); + ret = rsnd_dai_connect(mod, io); + if (ret < 0) + return ret; + } + + return ret; +} + static int rsnd_dai_probe(struct platform_device *pdev, - struct rcar_snd_info *info, struct rsnd_priv *priv) { struct snd_soc_dai_driver *drv; + struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct rsnd_dai *rdai; struct rsnd_mod *pmod, *cmod; struct device *dev = rsnd_priv_to_dev(priv); - int dai_nr; + int dai_nr = info->dai_info_nr; int i; - /* get max dai nr */ - for (dai_nr = 0; dai_nr < 32; dai_nr++) { - pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1); - cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0); + /* + * dai_nr should be set via dai_info_nr, + * but allow it to keeping compatible + */ + if (!dai_nr) { + /* get max dai nr */ + for (dai_nr = 0; dai_nr < 32; dai_nr++) { + pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1); + cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0); - if (!pmod && !cmod) - break; + if (!pmod && !cmod) + break; + } } if (!dai_nr) { @@ -572,7 +658,13 @@ static int rsnd_dai_probe(struct platform_device *pdev, return -ENOMEM; } + priv->rdai_nr = dai_nr; + priv->daidrv = drv; + priv->rdai = rdai; + for (i = 0; i < dai_nr; i++) { + if (info->dai_info) + rdai[i].info = &info->dai_info[i]; pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1); cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0); @@ -580,9 +672,6 @@ static int rsnd_dai_probe(struct platform_device *pdev, /* * init rsnd_dai */ - INIT_LIST_HEAD(&rdai[i].playback.head); - INIT_LIST_HEAD(&rdai[i].capture.head); - snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i); /* @@ -595,12 +684,20 @@ static int rsnd_dai_probe(struct platform_device *pdev, drv[i].playback.formats = RSND_FMTS; drv[i].playback.channels_min = 2; drv[i].playback.channels_max = 2; + + if (info->dai_info) + rdai[i].playback.info = &info->dai_info[i].playback; + rsnd_path_init(priv, &rdai[i], &rdai[i].playback); } if (cmod) { drv[i].capture.rates = RSND_RATES; drv[i].capture.formats = RSND_FMTS; drv[i].capture.channels_min = 2; drv[i].capture.channels_max = 2; + + if (info->dai_info) + rdai[i].capture.info = &info->dai_info[i].capture; + rsnd_path_init(priv, &rdai[i], &rdai[i].capture); } dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name, @@ -608,18 +705,9 @@ static int rsnd_dai_probe(struct platform_device *pdev, cmod ? "capture" : " -- "); } - priv->dai_nr = dai_nr; - priv->daidrv = drv; - priv->rdai = rdai; - return 0; } -static void rsnd_dai_remove(struct platform_device *pdev, - struct rsnd_priv *priv) -{ -} - /* * pcm ops */ @@ -628,12 +716,6 @@ static struct snd_pcm_hardware rsnd_pcm_hardware = { SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE, - .formats = RSND_FMTS, - .rates = RSND_RATES, - .rate_min = 8000, - .rate_max = 192000, - .channels_min = 2, - .channels_max = 2, .buffer_bytes_max = 64 * 1024, .period_bytes_min = 32, .period_bytes_max = 8192, @@ -719,7 +801,16 @@ static int rsnd_probe(struct platform_device *pdev) struct rcar_snd_info *info; struct rsnd_priv *priv; struct device *dev = &pdev->dev; - int ret; + struct rsnd_dai *rdai; + int (*probe_func[])(struct platform_device *pdev, + struct rsnd_priv *priv) = { + rsnd_gen_probe, + rsnd_ssi_probe, + rsnd_src_probe, + rsnd_adg_probe, + rsnd_dai_probe, + }; + int ret, i; info = pdev->dev.platform_data; if (!info) { @@ -743,25 +834,21 @@ static int rsnd_probe(struct platform_device *pdev) /* * init each module */ - ret = rsnd_gen_probe(pdev, info, priv); - if (ret < 0) - return ret; - - ret = rsnd_scu_probe(pdev, info, priv); - if (ret < 0) - return ret; + for (i = 0; i < ARRAY_SIZE(probe_func); i++) { + ret = probe_func[i](pdev, priv); + if (ret) + return ret; + } - ret = rsnd_adg_probe(pdev, info, priv); - if (ret < 0) - return ret; + for_each_rsnd_dai(rdai, priv, i) { + ret = rsnd_dai_call(rdai, &rdai->playback, probe); + if (ret) + return ret; - ret = rsnd_ssi_probe(pdev, info, priv); - if (ret < 0) - return ret; - - ret = rsnd_dai_probe(pdev, info, priv); - if (ret < 0) - return ret; + ret = rsnd_dai_call(rdai, &rdai->capture, probe); + if (ret) + return ret; + } /* * asoc register @@ -773,7 +860,7 @@ static int rsnd_probe(struct platform_device *pdev) } ret = snd_soc_register_component(dev, &rsnd_soc_component, - priv->daidrv, rsnd_dai_nr(priv)); + priv->daidrv, rsnd_rdai_nr(priv)); if (ret < 0) { dev_err(dev, "cannot snd dai register\n"); goto exit_snd_soc; @@ -795,17 +882,20 @@ exit_snd_soc: static int rsnd_remove(struct platform_device *pdev) { struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); + struct rsnd_dai *rdai; + int ret, i; pm_runtime_disable(&pdev->dev); - /* - * remove each module - */ - rsnd_ssi_remove(pdev, priv); - rsnd_adg_remove(pdev, priv); - rsnd_scu_remove(pdev, priv); - rsnd_dai_remove(pdev, priv); - rsnd_gen_remove(pdev, priv); + for_each_rsnd_dai(rdai, priv, i) { + ret = rsnd_dai_call(rdai, &rdai->playback, remove); + if (ret) + return ret; + + ret = rsnd_dai_call(rdai, &rdai->capture, remove); + if (ret) + return ret; + } return 0; } diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 61212ee97c28..9094970dbdfb 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,73 +116,157 @@ 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); } +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; + + memset(®c, 0, sizeof(regc)); + regc.reg_bits = 32; + regc.val_bits = 32; + + gen->regmap = devm_regmap_init(dev, &rsnd_regmap_bus, priv, ®c); + 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; +} + /* * Gen2 - * will be filled in the future */ -/* - * Gen1 - */ -static int rsnd_gen1_path_init(struct rsnd_priv *priv, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) +/* 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, SSI_BUSIF_MODE, 0x0, 0x80), + RSND_GEN2_M_REG(gen, SSIU, SSI_BUSIF_ADINR,0x4, 0x80), + RSND_GEN2_M_REG(gen, SSIU, SSI_CTRL, 0x10, 0x80), + RSND_GEN2_M_REG(gen, SSIU, INT_ENABLE, 0x18, 0x80), + + RSND_GEN2_M_REG(gen, SCU, SRC_BUSIF_MODE, 0x0, 0x20), + RSND_GEN2_M_REG(gen, SCU, SRC_ROUTE_MODE0,0xc, 0x20), + RSND_GEN2_M_REG(gen, SCU, SRC_CTRL, 0x10, 0x20), + RSND_GEN2_M_REG(gen, SCU, SRC_SWRSR, 0x200, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_SRCIR, 0x204, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_ADINR, 0x214, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_IFSCR, 0x21c, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_IFSVR, 0x220, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_SRCCR, 0x224, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_BSDSR, 0x22c, 0x40), + RSND_GEN2_M_REG(gen, SCU, SRC_BSISR, 0x238, 0x40), + + 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_S_REG(gen, ADG, DIV_EN, 0x30), + RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL0, 0x34), + RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL1, 0x38), + RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL2, 0x3c), + RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL3, 0x40), + RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL4, 0x44), + RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL0, 0x48), + RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL1, 0x4c), + RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL2, 0x50), + RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL3, 0x54), + RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL4, 0x58), + + 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 rsnd_priv *priv) { - struct rsnd_mod *mod; + 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; - int id; /* - * Gen1 is created by SRU/SSI, and this SRU is base module of - * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU) - * - * Easy image is.. - * Gen1 SRU = Gen2 SCU + SSIU + etc - * - * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is - * using fixed path. - * - * Then, SSI id = SCU id here + * 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; - /* get SSI's ID */ - mod = rsnd_ssi_mod_get_frm_dai(priv, - rsnd_dai_id(priv, rdai), - rsnd_dai_is_play(rdai, io)); - id = rsnd_mod_id(mod); - - /* SSI */ - mod = rsnd_ssi_mod_get(priv, id); - ret = rsnd_dai_connect(rdai, mod, io); + ret = rsnd_gen2_regmap_init(priv, gen); if (ret < 0) return ret; - /* SCU */ - mod = rsnd_scu_mod_get(priv, id); - ret = rsnd_dai_connect(rdai, mod, io); + dev_dbg(dev, "Gen2 device probed\n"); + dev_dbg(dev, "SCU : %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 ret; + return 0; } -static int rsnd_gen1_path_exit(struct rsnd_priv *priv, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_mod *mod, *n; - int ret = 0; - - /* - * remove all mod from rdai - */ - for_each_rsnd_mod(mod, n, io) - ret |= rsnd_dai_disconnect(mod); - - return ret; -} +/* + * Gen1 + */ /* single address mapping */ #define RSND_GEN1_S_REG(gen, reg, id, offset) \ @@ -189,19 +278,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_BUSIF_MODE, 0x20, 0x4), + 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,28 +312,10 @@ static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) RSND_GEN1_M_REG(gen, SSI, SSIWSR, 0x20, 0x40), }; - memset(®c, 0, sizeof(regc)); - regc.reg_bits = 32; - regc.val_bits = 32; - - gen->regmap = devm_regmap_init(dev, &rsnd_regmap_bus, priv, ®c); - 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, - struct rcar_snd_info *info, struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); @@ -281,45 +356,15 @@ 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 +372,16 @@ int rsnd_gen_probe(struct platform_device *pdev, return -ENOMEM; } - if (rsnd_is_gen1(priv)) - gen->ops = &rsnd_gen1_ops; - - if (!gen->ops) { - dev_err(dev, "unknown generation R-Car sound device\n"); - return -ENODEV; - } - priv->gen = gen; - return gen->ops->probe(pdev, info, priv); -} + ret = -ENODEV; + if (rsnd_is_gen1(priv)) + ret = rsnd_gen1_probe(pdev, priv); + else if (rsnd_is_gen2(priv)) + ret = rsnd_gen2_probe(pdev, priv); -void rsnd_gen_remove(struct platform_device *pdev, - struct rsnd_priv *priv) -{ - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + if (ret < 0) + dev_err(dev, "unknown generation R-Car sound device\n"); - gen->ops->remove(pdev, priv); + return ret; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 9e463e50e7e6..c46e0afa54ae 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -31,16 +31,17 @@ * 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_SSI_MODE0, RSND_REG_SSI_MODE1, - RSND_REG_BUSIF_MODE, - RSND_REG_BUSIF_ADINR, + RSND_REG_SRC_BUSIF_MODE, + 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, /* ADG */ RSND_REG_BRRA, @@ -48,10 +49,6 @@ enum rsnd_reg { RSND_REG_SSICKR, 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, /* SSI */ RSND_REG_SSICR, @@ -60,9 +57,62 @@ enum rsnd_reg { RSND_REG_SSIRDR, RSND_REG_SSIWSR, + /* SHARE see below */ + RSND_REG_SHARE01, + RSND_REG_SHARE02, + RSND_REG_SHARE03, + RSND_REG_SHARE04, + RSND_REG_SHARE05, + RSND_REG_SHARE06, + RSND_REG_SHARE07, + RSND_REG_SHARE08, + RSND_REG_SHARE09, + RSND_REG_SHARE10, + RSND_REG_SHARE11, + RSND_REG_SHARE12, + RSND_REG_SHARE13, + RSND_REG_SHARE14, + RSND_REG_SHARE15, + RSND_REG_SHARE16, + RSND_REG_SHARE17, + RSND_REG_SHARE18, + RSND_REG_SHARE19, + RSND_REG_MAX, }; +/* Gen1 only */ +#define RSND_REG_SRC_ROUTE_SEL RSND_REG_SHARE01 +#define RSND_REG_SRC_TMG_SEL0 RSND_REG_SHARE02 +#define RSND_REG_SRC_TMG_SEL1 RSND_REG_SHARE03 +#define RSND_REG_SRC_TMG_SEL2 RSND_REG_SHARE04 +#define RSND_REG_SRC_ROUTE_CTRL RSND_REG_SHARE05 +#define RSND_REG_SRC_MNFSR RSND_REG_SHARE06 +#define RSND_REG_AUDIO_CLK_SEL3 RSND_REG_SHARE07 +#define RSND_REG_AUDIO_CLK_SEL4 RSND_REG_SHARE08 +#define RSND_REG_AUDIO_CLK_SEL5 RSND_REG_SHARE09 + +/* Gen2 only */ +#define RSND_REG_SRC_CTRL RSND_REG_SHARE01 +#define RSND_REG_SSI_CTRL RSND_REG_SHARE02 +#define RSND_REG_SSI_BUSIF_MODE RSND_REG_SHARE03 +#define RSND_REG_SSI_BUSIF_ADINR RSND_REG_SHARE04 +#define RSND_REG_INT_ENABLE RSND_REG_SHARE05 +#define RSND_REG_SRC_BSDSR RSND_REG_SHARE06 +#define RSND_REG_SRC_BSISR RSND_REG_SHARE07 +#define RSND_REG_DIV_EN RSND_REG_SHARE08 +#define RSND_REG_SRCIN_TIMSEL0 RSND_REG_SHARE09 +#define RSND_REG_SRCIN_TIMSEL1 RSND_REG_SHARE10 +#define RSND_REG_SRCIN_TIMSEL2 RSND_REG_SHARE11 +#define RSND_REG_SRCIN_TIMSEL3 RSND_REG_SHARE12 +#define RSND_REG_SRCIN_TIMSEL4 RSND_REG_SHARE13 +#define RSND_REG_SRCOUT_TIMSEL0 RSND_REG_SHARE14 +#define RSND_REG_SRCOUT_TIMSEL1 RSND_REG_SHARE15 +#define RSND_REG_SRCOUT_TIMSEL2 RSND_REG_SHARE16 +#define RSND_REG_SRCOUT_TIMSEL3 RSND_REG_SHARE17 +#define RSND_REG_SRCOUT_TIMSEL4 RSND_REG_SHARE18 +#define RSND_REG_AUDIO_CLK_SEL2 RSND_REG_SHARE19 + struct rsnd_priv; struct rsnd_mod; struct rsnd_dai; @@ -88,24 +138,20 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, * R-Car DMA */ struct rsnd_dma { - struct rsnd_priv *priv; struct sh_dmae_slave slave; struct work_struct work; struct dma_chan *chan; enum dma_data_direction dir; - int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len); - int (*complete)(struct rsnd_dma *dma); int submit_loop; + int offset; /* it cares A/B plane */ }; void rsnd_dma_start(struct rsnd_dma *dma); void rsnd_dma_stop(struct rsnd_dma *dma); int rsnd_dma_available(struct rsnd_dma *dma); int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, - int is_play, int id, - int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len), - int (*complete)(struct rsnd_dma *dma)); + int is_play, int id); void rsnd_dma_quit(struct rsnd_priv *priv, struct rsnd_dma *dma); @@ -113,9 +159,20 @@ void rsnd_dma_quit(struct rsnd_priv *priv, /* * R-Car sound mod */ +enum rsnd_mod_type { + RSND_MOD_SRC = 0, + RSND_MOD_SSI, + RSND_MOD_MAX, +}; struct rsnd_mod_ops { char *name; + int (*probe)(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); + int (*remove)(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); int (*init)(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io); @@ -130,28 +187,26 @@ struct rsnd_mod_ops { struct rsnd_dai_stream *io); }; +struct rsnd_dai_stream; struct rsnd_mod { int id; + enum rsnd_mod_type type; struct rsnd_priv *priv; struct rsnd_mod_ops *ops; - struct list_head list; /* connect to rsnd_dai playback/capture */ struct rsnd_dma dma; + struct rsnd_dai_stream *io; }; #define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_to_dma(mod) (&(mod)->dma) #define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) +#define rsnd_mod_to_io(mod) ((mod)->io) #define rsnd_mod_id(mod) ((mod)->id) -#define for_each_rsnd_mod(pos, n, io) \ - list_for_each_entry_safe(pos, n, &(io)->head, list) -#define rsnd_mod_call(mod, func, rdai, io) \ - (!(mod) ? -ENODEV : \ - !((mod)->ops->func) ? 0 : \ - (mod)->ops->func(mod, rdai, io)) void rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, struct rsnd_mod_ops *ops, + enum rsnd_mod_type type, int id); char *rsnd_mod_name(struct rsnd_mod *mod); @@ -160,13 +215,16 @@ char *rsnd_mod_name(struct rsnd_mod *mod); */ #define RSND_DAI_NAME_SIZE 16 struct rsnd_dai_stream { - struct list_head head; /* head of rsnd_mod list */ struct snd_pcm_substream *substream; + struct rsnd_mod *mod[RSND_MOD_MAX]; + struct rsnd_dai_path_info *info; /* rcar_snd.h */ int byte_pos; int period_pos; int byte_per_period; int next_period_byte; }; +#define rsnd_io_to_mod_ssi(io) ((io)->mod[RSND_MOD_SSI]) +#define rsnd_io_to_mod_src(io) ((io)->mod[RSND_MOD_SRC]) struct rsnd_dai { char name[RSND_DAI_NAME_SIZE]; @@ -174,23 +232,21 @@ 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) +#define rsnd_rdai_nr(priv) ((priv)->rdai_nr) #define for_each_rsnd_dai(rdai, priv, i) \ - for (i = 0, (rdai) = rsnd_dai_get(priv, i); \ - i < rsnd_dai_nr(priv); \ - i++, (rdai) = rsnd_dai_get(priv, i)) + for (i = 0; \ + (i < rsnd_rdai_nr(priv)) && \ + ((rdai) = rsnd_dai_get(priv, i)); \ + i++) struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id); -int rsnd_dai_disconnect(struct rsnd_mod *mod); -int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod, - struct rsnd_dai_stream *io); int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai); #define rsnd_dai_get_platform_info(rdai) ((rdai)->info) @@ -198,21 +254,13 @@ int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai); void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); +#define rsnd_dai_is_clk_master(rdai) ((rdai)->clk_master) /* * R-Car Gen1/Gen2 */ int rsnd_gen_probe(struct platform_device *pdev, - struct rcar_snd_info *info, struct rsnd_priv *priv); -void rsnd_gen_remove(struct platform_device *pdev, - struct rsnd_priv *priv); -int rsnd_gen_path_init(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); void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); @@ -225,10 +273,19 @@ void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod); int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); int rsnd_adg_probe(struct platform_device *pdev, - struct rcar_snd_info *info, - struct rsnd_priv *priv); -void rsnd_adg_remove(struct platform_device *pdev, struct rsnd_priv *priv); +int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, + struct rsnd_mod *mod, + unsigned int src_rate, + unsigned int dst_rate); +int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io, + unsigned int src_rate, + unsigned int dst_rate); +int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); /* * R-Car sound priv @@ -245,10 +302,10 @@ struct rsnd_priv { void *gen; /* - * below value will be filled on rsnd_scu_probe() + * below value will be filled on rsnd_src_probe() */ - void *scu; - int scu_nr; + void *src; + int src_nr; /* * below value will be filled on rsnd_adg_probe() @@ -258,42 +315,62 @@ struct rsnd_priv { /* * below value will be filled on rsnd_ssi_probe() */ - void *ssiu; + void *ssi; + int ssi_nr; /* * below value will be filled on rsnd_dai_probe() */ struct snd_soc_dai_driver *daidrv; struct rsnd_dai *rdai; - int dai_nr; + int rdai_nr; }; #define rsnd_priv_to_dev(priv) ((priv)->dev) +#define rsnd_priv_to_info(priv) ((priv)->info) #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) +#define rsnd_info_is_playback(priv, type) \ +({ \ + struct rcar_snd_info *info = rsnd_priv_to_info(priv); \ + int i, is_play = 0; \ + for (i = 0; i < info->dai_info_nr; i++) { \ + if (info->dai_info[i].playback.type == (type)->info) { \ + is_play = 1; \ + break; \ + } \ + } \ + is_play; \ +}) + /* - * R-Car SCU + * R-Car SRC */ -int rsnd_scu_probe(struct platform_device *pdev, - struct rcar_snd_info *info, +int rsnd_src_probe(struct platform_device *pdev, struct rsnd_priv *priv); -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); -#define rsnd_scu_nr(priv) ((priv)->scu_nr) +struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); +unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + struct snd_pcm_runtime *runtime); +int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); + +#define rsnd_src_nr(priv) ((priv)->src_nr) /* * R-Car SSI */ int rsnd_ssi_probe(struct platform_device *pdev, - struct rcar_snd_info *info, - struct rsnd_priv *priv); -void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, int dai_id, int is_play); +int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); +int rsnd_ssi_is_play(struct rsnd_mod *mod); #endif diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c deleted file mode 100644 index fa8fa15860b9..000000000000 --- a/sound/soc/sh/rcar/scu.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Renesas R-Car SCU support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * 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. - */ -#include "rsnd.h" - -struct rsnd_scu { - struct rsnd_scu_platform_info *info; /* rcar_snd.h */ - struct rsnd_mod mod; -}; - -#define rsnd_scu_mode_flags(p) ((p)->info->flags) - -/* - * ADINR - */ -#define OTBL_24 (0 << 16) -#define OTBL_22 (2 << 16) -#define OTBL_20 (4 << 16) -#define OTBL_18 (6 << 16) -#define OTBL_16 (8 << 16) - - -#define rsnd_mod_to_scu(_mod) \ - container_of((_mod), struct rsnd_scu, mod) - -#define for_each_rsnd_scu(pos, priv, i) \ - for ((i) = 0; \ - ((i) < rsnd_scu_nr(priv)) && \ - ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ - i++) - -static int rsnd_scu_set_route(struct rsnd_priv *priv, - struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct scu_route_config { - u32 mask; - int shift; - } routes[] = { - { 0xF, 0, }, /* 0 */ - { 0xF, 4, }, /* 1 */ - { 0xF, 8, }, /* 2 */ - { 0x7, 12, }, /* 3 */ - { 0x7, 16, }, /* 4 */ - { 0x7, 20, }, /* 5 */ - { 0x7, 24, }, /* 6 */ - { 0x3, 28, }, /* 7 */ - { 0x3, 30, }, /* 8 */ - }; - - u32 mask; - u32 val; - int shift; - int id; - - /* - * Gen1 only - */ - if (!rsnd_is_gen1(priv)) - return 0; - - id = rsnd_mod_id(mod); - if (id < 0 || id >= ARRAY_SIZE(routes)) - return -EIO; - - /* - * SRC_ROUTE_SELECT - */ - val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; - val = val << routes[id].shift; - mask = routes[id].mask << routes[id].shift; - - rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); - - /* - * SRC_TIMING_SELECT - */ - shift = (id % 4) * 8; - mask = 0x1F << shift; - if (8 == id) /* SRU8 is very special */ - val = id << shift; - else - val = (id + 1) << shift; - - switch (id / 4) { - case 0: - rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); - break; - case 1: - rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); - break; - case 2: - rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); - break; - } - - 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) -{ - int id = rsnd_mod_id(mod); - u32 val; - - if (rsnd_is_gen1(priv)) { - val = (1 << id); - rsnd_mod_bset(mod, SRC_CTRL, val, val); - } - - return 0; -} - -static int rsnd_scu_set_hpbif(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); - u32 adinr = runtime->channels; - - switch (runtime->sample_bits) { - case 16: - adinr |= OTBL_16; - break; - case 32: - adinr |= OTBL_24; - break; - default: - return -EIO; - } - - rsnd_mod_write(mod, BUSIF_MODE, 1); - rsnd_mod_write(mod, BUSIF_ADINR, adinr); - - return 0; -} - -bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod) -{ - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); - u32 flags = rsnd_scu_mode_flags(scu); - - return !!(flags & RSND_SCU_USE_HPBIF); -} - -static int rsnd_scu_start(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - int ret; - - /* - * SCU will be used if it has RSND_SCU_USE_HPBIF flags - */ - if (!rsnd_scu_hpbif_is_enable(mod)) { - /* it use PIO transter */ - dev_dbg(dev, "%s%d is not used\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return 0; - } - - /* it use DMA transter */ - ret = rsnd_scu_set_route(priv, mod, rdai, io); - if (ret < 0) - return ret; - - ret = rsnd_scu_set_mode(priv, mod, rdai, io); - if (ret < 0) - return ret; - - ret = rsnd_scu_set_hpbif(priv, mod, rdai, io); - if (ret < 0) - return ret; - - dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return 0; -} - -static struct rsnd_mod_ops rsnd_scu_ops = { - .name = "scu", - .start = rsnd_scu_start, -}; - -struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) -{ - if (WARN_ON(id < 0 || id >= rsnd_scu_nr(priv))) - id = 0; - - return &((struct rsnd_scu *)(priv->scu) + id)->mod; -} - -int rsnd_scu_probe(struct platform_device *pdev, - struct rcar_snd_info *info, - struct rsnd_priv *priv) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_scu *scu; - int i, nr; - - /* - * init SCU - */ - nr = info->scu_info_nr; - scu = devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL); - if (!scu) { - dev_err(dev, "SCU allocate failed\n"); - return -ENOMEM; - } - - priv->scu_nr = nr; - priv->scu = scu; - - for_each_rsnd_scu(scu, priv, i) { - rsnd_mod_init(priv, &scu->mod, - &rsnd_scu_ops, i); - scu->info = &info->scu_info[i]; - - dev_dbg(dev, "SCU%d probed\n", i); - } - dev_dbg(dev, "scu probed\n"); - - return 0; -} - -void rsnd_scu_remove(struct platform_device *pdev, - struct rsnd_priv *priv) -{ -} diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c new file mode 100644 index 000000000000..ea6a214985d0 --- /dev/null +++ b/sound/soc/sh/rcar/src.c @@ -0,0 +1,687 @@ +/* + * Renesas R-Car SRC support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * 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. + */ +#include "rsnd.h" + +struct rsnd_src { + struct rsnd_src_platform_info *info; /* rcar_snd.h */ + struct rsnd_mod mod; + struct clk *clk; +}; + +#define RSND_SRC_NAME_SIZE 16 + +/* + * ADINR + */ +#define OTBL_24 (0 << 16) +#define OTBL_22 (2 << 16) +#define OTBL_20 (4 << 16) +#define OTBL_18 (6 << 16) +#define OTBL_16 (8 << 16) + +#define rsnd_src_mode_flags(p) ((p)->info->flags) +#define rsnd_src_convert_rate(p) ((p)->info->convert_rate) +#define rsnd_mod_to_src(_mod) \ + container_of((_mod), struct rsnd_src, mod) +#define rsnd_src_hpbif_is_enable(src) \ + (rsnd_src_mode_flags(src) & RSND_SCU_USE_HPBIF) +#define rsnd_src_dma_available(src) \ + rsnd_dma_available(rsnd_mod_to_dma(&(src)->mod)) + +#define for_each_rsnd_src(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_src_nr(priv)) && \ + ((pos) = (struct rsnd_src *)(priv)->src + i); \ + i++) + + +/* + * image of SRC (Sampling Rate Converter) + * + * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+ + * 48kHz <-> | SRC | <------> | SSI | <-----> | codec | + * 44.1kHz <-> +-----+ +-----+ +-------+ + * ... + * + */ + +/* + * src.c is caring... + * + * Gen1 + * + * [mem] -> [SRU] -> [SSI] + * |--------| + * + * Gen2 + * + * [mem] -> [SRC] -> [SSIU] -> [SSI] + * |-----------------| + */ + +/* + * How to use SRC bypass mode for debugging + * + * SRC has bypass mode, and it is useful for debugging. + * In Gen2 case, + * SRCm_MODE controls whether SRC is used or not + * SSI_MODE0 controls whether SSIU which receives SRC data + * is used or not. + * Both SRCm_MODE/SSI_MODE0 settings are needed if you use SRC, + * but SRC bypass mode needs SSI_MODE0 only. + * + * This driver request + * struct rsnd_src_platform_info { + * u32 flags; + * u32 convert_rate; + * } + * + * rsnd_src_hpbif_is_enable() will be true + * if flags had RSND_SRC_USE_HPBIF, + * and it controls whether SSIU is used or not. + * + * rsnd_src_convert_rate() indicates + * above convert_rate, and it controls + * whether SRC is used or not. + * + * ex) doesn't use SRC + * struct rsnd_src_platform_info info = { + * .flags = 0, + * .convert_rate = 0, + * }; + * + * ex) uses SRC + * struct rsnd_src_platform_info info = { + * .flags = RSND_SRC_USE_HPBIF, + * .convert_rate = 48000, + * }; + * + * ex) uses SRC bypass mode + * struct rsnd_src_platform_info info = { + * .flags = RSND_SRC_USE_HPBIF, + * .convert_rate = 0, + * }; + * + */ + +/* + * Gen1/Gen2 common functions + */ +int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + int ssi_id = rsnd_mod_id(ssi_mod); + int has_src = 0; + + /* + * SSI_MODE0 + */ + if (info->dai_info) { + has_src = !!src_mod; + } else { + struct rsnd_src *src = rsnd_mod_to_src(src_mod); + has_src = rsnd_src_hpbif_is_enable(src); + } + + rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id), + has_src ? 0 : (1 << ssi_id)); + + /* + * SSI_MODE1 + */ + if (rsnd_ssi_is_pin_sharing(ssi_mod)) { + int shift = -1; + switch (ssi_id) { + case 1: + shift = 0; + break; + case 2: + shift = 2; + break; + case 4: + shift = 16; + break; + } + + if (shift >= 0) + rsnd_mod_bset(ssi_mod, SSI_MODE1, + 0x3 << shift, + rsnd_dai_is_clk_master(rdai) ? + 0x2 << shift : 0x1 << shift); + } + + return 0; +} + +int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + + /* enable PIO interrupt if Gen2 */ + if (rsnd_is_gen2(priv)) + rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000); + + return 0; +} + +unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + struct snd_pcm_runtime *runtime) +{ + struct rsnd_src *src; + unsigned int rate; + + src = rsnd_mod_to_src(rsnd_io_to_mod_src(io)); + + /* + * return convert rate if SRC is used, + * otherwise, return runtime->rate as usual + */ + rate = rsnd_src_convert_rate(src); + if (!rate) + rate = runtime->rate; + + return rate; +} + +static int rsnd_src_set_convert_rate(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_src *src = rsnd_mod_to_src(mod); + u32 convert_rate = rsnd_src_convert_rate(src); + u32 adinr = runtime->channels; + u32 fsrate = 0; + + if (convert_rate) + fsrate = 0x0400000 / convert_rate * runtime->rate; + + /* 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 + * see rsnd_src_start() + */ + rsnd_mod_write(mod, SRC_SRCIR, 1); + + /* Set channel number and output bit length */ + switch (runtime->sample_bits) { + case 16: + adinr |= OTBL_16; + break; + case 32: + adinr |= OTBL_24; + break; + default: + return -EIO; + } + rsnd_mod_write(mod, SRC_ADINR, adinr); + + /* Enable the initial value of IFS */ + if (fsrate) { + rsnd_mod_write(mod, SRC_IFSCR, 1); + + /* Set initial value of IFS */ + rsnd_mod_write(mod, SRC_IFSVR, fsrate); + } + + /* use DMA transfer */ + rsnd_mod_write(mod, SRC_BUSIF_MODE, 1); + + return 0; +} + +static int rsnd_src_init(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + + clk_enable(src->clk); + + return 0; +} + +static int rsnd_src_quit(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + + clk_disable(src->clk); + + return 0; +} + +static int rsnd_src_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + + /* + * Cancel the initialization and operate the SRC function + * see rsnd_src_set_convert_rate() + */ + rsnd_mod_write(mod, SRC_SRCIR, 0); + + if (rsnd_src_convert_rate(src)) + rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); + + return 0; +} + + +static int rsnd_src_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + + if (rsnd_src_convert_rate(src)) + rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0); + + return 0; +} + +static struct rsnd_mod_ops rsnd_src_non_ops = { + .name = "src (non)", +}; + +/* + * Gen1 functions + */ +static int rsnd_src_set_route_gen1(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct src_route_config { + u32 mask; + int shift; + } routes[] = { + { 0xF, 0, }, /* 0 */ + { 0xF, 4, }, /* 1 */ + { 0xF, 8, }, /* 2 */ + { 0x7, 12, }, /* 3 */ + { 0x7, 16, }, /* 4 */ + { 0x7, 20, }, /* 5 */ + { 0x7, 24, }, /* 6 */ + { 0x3, 28, }, /* 7 */ + { 0x3, 30, }, /* 8 */ + }; + u32 mask; + u32 val; + int id; + + id = rsnd_mod_id(mod); + if (id < 0 || id >= ARRAY_SIZE(routes)) + return -EIO; + + /* + * SRC_ROUTE_SELECT + */ + val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; + val = val << routes[id].shift; + mask = routes[id].mask << routes[id].shift; + + rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); + + return 0; +} + +static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_src *src = rsnd_mod_to_src(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 convert_rate = rsnd_src_convert_rate(src); + u32 mask; + u32 val; + int shift; + int id = rsnd_mod_id(mod); + int ret; + + /* + * SRC_TIMING_SELECT + */ + shift = (id % 4) * 8; + mask = 0x1F << shift; + + /* + * 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) + */ + ret = 0; + if (convert_rate) { + /* use ADG */ + val = 0; + ret = rsnd_adg_set_convert_clk_gen1(priv, mod, + runtime->rate, + convert_rate); + } else if (8 == id) { + /* use SSI WS, but SRU8 is special */ + val = id << shift; + } else { + /* use SSI WS */ + val = (id + 1) << shift; + } + + if (ret < 0) + return ret; + + switch (id / 4) { + case 0: + rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); + break; + case 1: + rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); + break; + case 2: + rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); + break; + } + + return 0; +} + +static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int ret; + + ret = rsnd_src_set_convert_rate(mod, rdai, io); + if (ret < 0) + return ret; + + /* 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, + rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98); + + /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */ + + return 0; +} + +static int rsnd_src_init_gen1(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int ret; + + ret = rsnd_src_init(mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_src_set_route_gen1(mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_src_set_convert_rate_gen1(mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_src_set_convert_timing_gen1(mod, rdai, io); + if (ret < 0) + return ret; + + return 0; +} + +static int rsnd_src_start_gen1(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int id = rsnd_mod_id(mod); + + rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id)); + + return rsnd_src_start(mod, rdai, io); +} + +static int rsnd_src_stop_gen1(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int id = rsnd_mod_id(mod); + + rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0); + + return rsnd_src_stop(mod, rdai, io); +} + +static struct rsnd_mod_ops rsnd_src_gen1_ops = { + .name = "sru (gen1)", + .init = rsnd_src_init_gen1, + .quit = rsnd_src_quit, + .start = rsnd_src_start_gen1, + .stop = rsnd_src_stop_gen1, +}; + +/* + * Gen2 functions + */ +static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int ret; + + ret = rsnd_src_set_convert_rate(mod, rdai, io); + if (ret < 0) + return ret; + + rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_mod_read(mod, SRC_ADINR)); + rsnd_mod_write(mod, SSI_BUSIF_MODE, rsnd_mod_read(mod, SRC_BUSIF_MODE)); + + rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); + + rsnd_mod_write(mod, SRC_BSDSR, 0x01800000); + rsnd_mod_write(mod, SRC_BSISR, 0x00100060); + + return 0; +} + +static int rsnd_src_set_convert_timing_gen2(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_src *src = rsnd_mod_to_src(mod); + u32 convert_rate = rsnd_src_convert_rate(src); + int ret; + + if (convert_rate) + ret = rsnd_adg_set_convert_clk_gen2(mod, rdai, io, + runtime->rate, + convert_rate); + else + ret = rsnd_adg_set_convert_timing_gen2(mod, rdai, io); + + return ret; +} + +static int rsnd_src_probe_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct rsnd_src *src = rsnd_mod_to_src(mod); + struct rsnd_mod *ssi = rsnd_ssi_mod_get(priv, rsnd_mod_id(mod)); + struct device *dev = rsnd_priv_to_dev(priv); + int ret; + int is_play; + + if (info->dai_info) + is_play = rsnd_info_is_playback(priv, src); + else + is_play = rsnd_ssi_is_play(ssi); + + ret = rsnd_dma_init(priv, + rsnd_mod_to_dma(mod), + is_play, + src->info->dma_id); + if (ret < 0) + dev_err(dev, "SRC DMA failed\n"); + + return ret; +} + +static int rsnd_src_remove_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod)); + + return 0; +} + +static int rsnd_src_init_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int ret; + + ret = rsnd_src_init(mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_src_set_convert_rate_gen2(mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_src_set_convert_timing_gen2(mod, rdai, io); + if (ret < 0) + return ret; + + return 0; +} + +static int rsnd_src_start_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + + rsnd_dma_start(rsnd_mod_to_dma(&src->mod)); + + rsnd_mod_write(mod, SSI_CTRL, 0x1); + rsnd_mod_write(mod, SRC_CTRL, 0x11); + + return rsnd_src_start(mod, rdai, io); +} + +static int rsnd_src_stop_gen2(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + + rsnd_mod_write(mod, SSI_CTRL, 0); + rsnd_mod_write(mod, SRC_CTRL, 0); + + rsnd_dma_stop(rsnd_mod_to_dma(&src->mod)); + + return rsnd_src_stop(mod, rdai, io); +} + +static struct rsnd_mod_ops rsnd_src_gen2_ops = { + .name = "src (gen2)", + .probe = rsnd_src_probe_gen2, + .remove = rsnd_src_remove_gen2, + .init = rsnd_src_init_gen2, + .quit = rsnd_src_quit, + .start = rsnd_src_start_gen2, + .stop = rsnd_src_stop_gen2, +}; + +struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) + id = 0; + + return &((struct rsnd_src *)(priv->src) + id)->mod; +} + +int rsnd_src_probe(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_src *src; + struct rsnd_mod_ops *ops; + struct clk *clk; + char name[RSND_SRC_NAME_SIZE]; + int i, nr; + + /* + * init SRC + */ + nr = info->src_info_nr; + if (!nr) + return 0; + + src = devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL); + if (!src) { + dev_err(dev, "SRC allocate failed\n"); + return -ENOMEM; + } + + priv->src_nr = nr; + priv->src = src; + + for_each_rsnd_src(src, priv, i) { + snprintf(name, RSND_SRC_NAME_SIZE, "src.%d", i); + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) { + snprintf(name, RSND_SRC_NAME_SIZE, "scu.%d", i); + clk = devm_clk_get(dev, name); + } + + if (IS_ERR(clk)) + return PTR_ERR(clk); + + src->info = &info->src_info[i]; + src->clk = clk; + + ops = &rsnd_src_non_ops; + if (rsnd_src_hpbif_is_enable(src)) { + if (rsnd_is_gen1(priv)) + ops = &rsnd_src_gen1_ops; + if (rsnd_is_gen2(priv)) + ops = &rsnd_src_gen2_ops; + } + + rsnd_mod_init(priv, &src->mod, ops, RSND_MOD_SRC, i); + + dev_dbg(dev, "SRC%d probed\n", i); + } + + return 0; +} diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 5ac20cd5e006..633b23d209b9 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -64,108 +64,29 @@ struct rsnd_ssi { struct rsnd_mod mod; struct rsnd_dai *rdai; - struct rsnd_dai_stream *io; u32 cr_own; u32 cr_clk; u32 cr_etc; int err; - int dma_offset; unsigned int usrcnt; unsigned int rate; }; -struct rsnd_ssiu { - u32 ssi_mode0; - u32 ssi_mode1; - - int ssi_nr; - struct rsnd_ssi *ssi; -}; - #define for_each_rsnd_ssi(pos, priv, i) \ for (i = 0; \ (i < rsnd_ssi_nr(priv)) && \ - ((pos) = ((struct rsnd_ssiu *)((priv)->ssiu))->ssi + i); \ + ((pos) = ((struct rsnd_ssi *)(priv)->ssi + i)); \ i++) -#define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr) +#define rsnd_ssi_nr(priv) ((priv)->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) #define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) #define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0) #define rsnd_ssi_dma_available(ssi) \ rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod)) #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) -#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) -#define rsnd_ssi_to_ssiu(ssi)\ - (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1) - -static void rsnd_ssi_mode_set(struct rsnd_priv *priv, - struct rsnd_dai *rdai, - struct rsnd_ssi *ssi) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_mod *scu; - struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi); - int id = rsnd_mod_id(&ssi->mod); - u32 flags; - u32 val; - - scu = rsnd_scu_mod_get(priv, rsnd_mod_id(&ssi->mod)); - - /* - * SSI_MODE0 - */ - - /* see also BUSIF_MODE */ - if (rsnd_scu_hpbif_is_enable(scu)) { - ssiu->ssi_mode0 &= ~(1 << id); - dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", id); - } else { - ssiu->ssi_mode0 |= (1 << id); - dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", id); - } - - /* - * SSI_MODE1 - */ -#define ssi_parent_set(p, sync, adg, ext) \ - do { \ - ssi->parent = ssiu->ssi + p; \ - if (rsnd_rdai_is_clk_master(rdai)) \ - val = adg; \ - else \ - val = ext; \ - if (flags & RSND_SSI_SYNC) \ - val |= sync; \ - } while (0) - - flags = rsnd_ssi_mode_flags(ssi); - if (flags & RSND_SSI_CLK_PIN_SHARE) { - - val = 0; - switch (id) { - case 1: - ssi_parent_set(0, (1 << 4), (0x2 << 0), (0x1 << 0)); - break; - case 2: - ssi_parent_set(0, (1 << 4), (0x2 << 2), (0x1 << 2)); - break; - case 4: - ssi_parent_set(3, (1 << 20), (0x2 << 16), (0x1 << 16)); - break; - case 8: - ssi_parent_set(7, 0, 0, 0); - break; - } - - ssiu->ssi_mode1 |= val; - } - - rsnd_mod_write(&ssi->mod, SSI_MODE0, ssiu->ssi_mode0); - rsnd_mod_write(&ssi->mod, SSI_MODE1, ssiu->ssi_mode1); -} static void rsnd_ssi_status_check(struct rsnd_mod *mod, u32 bit) @@ -187,9 +108,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 +121,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_src_get_ssi_rate(priv, io, runtime); /* * Find best clock, and try to start ADG @@ -209,7 +132,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]; @@ -250,15 +173,11 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, if (0 == ssi->usrcnt) { clk_enable(ssi->clk); - if (rsnd_rdai_is_clk_master(rdai)) { - struct snd_pcm_runtime *runtime; - - runtime = rsnd_io_to_runtime(io); - + if (rsnd_dai_is_clk_master(rdai)) { 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); } } @@ -304,7 +223,7 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi, rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */ rsnd_ssi_status_check(&ssi->mod, IIRQ); - if (rsnd_rdai_is_clk_master(rdai)) { + if (rsnd_dai_is_clk_master(rdai)) { if (rsnd_ssi_clk_from_parent(ssi)) rsnd_ssi_hw_stop(ssi->parent, rdai); else @@ -325,8 +244,6 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 cr; @@ -367,13 +284,10 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, * set ssi parameter */ ssi->rdai = rdai; - ssi->io = io; ssi->cr_own = cr; ssi->err = -1; /* ignore 1st error */ - rsnd_ssi_mode_set(priv, rdai, ssi); - - dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + rsnd_src_ssi_mode_init(mod, rdai, io); return 0; } @@ -386,13 +300,10 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); - dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - if (ssi->err > 0) dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err); ssi->rdai = NULL; - ssi->io = NULL; ssi->cr_own = 0; ssi->err = 0; @@ -416,8 +327,9 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) { struct rsnd_ssi *ssi = data; - struct rsnd_dai_stream *io = ssi->io; - u32 status = rsnd_mod_read(&ssi->mod, SSISR); + struct rsnd_mod *mod = &ssi->mod; + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + u32 status = rsnd_mod_read(mod, SSISR); irqreturn_t ret = IRQ_NONE; if (io && (status & DIRQ)) { @@ -434,9 +346,9 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) * see rsnd_ssi_init() */ if (rsnd_dai_is_play(rdai, io)) - rsnd_mod_write(&ssi->mod, SSITDR, *buf); + rsnd_mod_write(mod, SSITDR, *buf); else - *buf = rsnd_mod_read(&ssi->mod, SSIRDR); + *buf = rsnd_mod_read(mod, SSIRDR); rsnd_dai_pointer_update(io, sizeof(*buf)); @@ -446,20 +358,38 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) return ret; } -static int rsnd_ssi_pio_start(struct rsnd_mod *mod, +static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + int irq = ssi->info->pio_irq; + int ret; + + ret = devm_request_irq(dev, irq, + rsnd_ssi_pio_interrupt, + IRQF_SHARED, + dev_name(dev), ssi); + if (ret) + dev_err(dev, "SSI request interrupt failed\n"); + + return ret; +} + +static int rsnd_ssi_pio_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); /* enable PIO IRQ */ ssi->cr_etc = UIEN | OIEN | DIEN; - rsnd_ssi_hw_start(ssi, rdai, io); + rsnd_src_enable_ssi_irq(mod, rdai, io); - dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + rsnd_ssi_hw_start(ssi, rdai, io); return 0; } @@ -468,12 +398,8 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - ssi->cr_etc = 0; rsnd_ssi_hw_stop(ssi, rdai); @@ -483,35 +409,46 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .name = "ssi (pio)", + .probe = rsnd_ssi_pio_probe, .init = rsnd_ssi_init, .quit = rsnd_ssi_quit, .start = rsnd_ssi_pio_start, .stop = rsnd_ssi_pio_stop, }; -static int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len) +static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) { - struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); - struct rsnd_dai_stream *io = ssi->io; - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int dma_id = ssi->info->dma_id; + int is_play; + int ret; - *len = io->byte_per_period; - *buf = runtime->dma_addr + - rsnd_dai_pointer_offset(io, ssi->dma_offset + *len); - ssi->dma_offset = *len; /* it cares A/B plane */ + if (info->dai_info) + is_play = rsnd_info_is_playback(priv, ssi); + else + is_play = rsnd_ssi_is_play(&ssi->mod); - return 0; -} + ret = rsnd_dma_init( + priv, rsnd_mod_to_dma(mod), + is_play, + dma_id); -static int rsnd_ssi_dma_complete(struct rsnd_dma *dma) -{ - struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); - struct rsnd_dai_stream *io = ssi->io; - u32 status = rsnd_mod_read(&ssi->mod, SSISR); + if (ret < 0) + dev_err(dev, "SSI DMA failed\n"); - rsnd_ssi_record_error(ssi, status); + return ret; +} - rsnd_dai_pointer_update(ssi->io, io->byte_per_period); +static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod)); return 0; } @@ -525,14 +462,13 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod, /* enable DMA transfer */ ssi->cr_etc = DMEN; - ssi->dma_offset = 0; rsnd_dma_start(dma); rsnd_ssi_hw_start(ssi, ssi->rdai, io); /* enable WS continue */ - if (rsnd_rdai_is_clk_master(rdai)) + if (rsnd_dai_is_clk_master(rdai)) rsnd_mod_write(&ssi->mod, SSIWSR, CONT); return 0; @@ -547,6 +483,8 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, ssi->cr_etc = 0; + rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); + rsnd_ssi_hw_stop(ssi, rdai); rsnd_dma_stop(dma); @@ -556,6 +494,8 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .name = "ssi (dma)", + .probe = rsnd_ssi_dma_probe, + .remove = rsnd_ssi_dma_remove, .init = rsnd_ssi_init, .quit = rsnd_ssi_quit, .start = rsnd_ssi_dma_start, @@ -565,24 +505,8 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { /* * Non SSI */ -static int rsnd_ssi_non(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_dbg(dev, "%s\n", __func__); - - return 0; -} - static struct rsnd_mod_ops rsnd_ssi_non_ops = { .name = "ssi (non)", - .init = rsnd_ssi_non, - .quit = rsnd_ssi_non, - .start = rsnd_ssi_non, - .stop = rsnd_ssi_non, }; /* @@ -591,16 +515,30 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = { struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, int dai_id, int is_play) { + struct rsnd_dai_platform_info *dai_info = NULL; + struct rsnd_dai_path_info *path_info = NULL; + struct rsnd_ssi_platform_info *target_info = NULL; struct rsnd_ssi *ssi; int i, has_play; + if (priv->rdai) + dai_info = priv->rdai[dai_id].info; + if (dai_info) + path_info = (is_play) ? &dai_info->playback : &dai_info->capture; + if (path_info) + target_info = path_info->ssi; + is_play = !!is_play; for_each_rsnd_ssi(ssi, priv, i) { + if (target_info == ssi->info) + return &ssi->mod; + + /* for compatible */ if (rsnd_ssi_dai_id(ssi) != dai_id) continue; - has_play = !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY); + has_play = rsnd_ssi_is_play(&ssi->mod); if (is_play == has_play) return &ssi->mod; @@ -614,43 +552,73 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) id = 0; - return &(((struct rsnd_ssiu *)(priv->ssiu))->ssi + id)->mod; + return &((struct rsnd_ssi *)(priv->ssi) + id)->mod; +} + +int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); +} + +int rsnd_ssi_is_play(struct rsnd_mod *mod) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY); +} + +static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) +{ + if (!rsnd_ssi_is_pin_sharing(&ssi->mod)) + return; + + switch (rsnd_mod_id(&ssi->mod)) { + case 1: + case 2: + ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0)); + break; + case 4: + ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 3)); + break; + case 8: + ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 7)); + break; + } } int rsnd_ssi_probe(struct platform_device *pdev, - struct rcar_snd_info *info, struct rsnd_priv *priv) { + struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct rsnd_ssi_platform_info *pinfo; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod_ops *ops; struct clk *clk; - struct rsnd_ssiu *ssiu; struct rsnd_ssi *ssi; char name[RSND_SSI_NAME_SIZE]; - int i, nr, ret; + int i, nr; /* * init SSI */ nr = info->ssi_info_nr; - ssiu = devm_kzalloc(dev, sizeof(*ssiu) + (sizeof(*ssi) * nr), - GFP_KERNEL); - if (!ssiu) { + ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL); + if (!ssi) { dev_err(dev, "SSI allocate failed\n"); return -ENOMEM; } - priv->ssiu = ssiu; - ssiu->ssi = (struct rsnd_ssi *)(ssiu + 1); - ssiu->ssi_nr = nr; + priv->ssi = ssi; + priv->ssi_nr = nr; for_each_rsnd_ssi(ssi, priv, i) { pinfo = &info->ssi_info[i]; 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); @@ -658,62 +626,15 @@ int rsnd_ssi_probe(struct platform_device *pdev, ssi->clk = clk; ops = &rsnd_ssi_non_ops; + if (pinfo->dma_id > 0) + ops = &rsnd_ssi_dma_ops; + else if (rsnd_ssi_pio_available(ssi)) + ops = &rsnd_ssi_pio_ops; - /* - * SSI DMA case - */ - if (pinfo->dma_id > 0) { - ret = rsnd_dma_init( - priv, rsnd_mod_to_dma(&ssi->mod), - (rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY), - pinfo->dma_id, - rsnd_ssi_dma_inquiry, - rsnd_ssi_dma_complete); - if (ret < 0) - dev_info(dev, "SSI DMA failed. try PIO transter\n"); - else - ops = &rsnd_ssi_dma_ops; - - dev_dbg(dev, "SSI%d use DMA transfer\n", i); - } - - /* - * SSI PIO case - */ - if (!rsnd_ssi_dma_available(ssi) && - rsnd_ssi_pio_available(ssi)) { - ret = devm_request_irq(dev, pinfo->pio_irq, - &rsnd_ssi_pio_interrupt, - IRQF_SHARED, - dev_name(dev), ssi); - if (ret) { - dev_err(dev, "SSI request interrupt failed\n"); - return ret; - } - - ops = &rsnd_ssi_pio_ops; + rsnd_mod_init(priv, &ssi->mod, ops, RSND_MOD_SSI, i); - dev_dbg(dev, "SSI%d use PIO transfer\n", i); - } - - rsnd_mod_init(priv, &ssi->mod, ops, i); + rsnd_ssi_parent_clk_setup(priv, ssi); } - dev_dbg(dev, "ssi probed\n"); - return 0; } - -void rsnd_ssi_remove(struct platform_device *pdev, - struct rsnd_priv *priv) -{ - struct rsnd_ssi *ssi; - 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/sirf/Kconfig b/sound/soc/sirf/Kconfig new file mode 100644 index 000000000000..89e89429b04a --- /dev/null +++ b/sound/soc/sirf/Kconfig @@ -0,0 +1,14 @@ +config SND_SOC_SIRF + tristate "SoC Audio for the SiRF SoC chips" + depends on ARCH_SIRF || COMPILE_TEST + select SND_SOC_GENERIC_DMAENGINE_PCM + +config SND_SOC_SIRF_AUDIO + tristate "SoC Audio support for SiRF internal audio codec" + depends on SND_SOC_SIRF + select SND_SOC_SIRF_AUDIO_CODEC + select SND_SOC_SIRF_AUDIO_PORT + +config SND_SOC_SIRF_AUDIO_PORT + select REGMAP_MMIO + tristate diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile new file mode 100644 index 000000000000..913b93231d4e --- /dev/null +++ b/sound/soc/sirf/Makefile @@ -0,0 +1,5 @@ +snd-soc-sirf-audio-objs := sirf-audio.o +snd-soc-sirf-audio-port-objs := sirf-audio-port.o + +obj-$(CONFIG_SND_SOC_SIRF_AUDIO) += snd-soc-sirf-audio.o +obj-$(CONFIG_SND_SOC_SIRF_AUDIO_PORT) += snd-soc-sirf-audio-port.o diff --git a/sound/soc/sirf/sirf-audio-port.c b/sound/soc/sirf/sirf-audio-port.c new file mode 100644 index 000000000000..b04a53f2b4f6 --- /dev/null +++ b/sound/soc/sirf/sirf-audio-port.c @@ -0,0 +1,194 @@ +/* + * SiRF Audio port driver + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ +#include <linux/module.h> +#include <linux/io.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/dmaengine_pcm.h> + +#include "sirf-audio-port.h" + +struct sirf_audio_port { + struct regmap *regmap; + struct snd_dmaengine_dai_dma_data playback_dma_data; + struct snd_dmaengine_dai_dma_data capture_dma_data; +}; + +static void sirf_audio_port_tx_enable(struct sirf_audio_port *port) +{ + regmap_update_bits(port->regmap, AUDIO_PORT_IC_TXFIFO_OP, + AUDIO_FIFO_RESET, AUDIO_FIFO_RESET); + regmap_write(port->regmap, AUDIO_PORT_IC_TXFIFO_INT_MSK, 0); + regmap_write(port->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0); + regmap_update_bits(port->regmap, AUDIO_PORT_IC_TXFIFO_OP, + AUDIO_FIFO_START, AUDIO_FIFO_START); + regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_TX_CTRL, + IC_TX_ENABLE, IC_TX_ENABLE); +} + +static void sirf_audio_port_tx_disable(struct sirf_audio_port *port) +{ + regmap_write(port->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0); + regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_TX_CTRL, + IC_TX_ENABLE, ~IC_TX_ENABLE); +} + +static void sirf_audio_port_rx_enable(struct sirf_audio_port *port, + int channels) +{ + regmap_update_bits(port->regmap, AUDIO_PORT_IC_RXFIFO_OP, + AUDIO_FIFO_RESET, AUDIO_FIFO_RESET); + regmap_write(port->regmap, AUDIO_PORT_IC_RXFIFO_INT_MSK, 0); + regmap_write(port->regmap, AUDIO_PORT_IC_RXFIFO_OP, 0); + regmap_update_bits(port->regmap, AUDIO_PORT_IC_RXFIFO_OP, + AUDIO_FIFO_START, AUDIO_FIFO_START); + if (channels == 1) + regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_RX_CTRL, + IC_RX_ENABLE_MONO, IC_RX_ENABLE_MONO); + else + regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_RX_CTRL, + IC_RX_ENABLE_STEREO, IC_RX_ENABLE_STEREO); +} + +static void sirf_audio_port_rx_disable(struct sirf_audio_port *port) +{ + regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_RX_CTRL, + IC_RX_ENABLE_STEREO, ~IC_RX_ENABLE_STEREO); +} + +static int sirf_audio_port_dai_probe(struct snd_soc_dai *dai) +{ + struct sirf_audio_port *port = snd_soc_dai_get_drvdata(dai); + snd_soc_dai_init_dma_data(dai, &port->playback_dma_data, + &port->capture_dma_data); + return 0; +} + +static int sirf_audio_port_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct sirf_audio_port *port = snd_soc_dai_get_drvdata(dai); + int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (playback) + sirf_audio_port_tx_disable(port); + else + sirf_audio_port_rx_disable(port); + break; + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (playback) + sirf_audio_port_tx_enable(port); + else + sirf_audio_port_rx_enable(port, + substream->runtime->channels); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops sirf_audio_port_dai_ops = { + .trigger = sirf_audio_port_trigger, +}; + +static struct snd_soc_dai_driver sirf_audio_port_dai = { + .probe = sirf_audio_port_dai_probe, + .name = "sirf-audio-port", + .id = 0, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &sirf_audio_port_dai_ops, +}; + +static const struct snd_soc_component_driver sirf_audio_port_component = { + .name = "sirf-audio-port", +}; + +static const struct regmap_config sirf_audio_port_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = AUDIO_PORT_IC_RXFIFO_INT_MSK, + .cache_type = REGCACHE_NONE, +}; + +static int sirf_audio_port_probe(struct platform_device *pdev) +{ + int ret; + struct sirf_audio_port *port; + void __iomem *base; + struct resource *mem_res; + + port = devm_kzalloc(&pdev->dev, + sizeof(struct sirf_audio_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + + base = devm_ioremap(&pdev->dev, mem_res->start, + resource_size(mem_res)); + if (base == NULL) + return -ENOMEM; + + port->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &sirf_audio_port_regmap_config); + if (IS_ERR(port->regmap)) + return PTR_ERR(port->regmap); + + ret = devm_snd_soc_register_component(&pdev->dev, + &sirf_audio_port_component, &sirf_audio_port_dai, 1); + if (ret) + return ret; + + platform_set_drvdata(pdev, port); + return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); +} + +static const struct of_device_id sirf_audio_port_of_match[] = { + { .compatible = "sirf,audio-port", }, + {} +}; +MODULE_DEVICE_TABLE(of, sirf_audio_port_of_match); + +static struct platform_driver sirf_audio_port_driver = { + .driver = { + .name = "sirf-audio-port", + .owner = THIS_MODULE, + .of_match_table = sirf_audio_port_of_match, + }, + .probe = sirf_audio_port_probe, +}; + +module_platform_driver(sirf_audio_port_driver); + +MODULE_DESCRIPTION("SiRF Audio Port driver"); +MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/sirf/sirf-audio-port.h b/sound/soc/sirf/sirf-audio-port.h new file mode 100644 index 000000000000..f32dc54f4499 --- /dev/null +++ b/sound/soc/sirf/sirf-audio-port.h @@ -0,0 +1,62 @@ +/* + * SiRF Audio port controllers define + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef _SIRF_AUDIO_PORT_H +#define _SIRF_AUDIO_PORT_H + +#define AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK 0x3F +#define AUDIO_PORT_TX_FIFO_SC_OFFSET 0 +#define AUDIO_PORT_TX_FIFO_LC_OFFSET 10 +#define AUDIO_PORT_TX_FIFO_HC_OFFSET 20 + +#define TX_FIFO_SC(x) (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \ + << AUDIO_PORT_TX_FIFO_SC_OFFSET) +#define TX_FIFO_LC(x) (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \ + << AUDIO_PORT_TX_FIFO_LC_OFFSET) +#define TX_FIFO_HC(x) (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \ + << AUDIO_PORT_TX_FIFO_HC_OFFSET) + +#define AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK 0x0F +#define AUDIO_PORT_RX_FIFO_SC_OFFSET 0 +#define AUDIO_PORT_RX_FIFO_LC_OFFSET 10 +#define AUDIO_PORT_RX_FIFO_HC_OFFSET 20 + +#define RX_FIFO_SC(x) (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \ + << AUDIO_PORT_RX_FIFO_SC_OFFSET) +#define RX_FIFO_LC(x) (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \ + << AUDIO_PORT_RX_FIFO_LC_OFFSET) +#define RX_FIFO_HC(x) (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \ + << AUDIO_PORT_RX_FIFO_HC_OFFSET) +#define AUDIO_PORT_IC_CODEC_TX_CTRL (0x00F4) +#define AUDIO_PORT_IC_CODEC_RX_CTRL (0x00F8) + +#define AUDIO_PORT_IC_TXFIFO_OP (0x00FC) +#define AUDIO_PORT_IC_TXFIFO_LEV_CHK (0x0100) +#define AUDIO_PORT_IC_TXFIFO_STS (0x0104) +#define AUDIO_PORT_IC_TXFIFO_INT (0x0108) +#define AUDIO_PORT_IC_TXFIFO_INT_MSK (0x010C) + +#define AUDIO_PORT_IC_RXFIFO_OP (0x0110) +#define AUDIO_PORT_IC_RXFIFO_LEV_CHK (0x0114) +#define AUDIO_PORT_IC_RXFIFO_STS (0x0118) +#define AUDIO_PORT_IC_RXFIFO_INT (0x011C) +#define AUDIO_PORT_IC_RXFIFO_INT_MSK (0x0120) + +#define AUDIO_FIFO_START (1 << 0) +#define AUDIO_FIFO_RESET (1 << 1) + +#define AUDIO_FIFO_FULL (1 << 0) +#define AUDIO_FIFO_EMPTY (1 << 1) +#define AUDIO_FIFO_OFLOW (1 << 2) +#define AUDIO_FIFO_UFLOW (1 << 3) + +#define IC_TX_ENABLE (0x03) +#define IC_RX_ENABLE_MONO (0x01) +#define IC_RX_ENABLE_STEREO (0x03) + +#endif /*__SIRF_AUDIO_PORT_H*/ diff --git a/sound/soc/sirf/sirf-audio.c b/sound/soc/sirf/sirf-audio.c new file mode 100644 index 000000000000..ecef51021653 --- /dev/null +++ b/sound/soc/sirf/sirf-audio.c @@ -0,0 +1,156 @@ +/* + * SiRF audio card driver + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +struct sirf_audio_card { + unsigned int gpio_hp_pa; + unsigned int gpio_spk_pa; +}; + +static int sirf_audio_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *ctrl, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct sirf_audio_card *sirf_audio_card = snd_soc_card_get_drvdata(card); + int on = !SND_SOC_DAPM_EVENT_OFF(event); + if (gpio_is_valid(sirf_audio_card->gpio_hp_pa)) + gpio_set_value(sirf_audio_card->gpio_hp_pa, on); + return 0; +} + +static int sirf_audio_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *ctrl, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct sirf_audio_card *sirf_audio_card = snd_soc_card_get_drvdata(card); + int on = !SND_SOC_DAPM_EVENT_OFF(event); + + if (gpio_is_valid(sirf_audio_card->gpio_spk_pa)) + gpio_set_value(sirf_audio_card->gpio_spk_pa, on); + + return 0; +} +static const struct snd_soc_dapm_widget sirf_audio_dapm_widgets[] = { + SND_SOC_DAPM_HP("Hp", sirf_audio_hp_event), + SND_SOC_DAPM_SPK("Ext Spk", sirf_audio_spk_event), + SND_SOC_DAPM_MIC("Ext Mic", NULL), +}; + +static const struct snd_soc_dapm_route intercon[] = { + {"Hp", NULL, "HPOUTL"}, + {"Hp", NULL, "HPOUTR"}, + {"Ext Spk", NULL, "SPKOUT"}, + {"MICIN1", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Ext Mic"}, +}; + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link sirf_audio_dai_link[] = { + { + .name = "SiRF audio card", + .stream_name = "SiRF audio HiFi", + .codec_dai_name = "sirf-audio-codec", + }, +}; + +/* Audio machine driver */ +static struct snd_soc_card snd_soc_sirf_audio_card = { + .name = "SiRF audio card", + .owner = THIS_MODULE, + .dai_link = sirf_audio_dai_link, + .num_links = ARRAY_SIZE(sirf_audio_dai_link), + .dapm_widgets = sirf_audio_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sirf_audio_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), +}; + +static int sirf_audio_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &snd_soc_sirf_audio_card; + struct sirf_audio_card *sirf_audio_card; + int ret; + + sirf_audio_card = devm_kzalloc(&pdev->dev, sizeof(struct sirf_audio_card), + GFP_KERNEL); + if (sirf_audio_card == NULL) + return -ENOMEM; + + sirf_audio_dai_link[0].cpu_of_node = + of_parse_phandle(pdev->dev.of_node, "sirf,audio-platform", 0); + sirf_audio_dai_link[0].platform_of_node = + of_parse_phandle(pdev->dev.of_node, "sirf,audio-platform", 0); + sirf_audio_dai_link[0].codec_of_node = + of_parse_phandle(pdev->dev.of_node, "sirf,audio-codec", 0); + sirf_audio_card->gpio_spk_pa = of_get_named_gpio(pdev->dev.of_node, + "spk-pa-gpios", 0); + sirf_audio_card->gpio_hp_pa = of_get_named_gpio(pdev->dev.of_node, + "hp-pa-gpios", 0); + if (gpio_is_valid(sirf_audio_card->gpio_spk_pa)) { + ret = devm_gpio_request_one(&pdev->dev, + sirf_audio_card->gpio_spk_pa, + GPIOF_OUT_INIT_LOW, "SPA_PA_SD"); + if (ret) { + dev_err(&pdev->dev, + "Failed to request GPIO_%d for reset: %d\n", + sirf_audio_card->gpio_spk_pa, ret); + return ret; + } + } + if (gpio_is_valid(sirf_audio_card->gpio_hp_pa)) { + ret = devm_gpio_request_one(&pdev->dev, + sirf_audio_card->gpio_hp_pa, + GPIOF_OUT_INIT_LOW, "HP_PA_SD"); + if (ret) { + dev_err(&pdev->dev, + "Failed to request GPIO_%d for reset: %d\n", + sirf_audio_card->gpio_hp_pa, ret); + return ret; + } + } + + card->dev = &pdev->dev; + snd_soc_card_set_drvdata(card, sirf_audio_card); + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret); + + return ret; +} + +static const struct of_device_id sirf_audio_of_match[] = { + {.compatible = "sirf,sirf-audio-card", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sirf_audio_of_match); + +static struct platform_driver sirf_audio_driver = { + .driver = { + .name = "sirf-audio-card", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = sirf_audio_of_match, + }, + .probe = sirf_audio_probe, +}; +module_platform_driver(sirf_audio_driver); + +MODULE_AUTHOR("RongJun Ying <RongJun.Ying@csr.com>"); +MODULE_DESCRIPTION("ALSA SoC SIRF audio card driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 375dc6dfba4e..bfed3e4c45ff 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -96,8 +96,7 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec) { dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n", codec->name); - if (!codec->reg_cache) - return 0; + kfree(codec->reg_cache); codec->reg_cache = NULL; return 0; @@ -117,8 +116,9 @@ int snd_soc_cache_read(struct snd_soc_codec *codec, return -EINVAL; mutex_lock(&codec->cache_rw_mutex); - *value = snd_soc_get_cache_val(codec->reg_cache, reg, - codec->driver->reg_word_size); + if (!ZERO_OR_NULL_PTR(codec->reg_cache)) + *value = snd_soc_get_cache_val(codec->reg_cache, reg, + codec->driver->reg_word_size); mutex_unlock(&codec->cache_rw_mutex); return 0; @@ -136,8 +136,9 @@ int snd_soc_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { mutex_lock(&codec->cache_rw_mutex); - snd_soc_set_cache_val(codec->reg_cache, reg, value, - codec->driver->reg_word_size); + if (!ZERO_OR_NULL_PTR(codec->reg_cache)) + snd_soc_set_cache_val(codec->reg_cache, reg, value, + codec->driver->reg_word_size); mutex_unlock(&codec->cache_rw_mutex); return 0; diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 53c9ecdd119f..91083e6a6b38 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -24,13 +24,12 @@ #include <sound/compress_driver.h> #include <sound/soc.h> #include <sound/initval.h> +#include <sound/soc-dpcm.h> static int soc_compr_open(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; 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; int ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -51,17 +50,7 @@ static int soc_compr_open(struct snd_compr_stream *cstream) } } - if (cstream->direction == SND_COMPRESS_PLAYBACK) { - cpu_dai->playback_active++; - codec_dai->playback_active++; - } else { - cpu_dai->capture_active++; - codec_dai->capture_active++; - } - - cpu_dai->active++; - codec_dai->active++; - rtd->codec->active++; + snd_soc_runtime_activate(rtd, cstream->direction); mutex_unlock(&rtd->pcm_mutex); @@ -75,6 +64,86 @@ out: return ret; } +static int soc_compr_open_fe(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *fe = cstream->private_data; + struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream; + struct snd_soc_platform *platform = fe->platform; + struct snd_soc_dpcm *dpcm; + struct snd_soc_dapm_widget_list *list; + int stream; + int ret = 0; + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + stream = SNDRV_PCM_STREAM_CAPTURE; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + + if (platform->driver->compr_ops && platform->driver->compr_ops->open) { + ret = platform->driver->compr_ops->open(cstream); + if (ret < 0) { + pr_err("compress asoc: can't open platform %s\n", platform->name); + goto out; + } + } + + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) { + ret = fe->dai_link->compr_ops->startup(cstream); + if (ret < 0) { + pr_err("compress asoc: %s startup failed\n", fe->dai_link->name); + goto machine_err; + } + } + + fe->dpcm[stream].runtime = fe_substream->runtime; + + if (dpcm_path_get(fe, stream, &list) <= 0) { + dev_dbg(fe->dev, "ASoC: %s no valid %s route\n", + fe->dai_link->name, stream ? "capture" : "playback"); + } + + /* calculate valid and active FE <-> BE dpcms */ + dpcm_process_paths(fe, stream, &list, 1); + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_startup(fe, stream); + if (ret < 0) { + /* clean up all links */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) + dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; + + dpcm_be_disconnect(fe, stream); + fe->dpcm[stream].runtime = NULL; + goto fe_err; + } + + dpcm_clear_pending_state(fe, stream); + dpcm_path_put(&list); + + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + + snd_soc_runtime_activate(fe, stream); + + mutex_unlock(&fe->card->mutex); + + return 0; + +fe_err: + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) + fe->dai_link->compr_ops->shutdown(cstream); +machine_err: + if (platform->driver->compr_ops && platform->driver->compr_ops->free) + platform->driver->compr_ops->free(cstream); +out: + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + mutex_unlock(&fe->card->mutex); + return ret; +} + /* * Power down the audio subsystem pmdown_time msecs after close is called. * This is to ensure there are no pops or clicks in between any music tracks @@ -109,23 +178,18 @@ static int soc_compr_free(struct snd_compr_stream *cstream) 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; + int stream; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (cstream->direction == SND_COMPRESS_PLAYBACK) { - cpu_dai->playback_active--; - codec_dai->playback_active--; - } else { - cpu_dai->capture_active--; - codec_dai->capture_active--; - } + if (cstream->direction == SND_COMPRESS_PLAYBACK) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + stream = SNDRV_PCM_STREAM_CAPTURE; - snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); + snd_soc_runtime_deactivate(rtd, stream); - cpu_dai->active--; - codec_dai->active--; - codec->active--; + snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); if (!cpu_dai->active) cpu_dai->rate = 0; @@ -142,8 +206,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream) cpu_dai->runtime = NULL; if (cstream->direction == SND_COMPRESS_PLAYBACK) { - if (!rtd->pmdown_time || codec->ignore_pmdown_time || - rtd->dai_link->ignore_pmdown_time) { + if (snd_soc_runtime_ignore_pmdown_time(rtd)) { snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_STOP); @@ -164,6 +227,56 @@ static int soc_compr_free(struct snd_compr_stream *cstream) return 0; } +static int soc_compr_free_fe(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *fe = cstream->private_data; + struct snd_soc_platform *platform = fe->platform; + struct snd_soc_dpcm *dpcm; + int stream, ret; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + stream = SNDRV_PCM_STREAM_CAPTURE; + + snd_soc_runtime_deactivate(fe, stream); + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_hw_free(fe, stream); + if (ret < 0) + dev_err(fe->dev, "compressed hw_free failed %d\n", ret); + + ret = dpcm_be_dai_shutdown(fe, stream); + + /* mark FE's links ready to prune */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) + dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); + else + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); + + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + + dpcm_be_disconnect(fe, stream); + + fe->dpcm[stream].runtime = NULL; + + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) + fe->dai_link->compr_ops->shutdown(cstream); + + if (platform->driver->compr_ops && platform->driver->compr_ops->free) + platform->driver->compr_ops->free(cstream); + + mutex_unlock(&fe->card->mutex); + return 0; +} + static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) { @@ -194,6 +307,59 @@ out: return ret; } +static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) +{ + struct snd_soc_pcm_runtime *fe = cstream->private_data; + struct snd_soc_platform *platform = fe->platform; + int ret = 0, stream; + + if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN || + cmd == SND_COMPR_TRIGGER_DRAIN) { + + if (platform->driver->compr_ops && + platform->driver->compr_ops->trigger) + return platform->driver->compr_ops->trigger(cstream, cmd); + } + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + stream = SNDRV_PCM_STREAM_CAPTURE; + + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + + if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { + ret = platform->driver->compr_ops->trigger(cstream, cmd); + if (ret < 0) + goto out; + } + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_trigger(fe, stream, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; + break; + } + +out: + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + mutex_unlock(&fe->card->mutex); + return ret; +} + static int soc_compr_set_params(struct snd_compr_stream *cstream, struct snd_compr_params *params) { @@ -241,6 +407,64 @@ err: return ret; } +static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + struct snd_soc_pcm_runtime *fe = cstream->private_data; + struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream; + struct snd_soc_platform *platform = fe->platform; + int ret = 0, stream; + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + stream = SNDRV_PCM_STREAM_CAPTURE; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + + if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { + ret = platform->driver->compr_ops->set_params(cstream, params); + if (ret < 0) + goto out; + } + + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) { + ret = fe->dai_link->compr_ops->set_params(cstream); + if (ret < 0) + goto out; + } + + /* + * Create an empty hw_params for the BE as the machine driver must + * fix this up to match DSP decoder and ASRC configuration. + * I.e. machine driver fixup for compressed BE is mandatory. + */ + memset(&fe->dpcm[fe_substream->stream].hw_params, 0, + sizeof(struct snd_pcm_hw_params)); + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_hw_params(fe, stream); + if (ret < 0) + goto out; + + ret = dpcm_be_dai_prepare(fe, stream); + if (ret < 0) + goto out; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); + else + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); + + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; + +out: + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + mutex_unlock(&fe->card->mutex); + return ret; +} + static int soc_compr_get_params(struct snd_compr_stream *cstream, struct snd_codec *params) { @@ -360,6 +584,7 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, return ret; } + /* ASoC Compress operations */ static struct snd_compr_ops soc_compr_ops = { .open = soc_compr_open, @@ -375,6 +600,21 @@ static struct snd_compr_ops soc_compr_ops = { .get_codec_caps = soc_compr_get_codec_caps }; +/* ASoC Dynamic Compress operations */ +static struct snd_compr_ops soc_compr_dyn_ops = { + .open = soc_compr_open_fe, + .free = soc_compr_free_fe, + .set_params = soc_compr_set_params_fe, + .get_params = soc_compr_get_params, + .set_metadata = soc_compr_set_metadata, + .get_metadata = soc_compr_get_metadata, + .trigger = soc_compr_trigger_fe, + .pointer = soc_compr_pointer, + .ack = soc_compr_ack, + .get_caps = soc_compr_get_caps, + .get_codec_caps = soc_compr_get_codec_caps +}; + /* create a new compress */ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { @@ -383,6 +623,7 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_compr *compr; + struct snd_pcm *be_pcm; char new_name[64]; int ret = 0, direction = 0; @@ -410,7 +651,26 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) ret = -ENOMEM; goto compr_err; } - memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); + + if (rtd->dai_link->dynamic) { + snprintf(new_name, sizeof(new_name), "(%s)", + rtd->dai_link->stream_name); + + ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, + 1, 0, &be_pcm); + if (ret < 0) { + dev_err(rtd->card->dev, "ASoC: can't create compressed for %s\n", + rtd->dai_link->name); + goto compr_err; + } + + rtd->pcm = be_pcm; + rtd->fe_compr = 1; + be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; + be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; + memcpy(compr->ops, &soc_compr_dyn_ops, sizeof(soc_compr_dyn_ops)); + } else + memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); /* Add copy callback for not memory mapped DSPs */ if (platform->driver->compr_ops && platform->driver->compr_ops->copy) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4e53d87e881d..359c2849b364 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -56,7 +56,6 @@ EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); #endif static DEFINE_MUTEX(client_mutex); -static LIST_HEAD(dai_list); static LIST_HEAD(platform_list); static LIST_HEAD(codec_list); static LIST_HEAD(component_list); @@ -370,18 +369,22 @@ static ssize_t dai_list_read_file(struct file *file, char __user *user_buf, { char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); ssize_t len, ret = 0; + struct snd_soc_component *component; struct snd_soc_dai *dai; if (!buf) return -ENOMEM; - list_for_each_entry(dai, &dai_list, list) { - len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n", dai->name); - if (len >= 0) - ret += len; - if (ret > PAGE_SIZE) { - ret = PAGE_SIZE; - break; + list_for_each_entry(component, &component_list, list) { + list_for_each_entry(dai, &component->dai_list, list) { + len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n", + dai->name); + if (len >= 0) + ret += len; + if (ret > PAGE_SIZE) { + ret = PAGE_SIZE; + break; + } } } @@ -855,6 +858,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; + struct snd_soc_component *component; struct snd_soc_codec *codec; struct snd_soc_platform *platform; struct snd_soc_dai *codec_dai, *cpu_dai; @@ -863,18 +867,20 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num); /* Find CPU DAI from registered DAIs*/ - list_for_each_entry(cpu_dai, &dai_list, list) { + list_for_each_entry(component, &component_list, list) { if (dai_link->cpu_of_node && - (cpu_dai->dev->of_node != dai_link->cpu_of_node)) + component->dev->of_node != dai_link->cpu_of_node) continue; if (dai_link->cpu_name && - strcmp(dev_name(cpu_dai->dev), dai_link->cpu_name)) - continue; - if (dai_link->cpu_dai_name && - strcmp(cpu_dai->name, dai_link->cpu_dai_name)) + strcmp(dev_name(component->dev), dai_link->cpu_name)) continue; + list_for_each_entry(cpu_dai, &component->dai_list, list) { + if (dai_link->cpu_dai_name && + strcmp(cpu_dai->name, dai_link->cpu_dai_name)) + continue; - rtd->cpu_dai = cpu_dai; + rtd->cpu_dai = cpu_dai; + } } if (!rtd->cpu_dai) { @@ -899,12 +905,10 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) * CODEC found, so find CODEC DAI from registered DAIs from * this CODEC */ - list_for_each_entry(codec_dai, &dai_list, list) { - if (codec->dev == codec_dai->dev && - !strcmp(codec_dai->name, - dai_link->codec_dai_name)) { - + list_for_each_entry(codec_dai, &codec->component.dai_list, list) { + if (!strcmp(codec_dai->name, dai_link->codec_dai_name)) { rtd->codec_dai = codec_dai; + break; } } @@ -1128,12 +1132,8 @@ static int soc_probe_codec(struct snd_soc_card *card, driver->num_dapm_widgets); /* Create DAPM widgets for each DAI stream */ - list_for_each_entry(dai, &dai_list, list) { - if (dai->dev != codec->dev) - continue; - + list_for_each_entry(dai, &codec->component.dai_list, list) snd_soc_dapm_new_dai_widgets(&codec->dapm, dai); - } codec->dapm.idle_bias_off = driver->idle_bias_off; @@ -1180,6 +1180,7 @@ static int soc_probe_platform(struct snd_soc_card *card, { int ret = 0; const struct snd_soc_platform_driver *driver = platform->driver; + struct snd_soc_component *component; struct snd_soc_dai *dai; platform->card = card; @@ -1195,11 +1196,11 @@ static int soc_probe_platform(struct snd_soc_card *card, driver->dapm_widgets, driver->num_dapm_widgets); /* Create DAPM widgets for each DAI stream */ - list_for_each_entry(dai, &dai_list, list) { - if (dai->dev != platform->dev) + list_for_each_entry(component, &component_list, list) { + if (component->dev != platform->dev) continue; - - snd_soc_dapm_new_dai_widgets(&platform->dapm, dai); + list_for_each_entry(dai, &component->dai_list, list) + snd_soc_dapm_new_dai_widgets(&platform->dapm, dai); } platform->dapm.idle_bias_off = 1; @@ -1728,6 +1729,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) } snd_soc_dapm_link_dai_widgets(card); + snd_soc_dapm_connect_dai_link_widgets(card); if (card->controls) snd_soc_add_card_controls(card, card->controls, card->num_controls); @@ -2570,10 +2572,10 @@ int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = e->shift_l == e->shift_r ? 1 : 2; - uinfo->value.enumerated.items = e->max; + uinfo->value.enumerated.items = e->items; - if (uinfo->value.enumerated.item > e->max - 1) - uinfo->value.enumerated.item = e->max - 1; + if (uinfo->value.enumerated.item >= e->items) + uinfo->value.enumerated.item = e->items - 1; strlcpy(uinfo->value.enumerated.name, e->texts[uinfo->value.enumerated.item], sizeof(uinfo->value.enumerated.name)); @@ -2595,14 +2597,18 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val; + unsigned int val, item; + unsigned int reg_val; - val = snd_soc_read(codec, e->reg); - ucontrol->value.enumerated.item[0] - = (val >> e->shift_l) & e->mask; - if (e->shift_l != e->shift_r) - ucontrol->value.enumerated.item[1] = - (val >> e->shift_r) & e->mask; + reg_val = snd_soc_read(codec, e->reg); + val = (reg_val >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + ucontrol->value.enumerated.item[0] = item; + if (e->shift_l != e->shift_r) { + val = (reg_val >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + ucontrol->value.enumerated.item[1] = item; + } return 0; } @@ -2622,17 +2628,18 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; unsigned int val; unsigned int mask; - if (ucontrol->value.enumerated.item[0] > e->max - 1) + if (item[0] >= e->items) return -EINVAL; - val = ucontrol->value.enumerated.item[0] << e->shift_l; + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; mask = e->mask << e->shift_l; if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] > e->max - 1) + if (item[1] >= e->items) return -EINVAL; - val |= ucontrol->value.enumerated.item[1] << e->shift_r; + val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r; mask |= e->mask << e->shift_r; } @@ -2641,78 +2648,46 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); /** - * snd_soc_get_value_enum_double - semi enumerated double mixer get callback - * @kcontrol: mixer control - * @ucontrol: control element information + * snd_soc_read_signed - Read a codec register and interprete as signed value + * @codec: codec + * @reg: Register to read + * @mask: Mask to use after shifting the register value + * @shift: Right shift of register value + * @sign_bit: Bit that describes if a number is negative or not. * - * Callback to get the value of a double semi enumerated mixer. + * This functions reads a codec register. The register value is shifted right + * by 'shift' bits and masked with the given 'mask'. Afterwards it translates + * the given registervalue into a signed integer if sign_bit is non-zero. * - * Semi enumerated mixer: the enumerated items are referred as values. Can be - * used for handling bitfield coded enumeration for example. - * - * Returns 0 for success. + * Returns the register value as signed int. */ -int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_soc_read_signed(struct snd_soc_codec *codec, unsigned int reg, + unsigned int mask, unsigned int shift, unsigned int sign_bit) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int reg_val, val, mux; + int ret; + unsigned int val; - reg_val = snd_soc_read(codec, e->reg); - val = (reg_val >> e->shift_l) & e->mask; - for (mux = 0; mux < e->max; mux++) { - if (val == e->values[mux]) - break; - } - ucontrol->value.enumerated.item[0] = mux; - if (e->shift_l != e->shift_r) { - val = (reg_val >> e->shift_r) & e->mask; - for (mux = 0; mux < e->max; mux++) { - if (val == e->values[mux]) - break; - } - ucontrol->value.enumerated.item[1] = mux; - } + val = (snd_soc_read(codec, reg) >> shift) & mask; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_get_value_enum_double); + if (!sign_bit) + return val; -/** - * snd_soc_put_value_enum_double - semi enumerated double mixer put callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value of a double semi enumerated mixer. - * - * Semi enumerated mixer: the enumerated items are referred as values. Can be - * used for handling bitfield coded enumeration for example. - * - * Returns 0 for success. - */ -int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val; - unsigned int mask; + /* non-negative number */ + if (!(val & BIT(sign_bit))) + return val; - if (ucontrol->value.enumerated.item[0] > e->max - 1) - return -EINVAL; - val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l; - mask = e->mask << e->shift_l; - if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] > e->max - 1) - return -EINVAL; - val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r; - mask |= e->mask << e->shift_r; - } + ret = val; - return snd_soc_update_bits_locked(codec, e->reg, mask, val); + /* + * The register most probably does not contain a full-sized int. + * Instead we have an arbitrary number of bits in a signed + * representation which has to be translated into a full-sized int. + * This is done by filling up all bits above the sign-bit. + */ + ret |= ~((int)(BIT(sign_bit) - 1)); + + return ret; } -EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double); /** * snd_soc_info_volsw - single mixer info callback @@ -2742,7 +2717,7 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = platform_max; + uinfo->value.integer.max = platform_max - mc->min; return 0; } EXPORT_SYMBOL_GPL(snd_soc_info_volsw); @@ -2768,11 +2743,16 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, unsigned int shift = mc->shift; unsigned int rshift = mc->rshift; int max = mc->max; + int min = mc->min; + int sign_bit = mc->sign_bit; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - ucontrol->value.integer.value[0] = - (snd_soc_read(codec, reg) >> shift) & mask; + if (sign_bit) + mask = BIT(sign_bit + 1) - 1; + + ucontrol->value.integer.value[0] = snd_soc_read_signed(codec, reg, mask, + shift, sign_bit) - min; if (invert) ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; @@ -2780,10 +2760,12 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, if (snd_soc_volsw_is_stereo(mc)) { if (reg == reg2) ucontrol->value.integer.value[1] = - (snd_soc_read(codec, reg) >> rshift) & mask; + snd_soc_read_signed(codec, reg, mask, rshift, + sign_bit) - min; else ucontrol->value.integer.value[1] = - (snd_soc_read(codec, reg2) >> shift) & mask; + snd_soc_read_signed(codec, reg2, mask, shift, + sign_bit) - min; if (invert) ucontrol->value.integer.value[1] = max - ucontrol->value.integer.value[1]; @@ -2814,20 +2796,25 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, unsigned int shift = mc->shift; unsigned int rshift = mc->rshift; int max = mc->max; + int min = mc->min; + unsigned int sign_bit = mc->sign_bit; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; int err; - bool type_2r = 0; + bool type_2r = false; unsigned int val2 = 0; unsigned int val, val_mask; - val = (ucontrol->value.integer.value[0] & mask); + if (sign_bit) + mask = BIT(sign_bit + 1) - 1; + + val = ((ucontrol->value.integer.value[0] + min) & mask); if (invert) val = max - val; val_mask = mask << shift; val = val << shift; if (snd_soc_volsw_is_stereo(mc)) { - val2 = (ucontrol->value.integer.value[1] & mask); + val2 = ((ucontrol->value.integer.value[1] + min) & mask); if (invert) val2 = max - val2; if (reg == reg2) { @@ -2835,7 +2822,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, val |= val2 << rshift; } else { val2 = val2 << shift; - type_2r = 1; + type_2r = true; } } err = snd_soc_update_bits_locked(codec, reg, val_mask, val); @@ -3212,11 +3199,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; @@ -3233,7 +3220,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, struct soc_bytes *params = (void *)kcontrol->private_value; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); int ret, len; - unsigned int val; + unsigned int val, mask; void *data; if (!codec->using_regmap) @@ -3263,12 +3250,36 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, ((u8 *)data)[0] |= val; break; case 2: - ((u16 *)data)[0] &= cpu_to_be16(~params->mask); - ((u16 *)data)[0] |= cpu_to_be16(val); + mask = ~params->mask; + ret = regmap_parse_val(codec->control_data, + &mask, &mask); + if (ret != 0) + goto out; + + ((u16 *)data)[0] &= mask; + + ret = regmap_parse_val(codec->control_data, + &val, &val); + if (ret != 0) + goto out; + + ((u16 *)data)[0] |= val; break; case 4: - ((u32 *)data)[0] &= cpu_to_be32(~params->mask); - ((u32 *)data)[0] |= cpu_to_be32(val); + mask = ~params->mask; + ret = regmap_parse_val(codec->control_data, + &mask, &mask); + if (ret != 0) + goto out; + + ((u32 *)data)[0] &= mask; + + ret = regmap_parse_val(codec->control_data, + &val, &val); + if (ret != 0) + goto out; + + ((u32 *)data)[0] |= val; break; default: ret = -EINVAL; @@ -3484,7 +3495,7 @@ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, return dai->codec->driver->set_sysclk(dai->codec, clk_id, 0, freq, dir); else - return -EINVAL; + return -ENOTSUPP; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); @@ -3505,7 +3516,7 @@ int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, return codec->driver->set_sysclk(codec, clk_id, source, freq, dir); else - return -EINVAL; + return -ENOTSUPP; } EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk); @@ -3608,6 +3619,30 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt); /** + * snd_soc_of_xlate_tdm_slot - generate tx/rx slot mask. + * @slots: Number of slots in use. + * @tx_mask: bitmask representing active TX slots. + * @rx_mask: bitmask representing active RX slots. + * + * Generates the TDM tx and rx slot default masks for DAI. + */ +static int snd_soc_of_xlate_tdm_slot_mask(unsigned int slots, + unsigned int *tx_mask, + unsigned int *rx_mask) +{ + if (*tx_mask || *rx_mask) + return 0; + + if (!slots) + return -EINVAL; + + *tx_mask = (1 << slots) - 1; + *rx_mask = (1 << slots) - 1; + + return 0; +} + +/** * snd_soc_dai_set_tdm_slot - configure DAI TDM. * @dai: DAI * @tx_mask: bitmask representing active TX slots. @@ -3621,11 +3656,17 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt); int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { + if (dai->driver && dai->driver->ops->of_xlate_tdm_slot_mask) + dai->driver->ops->of_xlate_tdm_slot_mask(slots, + &tx_mask, &rx_mask); + else + snd_soc_of_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); + if (dai->driver && dai->driver->ops->set_tdm_slot) return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, slots, slot_width); else - return -EINVAL; + return -ENOTSUPP; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); @@ -3881,95 +3922,42 @@ static inline char *fmt_multiple_name(struct device *dev, } /** - * snd_soc_register_dai - Register a DAI with the ASoC core + * snd_soc_unregister_dai - Unregister DAIs from the ASoC core * - * @dai: DAI to register + * @component: The component for which the DAIs should be unregistered */ -static int snd_soc_register_dai(struct device *dev, - struct snd_soc_dai_driver *dai_drv) +static void snd_soc_unregister_dais(struct snd_soc_component *component) { - struct snd_soc_codec *codec; - struct snd_soc_dai *dai; + struct snd_soc_dai *dai, *_dai; - dev_dbg(dev, "ASoC: dai register %s\n", dev_name(dev)); - - dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); - if (dai == NULL) - return -ENOMEM; - - /* create DAI component name */ - dai->name = fmt_single_name(dev, &dai->id); - if (dai->name == NULL) { + list_for_each_entry_safe(dai, _dai, &component->dai_list, list) { + dev_dbg(component->dev, "ASoC: Unregistered DAI '%s'\n", + dai->name); + list_del(&dai->list); + kfree(dai->name); kfree(dai); - return -ENOMEM; - } - - dai->dev = dev; - dai->driver = dai_drv; - dai->dapm.dev = dev; - if (!dai->driver->ops) - dai->driver->ops = &null_dai_ops; - - mutex_lock(&client_mutex); - - list_for_each_entry(codec, &codec_list, list) { - if (codec->dev == dev) { - dev_dbg(dev, "ASoC: Mapped DAI %s to CODEC %s\n", - dai->name, codec->name); - dai->codec = codec; - break; - } - } - - if (!dai->codec) - dai->dapm.idle_bias_off = 1; - - list_add(&dai->list, &dai_list); - - mutex_unlock(&client_mutex); - - dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); - - return 0; -} - -/** - * snd_soc_unregister_dai - Unregister a DAI from the ASoC core - * - * @dai: DAI to unregister - */ -static void snd_soc_unregister_dai(struct device *dev) -{ - struct snd_soc_dai *dai; - - list_for_each_entry(dai, &dai_list, list) { - if (dev == dai->dev) - goto found; } - return; - -found: - mutex_lock(&client_mutex); - list_del(&dai->list); - mutex_unlock(&client_mutex); - - dev_dbg(dev, "ASoC: Unregistered DAI '%s'\n", dai->name); - kfree(dai->name); - kfree(dai); } /** - * snd_soc_register_dais - Register multiple DAIs with the ASoC core + * snd_soc_register_dais - Register a DAI with the ASoC core * - * @dai: Array of DAIs to register + * @component: The component the DAIs are registered for + * @codec: The CODEC that the DAIs are registered for, NULL if the component is + * not a CODEC. + * @dai_drv: DAI driver to use for the DAIs * @count: Number of DAIs + * @legacy_dai_naming: Use the legacy naming scheme and let the DAI inherit the + * parent's name. */ -static int snd_soc_register_dais(struct device *dev, - struct snd_soc_dai_driver *dai_drv, size_t count) +static int snd_soc_register_dais(struct snd_soc_component *component, + struct snd_soc_codec *codec, struct snd_soc_dai_driver *dai_drv, + size_t count, bool legacy_dai_naming) { - struct snd_soc_codec *codec; + struct device *dev = component->dev; struct snd_soc_dai *dai; - int i, ret = 0; + unsigned int i; + int ret; dev_dbg(dev, "ASoC: dai register %s #%Zu\n", dev_name(dev), count); @@ -3981,70 +3969,54 @@ static int snd_soc_register_dais(struct device *dev, goto err; } - /* create DAI component name */ - dai->name = fmt_multiple_name(dev, &dai_drv[i]); + /* + * Back in the old days when we still had component-less DAIs, + * instead of having a static name, component-less DAIs would + * inherit the name of the parent device so it is possible to + * register multiple instances of the DAI. We still need to keep + * the same naming style even though those DAIs are not + * component-less anymore. + */ + if (count == 1 && legacy_dai_naming) { + dai->name = fmt_single_name(dev, &dai->id); + } else { + dai->name = fmt_multiple_name(dev, &dai_drv[i]); + if (dai_drv[i].id) + dai->id = dai_drv[i].id; + else + dai->id = i; + } if (dai->name == NULL) { kfree(dai); - ret = -EINVAL; + ret = -ENOMEM; goto err; } + dai->component = component; + dai->codec = codec; dai->dev = dev; dai->driver = &dai_drv[i]; - if (dai->driver->id) - dai->id = dai->driver->id; - else - dai->id = i; dai->dapm.dev = dev; if (!dai->driver->ops) dai->driver->ops = &null_dai_ops; - mutex_lock(&client_mutex); - - list_for_each_entry(codec, &codec_list, list) { - if (codec->dev == dev) { - dev_dbg(dev, - "ASoC: Mapped DAI %s to CODEC %s\n", - dai->name, codec->name); - dai->codec = codec; - break; - } - } - if (!dai->codec) dai->dapm.idle_bias_off = 1; - list_add(&dai->list, &dai_list); - - mutex_unlock(&client_mutex); + list_add(&dai->list, &component->dai_list); - dev_dbg(dai->dev, "ASoC: Registered DAI '%s'\n", dai->name); + dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); } return 0; err: - for (i--; i >= 0; i--) - snd_soc_unregister_dai(dev); + snd_soc_unregister_dais(component); return ret; } /** - * snd_soc_unregister_dais - Unregister multiple DAIs from the ASoC core - * - * @dai: Array of DAIs to unregister - * @count: Number of DAIs - */ -static void snd_soc_unregister_dais(struct device *dev, size_t count) -{ - int i; - - for (i = 0; i < count; i++) - snd_soc_unregister_dai(dev); -} - -/** * snd_soc_register_component - Register a component with the ASoC core * */ @@ -4052,6 +4024,7 @@ static int __snd_soc_register_component(struct device *dev, struct snd_soc_component *cmpnt, const struct snd_soc_component_driver *cmpnt_drv, + struct snd_soc_codec *codec, struct snd_soc_dai_driver *dai_drv, int num_dai, bool allow_single_dai) { @@ -4074,20 +4047,10 @@ __snd_soc_register_component(struct device *dev, cmpnt->driver = cmpnt_drv; cmpnt->dai_drv = dai_drv; cmpnt->num_dai = num_dai; + INIT_LIST_HEAD(&cmpnt->dai_list); - /* - * snd_soc_register_dai() uses fmt_single_name(), and - * snd_soc_register_dais() uses fmt_multiple_name() - * for dai->name which is used for name based matching - * - * this function is used from cpu/codec. - * allow_single_dai flag can ignore "codec" driver reworking - * since it had been used snd_soc_register_dais(), - */ - if ((1 == num_dai) && allow_single_dai) - ret = snd_soc_register_dai(dev, dai_drv); - else - ret = snd_soc_register_dais(dev, dai_drv, num_dai); + ret = snd_soc_register_dais(cmpnt, codec, dai_drv, num_dai, + allow_single_dai); if (ret < 0) { dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret); goto error_component_name; @@ -4120,7 +4083,9 @@ int snd_soc_register_component(struct device *dev, return -ENOMEM; } - return __snd_soc_register_component(dev, cmpnt, cmpnt_drv, + cmpnt->ignore_pmdown_time = true; + + return __snd_soc_register_component(dev, cmpnt, cmpnt_drv, NULL, dai_drv, num_dai, true); } EXPORT_SYMBOL_GPL(snd_soc_register_component); @@ -4140,7 +4105,7 @@ void snd_soc_unregister_component(struct device *dev) return; found: - snd_soc_unregister_dais(dev, cmpnt->num_dai); + snd_soc_unregister_dais(cmpnt); mutex_lock(&client_mutex); list_del(&cmpnt->list); @@ -4318,7 +4283,7 @@ int snd_soc_register_codec(struct device *dev, codec->volatile_register = codec_drv->volatile_register; codec->readable_register = codec_drv->readable_register; codec->writable_register = codec_drv->writable_register; - codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time; + codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; codec->dapm.codec = codec; @@ -4341,7 +4306,7 @@ int snd_soc_register_codec(struct device *dev, /* register component */ ret = __snd_soc_register_component(dev, &codec->component, &codec_drv->component_driver, - dai_drv, num_dai, false); + codec, dai_drv, num_dai, false); if (ret < 0) { dev_err(codec->dev, "ASoC: Failed to regster component: %d\n", ret); goto fail_codec_name; @@ -4416,6 +4381,122 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name); +static const struct snd_soc_dapm_widget simple_widgets[] = { + SND_SOC_DAPM_MIC("Microphone", NULL), + SND_SOC_DAPM_LINE("Line", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, + const char *propname) +{ + struct device_node *np = card->dev->of_node; + struct snd_soc_dapm_widget *widgets; + const char *template, *wname; + int i, j, num_widgets, ret; + + num_widgets = of_property_count_strings(np, propname); + if (num_widgets < 0) { + dev_err(card->dev, + "ASoC: Property '%s' does not exist\n", propname); + return -EINVAL; + } + if (num_widgets & 1) { + dev_err(card->dev, + "ASoC: Property '%s' length is not even\n", propname); + return -EINVAL; + } + + num_widgets /= 2; + if (!num_widgets) { + dev_err(card->dev, "ASoC: Property '%s's length is zero\n", + propname); + return -EINVAL; + } + + widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets), + GFP_KERNEL); + if (!widgets) { + dev_err(card->dev, + "ASoC: Could not allocate memory for widgets\n"); + return -ENOMEM; + } + + for (i = 0; i < num_widgets; i++) { + ret = of_property_read_string_index(np, propname, + 2 * i, &template); + if (ret) { + dev_err(card->dev, + "ASoC: Property '%s' index %d read error:%d\n", + propname, 2 * i, ret); + return -EINVAL; + } + + for (j = 0; j < ARRAY_SIZE(simple_widgets); j++) { + if (!strncmp(template, simple_widgets[j].name, + strlen(simple_widgets[j].name))) { + widgets[i] = simple_widgets[j]; + break; + } + } + + if (j >= ARRAY_SIZE(simple_widgets)) { + dev_err(card->dev, + "ASoC: DAPM widget '%s' is not supported\n", + template); + return -EINVAL; + } + + ret = of_property_read_string_index(np, propname, + (2 * i) + 1, + &wname); + if (ret) { + dev_err(card->dev, + "ASoC: Property '%s' index %d read error:%d\n", + propname, (2 * i) + 1, ret); + return -EINVAL; + } + + widgets[i].name = wname; + } + + card->dapm_widgets = widgets; + card->num_dapm_widgets = num_widgets; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); + +int snd_soc_of_parse_tdm_slot(struct device_node *np, + unsigned int *slots, + unsigned int *slot_width) +{ + u32 val; + int ret; + + if (of_property_read_bool(np, "dai-tdm-slot-num")) { + ret = of_property_read_u32(np, "dai-tdm-slot-num", &val); + if (ret) + return ret; + + if (slots) + *slots = val; + } + + if (of_property_read_bool(np, "dai-tdm-slot-width")) { + ret = of_property_read_u32(np, "dai-tdm-slot-width", &val); + if (ret) + return ret; + + if (slot_width) + *slot_width = val; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot); + int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname) { @@ -4617,10 +4698,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..c8a780d0d057 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -70,8 +70,6 @@ static int dapm_up_seq[] = { [snd_soc_dapm_aif_out] = 4, [snd_soc_dapm_mic] = 5, [snd_soc_dapm_mux] = 6, - [snd_soc_dapm_virt_mux] = 6, - [snd_soc_dapm_value_mux] = 6, [snd_soc_dapm_dac] = 7, [snd_soc_dapm_switch] = 8, [snd_soc_dapm_mixer] = 8, @@ -102,8 +100,6 @@ static int dapm_down_seq[] = { [snd_soc_dapm_mic] = 7, [snd_soc_dapm_micbias] = 8, [snd_soc_dapm_mux] = 9, - [snd_soc_dapm_virt_mux] = 9, - [snd_soc_dapm_value_mux] = 9, [snd_soc_dapm_aif_in] = 10, [snd_soc_dapm_aif_out] = 10, [snd_soc_dapm_dai_in] = 10, @@ -115,6 +111,12 @@ static int dapm_down_seq[] = { [snd_soc_dapm_post] = 14, }; +static void dapm_assert_locked(struct snd_soc_dapm_context *dapm) +{ + if (dapm->card && dapm->card->instantiated) + lockdep_assert_held(&dapm->card->dapm_mutex); +} + static void pop_wait(u32 pop_time) { if (pop_time) @@ -146,15 +148,16 @@ static bool dapm_dirty_widget(struct snd_soc_dapm_widget *w) return !list_empty(&w->dirty); } -void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) +static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) { + dapm_assert_locked(w->dapm); + if (!dapm_dirty_widget(w)) { dev_vdbg(w->dapm->dev, "Marking %s dirty due to %s\n", w->name, reason); list_add_tail(&w->dirty, &w->dapm->card->dapm_dirty); } } -EXPORT_SYMBOL_GPL(dapm_mark_dirty); void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm) { @@ -361,6 +364,8 @@ static void dapm_reset(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; + lockdep_assert_held(&card->dapm_mutex); + memset(&card->dapm_stats, 0, sizeof(card->dapm_stats)); list_for_each_entry(w, &card->widgets, list) { @@ -371,18 +376,23 @@ static void dapm_reset(struct snd_soc_card *card) } } -static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg) +static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg, + unsigned int *value) { - if (w->codec) - return snd_soc_read(w->codec, reg); - else if (w->platform) - return snd_soc_platform_read(w->platform, reg); + if (w->codec) { + *value = snd_soc_read(w->codec, reg); + return 0; + } else if (w->platform) { + *value = snd_soc_platform_read(w->platform, reg); + return 0; + } dev_err(w->dapm->dev, "ASoC: no valid widget read method\n"); return -1; } -static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, int val) +static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, + unsigned int val) { if (w->codec) return snd_soc_write(w->codec, reg, val); @@ -430,13 +440,12 @@ static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w, return ret; } else { soc_widget_lock(w); - ret = soc_widget_read(w, reg); + ret = soc_widget_read(w, reg, &old); if (ret < 0) { soc_widget_unlock(w); return ret; } - old = ret; new = (old & ~mask) | (value & mask); change = old != new; if (change) { @@ -495,131 +504,40 @@ out: return ret; } -/* set up initial codec paths */ -static void dapm_set_path_status(struct snd_soc_dapm_widget *w, - struct snd_soc_dapm_path *p, int i) +/* connect mux widget to its interconnecting audio paths */ +static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, + struct snd_soc_dapm_path *path, const char *control_name, + const struct snd_kcontrol_new *kcontrol) { - switch (w->id) { - case snd_soc_dapm_switch: - case snd_soc_dapm_mixer: - case snd_soc_dapm_mixer_named_ctl: { - int val; - struct soc_mixer_control *mc = (struct soc_mixer_control *) - w->kcontrol_news[i].private_value; - int reg = mc->reg; - unsigned int shift = mc->shift; - int max = mc->max; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int invert = mc->invert; - - if (reg != SND_SOC_NOPM) { - val = soc_widget_read(w, reg); - val = (val >> shift) & mask; - if (invert) - val = max - val; - p->connect = !!val; - } else { - p->connect = 0; - } - - } - break; - case snd_soc_dapm_mux: { - struct soc_enum *e = (struct soc_enum *) - w->kcontrol_news[i].private_value; - int val, item; - - val = soc_widget_read(w, e->reg); - item = (val >> e->shift_l) & e->mask; - - if (item < e->max && !strcmp(p->name, e->texts[item])) - p->connect = 1; - else - p->connect = 0; - } - break; - case snd_soc_dapm_virt_mux: { - struct soc_enum *e = (struct soc_enum *) - w->kcontrol_news[i].private_value; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val, item; + int i; - p->connect = 0; + if (e->reg != SND_SOC_NOPM) { + soc_widget_read(dest, e->reg, &val); + val = (val >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + } else { /* since a virtual mux has no backing registers to * decide which path to connect, it will try to match * with the first enumeration. This is to ensure * that the default mux choice (the first) will be * correctly powered up during initialization. */ - if (!strcmp(p->name, e->texts[0])) - p->connect = 1; - } - break; - case snd_soc_dapm_value_mux: { - struct soc_enum *e = (struct soc_enum *) - w->kcontrol_news[i].private_value; - int val, item; - - val = soc_widget_read(w, e->reg); - val = (val >> e->shift_l) & e->mask; - for (item = 0; item < e->max; item++) { - if (val == e->values[item]) - break; - } - - if (item < e->max && !strcmp(p->name, e->texts[item])) - p->connect = 1; - else - p->connect = 0; - } - break; - /* does not affect routing - always connected */ - case snd_soc_dapm_pga: - case snd_soc_dapm_out_drv: - case snd_soc_dapm_output: - case snd_soc_dapm_adc: - case snd_soc_dapm_input: - case snd_soc_dapm_siggen: - case snd_soc_dapm_dac: - case snd_soc_dapm_micbias: - case snd_soc_dapm_vmid: - case snd_soc_dapm_supply: - case snd_soc_dapm_regulator_supply: - case snd_soc_dapm_clock_supply: - case snd_soc_dapm_aif_in: - case snd_soc_dapm_aif_out: - case snd_soc_dapm_dai_in: - case snd_soc_dapm_dai_out: - case snd_soc_dapm_hp: - case snd_soc_dapm_mic: - case snd_soc_dapm_spk: - case snd_soc_dapm_line: - case snd_soc_dapm_dai_link: - case snd_soc_dapm_kcontrol: - p->connect = 1; - break; - /* does affect routing - dynamically connected */ - case snd_soc_dapm_pre: - case snd_soc_dapm_post: - p->connect = 0; - break; + item = 0; } -} -/* connect mux widget to its interconnecting audio paths */ -static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, - struct snd_soc_dapm_path *path, const char *control_name, - const struct snd_kcontrol_new *kcontrol) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - int i; - - for (i = 0; i < e->max; i++) { + for (i = 0; i < e->items; i++) { if (!(strcmp(control_name, e->texts[i]))) { list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &dest->sources); list_add(&path->list_source, &src->sinks); path->name = (char*)e->texts[i]; - dapm_set_path_status(dest, path, 0); + if (i == item) + path->connect = 1; + else + path->connect = 0; return 0; } } @@ -627,6 +545,30 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, return -ENODEV; } +/* set up initial codec paths */ +static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_path *p, int i) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *) + w->kcontrol_news[i].private_value; + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val; + + if (reg != SND_SOC_NOPM) { + soc_widget_read(w, reg, &val); + val = (val >> shift) & mask; + if (invert) + val = max - val; + p->connect = !!val; + } else { + p->connect = 0; + } +} + /* connect mixer widget to its interconnecting audio paths */ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, @@ -641,7 +583,7 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, list_add(&path->list_sink, &dest->sources); list_add(&path->list_source, &src->sinks); path->name = dest->kcontrol_news[i].name; - dapm_set_path_status(dest, path, i); + dapm_set_mixer_path_status(dest, path, i); return 0; } } @@ -720,8 +662,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, kcname_in_long_name = true; break; case snd_soc_dapm_mux: - case snd_soc_dapm_virt_mux: - case snd_soc_dapm_value_mux: wname_in_long_name = true; kcname_in_long_name = false; break; @@ -1215,7 +1155,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, ret = regulator_allow_bypass(w->regulator, false); if (ret != 0) dev_warn(w->dapm->dev, - "ASoC: Failed to bypass %s: %d\n", + "ASoC: Failed to unbypass %s: %d\n", w->name, ret); } @@ -1225,7 +1165,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, ret = regulator_allow_bypass(w->regulator, true); if (ret != 0) dev_warn(w->dapm->dev, - "ASoC: Failed to unbypass %s: %d\n", + "ASoC: Failed to bypass %s: %d\n", w->name, ret); } @@ -1820,6 +1760,8 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) ASYNC_DOMAIN_EXCLUSIVE(async_domain); enum snd_soc_bias_level bias; + lockdep_assert_held(&card->dapm_mutex); + trace_snd_soc_dapm_start(card); list_for_each_entry(d, &card->dapm_list, list) { @@ -1894,10 +1836,14 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) trace_snd_soc_dapm_walk_done(card); - /* Run all the bias changes in parallel */ - list_for_each_entry(d, &card->dapm_list, list) - async_schedule_domain(dapm_pre_sequence_async, d, - &async_domain); + /* Run card bias changes at first */ + dapm_pre_sequence_async(&card->dapm, 0); + /* Run other bias changes in parallel */ + list_for_each_entry(d, &card->dapm_list, list) { + if (d != &card->dapm) + async_schedule_domain(dapm_pre_sequence_async, d, + &async_domain); + } async_synchronize_full_domain(&async_domain); list_for_each_entry(w, &down_list, power_list) { @@ -1917,10 +1863,14 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) dapm_seq_run(card, &up_list, event, true); /* Run all the bias changes in parallel */ - list_for_each_entry(d, &card->dapm_list, list) - async_schedule_domain(dapm_post_sequence_async, d, - &async_domain); + list_for_each_entry(d, &card->dapm_list, list) { + if (d != &card->dapm) + async_schedule_domain(dapm_post_sequence_async, d, + &async_domain); + } async_synchronize_full_domain(&async_domain); + /* Run card bias changes at last */ + dapm_post_sequence_async(&card->dapm, 0); /* do we need to notify any clients that DAPM event is complete */ list_for_each_entry(d, &card->dapm_list, list) { @@ -2107,6 +2057,8 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card, struct snd_soc_dapm_path *path; int found = 0; + lockdep_assert_held(&card->dapm_mutex); + /* find dapm widget path assoc with kcontrol */ dapm_kcontrol_for_each_path(path, kcontrol) { if (!path->name || !e->texts[mux]) @@ -2157,6 +2109,8 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, struct snd_soc_dapm_path *path; int found = 0; + lockdep_assert_held(&card->dapm_mutex); + /* find dapm widget path assoc with kcontrol */ dapm_kcontrol_for_each_path(path, kcontrol) { found = 1; @@ -2322,6 +2276,8 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); + dapm_assert_locked(dapm); + if (!w) { dev_err(dapm->dev, "ASoC: DAPM unknown pin %s\n", pin); return -EINVAL; @@ -2338,18 +2294,18 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, } /** - * snd_soc_dapm_sync - scan and power dapm paths + * snd_soc_dapm_sync_unlocked - scan and power dapm paths * @dapm: DAPM context * * Walks all dapm audio paths and powers widgets according to their * stream or path usage. * + * Requires external locking. + * * Returns 0 for success. */ -int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) +int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm) { - int ret; - /* * Suppress early reports (eg, jacks syncing their state) to avoid * silly DAPM runs during card startup. @@ -2357,8 +2313,25 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) if (!dapm->card || !dapm->card->instantiated) return 0; + return dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_unlocked); + +/** + * snd_soc_dapm_sync - scan and power dapm paths + * @dapm: DAPM context + * + * Walks all dapm audio paths and powers widgets according to their + * stream or path usage. + * + * Returns 0 for success. + */ +int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) +{ + int ret; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - ret = dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP); + ret = snd_soc_dapm_sync_unlocked(dapm); mutex_unlock(&dapm->card->dapm_mutex); return ret; } @@ -2441,8 +2414,6 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, path->connect = 1; return 0; case snd_soc_dapm_mux: - case snd_soc_dapm_virt_mux: - case snd_soc_dapm_value_mux: ret = dapm_connect_mux(dapm, wsource, wsink, path, control, &wsink->kcontrol_news[0]); if (ret != 0) @@ -2473,7 +2444,8 @@ err: } static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_route *route) + const struct snd_soc_dapm_route *route, + unsigned int is_prefixed) { struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL; @@ -2483,7 +2455,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, char prefixed_source[80]; int ret; - if (dapm->codec && dapm->codec->name_prefix) { + if (dapm->codec && dapm->codec->name_prefix && !is_prefixed) { snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", dapm->codec->name_prefix, route->sink); sink = prefixed_sink; @@ -2611,7 +2583,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { - r = snd_soc_dapm_add_route(dapm, route); + r = snd_soc_dapm_add_route(dapm, route, false); if (r < 0) { dev_err(dapm->dev, "ASoC: Failed to add route %s -> %s -> %s\n", route->source, @@ -2768,8 +2740,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) dapm_new_mixer(w); break; case snd_soc_dapm_mux: - case snd_soc_dapm_virt_mux: - case snd_soc_dapm_value_mux: dapm_new_mux(w); break; case snd_soc_dapm_pga: @@ -2782,7 +2752,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) /* Read the initial power state from the device */ if (w->reg >= 0) { - val = soc_widget_read(w, w->reg) >> w->shift; + soc_widget_read(w, w->reg, &val); + val = val >> w->shift; val &= w->mask; if (val == w->on_val) w->power = 1; @@ -2868,6 +2839,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 +2873,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); @@ -2925,13 +2901,20 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val; + unsigned int reg_val, val; - val = snd_soc_read(codec, e->reg); - ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & e->mask; - if (e->shift_l != e->shift_r) - ucontrol->value.enumerated.item[1] = - (val >> e->shift_r) & e->mask; + if (e->reg != SND_SOC_NOPM) + reg_val = snd_soc_read(codec, e->reg); + else + reg_val = dapm_kcontrol_get_value(kcontrol); + + val = (reg_val >> e->shift_l) & e->mask; + ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); + if (e->shift_l != e->shift_r) { + val = (reg_val >> e->shift_r) & e->mask; + val = snd_soc_enum_val_to_item(e, val); + ucontrol->value.enumerated.item[1] = val; + } return 0; } @@ -2952,183 +2935,53 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val, mux, change; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, change; unsigned int mask; struct snd_soc_dapm_update update; + int ret = 0; - if (ucontrol->value.enumerated.item[0] > e->max - 1) + if (item[0] >= e->items) return -EINVAL; - mux = ucontrol->value.enumerated.item[0]; - val = mux << e->shift_l; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; mask = e->mask << e->shift_l; if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] > e->max - 1) + if (item[1] > e->items) return -EINVAL; - val |= ucontrol->value.enumerated.item[1] << e->shift_r; + val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_l; mask |= e->mask << e->shift_r; } mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = snd_soc_test_bits(codec, e->reg, mask, val); + if (e->reg != SND_SOC_NOPM) + change = snd_soc_test_bits(codec, e->reg, mask, val); + else + change = dapm_kcontrol_set_value(kcontrol, val); + if (change) { - update.kcontrol = kcontrol; - update.reg = e->reg; - update.mask = mask; - update.val = val; - card->update = &update; + if (e->reg != SND_SOC_NOPM) { + update.kcontrol = kcontrol; + update.reg = e->reg; + update.mask = mask; + update.val = val; + card->update = &update; + } - soc_dapm_mux_update_power(card, kcontrol, mux, e); + ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e); card->update = NULL; } mutex_unlock(&card->dapm_mutex); - return change; -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); - -/** - * snd_soc_dapm_get_enum_virt - Get virtual DAPM mux - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Returns 0 for success. - */ -int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.enumerated.item[0] = dapm_kcontrol_get_value(kcontrol); - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); - -/** - * snd_soc_dapm_put_enum_virt - Set virtual DAPM mux - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Returns 0 for success. - */ -int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); - struct snd_soc_card *card = codec->card; - unsigned int value; - struct soc_enum *e = - (struct soc_enum *)kcontrol->private_value; - int change; - - if (ucontrol->value.enumerated.item[0] >= e->max) - return -EINVAL; - - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - - value = ucontrol->value.enumerated.item[0]; - change = dapm_kcontrol_set_value(kcontrol, value); - if (change) - soc_dapm_mux_update_power(card, kcontrol, value, e); - - mutex_unlock(&card->dapm_mutex); - return change; -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); - -/** - * snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get - * callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value of a dapm semi enumerated double mixer control. - * - * Semi enumerated mixer: the enumerated items are referred as values. Can be - * used for handling bitfield coded enumeration for example. - * - * Returns 0 for success. - */ -int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int reg_val, val, mux; - - reg_val = snd_soc_read(codec, e->reg); - val = (reg_val >> e->shift_l) & e->mask; - for (mux = 0; mux < e->max; mux++) { - if (val == e->values[mux]) - break; - } - ucontrol->value.enumerated.item[0] = mux; - if (e->shift_l != e->shift_r) { - val = (reg_val >> e->shift_r) & e->mask; - for (mux = 0; mux < e->max; mux++) { - if (val == e->values[mux]) - break; - } - ucontrol->value.enumerated.item[1] = mux; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double); - -/** - * snd_soc_dapm_put_value_enum_double - dapm semi enumerated double mixer set - * callback - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value of a dapm semi enumerated double mixer control. - * - * Semi enumerated mixer: the enumerated items are referred as values. Can be - * used for handling bitfield coded enumeration for example. - * - * Returns 0 for success. - */ -int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); - struct snd_soc_card *card = codec->card; - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val, mux, change; - unsigned int mask; - struct snd_soc_dapm_update update; - - if (ucontrol->value.enumerated.item[0] > e->max - 1) - return -EINVAL; - mux = ucontrol->value.enumerated.item[0]; - val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l; - mask = e->mask << e->shift_l; - if (e->shift_l != e->shift_r) { - if (ucontrol->value.enumerated.item[1] > e->max - 1) - return -EINVAL; - val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r; - mask |= e->mask << e->shift_r; - } - - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = snd_soc_test_bits(codec, e->reg, mask, val); - if (change) { - update.kcontrol = kcontrol; - update.reg = e->reg; - update.mask = mask; - update.val = val; - card->update = &update; - - soc_dapm_mux_update_power(card, kcontrol, mux, e); - - card->update = NULL; - } + if (ret > 0) + soc_dpcm_runtime_update(card); - mutex_unlock(&card->dapm_mutex); return change; } -EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); +EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); /** * snd_soc_dapm_info_pin_switch - Info for a pin switch @@ -3185,15 +3038,11 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); const char *pin = (const char *)kcontrol->private_value; - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - if (ucontrol->value.integer.value[0]) snd_soc_dapm_enable_pin(&card->dapm, pin); else snd_soc_dapm_disable_pin(&card->dapm, pin); - mutex_unlock(&card->dapm_mutex); - snd_soc_dapm_sync(&card->dapm); return 0; } @@ -3223,7 +3072,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, ret = regulator_allow_bypass(w->regulator, true); if (ret != 0) dev_warn(w->dapm->dev, - "ASoC: Failed to unbypass %s: %d\n", + "ASoC: Failed to bypass %s: %d\n", w->name, ret); } break; @@ -3262,8 +3111,6 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_mux: - case snd_soc_dapm_virt_mux: - case snd_soc_dapm_value_mux: w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_dai_out: @@ -3614,6 +3461,55 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) return 0; } +void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *rtd = card->rtd; + struct snd_soc_dai *cpu_dai, *codec_dai; + struct snd_soc_dapm_route r; + int i; + + memset(&r, 0, sizeof(r)); + + /* for each BE DAI link... */ + for (i = 0; i < card->num_rtd; i++) { + rtd = &card->rtd[i]; + cpu_dai = rtd->cpu_dai; + codec_dai = rtd->codec_dai; + + /* dynamic FE links have no fixed DAI mapping */ + if (rtd->dai_link->dynamic) + continue; + + /* there is no point in connecting BE DAI links with dummies */ + if (snd_soc_dai_is_dummy(codec_dai) || + snd_soc_dai_is_dummy(cpu_dai)) + continue; + + /* connect BE DAI playback if widgets are valid */ + if (codec_dai->playback_widget && cpu_dai->playback_widget) { + r.source = cpu_dai->playback_widget->name; + r.sink = codec_dai->playback_widget->name; + dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", + cpu_dai->codec->name, r.source, + codec_dai->platform->name, r.sink); + + snd_soc_dapm_add_route(&card->dapm, &r, true); + } + + /* connect BE DAI capture if widgets are valid */ + if (codec_dai->capture_widget && cpu_dai->capture_widget) { + r.source = codec_dai->capture_widget->name; + r.sink = cpu_dai->capture_widget->name; + dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", + codec_dai->codec->name, r.source, + cpu_dai->platform->name, r.sink); + + snd_soc_dapm_add_route(&card->dapm, &r, true); + } + + } +} + static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, int event) { @@ -3693,23 +3589,52 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, } /** + * snd_soc_dapm_enable_pin_unlocked - enable pin. + * @dapm: DAPM context + * @pin: pin name + * + * Enables input/output pin and its parents or children widgets iff there is + * a valid audio route and active audio stream. + * + * Requires external locking. + * + * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to + * do any widget power switching. + */ +int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, + const char *pin) +{ + return snd_soc_dapm_set_pin(dapm, pin, 1); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin_unlocked); + +/** * snd_soc_dapm_enable_pin - enable pin. * @dapm: DAPM context * @pin: pin name * * Enables input/output pin and its parents or children widgets iff there is * a valid audio route and active audio stream. + * * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to * do any widget power switching. */ int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin) { - return snd_soc_dapm_set_pin(dapm, pin, 1); + int ret; + + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_dapm_set_pin(dapm, pin, 1); + + mutex_unlock(&dapm->card->dapm_mutex); + + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); /** - * snd_soc_dapm_force_enable_pin - force a pin to be enabled + * snd_soc_dapm_force_enable_pin_unlocked - force a pin to be enabled * @dapm: DAPM context * @pin: pin name * @@ -3717,11 +3642,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); * intended for use with microphone bias supplies used in microphone * jack detection. * + * Requires external locking. + * * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to * do any widget power switching. */ -int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, - const char *pin) +int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, + const char *pin) { struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); @@ -3737,25 +3664,103 @@ int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, return 0; } +EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin_unlocked); + +/** + * snd_soc_dapm_force_enable_pin - force a pin to be enabled + * @dapm: DAPM context + * @pin: pin name + * + * Enables input/output pin regardless of any other state. This is + * intended for use with microphone bias supplies used in microphone + * jack detection. + * + * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to + * do any widget power switching. + */ +int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, + const char *pin) +{ + int ret; + + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_dapm_force_enable_pin_unlocked(dapm, pin); + + mutex_unlock(&dapm->card->dapm_mutex); + + return ret; +} EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin); /** + * snd_soc_dapm_disable_pin_unlocked - disable pin. + * @dapm: DAPM context + * @pin: pin name + * + * Disables input/output pin and its parents or children widgets. + * + * Requires external locking. + * + * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to + * do any widget power switching. + */ +int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm, + const char *pin) +{ + return snd_soc_dapm_set_pin(dapm, pin, 0); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin_unlocked); + +/** * snd_soc_dapm_disable_pin - disable pin. * @dapm: DAPM context * @pin: pin name * * Disables input/output pin and its parents or children widgets. + * * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to * do any widget power switching. */ int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, const char *pin) { - return snd_soc_dapm_set_pin(dapm, pin, 0); + int ret; + + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_dapm_set_pin(dapm, pin, 0); + + mutex_unlock(&dapm->card->dapm_mutex); + + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); /** + * snd_soc_dapm_nc_pin_unlocked - permanently disable pin. + * @dapm: DAPM context + * @pin: pin name + * + * Marks the specified pin as being not connected, disabling it along + * any parent or child widgets. At present this is identical to + * snd_soc_dapm_disable_pin() but in future it will be extended to do + * additional things such as disabling controls which only affect + * paths through the pin. + * + * Requires external locking. + * + * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to + * do any widget power switching. + */ +int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm, + const char *pin) +{ + return snd_soc_dapm_set_pin(dapm, pin, 0); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin_unlocked); + +/** * snd_soc_dapm_nc_pin - permanently disable pin. * @dapm: DAPM context * @pin: pin name @@ -3771,7 +3776,15 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); */ int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin) { - return snd_soc_dapm_set_pin(dapm, pin, 0); + int ret; + + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_dapm_set_pin(dapm, pin, 0); + + mutex_unlock(&dapm->card->dapm_mutex); + + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin); @@ -3911,7 +3924,7 @@ void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm) } EXPORT_SYMBOL_GPL(snd_soc_dapm_free); -static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) +static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm) { struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; @@ -3951,14 +3964,21 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) */ void snd_soc_dapm_shutdown(struct snd_soc_card *card) { - struct snd_soc_codec *codec; + struct snd_soc_dapm_context *dapm; - list_for_each_entry(codec, &card->codec_dev_list, card_list) { - soc_dapm_shutdown_codec(&codec->dapm); - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) - snd_soc_dapm_set_bias_level(&codec->dapm, - SND_SOC_BIAS_OFF); + list_for_each_entry(dapm, &card->dapm_list, list) { + if (dapm != &card->dapm) { + soc_dapm_shutdown_dapm(dapm); + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) + snd_soc_dapm_set_bias_level(dapm, + SND_SOC_BIAS_OFF); + } } + + soc_dapm_shutdown_dapm(&card->dapm); + if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) + snd_soc_dapm_set_bias_level(&card->dapm, + SND_SOC_BIAS_OFF); } /* Module information */ 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..5bace124ef43 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -137,10 +137,15 @@ 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) hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME; + if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT) + hw.info |= SNDRV_PCM_INFO_BATCH; } return snd_soc_set_runtime_hwparams(substream, &hw); @@ -171,17 +176,35 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( { struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); struct snd_dmaengine_dai_dma_data *dma_data; + dma_filter_fn fn = NULL; dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0]) return pcm->chan[0]; - if (pcm->config->compat_request_channel) + if (pcm->config && pcm->config->compat_request_channel) return pcm->config->compat_request_channel(rtd, substream); - return snd_dmaengine_pcm_request_channel(pcm->config->compat_filter_fn, - dma_data->filter_data); + if (pcm->config) + fn = pcm->config->compat_filter_fn; + + return snd_dmaengine_pcm_request_channel(fn, dma_data->filter_data); +} + +static bool dmaengine_pcm_can_report_residue(struct dma_chan *chan) +{ + struct dma_slave_caps dma_caps; + int ret; + + ret = dma_get_slave_caps(chan, &dma_caps); + if (ret != 0) + return true; + + if (dma_caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR) + return false; + + return true; } static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) @@ -236,6 +259,16 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) max_buffer_size); if (ret) goto err_free; + + /* + * This will only return false if we know for sure that at least + * one channel does not support residue reporting. If the DMA + * driver does not implement the slave_caps API we rely having + * the NO_RESIDUE flag set manually in case residue reporting is + * not supported. + */ + if (!dmaengine_pcm_can_report_residue(pcm->chan[i])) + pcm->flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE; } return 0; @@ -245,6 +278,18 @@ err_free: return ret; } +static snd_pcm_uframes_t dmaengine_pcm_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); + + if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) + return snd_dmaengine_pcm_pointer_no_residue(substream); + else + return snd_dmaengine_pcm_pointer(substream); +} + static const struct snd_pcm_ops dmaengine_pcm_ops = { .open = dmaengine_pcm_open, .close = snd_dmaengine_pcm_close, @@ -252,7 +297,7 @@ static const struct snd_pcm_ops dmaengine_pcm_ops = { .hw_params = dmaengine_pcm_hw_params, .hw_free = snd_pcm_lib_free_pages, .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, + .pointer = dmaengine_pcm_pointer, }; static const struct snd_soc_platform_driver dmaengine_pcm_platform = { @@ -262,46 +307,72 @@ static const struct snd_soc_platform_driver dmaengine_pcm_platform = { .probe_order = SND_SOC_COMP_ORDER_LATE, }; -static const struct snd_pcm_ops dmaengine_no_residue_pcm_ops = { - .open = dmaengine_pcm_open, - .close = snd_dmaengine_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = dmaengine_pcm_hw_params, - .hw_free = snd_pcm_lib_free_pages, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer_no_residue, -}; - -static const struct snd_soc_platform_driver dmaengine_no_residue_pcm_platform = { - .ops = &dmaengine_no_residue_pcm_ops, - .pcm_new = dmaengine_pcm_new, - .pcm_free = dmaengine_pcm_free, - .probe_order = SND_SOC_COMP_ORDER_LATE, -}; - static const char * const dmaengine_pcm_dma_channel_names[] = { [SNDRV_PCM_STREAM_PLAYBACK] = "tx", [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 +386,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 +395,21 @@ 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, - &dmaengine_no_residue_pcm_platform); - else - return snd_soc_add_platform(dev, &pcm->platform, - &dmaengine_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 +424,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 +431,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..330eaf007d89 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -35,6 +35,86 @@ #define DPCM_MAX_BE_USERS 8 /** + * snd_soc_runtime_activate() - Increment active count for PCM runtime components + * @rtd: ASoC PCM runtime that is activated + * @stream: Direction of the PCM stream + * + * Increments the active count for all the DAIs and components attached to a PCM + * runtime. Should typically be called when a stream is opened. + * + * Must be called with the rtd->pcm_mutex being held + */ +void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + lockdep_assert_held(&rtd->pcm_mutex); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + cpu_dai->playback_active++; + codec_dai->playback_active++; + } else { + cpu_dai->capture_active++; + codec_dai->capture_active++; + } + + cpu_dai->active++; + codec_dai->active++; + cpu_dai->component->active++; + codec_dai->component->active++; +} + +/** + * snd_soc_runtime_deactivate() - Decrement active count for PCM runtime components + * @rtd: ASoC PCM runtime that is deactivated + * @stream: Direction of the PCM stream + * + * Decrements the active count for all the DAIs and components attached to a PCM + * runtime. Should typically be called when a stream is closed. + * + * Must be called with the rtd->pcm_mutex being held + */ +void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + lockdep_assert_held(&rtd->pcm_mutex); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + cpu_dai->playback_active--; + codec_dai->playback_active--; + } else { + cpu_dai->capture_active--; + codec_dai->capture_active--; + } + + cpu_dai->active--; + codec_dai->active--; + cpu_dai->component->active--; + codec_dai->component->active--; +} + +/** + * snd_soc_runtime_ignore_pmdown_time() - Check whether to ignore the power down delay + * @rtd: The ASoC PCM runtime that should be checked. + * + * This function checks whether the power down delay should be ignored for a + * specific PCM runtime. Returns true if the delay is 0, if it the DAI link has + * been configured to ignore the delay, or if none of the components benefits + * from having the delay. + */ +bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) +{ + if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) + return true; + + return rtd->cpu_dai->component->ignore_pmdown_time && + rtd->codec_dai->component->ignore_pmdown_time; +} + +/** * snd_soc_set_runtime_hwparams - set the runtime hardware parameters * @substream: the pcm substream * @hw: the hardware parameters @@ -58,7 +138,7 @@ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); /* DPCM stream event, send event to FE and all active BEs. */ -static int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, +int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event) { struct snd_soc_dpcm *dpcm; @@ -84,35 +164,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); - /* 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; + 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; + } } - dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", soc_dai->rate); + 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_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_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; + } + } + + 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_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,24 +310,32 @@ 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, cpu_stream->channels_max); - hw->formats = codec_stream->formats & cpu_stream->formats; - hw->rates = codec_stream->rates & cpu_stream->rates; - if (codec_stream->rates - & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) - hw->rates |= cpu_stream->rates; - if (cpu_stream->rates - & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) - hw->rates |= codec_stream->rates; + if (hw->formats) + hw->formats &= codec_stream->formats & cpu_stream->formats; + else + hw->formats = codec_stream->formats & cpu_stream->formats; + hw->rates = snd_pcm_rate_mask_intersect(codec_stream->rates, + cpu_stream->rates); + + hw->rate_min = 0; + hw->rate_max = UINT_MAX; + + 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 +405,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); @@ -286,16 +458,9 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) runtime->hw.rate_max); dynamic: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - cpu_dai->playback_active++; - codec_dai->playback_active++; - } else { - cpu_dai->capture_active++; - codec_dai->capture_active++; - } - cpu_dai->active++; - codec_dai->active++; - rtd->codec->active++; + + snd_soc_runtime_activate(rtd, substream->stream); + mutex_unlock(&rtd->pcm_mutex); return 0; @@ -367,21 +532,10 @@ static int soc_pcm_close(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; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - cpu_dai->playback_active--; - codec_dai->playback_active--; - } else { - cpu_dai->capture_active--; - codec_dai->capture_active--; - } - - cpu_dai->active--; - codec_dai->active--; - codec->active--; + snd_soc_runtime_deactivate(rtd, substream->stream); /* clear the corresponding DAIs rate when inactive */ if (!cpu_dai->active) @@ -390,11 +544,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); @@ -409,8 +558,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) cpu_dai->runtime = NULL; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (!rtd->pmdown_time || codec->ignore_pmdown_time || - rtd->dai_link->ignore_pmdown_time) { + if (snd_soc_runtime_ignore_pmdown_time(rtd)) { /* powered down playback stream now */ snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, @@ -525,6 +673,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 +713,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 +753,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 */ @@ -665,7 +838,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, return ret; } - if (platform->driver->ops && platform->driver->bespoke_trigger) { + if (platform->driver->bespoke_trigger) { ret = platform->driver->bespoke_trigger(substream, cmd); if (ret < 0) return ret; @@ -773,7 +946,7 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, } /* disconnect a BE and FE */ -static void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) +void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm, *d; @@ -869,7 +1042,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list, return 0; } -static int dpcm_path_get(struct snd_soc_pcm_runtime *fe, +int dpcm_path_get(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list_) { struct snd_soc_dai *cpu_dai = fe->cpu_dai; @@ -891,11 +1064,6 @@ static int dpcm_path_get(struct snd_soc_pcm_runtime *fe, return paths; } -static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list) -{ - kfree(*list); -} - static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list_) { @@ -965,7 +1133,7 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, continue; /* don't connect if FE is not running */ - if (!fe->dpcm[stream].runtime) + if (!fe->dpcm[stream].runtime && !fe->fe_compr) continue; /* newly connected FE and BE */ @@ -990,7 +1158,7 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, * Find the corresponding BE DAIs that source or sink audio to this * FE substream. */ -static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, +int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list, int new) { if (new) @@ -999,7 +1167,7 @@ static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, return dpcm_prune_paths(fe, stream, list); } -static void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) +void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; @@ -1037,7 +1205,7 @@ static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe, } } -static int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; int err, count = 0; @@ -1124,6 +1292,20 @@ unwind: return err; } +static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime, + struct snd_soc_pcm_stream *stream) +{ + runtime->hw.rate_min = stream->rate_min; + runtime->hw.rate_max = stream->rate_max; + runtime->hw.channels_min = stream->channels_min; + runtime->hw.channels_max = stream->channels_max; + if (runtime->hw.formats) + runtime->hw.formats &= stream->formats; + else + runtime->hw.formats = stream->formats; + runtime->hw.rates = stream->rates; +} + static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -1131,21 +1313,10 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - runtime->hw.rate_min = cpu_dai_drv->playback.rate_min; - runtime->hw.rate_max = cpu_dai_drv->playback.rate_max; - runtime->hw.channels_min = cpu_dai_drv->playback.channels_min; - runtime->hw.channels_max = cpu_dai_drv->playback.channels_max; - runtime->hw.formats &= cpu_dai_drv->playback.formats; - runtime->hw.rates = cpu_dai_drv->playback.rates; - } else { - runtime->hw.rate_min = cpu_dai_drv->capture.rate_min; - runtime->hw.rate_max = cpu_dai_drv->capture.rate_max; - runtime->hw.channels_min = cpu_dai_drv->capture.channels_min; - runtime->hw.channels_max = cpu_dai_drv->capture.channels_max; - runtime->hw.formats &= cpu_dai_drv->capture.formats; - runtime->hw.rates = cpu_dai_drv->capture.rates; - } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback); + else + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); } static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) @@ -1186,7 +1357,7 @@ be_err: return ret; } -static int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; @@ -1247,7 +1418,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) return 0; } -static int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; @@ -1312,7 +1483,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) return 0; } -static int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; int ret; @@ -1442,7 +1613,7 @@ static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm, return ret; } -static int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, +int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd) { struct snd_soc_dpcm *dpcm; @@ -1610,7 +1781,7 @@ out: return ret; } -static int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; int ret = 0; @@ -1879,6 +2050,7 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card) paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list); if (paths < 0) { + dpcm_path_put(&list); dev_warn(fe->dev, "ASoC: %s no valid %s path\n", fe->dai_link->name, "playback"); mutex_unlock(&card->mutex); @@ -1908,6 +2080,7 @@ capture: paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list); if (paths < 0) { + dpcm_path_put(&list); dev_warn(fe->dev, "ASoC: %s no valid %s path\n", fe->dai_link->name, "capture"); mutex_unlock(&card->mutex); @@ -1972,6 +2145,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) fe->dpcm[stream].runtime = fe_substream->runtime; if (dpcm_path_get(fe, stream, &list) <= 0) { + dpcm_path_put(&list); dev_dbg(fe->dev, "ASoC: %s no valid %s route\n", fe->dai_link->name, stream ? "capture" : "playback"); } @@ -2026,10 +2200,8 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) int ret = 0, playback = 0, capture = 0; if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) { - if (cpu_dai->driver->playback.channels_min) - playback = 1; - if (cpu_dai->driver->capture.channels_min) - capture = 1; + playback = rtd->dai_link->dpcm_playback; + capture = rtd->dai_link->dpcm_capture; } else { if (codec_dai->driver->playback.channels_min && cpu_dai->driver->playback.channels_min) diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 5e633659c1b3..7f22ca35a413 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, @@ -123,6 +119,13 @@ static struct snd_soc_dai_driver dummy_dai = { }, }; +int snd_soc_dai_is_dummy(struct snd_soc_dai *dai) +{ + if (dai->driver == &dummy_dai) + return 1; + return 0; +} + static int snd_soc_dummy_probe(struct platform_device *pdev) { int ret; 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..19cca043e6e4 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) @@ -209,10 +213,7 @@ static int spdif_digital_mute(struct snd_soc_dai *dai, int mute) static int spdif_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct snd_soc_card *card = codec->card; - struct snd_soc_pcm_runtime *rtd = card->rtd; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); struct spdif_out_dev *host = snd_soc_dai_get_drvdata(cpu_dai); ucontrol->value.integer.value[0] = host->saved_params.mute; @@ -222,10 +223,7 @@ static int spdif_mute_get(struct snd_kcontrol *kcontrol, static int spdif_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct snd_soc_card *card = codec->card; - struct snd_soc_pcm_runtime *rtd = card->rtd; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); struct spdif_out_dev *host = snd_soc_dai_get_drvdata(cpu_dai); if (host->saved_params.mute == ucontrol->value.integer.value[0]) @@ -244,7 +242,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 +279,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 +302,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..31198cf7f88d 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -1,6 +1,8 @@ config SND_SOC_TEGRA tristate "SoC Audio for the Tegra System-on-Chip" depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST + depends on COMMON_CLK + depends on RESET_CONTROLLER select REGMAP_MMIO select SND_SOC_GENERIC_DMAENGINE_PCM help @@ -103,7 +105,7 @@ config SND_SOC_TEGRA_TRIMSLICE tristate "SoC Audio support for TrimSlice board" depends on SND_SOC_TEGRA && I2C select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC - select SND_SOC_TLV320AIC23 + select SND_SOC_TLV320AIC23_I2C help Say Y or M here if you want to add support for SoC audio on the TrimSlice platform. @@ -116,3 +118,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..cf5e1cfe818d 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -313,7 +313,6 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) { struct tegra20_ac97 *ac97; struct resource *mem; - u32 of_dma[2]; void __iomem *regs; int ret = 0; @@ -348,14 +347,6 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) goto err_clk_put; } - if (of_property_read_u32_array(pdev->dev.of_node, - "nvidia,dma-request-selector", - of_dma, 2) < 0) { - dev_err(&pdev->dev, "No DMA resource\n"); - ret = -ENODEV; - goto err_clk_put; - } - ac97->reset_gpio = of_get_named_gpio(pdev->dev.of_node, "nvidia,codec-reset-gpio", 0); if (gpio_is_valid(ac97->reset_gpio)) { @@ -380,12 +371,10 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) ac97->capture_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_RX1; ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ac97->capture_dma_data.maxburst = 4; - ac97->capture_dma_data.slave_id = of_dma[1]; ac97->playback_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_TX1; ac97->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ac97->playback_dma_data.maxburst = 4; - ac97->playback_dma_data.slave_id = of_dma[1]; ret = tegra_asoc_utils_init(&ac97->util_data, &pdev->dev); if (ret) @@ -404,7 +393,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 +401,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 +417,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..42c1f6bfaf2e 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; @@ -339,9 +339,7 @@ static const struct regmap_config tegra20_i2s_regmap_config = { static int tegra20_i2s_platform_probe(struct platform_device *pdev) { struct tegra20_i2s *i2s; - struct resource *mem, *memregion, *dmareq; - u32 of_dma[2]; - u32 dma_ch; + struct resource *mem, *memregion; void __iomem *regs; int ret; @@ -370,20 +368,6 @@ static int tegra20_i2s_platform_probe(struct platform_device *pdev) goto err_clk_put; } - dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dmareq) { - if (of_property_read_u32_array(pdev->dev.of_node, - "nvidia,dma-request-selector", - of_dma, 2) < 0) { - dev_err(&pdev->dev, "No DMA resource\n"); - ret = -ENODEV; - goto err_clk_put; - } - dma_ch = of_dma[1]; - } else { - dma_ch = dmareq->start; - } - memregion = devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem), DRV_NAME); if (!memregion) { @@ -410,12 +394,10 @@ static int tegra20_i2s_platform_probe(struct platform_device *pdev) i2s->capture_dma_data.addr = mem->start + TEGRA20_I2S_FIFO2; i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; i2s->capture_dma_data.maxburst = 4; - i2s->capture_dma_data.slave_id = dma_ch; i2s->playback_dma_data.addr = mem->start + TEGRA20_I2S_FIFO1; i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; i2s->playback_dma_data.maxburst = 4; - i2s->playback_dma_data.slave_id = dma_ch; pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { 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_ahub.c b/sound/soc/tegra/tegra30_ahub.c index 31154338c1eb..d6f4c9940e0c 100644 --- a/sound/soc/tegra/tegra30_ahub.c +++ b/sound/soc/tegra/tegra30_ahub.c @@ -24,8 +24,8 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> +#include <linux/reset.h> #include <linux/slab.h> -#include <linux/clk/tegra.h> #include <sound/soc.h> #include "tegra30_ahub.h" @@ -95,8 +95,8 @@ static int tegra30_ahub_runtime_resume(struct device *dev) } int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, - dma_addr_t *fiforeg, - unsigned int *reqsel) + char *dmachan, int dmachan_len, + dma_addr_t *fiforeg) { int channel; u32 reg, val; @@ -110,9 +110,11 @@ int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, __set_bit(channel, ahub->rx_usage); *rxcif = TEGRA30_AHUB_RXCIF_APBIF_RX0 + channel; + snprintf(dmachan, dmachan_len, "rx%d", channel); *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO + (channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE); - *reqsel = ahub->dma_sel + channel; + + pm_runtime_get_sync(ahub->dev); reg = TEGRA30_AHUB_CHANNEL_CTRL + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); @@ -140,6 +142,8 @@ int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE); ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf); + pm_runtime_put(ahub->dev); + return 0; } EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_rx_fifo); @@ -149,12 +153,16 @@ int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif) int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; int reg, val; + pm_runtime_get_sync(ahub->dev); + reg = TEGRA30_AHUB_CHANNEL_CTRL + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); val = tegra30_apbif_read(reg); val |= TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; tegra30_apbif_write(reg, val); + pm_runtime_put(ahub->dev); + return 0; } EXPORT_SYMBOL_GPL(tegra30_ahub_enable_rx_fifo); @@ -164,12 +172,16 @@ int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif) int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; int reg, val; + pm_runtime_get_sync(ahub->dev); + reg = TEGRA30_AHUB_CHANNEL_CTRL + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); val = tegra30_apbif_read(reg); val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_EN; tegra30_apbif_write(reg, val); + pm_runtime_put(ahub->dev); + return 0; } EXPORT_SYMBOL_GPL(tegra30_ahub_disable_rx_fifo); @@ -185,8 +197,8 @@ int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif) EXPORT_SYMBOL_GPL(tegra30_ahub_free_rx_fifo); int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, - dma_addr_t *fiforeg, - unsigned int *reqsel) + char *dmachan, int dmachan_len, + dma_addr_t *fiforeg) { int channel; u32 reg, val; @@ -200,9 +212,11 @@ int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, __set_bit(channel, ahub->tx_usage); *txcif = TEGRA30_AHUB_TXCIF_APBIF_TX0 + channel; + snprintf(dmachan, dmachan_len, "tx%d", channel); *fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO + (channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE); - *reqsel = ahub->dma_sel + channel; + + pm_runtime_get_sync(ahub->dev); reg = TEGRA30_AHUB_CHANNEL_CTRL + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); @@ -230,6 +244,8 @@ int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE); ahub->soc_data->set_audio_cif(ahub->regmap_apbif, reg, &cif_conf); + pm_runtime_put(ahub->dev); + return 0; } EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_tx_fifo); @@ -239,12 +255,16 @@ int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif) int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; int reg, val; + pm_runtime_get_sync(ahub->dev); + reg = TEGRA30_AHUB_CHANNEL_CTRL + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); val = tegra30_apbif_read(reg); val |= TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; tegra30_apbif_write(reg, val); + pm_runtime_put(ahub->dev); + return 0; } EXPORT_SYMBOL_GPL(tegra30_ahub_enable_tx_fifo); @@ -254,12 +274,16 @@ int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif) int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0; int reg, val; + pm_runtime_get_sync(ahub->dev); + reg = TEGRA30_AHUB_CHANNEL_CTRL + (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE); val = tegra30_apbif_read(reg); val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_EN; tegra30_apbif_write(reg, val); + pm_runtime_put(ahub->dev); + return 0; } EXPORT_SYMBOL_GPL(tegra30_ahub_disable_tx_fifo); @@ -280,10 +304,14 @@ int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif, int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; int reg; + pm_runtime_get_sync(ahub->dev); + reg = TEGRA30_AHUB_AUDIO_RX + (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); tegra30_audio_write(reg, 1 << txcif); + pm_runtime_put(ahub->dev); + return 0; } EXPORT_SYMBOL_GPL(tegra30_ahub_set_rx_cif_source); @@ -293,35 +321,51 @@ int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif) int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0; int reg; + pm_runtime_get_sync(ahub->dev); + reg = TEGRA30_AHUB_AUDIO_RX + (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE); tegra30_audio_write(reg, 0); + pm_runtime_put(ahub->dev); + return 0; } EXPORT_SYMBOL_GPL(tegra30_ahub_unset_rx_cif_source); -#define CLK_LIST_MASK_TEGRA30 BIT(0) -#define CLK_LIST_MASK_TEGRA114 BIT(1) +#define MOD_LIST_MASK_TEGRA30 BIT(0) +#define MOD_LIST_MASK_TEGRA114 BIT(1) +#define MOD_LIST_MASK_TEGRA124 BIT(2) -#define CLK_LIST_MASK_TEGRA30_OR_LATER \ - (CLK_LIST_MASK_TEGRA30 | CLK_LIST_MASK_TEGRA114) +#define MOD_LIST_MASK_TEGRA30_OR_LATER \ + (MOD_LIST_MASK_TEGRA30 | MOD_LIST_MASK_TEGRA114 | \ + MOD_LIST_MASK_TEGRA124) +#define MOD_LIST_MASK_TEGRA114_OR_LATER \ + (MOD_LIST_MASK_TEGRA114 | MOD_LIST_MASK_TEGRA124) static const struct { - const char *clk_name; - u32 clk_list_mask; -} configlink_clocks[] = { - { "i2s0", CLK_LIST_MASK_TEGRA30_OR_LATER }, - { "i2s1", CLK_LIST_MASK_TEGRA30_OR_LATER }, - { "i2s2", CLK_LIST_MASK_TEGRA30_OR_LATER }, - { "i2s3", CLK_LIST_MASK_TEGRA30_OR_LATER }, - { "i2s4", CLK_LIST_MASK_TEGRA30_OR_LATER }, - { "dam0", CLK_LIST_MASK_TEGRA30_OR_LATER }, - { "dam1", CLK_LIST_MASK_TEGRA30_OR_LATER }, - { "dam2", CLK_LIST_MASK_TEGRA30_OR_LATER }, - { "spdif_in", CLK_LIST_MASK_TEGRA30_OR_LATER }, - { "amx", CLK_LIST_MASK_TEGRA114 }, - { "adx", CLK_LIST_MASK_TEGRA114 }, + const char *rst_name; + u32 mod_list_mask; +} configlink_mods[] = { + { "i2s0", MOD_LIST_MASK_TEGRA30_OR_LATER }, + { "i2s1", MOD_LIST_MASK_TEGRA30_OR_LATER }, + { "i2s2", MOD_LIST_MASK_TEGRA30_OR_LATER }, + { "i2s3", MOD_LIST_MASK_TEGRA30_OR_LATER }, + { "i2s4", MOD_LIST_MASK_TEGRA30_OR_LATER }, + { "dam0", MOD_LIST_MASK_TEGRA30_OR_LATER }, + { "dam1", MOD_LIST_MASK_TEGRA30_OR_LATER }, + { "dam2", MOD_LIST_MASK_TEGRA30_OR_LATER }, + { "spdif", MOD_LIST_MASK_TEGRA30_OR_LATER }, + { "amx", MOD_LIST_MASK_TEGRA114_OR_LATER }, + { "adx", MOD_LIST_MASK_TEGRA114_OR_LATER }, + { "amx1", MOD_LIST_MASK_TEGRA124 }, + { "adx1", MOD_LIST_MASK_TEGRA124 }, + { "afc0", MOD_LIST_MASK_TEGRA124 }, + { "afc1", MOD_LIST_MASK_TEGRA124 }, + { "afc2", MOD_LIST_MASK_TEGRA124 }, + { "afc3", MOD_LIST_MASK_TEGRA124 }, + { "afc4", MOD_LIST_MASK_TEGRA124 }, + { "afc5", MOD_LIST_MASK_TEGRA124 }, }; #define LAST_REG(name) \ @@ -450,17 +494,17 @@ static const struct regmap_config tegra30_ahub_ahub_regmap_config = { }; static struct tegra30_ahub_soc_data soc_data_tegra30 = { - .clk_list_mask = CLK_LIST_MASK_TEGRA30, + .mod_list_mask = MOD_LIST_MASK_TEGRA30, .set_audio_cif = tegra30_ahub_set_cif, }; static struct tegra30_ahub_soc_data soc_data_tegra114 = { - .clk_list_mask = CLK_LIST_MASK_TEGRA114, + .mod_list_mask = MOD_LIST_MASK_TEGRA114, .set_audio_cif = tegra30_ahub_set_cif, }; static struct tegra30_ahub_soc_data soc_data_tegra124 = { - .clk_list_mask = CLK_LIST_MASK_TEGRA114, + .mod_list_mask = MOD_LIST_MASK_TEGRA124, .set_audio_cif = tegra124_ahub_set_cif, }; @@ -475,10 +519,9 @@ static int tegra30_ahub_probe(struct platform_device *pdev) { const struct of_device_id *match; const struct tegra30_ahub_soc_data *soc_data; - struct clk *clk; + struct reset_control *rst; int i; struct resource *res0, *res1, *region; - u32 of_dma[2]; void __iomem *regs_apbif, *regs_ahub; int ret = 0; @@ -495,19 +538,24 @@ static int tegra30_ahub_probe(struct platform_device *pdev) * operate correctly, all devices on this bus must be out of reset. * Ensure that here. */ - for (i = 0; i < ARRAY_SIZE(configlink_clocks); i++) { - if (!(configlink_clocks[i].clk_list_mask & - soc_data->clk_list_mask)) + for (i = 0; i < ARRAY_SIZE(configlink_mods); i++) { + if (!(configlink_mods[i].mod_list_mask & + soc_data->mod_list_mask)) continue; - clk = clk_get(&pdev->dev, configlink_clocks[i].clk_name); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "Can't get clock %s\n", - configlink_clocks[i].clk_name); - ret = PTR_ERR(clk); + + rst = reset_control_get(&pdev->dev, + configlink_mods[i].rst_name); + if (IS_ERR(rst)) { + dev_err(&pdev->dev, "Can't get reset %s\n", + configlink_mods[i].rst_name); + ret = PTR_ERR(rst); goto err; } - tegra_periph_reset_deassert(clk); - clk_put(clk); + + ret = reset_control_deassert(rst); + reset_control_put(rst); + if (ret) + goto err; } ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub), @@ -536,16 +584,6 @@ static int tegra30_ahub_probe(struct platform_device *pdev) goto err_clk_put_d_audio; } - if (of_property_read_u32_array(pdev->dev.of_node, - "nvidia,dma-request-selector", - of_dma, 2) < 0) { - dev_err(&pdev->dev, - "Missing property nvidia,dma-request-selector\n"); - ret = -ENODEV; - goto err_clk_put_d_audio; - } - ahub->dma_sel = of_dma[1]; - res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res0) { dev_err(&pdev->dev, "No apbif memory resource\n"); diff --git a/sound/soc/tegra/tegra30_ahub.h b/sound/soc/tegra/tegra30_ahub.h index d67321d90faa..fd7ba75ed814 100644 --- a/sound/soc/tegra/tegra30_ahub.h +++ b/sound/soc/tegra/tegra30_ahub.h @@ -465,15 +465,15 @@ enum tegra30_ahub_rxcif { }; extern int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif, - dma_addr_t *fiforeg, - unsigned int *reqsel); + char *dmachan, int dmachan_len, + dma_addr_t *fiforeg); extern int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif); extern int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif); extern int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif); extern int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif, - dma_addr_t *fiforeg, - unsigned int *reqsel); + char *dmachan, int dmachan_len, + dma_addr_t *fiforeg); extern int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif); extern int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif); extern int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif); @@ -502,7 +502,7 @@ void tegra124_ahub_set_cif(struct regmap *regmap, unsigned int reg, struct tegra30_ahub_cif_conf *conf); struct tegra30_ahub_soc_data { - u32 clk_list_mask; + u32 mod_list_mask; void (*set_audio_cif)(struct regmap *regmap, unsigned int reg, struct tegra30_ahub_cif_conf *conf); @@ -524,7 +524,6 @@ struct tegra30_ahub { struct device *dev; struct clk *clk_d_audio; struct clk *clk_apbif; - int dma_sel; resource_size_t apbif_addr; struct regmap *regmap_apbif; struct regmap *regmap_ahub; diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index 231a785b3921..49ad9366add8 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -73,52 +73,11 @@ static int tegra30_i2s_runtime_resume(struct device *dev) return 0; } -static int tegra30_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); - int ret; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ret = tegra30_ahub_allocate_tx_fifo(&i2s->playback_fifo_cif, - &i2s->playback_dma_data.addr, - &i2s->playback_dma_data.slave_id); - i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - i2s->playback_dma_data.maxburst = 4; - tegra30_ahub_set_rx_cif_source(i2s->playback_i2s_cif, - i2s->playback_fifo_cif); - } else { - ret = tegra30_ahub_allocate_rx_fifo(&i2s->capture_fifo_cif, - &i2s->capture_dma_data.addr, - &i2s->capture_dma_data.slave_id); - i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - i2s->capture_dma_data.maxburst = 4; - tegra30_ahub_set_rx_cif_source(i2s->capture_fifo_cif, - i2s->capture_i2s_cif); - } - - return ret; -} - -static void tegra30_i2s_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif); - tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif); - } else { - tegra30_ahub_unset_rx_cif_source(i2s->capture_fifo_cif); - tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif); - } -} - 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 +86,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; @@ -317,8 +276,6 @@ static int tegra30_i2s_probe(struct snd_soc_dai *dai) } static struct snd_soc_dai_ops tegra30_i2s_dai_ops = { - .startup = tegra30_i2s_startup, - .shutdown = tegra30_i2s_shutdown, .set_fmt = tegra30_i2s_set_fmt, .hw_params = tegra30_i2s_hw_params, .trigger = tegra30_i2s_trigger, @@ -499,15 +456,51 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev) goto err_pm_disable; } + i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + i2s->playback_dma_data.maxburst = 4; + ret = tegra30_ahub_allocate_tx_fifo(&i2s->playback_fifo_cif, + i2s->playback_dma_chan, + sizeof(i2s->playback_dma_chan), + &i2s->playback_dma_data.addr); + if (ret) { + dev_err(&pdev->dev, "Could not alloc TX FIFO: %d\n", ret); + goto err_suspend; + } + ret = tegra30_ahub_set_rx_cif_source(i2s->playback_i2s_cif, + i2s->playback_fifo_cif); + if (ret) { + dev_err(&pdev->dev, "Could not route TX FIFO: %d\n", ret); + goto err_free_tx_fifo; + } + + i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + i2s->capture_dma_data.maxburst = 4; + ret = tegra30_ahub_allocate_rx_fifo(&i2s->capture_fifo_cif, + i2s->capture_dma_chan, + sizeof(i2s->capture_dma_chan), + &i2s->capture_dma_data.addr); + if (ret) { + dev_err(&pdev->dev, "Could not alloc RX FIFO: %d\n", ret); + goto err_unroute_tx_fifo; + } + ret = tegra30_ahub_set_rx_cif_source(i2s->capture_fifo_cif, + i2s->capture_i2s_cif); + if (ret) { + dev_err(&pdev->dev, "Could not route TX FIFO: %d\n", ret); + goto err_free_rx_fifo; + } + ret = snd_soc_register_component(&pdev->dev, &tegra30_i2s_component, &i2s->dai, 1); if (ret) { dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); ret = -ENOMEM; - goto err_suspend; + goto err_unroute_rx_fifo; } - ret = tegra_pcm_platform_register(&pdev->dev); + ret = tegra_pcm_platform_register_with_chan_names(&pdev->dev, + &i2s->dma_config, i2s->playback_dma_chan, + i2s->capture_dma_chan); if (ret) { dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); goto err_unregister_component; @@ -517,6 +510,14 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev) err_unregister_component: snd_soc_unregister_component(&pdev->dev); +err_unroute_rx_fifo: + tegra30_ahub_unset_rx_cif_source(i2s->capture_fifo_cif); +err_free_rx_fifo: + tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif); +err_unroute_tx_fifo: + tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif); +err_free_tx_fifo: + tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif); err_suspend: if (!pm_runtime_status_suspended(&pdev->dev)) tegra30_i2s_runtime_suspend(&pdev->dev); @@ -539,6 +540,12 @@ static int tegra30_i2s_platform_remove(struct platform_device *pdev) tegra_pcm_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); + tegra30_ahub_unset_rx_cif_source(i2s->capture_fifo_cif); + tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif); + + tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif); + tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif); + clk_put(i2s->clk_i2s); return 0; diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h index 4d0b0a30dbfb..774fc6ad2026 100644 --- a/sound/soc/tegra/tegra30_i2s.h +++ b/sound/soc/tegra/tegra30_i2s.h @@ -238,11 +238,14 @@ struct tegra30_i2s { struct clk *clk_i2s; enum tegra30_ahub_txcif capture_i2s_cif; enum tegra30_ahub_rxcif capture_fifo_cif; + char capture_dma_chan[8]; struct snd_dmaengine_dai_dma_data capture_dma_data; enum tegra30_ahub_rxcif playback_i2s_cif; enum tegra30_ahub_txcif playback_fifo_cif; + char playback_dma_chan[8]; struct snd_dmaengine_dai_dma_data playback_dma_data; struct regmap *regmap; + struct snd_dmaengine_pcm_config dma_config; }; #endif 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..93caed500565 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, @@ -61,12 +58,23 @@ static const struct snd_dmaengine_pcm_config tegra_dmaengine_pcm_config = { int tegra_pcm_platform_register(struct device *dev) { - return snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, - SND_DMAENGINE_PCM_FLAG_NO_DT | - SND_DMAENGINE_PCM_FLAG_COMPAT); + return snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0); } EXPORT_SYMBOL_GPL(tegra_pcm_platform_register); +int tegra_pcm_platform_register_with_chan_names(struct device *dev, + struct snd_dmaengine_pcm_config *config, + char *txdmachan, char *rxdmachan) +{ + *config = tegra_dmaengine_pcm_config; + config->dma_dev = dev->parent; + config->chan_names[0] = txdmachan; + config->chan_names[1] = rxdmachan; + + return snd_dmaengine_pcm_register(dev, config, 0); +} +EXPORT_SYMBOL_GPL(tegra_pcm_platform_register_with_chan_names); + void tegra_pcm_platform_unregister(struct device *dev) { return snd_dmaengine_pcm_unregister(dev); diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h index 68ad901714a9..7883dec748a3 100644 --- a/sound/soc/tegra/tegra_pcm.h +++ b/sound/soc/tegra/tegra_pcm.h @@ -31,7 +31,12 @@ #ifndef __TEGRA_PCM_H__ #define __TEGRA_PCM_H__ +struct snd_dmaengine_pcm_config; + int tegra_pcm_platform_register(struct device *dev); +int tegra_pcm_platform_register_with_chan_names(struct device *dev, + struct snd_dmaengine_pcm_config *config, + char *txdmachan, char *rxdmachan); void tegra_pcm_platform_unregister(struct device *dev); #endif 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-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c index e0305a148568..9edd68db9f48 100644 --- a/sound/soc/txx9/txx9aclc-ac97.c +++ b/sound/soc/txx9/txx9aclc-ac97.c @@ -183,14 +183,16 @@ static int txx9aclc_ac97_dev_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; + + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); drvdata->base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(drvdata->base)) return PTR_ERR(drvdata->base); - drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); - if (!drvdata) - return -ENOMEM; platform_set_drvdata(pdev, drvdata); drvdata->physbase = r->start; if (sizeof(drvdata->physbase) > sizeof(r->start) && 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, diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index 178d1bad6259..b3b66aa98dce 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -91,6 +91,8 @@ static int mop500_of_probe(struct platform_device *pdev, for (i = 0; i < 2; i++) { mop500_dai_links[i].cpu_of_node = msp_np[i]; mop500_dai_links[i].cpu_dai_name = NULL; + mop500_dai_links[i].platform_of_node = msp_np[i]; + mop500_dai_links[i].platform_name = NULL; mop500_dai_links[i].codec_of_node = codec_np; mop500_dai_links[i].codec_name = NULL; } diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index c6fb5cce980e..5f4807b2c007 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -17,12 +17,14 @@ #include <linux/bitops.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/of.h> #include <linux/regulator/consumer.h> #include <linux/mfd/dbx500-prcmu.h> #include <linux/platform_data/asoc-ux500-msp.h> #include <sound/soc.h> #include <sound/soc-dai.h> +#include <sound/dmaengine_pcm.h> #include "ux500_msp_i2s.h" #include "ux500_msp_dai.h" @@ -654,16 +656,52 @@ static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream, return ret; } +static int ux500_msp_dai_of_probe(struct snd_soc_dai *dai) +{ + struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + struct snd_dmaengine_dai_dma_data *playback_dma_data; + struct snd_dmaengine_dai_dma_data *capture_dma_data; + + playback_dma_data = devm_kzalloc(dai->dev, + sizeof(*playback_dma_data), + GFP_KERNEL); + if (!playback_dma_data) + return -ENOMEM; + + capture_dma_data = devm_kzalloc(dai->dev, + sizeof(*capture_dma_data), + GFP_KERNEL); + if (!capture_dma_data) + return -ENOMEM; + + playback_dma_data->addr = drvdata->msp->playback_dma_data.tx_rx_addr; + capture_dma_data->addr = drvdata->msp->capture_dma_data.tx_rx_addr; + + playback_dma_data->maxburst = 4; + capture_dma_data->maxburst = 4; + + snd_soc_dai_init_dma_data(dai, playback_dma_data, capture_dma_data); + + return 0; +} + static int ux500_msp_dai_probe(struct snd_soc_dai *dai) { struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); + struct msp_i2s_platform_data *pdata = dai->dev->platform_data; + int ret; - dai->playback_dma_data = &drvdata->msp->playback_dma_data; - dai->capture_dma_data = &drvdata->msp->capture_dma_data; + if (!pdata) { + ret = ux500_msp_dai_of_probe(dai); + return ret; + } drvdata->msp->playback_dma_data.data_size = drvdata->slot_width; drvdata->msp->capture_dma_data.data_size = drvdata->slot_width; + snd_soc_dai_init_dma_data(dai, + &drvdata->msp->playback_dma_data, + &drvdata->msp->capture_dma_data); return 0; } @@ -680,87 +718,19 @@ static struct snd_soc_dai_ops ux500_msp_dai_ops[] = { } }; -static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = { - { - .name = "ux500-msp-i2s.0", - .probe = ux500_msp_dai_probe, - .id = 0, - .suspend = NULL, - .resume = NULL, - .playback = { - .channels_min = UX500_MSP_MIN_CHANNELS, - .channels_max = UX500_MSP_MAX_CHANNELS, - .rates = UX500_I2S_RATES, - .formats = UX500_I2S_FORMATS, - }, - .capture = { - .channels_min = UX500_MSP_MIN_CHANNELS, - .channels_max = UX500_MSP_MAX_CHANNELS, - .rates = UX500_I2S_RATES, - .formats = UX500_I2S_FORMATS, - }, - .ops = ux500_msp_dai_ops, - }, - { - .name = "ux500-msp-i2s.1", - .probe = ux500_msp_dai_probe, - .id = 1, - .suspend = NULL, - .resume = NULL, - .playback = { - .channels_min = UX500_MSP_MIN_CHANNELS, - .channels_max = UX500_MSP_MAX_CHANNELS, - .rates = UX500_I2S_RATES, - .formats = UX500_I2S_FORMATS, - }, - .capture = { - .channels_min = UX500_MSP_MIN_CHANNELS, - .channels_max = UX500_MSP_MAX_CHANNELS, - .rates = UX500_I2S_RATES, - .formats = UX500_I2S_FORMATS, - }, - .ops = ux500_msp_dai_ops, - }, - { - .name = "ux500-msp-i2s.2", - .id = 2, - .probe = ux500_msp_dai_probe, - .suspend = NULL, - .resume = NULL, - .playback = { - .channels_min = UX500_MSP_MIN_CHANNELS, - .channels_max = UX500_MSP_MAX_CHANNELS, - .rates = UX500_I2S_RATES, - .formats = UX500_I2S_FORMATS, - }, - .capture = { - .channels_min = UX500_MSP_MIN_CHANNELS, - .channels_max = UX500_MSP_MAX_CHANNELS, - .rates = UX500_I2S_RATES, - .formats = UX500_I2S_FORMATS, - }, - .ops = ux500_msp_dai_ops, - }, - { - .name = "ux500-msp-i2s.3", - .probe = ux500_msp_dai_probe, - .id = 3, - .suspend = NULL, - .resume = NULL, - .playback = { - .channels_min = UX500_MSP_MIN_CHANNELS, - .channels_max = UX500_MSP_MAX_CHANNELS, - .rates = UX500_I2S_RATES, - .formats = UX500_I2S_FORMATS, - }, - .capture = { - .channels_min = UX500_MSP_MIN_CHANNELS, - .channels_max = UX500_MSP_MAX_CHANNELS, - .rates = UX500_I2S_RATES, - .formats = UX500_I2S_FORMATS, - }, - .ops = ux500_msp_dai_ops, - }, +static struct snd_soc_dai_driver ux500_msp_dai_drv = { + .probe = ux500_msp_dai_probe, + .suspend = NULL, + .resume = NULL, + .playback.channels_min = UX500_MSP_MIN_CHANNELS, + .playback.channels_max = UX500_MSP_MAX_CHANNELS, + .playback.rates = UX500_I2S_RATES, + .playback.formats = UX500_I2S_FORMATS, + .capture.channels_min = UX500_MSP_MIN_CHANNELS, + .capture.channels_max = UX500_MSP_MAX_CHANNELS, + .capture.rates = UX500_I2S_RATES, + .capture.formats = UX500_I2S_FORMATS, + .ops = ux500_msp_dai_ops, }; static const struct snd_soc_component_driver ux500_msp_component = { @@ -771,10 +741,14 @@ static const struct snd_soc_component_driver ux500_msp_component = { static int ux500_msp_drv_probe(struct platform_device *pdev) { struct ux500_msp_i2s_drvdata *drvdata; + struct msp_i2s_platform_data *pdata = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; int ret = 0; - dev_dbg(&pdev->dev, "%s: Enter (pdev->name = %s).\n", __func__, - pdev->name); + if (!pdata && !np) { + dev_err(&pdev->dev, "No platform data or Device Tree found\n"); + return -ENODEV; + } drvdata = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp_i2s_drvdata), @@ -826,7 +800,7 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, drvdata); ret = snd_soc_register_component(&pdev->dev, &ux500_msp_component, - &ux500_msp_dai_drv[drvdata->msp->id], 1); + &ux500_msp_dai_drv, 1); if (ret < 0) { dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n", __func__, drvdata->msp->id); diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c index 1ca8b08ae993..959d7b4edf56 100644 --- a/sound/soc/ux500/ux500_msp_i2s.c +++ b/sound/soc/ux500/ux500_msp_i2s.c @@ -646,6 +646,34 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir) } +static int ux500_msp_i2s_of_init_msp(struct platform_device *pdev, + struct ux500_msp *msp, + struct msp_i2s_platform_data **platform_data) +{ + struct msp_i2s_platform_data *pdata; + + *platform_data = devm_kzalloc(&pdev->dev, + sizeof(struct msp_i2s_platform_data), + GFP_KERNEL); + pdata = *platform_data; + if (!pdata) + return -ENOMEM; + + msp->playback_dma_data.dma_cfg = devm_kzalloc(&pdev->dev, + sizeof(struct stedma40_chan_cfg), + GFP_KERNEL); + if (!msp->playback_dma_data.dma_cfg) + return -ENOMEM; + + msp->capture_dma_data.dma_cfg = devm_kzalloc(&pdev->dev, + sizeof(struct stedma40_chan_cfg), + GFP_KERNEL); + if (!msp->capture_dma_data.dma_cfg) + return -ENOMEM; + + return 0; +} + int ux500_msp_i2s_init_msp(struct platform_device *pdev, struct ux500_msp **msp_p, struct msp_i2s_platform_data *platform_data) @@ -653,30 +681,28 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev, struct resource *res = NULL; struct device_node *np = pdev->dev.of_node; struct ux500_msp *msp; + int ret; *msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL); msp = *msp_p; if (!msp) return -ENOMEM; - if (np) { - if (!platform_data) { - platform_data = devm_kzalloc(&pdev->dev, - sizeof(struct msp_i2s_platform_data), GFP_KERNEL); - if (!platform_data) - return -ENOMEM; - } - } else - if (!platform_data) + if (!platform_data) { + if (np) { + ret = ux500_msp_i2s_of_init_msp(pdev, msp, + &platform_data); + if (ret) + return ret; + } else return -EINVAL; + } else { + msp->playback_dma_data.dma_cfg = platform_data->msp_i2s_dma_tx; + msp->capture_dma_data.dma_cfg = platform_data->msp_i2s_dma_rx; + msp->id = platform_data->id; + } - dev_dbg(&pdev->dev, "%s: Enter (name: %s, id: %d).\n", __func__, - pdev->name, platform_data->id); - - msp->id = platform_data->id; msp->dev = &pdev->dev; - msp->playback_dma_data.dma_cfg = platform_data->msp_i2s_dma_tx; - msp->capture_dma_data.dma_cfg = platform_data->msp_i2s_dma_rx; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h index 258d0bcee0bd..875de0f68b85 100644 --- a/sound/soc/ux500/ux500_msp_i2s.h +++ b/sound/soc/ux500/ux500_msp_i2s.h @@ -475,7 +475,7 @@ struct ux500_msp_dma_params { }; struct ux500_msp { - enum msp_i2s_id id; + int id; void __iomem *registers; struct device *dev; struct ux500_msp_dma_params playback_dma_data; diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index ce554de5d9dc..51a66a87305a 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -28,12 +28,6 @@ #include "ux500_msp_i2s.h" #include "ux500_pcm.h" -#define UX500_PLATFORM_MIN_RATE 8000 -#define UX500_PLATFORM_MAX_RATE 48000 - -#define UX500_PLATFORM_MIN_CHANNELS 1 -#define UX500_PLATFORM_MAX_CHANNELS 8 - #define UX500_PLATFORM_PERIODS_BYTES_MIN 128 #define UX500_PLATFORM_PERIODS_BYTES_MAX (64 * PAGE_SIZE) #define UX500_PLATFORM_PERIODS_MIN 2 @@ -45,15 +39,6 @@ static const struct snd_pcm_hardware ux500_pcm_hw = { SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_PAUSE, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_U16_LE | - SNDRV_PCM_FMTBIT_S16_BE | - SNDRV_PCM_FMTBIT_U16_BE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = UX500_PLATFORM_MIN_RATE, - .rate_max = UX500_PLATFORM_MAX_RATE, - .channels_min = UX500_PLATFORM_MIN_CHANNELS, - .channels_max = UX500_PLATFORM_MAX_CHANNELS, .buffer_bytes_max = UX500_PLATFORM_BUFFER_BYTES_MAX, .period_bytes_min = UX500_PLATFORM_PERIODS_BYTES_MIN, .period_bytes_max = UX500_PLATFORM_PERIODS_BYTES_MAX, @@ -65,14 +50,10 @@ static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_substream *substream) { struct snd_soc_dai *dai = rtd->cpu_dai; - struct device *dev = dai->dev; u16 per_data_width, mem_data_width; struct stedma40_chan_cfg *dma_cfg; struct ux500_msp_dma_params *dma_params; - dev_dbg(dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, - snd_pcm_stream_str(substream)); - dma_params = snd_soc_dai_get_dma_data(dai, substream); dma_cfg = dma_params->dma_cfg; @@ -108,26 +89,36 @@ static int ux500_pcm_prepare_slave_config(struct snd_pcm_substream *substream, struct dma_slave_config *slave_config) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct ux500_msp_dma_params *dma_params; - struct stedma40_chan_cfg *dma_cfg; + struct msp_i2s_platform_data *pdata = rtd->cpu_dai->dev->platform_data; + struct snd_dmaengine_dai_dma_data *snd_dma_params; + struct ux500_msp_dma_params *ste_dma_params; + dma_addr_t dma_addr; int ret; - dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - dma_cfg = dma_params->dma_cfg; + if (pdata) { + ste_dma_params = + snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + dma_addr = ste_dma_params->tx_rx_addr; + } else { + snd_dma_params = + snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + dma_addr = snd_dma_params->addr; + } ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); if (ret) return ret; slave_config->dst_maxburst = 4; - slave_config->dst_addr_width = dma_cfg->dst_info.data_width; slave_config->src_maxburst = 4; - slave_config->src_addr_width = dma_cfg->src_info.data_width; + + slave_config->src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - slave_config->dst_addr = dma_params->tx_rx_addr; + slave_config->dst_addr = dma_addr; else - slave_config->src_addr = dma_params->tx_rx_addr; + slave_config->src_addr = dma_addr; return 0; } @@ -139,15 +130,25 @@ static const struct snd_dmaengine_pcm_config ux500_dmaengine_pcm_config = { .prepare_slave_config = ux500_pcm_prepare_slave_config, }; +static const struct snd_dmaengine_pcm_config ux500_dmaengine_of_pcm_config = { + .compat_request_channel = ux500_pcm_request_chan, + .prepare_slave_config = ux500_pcm_prepare_slave_config, +}; + int ux500_pcm_register_platform(struct platform_device *pdev) { + const struct snd_dmaengine_pcm_config *pcm_config; + struct device_node *np = pdev->dev.of_node; int ret; - ret = snd_dmaengine_pcm_register(&pdev->dev, - &ux500_dmaengine_pcm_config, - SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | - SND_DMAENGINE_PCM_FLAG_COMPAT | - SND_DMAENGINE_PCM_FLAG_NO_DT); + if (np) + pcm_config = &ux500_dmaengine_of_pcm_config; + else + pcm_config = &ux500_dmaengine_pcm_config; + + ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | + SND_DMAENGINE_PCM_FLAG_COMPAT); if (ret < 0) { dev_err(&pdev->dev, "%s: ERROR: Failed to register platform '%s' (%d)!\n", |