diff options
Diffstat (limited to 'sound/soc')
186 files changed, 8451 insertions, 3928 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 5138b8493051..d62ce483a443 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -31,8 +31,10 @@ config SND_SOC_GENERIC_DMAENGINE_PCM select SND_DMAENGINE_PCM # All the supported SoCs +source "sound/soc/adi/Kconfig" source "sound/soc/atmel/Kconfig" source "sound/soc/au1x/Kconfig" +source "sound/soc/bcm/Kconfig" source "sound/soc/blackfin/Kconfig" source "sound/soc/cirrus/Kconfig" source "sound/soc/davinci/Kconfig" @@ -42,7 +44,7 @@ source "sound/soc/jz4740/Kconfig" source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" source "sound/soc/kirkwood/Kconfig" -source "sound/soc/mid-x86/Kconfig" +source "sound/soc/intel/Kconfig" source "sound/soc/mxs/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/samsung/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 8b9e70105dd2..62a1822e77bf 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -8,15 +8,17 @@ endif obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ obj-$(CONFIG_SND_SOC) += generic/ +obj-$(CONFIG_SND_SOC) += adi/ obj-$(CONFIG_SND_SOC) += atmel/ obj-$(CONFIG_SND_SOC) += au1x/ +obj-$(CONFIG_SND_SOC) += bcm/ obj-$(CONFIG_SND_SOC) += blackfin/ obj-$(CONFIG_SND_SOC) += cirrus/ obj-$(CONFIG_SND_SOC) += davinci/ obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += jz4740/ -obj-$(CONFIG_SND_SOC) += mid-x86/ +obj-$(CONFIG_SND_SOC) += intel/ obj-$(CONFIG_SND_SOC) += mxs/ obj-$(CONFIG_SND_SOC) += nuc900/ obj-$(CONFIG_SND_SOC) += omap/ diff --git a/sound/soc/adi/Kconfig b/sound/soc/adi/Kconfig new file mode 100644 index 000000000000..dd763f55edac --- /dev/null +++ b/sound/soc/adi/Kconfig @@ -0,0 +1,21 @@ +config SND_SOC_ADI + tristate "Audio support for Analog Devices reference designs" + depends on MICROBLAZE || ARCH_ZYNQ || COMPILE_TEST + help + Audio support for various reference designs by Analog Devices. + +config SND_SOC_ADI_AXI_I2S + tristate "AXI-I2S support" + depends on SND_SOC_ADI + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + help + ASoC driver for the Analog Devices AXI-I2S softcore peripheral. + +config SND_SOC_ADI_AXI_SPDIF + tristate "AXI-SPDIF support" + depends on SND_SOC_ADI + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + help + ASoC driver for the Analog Devices AXI-SPDIF softcore peripheral. diff --git a/sound/soc/adi/Makefile b/sound/soc/adi/Makefile new file mode 100644 index 000000000000..64456c1e5347 --- /dev/null +++ b/sound/soc/adi/Makefile @@ -0,0 +1,5 @@ +snd-soc-adi-axi-i2s-objs := axi-i2s.o +snd-soc-adi-axi-spdif-objs := axi-spdif.o + +obj-$(CONFIG_SND_SOC_ADI_AXI_I2S) += snd-soc-adi-axi-i2s.o +obj-$(CONFIG_SND_SOC_ADI_AXI_SPDIF) += snd-soc-adi-axi-spdif.o diff --git a/sound/soc/adi/axi-i2s.c b/sound/soc/adi/axi-i2s.c new file mode 100644 index 000000000000..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/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..1ead3c977a51 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -648,7 +648,7 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream, dma_params = ssc_p->dma_params[dir]; - ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable); + ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable); ssc_writel(ssc_p->ssc->regs, IDR, dma_params->mask->ssc_error); pr_debug("%s enabled SSC_SR=0x%08x\n", @@ -657,6 +657,33 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream, return 0; } +static int atmel_ssc_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; + struct atmel_pcm_dma_params *dma_params; + int dir; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = 0; + else + dir = 1; + + dma_params = ssc_p->dma_params[dir]; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable); + break; + default: + ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable); + break; + } + + return 0; +} #ifdef CONFIG_PM static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai) @@ -731,6 +758,7 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { .startup = atmel_ssc_startup, .shutdown = atmel_ssc_shutdown, .prepare = atmel_ssc_prepare, + .trigger = atmel_ssc_trigger, .hw_params = atmel_ssc_hw_params, .set_fmt = atmel_ssc_set_dai_fmt, .set_clkdiv = atmel_ssc_set_dai_clkdiv, diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c index 992ae38d5a15..3188036a18f0 100644 --- a/sound/soc/atmel/sam9x5_wm8731.c +++ b/sound/soc/atmel/sam9x5_wm8731.c @@ -97,6 +97,8 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev) goto out; } + snd_soc_card_set_drvdata(card, priv); + card->dev = &pdev->dev; card->owner = THIS_MODULE; card->dai_link = dai; @@ -107,7 +109,7 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev) dai->stream_name = "WM8731 PCM"; dai->codec_dai_name = "wm8731-hifi"; dai->init = sam9x5_wm8731_init; - dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + dai->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM; ret = snd_soc_of_parse_card_name(card, "atmel,model"); @@ -153,8 +155,6 @@ static int sam9x5_wm8731_driver_probe(struct platform_device *pdev) of_node_put(codec_np); of_node_put(cpu_np); - platform_set_drvdata(pdev, card); - ret = snd_soc_register_card(card); if (ret) { dev_err(&pdev->dev, diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index 3b4eafaf30d3..17a24d804734 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -65,19 +65,10 @@ struct au1xpsc_audio_dmadata { #define AU1XPSC_PERIOD_MIN_BYTES 1024 #define AU1XPSC_BUFFER_MIN_BYTES 65536 -#define AU1XPSC_PCM_FMTS \ - (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \ - SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ - SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE | \ - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | \ - SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE | \ - 0) - /* PCM hardware DMA capabilities - platform specific */ static const struct snd_pcm_hardware au1xpsc_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH, - .formats = AU1XPSC_PCM_FMTS, .period_bytes_min = AU1XPSC_PERIOD_MIN_BYTES, .period_bytes_max = 4096 * 1024 - 1, .periods_min = 2, diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c index befd1074f9bd..e920b60bf6c2 100644 --- a/sound/soc/au1x/dma.c +++ b/sound/soc/au1x/dma.c @@ -21,14 +21,6 @@ #include "psc.h" -#define ALCHEMY_PCM_FMTS \ - (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \ - SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ - SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE | \ - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | \ - SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE | \ - 0) - struct pcm_period { u32 start; u32 relative_end; /* relative to start of buffer */ @@ -171,12 +163,6 @@ static irqreturn_t au1000_dma_interrupt(int irq, void *ptr) static const struct snd_pcm_hardware alchemy_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH, - .formats = ALCHEMY_PCM_FMTS, - .rates = SNDRV_PCM_RATE_8000_192000, - .rate_min = SNDRV_PCM_RATE_8000, - .rate_max = SNDRV_PCM_RATE_192000, - .channels_min = 2, - .channels_max = 2, .period_bytes_min = 1024, .period_bytes_max = 16 * 1024 - 1, .periods_min = 4, diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig new file mode 100644 index 000000000000..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/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c index 1d4c676eb6cc..cdb8ee75ded9 100644 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.c +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c @@ -107,7 +107,6 @@ static const struct snd_pcm_hardware bf5xx_pcm_hardware = { #endif SNDRV_PCM_INFO_BLOCK_TRANSFER, - .formats = SNDRV_PCM_FMTBIT_S16_LE, .period_bytes_min = 32, .period_bytes_max = 0x10000, .periods_min = 1, diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index 2a5b43417fd5..a3881c4381c9 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -52,9 +52,6 @@ static const struct snd_pcm_hardware bf5xx_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, .period_bytes_min = 32, .period_bytes_max = 0x10000, .periods_min = 1, diff --git a/sound/soc/cirrus/edb93xx.c b/sound/soc/cirrus/edb93xx.c index c43fb214558a..4f900efc437c 100644 --- a/sound/soc/cirrus/edb93xx.c +++ b/sound/soc/cirrus/edb93xx.c @@ -63,7 +63,7 @@ static struct snd_soc_ops edb93xx_ops = { static struct snd_soc_dai_link edb93xx_dai = { .name = "CS4271", .stream_name = "CS4271 HiFi", - .platform_name = "ep93xx-pcm-audio", + .platform_name = "ep93xx-i2s", .cpu_dai_name = "ep93xx-i2s", .codec_name = "spi0.0", .codec_dai_name = "cs4271-hifi", diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c index efa75b5086a4..f30dadf85b99 100644 --- a/sound/soc/cirrus/ep93xx-ac97.c +++ b/sound/soc/cirrus/ep93xx-ac97.c @@ -19,11 +19,14 @@ #include <linux/slab.h> #include <sound/core.h> +#include <sound/dmaengine_pcm.h> #include <sound/ac97_codec.h> #include <sound/soc.h> #include <linux/platform_data/dma-ep93xx.h> +#include "ep93xx-pcm.h" + /* * Per channel (1-4) registers. */ @@ -95,6 +98,8 @@ struct ep93xx_ac97_info { struct device *dev; void __iomem *regs; struct completion done; + struct snd_dmaengine_dai_dma_data dma_params_rx; + struct snd_dmaengine_dai_dma_data dma_params_tx; }; /* currently ALSA only supports a single AC97 device */ @@ -315,8 +320,13 @@ static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream, static int ep93xx_ac97_dai_probe(struct snd_soc_dai *dai) { - dai->playback_dma_data = &ep93xx_ac97_pcm_out; - dai->capture_dma_data = &ep93xx_ac97_pcm_in; + struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai); + + info->dma_params_tx.filter_data = &ep93xx_ac97_pcm_out; + info->dma_params_rx.filter_data = &ep93xx_ac97_pcm_in; + + dai->playback_dma_data = &info->dma_params_tx; + dai->capture_dma_data = &info->dma_params_rx; return 0; } @@ -394,8 +404,14 @@ static int ep93xx_ac97_probe(struct platform_device *pdev) if (ret) goto fail; + ret = devm_ep93xx_pcm_platform_register(&pdev->dev); + if (ret) + goto fail_unregister; + return 0; +fail_unregister: + snd_soc_unregister_component(&pdev->dev); fail: ep93xx_ac97_info = NULL; snd_soc_set_ac97_ops(NULL); diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index a57643d6402f..943145f9d1b6 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -21,6 +21,7 @@ #include <linux/io.h> #include <sound/core.h> +#include <sound/dmaengine_pcm.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/initval.h> @@ -30,6 +31,8 @@ #include <mach/ep93xx-regs.h> #include <linux/platform_data/dma-ep93xx.h> +#include "ep93xx-pcm.h" + #define EP93XX_I2S_TXCLKCFG 0x00 #define EP93XX_I2S_RXCLKCFG 0x04 #define EP93XX_I2S_GLCTRL 0x0C @@ -61,6 +64,8 @@ struct ep93xx_i2s_info { struct clk *sclk; struct clk *lrclk; void __iomem *regs; + struct snd_dmaengine_dai_dma_data dma_params_rx; + struct snd_dmaengine_dai_dma_data dma_params_tx; }; static struct ep93xx_dma_data ep93xx_i2s_dma_data[] = { @@ -140,8 +145,15 @@ static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream) static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai) { - dai->playback_dma_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dai->capture_dma_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE]; + struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); + + info->dma_params_tx.filter_data = + &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + info->dma_params_rx.filter_data = + &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE]; + + dai->playback_dma_data = &info->dma_params_tx; + dai->capture_dma_data = &info->dma_params_rx; return 0; } @@ -405,8 +417,14 @@ static int ep93xx_i2s_probe(struct platform_device *pdev) if (err) goto fail_put_lrclk; + err = devm_ep93xx_pcm_platform_register(&pdev->dev); + if (err) + goto fail_unregister; + return 0; +fail_unregister: + snd_soc_unregister_component(&pdev->dev); fail_put_lrclk: clk_put(info->lrclk); fail_put_sclk: diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c index cfe517e68009..5f664471d99e 100644 --- a/sound/soc/cirrus/ep93xx-pcm.c +++ b/sound/soc/cirrus/ep93xx-pcm.c @@ -23,20 +23,13 @@ #include <linux/platform_data/dma-ep93xx.h> +#include "ep93xx-pcm.h" + static const struct snd_pcm_hardware ep93xx_pcm_hardware = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER), - - .rates = SNDRV_PCM_RATE_8000_192000, - .rate_min = SNDRV_PCM_RATE_8000, - .rate_max = SNDRV_PCM_RATE_192000, - - .formats = (SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE), - .buffer_bytes_max = 131072, .period_bytes_min = 32, .period_bytes_max = 32768, @@ -57,53 +50,22 @@ static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param) return false; } -static struct dma_chan *ep93xx_compat_request_channel( - struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_substream *substream) -{ - struct snd_dmaengine_dai_dma_data *dma_data; - - dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - return snd_dmaengine_pcm_request_channel(ep93xx_pcm_dma_filter, - dma_data); -} - static const struct snd_dmaengine_pcm_config ep93xx_dmaengine_pcm_config = { .pcm_hardware = &ep93xx_pcm_hardware, .compat_filter_fn = ep93xx_pcm_dma_filter, - .compat_request_channel = ep93xx_compat_request_channel, .prealloc_buffer_size = 131072, }; -static int ep93xx_soc_platform_probe(struct platform_device *pdev) +int devm_ep93xx_pcm_platform_register(struct device *dev) { - return snd_dmaengine_pcm_register(&pdev->dev, + return devm_snd_dmaengine_pcm_register(dev, &ep93xx_dmaengine_pcm_config, SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | SND_DMAENGINE_PCM_FLAG_NO_DT | SND_DMAENGINE_PCM_FLAG_COMPAT); } - -static int ep93xx_soc_platform_remove(struct platform_device *pdev) -{ - snd_dmaengine_pcm_unregister(&pdev->dev); - return 0; -} - -static struct platform_driver ep93xx_pcm_driver = { - .driver = { - .name = "ep93xx-pcm-audio", - .owner = THIS_MODULE, - }, - - .probe = ep93xx_soc_platform_probe, - .remove = ep93xx_soc_platform_remove, -}; - -module_platform_driver(ep93xx_pcm_driver); +EXPORT_SYMBOL_GPL(devm_ep93xx_pcm_platform_register); MODULE_AUTHOR("Ryan Mallon"); MODULE_DESCRIPTION("EP93xx ALSA PCM interface"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:ep93xx-pcm-audio"); diff --git a/sound/soc/cirrus/ep93xx-pcm.h b/sound/soc/cirrus/ep93xx-pcm.h new file mode 100644 index 000000000000..b7a12a2fae9c --- /dev/null +++ b/sound/soc/cirrus/ep93xx-pcm.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __EP93XX_PCM_H__ +#define __EP93XX_PCM_H__ + +int devm_ep93xx_pcm_platform_register(struct device *dev); + +#endif diff --git a/sound/soc/cirrus/simone.c b/sound/soc/cirrus/simone.c index 4d094d00c34a..822a19a89e74 100644 --- a/sound/soc/cirrus/simone.c +++ b/sound/soc/cirrus/simone.c @@ -27,7 +27,7 @@ static struct snd_soc_dai_link simone_dai = { .cpu_dai_name = "ep93xx-ac97", .codec_dai_name = "ac97-hifi", .codec_name = "ac97-codec", - .platform_name = "ep93xx-pcm-audio", + .platform_name = "ep93xx-ac97", }; static struct snd_soc_card snd_soc_simone = { diff --git a/sound/soc/cirrus/snappercl15.c b/sound/soc/cirrus/snappercl15.c index 69041074f2c1..29238a7476dd 100644 --- a/sound/soc/cirrus/snappercl15.c +++ b/sound/soc/cirrus/snappercl15.c @@ -83,7 +83,7 @@ static struct snd_soc_dai_link snappercl15_dai = { .cpu_dai_name = "ep93xx-i2s", .codec_dai_name = "tlv320aic23-hifi", .codec_name = "tlv320aic23-codec.0-001a", - .platform_name = "ep93xx-pcm-audio", + .platform_name = "ep93xx-i2s", .init = snappercl15_tlv320aic23_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_CBS_CFS, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b33b45dfceec..983d087aa92a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -163,8 +163,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 diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 9a92b7962f41..77f459868579 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -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.c b/sound/soc/codecs/ad193x.c index aea7e52cf714..5a42dca535b7 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -249,15 +249,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; } @@ -413,7 +413,7 @@ static struct spi_driver ad193x_spi_driver = { }; #endif -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static const struct regmap_config ad193x_i2c_regmap_config = { .val_bits = 8, @@ -470,7 +470,7 @@ static int __init ad193x_modinit(void) { int ret; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&ad193x_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register AD193X I2C driver: %d\n", @@ -495,7 +495,7 @@ static void __exit ad193x_modexit(void) spi_unregister_driver(&ad193x_spi_driver); #endif -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&ad193x_i2c_driver); #endif } diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 59654b1e7f3f..eb836ed5271f 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -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/adav80x.c b/sound/soc/codecs/adav80x.c index 14a7c169d004..f78b27a7c461 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -453,22 +453,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 +482,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 +490,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 +524,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; @@ -939,7 +937,7 @@ static struct spi_driver adav80x_spi_driver = { }; #endif -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static const struct regmap_config adav80x_i2c_regmap_config = { .val_bits = 8, .pad_bits = 1, @@ -985,7 +983,7 @@ static int __init adav80x_init(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&adav80x_i2c_driver); if (ret) return ret; @@ -1001,7 +999,7 @@ module_init(adav80x_init); static void __exit adav80x_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&adav80x_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 49cc5f6d6dba..94cbe508dd37 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -17,6 +17,7 @@ #include <linux/gpio.h> #include <linux/pm.h> #include <linux/i2c.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -30,6 +31,7 @@ /* codec private data */ struct ak4641_priv { + struct regmap *regmap; unsigned int sysclk; int deemph; int playback_fs; @@ -38,12 +40,12 @@ struct ak4641_priv { /* * ak4641 register cache */ -static const u8 ak4641_reg[AK4641_CACHEREGNUM] = { - 0x00, 0x80, 0x00, 0x80, - 0x02, 0x00, 0x11, 0x05, - 0x00, 0x00, 0x36, 0x10, - 0x00, 0x00, 0x57, 0x00, - 0x88, 0x88, 0x08, 0x08 +static const struct reg_default ak4641_reg_defaults[] = { + { 0, 0x00 }, { 1, 0x80 }, { 2, 0x00 }, { 3, 0x80 }, + { 4, 0x02 }, { 5, 0x00 }, { 6, 0x11 }, { 7, 0x05 }, + { 8, 0x00 }, { 9, 0x00 }, { 10, 0x36 }, { 11, 0x10 }, + { 12, 0x00 }, { 13, 0x00 }, { 14, 0x57 }, { 15, 0x00 }, + { 16, 0x88 }, { 17, 0x88 }, { 18, 0x08 }, { 19, 0x08 } }; static const int deemph_settings[] = {44100, 0, 48000, 32000}; @@ -396,6 +398,7 @@ static int ak4641_mute(struct snd_soc_dai *dai, int mute) static int ak4641_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec); struct ak4641_platform_data *pdata = codec->dev->platform_data; int ret; @@ -417,7 +420,7 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec, gpio_set_value(pdata->gpio_npdn, 1); mdelay(1); - ret = snd_soc_cache_sync(codec); + ret = regcache_sync(ak4641->regmap); if (ret) { dev_err(codec->dev, "Failed to sync cache: %d\n", ret); @@ -433,7 +436,7 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec, gpio_set_value(pdata->gpio_npdn, 0); if (pdata && gpio_is_valid(pdata->gpio_power)) gpio_set_value(pdata->gpio_power, 0); - codec->cache_sync = 1; + regcache_mark_dirty(ak4641->regmap); break; } codec->dapm.bias_level = level; @@ -518,7 +521,7 @@ static int ak4641_probe(struct snd_soc_codec *codec) { int ret; - ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); if (ret != 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; @@ -550,12 +553,17 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4641 = { .dapm_routes = ak4641_audio_map, .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map), .set_bias_level = ak4641_set_bias_level, - .reg_cache_size = ARRAY_SIZE(ak4641_reg), - .reg_word_size = sizeof(u8), - .reg_cache_default = ak4641_reg, - .reg_cache_step = 1, }; +static const struct regmap_config ak4641_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = AK4641_BTIF, + .reg_defaults = ak4641_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4641_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; static int ak4641_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) @@ -569,6 +577,10 @@ static int ak4641_i2c_probe(struct i2c_client *i2c, if (!ak4641) return -ENOMEM; + ak4641->regmap = devm_regmap_init_i2c(i2c, &ak4641_regmap); + if (IS_ERR(ak4641->regmap)) + return PTR_ERR(ak4641->regmap); + if (pdata) { if (gpio_is_valid(pdata->gpio_power)) { ret = gpio_request_one(pdata->gpio_power, diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 090d499bb7eb..1f646c6e90c6 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -28,6 +28,7 @@ #include <linux/slab.h> #include <linux/of_device.h> #include <linux/module.h> +#include <linux/regmap.h> #include <sound/soc.h> #include <sound/initval.h> #include <sound/tlv.h> @@ -198,30 +199,30 @@ static const struct snd_soc_dapm_route ak4642_intercon[] = { /* * ak4642 register cache */ -static const u8 ak4642_reg[] = { - 0x00, 0x00, 0x01, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0xe1, 0xe1, 0x18, 0x00, - 0xe1, 0x18, 0x11, 0x08, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, +static const struct reg_default ak4642_reg[] = { + { 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 }, + { 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 }, + { 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 }, + { 12, 0xe1 }, { 13, 0x18 }, { 14, 0x11 }, { 15, 0x08 }, + { 16, 0x00 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x00 }, + { 20, 0x00 }, { 21, 0x00 }, { 22, 0x00 }, { 23, 0x00 }, + { 24, 0x00 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0x00 }, + { 28, 0x00 }, { 29, 0x00 }, { 30, 0x00 }, { 31, 0x00 }, + { 32, 0x00 }, { 33, 0x00 }, { 34, 0x00 }, { 35, 0x00 }, + { 36, 0x00 }, }; -static const u8 ak4648_reg[] = { - 0x00, 0x00, 0x01, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0xe1, 0xe1, 0x18, 0x00, - 0xe1, 0x18, 0x11, 0xb8, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x88, 0x88, 0x08, +static const struct reg_default ak4648_reg[] = { + { 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 }, + { 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 }, + { 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 }, + { 12, 0xe1 }, { 13, 0x18 }, { 14, 0x11 }, { 15, 0xb8 }, + { 16, 0x00 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x00 }, + { 20, 0x00 }, { 21, 0x00 }, { 22, 0x00 }, { 23, 0x00 }, + { 24, 0x00 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0x00 }, + { 28, 0x00 }, { 29, 0x00 }, { 30, 0x00 }, { 31, 0x00 }, + { 32, 0x00 }, { 33, 0x00 }, { 34, 0x00 }, { 35, 0x00 }, + { 36, 0x00 }, { 37, 0x88 }, { 38, 0x88 }, { 39, 0x08 }, }; static int ak4642_dai_startup(struct snd_pcm_substream *substream, @@ -454,7 +455,10 @@ static struct snd_soc_dai_driver ak4642_dai = { static int ak4642_resume(struct snd_soc_codec *codec) { - snd_soc_cache_sync(codec); + struct regmap *regmap = dev_get_regmap(codec->dev, NULL); + + regcache_mark_dirty(regmap); + regcache_sync(regmap); return 0; } @@ -463,15 +467,12 @@ static int ak4642_probe(struct snd_soc_codec *codec) { int ret; - ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; } - snd_soc_add_codec_controls(codec, ak4642_snd_controls, - ARRAY_SIZE(ak4642_snd_controls)); - ak4642_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; @@ -488,55 +489,59 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4642 = { .remove = ak4642_remove, .resume = ak4642_resume, .set_bias_level = ak4642_set_bias_level, - .reg_cache_default = ak4642_reg, /* ak4642 reg */ - .reg_cache_size = ARRAY_SIZE(ak4642_reg), /* ak4642 reg */ - .reg_word_size = sizeof(u8), + .controls = ak4642_snd_controls, + .num_controls = ARRAY_SIZE(ak4642_snd_controls), .dapm_widgets = ak4642_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(ak4642_dapm_widgets), .dapm_routes = ak4642_intercon, .num_dapm_routes = ARRAY_SIZE(ak4642_intercon), }; -static struct snd_soc_codec_driver soc_codec_dev_ak4648 = { - .probe = ak4642_probe, - .remove = ak4642_remove, - .resume = ak4642_resume, - .set_bias_level = ak4642_set_bias_level, - .reg_cache_default = ak4648_reg, /* ak4648 reg */ - .reg_cache_size = ARRAY_SIZE(ak4648_reg), /* ak4648 reg */ - .reg_word_size = sizeof(u8), - .dapm_widgets = ak4642_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ak4642_dapm_widgets), - .dapm_routes = ak4642_intercon, - .num_dapm_routes = ARRAY_SIZE(ak4642_intercon), +static const struct regmap_config ak4642_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ARRAY_SIZE(ak4642_reg) + 1, + .reg_defaults = ak4642_reg, + .num_reg_defaults = ARRAY_SIZE(ak4642_reg), +}; + +static const struct regmap_config ak4648_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ARRAY_SIZE(ak4648_reg) + 1, + .reg_defaults = ak4648_reg, + .num_reg_defaults = ARRAY_SIZE(ak4648_reg), }; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) static struct of_device_id ak4642_of_match[]; static int ak4642_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct device_node *np = i2c->dev.of_node; - const struct snd_soc_codec_driver *driver; + const struct regmap_config *regmap_config = NULL; + struct regmap *regmap; - driver = NULL; if (np) { const struct of_device_id *of_id; of_id = of_match_device(ak4642_of_match, &i2c->dev); if (of_id) - driver = of_id->data; + regmap_config = of_id->data; } else { - driver = (struct snd_soc_codec_driver *)id->driver_data; + regmap_config = (const struct regmap_config *)id->driver_data; } - if (!driver) { - dev_err(&i2c->dev, "no driver\n"); + if (!regmap_config) { + dev_err(&i2c->dev, "Unknown device type\n"); return -EINVAL; } + regmap = devm_regmap_init_i2c(i2c, regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + return snd_soc_register_codec(&i2c->dev, - driver, &ak4642_dai, 1); + &soc_codec_dev_ak4642, &ak4642_dai, 1); } static int ak4642_i2c_remove(struct i2c_client *client) @@ -546,17 +551,17 @@ static int ak4642_i2c_remove(struct i2c_client *client) } static struct of_device_id ak4642_of_match[] = { - { .compatible = "asahi-kasei,ak4642", .data = &soc_codec_dev_ak4642}, - { .compatible = "asahi-kasei,ak4643", .data = &soc_codec_dev_ak4642}, - { .compatible = "asahi-kasei,ak4648", .data = &soc_codec_dev_ak4648}, + { .compatible = "asahi-kasei,ak4642", .data = &ak4642_regmap}, + { .compatible = "asahi-kasei,ak4643", .data = &ak4642_regmap}, + { .compatible = "asahi-kasei,ak4648", .data = &ak4648_regmap}, {}, }; MODULE_DEVICE_TABLE(of, ak4642_of_match); static const struct i2c_device_id ak4642_i2c_id[] = { - { "ak4642", (kernel_ulong_t)&soc_codec_dev_ak4642 }, - { "ak4643", (kernel_ulong_t)&soc_codec_dev_ak4642 }, - { "ak4648", (kernel_ulong_t)&soc_codec_dev_ak4648 }, + { "ak4642", (kernel_ulong_t)&ak4642_regmap }, + { "ak4643", (kernel_ulong_t)&ak4642_regmap }, + { "ak4648", (kernel_ulong_t)&ak4648_regmap }, { } }; MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id); @@ -571,27 +576,8 @@ static struct i2c_driver ak4642_i2c_driver = { .remove = ak4642_i2c_remove, .id_table = ak4642_i2c_id, }; -#endif - -static int __init ak4642_modinit(void) -{ - int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - ret = i2c_add_driver(&ak4642_i2c_driver); -#endif - return ret; -} -module_init(ak4642_modinit); - -static void __exit ak4642_exit(void) -{ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_del_driver(&ak4642_i2c_driver); -#endif - -} -module_exit(ak4642_exit); +module_i2c_driver(ak4642_i2c_driver); MODULE_DESCRIPTION("Soc AK4642 driver"); MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>"); diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 256c364193a5..d3036283482a 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -714,17 +714,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: diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index 19e9f222d09c..fb001c56cf8d 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -869,14 +869,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..e4295fee8f13 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -93,7 +93,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 +105,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 +120,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 +296,10 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "AIF1RX8", "AIF2RX1", "AIF2RX2", + "AIF2RX3", + "AIF2RX4", + "AIF2RX5", + "AIF2RX6", "AIF3RX1", "AIF3RX2", "SLIMRX1", @@ -395,6 +403,10 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { 0x27, 0x28, /* AIF2RX1 */ 0x29, + 0x2a, + 0x2b, + 0x2c, + 0x2d, 0x30, /* AIF3RX1 */ 0x31, 0x38, /* SLIMRX1 */ @@ -486,6 +498,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,6 +530,13 @@ 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", @@ -560,6 +595,16 @@ const struct soc_enum arizona_ng_hold = 4, 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" +}; + +const struct soc_enum arizona_in_hpf_cut_enum = + SOC_ENUM_SINGLE(ARIZONA_HPF_CONTROL, ARIZONA_IN_HPF_CUT_SHIFT, + ARRAY_SIZE(arizona_in_hpf_cut_text), + 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 +714,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 +737,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 +893,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 +951,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 +1215,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; } @@ -1428,31 +1483,31 @@ 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); + 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); + regmap_update_bits_async(arizona->regmap, base + 0x7, + ARIZONA_FLL1_GAIN_MASK, + cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); else - regmap_update_bits(arizona->regmap, base + 0x9, - ARIZONA_FLL1_GAIN_MASK, - cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); + regmap_update_bits_async(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) @@ -1485,9 +1540,9 @@ 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); + regmap_update_bits_async(arizona->regmap, fll->base + 5, + ARIZONA_FLL1_OUTDIV_MASK, + ref->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); arizona_apply_fll(arizona, fll->base, ref, fll->ref_src, false); @@ -1497,15 +1552,15 @@ static void arizona_enable_fll(struct arizona_fll *fll, 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); + regmap_update_bits_async(arizona->regmap, fll->base + 5, + ARIZONA_FLL1_OUTDIV_MASK, + sync->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); arizona_apply_fll(arizona, fll->base, sync, 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 +1571,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 +1584,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 +1604,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, 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..ce05fd93dc74 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -675,7 +675,7 @@ static struct spi_driver cs4271_spi_driver = { }; #endif /* defined(CONFIG_SPI_MASTER) */ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static const struct i2c_device_id cs4271_i2c_id[] = { {"cs4271", 0}, {} @@ -728,7 +728,7 @@ static struct i2c_driver cs4271_i2c_driver = { .probe = cs4271_i2c_probe, .remove = cs4271_i2c_remove, }; -#endif /* defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) */ +#endif /* IS_ENABLED(CONFIG_I2C) */ /* * We only register our serial bus driver here without @@ -741,7 +741,7 @@ static int __init cs4271_modinit(void) { int ret; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&cs4271_i2c_driver); if (ret) { pr_err("Failed to register CS4271 I2C driver: %d\n", ret); @@ -767,7 +767,7 @@ static void __exit cs4271_modexit(void) spi_unregister_driver(&cs4271_spi_driver); #endif -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&cs4271_i2c_driver); #endif } diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 1e0fa3b5f79a..6e9ea8379a91 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -423,21 +423,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: diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 8b427c977083..0bac6d5a4ac8 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -17,7 +17,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/of_gpio.h> #include <linux/pm.h> #include <linux/i2c.h> #include <linux/input.h> @@ -50,7 +50,7 @@ struct cs42l52_private { u8 mclksel; u32 mclk; u8 flags; -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +#if IS_ENABLED(CONFIG_INPUT) struct input_dev *beep; struct work_struct beep_work; int beep_rate; @@ -233,7 +233,7 @@ static const struct soc_enum mic_bias_level_enum = SOC_ENUM_SINGLE(CS42L52_IFACE_CTL2, 0, ARRAY_SIZE(mic_bias_level_text), mic_bias_level_text); -static const char * const cs42l52_mic_text[] = { "Single", "Differential" }; +static const char * const cs42l52_mic_text[] = { "MIC1", "MIC2" }; static const struct soc_enum mica_enum = SOC_ENUM_SINGLE(CS42L52_MICA_CTL, 5, @@ -243,12 +243,6 @@ static const struct soc_enum micb_enum = SOC_ENUM_SINGLE(CS42L52_MICB_CTL, 5, ARRAY_SIZE(cs42l52_mic_text), cs42l52_mic_text); -static const struct snd_kcontrol_new mica_mux = - SOC_DAPM_ENUM("Left Mic Input Capture Mux", mica_enum); - -static const struct snd_kcontrol_new micb_mux = - SOC_DAPM_ENUM("Right Mic Input Capture Mux", micb_enum); - static const char * const digital_output_mux_text[] = {"ADC", "DSP"}; static const struct soc_enum digital_output_mux_enum = @@ -531,6 +525,30 @@ static const struct snd_kcontrol_new cs42l52_snd_controls[] = { }; +static const struct snd_kcontrol_new cs42l52_mica_controls[] = { + SOC_ENUM("MICA Select", mica_enum), +}; + +static const struct snd_kcontrol_new cs42l52_micb_controls[] = { + SOC_ENUM("MICB Select", micb_enum), +}; + +static int cs42l52_add_mic_controls(struct snd_soc_codec *codec) +{ + struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); + struct cs42l52_platform_data *pdata = &cs42l52->pdata; + + if (!pdata->mica_diff_cfg) + snd_soc_add_codec_controls(codec, cs42l52_mica_controls, + ARRAY_SIZE(cs42l52_mica_controls)); + + if (!pdata->micb_diff_cfg) + snd_soc_add_codec_controls(codec, cs42l52_micb_controls, + ARRAY_SIZE(cs42l52_micb_controls)); + + return 0; +} + static const struct snd_soc_dapm_widget cs42l52_dapm_widgets[] = { SND_SOC_DAPM_INPUT("AIN1L"), @@ -550,9 +568,6 @@ static const struct snd_soc_dapm_widget cs42l52_dapm_widgets[] = { SND_SOC_DAPM_AIF_OUT("AIFOUTR", NULL, 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_MUX("MICA Mux", SND_SOC_NOPM, 0, 0, &mica_mux), - SND_SOC_DAPM_MUX("MICB Mux", SND_SOC_NOPM, 0, 0, &micb_mux), - SND_SOC_DAPM_ADC("ADC Left", NULL, CS42L52_PWRCTL1, 1, 1), SND_SOC_DAPM_ADC("ADC Right", NULL, CS42L52_PWRCTL1, 2, 1), SND_SOC_DAPM_PGA("PGA Left", CS42L52_PWRCTL1, 3, 1, NULL, 0), @@ -953,7 +968,7 @@ static int cs42l52_resume(struct snd_soc_codec *codec) return 0; } -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +#if IS_ENABLED(CONFIG_INPUT) static int beep_rates[] = { 261, 522, 585, 667, 706, 774, 889, 1000, 1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182 @@ -1110,6 +1125,8 @@ static int cs42l52_probe(struct snd_soc_codec *codec) } regcache_cache_only(cs42l52->regmap, true); + cs42l52_add_mic_controls(codec); + cs42l52_init_beep(codec); cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -1176,6 +1193,7 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, int ret; unsigned int devid = 0; unsigned int reg; + u32 val32; cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l52_private), GFP_KERNEL); @@ -1189,9 +1207,39 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); return ret; } - - if (pdata) + if (pdata) { + cs42l52->pdata = *pdata; + } else { + pdata = devm_kzalloc(&i2c_client->dev, + sizeof(struct cs42l52_platform_data), + GFP_KERNEL); + if (!pdata) { + dev_err(&i2c_client->dev, "could not allocate pdata\n"); + return -ENOMEM; + } + if (i2c_client->dev.of_node) { + if (of_property_read_bool(i2c_client->dev.of_node, + "cirrus,mica-differential-cfg")) + pdata->mica_diff_cfg = true; + + if (of_property_read_bool(i2c_client->dev.of_node, + "cirrus,micb-differential-cfg")) + pdata->micb_diff_cfg = true; + + if (of_property_read_u32(i2c_client->dev.of_node, + "cirrus,micbias-lvl", &val32) >= 0) + pdata->micbias_lvl = val32; + + if (of_property_read_u32(i2c_client->dev.of_node, + "cirrus,chgfreq-divisor", &val32) >= 0) + pdata->chgfreq = val32; + + pdata->reset_gpio = + of_get_named_gpio(i2c_client->dev.of_node, + "cirrus,reset-gpio", 0); + } cs42l52->pdata = *pdata; + } if (cs42l52->pdata.reset_gpio) { ret = gpio_request_one(cs42l52->pdata.reset_gpio, @@ -1227,29 +1275,18 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client, reg & 0xFF); /* Set Platform Data */ - if (cs42l52->pdata.mica_cfg) + if (cs42l52->pdata.mica_diff_cfg) regmap_update_bits(cs42l52->regmap, CS42L52_MICA_CTL, CS42L52_MIC_CTL_TYPE_MASK, - cs42l52->pdata.mica_cfg << + cs42l52->pdata.mica_diff_cfg << CS42L52_MIC_CTL_TYPE_SHIFT); - if (cs42l52->pdata.micb_cfg) + if (cs42l52->pdata.micb_diff_cfg) regmap_update_bits(cs42l52->regmap, CS42L52_MICB_CTL, CS42L52_MIC_CTL_TYPE_MASK, - cs42l52->pdata.micb_cfg << + cs42l52->pdata.micb_diff_cfg << CS42L52_MIC_CTL_TYPE_SHIFT); - if (cs42l52->pdata.mica_sel) - regmap_update_bits(cs42l52->regmap, CS42L52_MICA_CTL, - CS42L52_MIC_CTL_MIC_SEL_MASK, - cs42l52->pdata.mica_sel << - CS42L52_MIC_CTL_MIC_SEL_SHIFT); - if (cs42l52->pdata.micb_sel) - regmap_update_bits(cs42l52->regmap, CS42L52_MICB_CTL, - CS42L52_MIC_CTL_MIC_SEL_MASK, - cs42l52->pdata.micb_sel << - CS42L52_MIC_CTL_MIC_SEL_SHIFT); - if (cs42l52->pdata.chgfreq) regmap_update_bits(cs42l52->regmap, CS42L52_CHARGE_PUMP, CS42L52_CHARGE_PUMP_MASK, @@ -1274,6 +1311,13 @@ static int cs42l52_i2c_remove(struct i2c_client *client) return 0; } +static const struct of_device_id cs42l52_of_match[] = { + { .compatible = "cirrus,cs42l52", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs42l52_of_match); + + static const struct i2c_device_id cs42l52_id[] = { { "cs42l52", 0 }, { } @@ -1284,6 +1328,7 @@ static struct i2c_driver cs42l52_i2c_driver = { .driver = { .name = "cs42l52", .owner = THIS_MODULE, + .of_match_table = cs42l52_of_match, }, .id_table = cs42l52_id, .probe = cs42l52_i2c_probe, diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index 9c1231456502..e62e294a8033 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -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..0c77e7ad7423 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1067,17 +1067,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..f295b6569910 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -973,17 +973,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: diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index fc9802d1281d..52b79a487ac7 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -1058,17 +1058,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: 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..5839048ec467 100644 --- a/sound/soc/codecs/isabelle.c +++ b/sound/soc/codecs/isabelle.c @@ -951,11 +951,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/max98088.c b/sound/soc/codecs/max98088.c index 53d7dab4e054..ee660e2d3df3 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; diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 0569a4c3ae00..51f9b3d16b41 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -1840,8 +1840,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..3ba1170ebb53 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; 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/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 1f4093f3f3a1..0fcbe90f3ef2 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -115,6 +115,7 @@ struct sgtl5000_priv { struct ldo_regulator *ldo; struct regmap *regmap; struct clk *mclk; + int revision; }; /* @@ -1285,41 +1286,45 @@ static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec) sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME; - ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); - - if (ret) { - ldo_regulator_remove(codec); - dev_err(codec->dev, "Failed to request supplies: %d\n", ret); - return ret; - } - dev_info(codec->dev, "Using internal LDO instead of VDDD\n"); return 0; } static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) { - int reg; int ret; - int rev; int i; int external_vddd = 0; struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + struct regulator *vddd; for (i = 0; i < ARRAY_SIZE(sgtl5000->supplies); i++) sgtl5000->supplies[i].supply = supply_names[i]; - ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); - if (!ret) - external_vddd = 1; - else { + /* External VDDD only works before revision 0x11 */ + if (sgtl5000->revision < 0x11) { + vddd = regulator_get_optional(codec->dev, "VDDD"); + if (IS_ERR(vddd)) { + /* See if it's just not registered yet */ + if (PTR_ERR(vddd) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } else { + external_vddd = 1; + regulator_put(vddd); + } + } + + if (!external_vddd) { ret = sgtl5000_replace_vddd_with_ldo(codec); if (ret) return ret; } + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) + goto err_ldo_remove; + ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); if (ret) @@ -1328,47 +1333,13 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) /* wait for all power rails bring up */ udelay(10); - /* - * workaround for revision 0x11 and later, - * roll back to use internal LDO - */ - - ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, ®); - if (ret) - goto err_regulator_disable; - - rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; - - if (external_vddd && rev >= 0x11) { - /* disable all regulator first */ - regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); - /* free VDDD regulator */ - regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); - - ret = sgtl5000_replace_vddd_with_ldo(codec); - if (ret) - return ret; - - ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); - if (ret) - goto err_regulator_free; - - /* wait for all power rails bring up */ - udelay(10); - } - return 0; -err_regulator_disable: - regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), - sgtl5000->supplies); err_regulator_free: regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); - if (external_vddd) +err_ldo_remove: + if (!external_vddd) ldo_regulator_remove(codec); return ret; @@ -1566,6 +1537,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev); + sgtl5000->revision = rev; i2c_set_clientdata(client, sgtl5000); diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index 95aed552139a..cc8debce752f 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -549,13 +549,13 @@ static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, right_slot = 0; } else { /* We assume the left channel < right channel */ - left_slot = ffs(tx_mask); - tx_mask &= ~(1 << tx_mask); + left_slot = __ffs(tx_mask); + tx_mask &= ~(1 << left_slot); if (tx_mask == 0) { right_slot = left_slot; } else { - right_slot = ffs(tx_mask); - tx_mask &= ~(1 << tx_mask); + right_slot = __ffs(tx_mask); + tx_mask &= ~(1 << right_slot); } } diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 492644e67ace..af76bbd1b24f 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -53,8 +53,6 @@ enum ssm2602_type { struct ssm2602_priv { unsigned int sysclk; struct snd_pcm_hw_constraint_list *sysclk_constraints; - struct snd_pcm_substream *master_substream; - struct snd_pcm_substream *slave_substream; struct regmap *regmap; @@ -196,7 +194,7 @@ 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 = { @@ -233,6 +231,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 +280,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 +312,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 +322,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 +478,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 +489,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 +509,8 @@ static struct snd_soc_dai_driver ssm2602_dai = { .rates = SSM2602_RATES, .formats = SSM2602_FORMATS,}, .ops = &ssm2602_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, }; static int ssm2602_suspend(struct snd_soc_codec *codec) @@ -730,7 +690,7 @@ static struct spi_driver ssm2602_spi_driver = { }; #endif -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) /* * ssm2602 2 wire address is determined by GPIO5 * state during powerup. @@ -797,7 +757,7 @@ static int __init ssm2602_modinit(void) return ret; #endif -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&ssm2602_i2c_driver); if (ret) return ret; @@ -813,7 +773,7 @@ static void __exit ssm2602_exit(void) spi_unregister_driver(&ssm2602_spi_driver); #endif -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&ssm2602_i2c_driver); #endif } diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 18cdcca9014c..385dec16eb8a 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -267,8 +267,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, }, }; 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/twl4030.c b/sound/soc/codecs/twl4030.c index dfc51bb425da..00665ada23e2 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; + } + + 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 (write_to_reg) - return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, - value, reg); + + 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 */ @@ -671,20 +572,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 +599,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 +626,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 +640,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 +654,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 +675,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 +687,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 +709,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 +730,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 +744,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 +763,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 +787,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 +811,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 +832,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 +861,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 +890,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 +917,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; @@ -1751,11 +1648,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 +1681,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 +1720,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 +1729,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 +1746,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 +1788,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 +1837,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 +1863,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 +1920,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 +1933,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 +1953,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 +1972,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 +1985,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 +1994,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 +2005,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 +2040,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 +2061,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 +2115,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 +2207,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 +2218,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 +2234,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..0afe8bef6765 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -72,6 +72,7 @@ struct twl6040_data { int hs_power_mode_locked; bool dl1_unmuted; bool dl2_unmuted; + u8 dl12_cache[TWL6040_REG_HFRCTL - TWL6040_REG_HSLCTL + 1]; unsigned int clk_in; unsigned int sysclk; struct twl6040_jack_data hs_jack; @@ -79,75 +80,6 @@ struct twl6040_data { struct mutex mutex; }; -/* - * twl6040 register cache & default register settings - */ -static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = { - 0x00, /* not used 0x00 */ - 0x4B, /* REG_ASICID 0x01 (ro) */ - 0x00, /* REG_ASICREV 0x02 (ro) */ - 0x00, /* REG_INTID 0x03 */ - 0x00, /* REG_INTMR 0x04 */ - 0x00, /* REG_NCPCTRL 0x05 */ - 0x00, /* REG_LDOCTL 0x06 */ - 0x60, /* REG_HPPLLCTL 0x07 */ - 0x00, /* REG_LPPLLCTL 0x08 */ - 0x4A, /* REG_LPPLLDIV 0x09 */ - 0x00, /* REG_AMICBCTL 0x0A */ - 0x00, /* REG_DMICBCTL 0x0B */ - 0x00, /* REG_MICLCTL 0x0C */ - 0x00, /* REG_MICRCTL 0x0D */ - 0x00, /* REG_MICGAIN 0x0E */ - 0x1B, /* REG_LINEGAIN 0x0F */ - 0x00, /* REG_HSLCTL 0x10 */ - 0x00, /* REG_HSRCTL 0x11 */ - 0x00, /* REG_HSGAIN 0x12 */ - 0x00, /* REG_EARCTL 0x13 */ - 0x00, /* REG_HFLCTL 0x14 */ - 0x00, /* REG_HFLGAIN 0x15 */ - 0x00, /* REG_HFRCTL 0x16 */ - 0x00, /* REG_HFRGAIN 0x17 */ - 0x00, /* REG_VIBCTLL 0x18 */ - 0x00, /* REG_VIBDATL 0x19 */ - 0x00, /* REG_VIBCTLR 0x1A */ - 0x00, /* REG_VIBDATR 0x1B */ - 0x00, /* REG_HKCTL1 0x1C */ - 0x00, /* REG_HKCTL2 0x1D */ - 0x00, /* REG_GPOCTL 0x1E */ - 0x00, /* REG_ALB 0x1F */ - 0x00, /* REG_DLB 0x20 */ - 0x00, /* not used 0x21 */ - 0x00, /* not used 0x22 */ - 0x00, /* not used 0x23 */ - 0x00, /* not used 0x24 */ - 0x00, /* not used 0x25 */ - 0x00, /* not used 0x26 */ - 0x00, /* not used 0x27 */ - 0x00, /* REG_TRIM1 0x28 */ - 0x00, /* REG_TRIM2 0x29 */ - 0x00, /* REG_TRIM3 0x2A */ - 0x00, /* REG_HSOTRIM 0x2B */ - 0x00, /* REG_HFOTRIM 0x2C */ - 0x09, /* REG_ACCCTL 0x2D */ - 0x00, /* REG_STATUS 0x2E (ro) */ -}; - -/* List of registers to be restored after power up */ -static const int twl6040_restore_list[] = { - TWL6040_REG_MICLCTL, - TWL6040_REG_MICRCTL, - TWL6040_REG_MICGAIN, - TWL6040_REG_LINEGAIN, - TWL6040_REG_HSLCTL, - TWL6040_REG_HSRCTL, - TWL6040_REG_HSGAIN, - TWL6040_REG_EARCTL, - TWL6040_REG_HFLCTL, - TWL6040_REG_HFLGAIN, - TWL6040_REG_HFRCTL, - TWL6040_REG_HFRGAIN, -}; - /* set of rates for each pll: low-power and high-performance */ static unsigned int lp_rates[] = { 8000, @@ -174,53 +106,33 @@ static struct snd_pcm_hw_constraint_list sysclk_constraints[] = { { .count = ARRAY_SIZE(hp_rates), .list = hp_rates, }, }; -/* - * read twl6040 register cache - */ -static inline unsigned int twl6040_read_reg_cache(struct snd_soc_codec *codec, - unsigned int reg) -{ - u8 *cache = codec->reg_cache; - - if (reg >= TWL6040_CACHEREGNUM) - return -EIO; - - return cache[reg]; -} - -/* - * write twl6040 register cache - */ -static inline void twl6040_write_reg_cache(struct snd_soc_codec *codec, - u8 reg, u8 value) -{ - u8 *cache = codec->reg_cache; - - if (reg >= TWL6040_CACHEREGNUM) - return; - cache[reg] = value; -} - -/* - * read from twl6040 hardware register - */ -static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, - unsigned int reg) +static unsigned int twl6040_read(struct snd_soc_codec *codec, unsigned int reg) { + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); struct twl6040 *twl6040 = codec->control_data; u8 value; if (reg >= TWL6040_CACHEREGNUM) return -EIO; - value = twl6040_reg_read(twl6040, reg); - twl6040_write_reg_cache(codec, reg, value); + switch (reg) { + case TWL6040_REG_HSLCTL: + case TWL6040_REG_HSRCTL: + case TWL6040_REG_EARCTL: + case TWL6040_REG_HFLCTL: + case TWL6040_REG_HFRCTL: + value = priv->dl12_cache[reg - TWL6040_REG_HSLCTL]; + break; + default: + value = twl6040_reg_read(twl6040, reg); + break; + } return value; } -static bool twl6040_is_path_unmuted(struct snd_soc_codec *codec, - unsigned int reg) +static bool twl6040_can_write_to_chip(struct snd_soc_codec *codec, + unsigned int reg) { struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); @@ -238,9 +150,24 @@ static bool twl6040_is_path_unmuted(struct snd_soc_codec *codec, } } -/* - * write to the twl6040 register space - */ +static inline void twl6040_update_dl12_cache(struct snd_soc_codec *codec, + u8 reg, u8 value) +{ + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + switch (reg) { + case TWL6040_REG_HSLCTL: + case TWL6040_REG_HSRCTL: + case TWL6040_REG_EARCTL: + case TWL6040_REG_HFLCTL: + case TWL6040_REG_HFRCTL: + priv->dl12_cache[reg - TWL6040_REG_HSLCTL] = value; + break; + default: + break; + } +} + static int twl6040_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { @@ -249,8 +176,8 @@ static int twl6040_write(struct snd_soc_codec *codec, if (reg >= TWL6040_CACHEREGNUM) return -EIO; - twl6040_write_reg_cache(codec, reg, value); - if (twl6040_is_path_unmuted(codec, reg)) + twl6040_update_dl12_cache(codec, reg, value); + if (twl6040_can_write_to_chip(codec, reg)) return twl6040_reg_write(twl6040, reg, value); else return 0; @@ -258,45 +185,27 @@ static int twl6040_write(struct snd_soc_codec *codec, static void twl6040_init_chip(struct snd_soc_codec *codec) { - struct twl6040 *twl6040 = codec->control_data; - u8 val; - - /* Update reg_cache: ASICREV, and TRIM values */ - val = twl6040_get_revid(twl6040); - twl6040_write_reg_cache(codec, TWL6040_REG_ASICREV, val); - - twl6040_read_reg_volatile(codec, TWL6040_REG_TRIM1); - twl6040_read_reg_volatile(codec, TWL6040_REG_TRIM2); - twl6040_read_reg_volatile(codec, TWL6040_REG_TRIM3); - twl6040_read_reg_volatile(codec, TWL6040_REG_HSOTRIM); - twl6040_read_reg_volatile(codec, TWL6040_REG_HFOTRIM); + twl6040_read(codec, TWL6040_REG_TRIM1); + twl6040_read(codec, TWL6040_REG_TRIM2); + twl6040_read(codec, TWL6040_REG_TRIM3); + twl6040_read(codec, TWL6040_REG_HSOTRIM); + twl6040_read(codec, TWL6040_REG_HFOTRIM); /* Change chip defaults */ /* No imput selected for microphone amplifiers */ - twl6040_write_reg_cache(codec, TWL6040_REG_MICLCTL, 0x18); - twl6040_write_reg_cache(codec, TWL6040_REG_MICRCTL, 0x18); + twl6040_write(codec, TWL6040_REG_MICLCTL, 0x18); + twl6040_write(codec, TWL6040_REG_MICRCTL, 0x18); /* * We need to lower the default gain values, so the ramp code * can work correctly for the first playback. * This reduces the pop noise heard at the first playback. */ - twl6040_write_reg_cache(codec, TWL6040_REG_HSGAIN, 0xff); - twl6040_write_reg_cache(codec, TWL6040_REG_EARCTL, 0x1e); - twl6040_write_reg_cache(codec, TWL6040_REG_HFLGAIN, 0x1d); - twl6040_write_reg_cache(codec, TWL6040_REG_HFRGAIN, 0x1d); - twl6040_write_reg_cache(codec, TWL6040_REG_LINEGAIN, 0); -} - -static void twl6040_restore_regs(struct snd_soc_codec *codec) -{ - u8 *cache = codec->reg_cache; - int reg, i; - - for (i = 0; i < ARRAY_SIZE(twl6040_restore_list); i++) { - reg = twl6040_restore_list[i]; - twl6040_write(codec, reg, cache[reg]); - } + twl6040_write(codec, TWL6040_REG_HSGAIN, 0xff); + twl6040_write(codec, TWL6040_REG_EARCTL, 0x1e); + twl6040_write(codec, TWL6040_REG_HFLGAIN, 0x1d); + twl6040_write(codec, TWL6040_REG_HFRGAIN, 0x1d); + twl6040_write(codec, TWL6040_REG_LINEGAIN, 0); } /* set headset dac and driver power mode */ @@ -305,8 +214,8 @@ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) int hslctl, hsrctl; int mask = TWL6040_HSDRVMODE | TWL6040_HSDACMODE; - hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL); - hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL); + hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL); + hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL); if (high_perf) { hslctl &= ~mask; @@ -333,8 +242,8 @@ static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w, * Both HS DAC need to be turned on (before the HS driver) and off at * the same time. */ - hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL); - hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL); + hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL); + hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL); if (SND_SOC_DAPM_EVENT_ON(event)) { hslctl |= TWL6040_HSDACENA; hsrctl |= TWL6040_HSDACENA; @@ -379,7 +288,7 @@ static void twl6040_hs_jack_report(struct snd_soc_codec *codec, mutex_lock(&priv->mutex); /* Sync status */ - status = twl6040_read_reg_volatile(codec, TWL6040_REG_STATUS); + status = twl6040_read(codec, TWL6040_REG_STATUS); if (status & TWL6040_PLUGCOMP) snd_soc_jack_report(jack, report, report); else @@ -431,7 +340,7 @@ static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol, unsigned int val; /* Do not allow changes while Input/FF efect is running */ - val = twl6040_read_reg_volatile(codec, e->reg); + val = twl6040_read(codec, e->reg); if (val & TWL6040_VIBENA && !(val & TWL6040_VIBSEL)) return -EBUSY; @@ -656,7 +565,7 @@ int twl6040_get_trim_value(struct snd_soc_codec *codec, enum twl6040_trim trim) if (unlikely(trim >= TWL6040_TRIM_INVAL)) return -EINVAL; - return twl6040_read_reg_cache(codec, TWL6040_REG_TRIM1 + trim); + return twl6040_read(codec, TWL6040_REG_TRIM1 + trim); } EXPORT_SYMBOL_GPL(twl6040_get_trim_value); @@ -931,8 +840,6 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, priv->codec_powered = 1; - twl6040_restore_regs(codec); - /* Set external boost GPO */ twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02); break; @@ -1053,9 +960,9 @@ static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id i switch (id) { case TWL6040_DAI_DL1: - hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL); - hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL); - earctl = twl6040_read_reg_cache(codec, TWL6040_REG_EARCTL); + hslctl = twl6040_read(codec, TWL6040_REG_HSLCTL); + hsrctl = twl6040_read(codec, TWL6040_REG_HSRCTL); + earctl = twl6040_read(codec, TWL6040_REG_EARCTL); if (mute) { /* Power down drivers and DACs */ @@ -1071,8 +978,8 @@ static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id i priv->dl1_unmuted = !mute; break; case TWL6040_DAI_DL2: - hflctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFLCTL); - hfrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFRCTL); + hflctl = twl6040_read(codec, TWL6040_REG_HFLCTL); + hfrctl = twl6040_read(codec, TWL6040_REG_HFRCTL); if (mute) { /* Power down drivers and DACs */ @@ -1209,6 +1116,7 @@ static int twl6040_resume(struct snd_soc_codec *codec) static int twl6040_probe(struct snd_soc_codec *codec) { struct twl6040_data *priv; + struct twl6040 *twl6040 = dev_get_drvdata(codec->dev->parent); struct platform_device *pdev = container_of(codec->dev, struct platform_device, dev); int ret = 0; @@ -1220,7 +1128,7 @@ static int twl6040_probe(struct snd_soc_codec *codec) snd_soc_codec_set_drvdata(codec, priv); priv->codec = codec; - codec->control_data = dev_get_drvdata(codec->dev->parent); + codec->control_data = twl6040; priv->plug_irq = platform_get_irq(pdev, 0); if (priv->plug_irq < 0) { @@ -1240,10 +1148,10 @@ static int twl6040_probe(struct snd_soc_codec *codec) return ret; } + twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); twl6040_init_chip(codec); - /* power on device */ - return twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; } static int twl6040_remove(struct snd_soc_codec *codec) @@ -1261,12 +1169,9 @@ static struct snd_soc_codec_driver soc_codec_dev_twl6040 = { .remove = twl6040_remove, .suspend = twl6040_suspend, .resume = twl6040_resume, - .read = twl6040_read_reg_cache, + .read = twl6040_read, .write = twl6040_write, .set_bias_level = twl6040_set_bias_level, - .reg_cache_size = ARRAY_SIZE(twl6040_reg), - .reg_word_size = sizeof(u8), - .reg_cache_default = twl6040_reg, .ignore_pmdown_time = true, .controls = twl6040_snd_controls, diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index fd0a314bc209..726df6d43c2b 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -794,7 +794,7 @@ static struct snd_soc_codec_driver soc_codec_dev_uda1380 = { .num_dapm_routes = ARRAY_SIZE(uda1380_dapm_routes), }; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int uda1380_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -840,7 +840,7 @@ static struct i2c_driver uda1380_i2c_driver = { static int __init uda1380_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&uda1380_i2c_driver); if (ret != 0) pr_err("Failed to register UDA1380 I2C driver: %d\n", ret); @@ -851,7 +851,7 @@ module_init(uda1380_modinit); static void __exit uda1380_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&uda1380_i2c_driver); #endif } diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index a08e8bf6d07c..ce9c8e14d4bd 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -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: diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index c3c7396a6181..d862f76b59f9 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -30,13 +30,51 @@ #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 }, @@ -67,8 +105,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 +155,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, @@ -220,6 +277,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 +313,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 +352,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 +397,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 +430,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 +476,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 +498,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 +668,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 +769,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 +927,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 +949,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 +1027,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 +1056,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 +1176,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 +1270,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 +1310,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 +1358,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 +1441,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 +1550,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 +1608,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 +1619,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 +1647,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/wm8510.c b/sound/soc/codecs/wm8510.c index 6ed5433943ea..7df7d4572755 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -684,7 +684,7 @@ static struct spi_driver wm8510_spi_driver = { }; #endif /* CONFIG_SPI_MASTER */ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8510_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -735,7 +735,7 @@ static struct i2c_driver wm8510_i2c_driver = { static int __init wm8510_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8510_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register WM8510 I2C driver: %d\n", @@ -755,7 +755,7 @@ module_init(wm8510_modinit); static void __exit wm8510_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8510_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 139bf9ac9407..74d106dc7667 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -452,7 +452,7 @@ static const struct regmap_config wm8523_regmap = { .volatile_reg = wm8523_volatile_register, }; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8523_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -555,7 +555,7 @@ static struct i2c_driver wm8523_i2c_driver = { static int __init wm8523_modinit(void) { int ret; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8523_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register WM8523 I2C driver: %d\n", @@ -568,7 +568,7 @@ module_init(wm8523_modinit); static void __exit wm8523_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8523_i2c_driver); #endif } diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 08a414b57b1e..318989acbbe5 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -941,7 +941,7 @@ static const struct regmap_config wm8580_regmap = { .volatile_reg = wm8580_volatile, }; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8580_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1003,7 +1003,7 @@ static int __init wm8580_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8580_i2c_driver); if (ret != 0) { pr_err("Failed to register WM8580 I2C driver: %d\n", ret); @@ -1016,7 +1016,7 @@ module_init(wm8580_modinit); static void __exit wm8580_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8580_i2c_driver); #endif } diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 5b428b060d41..d99f948c513c 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -469,7 +469,7 @@ static struct spi_driver wm8711_spi_driver = { }; #endif /* CONFIG_SPI_MASTER */ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8711_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -520,7 +520,7 @@ static struct i2c_driver wm8711_i2c_driver = { static int __init wm8711_modinit(void) { int ret; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8711_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n", @@ -540,7 +540,7 @@ module_init(wm8711_modinit); static void __exit wm8711_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8711_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index c6a292dcded0..cd89033e84c0 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -320,7 +320,7 @@ static struct spi_driver wm8728_spi_driver = { }; #endif /* CONFIG_SPI_MASTER */ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8728_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -371,7 +371,7 @@ static struct i2c_driver wm8728_i2c_driver = { static int __init wm8728_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8728_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register wm8728 I2C driver: %d\n", @@ -391,7 +391,7 @@ module_init(wm8728_modinit); static void __exit wm8728_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8728_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 456bb8c6d759..029720366ff8 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -447,10 +447,10 @@ static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai, iface |= 0x0001; break; case SND_SOC_DAIFMT_DSP_A: - iface |= 0x0003; + iface |= 0x0013; break; case SND_SOC_DAIFMT_DSP_B: - iface |= 0x0013; + iface |= 0x0003; break; default: return -EINVAL; @@ -732,7 +732,7 @@ static struct spi_driver wm8731_spi_driver = { }; #endif /* CONFIG_SPI_MASTER */ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8731_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -791,7 +791,7 @@ static struct i2c_driver wm8731_i2c_driver = { static int __init wm8731_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8731_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n", @@ -811,7 +811,7 @@ module_init(wm8731_modinit); static void __exit wm8731_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8731_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index b18813cc7ba9..2895c8d3b5e4 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -500,7 +500,7 @@ static const struct regmap_config wm8741_regmap = { .readable_reg = wm8741_readable, }; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8741_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -617,7 +617,7 @@ static int __init wm8741_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8741_i2c_driver); if (ret != 0) pr_err("Failed to register WM8741 I2C driver: %d\n", ret); @@ -639,7 +639,7 @@ static void __exit wm8741_exit(void) #if defined(CONFIG_SPI_MASTER) spi_unregister_driver(&wm8741_spi_driver); #endif -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8741_i2c_driver); #endif } diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 50d5ff616232..78616a638a55 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -816,7 +816,7 @@ static struct spi_driver wm8750_spi_driver = { }; #endif /* CONFIG_SPI_MASTER */ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8750_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -868,7 +868,7 @@ static struct i2c_driver wm8750_i2c_driver = { static int __init wm8750_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8750_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register wm8750 I2C driver: %d\n", @@ -888,7 +888,7 @@ module_init(wm8750_modinit); static void __exit wm8750_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8750_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index d96ebf52d953..be85da93a268 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1596,7 +1596,7 @@ static struct spi_driver wm8753_spi_driver = { }; #endif /* CONFIG_SPI_MASTER */ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8753_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1653,7 +1653,7 @@ static struct i2c_driver wm8753_i2c_driver = { static int __init wm8753_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8753_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register wm8753 I2C driver: %d\n", @@ -1673,7 +1673,7 @@ module_init(wm8753_modinit); static void __exit wm8753_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8753_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index 942d58e455f3..ef8246725232 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -532,7 +532,7 @@ static struct spi_driver wm8776_spi_driver = { }; #endif /* CONFIG_SPI_MASTER */ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8776_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -584,7 +584,7 @@ static struct i2c_driver wm8776_i2c_driver = { static int __init wm8776_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8776_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register wm8776 I2C driver: %d\n", @@ -604,7 +604,7 @@ module_init(wm8776_modinit); static void __exit wm8776_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8776_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 1704b1e119cb..9bc8206a6807 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -739,7 +739,7 @@ static struct spi_driver wm8804_spi_driver = { }; #endif -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8804_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -791,7 +791,7 @@ static int __init wm8804_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8804_i2c_driver); if (ret) { printk(KERN_ERR "Failed to register wm8804 I2C driver: %d\n", @@ -811,7 +811,7 @@ module_init(wm8804_modinit); static void __exit wm8804_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8804_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 734209e252c3..e98bc7038a08 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1288,7 +1288,7 @@ static struct spi_driver wm8900_spi_driver = { }; #endif /* CONFIG_SPI_MASTER */ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8900_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1338,7 +1338,7 @@ static struct i2c_driver wm8900_i2c_driver = { static int __init wm8900_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8900_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register wm8900 I2C driver: %d\n", @@ -1358,7 +1358,7 @@ module_init(wm8900_modinit); static void __exit wm8900_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8900_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 3938fb1c203e..53bbfac6a83a 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1444,7 +1444,7 @@ static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: - aif1 |= WM8904_AIF_LRCLK_INV; + aif1 |= 0x3 | WM8904_AIF_LRCLK_INV; case SND_SOC_DAIFMT_DSP_A: aif1 |= 0x3; break; diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index b1591c61c254..b404c26c1753 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -28,7 +28,7 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> -#include <linux/spi/spi.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -41,78 +41,116 @@ struct wm8940_priv { unsigned int sysclk; - enum snd_soc_control_type control_type; + struct regmap *regmap; }; -static int wm8940_volatile_register(struct snd_soc_codec *codec, - unsigned int reg) +static bool wm8940_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { case WM8940_SOFTRESET: - return 1; + return true; default: - return 0; + return false; + } +} + +static bool wm8940_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8940_SOFTRESET: + case WM8940_POWER1: + case WM8940_POWER2: + case WM8940_POWER3: + case WM8940_IFACE: + case WM8940_COMPANDINGCTL: + case WM8940_CLOCK: + case WM8940_ADDCNTRL: + case WM8940_GPIO: + case WM8940_CTLINT: + case WM8940_DAC: + case WM8940_DACVOL: + case WM8940_ADC: + case WM8940_ADCVOL: + case WM8940_NOTCH1: + case WM8940_NOTCH2: + case WM8940_NOTCH3: + case WM8940_NOTCH4: + case WM8940_NOTCH5: + case WM8940_NOTCH6: + case WM8940_NOTCH7: + case WM8940_NOTCH8: + case WM8940_DACLIM1: + case WM8940_DACLIM2: + case WM8940_ALC1: + case WM8940_ALC2: + case WM8940_ALC3: + case WM8940_NOISEGATE: + case WM8940_PLLN: + case WM8940_PLLK1: + case WM8940_PLLK2: + case WM8940_PLLK3: + case WM8940_ALC4: + case WM8940_INPUTCTL: + case WM8940_PGAGAIN: + case WM8940_ADCBOOST: + case WM8940_OUTPUTCTL: + case WM8940_SPKMIX: + case WM8940_SPKVOL: + case WM8940_MONOMIX: + return true; + default: + return false; } } -static u16 wm8940_reg_defaults[] = { - 0x8940, /* Soft Reset */ - 0x0000, /* Power 1 */ - 0x0000, /* Power 2 */ - 0x0000, /* Power 3 */ - 0x0010, /* Interface Control */ - 0x0000, /* Companding Control */ - 0x0140, /* Clock Control */ - 0x0000, /* Additional Controls */ - 0x0000, /* GPIO Control */ - 0x0002, /* Auto Increment Control */ - 0x0000, /* DAC Control */ - 0x00FF, /* DAC Volume */ - 0, - 0, - 0x0100, /* ADC Control */ - 0x00FF, /* ADC Volume */ - 0x0000, /* Notch Filter 1 Control 1 */ - 0x0000, /* Notch Filter 1 Control 2 */ - 0x0000, /* Notch Filter 2 Control 1 */ - 0x0000, /* Notch Filter 2 Control 2 */ - 0x0000, /* Notch Filter 3 Control 1 */ - 0x0000, /* Notch Filter 3 Control 2 */ - 0x0000, /* Notch Filter 4 Control 1 */ - 0x0000, /* Notch Filter 4 Control 2 */ - 0x0032, /* DAC Limit Control 1 */ - 0x0000, /* DAC Limit Control 2 */ - 0, - 0, - 0, - 0, - 0, - 0, - 0x0038, /* ALC Control 1 */ - 0x000B, /* ALC Control 2 */ - 0x0032, /* ALC Control 3 */ - 0x0000, /* Noise Gate */ - 0x0041, /* PLLN */ - 0x000C, /* PLLK1 */ - 0x0093, /* PLLK2 */ - 0x00E9, /* PLLK3 */ - 0, - 0, - 0x0030, /* ALC Control 4 */ - 0, - 0x0002, /* Input Control */ - 0x0050, /* PGA Gain */ - 0, - 0x0002, /* ADC Boost Control */ - 0, - 0x0002, /* Output Control */ - 0x0000, /* Speaker Mixer Control */ - 0, - 0, - 0, - 0x0079, /* Speaker Volume */ - 0, - 0x0000, /* Mono Mixer Control */ +static const struct reg_default wm8940_reg_defaults[] = { + { 0x1, 0x0000 }, /* Power 1 */ + { 0x2, 0x0000 }, /* Power 2 */ + { 0x3, 0x0000 }, /* Power 3 */ + { 0x4, 0x0010 }, /* Interface Control */ + { 0x5, 0x0000 }, /* Companding Control */ + { 0x6, 0x0140 }, /* Clock Control */ + { 0x7, 0x0000 }, /* Additional Controls */ + { 0x8, 0x0000 }, /* GPIO Control */ + { 0x9, 0x0002 }, /* Auto Increment Control */ + { 0xa, 0x0000 }, /* DAC Control */ + { 0xb, 0x00FF }, /* DAC Volume */ + + { 0xe, 0x0100 }, /* ADC Control */ + { 0xf, 0x00FF }, /* ADC Volume */ + { 0x10, 0x0000 }, /* Notch Filter 1 Control 1 */ + { 0x11, 0x0000 }, /* Notch Filter 1 Control 2 */ + { 0x12, 0x0000 }, /* Notch Filter 2 Control 1 */ + { 0x13, 0x0000 }, /* Notch Filter 2 Control 2 */ + { 0x14, 0x0000 }, /* Notch Filter 3 Control 1 */ + { 0x15, 0x0000 }, /* Notch Filter 3 Control 2 */ + { 0x16, 0x0000 }, /* Notch Filter 4 Control 1 */ + { 0x17, 0x0000 }, /* Notch Filter 4 Control 2 */ + { 0x18, 0x0032 }, /* DAC Limit Control 1 */ + { 0x19, 0x0000 }, /* DAC Limit Control 2 */ + + { 0x20, 0x0038 }, /* ALC Control 1 */ + { 0x21, 0x000B }, /* ALC Control 2 */ + { 0x22, 0x0032 }, /* ALC Control 3 */ + { 0x23, 0x0000 }, /* Noise Gate */ + { 0x24, 0x0041 }, /* PLLN */ + { 0x25, 0x000C }, /* PLLK1 */ + { 0x26, 0x0093 }, /* PLLK2 */ + { 0x27, 0x00E9 }, /* PLLK3 */ + + { 0x2a, 0x0030 }, /* ALC Control 4 */ + + { 0x2c, 0x0002 }, /* Input Control */ + { 0x2d, 0x0050 }, /* PGA Gain */ + + { 0x2f, 0x0002 }, /* ADC Boost Control */ + + { 0x31, 0x0002 }, /* Output Control */ + { 0x32, 0x0000 }, /* Speaker Mixer Control */ + + { 0x36, 0x0079 }, /* Speaker Volume */ + + { 0x38, 0x0000 }, /* Mono Mixer Control */ }; static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" }; @@ -264,7 +302,7 @@ static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = { SND_SOC_DAPM_INPUT("AUX"), }; -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route wm8940_dapm_routes[] = { /* Mono output mixer */ {"Mono Mixer", "PCM Playback Switch", "DAC"}, {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, @@ -296,21 +334,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"ADC", NULL, "Boost Mixer"}, }; -static int wm8940_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - int ret; - - ret = snd_soc_dapm_new_controls(dapm, wm8940_dapm_widgets, - ARRAY_SIZE(wm8940_dapm_widgets)); - if (ret) - goto error_ret; - ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - -error_ret: - return ret; -} - #define wm8940_reset(c) snd_soc_write(c, WM8940_SOFTRESET, 0); static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, @@ -446,6 +469,7 @@ static int wm8940_mute(struct snd_soc_dai *dai, int mute) static int wm8940_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec); u16 val; u16 pwr_reg = snd_soc_read(codec, WM8940_POWER1) & 0x1F0; int ret = 0; @@ -469,7 +493,7 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { - ret = snd_soc_cache_sync(codec); + ret = regcache_sync(wm8940->regmap); if (ret < 0) { dev_err(codec->dev, "Failed to sync cache: %d\n", ret); return ret; @@ -684,12 +708,11 @@ static int wm8940_resume(struct snd_soc_codec *codec) static int wm8940_probe(struct snd_soc_codec *codec) { - struct wm8940_priv *wm8940 = snd_soc_codec_get_drvdata(codec); struct wm8940_setup_data *pdata = codec->dev->platform_data; int ret; u16 reg; - ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8940->control_type); + ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; @@ -716,11 +739,6 @@ static int wm8940_probe(struct snd_soc_codec *codec) return ret; } - ret = snd_soc_add_codec_controls(codec, wm8940_snd_controls, - ARRAY_SIZE(wm8940_snd_controls)); - if (ret) - return ret; - ret = wm8940_add_widgets(codec); return ret; } @@ -736,10 +754,24 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8940 = { .suspend = wm8940_suspend, .resume = wm8940_resume, .set_bias_level = wm8940_set_bias_level, - .reg_cache_size = ARRAY_SIZE(wm8940_reg_defaults), - .reg_word_size = sizeof(u16), - .reg_cache_default = wm8940_reg_defaults, - .volatile_register = wm8940_volatile_register, + .controls = wm8940_snd_controls, + .num_controls = ARRAY_SIZE(wm8940_snd_controls), + .dapm_widgets = wm8940_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8940_dapm_widgets), + .dapm_routes = wm8940_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8940_dapm_routes), +}; + +static const struct regmap_config wm8940_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = WM8940_MONOMIX, + .reg_defaults = wm8940_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8940_reg_defaults), + + .readable_reg = wm8940_readable_register, + .volatile_reg = wm8940_volatile_register, }; static int wm8940_i2c_probe(struct i2c_client *i2c, @@ -753,8 +785,11 @@ static int wm8940_i2c_probe(struct i2c_client *i2c, if (wm8940 == NULL) return -ENOMEM; + wm8940->regmap = devm_regmap_init_i2c(i2c, &wm8940_regmap); + if (IS_ERR(wm8940->regmap)) + return PTR_ERR(wm8940->regmap); + i2c_set_clientdata(i2c, wm8940); - wm8940->control_type = SND_SOC_I2C; ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8940, &wm8940_dai, 1); diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 543c5c2631b6..97db3b45b411 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -74,7 +74,7 @@ struct wm8962_priv { struct regulator_bulk_data supplies[WM8962_NUM_SUPPLIES]; struct notifier_block disable_nb[WM8962_NUM_SUPPLIES]; -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +#if IS_ENABLED(CONFIG_INPUT) struct input_dev *beep; struct work_struct beep_work; int beep_rate; @@ -2439,7 +2439,20 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8962_CLOCKING_4, WM8962_SYSCLK_RATE_MASK, clocking4); + /* DSPCLK_DIV can be only generated correctly after enabling SYSCLK. + * So we here provisionally enable it and then disable it afterward + * if current bias_level hasn't reached SND_SOC_BIAS_ON. + */ + if (codec->dapm.bias_level != SND_SOC_BIAS_ON) + snd_soc_update_bits(codec, WM8962_CLOCKING2, + WM8962_SYSCLK_ENA_MASK, WM8962_SYSCLK_ENA); + dspclk = snd_soc_read(codec, WM8962_CLOCKING1); + + if (codec->dapm.bias_level != SND_SOC_BIAS_ON) + snd_soc_update_bits(codec, WM8962_CLOCKING2, + WM8962_SYSCLK_ENA_MASK, 0); + if (dspclk < 0) { dev_err(codec->dev, "Failed to read DSPCLK: %d\n", dspclk); return; @@ -3108,7 +3121,7 @@ int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) } EXPORT_SYMBOL_GPL(wm8962_mic_detect); -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +#if IS_ENABLED(CONFIG_INPUT) static int beep_rates[] = { 500, 1000, 2000, 4000, }; diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index a2d01d10a5dd..15f45c7bd833 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -17,6 +17,7 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -27,22 +28,22 @@ #include "wm8974.h" -static const u16 wm8974_reg[WM8974_CACHEREGNUM] = { - 0x0000, 0x0000, 0x0000, 0x0000, - 0x0050, 0x0000, 0x0140, 0x0000, - 0x0000, 0x0000, 0x0000, 0x00ff, - 0x0000, 0x0000, 0x0100, 0x00ff, - 0x0000, 0x0000, 0x012c, 0x002c, - 0x002c, 0x002c, 0x002c, 0x0000, - 0x0032, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, - 0x0038, 0x000b, 0x0032, 0x0000, - 0x0008, 0x000c, 0x0093, 0x00e9, - 0x0000, 0x0000, 0x0000, 0x0000, - 0x0003, 0x0010, 0x0000, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0039, 0x0000, - 0x0000, +static const struct reg_default wm8974_reg_defaults[] = { + { 0, 0x0000 }, { 1, 0x0000 }, { 2, 0x0000 }, { 3, 0x0000 }, + { 4, 0x0050 }, { 5, 0x0000 }, { 6, 0x0140 }, { 7, 0x0000 }, + { 8, 0x0000 }, { 9, 0x0000 }, { 10, 0x0000 }, { 11, 0x00ff }, + { 12, 0x0000 }, { 13, 0x0000 }, { 14, 0x0100 }, { 15, 0x00ff }, + { 16, 0x0000 }, { 17, 0x0000 }, { 18, 0x012c }, { 19, 0x002c }, + { 20, 0x002c }, { 21, 0x002c }, { 22, 0x002c }, { 23, 0x0000 }, + { 24, 0x0032 }, { 25, 0x0000 }, { 26, 0x0000 }, { 27, 0x0000 }, + { 28, 0x0000 }, { 29, 0x0000 }, { 30, 0x0000 }, { 31, 0x0000 }, + { 32, 0x0038 }, { 33, 0x000b }, { 34, 0x0032 }, { 35, 0x0000 }, + { 36, 0x0008 }, { 37, 0x000c }, { 38, 0x0093 }, { 39, 0x00e9 }, + { 40, 0x0000 }, { 41, 0x0000 }, { 42, 0x0000 }, { 43, 0x0000 }, + { 44, 0x0003 }, { 45, 0x0010 }, { 46, 0x0000 }, { 47, 0x0000 }, + { 48, 0x0000 }, { 49, 0x0002 }, { 50, 0x0000 }, { 51, 0x0000 }, + { 52, 0x0000 }, { 53, 0x0000 }, { 54, 0x0039 }, { 55, 0x0000 }, + { 56, 0x0000 }, }; #define WM8974_POWER1_BIASEN 0x08 @@ -514,7 +515,7 @@ static int wm8974_set_bias_level(struct snd_soc_codec *codec, power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN; if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { - snd_soc_cache_sync(codec); + regcache_sync(dev_get_regmap(codec->dev, NULL)); /* Initial cap charge at VMID 5k */ snd_soc_write(codec, WM8974_POWER1, power1 | 0x3); @@ -579,11 +580,20 @@ static int wm8974_resume(struct snd_soc_codec *codec) return 0; } +static const struct regmap_config wm8974_regmap = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = WM8974_MONOMIX, + .reg_defaults = wm8974_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8974_reg_defaults), +}; + static int wm8974_probe(struct snd_soc_codec *codec) { int ret = 0; - ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C); + ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; @@ -613,9 +623,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8974 = { .suspend = wm8974_suspend, .resume = wm8974_resume, .set_bias_level = wm8974_set_bias_level, - .reg_cache_size = ARRAY_SIZE(wm8974_reg), - .reg_word_size = sizeof(u16), - .reg_cache_default = wm8974_reg, .controls = wm8974_snd_controls, .num_controls = ARRAY_SIZE(wm8974_snd_controls), @@ -628,8 +635,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8974 = { static int wm8974_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct regmap *regmap; int ret; + regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8974, &wm8974_dai, 1); diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index 18f2babe1090..271b517911a4 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -1148,7 +1148,7 @@ static struct spi_driver wm8985_spi_driver = { }; #endif -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8985_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1201,7 +1201,7 @@ static int __init wm8985_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8985_i2c_driver); if (ret) { printk(KERN_ERR "Failed to register wm8985 I2C driver: %d\n", @@ -1221,7 +1221,7 @@ module_init(wm8985_modinit); static void __exit wm8985_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8985_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 39b9acceb595..a55e1c2c382e 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -912,7 +912,7 @@ static struct spi_driver wm8988_spi_driver = { }; #endif /* CONFIG_SPI_MASTER */ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8988_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -964,7 +964,7 @@ static struct i2c_driver wm8988_i2c_driver = { static int __init wm8988_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8988_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register WM8988 I2C driver: %d\n", @@ -984,7 +984,7 @@ module_init(wm8988_modinit); static void __exit wm8988_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8988_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 253c88bb7a4c..0ccd4d8d043b 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -17,6 +17,7 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -30,13 +31,12 @@ /* codec private data */ struct wm8990_priv { - enum snd_soc_control_type control_type; + struct regmap *regmap; unsigned int sysclk; unsigned int pcmclk; }; -static int wm8990_volatile_register(struct snd_soc_codec *codec, - unsigned int reg) +static bool wm8990_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { case WM8990_RESET: @@ -46,71 +46,69 @@ static int wm8990_volatile_register(struct snd_soc_codec *codec, } } -static const u16 wm8990_reg[] = { - 0x8990, /* R0 - Reset */ - 0x0000, /* R1 - Power Management (1) */ - 0x6000, /* R2 - Power Management (2) */ - 0x0000, /* R3 - Power Management (3) */ - 0x4050, /* R4 - Audio Interface (1) */ - 0x4000, /* R5 - Audio Interface (2) */ - 0x01C8, /* R6 - Clocking (1) */ - 0x0000, /* R7 - Clocking (2) */ - 0x0040, /* R8 - Audio Interface (3) */ - 0x0040, /* R9 - Audio Interface (4) */ - 0x0004, /* R10 - DAC CTRL */ - 0x00C0, /* R11 - Left DAC Digital Volume */ - 0x00C0, /* R12 - Right DAC Digital Volume */ - 0x0000, /* R13 - Digital Side Tone */ - 0x0100, /* R14 - ADC CTRL */ - 0x00C0, /* R15 - Left ADC Digital Volume */ - 0x00C0, /* R16 - Right ADC Digital Volume */ - 0x0000, /* R17 */ - 0x0000, /* R18 - GPIO CTRL 1 */ - 0x1000, /* R19 - GPIO1 & GPIO2 */ - 0x1010, /* R20 - GPIO3 & GPIO4 */ - 0x1010, /* R21 - GPIO5 & GPIO6 */ - 0x8000, /* R22 - GPIOCTRL 2 */ - 0x0800, /* R23 - GPIO_POL */ - 0x008B, /* R24 - Left Line Input 1&2 Volume */ - 0x008B, /* R25 - Left Line Input 3&4 Volume */ - 0x008B, /* R26 - Right Line Input 1&2 Volume */ - 0x008B, /* R27 - Right Line Input 3&4 Volume */ - 0x0000, /* R28 - Left Output Volume */ - 0x0000, /* R29 - Right Output Volume */ - 0x0066, /* R30 - Line Outputs Volume */ - 0x0022, /* R31 - Out3/4 Volume */ - 0x0079, /* R32 - Left OPGA Volume */ - 0x0079, /* R33 - Right OPGA Volume */ - 0x0003, /* R34 - Speaker Volume */ - 0x0003, /* R35 - ClassD1 */ - 0x0000, /* R36 */ - 0x0100, /* R37 - ClassD3 */ - 0x0079, /* R38 - ClassD4 */ - 0x0000, /* R39 - Input Mixer1 */ - 0x0000, /* R40 - Input Mixer2 */ - 0x0000, /* R41 - Input Mixer3 */ - 0x0000, /* R42 - Input Mixer4 */ - 0x0000, /* R43 - Input Mixer5 */ - 0x0000, /* R44 - Input Mixer6 */ - 0x0000, /* R45 - Output Mixer1 */ - 0x0000, /* R46 - Output Mixer2 */ - 0x0000, /* R47 - Output Mixer3 */ - 0x0000, /* R48 - Output Mixer4 */ - 0x0000, /* R49 - Output Mixer5 */ - 0x0000, /* R50 - Output Mixer6 */ - 0x0180, /* R51 - Out3/4 Mixer */ - 0x0000, /* R52 - Line Mixer1 */ - 0x0000, /* R53 - Line Mixer2 */ - 0x0000, /* R54 - Speaker Mixer */ - 0x0000, /* R55 - Additional Control */ - 0x0000, /* R56 - AntiPOP1 */ - 0x0000, /* R57 - AntiPOP2 */ - 0x0000, /* R58 - MICBIAS */ - 0x0000, /* R59 */ - 0x0008, /* R60 - PLL1 */ - 0x0031, /* R61 - PLL2 */ - 0x0026, /* R62 - PLL3 */ - 0x0000, /* R63 - Driver internal */ +static const struct reg_default wm8990_reg_defaults[] = { + { 1, 0x0000 }, /* R1 - Power Management (1) */ + { 2, 0x6000 }, /* R2 - Power Management (2) */ + { 3, 0x0000 }, /* R3 - Power Management (3) */ + { 4, 0x4050 }, /* R4 - Audio Interface (1) */ + { 5, 0x4000 }, /* R5 - Audio Interface (2) */ + { 6, 0x01C8 }, /* R6 - Clocking (1) */ + { 7, 0x0000 }, /* R7 - Clocking (2) */ + { 8, 0x0040 }, /* R8 - Audio Interface (3) */ + { 9, 0x0040 }, /* R9 - Audio Interface (4) */ + { 10, 0x0004 }, /* R10 - DAC CTRL */ + { 11, 0x00C0 }, /* R11 - Left DAC Digital Volume */ + { 12, 0x00C0 }, /* R12 - Right DAC Digital Volume */ + { 13, 0x0000 }, /* R13 - Digital Side Tone */ + { 14, 0x0100 }, /* R14 - ADC CTRL */ + { 15, 0x00C0 }, /* R15 - Left ADC Digital Volume */ + { 16, 0x00C0 }, /* R16 - Right ADC Digital Volume */ + + { 18, 0x0000 }, /* R18 - GPIO CTRL 1 */ + { 19, 0x1000 }, /* R19 - GPIO1 & GPIO2 */ + { 20, 0x1010 }, /* R20 - GPIO3 & GPIO4 */ + { 21, 0x1010 }, /* R21 - GPIO5 & GPIO6 */ + { 22, 0x8000 }, /* R22 - GPIOCTRL 2 */ + { 23, 0x0800 }, /* R23 - GPIO_POL */ + { 24, 0x008B }, /* R24 - Left Line Input 1&2 Volume */ + { 25, 0x008B }, /* R25 - Left Line Input 3&4 Volume */ + { 26, 0x008B }, /* R26 - Right Line Input 1&2 Volume */ + { 27, 0x008B }, /* R27 - Right Line Input 3&4 Volume */ + { 28, 0x0000 }, /* R28 - Left Output Volume */ + { 29, 0x0000 }, /* R29 - Right Output Volume */ + { 30, 0x0066 }, /* R30 - Line Outputs Volume */ + { 31, 0x0022 }, /* R31 - Out3/4 Volume */ + { 32, 0x0079 }, /* R32 - Left OPGA Volume */ + { 33, 0x0079 }, /* R33 - Right OPGA Volume */ + { 34, 0x0003 }, /* R34 - Speaker Volume */ + { 35, 0x0003 }, /* R35 - ClassD1 */ + + { 37, 0x0100 }, /* R37 - ClassD3 */ + { 38, 0x0079 }, /* R38 - ClassD4 */ + { 39, 0x0000 }, /* R39 - Input Mixer1 */ + { 40, 0x0000 }, /* R40 - Input Mixer2 */ + { 41, 0x0000 }, /* R41 - Input Mixer3 */ + { 42, 0x0000 }, /* R42 - Input Mixer4 */ + { 43, 0x0000 }, /* R43 - Input Mixer5 */ + { 44, 0x0000 }, /* R44 - Input Mixer6 */ + { 45, 0x0000 }, /* R45 - Output Mixer1 */ + { 46, 0x0000 }, /* R46 - Output Mixer2 */ + { 47, 0x0000 }, /* R47 - Output Mixer3 */ + { 48, 0x0000 }, /* R48 - Output Mixer4 */ + { 49, 0x0000 }, /* R49 - Output Mixer5 */ + { 50, 0x0000 }, /* R50 - Output Mixer6 */ + { 51, 0x0180 }, /* R51 - Out3/4 Mixer */ + { 52, 0x0000 }, /* R52 - Line Mixer1 */ + { 53, 0x0000 }, /* R53 - Line Mixer2 */ + { 54, 0x0000 }, /* R54 - Speaker Mixer */ + { 55, 0x0000 }, /* R55 - Additional Control */ + { 56, 0x0000 }, /* R56 - AntiPOP1 */ + { 57, 0x0000 }, /* R57 - AntiPOP2 */ + { 58, 0x0000 }, /* R58 - MICBIAS */ + + { 60, 0x0008 }, /* R60 - PLL1 */ + { 61, 0x0031 }, /* R61 - PLL2 */ + { 62, 0x0026 }, /* R62 - PLL3 */ }; #define wm8990_reset(c) snd_soc_write(c, WM8990_RESET, 0) @@ -376,32 +374,6 @@ SOC_SINGLE("RIN34 Mute Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME, * _DAPM_ Controls */ -static int inmixer_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - u16 reg, fakepower; - - reg = snd_soc_read(w->codec, WM8990_POWER_MANAGEMENT_2); - fakepower = snd_soc_read(w->codec, WM8990_INTDRIVBITS); - - if (fakepower & ((1 << WM8990_INMIXL_PWR_BIT) | - (1 << WM8990_AINLMUX_PWR_BIT))) { - reg |= WM8990_AINL_ENA; - } else { - reg &= ~WM8990_AINL_ENA; - } - - if (fakepower & ((1 << WM8990_INMIXR_PWR_BIT) | - (1 << WM8990_AINRMUX_PWR_BIT))) { - reg |= WM8990_AINR_ENA; - } else { - reg &= ~WM8990_AINR_ENA; - } - snd_soc_write(w->codec, WM8990_POWER_MANAGEMENT_2, reg); - - return 0; -} - static int outmixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -656,6 +628,11 @@ SND_SOC_DAPM_INPUT("RIN1"), SND_SOC_DAPM_INPUT("RIN2"), SND_SOC_DAPM_INPUT("Internal ADC Source"), +SND_SOC_DAPM_SUPPLY("INL", WM8990_POWER_MANAGEMENT_2, WM8990_AINL_ENA_BIT, 0, + NULL, 0), +SND_SOC_DAPM_SUPPLY("INR", WM8990_POWER_MANAGEMENT_2, WM8990_AINR_ENA_BIT, 0, + NULL, 0), + /* DACs */ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8990_POWER_MANAGEMENT_2, WM8990_ADCL_ENA_BIT, 0), @@ -677,26 +654,20 @@ SND_SOC_DAPM_MIXER("RIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN34_ENA_BIT, ARRAY_SIZE(wm8990_dapm_rin34_pga_controls)), /* INMIXL */ -SND_SOC_DAPM_MIXER_E("INMIXL", WM8990_INTDRIVBITS, WM8990_INMIXL_PWR_BIT, 0, +SND_SOC_DAPM_MIXER("INMIXL", SND_SOC_NOPM, 0, 0, &wm8990_dapm_inmixl_controls[0], - ARRAY_SIZE(wm8990_dapm_inmixl_controls), - inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + ARRAY_SIZE(wm8990_dapm_inmixl_controls)), /* AINLMUX */ -SND_SOC_DAPM_MUX_E("AINLMUX", WM8990_INTDRIVBITS, WM8990_AINLMUX_PWR_BIT, 0, - &wm8990_dapm_ainlmux_controls, inmixer_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_MUX("AINLMUX", SND_SOC_NOPM, 0, 0, &wm8990_dapm_ainlmux_controls), /* INMIXR */ -SND_SOC_DAPM_MIXER_E("INMIXR", WM8990_INTDRIVBITS, WM8990_INMIXR_PWR_BIT, 0, +SND_SOC_DAPM_MIXER("INMIXR", SND_SOC_NOPM, 0, 0, &wm8990_dapm_inmixr_controls[0], - ARRAY_SIZE(wm8990_dapm_inmixr_controls), - inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + ARRAY_SIZE(wm8990_dapm_inmixr_controls)), /* AINRMUX */ -SND_SOC_DAPM_MUX_E("AINRMUX", WM8990_INTDRIVBITS, WM8990_AINRMUX_PWR_BIT, 0, - &wm8990_dapm_ainrmux_controls, inmixer_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_MUX("AINRMUX", SND_SOC_NOPM, 0, 0, &wm8990_dapm_ainrmux_controls), /* Output Side */ /* DACs */ @@ -787,7 +758,7 @@ SND_SOC_DAPM_OUTPUT("RON"), SND_SOC_DAPM_OUTPUT("Internal DAC Sink"), }; -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route wm8990_dapm_routes[] = { /* Make DACs turn on when playing even if not mixed into any outputs */ {"Internal DAC Sink", NULL, "Left DAC"}, {"Internal DAC Sink", NULL, "Right DAC"}, @@ -796,6 +767,11 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Left ADC", NULL, "Internal ADC Source"}, {"Right ADC", NULL, "Internal ADC Source"}, + {"AINLMUX", NULL, "INL"}, + {"INMIXL", NULL, "INL"}, + {"AINRMUX", NULL, "INR"}, + {"INMIXR", NULL, "INR"}, + /* Input Side */ /* LIN12 PGA */ {"LIN12 PGA", "LIN1 Switch", "LIN1"}, @@ -912,18 +888,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"RON", NULL, "RONMIX"}, }; -static int wm8990_add_widgets(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, wm8990_dapm_widgets, - ARRAY_SIZE(wm8990_dapm_widgets)); - /* set up the WM8990 audio map */ - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - - return 0; -} - /* PLL divisors */ struct _pll_div { u32 div2; @@ -1148,6 +1112,7 @@ static int wm8990_mute(struct snd_soc_dai *dai, int mute) static int wm8990_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct wm8990_priv *wm8990 = snd_soc_codec_get_drvdata(codec); int ret; switch (level) { @@ -1162,7 +1127,7 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { - ret = snd_soc_cache_sync(codec); + ret = regcache_sync(wm8990->regmap); if (ret < 0) { dev_err(codec->dev, "Failed to sync cache: %d\n", ret); return ret; @@ -1259,6 +1224,8 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec, /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ snd_soc_write(codec, WM8990_ANTIPOP2, 0x0); + + regcache_mark_dirty(wm8990->regmap); break; } @@ -1327,7 +1294,7 @@ static int wm8990_probe(struct snd_soc_codec *codec) { int ret; - ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); + ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP); if (ret < 0) { printk(KERN_ERR "wm8990: failed to set cache I/O: %d\n", ret); return ret; @@ -1350,10 +1317,6 @@ static int wm8990_probe(struct snd_soc_codec *codec) snd_soc_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8)); snd_soc_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8)); - snd_soc_add_codec_controls(codec, wm8990_snd_controls, - ARRAY_SIZE(wm8990_snd_controls)); - wm8990_add_widgets(codec); - return 0; } @@ -1370,13 +1333,25 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8990 = { .suspend = wm8990_suspend, .resume = wm8990_resume, .set_bias_level = wm8990_set_bias_level, - .reg_cache_size = ARRAY_SIZE(wm8990_reg), - .reg_word_size = sizeof(u16), - .reg_cache_default = wm8990_reg, - .volatile_register = wm8990_volatile_register, + .controls = wm8990_snd_controls, + .num_controls = ARRAY_SIZE(wm8990_snd_controls), + .dapm_widgets = wm8990_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8990_dapm_widgets), + .dapm_routes = wm8990_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8990_dapm_routes), +}; + +static const struct regmap_config wm8990_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = WM8990_PLL3, + .volatile_reg = wm8990_volatile_register, + .reg_defaults = wm8990_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8990_reg_defaults), + .cache_type = REGCACHE_RBTREE, }; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) static int wm8990_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1418,29 +1393,8 @@ static struct i2c_driver wm8990_i2c_driver = { .remove = wm8990_i2c_remove, .id_table = wm8990_i2c_id, }; -#endif -static int __init wm8990_modinit(void) -{ - int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - ret = i2c_add_driver(&wm8990_i2c_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register wm8990 I2C driver: %d\n", - ret); - } -#endif - return ret; -} -module_init(wm8990_modinit); - -static void __exit wm8990_exit(void) -{ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_del_driver(&wm8990_i2c_driver); -#endif -} -module_exit(wm8990_exit); +module_i2c_driver(wm8990_i2c_driver); MODULE_DESCRIPTION("ASoC WM8990 driver"); MODULE_AUTHOR("Liam Girdwood"); diff --git a/sound/soc/codecs/wm8990.h b/sound/soc/codecs/wm8990.h index 77c98a4bfe9c..0e9c78040c4c 100644 --- a/sound/soc/codecs/wm8990.h +++ b/sound/soc/codecs/wm8990.h @@ -78,7 +78,6 @@ #define WM8990_PLL1 0x3C #define WM8990_PLL2 0x3D #define WM8990_PLL3 0x3E -#define WM8990_INTDRIVBITS 0x3F #define WM8990_EXT_ACCESS_ENA 0x75 #define WM8990_EXT_CTL1 0x7a @@ -818,14 +817,6 @@ */ #define WM8990_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */ -/* - * R63 (0x3F) - Internal Driver Bits - */ -#define WM8990_INMIXL_PWR_BIT 0 -#define WM8990_AINLMUX_PWR_BIT 1 -#define WM8990_INMIXR_PWR_BIT 2 -#define WM8990_AINRMUX_PWR_BIT 3 - #define WM8990_MCLK_DIV 0 #define WM8990_DACCLK_DIV 1 #define WM8990_ADCCLK_DIV 2 diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index 3a39df7a3829..dba0306c42a5 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -18,6 +18,7 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -31,77 +32,84 @@ #include "wm8991.h" struct wm8991_priv { - enum snd_soc_control_type control_type; + struct regmap *regmap; unsigned int pcmclk; }; -static const u16 wm8991_reg_defs[] = { - 0x8991, /* R0 - Reset */ - 0x0000, /* R1 - Power Management (1) */ - 0x6000, /* R2 - Power Management (2) */ - 0x0000, /* R3 - Power Management (3) */ - 0x4050, /* R4 - Audio Interface (1) */ - 0x4000, /* R5 - Audio Interface (2) */ - 0x01C8, /* R6 - Clocking (1) */ - 0x0000, /* R7 - Clocking (2) */ - 0x0040, /* R8 - Audio Interface (3) */ - 0x0040, /* R9 - Audio Interface (4) */ - 0x0004, /* R10 - DAC CTRL */ - 0x00C0, /* R11 - Left DAC Digital Volume */ - 0x00C0, /* R12 - Right DAC Digital Volume */ - 0x0000, /* R13 - Digital Side Tone */ - 0x0100, /* R14 - ADC CTRL */ - 0x00C0, /* R15 - Left ADC Digital Volume */ - 0x00C0, /* R16 - Right ADC Digital Volume */ - 0x0000, /* R17 */ - 0x0000, /* R18 - GPIO CTRL 1 */ - 0x1000, /* R19 - GPIO1 & GPIO2 */ - 0x1010, /* R20 - GPIO3 & GPIO4 */ - 0x1010, /* R21 - GPIO5 & GPIO6 */ - 0x8000, /* R22 - GPIOCTRL 2 */ - 0x0800, /* R23 - GPIO_POL */ - 0x008B, /* R24 - Left Line Input 1&2 Volume */ - 0x008B, /* R25 - Left Line Input 3&4 Volume */ - 0x008B, /* R26 - Right Line Input 1&2 Volume */ - 0x008B, /* R27 - Right Line Input 3&4 Volume */ - 0x0000, /* R28 - Left Output Volume */ - 0x0000, /* R29 - Right Output Volume */ - 0x0066, /* R30 - Line Outputs Volume */ - 0x0022, /* R31 - Out3/4 Volume */ - 0x0079, /* R32 - Left OPGA Volume */ - 0x0079, /* R33 - Right OPGA Volume */ - 0x0003, /* R34 - Speaker Volume */ - 0x0003, /* R35 - ClassD1 */ - 0x0000, /* R36 */ - 0x0100, /* R37 - ClassD3 */ - 0x0000, /* R38 */ - 0x0000, /* R39 - Input Mixer1 */ - 0x0000, /* R40 - Input Mixer2 */ - 0x0000, /* R41 - Input Mixer3 */ - 0x0000, /* R42 - Input Mixer4 */ - 0x0000, /* R43 - Input Mixer5 */ - 0x0000, /* R44 - Input Mixer6 */ - 0x0000, /* R45 - Output Mixer1 */ - 0x0000, /* R46 - Output Mixer2 */ - 0x0000, /* R47 - Output Mixer3 */ - 0x0000, /* R48 - Output Mixer4 */ - 0x0000, /* R49 - Output Mixer5 */ - 0x0000, /* R50 - Output Mixer6 */ - 0x0180, /* R51 - Out3/4 Mixer */ - 0x0000, /* R52 - Line Mixer1 */ - 0x0000, /* R53 - Line Mixer2 */ - 0x0000, /* R54 - Speaker Mixer */ - 0x0000, /* R55 - Additional Control */ - 0x0000, /* R56 - AntiPOP1 */ - 0x0000, /* R57 - AntiPOP2 */ - 0x0000, /* R58 - MICBIAS */ - 0x0000, /* R59 */ - 0x0008, /* R60 - PLL1 */ - 0x0031, /* R61 - PLL2 */ - 0x0026, /* R62 - PLL3 */ +static const struct reg_default wm8991_reg_defaults[] = { + { 1, 0x0000 }, /* R1 - Power Management (1) */ + { 2, 0x6000 }, /* R2 - Power Management (2) */ + { 3, 0x0000 }, /* R3 - Power Management (3) */ + { 4, 0x4050 }, /* R4 - Audio Interface (1) */ + { 5, 0x4000 }, /* R5 - Audio Interface (2) */ + { 6, 0x01C8 }, /* R6 - Clocking (1) */ + { 7, 0x0000 }, /* R7 - Clocking (2) */ + { 8, 0x0040 }, /* R8 - Audio Interface (3) */ + { 9, 0x0040 }, /* R9 - Audio Interface (4) */ + { 10, 0x0004 }, /* R10 - DAC CTRL */ + { 11, 0x00C0 }, /* R11 - Left DAC Digital Volume */ + { 12, 0x00C0 }, /* R12 - Right DAC Digital Volume */ + { 13, 0x0000 }, /* R13 - Digital Side Tone */ + { 14, 0x0100 }, /* R14 - ADC CTRL */ + { 15, 0x00C0 }, /* R15 - Left ADC Digital Volume */ + { 16, 0x00C0 }, /* R16 - Right ADC Digital Volume */ + + { 18, 0x0000 }, /* R18 - GPIO CTRL 1 */ + { 19, 0x1000 }, /* R19 - GPIO1 & GPIO2 */ + { 20, 0x1010 }, /* R20 - GPIO3 & GPIO4 */ + { 21, 0x1010 }, /* R21 - GPIO5 & GPIO6 */ + { 22, 0x8000 }, /* R22 - GPIOCTRL 2 */ + { 23, 0x0800 }, /* R23 - GPIO_POL */ + { 24, 0x008B }, /* R24 - Left Line Input 1&2 Volume */ + { 25, 0x008B }, /* R25 - Left Line Input 3&4 Volume */ + { 26, 0x008B }, /* R26 - Right Line Input 1&2 Volume */ + { 27, 0x008B }, /* R27 - Right Line Input 3&4 Volume */ + { 28, 0x0000 }, /* R28 - Left Output Volume */ + { 29, 0x0000 }, /* R29 - Right Output Volume */ + { 30, 0x0066 }, /* R30 - Line Outputs Volume */ + { 31, 0x0022 }, /* R31 - Out3/4 Volume */ + { 32, 0x0079 }, /* R32 - Left OPGA Volume */ + { 33, 0x0079 }, /* R33 - Right OPGA Volume */ + { 34, 0x0003 }, /* R34 - Speaker Volume */ + { 35, 0x0003 }, /* R35 - ClassD1 */ + + { 37, 0x0100 }, /* R37 - ClassD3 */ + + { 39, 0x0000 }, /* R39 - Input Mixer1 */ + { 40, 0x0000 }, /* R40 - Input Mixer2 */ + { 41, 0x0000 }, /* R41 - Input Mixer3 */ + { 42, 0x0000 }, /* R42 - Input Mixer4 */ + { 43, 0x0000 }, /* R43 - Input Mixer5 */ + { 44, 0x0000 }, /* R44 - Input Mixer6 */ + { 45, 0x0000 }, /* R45 - Output Mixer1 */ + { 46, 0x0000 }, /* R46 - Output Mixer2 */ + { 47, 0x0000 }, /* R47 - Output Mixer3 */ + { 48, 0x0000 }, /* R48 - Output Mixer4 */ + { 49, 0x0000 }, /* R49 - Output Mixer5 */ + { 50, 0x0000 }, /* R50 - Output Mixer6 */ + { 51, 0x0180 }, /* R51 - Out3/4 Mixer */ + { 52, 0x0000 }, /* R52 - Line Mixer1 */ + { 53, 0x0000 }, /* R53 - Line Mixer2 */ + { 54, 0x0000 }, /* R54 - Speaker Mixer */ + { 55, 0x0000 }, /* R55 - Additional Control */ + { 56, 0x0000 }, /* R56 - AntiPOP1 */ + { 57, 0x0000 }, /* R57 - AntiPOP2 */ + { 58, 0x0000 }, /* R58 - MICBIAS */ + + { 60, 0x0008 }, /* R60 - PLL1 */ + { 61, 0x0031 }, /* R61 - PLL2 */ + { 62, 0x0026 }, /* R62 - PLL3 */ }; -#define wm8991_reset(c) snd_soc_write(c, WM8991_RESET, 0) +static bool wm8991_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8991_RESET: + return true; + default: + return false; + } +} static const unsigned int rec_mix_tlv[] = { TLV_DB_RANGE_HEAD(1), @@ -374,30 +382,6 @@ static const struct snd_kcontrol_new wm8991_snd_controls[] = { /* * _DAPM_ Controls */ -static int inmixer_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - u16 reg, fakepower; - - reg = snd_soc_read(w->codec, WM8991_POWER_MANAGEMENT_2); - fakepower = snd_soc_read(w->codec, WM8991_INTDRIVBITS); - - if (fakepower & ((1 << WM8991_INMIXL_PWR_BIT) | - (1 << WM8991_AINLMUX_PWR_BIT))) - reg |= WM8991_AINL_ENA; - else - reg &= ~WM8991_AINL_ENA; - - if (fakepower & ((1 << WM8991_INMIXR_PWR_BIT) | - (1 << WM8991_AINRMUX_PWR_BIT))) - reg |= WM8991_AINR_ENA; - else - reg &= ~WM8991_AINR_ENA; - - snd_soc_write(w->codec, WM8991_POWER_MANAGEMENT_2, reg); - return 0; -} - static int outmixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -655,6 +639,11 @@ static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = { SND_SOC_DAPM_INPUT("RIN2"), SND_SOC_DAPM_INPUT("Internal ADC Source"), + SND_SOC_DAPM_SUPPLY("INL", WM8991_POWER_MANAGEMENT_2, + WM8991_AINL_ENA_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("INR", WM8991_POWER_MANAGEMENT_2, + WM8991_AINR_ENA_BIT, 0, NULL, 0), + /* DACs */ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8991_POWER_MANAGEMENT_2, WM8991_ADCL_ENA_BIT, 0), @@ -676,26 +665,22 @@ static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = { ARRAY_SIZE(wm8991_dapm_rin34_pga_controls)), /* INMIXL */ - SND_SOC_DAPM_MIXER_E("INMIXL", WM8991_INTDRIVBITS, WM8991_INMIXL_PWR_BIT, 0, + SND_SOC_DAPM_MIXER("INMIXL", SND_SOC_NOPM, 0, 0, &wm8991_dapm_inmixl_controls[0], - ARRAY_SIZE(wm8991_dapm_inmixl_controls), - inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + ARRAY_SIZE(wm8991_dapm_inmixl_controls)), /* AINLMUX */ - SND_SOC_DAPM_MUX_E("AINLMUX", WM8991_INTDRIVBITS, WM8991_AINLMUX_PWR_BIT, 0, - &wm8991_dapm_ainlmux_controls, inmixer_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("AINLMUX", SND_SOC_NOPM, 0, 0, + &wm8991_dapm_ainlmux_controls), /* INMIXR */ - SND_SOC_DAPM_MIXER_E("INMIXR", WM8991_INTDRIVBITS, WM8991_INMIXR_PWR_BIT, 0, + SND_SOC_DAPM_MIXER("INMIXR", SND_SOC_NOPM, 0, 0, &wm8991_dapm_inmixr_controls[0], - ARRAY_SIZE(wm8991_dapm_inmixr_controls), - inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + ARRAY_SIZE(wm8991_dapm_inmixr_controls)), /* AINRMUX */ - SND_SOC_DAPM_MUX_E("AINRMUX", WM8991_INTDRIVBITS, WM8991_AINRMUX_PWR_BIT, 0, - &wm8991_dapm_ainrmux_controls, inmixer_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("AINRMUX", SND_SOC_NOPM, 0, 0, + &wm8991_dapm_ainrmux_controls), /* Output Side */ /* DACs */ @@ -787,7 +772,7 @@ static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("Internal DAC Sink"), }; -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route wm8991_dapm_routes[] = { /* Make DACs turn on when playing even if not mixed into any outputs */ {"Internal DAC Sink", NULL, "Left DAC"}, {"Internal DAC Sink", NULL, "Right DAC"}, @@ -797,6 +782,10 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Right ADC", NULL, "Internal ADC Source"}, /* Input Side */ + {"INMIXL", NULL, "INL"}, + {"AINLMUX", NULL, "INL"}, + {"INMIXR", NULL, "INR"}, + {"AINRMUX", NULL, "INR"}, /* LIN12 PGA */ {"LIN12 PGA", "LIN1 Switch", "LIN1"}, {"LIN12 PGA", "LIN2 Switch", "LIN2"}, @@ -1129,6 +1118,7 @@ static int wm8991_mute(struct snd_soc_dai *dai, int mute) static int wm8991_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct wm8991_priv *wm8991 = snd_soc_codec_get_drvdata(codec); u16 val; switch (level) { @@ -1144,7 +1134,7 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { - snd_soc_cache_sync(codec); + regcache_sync(wm8991->regmap); /* Enable all output discharge bits */ snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE | WM8991_DIS_RLINE | WM8991_DIS_OUT3 | @@ -1232,7 +1222,7 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec, /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ snd_soc_write(codec, WM8991_ANTIPOP2, 0x0); - codec->cache_sync = 1; + regcache_mark_dirty(wm8991->regmap); break; } @@ -1266,44 +1256,14 @@ static int wm8991_probe(struct snd_soc_codec *codec) wm8991 = snd_soc_codec_get_drvdata(codec); - ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8991->control_type); + ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP); if (ret < 0) { dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret); return ret; } - ret = wm8991_reset(codec); - if (ret < 0) { - dev_err(codec->dev, "Failed to issue reset\n"); - return ret; - } - wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_update_bits(codec, WM8991_AUDIO_INTERFACE_4, - WM8991_ALRCGPIO1, WM8991_ALRCGPIO1); - - snd_soc_update_bits(codec, WM8991_GPIO1_GPIO2, - WM8991_GPIO1_SEL_MASK, 1); - - snd_soc_update_bits(codec, WM8991_POWER_MANAGEMENT_1, - WM8991_VREF_ENA | WM8991_VMID_MODE_MASK, - WM8991_VREF_ENA | WM8991_VMID_MODE_MASK); - - snd_soc_update_bits(codec, WM8991_POWER_MANAGEMENT_2, - WM8991_OPCLK_ENA, WM8991_OPCLK_ENA); - - snd_soc_write(codec, WM8991_DAC_CTRL, 0); - snd_soc_write(codec, WM8991_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8)); - snd_soc_write(codec, WM8991_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8)); - - snd_soc_add_codec_controls(codec, wm8991_snd_controls, - ARRAY_SIZE(wm8991_snd_controls)); - - snd_soc_dapm_new_controls(&codec->dapm, wm8991_dapm_widgets, - ARRAY_SIZE(wm8991_dapm_widgets)); - snd_soc_dapm_add_routes(&codec->dapm, audio_map, - ARRAY_SIZE(audio_map)); return 0; } @@ -1352,24 +1312,77 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8991 = { .suspend = wm8991_suspend, .resume = wm8991_resume, .set_bias_level = wm8991_set_bias_level, - .reg_cache_size = WM8991_MAX_REGISTER + 1, - .reg_word_size = sizeof(u16), - .reg_cache_default = wm8991_reg_defs + .controls = wm8991_snd_controls, + .num_controls = ARRAY_SIZE(wm8991_snd_controls), + .dapm_widgets = wm8991_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8991_dapm_widgets), + .dapm_routes = wm8991_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8991_dapm_routes), +}; + +static const struct regmap_config wm8991_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = WM8991_PLL3, + .volatile_reg = wm8991_volatile, + .reg_defaults = wm8991_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8991_reg_defaults), + .cache_type = REGCACHE_RBTREE, }; static int wm8991_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm8991_priv *wm8991; + unsigned int val; int ret; wm8991 = devm_kzalloc(&i2c->dev, sizeof(*wm8991), GFP_KERNEL); if (!wm8991) return -ENOMEM; - wm8991->control_type = SND_SOC_I2C; + wm8991->regmap = devm_regmap_init_i2c(i2c, &wm8991_regmap); + if (IS_ERR(wm8991->regmap)) + return PTR_ERR(wm8991->regmap); + i2c_set_clientdata(i2c, wm8991); + ret = regmap_read(wm8991->regmap, WM8991_RESET, &val); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read device ID: %d\n", ret); + return ret; + } + if (val != 0x8991) { + dev_err(&i2c->dev, "Device with ID %x is not a WM8991\n", val); + return -EINVAL; + } + + ret = regmap_write(wm8991->regmap, WM8991_RESET, 0); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); + return ret; + } + + regmap_update_bits(wm8991->regmap, WM8991_AUDIO_INTERFACE_4, + WM8991_ALRCGPIO1, WM8991_ALRCGPIO1); + + regmap_update_bits(wm8991->regmap, WM8991_GPIO1_GPIO2, + WM8991_GPIO1_SEL_MASK, 1); + + regmap_update_bits(wm8991->regmap, WM8991_POWER_MANAGEMENT_1, + WM8991_VREF_ENA | WM8991_VMID_MODE_MASK, + WM8991_VREF_ENA | WM8991_VMID_MODE_MASK); + + regmap_update_bits(wm8991->regmap, WM8991_POWER_MANAGEMENT_2, + WM8991_OPCLK_ENA, WM8991_OPCLK_ENA); + + regmap_write(wm8991->regmap, WM8991_DAC_CTRL, 0); + regmap_write(wm8991->regmap, WM8991_LEFT_OUTPUT_VOLUME, + 0x50 | (1<<8)); + regmap_write(wm8991->regmap, WM8991_RIGHT_OUTPUT_VOLUME, + 0x50 | (1<<8)); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8991, &wm8991_dai, 1); diff --git a/sound/soc/codecs/wm8991.h b/sound/soc/codecs/wm8991.h index 07707d8d7e20..08ed383303c0 100644 --- a/sound/soc/codecs/wm8991.h +++ b/sound/soc/codecs/wm8991.h @@ -76,7 +76,6 @@ #define WM8991_PLL1 0x3C #define WM8991_PLL2 0x3D #define WM8991_PLL3 0x3E -#define WM8991_INTDRIVBITS 0x3F #define WM8991_REGISTER_COUNT 60 #define WM8991_MAX_REGISTER 0x3F @@ -807,14 +806,6 @@ */ #define WM8991_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */ -/* - * R63 (0x3F) - Internal Driver Bits - */ -#define WM8991_INMIXL_PWR_BIT 0 -#define WM8991_AINLMUX_PWR_BIT 1 -#define WM8991_INMIXR_PWR_BIT 2 -#define WM8991_AINRMUX_PWR_BIT 3 - #define WM8991_MCLK_DIV 0 #define WM8991_DACCLK_DIV 1 #define WM8991_ADCCLK_DIV 2 diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 86426a117b07..b9be9cbc4603 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -4077,12 +4077,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, wm8994_temp_shut, "Thermal shutdown", codec); - ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE, - wm_hubs_dcs_done, "DC servo done", - &wm8994->hubs); - if (ret == 0) - wm8994->hubs.dcs_done_irq = true; - switch (control->type) { case WM8994: if (wm8994->micdet_irq) { @@ -4313,6 +4307,11 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) } wm_hubs_add_analogue_routes(codec, 0, 0); + ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE, + wm_hubs_dcs_done, "DC servo done", + &wm8994->hubs); + if (ret == 0) + wm8994->hubs.dcs_done_irq = true; snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); switch (control->type) { diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index da2899e6c401..4300caff1783 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -2293,7 +2293,7 @@ static struct spi_driver wm8995_spi_driver = { }; #endif -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8995_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -2350,7 +2350,7 @@ static int __init wm8995_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8995_i2c_driver); if (ret) { printk(KERN_ERR "Failed to register wm8995 I2C driver: %d\n", @@ -2371,7 +2371,7 @@ module_init(wm8995_modinit); static void __exit wm8995_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8995_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index 1392bb3c9254..555115ee2159 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -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; 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..444626fcab40 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1286,6 +1286,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) reg = wm_adsp_region_to_reg(mem, reg); reg += offset; + break; } } @@ -1468,19 +1469,23 @@ static int wm_adsp2_ena(struct wm_adsp *dsp) unsigned int val; int ret, count; - ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_SYS_ENA, ADSP2_SYS_ENA); + ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, + ADSP2_SYS_ENA, ADSP2_SYS_ENA); if (ret != 0) return ret; /* Wait for the RAM to start, should be near instantaneous */ - count = 0; - do { + for (count = 0; count < 10; ++count) { ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); if (ret != 0) return ret; - } while (!(val & ADSP2_RAM_RDY) && ++count < 10); + + if (val & ADSP2_RAM_RDY) + break; + + msleep(1); + } if (!(val & ADSP2_RAM_RDY)) { adsp_err(dsp, "Failed to start DSP RAM\n"); @@ -1488,112 +1493,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: @@ -1664,6 +1710,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..70ff3772079f 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -28,14 +28,11 @@ #include "davinci-pcm.h" #include "davinci-i2s.h" -#include "davinci-mcasp.h" struct snd_soc_card_drvdata_davinci { unsigned sysclk; }; -#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \ - SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF) static int evm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -48,16 +45,6 @@ static int evm_hw_params(struct snd_pcm_substream *substream, unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *) snd_soc_card_get_drvdata(soc_card))->sysclk; - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT); - if (ret < 0) - return ret; - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT); - if (ret < 0) - return ret; - /* set the codec system clock */ ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT); if (ret < 0) @@ -71,24 +58,10 @@ static int evm_hw_params(struct snd_pcm_substream *substream, return 0; } -static int evm_spdif_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - /* set cpu DAI configuration */ - return snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT); -} - static struct snd_soc_ops evm_ops = { .hw_params = evm_hw_params, }; -static struct snd_soc_ops evm_spdif_ops = { - .hw_params = evm_spdif_hw_params, -}; - /* davinci-evm machine dapm widgets */ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), @@ -165,6 +138,8 @@ static struct snd_soc_dai_link dm6446_evm_dai = { .platform_name = "davinci-mcbsp", .init = evm_aic3x_init, .ops = &evm_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, }; static struct snd_soc_dai_link dm355_evm_dai = { @@ -176,6 +151,8 @@ static struct snd_soc_dai_link dm355_evm_dai = { .platform_name = "davinci-mcbsp.1", .init = evm_aic3x_init, .ops = &evm_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, }; static struct snd_soc_dai_link dm365_evm_dai = { @@ -184,10 +161,12 @@ static struct snd_soc_dai_link dm365_evm_dai = { .stream_name = "AIC3X", .cpu_dai_name = "davinci-mcbsp", .codec_dai_name = "tlv320aic3x-hifi", - .init = evm_aic3x_init, .codec_name = "tlv320aic3x-codec.1-0018", - .ops = &evm_ops, .platform_name = "davinci-mcbsp", + .init = evm_aic3x_init, + .ops = &evm_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, #elif defined(CONFIG_SND_DM365_VOICE_CODEC) .name = "Voice Codec - CQ93VC", .stream_name = "CQ93", @@ -208,6 +187,8 @@ static struct snd_soc_dai_link dm6467_evm_dai[] = { .codec_name = "tlv320aic3x-codec.0-001a", .init = evm_aic3x_init, .ops = &evm_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, }, { .name = "McASP", @@ -216,7 +197,8 @@ static struct snd_soc_dai_link dm6467_evm_dai[] = { .codec_dai_name = "dit-hifi", .codec_name = "spdif_dit", .platform_name = "davinci-mcasp.1", - .ops = &evm_spdif_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, }, }; @@ -229,6 +211,8 @@ static struct snd_soc_dai_link da830_evm_dai = { .platform_name = "davinci-mcasp.1", .init = evm_aic3x_init, .ops = &evm_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, }; static struct snd_soc_dai_link da850_evm_dai = { @@ -240,6 +224,8 @@ static struct snd_soc_dai_link da850_evm_dai = { .platform_name = "davinci-mcasp.0", .init = evm_aic3x_init, .ops = &evm_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, }; /* davinci dm6446 evm audio machine driver */ @@ -336,6 +322,8 @@ static struct snd_soc_dai_link evm_dai_tlv320aic3x = { .codec_dai_name = "tlv320aic3x-hifi", .ops = &evm_ops, .init = evm_aic3x_init, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, }; static const struct of_device_id davinci_evm_dt_ids[] = { diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 71e14bb3a8cd..b7858bfa0295 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -21,6 +21,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/clk.h> #include <linux/pm_runtime.h> #include <linux/of.h> #include <linux/of_platform.h> @@ -31,351 +32,147 @@ #include <sound/pcm_params.h> #include <sound/initval.h> #include <sound/soc.h> +#include <sound/dmaengine_pcm.h> #include "davinci-pcm.h" #include "davinci-mcasp.h" -/* - * McASP register definitions - */ -#define DAVINCI_MCASP_PID_REG 0x00 -#define DAVINCI_MCASP_PWREMUMGT_REG 0x04 - -#define DAVINCI_MCASP_PFUNC_REG 0x10 -#define DAVINCI_MCASP_PDIR_REG 0x14 -#define DAVINCI_MCASP_PDOUT_REG 0x18 -#define DAVINCI_MCASP_PDSET_REG 0x1c - -#define DAVINCI_MCASP_PDCLR_REG 0x20 - -#define DAVINCI_MCASP_TLGC_REG 0x30 -#define DAVINCI_MCASP_TLMR_REG 0x34 - -#define DAVINCI_MCASP_GBLCTL_REG 0x44 -#define DAVINCI_MCASP_AMUTE_REG 0x48 -#define DAVINCI_MCASP_LBCTL_REG 0x4c - -#define DAVINCI_MCASP_TXDITCTL_REG 0x50 - -#define DAVINCI_MCASP_GBLCTLR_REG 0x60 -#define DAVINCI_MCASP_RXMASK_REG 0x64 -#define DAVINCI_MCASP_RXFMT_REG 0x68 -#define DAVINCI_MCASP_RXFMCTL_REG 0x6c - -#define DAVINCI_MCASP_ACLKRCTL_REG 0x70 -#define DAVINCI_MCASP_AHCLKRCTL_REG 0x74 -#define DAVINCI_MCASP_RXTDM_REG 0x78 -#define DAVINCI_MCASP_EVTCTLR_REG 0x7c - -#define DAVINCI_MCASP_RXSTAT_REG 0x80 -#define DAVINCI_MCASP_RXTDMSLOT_REG 0x84 -#define DAVINCI_MCASP_RXCLKCHK_REG 0x88 -#define DAVINCI_MCASP_REVTCTL_REG 0x8c - -#define DAVINCI_MCASP_GBLCTLX_REG 0xa0 -#define DAVINCI_MCASP_TXMASK_REG 0xa4 -#define DAVINCI_MCASP_TXFMT_REG 0xa8 -#define DAVINCI_MCASP_TXFMCTL_REG 0xac - -#define DAVINCI_MCASP_ACLKXCTL_REG 0xb0 -#define DAVINCI_MCASP_AHCLKXCTL_REG 0xb4 -#define DAVINCI_MCASP_TXTDM_REG 0xb8 -#define DAVINCI_MCASP_EVTCTLX_REG 0xbc - -#define DAVINCI_MCASP_TXSTAT_REG 0xc0 -#define DAVINCI_MCASP_TXTDMSLOT_REG 0xc4 -#define DAVINCI_MCASP_TXCLKCHK_REG 0xc8 -#define DAVINCI_MCASP_XEVTCTL_REG 0xcc - -/* Left(even TDM Slot) Channel Status Register File */ -#define DAVINCI_MCASP_DITCSRA_REG 0x100 -/* Right(odd TDM slot) Channel Status Register File */ -#define DAVINCI_MCASP_DITCSRB_REG 0x118 -/* Left(even TDM slot) User Data Register File */ -#define DAVINCI_MCASP_DITUDRA_REG 0x130 -/* Right(odd TDM Slot) User Data Register File */ -#define DAVINCI_MCASP_DITUDRB_REG 0x148 - -/* Serializer n Control Register */ -#define DAVINCI_MCASP_XRSRCTL_BASE_REG 0x180 -#define DAVINCI_MCASP_XRSRCTL_REG(n) (DAVINCI_MCASP_XRSRCTL_BASE_REG + \ - (n << 2)) - -/* Transmit Buffer for Serializer n */ -#define DAVINCI_MCASP_TXBUF_REG 0x200 -/* Receive Buffer for Serializer n */ -#define DAVINCI_MCASP_RXBUF_REG 0x280 - -/* McASP FIFO Registers */ -#define DAVINCI_MCASP_WFIFOCTL (0x1010) -#define DAVINCI_MCASP_WFIFOSTS (0x1014) -#define DAVINCI_MCASP_RFIFOCTL (0x1018) -#define DAVINCI_MCASP_RFIFOSTS (0x101C) -#define MCASP_VER3_WFIFOCTL (0x1000) -#define MCASP_VER3_WFIFOSTS (0x1004) -#define MCASP_VER3_RFIFOCTL (0x1008) -#define MCASP_VER3_RFIFOSTS (0x100C) - -/* - * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management - * Register Bits - */ -#define MCASP_FREE BIT(0) -#define MCASP_SOFT BIT(1) - -/* - * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits - */ -#define AXR(n) (1<<n) -#define PFUNC_AMUTE BIT(25) -#define ACLKX BIT(26) -#define AHCLKX BIT(27) -#define AFSX BIT(28) -#define ACLKR BIT(29) -#define AHCLKR BIT(30) -#define AFSR BIT(31) - -/* - * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits - */ -#define AXR(n) (1<<n) -#define PDIR_AMUTE BIT(25) -#define ACLKX BIT(26) -#define AHCLKX BIT(27) -#define AFSX BIT(28) -#define ACLKR BIT(29) -#define AHCLKR BIT(30) -#define AFSR BIT(31) - -/* - * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits - */ -#define DITEN BIT(0) /* Transmit DIT mode enable/disable */ -#define VA BIT(2) -#define VB BIT(3) - -/* - * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits - */ -#define TXROT(val) (val) -#define TXSEL BIT(3) -#define TXSSZ(val) (val<<4) -#define TXPBIT(val) (val<<8) -#define TXPAD(val) (val<<13) -#define TXORD BIT(15) -#define FSXDLY(val) (val<<16) - -/* - * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits - */ -#define RXROT(val) (val) -#define RXSEL BIT(3) -#define RXSSZ(val) (val<<4) -#define RXPBIT(val) (val<<8) -#define RXPAD(val) (val<<13) -#define RXORD BIT(15) -#define FSRDLY(val) (val<<16) - -/* - * DAVINCI_MCASP_TXFMCTL_REG - Transmit Frame Control Register Bits - */ -#define FSXPOL BIT(0) -#define AFSXE BIT(1) -#define FSXDUR BIT(4) -#define FSXMOD(val) (val<<7) - -/* - * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits - */ -#define FSRPOL BIT(0) -#define AFSRE BIT(1) -#define FSRDUR BIT(4) -#define FSRMOD(val) (val<<7) - -/* - * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits - */ -#define ACLKXDIV(val) (val) -#define ACLKXE BIT(5) -#define TX_ASYNC BIT(6) -#define ACLKXPOL BIT(7) -#define ACLKXDIV_MASK 0x1f - -/* - * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits - */ -#define ACLKRDIV(val) (val) -#define ACLKRE BIT(5) -#define RX_ASYNC BIT(6) -#define ACLKRPOL BIT(7) -#define ACLKRDIV_MASK 0x1f - -/* - * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control - * Register Bits - */ -#define AHCLKXDIV(val) (val) -#define AHCLKXPOL BIT(14) -#define AHCLKXE BIT(15) -#define AHCLKXDIV_MASK 0xfff +struct davinci_mcasp { + struct davinci_pcm_dma_params dma_params[2]; + struct snd_dmaengine_dai_dma_data dma_data[2]; + void __iomem *base; + u32 fifo_base; + struct device *dev; -/* - * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control - * Register Bits - */ -#define AHCLKRDIV(val) (val) -#define AHCLKRPOL BIT(14) -#define AHCLKRE BIT(15) -#define AHCLKRDIV_MASK 0xfff + /* McASP specific data */ + int tdm_slots; + u8 op_mode; + u8 num_serializer; + u8 *serial_dir; + u8 version; + u16 bclk_lrclk_ratio; + int streams; -/* - * DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits - */ -#define MODE(val) (val) -#define DISMOD (val)(val<<2) -#define TXSTATE BIT(4) -#define RXSTATE BIT(5) -#define SRMOD_MASK 3 -#define SRMOD_INACTIVE 0 - -/* - * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits - */ -#define LBEN BIT(0) -#define LBORD BIT(1) -#define LBGENMODE(val) (val<<2) + /* McASP FIFO related */ + u8 txnumevt; + u8 rxnumevt; -/* - * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration - */ -#define TXTDMS(n) (1<<n) - -/* - * DAVINCI_MCASP_RXTDMSLOT_REG - Receive TDM Slot Register configuration - */ -#define RXTDMS(n) (1<<n) - -/* - * DAVINCI_MCASP_GBLCTL_REG - Global Control Register Bits - */ -#define RXCLKRST BIT(0) /* Receiver Clock Divider Reset */ -#define RXHCLKRST BIT(1) /* Receiver High Frequency Clock Divider */ -#define RXSERCLR BIT(2) /* Receiver Serializer Clear */ -#define RXSMRST BIT(3) /* Receiver State Machine Reset */ -#define RXFSRST BIT(4) /* Frame Sync Generator Reset */ -#define TXCLKRST BIT(8) /* Transmitter Clock Divider Reset */ -#define TXHCLKRST BIT(9) /* Transmitter High Frequency Clock Divider*/ -#define TXSERCLR BIT(10) /* Transmit Serializer Clear */ -#define TXSMRST BIT(11) /* Transmitter State Machine Reset */ -#define TXFSRST BIT(12) /* Frame Sync Generator Reset */ + bool dat_port; -/* - * DAVINCI_MCASP_AMUTE_REG - Mute Control Register Bits - */ -#define MUTENA(val) (val) -#define MUTEINPOL BIT(2) -#define MUTEINENA BIT(3) -#define MUTEIN BIT(4) -#define MUTER BIT(5) -#define MUTEX BIT(6) -#define MUTEFSR BIT(7) -#define MUTEFSX BIT(8) -#define MUTEBADCLKR BIT(9) -#define MUTEBADCLKX BIT(10) -#define MUTERXDMAERR BIT(11) -#define MUTETXDMAERR BIT(12) - -/* - * DAVINCI_MCASP_REVTCTL_REG - Receiver DMA Event Control Register bits - */ -#define RXDATADMADIS BIT(0) - -/* - * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits - */ -#define TXDATADMADIS BIT(0) - -/* - * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits - */ -#define FIFO_ENABLE BIT(16) -#define NUMEVT_MASK (0xFF << 8) -#define NUMDMA_MASK (0xFF) - -#define DAVINCI_MCASP_NUM_SERIALIZER 16 +#ifdef CONFIG_PM_SLEEP + struct { + u32 txfmtctl; + u32 rxfmtctl; + u32 txfmt; + u32 rxfmt; + u32 aclkxctl; + u32 aclkrctl; + u32 pdir; + } context; +#endif +}; -static inline void mcasp_set_bits(void __iomem *reg, u32 val) +static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset, + u32 val) { + void __iomem *reg = mcasp->base + offset; __raw_writel(__raw_readl(reg) | val, reg); } -static inline void mcasp_clr_bits(void __iomem *reg, u32 val) +static inline void mcasp_clr_bits(struct davinci_mcasp *mcasp, u32 offset, + u32 val) { + void __iomem *reg = mcasp->base + offset; __raw_writel((__raw_readl(reg) & ~(val)), reg); } -static inline void mcasp_mod_bits(void __iomem *reg, u32 val, u32 mask) +static inline void mcasp_mod_bits(struct davinci_mcasp *mcasp, u32 offset, + u32 val, u32 mask) { + void __iomem *reg = mcasp->base + offset; __raw_writel((__raw_readl(reg) & ~mask) | val, reg); } -static inline void mcasp_set_reg(void __iomem *reg, u32 val) +static inline void mcasp_set_reg(struct davinci_mcasp *mcasp, u32 offset, + u32 val) { - __raw_writel(val, reg); + __raw_writel(val, mcasp->base + offset); } -static inline u32 mcasp_get_reg(void __iomem *reg) +static inline u32 mcasp_get_reg(struct davinci_mcasp *mcasp, u32 offset) { - return (unsigned int)__raw_readl(reg); + return (u32)__raw_readl(mcasp->base + offset); } -static inline void mcasp_set_ctl_reg(void __iomem *regs, u32 val) +static void mcasp_set_ctl_reg(struct davinci_mcasp *mcasp, u32 ctl_reg, u32 val) { int i = 0; - mcasp_set_bits(regs, val); + mcasp_set_bits(mcasp, ctl_reg, val); /* programming GBLCTL needs to read back from GBLCTL and verfiy */ /* loop count is to avoid the lock-up */ for (i = 0; i < 1000; i++) { - if ((mcasp_get_reg(regs) & val) == val) + if ((mcasp_get_reg(mcasp, ctl_reg) & val) == val) break; } - if (i == 1000 && ((mcasp_get_reg(regs) & val) != val)) + if (i == 1000 && ((mcasp_get_reg(mcasp, ctl_reg) & val) != val)) printk(KERN_ERR "GBLCTL write error\n"); } -static void mcasp_start_rx(struct davinci_audio_dev *dev) +static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp) { - mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST); - mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST); - mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR); - mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0); + u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); + u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG); - mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); - mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); - mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0); + return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE; +} - mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); - mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); +static void mcasp_start_rx(struct davinci_mcasp *mcasp) +{ + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST); + + /* + * When ASYNC == 0 the transmit and receive sections operate + * synchronously from the transmit clock and frame sync. We need to make + * sure that the TX signlas are enabled when starting reception. + */ + if (mcasp_is_synchronous(mcasp)) { + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); + } + + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0); + + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0); + + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); + + if (mcasp_is_synchronous(mcasp)) + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); } -static void mcasp_start_tx(struct davinci_audio_dev *dev) +static void mcasp_start_tx(struct davinci_mcasp *mcasp) { u8 offset = 0, i; u32 cnt; - mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); - mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); - mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR); - mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0); - mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); - mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); - mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0); - for (i = 0; i < dev->num_serializer; i++) { - if (dev->serial_dir[i] == TX_MODE) { + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0); + for (i = 0; i < mcasp->num_serializer; i++) { + if (mcasp->serial_dir[i] == TX_MODE) { offset = i; break; } @@ -383,156 +180,140 @@ static void mcasp_start_tx(struct davinci_audio_dev *dev) /* wait for TX ready */ cnt = 0; - while (!(mcasp_get_reg(dev->base + DAVINCI_MCASP_XRSRCTL_REG(offset)) & + while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(offset)) & TXSTATE) && (cnt < 100000)) cnt++; - mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0); } -static void davinci_mcasp_start(struct davinci_audio_dev *dev, int stream) +static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream) { + u32 reg; + + mcasp->streams++; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (dev->txnumevt) { /* enable FIFO */ - switch (dev->version) { - case MCASP_VERSION_3: - mcasp_clr_bits(dev->base + MCASP_VER3_WFIFOCTL, - FIFO_ENABLE); - mcasp_set_bits(dev->base + MCASP_VER3_WFIFOCTL, - FIFO_ENABLE); - break; - default: - mcasp_clr_bits(dev->base + - DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE); - mcasp_set_bits(dev->base + - DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE); - } + if (mcasp->txnumevt) { /* enable FIFO */ + reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + mcasp_set_bits(mcasp, reg, FIFO_ENABLE); } - mcasp_start_tx(dev); + mcasp_start_tx(mcasp); } else { - if (dev->rxnumevt) { /* enable FIFO */ - switch (dev->version) { - case MCASP_VERSION_3: - mcasp_clr_bits(dev->base + MCASP_VER3_RFIFOCTL, - FIFO_ENABLE); - mcasp_set_bits(dev->base + MCASP_VER3_RFIFOCTL, - FIFO_ENABLE); - break; - default: - mcasp_clr_bits(dev->base + - DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE); - mcasp_set_bits(dev->base + - DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE); - } + if (mcasp->rxnumevt) { /* enable FIFO */ + reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + mcasp_set_bits(mcasp, reg, FIFO_ENABLE); } - mcasp_start_rx(dev); + mcasp_start_rx(mcasp); } } -static void mcasp_stop_rx(struct davinci_audio_dev *dev) +static void mcasp_stop_rx(struct davinci_mcasp *mcasp) { - mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, 0); - mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); + /* + * In synchronous mode stop the TX clocks if no other stream is + * running + */ + if (mcasp_is_synchronous(mcasp) && !mcasp->streams) + mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); + + mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); } -static void mcasp_stop_tx(struct davinci_audio_dev *dev) +static void mcasp_stop_tx(struct davinci_mcasp *mcasp) { - mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, 0); - mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); + u32 val = 0; + + /* + * In synchronous mode keep TX clocks running if the capture stream is + * still running. + */ + if (mcasp_is_synchronous(mcasp) && mcasp->streams) + val = TXHCLKRST | TXCLKRST | TXFSRST; + + mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); } -static void davinci_mcasp_stop(struct davinci_audio_dev *dev, int stream) +static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream) { + u32 reg; + + mcasp->streams--; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (dev->txnumevt) { /* disable FIFO */ - switch (dev->version) { - case MCASP_VERSION_3: - mcasp_clr_bits(dev->base + MCASP_VER3_WFIFOCTL, - FIFO_ENABLE); - break; - default: - mcasp_clr_bits(dev->base + - DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE); - } + if (mcasp->txnumevt) { /* disable FIFO */ + reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); } - mcasp_stop_tx(dev); + mcasp_stop_tx(mcasp); } else { - if (dev->rxnumevt) { /* disable FIFO */ - switch (dev->version) { - case MCASP_VERSION_3: - mcasp_clr_bits(dev->base + MCASP_VER3_RFIFOCTL, - FIFO_ENABLE); - break; - - default: - mcasp_clr_bits(dev->base + - DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE); - } + if (mcasp->rxnumevt) { /* disable FIFO */ + reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); } - mcasp_stop_rx(dev); + mcasp_stop_rx(mcasp); } } static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { - struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); - void __iomem *base = dev->base; + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: case SND_SOC_DAIFMT_AC97: - mcasp_clr_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); - mcasp_clr_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); break; default: /* configure a full-word SYNC pulse (LRCLK) */ - mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); - mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); /* make 1st data bit occur one ACLK cycle after the frame sync */ - mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, FSXDLY(1)); - mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, FSRDLY(1)); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, FSXDLY(1)); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, FSRDLY(1)); break; } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: /* codec is clock and frame slave */ - mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); - mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE); - mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); - mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, - ACLKX | ACLKR); - mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, - AFSX | AFSR); + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR); + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR); break; case SND_SOC_DAIFMT_CBM_CFS: /* codec is clock master and frame slave */ - mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); - mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE); - mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); - mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, - ACLKX | ACLKR); - mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, - AFSX | AFSR); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR); + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR); break; case SND_SOC_DAIFMT_CBM_CFM: /* codec is clock and frame master */ - mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); - mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE); - mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); - mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, - ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, + ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR); break; default: @@ -541,35 +322,35 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_IB_NF: - mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); - mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); - mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); - mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); break; case SND_SOC_DAIFMT_NB_IF: - mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); - mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); - mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); - mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); break; case SND_SOC_DAIFMT_IB_IF: - mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); - mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); - mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); - mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); break; case SND_SOC_DAIFMT_NB_NF: - mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); - mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); - mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); - mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); break; default: @@ -581,25 +362,25 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) { - struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(dai); + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); switch (div_id) { case 0: /* MCLK divider */ - mcasp_mod_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, + mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(div - 1), AHCLKXDIV_MASK); - mcasp_mod_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG, + mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRDIV(div - 1), AHCLKRDIV_MASK); break; case 1: /* BCLK divider */ - mcasp_mod_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG, + mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXDIV(div - 1), ACLKXDIV_MASK); - mcasp_mod_bits(dev->base + DAVINCI_MCASP_ACLKRCTL_REG, + mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRDIV(div - 1), ACLKRDIV_MASK); break; case 2: /* BCLK/LRCLK ratio */ - dev->bclk_lrclk_ratio = div; + mcasp->bclk_lrclk_ratio = div; break; default: @@ -612,22 +393,22 @@ static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { - struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(dai); + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); if (dir == SND_SOC_CLOCK_OUT) { - mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); - mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); - mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG, AHCLKX); + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AHCLKX); } else { - mcasp_clr_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); - mcasp_clr_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); - mcasp_clr_bits(dev->base + DAVINCI_MCASP_PDIR_REG, AHCLKX); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AHCLKX); } return 0; } -static int davinci_config_channel_size(struct davinci_audio_dev *dev, +static int davinci_config_channel_size(struct davinci_mcasp *mcasp, int word_length) { u32 fmt; @@ -644,71 +425,68 @@ static int davinci_config_channel_size(struct davinci_audio_dev *dev, * both left and right channels), so it has to be divided by number of * tdm-slots (for I2S - divided by 2). */ - if (dev->bclk_lrclk_ratio) - word_length = dev->bclk_lrclk_ratio / dev->tdm_slots; + if (mcasp->bclk_lrclk_ratio) + word_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots; /* mapping of the XSSZ bit-field as described in the datasheet */ fmt = (word_length >> 1) - 1; - if (dev->op_mode != DAVINCI_MCASP_DIT_MODE) { - mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, - RXSSZ(fmt), RXSSZ(0x0F)); - mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, - TXSSZ(fmt), TXSSZ(0x0F)); - mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, - TXROT(tx_rotate), TXROT(7)); - mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, - RXROT(rx_rotate), RXROT(7)); - mcasp_set_reg(dev->base + DAVINCI_MCASP_RXMASK_REG, - mask); + if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) { + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt), + RXSSZ(0x0F)); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt), + TXSSZ(0x0F)); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate), + TXROT(7)); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate), + RXROT(7)); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask); } - mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, mask); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask); return 0; } -static int davinci_hw_common_param(struct davinci_audio_dev *dev, int stream, +static int davinci_hw_common_param(struct davinci_mcasp *mcasp, int stream, int channels) { int i; u8 tx_ser = 0; u8 rx_ser = 0; u8 ser; - u8 slots = dev->tdm_slots; + u8 slots = mcasp->tdm_slots; u8 max_active_serializers = (channels + slots - 1) / slots; + u32 reg; /* Default configuration */ - mcasp_set_bits(dev->base + DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT); + if (mcasp->version != MCASP_VERSION_4) + mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT); /* All PINS as McASP */ - mcasp_set_reg(dev->base + DAVINCI_MCASP_PFUNC_REG, 0x00000000); + mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000); if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); - mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG, - TXDATADMADIS); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS); } else { - mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); - mcasp_clr_bits(dev->base + DAVINCI_MCASP_REVTCTL_REG, - RXDATADMADIS); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_REVTCTL_REG, RXDATADMADIS); } - for (i = 0; i < dev->num_serializer; i++) { - mcasp_set_bits(dev->base + DAVINCI_MCASP_XRSRCTL_REG(i), - dev->serial_dir[i]); - if (dev->serial_dir[i] == TX_MODE && + for (i = 0; i < mcasp->num_serializer; i++) { + mcasp_set_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), + mcasp->serial_dir[i]); + if (mcasp->serial_dir[i] == TX_MODE && tx_ser < max_active_serializers) { - mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG, - AXR(i)); + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i)); tx_ser++; - } else if (dev->serial_dir[i] == RX_MODE && + } else if (mcasp->serial_dir[i] == RX_MODE && rx_ser < max_active_serializers) { - mcasp_clr_bits(dev->base + DAVINCI_MCASP_PDIR_REG, - AXR(i)); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i)); rx_ser++; } else { - mcasp_mod_bits(dev->base + DAVINCI_MCASP_XRSRCTL_REG(i), - SRMOD_INACTIVE, SRMOD_MASK); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), + SRMOD_INACTIVE, SRMOD_MASK); } } @@ -718,127 +496,113 @@ static int davinci_hw_common_param(struct davinci_audio_dev *dev, int stream, ser = rx_ser; if (ser < max_active_serializers) { - dev_warn(dev->dev, "stream has more channels (%d) than are " + dev_warn(mcasp->dev, "stream has more channels (%d) than are " "enabled in mcasp (%d)\n", channels, ser * slots); return -EINVAL; } - if (dev->txnumevt && stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (dev->txnumevt * tx_ser > 64) - dev->txnumevt = 1; + if (mcasp->txnumevt && stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (mcasp->txnumevt * tx_ser > 64) + mcasp->txnumevt = 1; - switch (dev->version) { - case MCASP_VERSION_3: - mcasp_mod_bits(dev->base + MCASP_VER3_WFIFOCTL, tx_ser, - NUMDMA_MASK); - mcasp_mod_bits(dev->base + MCASP_VER3_WFIFOCTL, - ((dev->txnumevt * tx_ser) << 8), NUMEVT_MASK); - break; - default: - mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, - tx_ser, NUMDMA_MASK); - mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, - ((dev->txnumevt * tx_ser) << 8), NUMEVT_MASK); - } + reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + mcasp_mod_bits(mcasp, reg, tx_ser, NUMDMA_MASK); + mcasp_mod_bits(mcasp, reg, ((mcasp->txnumevt * tx_ser) << 8), + NUMEVT_MASK); } - if (dev->rxnumevt && stream == SNDRV_PCM_STREAM_CAPTURE) { - if (dev->rxnumevt * rx_ser > 64) - dev->rxnumevt = 1; - switch (dev->version) { - case MCASP_VERSION_3: - mcasp_mod_bits(dev->base + MCASP_VER3_RFIFOCTL, rx_ser, - NUMDMA_MASK); - mcasp_mod_bits(dev->base + MCASP_VER3_RFIFOCTL, - ((dev->rxnumevt * rx_ser) << 8), NUMEVT_MASK); - break; - default: - mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, - rx_ser, NUMDMA_MASK); - mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, - ((dev->rxnumevt * rx_ser) << 8), NUMEVT_MASK); - } + if (mcasp->rxnumevt && stream == SNDRV_PCM_STREAM_CAPTURE) { + if (mcasp->rxnumevt * rx_ser > 64) + mcasp->rxnumevt = 1; + + reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + mcasp_mod_bits(mcasp, reg, rx_ser, NUMDMA_MASK); + mcasp_mod_bits(mcasp, reg, ((mcasp->rxnumevt * rx_ser) << 8), + NUMEVT_MASK); } return 0; } -static void davinci_hw_param(struct davinci_audio_dev *dev, int stream) +static void davinci_hw_param(struct davinci_mcasp *mcasp, int stream) { int i, active_slots; u32 mask = 0; + u32 busel = 0; - active_slots = (dev->tdm_slots > 31) ? 32 : dev->tdm_slots; + active_slots = (mcasp->tdm_slots > 31) ? 32 : mcasp->tdm_slots; for (i = 0; i < active_slots; i++) mask |= (1 << i); - mcasp_clr_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC); + + if (!mcasp->dat_port) + busel = TXSEL; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { /* bit stream is MSB first with no delay */ /* DSP_B mode */ - mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, mask); - mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXORD); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD); - if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32)) - mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, - FSXMOD(dev->tdm_slots), FSXMOD(0x1FF)); + if ((mcasp->tdm_slots >= 2) && (mcasp->tdm_slots <= 32)) + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, + FSXMOD(mcasp->tdm_slots), FSXMOD(0x1FF)); else printk(KERN_ERR "playback tdm slot %d not supported\n", - dev->tdm_slots); + mcasp->tdm_slots); } else { /* bit stream is MSB first with no delay */ /* DSP_B mode */ - mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, RXORD); - mcasp_set_reg(dev->base + DAVINCI_MCASP_RXTDM_REG, mask); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask); - if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32)) - mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, - FSRMOD(dev->tdm_slots), FSRMOD(0x1FF)); + if ((mcasp->tdm_slots >= 2) && (mcasp->tdm_slots <= 32)) + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, + FSRMOD(mcasp->tdm_slots), FSRMOD(0x1FF)); else printk(KERN_ERR "capture tdm slot %d not supported\n", - dev->tdm_slots); + mcasp->tdm_slots); } } /* S/PDIF */ -static void davinci_hw_dit_param(struct davinci_audio_dev *dev) +static void davinci_hw_dit_param(struct davinci_mcasp *mcasp) { /* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0 and LSB first */ - mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, - TXROT(6) | TXSSZ(15)); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(6) | TXSSZ(15)); /* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */ - mcasp_set_reg(dev->base + DAVINCI_MCASP_TXFMCTL_REG, - AFSXE | FSXMOD(0x180)); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE | FSXMOD(0x180)); /* Set the TX tdm : for all the slots */ - mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF); /* Set the TX clock controls : div = 1 and internal */ - mcasp_set_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG, - ACLKXE | TX_ASYNC); + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE | TX_ASYNC); - mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS); /* Only 44100 and 48000 are valid, both have the same setting */ - mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3)); + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3)); /* Enable the DIT */ - mcasp_set_bits(dev->base + DAVINCI_MCASP_TXDITCTL_REG, DITEN); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXDITCTL_REG, DITEN); } static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); struct davinci_pcm_dma_params *dma_params = - &dev->dma_params[substream->stream]; + &mcasp->dma_params[substream->stream]; + struct snd_dmaengine_dai_dma_data *dma_data = + &mcasp->dma_data[substream->stream]; int word_length; u8 fifo_level; - u8 slots = dev->tdm_slots; + u8 slots = mcasp->tdm_slots; u8 active_serializers; int channels; struct snd_interval *pcm_channels = hw_param_interval(params, @@ -847,17 +611,17 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, active_serializers = (channels + slots - 1) / slots; - if (davinci_hw_common_param(dev, substream->stream, channels) == -EINVAL) + if (davinci_hw_common_param(mcasp, substream->stream, channels) == -EINVAL) return -EINVAL; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - fifo_level = dev->txnumevt * active_serializers; + fifo_level = mcasp->txnumevt * active_serializers; else - fifo_level = dev->rxnumevt * active_serializers; + fifo_level = mcasp->rxnumevt * active_serializers; - if (dev->op_mode == DAVINCI_MCASP_DIT_MODE) - davinci_hw_dit_param(dev); + if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) + davinci_hw_dit_param(mcasp); else - davinci_hw_param(dev, substream->stream); + davinci_hw_param(mcasp, substream->stream); switch (params_format(params)) { case SNDRV_PCM_FORMAT_U8: @@ -891,13 +655,15 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - if (dev->version == MCASP_VERSION_2 && !fifo_level) + if (mcasp->version == MCASP_VERSION_2 && !fifo_level) dma_params->acnt = 4; else dma_params->acnt = dma_params->data_type; dma_params->fifo_level = fifo_level; - davinci_config_channel_size(dev, word_length); + dma_data->maxburst = fifo_level; + + davinci_config_channel_size(mcasp, word_length); return 0; } @@ -905,29 +671,29 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, static int davinci_mcasp_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { - struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ret = pm_runtime_get_sync(dev->dev); + ret = pm_runtime_get_sync(mcasp->dev); if (IS_ERR_VALUE(ret)) - dev_err(dev->dev, "pm_runtime_get_sync() failed\n"); - davinci_mcasp_start(dev, substream->stream); + dev_err(mcasp->dev, "pm_runtime_get_sync() failed\n"); + davinci_mcasp_start(mcasp, substream->stream); break; case SNDRV_PCM_TRIGGER_SUSPEND: - davinci_mcasp_stop(dev, substream->stream); - ret = pm_runtime_put_sync(dev->dev); + davinci_mcasp_stop(mcasp, substream->stream); + ret = pm_runtime_put_sync(mcasp->dev); if (IS_ERR_VALUE(ret)) - dev_err(dev->dev, "pm_runtime_put_sync() failed\n"); + dev_err(mcasp->dev, "pm_runtime_put_sync() failed\n"); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - davinci_mcasp_stop(dev, substream->stream); + davinci_mcasp_stop(mcasp, substream->stream); break; default: @@ -940,9 +706,14 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream, static int davinci_mcasp_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(dai); + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + + if (mcasp->version == MCASP_VERSION_4) + snd_soc_dai_set_dma_data(dai, substream, + &mcasp->dma_data[substream->stream]); + else + snd_soc_dai_set_dma_data(dai, substream, mcasp->dma_params); - snd_soc_dai_set_dma_data(dai, substream, dev->dma_params); return 0; } @@ -955,6 +726,8 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { .set_sysclk = davinci_mcasp_set_sysclk, }; +#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000 + #define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \ SNDRV_PCM_FMTBIT_U8 | \ SNDRV_PCM_FMTBIT_S16_LE | \ @@ -985,7 +758,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = { }, { - "davinci-mcasp.1", + .name = "davinci-mcasp.1", .playback = { .channels_min = 1, .channels_max = 384, @@ -1016,13 +789,20 @@ static struct snd_platform_data da830_mcasp_pdata = { .version = MCASP_VERSION_2, }; -static struct snd_platform_data omap2_mcasp_pdata = { +static struct snd_platform_data am33xx_mcasp_pdata = { .tx_dma_offset = 0, .rx_dma_offset = 0, .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_3, }; +static struct snd_platform_data dra7_mcasp_pdata = { + .tx_dma_offset = 0x200, + .rx_dma_offset = 0x284, + .asp_chan_q = EVENTQ_0, + .version = MCASP_VERSION_4, +}; + static const struct of_device_id mcasp_dt_ids[] = { { .compatible = "ti,dm646x-mcasp-audio", @@ -1034,12 +814,56 @@ static const struct of_device_id mcasp_dt_ids[] = { }, { .compatible = "ti,am33xx-mcasp-audio", - .data = &omap2_mcasp_pdata, + .data = &am33xx_mcasp_pdata, + }, + { + .compatible = "ti,dra7-mcasp-audio", + .data = &dra7_mcasp_pdata, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mcasp_dt_ids); +static int mcasp_reparent_fck(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct clk *gfclk, *parent_clk; + const char *parent_name; + int ret; + + if (!node) + return 0; + + parent_name = of_get_property(node, "fck_parent", NULL); + if (!parent_name) + return 0; + + gfclk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(gfclk)) { + dev_err(&pdev->dev, "failed to get fck\n"); + return PTR_ERR(gfclk); + } + + parent_clk = clk_get(NULL, parent_name); + if (IS_ERR(parent_clk)) { + dev_err(&pdev->dev, "failed to get parent clock\n"); + ret = PTR_ERR(parent_clk); + goto err1; + } + + ret = clk_set_parent(gfclk, parent_clk); + if (ret) { + dev_err(&pdev->dev, "failed to reparent fck\n"); + goto err2; + } + +err2: + clk_put(parent_clk); +err1: + clk_put(gfclk); + return ret; +} + static struct snd_platform_data *davinci_mcasp_set_pdata_from_of( struct platform_device *pdev) { @@ -1152,7 +976,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) struct davinci_pcm_dma_params *dma_data; struct resource *mem, *ioarea, *res, *dat; struct snd_platform_data *pdata; - struct davinci_audio_dev *dev; + struct davinci_mcasp *mcasp; int ret; if (!pdev->dev.platform_data && !pdev->dev.of_node) { @@ -1160,9 +984,9 @@ static int davinci_mcasp_probe(struct platform_device *pdev) return -EINVAL; } - dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_audio_dev), + mcasp = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcasp), GFP_KERNEL); - if (!dev) + if (!mcasp) return -ENOMEM; pdata = davinci_mcasp_set_pdata_from_of(pdev); @@ -1173,7 +997,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); if (!mem) { - dev_warn(dev->dev, + dev_warn(mcasp->dev, "\"mpu\" mem resource not found, using index 0\n"); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { @@ -1197,32 +1021,39 @@ static int davinci_mcasp_probe(struct platform_device *pdev) return ret; } - dev->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); - if (!dev->base) { + mcasp->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!mcasp->base) { dev_err(&pdev->dev, "ioremap failed\n"); ret = -ENOMEM; goto err_release_clk; } - dev->op_mode = pdata->op_mode; - dev->tdm_slots = pdata->tdm_slots; - dev->num_serializer = pdata->num_serializer; - dev->serial_dir = pdata->serial_dir; - dev->version = pdata->version; - dev->txnumevt = pdata->txnumevt; - dev->rxnumevt = pdata->rxnumevt; - dev->dev = &pdev->dev; + mcasp->op_mode = pdata->op_mode; + mcasp->tdm_slots = pdata->tdm_slots; + mcasp->num_serializer = pdata->num_serializer; + mcasp->serial_dir = pdata->serial_dir; + mcasp->version = pdata->version; + mcasp->txnumevt = pdata->txnumevt; + mcasp->rxnumevt = pdata->rxnumevt; + + mcasp->dev = &pdev->dev; dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); - if (!dat) - dat = mem; + if (dat) + mcasp->dat_port = true; - dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; + dma_data = &mcasp->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q; dma_data->sram_pool = pdata->sram_pool; dma_data->sram_size = pdata->sram_size_playback; - dma_data->dma_addr = dat->start + pdata->tx_dma_offset; + if (dat) + dma_data->dma_addr = dat->start; + else + dma_data->dma_addr = mem->start + pdata->tx_dma_offset; + + /* Unconditional dmaengine stuff */ + mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = dma_data->dma_addr; res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (res) @@ -1230,12 +1061,26 @@ static int davinci_mcasp_probe(struct platform_device *pdev) else dma_data->channel = pdata->tx_dma_channel; - dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]; + dma_data = &mcasp->dma_params[SNDRV_PCM_STREAM_CAPTURE]; dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q; dma_data->sram_pool = pdata->sram_pool; dma_data->sram_size = pdata->sram_size_capture; - dma_data->dma_addr = dat->start + pdata->rx_dma_offset; + if (dat) + dma_data->dma_addr = dat->start; + else + dma_data->dma_addr = mem->start + pdata->rx_dma_offset; + + /* Unconditional dmaengine stuff */ + mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = dma_data->dma_addr; + + if (mcasp->version < MCASP_VERSION_3) { + mcasp->fifo_base = DAVINCI_MCASP_V2_AFIFO_BASE; + /* dma_data->dma_addr is pointing to the data port address */ + mcasp->dat_port = true; + } else { + mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE; + } res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (res) @@ -1243,17 +1088,26 @@ static int davinci_mcasp_probe(struct platform_device *pdev) else dma_data->channel = pdata->rx_dma_channel; - dev_set_drvdata(&pdev->dev, dev); + /* Unconditional dmaengine stuff */ + mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = "tx"; + mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = "rx"; + + dev_set_drvdata(&pdev->dev, mcasp); + + mcasp_reparent_fck(pdev); + ret = snd_soc_register_component(&pdev->dev, &davinci_mcasp_component, &davinci_mcasp_dai[pdata->op_mode], 1); if (ret != 0) goto err_release_clk; - ret = davinci_soc_platform_register(&pdev->dev); - if (ret) { - dev_err(&pdev->dev, "register PCM failed: %d\n", ret); - goto err_unregister_component; + if (mcasp->version != MCASP_VERSION_4) { + ret = davinci_soc_platform_register(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "register PCM failed: %d\n", ret); + goto err_unregister_component; + } } return 0; @@ -1268,9 +1122,11 @@ err_release_clk: static int davinci_mcasp_remove(struct platform_device *pdev) { + struct davinci_mcasp *mcasp = dev_get_drvdata(&pdev->dev); snd_soc_unregister_component(&pdev->dev); - davinci_soc_platform_unregister(&pdev->dev); + if (mcasp->version != MCASP_VERSION_4) + davinci_soc_platform_unregister(&pdev->dev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -1281,32 +1137,30 @@ static int davinci_mcasp_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int davinci_mcasp_suspend(struct device *dev) { - struct davinci_audio_dev *a = dev_get_drvdata(dev); - void __iomem *base = a->base; + struct davinci_mcasp *mcasp = dev_get_drvdata(dev); - a->context.txfmtctl = mcasp_get_reg(base + DAVINCI_MCASP_TXFMCTL_REG); - a->context.rxfmtctl = mcasp_get_reg(base + DAVINCI_MCASP_RXFMCTL_REG); - a->context.txfmt = mcasp_get_reg(base + DAVINCI_MCASP_TXFMT_REG); - a->context.rxfmt = mcasp_get_reg(base + DAVINCI_MCASP_RXFMT_REG); - a->context.aclkxctl = mcasp_get_reg(base + DAVINCI_MCASP_ACLKXCTL_REG); - a->context.aclkrctl = mcasp_get_reg(base + DAVINCI_MCASP_ACLKRCTL_REG); - a->context.pdir = mcasp_get_reg(base + DAVINCI_MCASP_PDIR_REG); + mcasp->context.txfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG); + mcasp->context.rxfmtctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); + mcasp->context.txfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXFMT_REG); + mcasp->context.rxfmt = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMT_REG); + mcasp->context.aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG); + mcasp->context.aclkrctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG); + mcasp->context.pdir = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG); return 0; } static int davinci_mcasp_resume(struct device *dev) { - struct davinci_audio_dev *a = dev_get_drvdata(dev); - void __iomem *base = a->base; - - mcasp_set_reg(base + DAVINCI_MCASP_TXFMCTL_REG, a->context.txfmtctl); - mcasp_set_reg(base + DAVINCI_MCASP_RXFMCTL_REG, a->context.rxfmtctl); - mcasp_set_reg(base + DAVINCI_MCASP_TXFMT_REG, a->context.txfmt); - mcasp_set_reg(base + DAVINCI_MCASP_RXFMT_REG, a->context.rxfmt); - mcasp_set_reg(base + DAVINCI_MCASP_ACLKXCTL_REG, a->context.aclkxctl); - mcasp_set_reg(base + DAVINCI_MCASP_ACLKRCTL_REG, a->context.aclkrctl); - mcasp_set_reg(base + DAVINCI_MCASP_PDIR_REG, a->context.pdir); + struct davinci_mcasp *mcasp = dev_get_drvdata(dev); + + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, mcasp->context.txfmtctl); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG, mcasp->context.rxfmtctl); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMT_REG, mcasp->context.txfmt); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXFMT_REG, mcasp->context.rxfmt); + mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, mcasp->context.aclkxctl); + mcasp_set_reg(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, mcasp->context.aclkrctl); + mcasp_set_reg(mcasp, DAVINCI_MCASP_PDIR_REG, mcasp->context.pdir); return 0; } diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h index a2e27e1c32f3..8fed757d6087 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/davinci/davinci-mcasp.h @@ -18,43 +18,271 @@ #ifndef DAVINCI_MCASP_H #define DAVINCI_MCASP_H -#include <linux/io.h> -#include <linux/platform_data/davinci_asp.h> - -#include "davinci-pcm.h" - -#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000 -#define DAVINCI_MCASP_I2S_DAI 0 -#define DAVINCI_MCASP_DIT_DAI 1 - -struct davinci_audio_dev { - struct davinci_pcm_dma_params dma_params[2]; - void __iomem *base; - struct device *dev; - - /* McASP specific data */ - int tdm_slots; - u8 op_mode; - u8 num_serializer; - u8 *serial_dir; - u8 version; - u16 bclk_lrclk_ratio; - - /* McASP FIFO related */ - u8 txnumevt; - u8 rxnumevt; - -#ifdef CONFIG_PM_SLEEP - struct { - u32 txfmtctl; - u32 rxfmtctl; - u32 txfmt; - u32 rxfmt; - u32 aclkxctl; - u32 aclkrctl; - u32 pdir; - } context; -#endif -}; +/* + * McASP register definitions + */ +#define DAVINCI_MCASP_PID_REG 0x00 +#define DAVINCI_MCASP_PWREMUMGT_REG 0x04 + +#define DAVINCI_MCASP_PFUNC_REG 0x10 +#define DAVINCI_MCASP_PDIR_REG 0x14 +#define DAVINCI_MCASP_PDOUT_REG 0x18 +#define DAVINCI_MCASP_PDSET_REG 0x1c + +#define DAVINCI_MCASP_PDCLR_REG 0x20 + +#define DAVINCI_MCASP_TLGC_REG 0x30 +#define DAVINCI_MCASP_TLMR_REG 0x34 + +#define DAVINCI_MCASP_GBLCTL_REG 0x44 +#define DAVINCI_MCASP_AMUTE_REG 0x48 +#define DAVINCI_MCASP_LBCTL_REG 0x4c + +#define DAVINCI_MCASP_TXDITCTL_REG 0x50 + +#define DAVINCI_MCASP_GBLCTLR_REG 0x60 +#define DAVINCI_MCASP_RXMASK_REG 0x64 +#define DAVINCI_MCASP_RXFMT_REG 0x68 +#define DAVINCI_MCASP_RXFMCTL_REG 0x6c + +#define DAVINCI_MCASP_ACLKRCTL_REG 0x70 +#define DAVINCI_MCASP_AHCLKRCTL_REG 0x74 +#define DAVINCI_MCASP_RXTDM_REG 0x78 +#define DAVINCI_MCASP_EVTCTLR_REG 0x7c + +#define DAVINCI_MCASP_RXSTAT_REG 0x80 +#define DAVINCI_MCASP_RXTDMSLOT_REG 0x84 +#define DAVINCI_MCASP_RXCLKCHK_REG 0x88 +#define DAVINCI_MCASP_REVTCTL_REG 0x8c + +#define DAVINCI_MCASP_GBLCTLX_REG 0xa0 +#define DAVINCI_MCASP_TXMASK_REG 0xa4 +#define DAVINCI_MCASP_TXFMT_REG 0xa8 +#define DAVINCI_MCASP_TXFMCTL_REG 0xac + +#define DAVINCI_MCASP_ACLKXCTL_REG 0xb0 +#define DAVINCI_MCASP_AHCLKXCTL_REG 0xb4 +#define DAVINCI_MCASP_TXTDM_REG 0xb8 +#define DAVINCI_MCASP_EVTCTLX_REG 0xbc + +#define DAVINCI_MCASP_TXSTAT_REG 0xc0 +#define DAVINCI_MCASP_TXTDMSLOT_REG 0xc4 +#define DAVINCI_MCASP_TXCLKCHK_REG 0xc8 +#define DAVINCI_MCASP_XEVTCTL_REG 0xcc + +/* Left(even TDM Slot) Channel Status Register File */ +#define DAVINCI_MCASP_DITCSRA_REG 0x100 +/* Right(odd TDM slot) Channel Status Register File */ +#define DAVINCI_MCASP_DITCSRB_REG 0x118 +/* Left(even TDM slot) User Data Register File */ +#define DAVINCI_MCASP_DITUDRA_REG 0x130 +/* Right(odd TDM Slot) User Data Register File */ +#define DAVINCI_MCASP_DITUDRB_REG 0x148 + +/* Serializer n Control Register */ +#define DAVINCI_MCASP_XRSRCTL_BASE_REG 0x180 +#define DAVINCI_MCASP_XRSRCTL_REG(n) (DAVINCI_MCASP_XRSRCTL_BASE_REG + \ + (n << 2)) + +/* Transmit Buffer for Serializer n */ +#define DAVINCI_MCASP_TXBUF_REG 0x200 +/* Receive Buffer for Serializer n */ +#define DAVINCI_MCASP_RXBUF_REG 0x280 + +/* McASP FIFO Registers */ +#define DAVINCI_MCASP_V2_AFIFO_BASE (0x1010) +#define DAVINCI_MCASP_V3_AFIFO_BASE (0x1000) + +/* FIFO register offsets from AFIFO base */ +#define MCASP_WFIFOCTL_OFFSET (0x0) +#define MCASP_WFIFOSTS_OFFSET (0x4) +#define MCASP_RFIFOCTL_OFFSET (0x8) +#define MCASP_RFIFOSTS_OFFSET (0xc) + +/* + * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management + * Register Bits + */ +#define MCASP_FREE BIT(0) +#define MCASP_SOFT BIT(1) + +/* + * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits + */ +#define AXR(n) (1<<n) +#define PFUNC_AMUTE BIT(25) +#define ACLKX BIT(26) +#define AHCLKX BIT(27) +#define AFSX BIT(28) +#define ACLKR BIT(29) +#define AHCLKR BIT(30) +#define AFSR BIT(31) + +/* + * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits + */ +#define AXR(n) (1<<n) +#define PDIR_AMUTE BIT(25) +#define ACLKX BIT(26) +#define AHCLKX BIT(27) +#define AFSX BIT(28) +#define ACLKR BIT(29) +#define AHCLKR BIT(30) +#define AFSR BIT(31) + +/* + * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits + */ +#define DITEN BIT(0) /* Transmit DIT mode enable/disable */ +#define VA BIT(2) +#define VB BIT(3) + +/* + * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits + */ +#define TXROT(val) (val) +#define TXSEL BIT(3) +#define TXSSZ(val) (val<<4) +#define TXPBIT(val) (val<<8) +#define TXPAD(val) (val<<13) +#define TXORD BIT(15) +#define FSXDLY(val) (val<<16) + +/* + * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits + */ +#define RXROT(val) (val) +#define RXSEL BIT(3) +#define RXSSZ(val) (val<<4) +#define RXPBIT(val) (val<<8) +#define RXPAD(val) (val<<13) +#define RXORD BIT(15) +#define FSRDLY(val) (val<<16) + +/* + * DAVINCI_MCASP_TXFMCTL_REG - Transmit Frame Control Register Bits + */ +#define FSXPOL BIT(0) +#define AFSXE BIT(1) +#define FSXDUR BIT(4) +#define FSXMOD(val) (val<<7) + +/* + * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits + */ +#define FSRPOL BIT(0) +#define AFSRE BIT(1) +#define FSRDUR BIT(4) +#define FSRMOD(val) (val<<7) + +/* + * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits + */ +#define ACLKXDIV(val) (val) +#define ACLKXE BIT(5) +#define TX_ASYNC BIT(6) +#define ACLKXPOL BIT(7) +#define ACLKXDIV_MASK 0x1f + +/* + * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits + */ +#define ACLKRDIV(val) (val) +#define ACLKRE BIT(5) +#define RX_ASYNC BIT(6) +#define ACLKRPOL BIT(7) +#define ACLKRDIV_MASK 0x1f + +/* + * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control + * Register Bits + */ +#define AHCLKXDIV(val) (val) +#define AHCLKXPOL BIT(14) +#define AHCLKXE BIT(15) +#define AHCLKXDIV_MASK 0xfff + +/* + * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control + * Register Bits + */ +#define AHCLKRDIV(val) (val) +#define AHCLKRPOL BIT(14) +#define AHCLKRE BIT(15) +#define AHCLKRDIV_MASK 0xfff + +/* + * DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits + */ +#define MODE(val) (val) +#define DISMOD (val)(val<<2) +#define TXSTATE BIT(4) +#define RXSTATE BIT(5) +#define SRMOD_MASK 3 +#define SRMOD_INACTIVE 0 + +/* + * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits + */ +#define LBEN BIT(0) +#define LBORD BIT(1) +#define LBGENMODE(val) (val<<2) + +/* + * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration + */ +#define TXTDMS(n) (1<<n) + +/* + * DAVINCI_MCASP_RXTDMSLOT_REG - Receive TDM Slot Register configuration + */ +#define RXTDMS(n) (1<<n) + +/* + * DAVINCI_MCASP_GBLCTL_REG - Global Control Register Bits + */ +#define RXCLKRST BIT(0) /* Receiver Clock Divider Reset */ +#define RXHCLKRST BIT(1) /* Receiver High Frequency Clock Divider */ +#define RXSERCLR BIT(2) /* Receiver Serializer Clear */ +#define RXSMRST BIT(3) /* Receiver State Machine Reset */ +#define RXFSRST BIT(4) /* Frame Sync Generator Reset */ +#define TXCLKRST BIT(8) /* Transmitter Clock Divider Reset */ +#define TXHCLKRST BIT(9) /* Transmitter High Frequency Clock Divider*/ +#define TXSERCLR BIT(10) /* Transmit Serializer Clear */ +#define TXSMRST BIT(11) /* Transmitter State Machine Reset */ +#define TXFSRST BIT(12) /* Frame Sync Generator Reset */ + +/* + * DAVINCI_MCASP_AMUTE_REG - Mute Control Register Bits + */ +#define MUTENA(val) (val) +#define MUTEINPOL BIT(2) +#define MUTEINENA BIT(3) +#define MUTEIN BIT(4) +#define MUTER BIT(5) +#define MUTEX BIT(6) +#define MUTEFSR BIT(7) +#define MUTEFSX BIT(8) +#define MUTEBADCLKR BIT(9) +#define MUTEBADCLKX BIT(10) +#define MUTERXDMAERR BIT(11) +#define MUTETXDMAERR BIT(12) + +/* + * DAVINCI_MCASP_REVTCTL_REG - Receiver DMA Event Control Register bits + */ +#define RXDATADMADIS BIT(0) + +/* + * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits + */ +#define TXDATADMADIS BIT(0) + +/* + * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits + */ +#define FIFO_ENABLE BIT(16) +#define NUMEVT_MASK (0xFF << 8) +#define NUMDMA_MASK (0xFF) #endif /* DAVINCI_MCASP_H */ diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index fb5d107f5603..14145cdf8a11 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -46,33 +46,11 @@ static void print_buf_info(int slot, char *name) } #endif -#define DAVINCI_PCM_FMTBITS (\ - SNDRV_PCM_FMTBIT_S8 |\ - SNDRV_PCM_FMTBIT_U8 |\ - SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S16_BE |\ - SNDRV_PCM_FMTBIT_U16_LE |\ - SNDRV_PCM_FMTBIT_U16_BE |\ - SNDRV_PCM_FMTBIT_S24_LE |\ - SNDRV_PCM_FMTBIT_S24_BE |\ - SNDRV_PCM_FMTBIT_U24_LE |\ - SNDRV_PCM_FMTBIT_U24_BE |\ - SNDRV_PCM_FMTBIT_S32_LE |\ - SNDRV_PCM_FMTBIT_S32_BE |\ - SNDRV_PCM_FMTBIT_U32_LE |\ - SNDRV_PCM_FMTBIT_U32_BE) - static struct snd_pcm_hardware pcm_hardware_playback = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME| SNDRV_PCM_INFO_BATCH), - .formats = DAVINCI_PCM_FMTBITS, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .rate_min = 8000, - .rate_max = 192000, - .channels_min = 2, - .channels_max = 384, .buffer_bytes_max = 128 * 1024, .period_bytes_min = 32, .period_bytes_max = 8 * 1024, @@ -86,12 +64,6 @@ static struct snd_pcm_hardware pcm_hardware_capture = { SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_BATCH), - .formats = DAVINCI_PCM_FMTBITS, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .rate_min = 8000, - .rate_max = 192000, - .channels_min = 2, - .channels_max = 384, .buffer_bytes_max = 128 * 1024, .period_bytes_min = 32, .period_bytes_max = 8 * 1024, diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index b7ab71f2ccc1..324988dea4bd 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,9 +1,16 @@ +config SND_SOC_FSL_SAI + tristate + select SND_SOC_GENERIC_DMAENGINE_PCM + config SND_SOC_FSL_SSI tristate config SND_SOC_FSL_SPDIF tristate +config SND_SOC_FSL_ESAI + tristate + config SND_SOC_FSL_UTILS tristate @@ -197,7 +204,6 @@ config SND_SOC_IMX_SPDIF tristate "SoC Audio support for i.MX boards with S/PDIF" select SND_SOC_IMX_PCM_DMA select SND_SOC_FSL_SPDIF - select SND_SOC_SPDIF select REGMAP_MMIO help SoC Audio support for i.MX boards with S/PDIF diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 8db705b0fdf9..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/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..d0c72ed261e7 --- /dev/null +++ b/sound/soc/fsl/fsl_esai.c @@ -0,0 +1,815 @@ +/* + * 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_xSMA_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_xSMA_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) +{ + 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 + */ + clk_prepare_enable(esai_priv->coreclk); + if (!IS_ERR(esai_priv->extalclk)) + clk_prepare_enable(esai_priv->extalclk); + if (!IS_ERR(esai_priv->fsysclk)) + clk_prepare_enable(esai_priv->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; +} + +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 const 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); + + /* 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..9c9f957fcae1 --- /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_xSMA_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..cdd3fa830704 --- /dev/null +++ b/sound/soc/fsl/fsl_sai.c @@ -0,0 +1,459 @@ +/* + * Freescale ALSA SoC Digital Audio Interface (SAI) driver. + * + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * + * This program is free software, you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or(at your + * option) any later version. + * + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> + +#include "fsl_sai.h" + +static inline u32 sai_readl(struct fsl_sai *sai, + const void __iomem *addr) +{ + u32 val; + + val = __raw_readl(addr); + + if (likely(sai->big_endian_regs)) + val = be32_to_cpu(val); + else + val = le32_to_cpu(val); + rmb(); + + return val; +} + +static inline void sai_writel(struct fsl_sai *sai, + u32 val, void __iomem *addr) +{ + wmb(); + if (likely(sai->big_endian_regs)) + val = cpu_to_be32(val); + else + val = cpu_to_le32(val); + + __raw_writel(val, addr); +} + +static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int fsl_dir) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 val_cr2, reg_cr2; + + if (fsl_dir == FSL_FMT_TRANSMITTER) + reg_cr2 = FSL_SAI_TCR2; + else + reg_cr2 = FSL_SAI_RCR2; + + val_cr2 = sai_readl(sai, sai->base + reg_cr2); + 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; + } + + sai_writel(sai, val_cr2, sai->base + reg_cr2); + + return 0; +} + +static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + int ret; + + if (dir == SND_SOC_CLOCK_IN) + return 0; + + ret = clk_prepare_enable(sai->clk); + if (ret) + return ret; + + ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, + FSL_FMT_TRANSMITTER); + if (ret) { + dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret); + goto err_clk; + } + + ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, + FSL_FMT_RECEIVER); + if (ret) { + dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret); + goto err_clk; + } + +err_clk: + clk_disable_unprepare(sai->clk); + + return ret; +} + +static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, + unsigned int fmt, int fsl_dir) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 val_cr2, val_cr4, reg_cr2, reg_cr4; + + if (fsl_dir == FSL_FMT_TRANSMITTER) { + reg_cr2 = FSL_SAI_TCR2; + reg_cr4 = FSL_SAI_TCR4; + } else { + reg_cr2 = FSL_SAI_RCR2; + reg_cr4 = FSL_SAI_RCR4; + } + + val_cr2 = sai_readl(sai, sai->base + reg_cr2); + val_cr4 = sai_readl(sai, sai->base + reg_cr4); + + if (sai->big_endian_data) + val_cr4 &= ~FSL_SAI_CR4_MF; + else + val_cr4 |= FSL_SAI_CR4_MF; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + val_cr4 |= FSL_SAI_CR4_FSE; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + val_cr4 |= FSL_SAI_CR4_FSP; + val_cr2 &= ~FSL_SAI_CR2_BCP; + break; + case SND_SOC_DAIFMT_IB_NF: + val_cr4 &= ~FSL_SAI_CR4_FSP; + val_cr2 &= ~FSL_SAI_CR2_BCP; + break; + case SND_SOC_DAIFMT_NB_IF: + val_cr4 |= FSL_SAI_CR4_FSP; + val_cr2 |= FSL_SAI_CR2_BCP; + break; + case SND_SOC_DAIFMT_NB_NF: + val_cr4 &= ~FSL_SAI_CR4_FSP; + val_cr2 |= FSL_SAI_CR2_BCP; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + val_cr2 |= FSL_SAI_CR2_BCD_MSTR; + val_cr4 |= FSL_SAI_CR4_FSD_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; + val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; + break; + default: + return -EINVAL; + } + + sai_writel(sai, val_cr2, sai->base + reg_cr2); + sai_writel(sai, val_cr4, sai->base + reg_cr4); + + return 0; +} + +static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + int ret; + + ret = clk_prepare_enable(sai->clk); + if (ret) + return ret; + + ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER); + if (ret) { + dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret); + goto err_clk; + } + + ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER); + if (ret) { + dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret); + goto err_clk; + } + +err_clk: + clk_disable_unprepare(sai->clk); + + return ret; +} + +static int fsl_sai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr; + unsigned int channels = params_channels(params); + u32 word_width = snd_pcm_format_width(params_format(params)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + reg_cr4 = FSL_SAI_TCR4; + reg_cr5 = FSL_SAI_TCR5; + reg_mr = FSL_SAI_TMR; + } else { + reg_cr4 = FSL_SAI_RCR4; + reg_cr5 = FSL_SAI_RCR5; + reg_mr = FSL_SAI_RMR; + } + + val_cr4 = sai_readl(sai, sai->base + reg_cr4); + val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK; + val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK; + + val_cr5 = sai_readl(sai, sai->base + reg_cr5); + val_cr5 &= ~FSL_SAI_CR5_WNW_MASK; + val_cr5 &= ~FSL_SAI_CR5_W0W_MASK; + val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; + + val_cr4 |= FSL_SAI_CR4_SYWD(word_width); + val_cr5 |= FSL_SAI_CR5_WNW(word_width); + val_cr5 |= FSL_SAI_CR5_W0W(word_width); + + val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; + if (sai->big_endian_data) + val_cr5 |= FSL_SAI_CR5_FBT(0); + else + val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); + + val_cr4 |= FSL_SAI_CR4_FRSZ(channels); + val_mr = ~0UL - ((1 << channels) - 1); + + sai_writel(sai, val_cr4, sai->base + reg_cr4); + sai_writel(sai, val_cr5, sai->base + reg_cr5); + sai_writel(sai, val_mr, sai->base + reg_mr); + + return 0; +} + +static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 tcsr, rcsr, val_cr2, val_cr3, reg_cr3; + + val_cr2 = sai_readl(sai, sai->base + FSL_SAI_TCR2); + val_cr2 &= ~FSL_SAI_CR2_SYNC; + sai_writel(sai, val_cr2, sai->base + FSL_SAI_TCR2); + + val_cr2 = sai_readl(sai, sai->base + FSL_SAI_RCR2); + val_cr2 |= FSL_SAI_CR2_SYNC; + sai_writel(sai, val_cr2, sai->base + FSL_SAI_RCR2); + + tcsr = sai_readl(sai, sai->base + FSL_SAI_TCSR); + rcsr = sai_readl(sai, sai->base + FSL_SAI_RCSR); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + tcsr |= FSL_SAI_CSR_FRDE; + rcsr &= ~FSL_SAI_CSR_FRDE; + reg_cr3 = FSL_SAI_TCR3; + } else { + rcsr |= FSL_SAI_CSR_FRDE; + tcsr &= ~FSL_SAI_CSR_FRDE; + reg_cr3 = FSL_SAI_RCR3; + } + + val_cr3 = sai_readl(sai, sai->base + reg_cr3); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + tcsr |= FSL_SAI_CSR_TERE; + rcsr |= FSL_SAI_CSR_TERE; + val_cr3 |= FSL_SAI_CR3_TRCE; + + sai_writel(sai, val_cr3, sai->base + reg_cr3); + sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); + sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (!(cpu_dai->playback_active || cpu_dai->capture_active)) { + tcsr &= ~FSL_SAI_CSR_TERE; + rcsr &= ~FSL_SAI_CSR_TERE; + } + + val_cr3 &= ~FSL_SAI_CR3_TRCE; + + sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); + sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); + sai_writel(sai, val_cr3, sai->base + reg_cr3); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fsl_sai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + return clk_prepare_enable(sai->clk); +} + +static void fsl_sai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + clk_disable_unprepare(sai->clk); +} + +static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { + .set_sysclk = fsl_sai_set_dai_sysclk, + .set_fmt = fsl_sai_set_dai_fmt, + .hw_params = fsl_sai_hw_params, + .trigger = fsl_sai_trigger, + .startup = fsl_sai_startup, + .shutdown = fsl_sai_shutdown, +}; + +static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); + int ret; + + ret = clk_prepare_enable(sai->clk); + if (ret) + return ret; + + sai_writel(sai, 0x0, sai->base + FSL_SAI_RCSR); + sai_writel(sai, 0x0, sai->base + FSL_SAI_TCSR); + sai_writel(sai, FSL_SAI_MAXBURST_TX * 2, sai->base + FSL_SAI_TCR1); + sai_writel(sai, FSL_SAI_MAXBURST_RX - 1, sai->base + FSL_SAI_RCR1); + + clk_disable_unprepare(sai->clk); + + snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx, + &sai->dma_params_rx); + + snd_soc_dai_set_drvdata(cpu_dai, sai); + + return 0; +} + +static struct snd_soc_dai_driver fsl_sai_dai = { + .probe = fsl_sai_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = FSL_SAI_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = FSL_SAI_FORMATS, + }, + .ops = &fsl_sai_pcm_dai_ops, +}; + +static const struct snd_soc_component_driver fsl_component = { + .name = "fsl-sai", +}; + +static int fsl_sai_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_sai *sai; + struct resource *res; + int ret; + + sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); + if (!sai) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sai->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sai->base)) + return PTR_ERR(sai->base); + + sai->clk = devm_clk_get(&pdev->dev, "sai"); + if (IS_ERR(sai->clk)) { + dev_err(&pdev->dev, "Cannot get SAI's clock\n"); + return PTR_ERR(sai->clk); + } + + sai->dma_params_rx.addr = res->start + FSL_SAI_RDR; + sai->dma_params_tx.addr = res->start + FSL_SAI_TDR; + sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; + sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX; + + sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); + sai->big_endian_data = of_property_read_bool(np, "big-endian-data"); + + platform_set_drvdata(pdev, sai); + + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component, + &fsl_sai_dai, 1); + if (ret) + return ret; + + return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); +} + +static const struct of_device_id fsl_sai_ids[] = { + { .compatible = "fsl,vf610-sai", }, + { /* sentinel */ } +}; + +static struct platform_driver fsl_sai_driver = { + .probe = fsl_sai_probe, + .driver = { + .name = "fsl-sai", + .owner = THIS_MODULE, + .of_match_table = fsl_sai_ids, + }, +}; +module_platform_driver(fsl_sai_driver); + +MODULE_DESCRIPTION("Freescale Soc SAI Interface"); +MODULE_AUTHOR("Xiubo Li, <Li.Xiubo@freescale.com>"); +MODULE_ALIAS("platform:fsl-sai"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h new file mode 100644 index 000000000000..41bb62e69361 --- /dev/null +++ b/sound/soc/fsl/fsl_sai.h @@ -0,0 +1,114 @@ +/* + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __FSL_SAI_H +#define __FSL_SAI_H + +#include <sound/dmaengine_pcm.h> + +#define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +/* SAI Transmit/Recieve Control Register */ +#define FSL_SAI_TCSR 0x00 +#define FSL_SAI_RCSR 0x80 +#define FSL_SAI_CSR_TERE BIT(31) +#define FSL_SAI_CSR_FWF BIT(17) +#define FSL_SAI_CSR_FRIE BIT(8) +#define FSL_SAI_CSR_FRDE BIT(0) + +/* SAI Transmit Data/FIFO/MASK Register */ +#define FSL_SAI_TDR 0x20 +#define FSL_SAI_TFR 0x40 +#define FSL_SAI_TMR 0x60 + +/* SAI Recieve Data/FIFO/MASK Register */ +#define FSL_SAI_RDR 0xa0 +#define FSL_SAI_RFR 0xc0 +#define FSL_SAI_RMR 0xe0 + +/* SAI Transmit and Recieve Configuration 1 Register */ +#define FSL_SAI_TCR1 0x04 +#define FSL_SAI_RCR1 0x84 + +/* SAI Transmit and Recieve Configuration 2 Register */ +#define FSL_SAI_TCR2 0x08 +#define FSL_SAI_RCR2 0x88 +#define FSL_SAI_CR2_SYNC BIT(30) +#define FSL_SAI_CR2_MSEL_MASK (0xff << 26) +#define FSL_SAI_CR2_MSEL_BUS 0 +#define FSL_SAI_CR2_MSEL_MCLK1 BIT(26) +#define FSL_SAI_CR2_MSEL_MCLK2 BIT(27) +#define FSL_SAI_CR2_MSEL_MCLK3 (BIT(26) | BIT(27)) +#define FSL_SAI_CR2_BCP BIT(25) +#define FSL_SAI_CR2_BCD_MSTR BIT(24) + +/* SAI Transmit and Recieve Configuration 3 Register */ +#define FSL_SAI_TCR3 0x0c +#define FSL_SAI_RCR3 0x8c +#define FSL_SAI_CR3_TRCE BIT(16) +#define FSL_SAI_CR3_WDFL(x) (x) +#define FSL_SAI_CR3_WDFL_MASK 0x1f + +/* SAI Transmit and Recieve Configuration 4 Register */ +#define FSL_SAI_TCR4 0x10 +#define FSL_SAI_RCR4 0x90 +#define FSL_SAI_CR4_FRSZ(x) (((x) - 1) << 16) +#define FSL_SAI_CR4_FRSZ_MASK (0x1f << 16) +#define FSL_SAI_CR4_SYWD(x) (((x) - 1) << 8) +#define FSL_SAI_CR4_SYWD_MASK (0x1f << 8) +#define FSL_SAI_CR4_MF BIT(4) +#define FSL_SAI_CR4_FSE BIT(3) +#define FSL_SAI_CR4_FSP BIT(1) +#define FSL_SAI_CR4_FSD_MSTR BIT(0) + +/* SAI Transmit and Recieve Configuration 5 Register */ +#define FSL_SAI_TCR5 0x14 +#define FSL_SAI_RCR5 0x94 +#define FSL_SAI_CR5_WNW(x) (((x) - 1) << 24) +#define FSL_SAI_CR5_WNW_MASK (0x1f << 24) +#define FSL_SAI_CR5_W0W(x) (((x) - 1) << 16) +#define FSL_SAI_CR5_W0W_MASK (0x1f << 16) +#define FSL_SAI_CR5_FBT(x) ((x) << 8) +#define FSL_SAI_CR5_FBT_MASK (0x1f << 8) + +/* SAI type */ +#define FSL_SAI_DMA BIT(0) +#define FSL_SAI_USE_AC97 BIT(1) +#define FSL_SAI_NET BIT(2) +#define FSL_SAI_TRA_SYN BIT(3) +#define FSL_SAI_REC_SYN BIT(4) +#define FSL_SAI_USE_I2S_SLAVE BIT(5) + +#define FSL_FMT_TRANSMITTER 0 +#define FSL_FMT_RECEIVER 1 + +/* SAI clock sources */ +#define FSL_SAI_CLK_BUS 0 +#define FSL_SAI_CLK_MAST1 1 +#define FSL_SAI_CLK_MAST2 2 +#define FSL_SAI_CLK_MAST3 3 + +/* SAI data transfer numbers per DMA request */ +#define FSL_SAI_MAXBURST_TX 6 +#define FSL_SAI_MAXBURST_RX 6 + +struct fsl_sai { + struct clk *clk; + + void __iomem *base; + + bool big_endian_regs; + bool big_endian_data; + + struct snd_dmaengine_dai_dma_data dma_params_rx; + struct snd_dmaengine_dai_dma_data dma_params_tx; +}; + +#endif /* __FSL_SAI_H */ diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 55193a5596ca..4d075f1abe78 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1181,13 +1181,6 @@ static int fsl_spdif_probe(struct platform_device *pdev) return ret; } -static int fsl_spdif_remove(struct platform_device *pdev) -{ - imx_pcm_dma_exit(pdev); - - return 0; -} - static const struct of_device_id fsl_spdif_dt_ids[] = { { .compatible = "fsl,imx35-spdif", }, {} @@ -1201,7 +1194,6 @@ static struct platform_driver fsl_spdif_driver = { .of_match_table = fsl_spdif_dt_ids, }, .probe = fsl_spdif_probe, - .remove = fsl_spdif_remove, }; module_platform_driver(fsl_spdif_driver); diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 35e277379b86..f9090b167ad7 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,29 @@ 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; + 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 +199,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 +232,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 +306,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 +364,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 +372,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 +669,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 +707,18 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private) * because it is also running without an active substream. Normally SSI * is only enabled when there is a substream. */ - if (ssi_private->imx_ac97) { - /* - * Setup the clock control register - */ - write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13), - &ssi->stccr); - write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13), - &ssi->srccr); - - /* - * Enable AC97 mode and startup the SSI - */ - write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV, - &ssi->sacnt); - write_ssi(0xff, &ssi->saccdis); - write_ssi(0x300, &ssi->saccen); - - /* - * Enable SSI, Transmit and Receive - */ - write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN | - CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE); + if (ssi_private->imx_ac97) + fsl_ssi_setup_ac97(ssi_private); - write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor); + /* + * 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)); } return 0; @@ -431,53 +739,17 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); - int synchronous = ssi_private->cpu_dai_drv.symmetric_rates; + unsigned long flags; - /* - * If this is the first stream opened, then request the IRQ - * and initialize the SSI registers. + /* First, we only do fsl_ssi_setup() when SSI is going to be active. + * Second, fsl_ssi_setup was already called by ac97_init earlier if + * the driver is in ac97 mode. */ - if (!ssi_private->first_stream) { - ssi_private->first_stream = substream; - - /* - * fsl_ssi_setup was already called by ac97_init earlier if - * the driver is in ac97 mode. - */ - if (!ssi_private->imx_ac97) - fsl_ssi_setup(ssi_private); - } else { - if (synchronous) { - struct snd_pcm_runtime *first_runtime = - ssi_private->first_stream->runtime; - /* - * This is the second stream open, and we're in - * synchronous mode, so we need to impose sample - * sample size constraints. This is because STCCR is - * used for playback and capture in synchronous mode, - * so there's no way to specify different word - * lengths. - * - * Note that this can cause a race condition if the - * second stream is opened before the first stream is - * fully initialized. We provide some protection by - * checking to make sure the first stream is - * initialized, but it's not perfect. ALSA sometimes - * re-initializes the driver with a different sample - * rate or size. If the second stream is opened - * before the first stream has received its final - * parameters, then the second stream may be - * constrained to the wrong sample rate or size. - */ - if (first_runtime->sample_bits) { - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - first_runtime->sample_bits, - first_runtime->sample_bits); - } - } - - ssi_private->second_stream = substream; + if (!dai->active && !ssi_private->imx_ac97) { + fsl_ssi_setup(ssi_private); + spin_lock_irqsave(&ssi_private->baudclk_lock, flags); + ssi_private->baudclk_locked = false; + spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags); } return 0; @@ -501,6 +773,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 +803,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 +1063,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 +1118,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 +1128,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 +1146,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 +1162,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 +1220,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 +1241,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,6 +1257,11 @@ static int fsl_ssi_probe(struct platform_device *pdev) if (!of_device_is_available(np)) return -ENODEV; + of_id = of_match_device(fsl_ssi_ids, &pdev->dev); + if (!of_id) + return -EINVAL; + hw_type = (enum fsl_ssi_type) of_id->data; + /* We only support the SSI in "I2S Slave" mode */ sprop = of_get_property(np, "fsl,mode", NULL); if (!sprop) { @@ -899,6 +1288,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 +1325,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,7 +1339,37 @@ 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")) { + 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]; ssi_private->ssi_on_imx = true; @@ -963,6 +1386,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_warn(&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(). @@ -1001,32 +1434,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 +1463,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 +1486,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 +1532,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 +1562,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-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..6553202dd48c 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, diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index 5d5b73303e11..c79cb27473be 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -40,16 +40,11 @@ struct imx_pcm_fiq_params { #if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA) int imx_pcm_dma_init(struct platform_device *pdev); -void imx_pcm_dma_exit(struct platform_device *pdev); #else static inline int imx_pcm_dma_init(struct platform_device *pdev) { return -ENODEV; } - -static inline void imx_pcm_dma_exit(struct platform_device *pdev) -{ -} #endif #if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_FIQ) diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index 8499d5292f08..e1dc40143600 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -14,17 +14,15 @@ #include <sound/soc.h> struct imx_spdif_data { - struct snd_soc_dai_link dai[2]; + struct snd_soc_dai_link dai; struct snd_soc_card card; - struct platform_device *txdev; - struct platform_device *rxdev; }; static int imx_spdif_audio_probe(struct platform_device *pdev) { struct device_node *spdif_np, *np = pdev->dev.of_node; struct imx_spdif_data *data; - int ret = 0, num_links = 0; + int ret = 0; spdif_np = of_parse_phandle(np, "spdif-controller", 0); if (!spdif_np) { @@ -35,74 +33,46 @@ static int imx_spdif_audio_probe(struct platform_device *pdev) data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) { - dev_err(&pdev->dev, "failed to allocate memory\n"); ret = -ENOMEM; goto end; } - if (of_property_read_bool(np, "spdif-out")) { - data->dai[num_links].name = "S/PDIF TX"; - data->dai[num_links].stream_name = "S/PDIF PCM Playback"; - data->dai[num_links].codec_dai_name = "dit-hifi"; - data->dai[num_links].codec_name = "spdif-dit"; - data->dai[num_links].cpu_of_node = spdif_np; - data->dai[num_links].platform_of_node = spdif_np; - num_links++; - - data->txdev = platform_device_register_simple("spdif-dit", -1, NULL, 0); - if (IS_ERR(data->txdev)) { - ret = PTR_ERR(data->txdev); - dev_err(&pdev->dev, "register dit failed: %d\n", ret); - goto end; - } - } + data->dai.name = "S/PDIF PCM"; + data->dai.stream_name = "S/PDIF PCM"; + data->dai.codec_dai_name = "snd-soc-dummy-dai"; + data->dai.codec_name = "snd-soc-dummy"; + data->dai.cpu_of_node = spdif_np; + data->dai.platform_of_node = spdif_np; + data->dai.playback_only = true; + data->dai.capture_only = true; - if (of_property_read_bool(np, "spdif-in")) { - data->dai[num_links].name = "S/PDIF RX"; - data->dai[num_links].stream_name = "S/PDIF PCM Capture"; - data->dai[num_links].codec_dai_name = "dir-hifi"; - data->dai[num_links].codec_name = "spdif-dir"; - data->dai[num_links].cpu_of_node = spdif_np; - data->dai[num_links].platform_of_node = spdif_np; - num_links++; - - data->rxdev = platform_device_register_simple("spdif-dir", -1, NULL, 0); - if (IS_ERR(data->rxdev)) { - ret = PTR_ERR(data->rxdev); - dev_err(&pdev->dev, "register dir failed: %d\n", ret); - goto error_dit; - } - } + if (of_property_read_bool(np, "spdif-out")) + data->dai.capture_only = false; + + if (of_property_read_bool(np, "spdif-in")) + data->dai.playback_only = false; - if (!num_links) { + if (data->dai.playback_only && data->dai.capture_only) { dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n"); - goto error_dir; + goto end; } data->card.dev = &pdev->dev; - data->card.num_links = num_links; - data->card.dai_link = data->dai; + data->card.dai_link = &data->dai; + data->card.num_links = 1; ret = snd_soc_of_parse_card_name(&data->card, "model"); if (ret) - goto error_dir; + goto end; ret = devm_snd_soc_register_card(&pdev->dev, &data->card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret); - goto error_dir; + goto end; } platform_set_drvdata(pdev, data); - goto end; - -error_dir: - if (data->rxdev) - platform_device_unregister(data->rxdev); -error_dit: - if (data->txdev) - platform_device_unregister(data->txdev); end: if (spdif_np) of_node_put(spdif_np); @@ -110,18 +80,6 @@ end: return ret; } -static int imx_spdif_audio_remove(struct platform_device *pdev) -{ - struct imx_spdif_data *data = platform_get_drvdata(pdev); - - if (data->rxdev) - platform_device_unregister(data->rxdev); - if (data->txdev) - platform_device_unregister(data->txdev); - - return 0; -} - static const struct of_device_id imx_spdif_dt_ids[] = { { .compatible = "fsl,imx-audio-spdif", }, { /* sentinel */ } @@ -135,7 +93,6 @@ static struct platform_driver imx_spdif_driver = { .of_match_table = imx_spdif_dt_ids, }, .probe = imx_spdif_audio_probe, - .remove = imx_spdif_audio_remove, }; module_platform_driver(imx_spdif_driver); diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index f5f248c91c16..df552fa1aa65 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -304,8 +304,7 @@ static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, scr |= SSI_SCR_RE; sier |= sier_bits; - if (++ssi->enabled == 1) - scr |= SSI_SCR_SSIEN; + scr |= SSI_SCR_SSIEN; break; @@ -318,7 +317,7 @@ static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, scr &= ~SSI_SCR_RE; sier &= ~sier_bits; - if (--ssi->enabled == 0) + if (!(scr & (SSI_SCR_TE | SSI_SCR_RE))) scr &= ~SSI_SCR_SSIEN; break; @@ -536,7 +535,9 @@ static int imx_ssi_probe(struct platform_device *pdev) ret); goto failed_clk; } - clk_prepare_enable(ssi->clk); + ret = clk_prepare_enable(ssi->clk); + if (ret) + goto failed_clk; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ssi->base = devm_ioremap_resource(&pdev->dev, res); @@ -624,9 +625,6 @@ static int imx_ssi_remove(struct platform_device *pdev) { struct imx_ssi *ssi = platform_get_drvdata(pdev); - if (!ssi->dma_init) - imx_pcm_dma_exit(pdev); - if (!ssi->fiq_init) imx_pcm_fiq_exit(pdev); diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h index 560c40fc9ebb..be6562365b6a 100644 --- a/sound/soc/fsl/imx-ssi.h +++ b/sound/soc/fsl/imx-ssi.h @@ -213,7 +213,6 @@ struct imx_ssi { int fiq_init; int dma_init; - int enabled; }; #endif /* _IMX_SSI_H */ diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c index 61e48852b9e8..3fd76bc391de 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -130,8 +130,6 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card, break; } - dapm->bias_level = level; - return 0; } diff --git a/sound/soc/fsl/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/generic/simple-card.c b/sound/soc/generic/simple-card.c index b2fbb7075a6c..2a1b1b5b5221 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -8,14 +8,13 @@ * 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/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/string.h> #include <sound/simple_card.h> -#define asoc_simple_get_card_info(p) \ - container_of(p->dai_link, struct asoc_simple_card_info, snd_link) - static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, struct asoc_simple_dai *set, unsigned int daifmt) @@ -24,7 +23,7 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, daifmt |= set->fmt; - if (!ret && daifmt) + if (daifmt) ret = snd_soc_dai_set_fmt(dai, daifmt); if (ret == -ENOTSUPP) { @@ -40,7 +39,8 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, 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 asoc_simple_card_info *info = + 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; @@ -57,22 +57,182 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) return 0; } +static int +asoc_simple_card_sub_parse_of(struct device_node *np, + struct asoc_simple_dai *dai, + struct device_node **node) +{ + struct clk *clk; + int ret; + + /* + * get node via "sound-dai = <&phandle port>" + * it will be used as xxx_of_node on soc_bind_dai_link() + */ + *node = of_parse_phandle(np, "sound-dai", 0); + if (!*node) + return -ENODEV; + + /* get dai->name */ + ret = snd_soc_of_get_dai_name(np, &dai->name); + if (ret < 0) + goto parse_error; + + /* + * bitclock-inversion, frame-inversion + * bitclock-master, frame-master + * and specific "format" if it has + */ + dai->fmt = snd_soc_of_parse_daifmt(np, NULL); + + /* + * dai->sysclk come from + * "clocks = <&xxx>" (if system has common clock) + * or "system-clock-frequency = <xxx>" + * or device's module clock. + */ + if (of_property_read_bool(np, "clocks")) { + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto parse_error; + } + + dai->sysclk = clk_get_rate(clk); + } else if (of_property_read_bool(np, "system-clock-frequency")) { + of_property_read_u32(np, + "system-clock-frequency", + &dai->sysclk); + } else { + clk = of_clk_get(*node, 0); + if (!IS_ERR(clk)) + dai->sysclk = clk_get_rate(clk); + } + + ret = 0; + +parse_error: + of_node_put(*node); + + return ret; +} + +static int asoc_simple_card_parse_of(struct device_node *node, + struct asoc_simple_card_info *info, + struct device *dev, + struct device_node **of_cpu, + struct device_node **of_codec, + struct device_node **of_platform) +{ + struct device_node *np; + char *name; + int ret; + + /* get CPU/CODEC common format via simple-audio-card,format */ + info->daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,") & + (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK); + + /* DAPM routes */ + if (of_property_read_bool(node, "simple-audio-card,routing")) { + ret = snd_soc_of_parse_audio_routing(&info->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, + &info->cpu_dai, + of_cpu); + if (ret < 0) + return ret; + + /* CODEC sub-node */ + ret = -EINVAL; + np = of_get_child_by_name(node, "simple-audio-card,codec"); + if (np) + ret = asoc_simple_card_sub_parse_of(np, + &info->codec_dai, + of_codec); + if (ret < 0) + return ret; + + if (!info->cpu_dai.name || !info->codec_dai.name) + return -EINVAL; + + /* card name is created from CPU/CODEC dai name */ + name = devm_kzalloc(dev, + strlen(info->cpu_dai.name) + + strlen(info->codec_dai.name) + 2, + GFP_KERNEL); + sprintf(name, "%s-%s", info->cpu_dai.name, info->codec_dai.name); + info->name = info->card = name; + + /* simple-card assumes platform == cpu */ + *of_platform = *of_cpu; + + dev_dbg(dev, "card-name : %s\n", info->card); + dev_dbg(dev, "platform : %04x\n", info->daifmt); + dev_dbg(dev, "cpu : %s / %04x / %d\n", + info->cpu_dai.name, + info->cpu_dai.fmt, + info->cpu_dai.sysclk); + dev_dbg(dev, "codec : %s / %04x / %d\n", + info->codec_dai.name, + info->codec_dai.fmt, + info->codec_dai.sysclk); + + return 0; +} + static int asoc_simple_card_probe(struct platform_device *pdev) { - struct asoc_simple_card_info *cinfo = pdev->dev.platform_data; + struct asoc_simple_card_info *cinfo; + struct device_node *np = pdev->dev.of_node; + struct device_node *of_cpu, *of_codec, *of_platform; struct device *dev = &pdev->dev; + int ret; - if (!cinfo) { - dev_err(dev, "no info for asoc-simple-card\n"); - return -EINVAL; + cinfo = NULL; + of_cpu = NULL; + of_codec = NULL; + of_platform = NULL; + + cinfo = devm_kzalloc(dev, sizeof(*cinfo), GFP_KERNEL); + if (!cinfo) + return -ENOMEM; + + if (np && of_device_is_available(np)) { + cinfo->snd_card.dev = dev; + + ret = asoc_simple_card_parse_of(np, cinfo, dev, + &of_cpu, + &of_codec, + &of_platform); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "parse error %d\n", ret); + return ret; + } + } else { + if (!dev->platform_data) { + dev_err(dev, "no info for asoc-simple-card\n"); + return -EINVAL; + } + + memcpy(cinfo, dev->platform_data, sizeof(*cinfo)); + cinfo->snd_card.dev = dev; } if (!cinfo->name || !cinfo->card || - !cinfo->codec || - !cinfo->platform || - !cinfo->cpu_dai.name || - !cinfo->codec_dai.name) { + !cinfo->codec_dai.name || + !(cinfo->codec || of_codec) || + !(cinfo->platform || of_platform) || + !(cinfo->cpu_dai.name || of_cpu)) { dev_err(dev, "insufficient asoc_simple_card_info settings\n"); return -EINVAL; } @@ -86,6 +246,9 @@ static int asoc_simple_card_probe(struct platform_device *pdev) cinfo->snd_link.platform_name = cinfo->platform; cinfo->snd_link.codec_name = cinfo->codec; cinfo->snd_link.codec_dai_name = cinfo->codec_dai.name; + cinfo->snd_link.cpu_of_node = of_cpu; + cinfo->snd_link.codec_of_node = of_codec; + cinfo->snd_link.platform_of_node = of_platform; cinfo->snd_link.init = asoc_simple_card_dai_init; /* @@ -95,25 +258,25 @@ static int asoc_simple_card_probe(struct platform_device *pdev) cinfo->snd_card.owner = THIS_MODULE; cinfo->snd_card.dai_link = &cinfo->snd_link; cinfo->snd_card.num_links = 1; - cinfo->snd_card.dev = &pdev->dev; - return snd_soc_register_card(&cinfo->snd_card); -} + snd_soc_card_set_drvdata(&cinfo->snd_card, cinfo); -static int asoc_simple_card_remove(struct platform_device *pdev) -{ - struct asoc_simple_card_info *cinfo = pdev->dev.platform_data; - - return snd_soc_unregister_card(&cinfo->snd_card); + return devm_snd_soc_register_card(&pdev->dev, &cinfo->snd_card); } +static const struct of_device_id asoc_simple_of_match[] = { + { .compatible = "simple-audio-card", }, + {}, +}; +MODULE_DEVICE_TABLE(of, asoc_simple_of_match); + static struct platform_driver asoc_simple_card = { .driver = { .name = "asoc-simple-card", .owner = THIS_MODULE, + .of_match_table = asoc_simple_of_match, }, .probe = asoc_simple_card_probe, - .remove = asoc_simple_card_remove, }; module_platform_driver(asoc_simple_card); diff --git a/sound/soc/mid-x86/Kconfig b/sound/soc/intel/Kconfig index 61c10bf503d2..61c10bf503d2 100644 --- a/sound/soc/mid-x86/Kconfig +++ b/sound/soc/intel/Kconfig diff --git a/sound/soc/mid-x86/Makefile b/sound/soc/intel/Makefile index 639883339465..639883339465 100644 --- a/sound/soc/mid-x86/Makefile +++ b/sound/soc/intel/Makefile diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/intel/mfld_machine.c index d3d4c32434f7..d3d4c32434f7 100644 --- a/sound/soc/mid-x86/mfld_machine.c +++ b/sound/soc/intel/mfld_machine.c diff --git a/sound/soc/mid-x86/sst_dsp.h b/sound/soc/intel/sst_dsp.h index 0fce1de284ff..0fce1de284ff 100644 --- a/sound/soc/mid-x86/sst_dsp.h +++ b/sound/soc/intel/sst_dsp.h diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/intel/sst_platform.c index b6b5eb698d33..f465a8180863 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/intel/sst_platform.c @@ -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, diff --git a/sound/soc/mid-x86/sst_platform.h b/sound/soc/intel/sst_platform.h index cacc9066ec52..bee64fb7d2ef 100644 --- a/sound/soc/mid-x86/sst_platform.h +++ b/sound/soc/intel/sst_platform.h @@ -33,10 +33,6 @@ #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/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/mcbsp.c b/sound/soc/omap/mcbsp.c index 83433fdea32a..86c75384c3c8 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -36,10 +36,10 @@ static void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) if (mcbsp->pdata->reg_size == 2) { ((u16 *)mcbsp->reg_cache)[reg] = (u16)val; - __raw_writew((u16)val, addr); + writew_relaxed((u16)val, addr); } else { ((u32 *)mcbsp->reg_cache)[reg] = val; - __raw_writel(val, addr); + writel_relaxed(val, addr); } } @@ -48,22 +48,22 @@ static int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache) void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; if (mcbsp->pdata->reg_size == 2) { - return !from_cache ? __raw_readw(addr) : + return !from_cache ? readw_relaxed(addr) : ((u16 *)mcbsp->reg_cache)[reg]; } else { - return !from_cache ? __raw_readl(addr) : + return !from_cache ? readl_relaxed(addr) : ((u32 *)mcbsp->reg_cache)[reg]; } } static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) { - __raw_writel(val, mcbsp->st_data->io_base_st + reg); + writel_relaxed(val, mcbsp->st_data->io_base_st + reg); } static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) { - return __raw_readl(mcbsp->st_data->io_base_st + reg); + return readl_relaxed(mcbsp->st_data->io_base_st + reg); } #define MCBSP_READ(mcbsp, reg) \ diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index 6d216cb6c19b..3fde9e402710 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -100,12 +100,12 @@ static int n810_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); n810_ext_control(&codec->dapm); - return clk_enable(sys_clkout2); + return clk_prepare_enable(sys_clkout2); } static void n810_shutdown(struct snd_pcm_substream *substream) { - clk_disable(sys_clkout2); + clk_disable_unprepare(sys_clkout2); } static int n810_hw_params(struct snd_pcm_substream *substream, diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c index 12e566be3793..1bd531d718f9 100644 --- a/sound/soc/omap/omap-dmic.c +++ b/sound/soc/omap/omap-dmic.c @@ -61,12 +61,12 @@ struct omap_dmic { static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val) { - __raw_writel(val, dmic->io_base + reg); + writel_relaxed(val, dmic->io_base + reg); } static inline int omap_dmic_read(struct omap_dmic *dmic, u16 reg) { - return __raw_readl(dmic->io_base + reg); + return readl_relaxed(dmic->io_base + reg); } static inline void omap_dmic_start(struct omap_dmic *dmic) diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index cd9ee167959d..2f5b1536477e 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -74,12 +74,12 @@ struct omap_mcpdm { static inline void omap_mcpdm_write(struct omap_mcpdm *mcpdm, u16 reg, u32 val) { - __raw_writel(val, mcpdm->io_base + reg); + writel_relaxed(val, mcpdm->io_base + reg); } static inline int omap_mcpdm_read(struct omap_mcpdm *mcpdm, u16 reg) { - return __raw_readl(mcpdm->io_base + reg); + return readl_relaxed(mcpdm->io_base + reg); } #ifdef DEBUG diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index b8fa9862e54c..07b8b7bc9d20 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -45,8 +45,6 @@ static const struct snd_pcm_hardware omap_pcm_hardware = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S32_LE, .period_bytes_min = 32, .period_bytes_max = 64 * 1024, .periods_min = 2, diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 4db74a083db1..6473052b6899 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -11,7 +11,7 @@ config SND_PXA2XX_SOC config SND_MMP_SOC bool "Soc Audio for Marvell MMP chips" depends on ARCH_MMP - select SND_DMAENGINE_PCM + select SND_SOC_GENERIC_DMAENGINE_PCM select SND_ARM help Say Y if you want to add support for codecs attached to diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c index 7929e19b0ef5..5e8d81330173 100644 --- a/sound/soc/pxa/mmp-pcm.c +++ b/sound/soc/pxa/mmp-pcm.c @@ -36,14 +36,9 @@ struct mmp_dma_data { SNDRV_PCM_INFO_PAUSE | \ SNDRV_PCM_INFO_RESUME) -#define MMP_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE) - static struct snd_pcm_hardware mmp_pcm_hardware[] = { { .info = MMP_PCM_INFO, - .formats = MMP_PCM_FORMATS, .period_bytes_min = 1024, .period_bytes_max = 2048, .periods_min = 2, @@ -53,7 +48,6 @@ static struct snd_pcm_hardware mmp_pcm_hardware[] = { }, { .info = MMP_PCM_INFO, - .formats = MMP_PCM_FORMATS, .period_bytes_min = 1024, .period_bytes_max = 2048, .periods_min = 2, @@ -67,27 +61,15 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_dmaengine_dai_dma_data *dma_params; struct dma_slave_config slave_config; int ret; - dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - if (!dma_params) - return 0; - - ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config); + ret = + snd_dmaengine_pcm_prepare_slave_config(substream, params, + &slave_config); if (ret) return ret; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - slave_config.dst_addr = dma_params->addr; - slave_config.dst_maxburst = 4; - } else { - slave_config.src_addr = dma_params->addr; - slave_config.src_maxburst = 4; - } - ret = dmaengine_slave_config(chan, &slave_config); if (ret) return ret; diff --git a/sound/soc/s6000/s6000-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..27930fc432dc 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -1,13 +1,22 @@ config SND_SOC_SAMSUNG tristate "ASoC support for Samsung" depends on PLAT_SAMSUNG - select S3C64XX_DMA if ARCH_S3C64XX - select S3C24XX_DMA if ARCH_S3C24XX + select S3C2410_DMA if ARCH_S3C24XX + select S3C64XX_PL080 if ARCH_S3C64XX + select SND_S3C_DMA if !ARCH_S3C24XX + select SND_S3C_DMA_LEGACY if ARCH_S3C24XX + select SND_SOC_GENERIC_DMAENGINE_PCM if !ARCH_S3C24XX help Say Y or M if you want to add support for codecs attached to the Samsung SoCs' Audio interfaces. You will also need to select the audio interfaces to support below. +config SND_S3C_DMA + tristate + +config SND_S3C_DMA_LEGACY + tristate + config SND_S3C24XX_I2S tristate select S3C2410_DMA diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 709f6059ad67..86715d8efee6 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -1,5 +1,6 @@ # S3c24XX Platform Support -snd-soc-s3c24xx-objs := dma.o +snd-soc-s3c-dma-objs := dmaengine.o +snd-soc-s3c-dma-legacy-objs := dma.o snd-soc-idma-objs := idma.o snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o @@ -9,7 +10,8 @@ snd-soc-samsung-spdif-objs := spdif.o snd-soc-pcm-objs := pcm.o snd-soc-i2s-objs := i2s.o -obj-$(CONFIG_SND_SOC_SAMSUNG) += snd-soc-s3c24xx.o +obj-$(CONFIG_SND_S3C_DMA) += snd-soc-s3c-dma.o +obj-$(CONFIG_SND_S3C_DMA_LEGACY) += snd-soc-s3c-dma-legacy.o obj-$(CONFIG_SND_S3C24XX_I2S) += snd-soc-s3c24xx-i2s.o obj-$(CONFIG_SND_SAMSUNG_AC97) += snd-soc-ac97.o obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c index 350ba23a9893..4a88e36c82ec 100644 --- a/sound/soc/samsung/ac97.c +++ b/sound/soc/samsung/ac97.c @@ -221,24 +221,6 @@ static struct snd_ac97_bus_ops s3c_ac97_ops = { .reset = s3c_ac97_cold_reset, }; -static int s3c_ac97_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct s3c_dma_params *dma_data; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dma_data = &s3c_ac97_pcm_out; - else - dma_data = &s3c_ac97_pcm_in; - - snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); - - return 0; -} - static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -279,21 +261,6 @@ static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - return -ENODEV; - else - snd_soc_dai_set_dma_data(cpu_dai, substream, &s3c_ac97_mic_in); - - return 0; -} - static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -329,15 +296,27 @@ static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops s3c_ac97_dai_ops = { - .hw_params = s3c_ac97_hw_params, .trigger = s3c_ac97_trigger, }; static const struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = { - .hw_params = s3c_ac97_hw_mic_params, .trigger = s3c_ac97_mic_trigger, }; +static int s3c_ac97_dai_probe(struct snd_soc_dai *dai) +{ + samsung_asoc_init_dma_data(dai, &s3c_ac97_pcm_out, &s3c_ac97_pcm_in); + + return 0; +} + +static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai) +{ + samsung_asoc_init_dma_data(dai, NULL, &s3c_ac97_mic_in); + + return 0; +} + static struct snd_soc_dai_driver s3c_ac97_dai[] = { [S3C_AC97_DAI_PCM] = { .name = "samsung-ac97", @@ -354,6 +333,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = { .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .probe = s3c_ac97_dai_probe, .ops = &s3c_ac97_dai_ops, }, [S3C_AC97_DAI_MIC] = { @@ -365,6 +345,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = { .channels_max = 1, .rates = SNDRV_PCM_RATE_8000_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .probe = s3c_ac97_mic_dai_probe, .ops = &s3c_ac97_mic_dai_ops, }, }; diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index fe2748b494d4..dc09b71b7d9f 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -35,12 +35,6 @@ static const struct snd_pcm_hardware dma_hardware = { SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_U16_LE | - SNDRV_PCM_FMTBIT_U8 | - SNDRV_PCM_FMTBIT_S8, - .channels_min = 2, - .channels_max = 2, .buffer_bytes_max = 128*1024, .period_bytes_min = PAGE_SIZE, .period_bytes_max = PAGE_SIZE*2, @@ -441,6 +435,14 @@ static struct snd_soc_platform_driver samsung_asoc_platform = { .pcm_free = dma_free_dma_buffers, }; +void samsung_asoc_init_dma_data(struct snd_soc_dai *dai, + struct s3c_dma_params *playback, + struct s3c_dma_params *capture) +{ + snd_soc_dai_init_dma_data(dai, playback, capture); +} +EXPORT_SYMBOL_GPL(samsung_asoc_init_dma_data); + int samsung_asoc_dma_platform_register(struct device *dev) { return snd_soc_register_platform(dev, &samsung_asoc_platform); diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h index 0e86315a3eaf..225e5378014e 100644 --- a/sound/soc/samsung/dma.h +++ b/sound/soc/samsung/dma.h @@ -12,6 +12,8 @@ #ifndef _S3C_AUDIO_H #define _S3C_AUDIO_H +#include <sound/dmaengine_pcm.h> + struct s3c_dma_params { struct s3c2410_dma_client *client; /* stream identifier */ int channel; /* Channel ID */ @@ -20,8 +22,12 @@ struct s3c_dma_params { unsigned ch; struct samsung_dma_ops *ops; char *ch_name; + struct snd_dmaengine_dai_dma_data dma_data; }; +void samsung_asoc_init_dma_data(struct snd_soc_dai *dai, + struct s3c_dma_params *playback, + struct s3c_dma_params *capture); int samsung_asoc_dma_platform_register(struct device *dev); void samsung_asoc_dma_platform_unregister(struct device *dev); diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c new file mode 100644 index 000000000000..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/i2s.c b/sound/soc/samsung/i2s.c index a5cbdb4f1655..92f64363427d 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -702,6 +702,8 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, } writel(mod, i2s->addr + I2SMOD); + samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); + i2s->frmclk = params_rate(params); return 0; @@ -946,8 +948,11 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; - if (other && other->clk) /* If this is probe on secondary */ + if (other && other->clk) { /* If this is probe on secondary */ + samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback, + NULL); goto probe_exit; + } i2s->addr = ioremap(i2s->base, 0x100); if (i2s->addr == NULL) { @@ -963,7 +968,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) } clk_prepare_enable(i2s->clk); - snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); + samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); if (other) { other->addr = i2s->addr; diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c index e4f318fc2f82..3d5cf1530b6f 100644 --- a/sound/soc/samsung/idma.c +++ b/sound/soc/samsung/idma.c @@ -35,14 +35,6 @@ static const struct snd_pcm_hardware idma_hardware = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_U16_LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_U24_LE | - SNDRV_PCM_FMTBIT_U8 | - SNDRV_PCM_FMTBIT_S8, - .channels_min = 2, - .channels_max = 2, .buffer_bytes_max = MAX_IDMA_BUFFER, .period_bytes_min = 128, .period_bytes_max = MAX_IDMA_PERIOD, diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c index e54256fc4b2c..6a5e4bf6ac96 100644 --- a/sound/soc/samsung/pcm.c +++ b/sound/soc/samsung/pcm.c @@ -275,7 +275,6 @@ static int s3c_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai); - struct s3c_dma_params *dma_data; void __iomem *regs = pcm->regs; struct clk *clk; int sclk_div, sync_div; @@ -284,13 +283,6 @@ static int s3c_pcm_hw_params(struct snd_pcm_substream *substream, dev_dbg(pcm->dev, "Entered %s\n", __func__); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dma_data = pcm->dma_playback; - else - dma_data = pcm->dma_capture; - - snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data); - /* Strictly check for sample size */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -461,10 +453,20 @@ static const struct snd_soc_dai_ops s3c_pcm_dai_ops = { .set_fmt = s3c_pcm_set_fmt, }; +static int s3c_pcm_dai_probe(struct snd_soc_dai *dai) +{ + struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, pcm->dma_playback, pcm->dma_capture); + + return 0; +} + #define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000 #define S3C_PCM_DAI_DECLARE \ .symmetric_rates = 1, \ + .probe = s3c_pcm_dai_probe, \ .ops = &s3c_pcm_dai_ops, \ .playback = { \ .channels_min = 2, \ diff --git a/sound/soc/samsung/regs-ac97.h b/sound/soc/samsung/regs-ac97.h index c3878f7acb83..a71be45bbffc 100644 --- a/sound/soc/samsung/regs-ac97.h +++ b/sound/soc/samsung/regs-ac97.h @@ -1,5 +1,4 @@ -/* arch/arm/mach-s3c2410/include/mach/regs-ac97.h - * +/* * Copyright (c) 2006 Simtec Electronics <linux@simtec.co.uk> * http://www.simtec.co.uk/products/SWLINUX/ * @@ -10,8 +9,8 @@ * S3C2440 AC97 Controller */ -#ifndef __ASM_ARCH_REGS_AC97_H -#define __ASM_ARCH_REGS_AC97_H __FILE__ +#ifndef __SAMSUNG_REGS_AC97_H__ +#define __SAMSUNG_REGS_AC97_H__ #define S3C_AC97_GLBCTRL (0x00) @@ -64,4 +63,4 @@ #define S3C_AC97_PCM_DATA (0x18) #define S3C_AC97_MIC_DATA (0x1C) -#endif /* __ASM_ARCH_REGS_AC97_H */ +#endif /* __SAMSUNG_REGS_AC97_H__ */ diff --git a/sound/soc/samsung/regs-iis.h b/sound/soc/samsung/regs-iis.h index a18d35e7a735..dc6cbbe9c4f0 100644 --- a/sound/soc/samsung/regs-iis.h +++ b/sound/soc/samsung/regs-iis.h @@ -1,5 +1,4 @@ -/* arch/arm/plat-samsung/include/plat/regs-iis.h - * +/* * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk> * http://www.simtec.co.uk/products/SWLINUX/ * @@ -10,8 +9,8 @@ * S3C2410 IIS register definition */ -#ifndef __ASM_ARCH_REGS_IIS_H -#define __ASM_ARCH_REGS_IIS_H +#ifndef __SAMSUNG_REGS_IIS_H__ +#define __SAMSUNG_REGS_IIS_H__ #define S3C2410_IISCON (0x00) @@ -67,4 +66,4 @@ #define S3C2410_IISFIFO (0x10) -#endif /* __ASM_ARCH_REGS_IIS_H */ +#endif /* __SAMSUNG_REGS_IIS_H__ */ diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 14011d90d70a..ff60e11ecb56 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -37,6 +37,7 @@ config SND_SOC_SH4_SIU config SND_SOC_RCAR tristate "R-Car series SRU/SCU/SSIU/SSI support" select SND_SIMPLE_CARD + select REGMAP help This option enables R-Car SUR/SCU/SSIU/SSI sound support diff --git a/sound/soc/sh/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/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 9430097979a5..a53235c4d1b0 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -19,8 +19,8 @@ struct rsnd_adg { struct clk *clk[CLKMAX]; - int rate_of_441khz_div_6; - int rate_of_48khz_div_6; + int rbga_rate_for_441khz_div_6; /* RBGA */ + int rbgb_rate_for_48khz_div_6; /* RBGB */ u32 ckr; }; @@ -30,41 +30,114 @@ struct rsnd_adg { i++, (pos) = adg->clk[i]) #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) -static enum rsnd_reg rsnd_adg_ssi_reg_get(int id) +static int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, + struct rsnd_mod *mod, + unsigned int src_rate, + unsigned int dst_rate) { - enum rsnd_reg reg; + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int idx, sel, div, shift; + u32 mask, val; + int id = rsnd_mod_id(mod); + unsigned int sel_rate [] = { + clk_get_rate(adg->clk[CLKA]), /* 000: CLKA */ + clk_get_rate(adg->clk[CLKB]), /* 001: CLKB */ + clk_get_rate(adg->clk[CLKC]), /* 010: CLKC */ + 0, /* 011: MLBCLK (not used) */ + adg->rbga_rate_for_441khz_div_6,/* 100: RBGA */ + adg->rbgb_rate_for_48khz_div_6, /* 101: RBGB */ + }; + + /* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */ + for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { + for (div = 128, idx = 0; + div <= 2048; + div *= 2, idx++) { + if (src_rate == sel_rate[sel] / div) { + val = (idx << 4) | sel; + goto find_rate; + } + } + } + dev_err(dev, "can't find convert src clk\n"); + return -EINVAL; + +find_rate: + shift = (id % 4) * 8; + mask = 0xFF << shift; + val = val << shift; + + dev_dbg(dev, "adg convert src clk = %02x\n", val); + + switch (id / 4) { + case 0: + rsnd_mod_bset(mod, AUDIO_CLK_SEL3, mask, val); + break; + case 1: + rsnd_mod_bset(mod, AUDIO_CLK_SEL4, mask, val); + break; + case 2: + rsnd_mod_bset(mod, AUDIO_CLK_SEL5, mask, val); + break; + } + + /* + * Gen1 doesn't need dst_rate settings, + * since it uses SSI WS pin. + * see also rsnd_src_set_route_if_gen1() + */ + + return 0; +} + +int rsnd_adg_set_convert_clk(struct rsnd_priv *priv, + struct rsnd_mod *mod, + unsigned int src_rate, + unsigned int dst_rate) +{ + if (rsnd_is_gen1(priv)) + return rsnd_adg_set_convert_clk_gen1(priv, mod, + src_rate, dst_rate); + + return -EINVAL; +} + +static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val) +{ + int id = rsnd_mod_id(mod); + int shift = (id % 4) * 8; + u32 mask = 0xFF << shift; + + val = val << shift; /* * SSI 8 is not connected to ADG. * it works with SSI 7 */ if (id == 8) - return RSND_REG_MAX; - - if (0 <= id && id <= 3) - reg = RSND_REG_AUDIO_CLK_SEL0; - else if (4 <= id && id <= 7) - reg = RSND_REG_AUDIO_CLK_SEL1; - else - reg = RSND_REG_AUDIO_CLK_SEL2; - - return reg; + return; + + switch (id / 4) { + case 0: + rsnd_mod_bset(mod, AUDIO_CLK_SEL0, mask, val); + break; + case 1: + rsnd_mod_bset(mod, AUDIO_CLK_SEL1, mask, val); + break; + case 2: + rsnd_mod_bset(mod, AUDIO_CLK_SEL2, mask, val); + break; + } } int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - enum rsnd_reg reg; - int id; - /* * "mod" = "ssi" here. * we can get "ssi id" from mod */ - id = rsnd_mod_id(mod); - reg = rsnd_adg_ssi_reg_get(id); - - rsnd_write(priv, mod, reg, 0); + rsnd_adg_set_ssi_clk(mod, 0); return 0; } @@ -75,8 +148,7 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct device *dev = rsnd_priv_to_dev(priv); struct clk *clk; - enum rsnd_reg reg; - int id, shift, i; + int i; u32 data; int sel_table[] = { [CLKA] = 0x1, @@ -102,12 +174,12 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) /* * find 1/6 clock from BRGA/BRGB */ - if (rate == adg->rate_of_441khz_div_6) { + if (rate == adg->rbga_rate_for_441khz_div_6) { data = 0x10; goto found_clock; } - if (rate == adg->rate_of_48khz_div_6) { + if (rate == adg->rbgb_rate_for_48khz_div_6) { data = 0x20; goto found_clock; } @@ -125,19 +197,10 @@ found_clock: * This "mod" = "ssi" here. * we can get "ssi id" from mod */ - id = rsnd_mod_id(mod); - reg = rsnd_adg_ssi_reg_get(id); - - dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", id, i, rate); - - /* - * Enable SSIx clock - */ - shift = (id % 4) * 8; + rsnd_adg_set_ssi_clk(mod, data); - rsnd_bset(priv, mod, reg, - 0xFF << shift, - data << shift); + dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", + rsnd_mod_id(mod), i, rate); return 0; } @@ -166,8 +229,8 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) * rsnd_adg_ssi_clk_try_start() */ ckr = 0; - adg->rate_of_441khz_div_6 = 0; - adg->rate_of_48khz_div_6 = 0; + adg->rbga_rate_for_441khz_div_6 = 0; + adg->rbgb_rate_for_48khz_div_6 = 0; for_each_rsnd_clk(clk, adg, i) { rate = clk_get_rate(clk); @@ -175,14 +238,14 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) continue; /* RBGA */ - if (!adg->rate_of_441khz_div_6 && (0 == rate % 44100)) { - adg->rate_of_441khz_div_6 = rate / 6; + if (!adg->rbga_rate_for_441khz_div_6 && (0 == rate % 44100)) { + adg->rbga_rate_for_441khz_div_6 = rate / 6; ckr |= brg_table[i] << 20; } /* RBGB */ - if (!adg->rate_of_48khz_div_6 && (0 == rate % 48000)) { - adg->rate_of_48khz_div_6 = rate / 6; + if (!adg->rbgb_rate_for_48khz_div_6 && (0 == rate % 48000)) { + adg->rbgb_rate_for_48khz_div_6 = rate / 6; ckr |= brg_table[i] << 16; } } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index b3653d37f75f..743de5e3b1e1 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -628,12 +628,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, diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 61212ee97c28..add088bd4b2a 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -10,20 +10,6 @@ */ #include "rsnd.h" -struct rsnd_gen_ops { - int (*probe)(struct platform_device *pdev, - struct rcar_snd_info *info, - struct rsnd_priv *priv); - void (*remove)(struct platform_device *pdev, - struct rsnd_priv *priv); - int (*path_init)(struct rsnd_priv *priv, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io); - int (*path_exit)(struct rsnd_priv *priv, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io); -}; - struct rsnd_gen { void __iomem *base[RSND_BASE_MAX]; @@ -86,12 +72,28 @@ static struct regmap_bus rsnd_regmap_bus = { .val_format_endian_default = REGMAP_ENDIAN_NATIVE, }; +static int rsnd_is_accessible_reg(struct rsnd_priv *priv, + struct rsnd_gen *gen, enum rsnd_reg reg) +{ + if (!gen->regs[reg]) { + struct device *dev = rsnd_priv_to_dev(priv); + + dev_err(dev, "unsupported register access %x\n", reg); + return 0; + } + + return 1; +} + u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg) { struct rsnd_gen *gen = rsnd_priv_to_gen(priv); u32 val; + if (!rsnd_is_accessible_reg(priv, gen, reg)) + return 0; + regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); return val; @@ -103,6 +105,9 @@ void rsnd_write(struct rsnd_priv *priv, { struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + if (!rsnd_is_accessible_reg(priv, gen, reg)) + return; + regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); } @@ -111,21 +116,48 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, { struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + if (!rsnd_is_accessible_reg(priv, gen, reg)) + return; + regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod), mask, data); } -/* - * Gen2 - * will be filled in the future - */ +static int rsnd_gen_regmap_init(struct rsnd_priv *priv, + struct rsnd_gen *gen, + struct reg_field *regf) +{ + int i; + struct device *dev = rsnd_priv_to_dev(priv); + struct regmap_config regc; -/* - * Gen1 - */ -static int rsnd_gen1_path_init(struct rsnd_priv *priv, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) + memset(®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; +} + +int rsnd_gen_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) { struct rsnd_mod *mod; int ret; @@ -163,9 +195,9 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv, return ret; } -static int rsnd_gen1_path_exit(struct rsnd_priv *priv, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) +int rsnd_gen_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) { struct rsnd_mod *mod, *n; int ret = 0; @@ -179,6 +211,94 @@ static int rsnd_gen1_path_exit(struct rsnd_priv *priv, return ret; } +/* + * Gen2 + */ + +/* single address mapping */ +#define RSND_GEN2_S_REG(gen, reg, id, offset) \ + RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN2_##reg, offset, 0, 10) + +/* multi address mapping */ +#define RSND_GEN2_M_REG(gen, reg, id, offset, _id_offset) \ + RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN2_##reg, offset, _id_offset, 10) + +static int rsnd_gen2_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) +{ + struct reg_field regf[RSND_REG_MAX] = { + RSND_GEN2_S_REG(gen, SSIU, SSI_MODE0, 0x800), + RSND_GEN2_S_REG(gen, SSIU, SSI_MODE1, 0x804), + /* FIXME: it needs SSI_MODE2/3 in the future */ + RSND_GEN2_M_REG(gen, SSIU, INT_ENABLE, 0x18, 0x80), + + RSND_GEN2_S_REG(gen, ADG, BRRA, 0x00), + RSND_GEN2_S_REG(gen, ADG, BRRB, 0x04), + RSND_GEN2_S_REG(gen, ADG, SSICKR, 0x08), + RSND_GEN2_S_REG(gen, ADG, AUDIO_CLK_SEL0, 0x0c), + RSND_GEN2_S_REG(gen, ADG, AUDIO_CLK_SEL1, 0x10), + RSND_GEN2_S_REG(gen, ADG, AUDIO_CLK_SEL2, 0x14), + + RSND_GEN2_M_REG(gen, SSI, SSICR, 0x00, 0x40), + RSND_GEN2_M_REG(gen, SSI, SSISR, 0x04, 0x40), + RSND_GEN2_M_REG(gen, SSI, SSITDR, 0x08, 0x40), + RSND_GEN2_M_REG(gen, SSI, SSIRDR, 0x0c, 0x40), + RSND_GEN2_M_REG(gen, SSI, SSIWSR, 0x20, 0x40), + }; + + return rsnd_gen_regmap_init(priv, gen, regf); +} + +static int rsnd_gen2_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + struct resource *scu_res; + struct resource *adg_res; + struct resource *ssiu_res; + struct resource *ssi_res; + int ret; + + /* + * map address + */ + scu_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_SCU); + adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_ADG); + ssiu_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_SSIU); + ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_SSI); + + gen->base[RSND_GEN2_SCU] = devm_ioremap_resource(dev, scu_res); + gen->base[RSND_GEN2_ADG] = devm_ioremap_resource(dev, adg_res); + gen->base[RSND_GEN2_SSIU] = devm_ioremap_resource(dev, ssiu_res); + gen->base[RSND_GEN2_SSI] = devm_ioremap_resource(dev, ssi_res); + if (IS_ERR(gen->base[RSND_GEN2_SCU]) || + IS_ERR(gen->base[RSND_GEN2_ADG]) || + IS_ERR(gen->base[RSND_GEN2_SSIU]) || + IS_ERR(gen->base[RSND_GEN2_SSI])) + return -ENODEV; + + ret = rsnd_gen2_regmap_init(priv, gen); + if (ret < 0) + return ret; + + dev_dbg(dev, "Gen2 device probed\n"); + dev_dbg(dev, "SRU : %08x => %p\n", scu_res->start, + gen->base[RSND_GEN2_SCU]); + dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start, + gen->base[RSND_GEN2_ADG]); + dev_dbg(dev, "SSIU : %08x => %p\n", ssiu_res->start, + gen->base[RSND_GEN2_SSIU]); + dev_dbg(dev, "SSI : %08x => %p\n", ssi_res->start, + gen->base[RSND_GEN2_SSI]); + + return 0; +} + +/* + * Gen1 + */ + /* single address mapping */ #define RSND_GEN1_S_REG(gen, reg, id, offset) \ RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN1_##reg, offset, 0, 9) @@ -189,19 +309,23 @@ static int rsnd_gen1_path_exit(struct rsnd_priv *priv, static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) { - int i; - struct device *dev = rsnd_priv_to_dev(priv); - struct regmap_config regc; struct reg_field regf[RSND_REG_MAX] = { RSND_GEN1_S_REG(gen, SRU, SRC_ROUTE_SEL, 0x00), RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL0, 0x08), RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL1, 0x0c), RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL2, 0x10), - RSND_GEN1_S_REG(gen, SRU, SRC_CTRL, 0xc0), + RSND_GEN1_S_REG(gen, SRU, SRC_ROUTE_CTRL, 0xc0), RSND_GEN1_S_REG(gen, SRU, SSI_MODE0, 0xD0), RSND_GEN1_S_REG(gen, SRU, SSI_MODE1, 0xD4), RSND_GEN1_M_REG(gen, SRU, BUSIF_MODE, 0x20, 0x4), - RSND_GEN1_M_REG(gen, SRU, BUSIF_ADINR, 0x214, 0x40), + RSND_GEN1_M_REG(gen, SRU, SRC_ROUTE_MODE0,0x50, 0x8), + RSND_GEN1_M_REG(gen, SRU, SRC_SWRSR, 0x200, 0x40), + RSND_GEN1_M_REG(gen, SRU, SRC_SRCIR, 0x204, 0x40), + RSND_GEN1_M_REG(gen, SRU, SRC_ADINR, 0x214, 0x40), + RSND_GEN1_M_REG(gen, SRU, SRC_IFSCR, 0x21c, 0x40), + RSND_GEN1_M_REG(gen, SRU, SRC_IFSVR, 0x220, 0x40), + RSND_GEN1_M_REG(gen, SRU, SRC_SRCCR, 0x224, 0x40), + RSND_GEN1_M_REG(gen, SRU, SRC_MNFSR, 0x228, 0x40), RSND_GEN1_S_REG(gen, ADG, BRRA, 0x00), RSND_GEN1_S_REG(gen, ADG, BRRB, 0x04), @@ -219,24 +343,7 @@ static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) RSND_GEN1_M_REG(gen, SSI, SSIWSR, 0x20, 0x40), }; - memset(®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, @@ -281,45 +388,16 @@ static int rsnd_gen1_probe(struct platform_device *pdev, } -static void rsnd_gen1_remove(struct platform_device *pdev, - struct rsnd_priv *priv) -{ -} - -static struct rsnd_gen_ops rsnd_gen1_ops = { - .probe = rsnd_gen1_probe, - .remove = rsnd_gen1_remove, - .path_init = rsnd_gen1_path_init, - .path_exit = rsnd_gen1_path_exit, -}; - /* * Gen */ -int rsnd_gen_path_init(struct rsnd_priv *priv, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - - return gen->ops->path_init(priv, rdai, io); -} - -int rsnd_gen_path_exit(struct rsnd_priv *priv, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - - return gen->ops->path_exit(priv, rdai, io); -} - int rsnd_gen_probe(struct platform_device *pdev, struct rcar_snd_info *info, struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen; + int ret; gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); if (!gen) { @@ -327,23 +405,21 @@ int rsnd_gen_probe(struct platform_device *pdev, return -ENOMEM; } + priv->gen = gen; + + ret = -ENODEV; if (rsnd_is_gen1(priv)) - gen->ops = &rsnd_gen1_ops; + ret = rsnd_gen1_probe(pdev, info, priv); + else if (rsnd_is_gen2(priv)) + ret = rsnd_gen2_probe(pdev, info, priv); - if (!gen->ops) { + if (ret < 0) dev_err(dev, "unknown generation R-Car sound device\n"); - return -ENODEV; - } - - priv->gen = gen; - return gen->ops->probe(pdev, info, priv); + return ret; } void rsnd_gen_remove(struct platform_device *pdev, struct rsnd_priv *priv) { - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - - gen->ops->remove(pdev, priv); } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 9e463e50e7e6..4ca66cd899c8 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -31,16 +31,24 @@ * see gen1/gen2 for detail */ enum rsnd_reg { - /* SRU/SCU */ - RSND_REG_SRC_ROUTE_SEL, - RSND_REG_SRC_TMG_SEL0, - RSND_REG_SRC_TMG_SEL1, - RSND_REG_SRC_TMG_SEL2, - RSND_REG_SRC_CTRL, + /* SRU/SCU/SSIU */ + RSND_REG_SRC_ROUTE_SEL, /* for Gen1 */ + RSND_REG_SRC_TMG_SEL0, /* for Gen1 */ + RSND_REG_SRC_TMG_SEL1, /* for Gen1 */ + RSND_REG_SRC_TMG_SEL2, /* for Gen1 */ + RSND_REG_SRC_ROUTE_CTRL, /* for Gen1 */ RSND_REG_SSI_MODE0, RSND_REG_SSI_MODE1, RSND_REG_BUSIF_MODE, - RSND_REG_BUSIF_ADINR, + RSND_REG_INT_ENABLE, /* for Gen2 */ + RSND_REG_SRC_ROUTE_MODE0, + RSND_REG_SRC_SWRSR, + RSND_REG_SRC_SRCIR, + RSND_REG_SRC_ADINR, + RSND_REG_SRC_IFSCR, + RSND_REG_SRC_IFSVR, + RSND_REG_SRC_SRCCR, + RSND_REG_SRC_MNFSR, /* ADG */ RSND_REG_BRRA, @@ -49,9 +57,9 @@ enum rsnd_reg { RSND_REG_AUDIO_CLK_SEL0, RSND_REG_AUDIO_CLK_SEL1, RSND_REG_AUDIO_CLK_SEL2, - RSND_REG_AUDIO_CLK_SEL3, - RSND_REG_AUDIO_CLK_SEL4, - RSND_REG_AUDIO_CLK_SEL5, + RSND_REG_AUDIO_CLK_SEL3, /* for Gen1 */ + RSND_REG_AUDIO_CLK_SEL4, /* for Gen1 */ + RSND_REG_AUDIO_CLK_SEL5, /* for Gen1 */ /* SSI */ RSND_REG_SSICR, @@ -174,11 +182,11 @@ struct rsnd_dai { struct rsnd_dai_stream playback; struct rsnd_dai_stream capture; - int clk_master:1; - int bit_clk_inv:1; - int frm_clk_inv:1; - int sys_delay:1; - int data_alignment:1; + unsigned int clk_master:1; + unsigned int bit_clk_inv:1; + unsigned int frm_clk_inv:1; + unsigned int sys_delay:1; + unsigned int data_alignment:1; }; #define rsnd_dai_nr(priv) ((priv)->dai_nr) @@ -229,6 +237,10 @@ int rsnd_adg_probe(struct platform_device *pdev, struct rsnd_priv *priv); void rsnd_adg_remove(struct platform_device *pdev, struct rsnd_priv *priv); +int rsnd_adg_set_convert_clk(struct rsnd_priv *priv, + struct rsnd_mod *mod, + unsigned int src_rate, + unsigned int dst_rate); /* * R-Car sound priv @@ -282,6 +294,10 @@ void rsnd_scu_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod); +unsigned int rsnd_scu_get_ssi_rate(struct rsnd_priv *priv, + struct rsnd_mod *ssi_mod, + struct snd_pcm_runtime *runtime); + #define rsnd_scu_nr(priv) ((priv)->scu_nr) /* diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c index fa8fa15860b9..9bb08bb1d455 100644 --- a/sound/soc/sh/rcar/scu.c +++ b/sound/soc/sh/rcar/scu.c @@ -13,9 +13,13 @@ struct rsnd_scu { struct rsnd_scu_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; + struct clk *clk; }; #define rsnd_scu_mode_flags(p) ((p)->info->flags) +#define rsnd_scu_convert_rate(p) ((p)->info->convert_rate) + +#define RSND_SCU_NAME_SIZE 16 /* * ADINR @@ -26,6 +30,15 @@ struct rsnd_scu { #define OTBL_18 (6 << 16) #define OTBL_16 (8 << 16) +/* + * image of SRC (Sampling Rate Converter) + * + * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+ + * 48kHz <-> | SRC | <------> | SSI | <-----> | codec | + * 44.1kHz <-> +-----+ +-----+ +-------+ + * ... + * + */ #define rsnd_mod_to_scu(_mod) \ container_of((_mod), struct rsnd_scu, mod) @@ -36,7 +49,8 @@ struct rsnd_scu { ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ i++) -static int rsnd_scu_set_route(struct rsnd_priv *priv, +/* Gen1 only */ +static int rsnd_src_set_route_if_gen1(struct rsnd_priv *priv, struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) @@ -55,7 +69,7 @@ static int rsnd_scu_set_route(struct rsnd_priv *priv, { 0x3, 28, }, /* 7 */ { 0x3, 30, }, /* 8 */ }; - + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); u32 mask; u32 val; int shift; @@ -85,9 +99,18 @@ static int rsnd_scu_set_route(struct rsnd_priv *priv, */ shift = (id % 4) * 8; mask = 0x1F << shift; - if (8 == id) /* SRU8 is very special */ + + /* + * ADG is used as source clock if SRC was used, + * then, SSI WS is used as destination clock. + * SSI WS is used as source clock if SRC is not used + * (when playback, source/destination become reverse when capture) + */ + if (rsnd_scu_convert_rate(scu)) /* use ADG */ + val = 0; + else if (8 == id) /* use SSI WS, but SRU8 is special */ val = id << shift; - else + else /* use SSI WS */ val = (id + 1) << shift; switch (id / 4) { @@ -105,30 +128,45 @@ static int rsnd_scu_set_route(struct rsnd_priv *priv, return 0; } -static int rsnd_scu_set_mode(struct rsnd_priv *priv, - struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) +unsigned int rsnd_scu_get_ssi_rate(struct rsnd_priv *priv, + struct rsnd_mod *ssi_mod, + struct snd_pcm_runtime *runtime) { - int id = rsnd_mod_id(mod); - u32 val; + struct rsnd_scu *scu; + unsigned int rate; - if (rsnd_is_gen1(priv)) { - val = (1 << id); - rsnd_mod_bset(mod, SRC_CTRL, val, val); - } + /* this function is assuming SSI id = SCU id here */ + scu = rsnd_mod_to_scu(rsnd_scu_mod_get(priv, rsnd_mod_id(ssi_mod))); - return 0; + /* + * return convert rate if SRC is used, + * otherwise, return runtime->rate as usual + */ + rate = rsnd_scu_convert_rate(scu); + if (!rate) + rate = runtime->rate; + + return rate; } -static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, +static int rsnd_scu_convert_rate_ctrl(struct rsnd_priv *priv, struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); + u32 convert_rate = rsnd_scu_convert_rate(scu); u32 adinr = runtime->channels; + /* set/clear soft reset */ + rsnd_mod_write(mod, SRC_SWRSR, 0); + rsnd_mod_write(mod, SRC_SWRSR, 1); + + /* Initialize the operation of the SRC internal circuits */ + rsnd_mod_write(mod, SRC_SRCIR, 1); + + /* Set channel number and output bit length */ switch (runtime->sample_bits) { case 16: adinr |= OTBL_16; @@ -139,9 +177,81 @@ static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, default: return -EIO; } + rsnd_mod_write(mod, SRC_ADINR, adinr); + + if (convert_rate) { + u32 fsrate = 0x0400000 / convert_rate * runtime->rate; + int ret; + + /* Enable the initial value of IFS */ + rsnd_mod_write(mod, SRC_IFSCR, 1); + + /* Set initial value of IFS */ + rsnd_mod_write(mod, SRC_IFSVR, fsrate); + + /* Select SRC mode (fixed value) */ + rsnd_mod_write(mod, SRC_SRCCR, 0x00010110); + + /* Set the restriction value of the FS ratio (98%) */ + rsnd_mod_write(mod, SRC_MNFSR, fsrate / 100 * 98); + + if (rsnd_is_gen1(priv)) { + /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */ + } + /* set convert clock */ + ret = rsnd_adg_set_convert_clk(priv, mod, + runtime->rate, + convert_rate); + if (ret < 0) + return ret; + } + + /* Cancel the initialization and operate the SRC function */ + rsnd_mod_write(mod, SRC_SRCIR, 0); + + /* use DMA transfer */ rsnd_mod_write(mod, BUSIF_MODE, 1); - rsnd_mod_write(mod, BUSIF_ADINR, adinr); + + return 0; +} + +static int rsnd_scu_transfer_start(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); + int id = rsnd_mod_id(mod); + u32 val; + + if (rsnd_is_gen1(priv)) { + val = (1 << id); + rsnd_mod_bset(mod, SRC_ROUTE_CTRL, val, val); + } + + if (rsnd_scu_convert_rate(scu)) + rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); + + return 0; +} + +static int rsnd_scu_transfer_stop(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); + int id = rsnd_mod_id(mod); + u32 mask; + + if (rsnd_is_gen1(priv)) { + mask = (1 << id); + rsnd_mod_bset(mod, SRC_ROUTE_CTRL, mask, 0); + } + + if (rsnd_scu_convert_rate(scu)) + rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0); return 0; } @@ -159,6 +269,7 @@ static int rsnd_scu_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); struct device *dev = rsnd_priv_to_dev(priv); int ret; @@ -173,16 +284,19 @@ static int rsnd_scu_start(struct rsnd_mod *mod, return 0; } + clk_enable(scu->clk); + /* it use DMA transter */ - ret = rsnd_scu_set_route(priv, mod, rdai, io); + + ret = rsnd_src_set_route_if_gen1(priv, mod, rdai, io); if (ret < 0) return ret; - ret = rsnd_scu_set_mode(priv, mod, rdai, io); + ret = rsnd_scu_convert_rate_ctrl(priv, mod, rdai, io); if (ret < 0) return ret; - ret = rsnd_scu_set_hpbif(priv, mod, rdai, io); + ret = rsnd_scu_transfer_start(priv, mod, rdai, io); if (ret < 0) return ret; @@ -191,9 +305,27 @@ static int rsnd_scu_start(struct rsnd_mod *mod, return 0; } +static int rsnd_scu_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); + + if (!rsnd_scu_hpbif_is_enable(mod)) + return 0; + + rsnd_scu_transfer_stop(priv, mod, rdai, io); + + clk_disable(scu->clk); + + return 0; +} + static struct rsnd_mod_ops rsnd_scu_ops = { .name = "scu", .start = rsnd_scu_start, + .stop = rsnd_scu_stop, }; struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) @@ -210,6 +342,8 @@ int rsnd_scu_probe(struct platform_device *pdev, { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_scu *scu; + struct clk *clk; + char name[RSND_SCU_NAME_SIZE]; int i, nr; /* @@ -226,9 +360,16 @@ int rsnd_scu_probe(struct platform_device *pdev, priv->scu = scu; for_each_rsnd_scu(scu, priv, i) { + snprintf(name, RSND_SCU_NAME_SIZE, "scu.%d", i); + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + rsnd_mod_init(priv, &scu->mod, &rsnd_scu_ops, i); scu->info = &info->scu_info[i]; + scu->clk = clk; dev_dbg(dev, "SCU%d probed\n", i); } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 5ac20cd5e006..4b8cf7ca9d19 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -187,9 +187,10 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, } static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, - unsigned int rate) + struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); int i, j, ret; int adg_clk_div_table[] = { @@ -199,6 +200,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, 1, 2, 4, 8, 16, 6, 12, }; unsigned int main_rate; + unsigned int rate = rsnd_scu_get_ssi_rate(priv, &ssi->mod, runtime); /* * Find best clock, and try to start ADG @@ -209,7 +211,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, /* * this driver is assuming that * system word is 64fs (= 2 x 32bit) - * see rsnd_ssi_start() + * see rsnd_ssi_init() */ main_rate = rate / adg_clk_div_table[i] * 32 * 2 * ssi_clk_mul_table[j]; @@ -251,14 +253,10 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, clk_enable(ssi->clk); if (rsnd_rdai_is_clk_master(rdai)) { - struct snd_pcm_runtime *runtime; - - runtime = rsnd_io_to_runtime(io); - if (rsnd_ssi_clk_from_parent(ssi)) rsnd_ssi_hw_start(ssi->parent, rdai, io); else - rsnd_ssi_master_clk_start(ssi, runtime->rate); + rsnd_ssi_master_clk_start(ssi, io); } } @@ -457,6 +455,10 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod, /* enable PIO IRQ */ ssi->cr_etc = UIEN | OIEN | DIEN; + /* enable PIO interrupt if gen2 */ + if (rsnd_is_gen2(priv)) + rsnd_mod_write(&ssi->mod, INT_ENABLE, 0x0f000000); + rsnd_ssi_hw_start(ssi, rdai, io); dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); @@ -650,7 +652,7 @@ int rsnd_ssi_probe(struct platform_device *pdev, snprintf(name, RSND_SSI_NAME_SIZE, "ssi.%d", i); - clk = clk_get(dev, name); + clk = devm_clk_get(dev, name); if (IS_ERR(clk)) return PTR_ERR(clk); @@ -711,7 +713,6 @@ void rsnd_ssi_remove(struct platform_device *pdev, int i; for_each_rsnd_ssi(ssi, priv, i) { - clk_put(ssi->clk); if (rsnd_ssi_dma_available(ssi)) rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod)); } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4e53d87e881d..fe1df50805a3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1728,6 +1728,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); @@ -3212,11 +3213,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; @@ -3484,7 +3485,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 +3506,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); @@ -4617,10 +4618,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..2a44fe9122a2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -371,12 +371,16 @@ 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; @@ -430,13 +434,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) { @@ -513,7 +516,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, unsigned int invert = mc->invert; if (reg != SND_SOC_NOPM) { - val = soc_widget_read(w, reg); + soc_widget_read(w, reg, &val); val = (val >> shift) & mask; if (invert) val = max - val; @@ -529,7 +532,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, w->kcontrol_news[i].private_value; int val, item; - val = soc_widget_read(w, e->reg); + soc_widget_read(w, e->reg, &val); item = (val >> e->shift_l) & e->mask; if (item < e->max && !strcmp(p->name, e->texts[item])) @@ -558,7 +561,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, w->kcontrol_news[i].private_value; int val, item; - val = soc_widget_read(w, e->reg); + soc_widget_read(w, e->reg, &val); val = (val >> e->shift_l) & e->mask; for (item = 0; item < e->max; item++) { if (val == e->values[item]) @@ -2782,7 +2785,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 +2872,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 +2906,16 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, card->update = &update; } - soc_dapm_mixer_update_power(card, kcontrol, connect); + ret = soc_dapm_mixer_update_power(card, kcontrol, connect); card->update = NULL; } mutex_unlock(&card->dapm_mutex); + + if (ret > 0) + soc_dpcm_runtime_update(card); + return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); @@ -2955,6 +2964,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, unsigned int val, mux, change; unsigned int mask; struct snd_soc_dapm_update update; + int ret = 0; if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -2978,12 +2988,16 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, update.val = val; card->update = &update; - soc_dapm_mux_update_power(card, kcontrol, mux, e); + ret = soc_dapm_mux_update_power(card, kcontrol, mux, e); card->update = NULL; } mutex_unlock(&card->dapm_mutex); + + if (ret > 0) + soc_dpcm_runtime_update(card); + return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); @@ -3019,6 +3033,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; + int ret = 0; if (ucontrol->value.enumerated.item[0] >= e->max) return -EINVAL; @@ -3028,9 +3043,13 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, value = ucontrol->value.enumerated.item[0]; change = dapm_kcontrol_set_value(kcontrol, value); if (change) - soc_dapm_mux_update_power(card, kcontrol, value, e); + ret = soc_dapm_mux_update_power(card, kcontrol, value, e); mutex_unlock(&card->dapm_mutex); + + if (ret > 0) + soc_dpcm_runtime_update(card); + return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); @@ -3097,6 +3116,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, unsigned int val, mux, change; unsigned int mask; struct snd_soc_dapm_update update; + int ret = 0; if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -3120,12 +3140,16 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, update.val = val; card->update = &update; - soc_dapm_mux_update_power(card, kcontrol, mux, e); + ret = soc_dapm_mux_update_power(card, kcontrol, mux, e); card->update = NULL; } mutex_unlock(&card->dapm_mutex); + + if (ret > 0) + soc_dpcm_runtime_update(card); + return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); @@ -3614,6 +3638,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); + } + + /* 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); + } + + } +} + static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, int event) { 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..560a7787d8a7 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); @@ -184,6 +189,21 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( 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) { struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); @@ -236,6 +256,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 +275,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 +294,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 +304,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 +383,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 +392,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 +421,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 +428,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..5932971cf54d 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -84,35 +84,117 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; int ret; - if (!soc_dai->driver->symmetric_rates && - !rtd->dai_link->symmetric_rates) - return 0; + if (soc_dai->rate && (soc_dai->driver->symmetric_rates || + rtd->dai_link->symmetric_rates)) { + dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", + soc_dai->rate); + + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + soc_dai->rate, soc_dai->rate); + if (ret < 0) { + dev_err(soc_dai->dev, + "ASoC: Unable to apply rate constraint: %d\n", + ret); + return ret; + } + } - /* This can happen if multiple streams are starting simultaneously - - * the second can need to get its constraints before the first has - * picked a rate. Complain and allow the application to carry on. - */ - if (!soc_dai->rate) { - dev_warn(soc_dai->dev, - "ASoC: Not enforcing symmetric_rates due to race\n"); - return 0; + if (soc_dai->channels && (soc_dai->driver->symmetric_channels || + rtd->dai_link->symmetric_channels)) { + dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n", + soc_dai->channels); + + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + soc_dai->channels, + soc_dai->channels); + if (ret < 0) { + dev_err(soc_dai->dev, + "ASoC: Unable to apply channel symmetry constraint: %d\n", + ret); + return ret; + } } - dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n", soc_dai->rate); + if (soc_dai->sample_bits && (soc_dai->driver->symmetric_samplebits || + rtd->dai_link->symmetric_samplebits)) { + dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n", + soc_dai->sample_bits); - ret = snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_RATE, - soc_dai->rate, soc_dai->rate); - if (ret < 0) { - dev_err(soc_dai->dev, - "ASoC: Unable to apply rate symmetry constraint: %d\n", - ret); - return ret; + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + soc_dai->sample_bits, + soc_dai->sample_bits); + if (ret < 0) { + dev_err(soc_dai->dev, + "ASoC: Unable to apply sample bits symmetry constraint: %d\n", + ret); + return ret; + } + } + + return 0; +} + +static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + unsigned int rate, channels, sample_bits, symmetry; + + rate = params_rate(params); + channels = params_channels(params); + sample_bits = snd_pcm_format_physical_width(params_format(params)); + + /* reject unmatched parameters when applying symmetry */ + symmetry = cpu_dai->driver->symmetric_rates || + codec_dai->driver->symmetric_rates || + rtd->dai_link->symmetric_rates; + if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) { + dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n", + cpu_dai->rate, rate); + return -EINVAL; + } + + symmetry = cpu_dai->driver->symmetric_channels || + codec_dai->driver->symmetric_channels || + rtd->dai_link->symmetric_channels; + if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) { + dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n", + cpu_dai->channels, channels); + return -EINVAL; + } + + symmetry = cpu_dai->driver->symmetric_samplebits || + codec_dai->driver->symmetric_samplebits || + rtd->dai_link->symmetric_samplebits; + if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) { + dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n", + cpu_dai->sample_bits, sample_bits); + return -EINVAL; } return 0; } +static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver; + struct snd_soc_dai_driver *codec_driver = rtd->codec_dai->driver; + struct snd_soc_dai_link *link = rtd->dai_link; + + return cpu_driver->symmetric_rates || codec_driver->symmetric_rates || + link->symmetric_rates || cpu_driver->symmetric_channels || + codec_driver->symmetric_channels || link->symmetric_channels || + cpu_driver->symmetric_samplebits || + codec_driver->symmetric_samplebits || + link->symmetric_samplebits; +} + /* * List of sample sizes that might go over the bus for parameter * application. There ought to be a wildcard sample size for things @@ -148,24 +230,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 +325,17 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) /* Check that the codec and cpu DAIs are compatible */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->playback, + soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->playback, &cpu_dai_drv->playback); } else { - soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->capture, + soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->capture, &cpu_dai_drv->capture); } + if (soc_pcm_has_symmetry(substream)) + runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; + ret = -EINVAL; - snd_pcm_limit_hw_rates(runtime); if (!runtime->hw.rates) { printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n", codec_dai->name, cpu_dai->name); @@ -390,11 +482,6 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) if (!codec_dai->active) codec_dai->rate = 0; - /* Muting the DAC suppresses artifacts caused during digital - * shutdown, for example from stopping clocks. - */ - snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); - if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); @@ -525,6 +612,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 +652,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 +692,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 +777,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; @@ -1124,6 +1236,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 +1257,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) @@ -2026,10 +2141,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..fe99f461aff0 100644 --- a/sound/soc/spear/spdif_out.c +++ b/sound/soc/spear/spdif_out.c @@ -18,10 +18,12 @@ #include <linux/ioport.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <sound/dmaengine_pcm.h> #include <sound/soc.h> #include <sound/spear_dma.h> #include <sound/spear_spdif.h> #include "spdif_out_regs.h" +#include "spear_pcm.h" struct spdif_out_params { u32 rate; @@ -35,6 +37,8 @@ struct spdif_out_dev { struct spdif_out_params saved_params; u32 running; void __iomem *io_base; + struct snd_dmaengine_dai_dma_data dma_params_tx; + struct snd_dmaengine_pcm_config config; }; static void spdif_out_configure(struct spdif_out_dev *host) @@ -244,7 +248,8 @@ static int spdif_soc_dai_probe(struct snd_soc_dai *dai) { struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai); - dai->playback_dma_data = &host->dma_params; + host->dma_params_tx.filter_data = &host->dma_params; + dai->playback_dma_data = &host->dma_params_tx; return snd_soc_add_dai_controls(dai, spdif_out_controls, ARRAY_SIZE(spdif_out_controls)); @@ -280,6 +285,7 @@ static int spdif_out_probe(struct platform_device *pdev) struct spdif_out_dev *host; struct spear_spdif_platform_data *pdata; struct resource *res; + int ret; host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); if (!host) { @@ -302,12 +308,16 @@ static int spdif_out_probe(struct platform_device *pdev) host->dma_params.addr = res->start + SPDIF_OUT_FIFO_DATA; host->dma_params.max_burst = 16; host->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - host->dma_params.filter = pdata->filter; dev_set_drvdata(&pdev->dev, host); - return devm_snd_soc_register_component(&pdev->dev, &spdif_out_component, - &spdif_out_dai, 1); + ret = devm_snd_soc_register_component(&pdev->dev, &spdif_out_component, + &spdif_out_dai, 1); + if (ret) + return ret; + + return devm_spear_pcm_platform_register(&pdev->dev, &host->config, + pdata->filter); } #ifdef CONFIG_PM diff --git a/sound/soc/spear/spear_pcm.c b/sound/soc/spear/spear_pcm.c index 4707f2b862c3..0e5a8f35d0ad 100644 --- a/sound/soc/spear/spear_pcm.c +++ b/sound/soc/spear/spear_pcm.c @@ -18,6 +18,7 @@ #include <sound/pcm.h> #include <sound/soc.h> #include <sound/spear_dma.h> +#include "spear_pcm.h" static const struct snd_pcm_hardware spear_pcm_hardware = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -31,49 +32,24 @@ static const struct snd_pcm_hardware spear_pcm_hardware = { .fifo_size = 0, /* fifo size in bytes */ }; -static struct dma_chan *spear_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_substream *substream) -{ - struct spear_dma_data *dma_data; - - dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - return snd_dmaengine_pcm_request_channel(dma_data->filter, dma_data); -} - static const struct snd_dmaengine_pcm_config spear_dmaengine_pcm_config = { .pcm_hardware = &spear_pcm_hardware, - .compat_request_channel = spear_pcm_request_chan, .prealloc_buffer_size = 16 * 1024, }; -static int spear_soc_platform_probe(struct platform_device *pdev) +int devm_spear_pcm_platform_register(struct device *dev, + struct snd_dmaengine_pcm_config *config, + bool (*filter)(struct dma_chan *chan, void *slave)) { - return snd_dmaengine_pcm_register(&pdev->dev, - &spear_dmaengine_pcm_config, + *config = spear_dmaengine_pcm_config; + config->compat_filter_fn = filter; + + return snd_dmaengine_pcm_register(dev, config, SND_DMAENGINE_PCM_FLAG_NO_DT | SND_DMAENGINE_PCM_FLAG_COMPAT); } - -static int spear_soc_platform_remove(struct platform_device *pdev) -{ - snd_dmaengine_pcm_unregister(&pdev->dev); - return 0; -} - -static struct platform_driver spear_pcm_driver = { - .driver = { - .name = "spear-pcm-audio", - .owner = THIS_MODULE, - }, - - .probe = spear_soc_platform_probe, - .remove = spear_soc_platform_remove, -}; - -module_platform_driver(spear_pcm_driver); +EXPORT_SYMBOL_GPL(devm_spear_pcm_platform_register); MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); MODULE_DESCRIPTION("SPEAr PCM DMA module"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:spear-pcm-audio"); diff --git a/sound/soc/spear/spear_pcm.h b/sound/soc/spear/spear_pcm.h new file mode 100644 index 000000000000..9b0ca62d6f02 --- /dev/null +++ b/sound/soc/spear/spear_pcm.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __SPEAR_PCM_H__ +#define __SPEAR_PCM_H__ + +int devm_spear_pcm_platform_register(struct device *dev, + struct snd_dmaengine_pcm_config *config, + bool (*filter)(struct dma_chan *chan, void *slave)); + +#endif diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 8fc653ca3ab4..65a85f542521 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -116,3 +116,13 @@ config SND_SOC_TEGRA_ALC5632 help Say Y or M here if you want to add support for SoC audio on the Toshiba AC100 netbook. + +config SND_SOC_TEGRA_MAX98090 + tristate "SoC Audio support for Tegra boards using a MAX98090 codec" + depends on SND_SOC_TEGRA && I2C && GPIOLIB + select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC + select SND_SOC_MAX98090 + help + Say Y or M here if you want to add support for SoC audio on Tegra + boards using the MAX98090 codec, such as Venice2. diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 21d2550a08a4..5ae588cd96c4 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -24,6 +24,7 @@ snd-soc-tegra-wm8903-objs := tegra_wm8903.o snd-soc-tegra-wm9712-objs := tegra_wm9712.o snd-soc-tegra-trimslice-objs := trimslice.o snd-soc-tegra-alc5632-objs := tegra_alc5632.o +snd-soc-tegra-max98090-objs := tegra_max98090.o obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o @@ -31,3 +32,4 @@ obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o +obj-$(CONFIG_SND_SOC_TEGRA_MAX98090) += snd-soc-tegra-max98090.o diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index ae27bcd586d2..088518d7694a 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -404,7 +404,7 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) ret = snd_soc_set_ac97_ops(&tegra20_ac97_ops); if (ret) { dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret); - goto err_asoc_utils_fini; + goto err_clk_disable_unprepare; } ret = snd_soc_register_component(&pdev->dev, &tegra20_ac97_component, @@ -412,7 +412,7 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); ret = -ENOMEM; - goto err_asoc_utils_fini; + goto err_clk_disable_unprepare; } ret = tegra_pcm_platform_register(&pdev->dev); @@ -428,6 +428,8 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) err_unregister_component: snd_soc_unregister_component(&pdev->dev); +err_clk_disable_unprepare: + clk_disable_unprepare(ac97->clk_ac97); err_asoc_utils_fini: tegra_asoc_utils_fini(&ac97->util_data); err_clk_put: diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index 364bf6a907e1..8c819f811470 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -74,7 +74,7 @@ static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); - unsigned int mask, val; + unsigned int mask = 0, val = 0; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: @@ -83,10 +83,10 @@ static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai, return -EINVAL; } - mask = TEGRA20_I2S_CTRL_MASTER_ENABLE; + mask |= TEGRA20_I2S_CTRL_MASTER_ENABLE; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: - val = TEGRA20_I2S_CTRL_MASTER_ENABLE; + val |= TEGRA20_I2S_CTRL_MASTER_ENABLE; break; case SND_SOC_DAIFMT_CBM_CFM: break; diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index 08bc6931c7c7..8c7c1028e579 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -67,15 +67,15 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, { struct device *dev = dai->dev; struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); - unsigned int mask, val; + unsigned int mask = 0, val = 0; int ret, spdifclock; - mask = TEGRA20_SPDIF_CTRL_PACK | - TEGRA20_SPDIF_CTRL_BIT_MODE_MASK; + mask |= TEGRA20_SPDIF_CTRL_PACK | + TEGRA20_SPDIF_CTRL_BIT_MODE_MASK; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - val = TEGRA20_SPDIF_CTRL_PACK | - TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT; + val |= TEGRA20_SPDIF_CTRL_PACK | + TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT; break; default: return -EINVAL; diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index 231a785b3921..02247fee1cf7 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -118,7 +118,7 @@ static int tegra30_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); - unsigned int mask, val; + unsigned int mask = 0, val = 0; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: @@ -127,10 +127,10 @@ static int tegra30_i2s_set_fmt(struct snd_soc_dai *dai, return -EINVAL; } - mask = TEGRA30_I2S_CTRL_MASTER_ENABLE; + mask |= TEGRA30_I2S_CTRL_MASTER_ENABLE; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: - val = TEGRA30_I2S_CTRL_MASTER_ENABLE; + val |= TEGRA30_I2S_CTRL_MASTER_ENABLE; break; case SND_SOC_DAIFMT_CBM_CFM: break; diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c new file mode 100644 index 000000000000..0283cfb7c031 --- /dev/null +++ b/sound/soc/tegra/tegra_max98090.c @@ -0,0 +1,275 @@ +/* + * Tegra machine ASoC driver for boards using a MAX90809 CODEC. + * + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Based on code copyright/by: + * + * Copyright (C) 2010-2012 - NVIDIA, Inc. + * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net> + * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. + * Copyright 2007 Wolfson Microelectronics PLC. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> + +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra_asoc_utils.h" + +#define DRV_NAME "tegra-snd-max98090" + +struct tegra_max98090 { + struct tegra_asoc_utils_data util_data; + int gpio_hp_det; +}; + +static int tegra_max98090_asoc_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_card *card = codec->card; + struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card); + int srate, mclk; + int err; + + srate = params_rate(params); + switch (srate) { + case 8000: + case 16000: + case 24000: + case 32000: + case 48000: + case 64000: + case 96000: + mclk = 12288000; + break; + case 11025: + case 22050: + case 44100: + case 88200: + mclk = 11289600; + break; + default: + mclk = 12000000; + break; + } + + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); + if (err < 0) { + dev_err(card->dev, "Can't configure clocks\n"); + return err; + } + + err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (err < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return err; + } + + return 0; +} + +static struct snd_soc_ops tegra_max98090_ops = { + .hw_params = tegra_max98090_asoc_hw_params, +}; + +static struct snd_soc_jack tegra_max98090_hp_jack; + +static struct snd_soc_jack_pin tegra_max98090_hp_jack_pins[] = { + { + .pin = "Headphones", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static struct snd_soc_jack_gpio tegra_max98090_hp_jack_gpio = { + .name = "Headphone detection", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, + .invert = 1, +}; + +static const struct snd_soc_dapm_widget tegra_max98090_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), +}; + +static const struct snd_kcontrol_new tegra_max98090_controls[] = { + SOC_DAPM_PIN_SWITCH("Speakers"), +}; + +static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; + struct tegra_max98090 *machine = snd_soc_card_get_drvdata(codec->card); + + if (gpio_is_valid(machine->gpio_hp_det)) { + snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE, + &tegra_max98090_hp_jack); + snd_soc_jack_add_pins(&tegra_max98090_hp_jack, + ARRAY_SIZE(tegra_max98090_hp_jack_pins), + tegra_max98090_hp_jack_pins); + + tegra_max98090_hp_jack_gpio.gpio = machine->gpio_hp_det; + snd_soc_jack_add_gpios(&tegra_max98090_hp_jack, + 1, + &tegra_max98090_hp_jack_gpio); + } + + return 0; +} + +static struct snd_soc_dai_link tegra_max98090_dai = { + .name = "max98090", + .stream_name = "max98090 PCM", + .codec_dai_name = "HiFi", + .init = tegra_max98090_asoc_init, + .ops = &tegra_max98090_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, +}; + +static struct snd_soc_card snd_soc_tegra_max98090 = { + .name = "tegra-max98090", + .owner = THIS_MODULE, + .dai_link = &tegra_max98090_dai, + .num_links = 1, + .controls = tegra_max98090_controls, + .num_controls = ARRAY_SIZE(tegra_max98090_controls), + .dapm_widgets = tegra_max98090_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra_max98090_dapm_widgets), + .fully_routed = true, +}; + +static int tegra_max98090_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &snd_soc_tegra_max98090; + struct tegra_max98090 *machine; + int ret; + + machine = devm_kzalloc(&pdev->dev, + sizeof(struct tegra_max98090), GFP_KERNEL); + if (!machine) { + dev_err(&pdev->dev, "Can't allocate tegra_max98090\n"); + return -ENOMEM; + } + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + + machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0); + if (machine->gpio_hp_det == -EPROBE_DEFER) + return -EPROBE_DEFER; + + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); + if (ret) + goto err; + + ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); + if (ret) + goto err; + + tegra_max98090_dai.codec_of_node = of_parse_phandle(np, + "nvidia,audio-codec", 0); + if (!tegra_max98090_dai.codec_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,audio-codec' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_max98090_dai.cpu_of_node = of_parse_phandle(np, + "nvidia,i2s-controller", 0); + if (!tegra_max98090_dai.cpu_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,i2s-controller' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_max98090_dai.platform_of_node = tegra_max98090_dai.cpu_of_node; + + ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); + if (ret) + goto err; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err_fini_utils; + } + + return 0; + +err_fini_utils: + tegra_asoc_utils_fini(&machine->util_data); +err: + return ret; +} + +static int tegra_max98090_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card); + + snd_soc_jack_free_gpios(&tegra_max98090_hp_jack, 1, + &tegra_max98090_hp_jack_gpio); + + snd_soc_unregister_card(card); + + tegra_asoc_utils_fini(&machine->util_data); + + return 0; +} + +static const struct of_device_id tegra_max98090_of_match[] = { + { .compatible = "nvidia,tegra-audio-max98090", }, + {}, +}; + +static struct platform_driver tegra_max98090_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = tegra_max98090_of_match, + }, + .probe = tegra_max98090_probe, + .remove = tegra_max98090_remove, +}; +module_platform_driver(tegra_max98090_driver); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra max98090 machine ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra_max98090_of_match); diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index 7b2d23ba69b3..c09ffd18791b 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -42,9 +42,6 @@ static const struct snd_pcm_hardware tegra_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .channels_min = 2, - .channels_max = 2, .period_bytes_min = 1024, .period_bytes_max = PAGE_SIZE, .periods_min = 2, diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c index 5e119630b0e0..45b57892b6a5 100644 --- a/sound/soc/tegra/tegra_wm9712.c +++ b/sound/soc/tegra/tegra_wm9712.c @@ -55,7 +55,6 @@ static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_link tegra_wm9712_dai = { .name = "AC97 HiFi", .stream_name = "AC97 HiFi", - .cpu_dai_name = "tegra20-ac97", .codec_dai_name = "wm9712-hifi", .codec_name = "wm9712-codec", .init = tegra_wm9712_init, diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index fbd077f4de72..f0829de28708 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -40,11 +40,6 @@ static const struct snd_pcm_hardware txx9aclc_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_PAUSE, -#ifdef __BIG_ENDIAN - .formats = SNDRV_PCM_FMTBIT_S16_BE, -#else - .formats = SNDRV_PCM_FMTBIT_S16_LE, -#endif .period_bytes_min = 1024, .period_bytes_max = 8 * 1024, .periods_min = 2, 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", |